00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include <config.h>
00027 #include <stdio.h>
00028 #include <string.h>
00029 #include "menu.h"
00030 #include "main.h"
00031 #include "util.h"
00032 #include "core.h"
00033 #include "themewidget.h"
00034 #include "metaaccellabel.h"
00035 #include "ui.h"
00036
00037 typedef struct _MenuItem MenuItem;
00038 typedef struct _MenuData MenuData;
00039
00040 typedef enum
00041 {
00042 MENU_ITEM_SEPARATOR = 0,
00043 MENU_ITEM_NORMAL,
00044 MENU_ITEM_IMAGE,
00045 MENU_ITEM_CHECKBOX,
00046 MENU_ITEM_RADIOBUTTON,
00047 MENU_ITEM_WORKSPACE_LIST,
00048 } MetaMenuItemType;
00049
00050 struct _MenuItem
00051 {
00052 MetaMenuOp op;
00053 MetaMenuItemType type;
00054 const char *stock_id;
00055 const gboolean checked;
00056 const char *label;
00057 };
00058
00059
00060 struct _MenuData
00061 {
00062 MetaWindowMenu *menu;
00063 MetaMenuOp op;
00064 };
00065
00066 static void activate_cb (GtkWidget *menuitem, gpointer data);
00067
00068 static MenuItem menuitems[] = {
00069
00070 { META_MENU_OP_MINIMIZE, MENU_ITEM_IMAGE, METACITY_STOCK_MINIMIZE, FALSE, N_("Mi_nimize") },
00071
00072 { META_MENU_OP_MAXIMIZE, MENU_ITEM_IMAGE, METACITY_STOCK_MAXIMIZE, FALSE, N_("Ma_ximize") },
00073
00074 { META_MENU_OP_UNMAXIMIZE, MENU_ITEM_NORMAL, NULL, FALSE, N_("Unma_ximize") },
00075
00076 { META_MENU_OP_SHADE, MENU_ITEM_NORMAL, NULL, FALSE, N_("Roll _Up") },
00077
00078 { META_MENU_OP_UNSHADE, MENU_ITEM_NORMAL, NULL, FALSE, N_("_Unroll") },
00079
00080 { META_MENU_OP_MOVE, MENU_ITEM_NORMAL, NULL, FALSE, N_("_Move") },
00081
00082 { META_MENU_OP_RESIZE, MENU_ITEM_NORMAL, NULL, FALSE, N_("_Resize") },
00083
00084 { META_MENU_OP_RECOVER, MENU_ITEM_NORMAL, NULL, FALSE, N_("Move Titlebar On_screen") },
00085 { META_MENU_OP_WORKSPACES, MENU_ITEM_SEPARATOR, NULL, FALSE, NULL },
00086
00087 { META_MENU_OP_ABOVE, MENU_ITEM_CHECKBOX, NULL, FALSE, N_("Always on _Top") },
00088
00089 { META_MENU_OP_UNABOVE, MENU_ITEM_CHECKBOX, NULL, TRUE, N_("Always on _Top") },
00090
00091 { META_MENU_OP_STICK, MENU_ITEM_RADIOBUTTON, NULL, FALSE, N_("_Always on Visible Workspace") },
00092
00093 { META_MENU_OP_UNSTICK, MENU_ITEM_RADIOBUTTON, NULL, FALSE, N_("_Only on This Workspace") },
00094
00095 { META_MENU_OP_MOVE_LEFT, MENU_ITEM_NORMAL, NULL, FALSE, N_("Move to Workspace _Left") },
00096
00097 { META_MENU_OP_MOVE_RIGHT, MENU_ITEM_NORMAL, NULL, FALSE, N_("Move to Workspace R_ight") },
00098
00099 { META_MENU_OP_MOVE_UP, MENU_ITEM_NORMAL, NULL, FALSE, N_("Move to Workspace _Up") },
00100
00101 { META_MENU_OP_MOVE_DOWN, MENU_ITEM_NORMAL, NULL, FALSE, N_("Move to Workspace _Down") },
00102 { 0, MENU_ITEM_WORKSPACE_LIST, NULL, FALSE, NULL },
00103 { 0, MENU_ITEM_SEPARATOR, NULL, FALSE, NULL },
00104
00105 { META_MENU_OP_DELETE, MENU_ITEM_IMAGE, METACITY_STOCK_DELETE, FALSE, N_("_Close") }
00106 };
00107
00108 static void
00109 popup_position_func (GtkMenu *menu,
00110 gint *x,
00111 gint *y,
00112 gboolean *push_in,
00113 gpointer user_data)
00114 {
00115 GtkRequisition req;
00116 GdkPoint *pos;
00117
00118 pos = user_data;
00119
00120 gtk_widget_size_request (GTK_WIDGET (menu), &req);
00121
00122 *x = pos->x;
00123 *y = pos->y;
00124
00125 if (meta_ui_get_direction() == META_UI_DIRECTION_RTL)
00126 *x = MAX (0, *x - req.width);
00127
00128
00129 *x = CLAMP (*x, 0, MAX (0, gdk_screen_width () - req.width));
00130 *y = CLAMP (*y, 0, MAX (0, gdk_screen_height () - req.height));
00131 }
00132
00133 static void
00134 menu_closed (GtkMenu *widget,
00135 gpointer data)
00136 {
00137 MetaWindowMenu *menu;
00138
00139 menu = data;
00140
00141 meta_frames_notify_menu_hide (menu->frames);
00142 (* menu->func) (menu, gdk_display,
00143 menu->client_xwindow,
00144 gtk_get_current_event_time (),
00145 0, 0,
00146 menu->data);
00147
00148
00149 }
00150
00151 static void
00152 activate_cb (GtkWidget *menuitem, gpointer data)
00153 {
00154 MenuData *md;
00155
00156 g_return_if_fail (GTK_IS_WIDGET (menuitem));
00157
00158 md = data;
00159
00160 meta_frames_notify_menu_hide (md->menu->frames);
00161 (* md->menu->func) (md->menu, gdk_display,
00162 md->menu->client_xwindow,
00163 gtk_get_current_event_time (),
00164 md->op,
00165 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (menuitem),
00166 "workspace")),
00167 md->menu->data);
00168
00169
00170 }
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184 static char*
00185 get_workspace_name_with_accel (Display *display,
00186 Window xroot,
00187 int index)
00188 {
00189 const char *name;
00190 int number;
00191 int charcount=0;
00192
00193 name = meta_core_get_workspace_name_with_index (display, xroot, index);
00194
00195 g_assert (name != NULL);
00196
00197
00198
00199
00200
00201
00202 number = 0;
00203 if (sscanf (name, _("Workspace %d%n"), &number, &charcount) != 0 &&
00204 *(name + charcount)=='\0')
00205 {
00206 char *new_name;
00207
00208
00209
00210
00211
00212 if (number == 10)
00213 new_name = g_strdup_printf (_("Workspace 1_0"));
00214 else
00215 new_name = g_strdup_printf (_("Workspace %s%d"),
00216 number < 10 ? "_" : "",
00217 number);
00218 return new_name;
00219 }
00220 else
00221 {
00222
00223
00224
00225
00226
00227 char *new_name;
00228 const char *source;
00229 char *dest;
00230
00231
00232
00233
00234
00235 new_name = g_malloc0 (strlen (name) * 2 + 6 + 1);
00236
00237
00238
00239
00240 dest = new_name;
00241 source = name;
00242 while (*source != '\0')
00243 {
00244 if (*source == '_')
00245 *dest++ = '_';
00246 *dest++ = *source++;
00247 }
00248
00249
00250 if (index < 9)
00251 {
00252 g_snprintf (dest, 6, " (_%d)", index + 1);
00253 }
00254 else if (index == 9)
00255 {
00256 g_snprintf (dest, 6, " (_0)");
00257 }
00258
00259 return new_name;
00260 }
00261 }
00262
00263 static GtkWidget *
00264 menu_item_new (MenuItem *menuitem, int workspace_id)
00265 {
00266 unsigned int key;
00267 MetaVirtualModifier mods;
00268 const char *i18n_label;
00269 GtkWidget *mi;
00270 GtkWidget *accel_label;
00271
00272 if (menuitem->type == MENU_ITEM_NORMAL)
00273 {
00274 mi = gtk_menu_item_new ();
00275 }
00276 else if (menuitem->type == MENU_ITEM_IMAGE)
00277 {
00278 GtkWidget *image;
00279
00280 image = gtk_image_new_from_stock (menuitem->stock_id, GTK_ICON_SIZE_MENU);
00281 mi = gtk_image_menu_item_new ();
00282
00283 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mi), image);
00284 gtk_widget_show (image);
00285 }
00286 else if (menuitem->type == MENU_ITEM_CHECKBOX)
00287 {
00288 mi = gtk_check_menu_item_new ();
00289
00290 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mi),
00291 menuitem->checked);
00292 }
00293 else if (menuitem->type == MENU_ITEM_RADIOBUTTON)
00294 {
00295 mi = gtk_check_menu_item_new ();
00296
00297 gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (mi),
00298 TRUE);
00299 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mi),
00300 menuitem->checked);
00301 }
00302 else if (menuitem->type == MENU_ITEM_WORKSPACE_LIST)
00303 return NULL;
00304 else
00305 return gtk_separator_menu_item_new ();
00306
00307 i18n_label = _(menuitem->label);
00308 meta_core_get_menu_accelerator (menuitem->op, workspace_id, &key, &mods);
00309
00310 accel_label = meta_accel_label_new_with_mnemonic (i18n_label);
00311 gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
00312
00313 gtk_container_add (GTK_CONTAINER (mi), accel_label);
00314 gtk_widget_show (accel_label);
00315
00316 meta_accel_label_set_accelerator (META_ACCEL_LABEL (accel_label),
00317 key, mods);
00318
00319 return mi;
00320 }
00321
00322 MetaWindowMenu*
00323 meta_window_menu_new (MetaFrames *frames,
00324 MetaMenuOp ops,
00325 MetaMenuOp insensitive,
00326 Window client_xwindow,
00327 unsigned long active_workspace,
00328 int n_workspaces,
00329 MetaWindowMenuFunc func,
00330 gpointer data)
00331 {
00332 int i;
00333 MetaWindowMenu *menu;
00334
00335
00336 if (n_workspaces < 2)
00337 ops &= ~(META_MENU_OP_STICK | META_MENU_OP_UNSTICK | META_MENU_OP_WORKSPACES);
00338 else if (n_workspaces == 2)
00339
00340 ops &= ~(META_MENU_OP_WORKSPACES);
00341
00342 menu = g_new (MetaWindowMenu, 1);
00343 menu->frames = frames;
00344 menu->client_xwindow = client_xwindow;
00345 menu->func = func;
00346 menu->data = data;
00347 menu->ops = ops;
00348 menu->insensitive = insensitive;
00349
00350 menu->menu = gtk_menu_new ();
00351
00352 gtk_menu_set_screen (GTK_MENU (menu->menu),
00353 gtk_widget_get_screen (GTK_WIDGET (frames)));
00354
00355 for (i = 0; i < (int) G_N_ELEMENTS (menuitems); i++)
00356 {
00357 MenuItem menuitem = menuitems[i];
00358 if (ops & menuitem.op || menuitem.op == 0)
00359 {
00360 GtkWidget *mi;
00361 MenuData *md;
00362 unsigned int key;
00363 MetaVirtualModifier mods;
00364
00365 mi = menu_item_new (&menuitem, -1);
00366
00367
00368 switch (menuitem.op)
00369 {
00370 case META_MENU_OP_STICK:
00371 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mi),
00372 active_workspace == 0xFFFFFFFF);
00373 break;
00374 case META_MENU_OP_UNSTICK:
00375 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mi),
00376 active_workspace != 0xFFFFFFFF);
00377 break;
00378 default:
00379 break;
00380 }
00381
00382 if (menuitem.type == MENU_ITEM_WORKSPACE_LIST)
00383 {
00384 if (ops & META_MENU_OP_WORKSPACES)
00385 {
00386 Display *display;
00387 Window xroot;
00388 GdkScreen *screen;
00389 GtkWidget *submenu;
00390 int j;
00391
00392 MenuItem to_another_workspace = {
00393 0, MENU_ITEM_NORMAL,
00394 NULL, FALSE,
00395 N_("Move to Another _Workspace")
00396 };
00397
00398 meta_verbose ("Creating %d-workspace menu current space %lu\n",
00399 n_workspaces, active_workspace);
00400
00401 display = gdk_x11_drawable_get_xdisplay (GTK_WIDGET (frames)->window);
00402
00403 screen = gdk_drawable_get_screen (GTK_WIDGET (frames)->window);
00404 xroot = GDK_DRAWABLE_XID (gdk_screen_get_root_window (screen));
00405
00406 submenu = gtk_menu_new ();
00407
00408 g_assert (mi==NULL);
00409 mi = menu_item_new (&to_another_workspace, -1);
00410 gtk_menu_item_set_submenu (GTK_MENU_ITEM (mi), submenu);
00411
00412 for (j = 0; j < n_workspaces; j++)
00413 {
00414 char *label;
00415 MenuData *md;
00416 unsigned int key;
00417 MetaVirtualModifier mods;
00418 MenuItem moveitem;
00419 GtkWidget *submi;
00420
00421 meta_core_get_menu_accelerator (META_MENU_OP_WORKSPACES,
00422 j + 1,
00423 &key, &mods);
00424
00425 label = get_workspace_name_with_accel (display, xroot, j);
00426
00427 moveitem.type = MENU_ITEM_NORMAL;
00428 moveitem.op = META_MENU_OP_WORKSPACES;
00429 moveitem.label = label;
00430 submi = menu_item_new (&moveitem, j + 1);
00431
00432 g_free (label);
00433
00434 if ((active_workspace == (unsigned)j) && (ops & META_MENU_OP_UNSTICK))
00435 gtk_widget_set_sensitive (submi, FALSE);
00436
00437 md = g_new (MenuData, 1);
00438
00439 md->menu = menu;
00440 md->op = META_MENU_OP_WORKSPACES;
00441
00442 g_object_set_data (G_OBJECT (submi),
00443 "workspace",
00444 GINT_TO_POINTER (j));
00445
00446 gtk_signal_connect_full (GTK_OBJECT (submi),
00447 "activate",
00448 GTK_SIGNAL_FUNC (activate_cb),
00449 NULL,
00450 md,
00451 g_free, FALSE, FALSE);
00452
00453 gtk_menu_shell_append (GTK_MENU_SHELL (submenu), submi);
00454
00455 gtk_widget_show (submi);
00456 }
00457 }
00458 else
00459 meta_verbose ("not creating workspace menu\n");
00460 }
00461 else if (menuitem.type != MENU_ITEM_SEPARATOR)
00462 {
00463 meta_core_get_menu_accelerator (menuitems[i].op, -1,
00464 &key, &mods);
00465
00466 if (insensitive & menuitem.op)
00467 gtk_widget_set_sensitive (mi, FALSE);
00468
00469 md = g_new (MenuData, 1);
00470
00471 md->menu = menu;
00472 md->op = menuitem.op;
00473
00474 gtk_signal_connect_full (GTK_OBJECT (mi),
00475 "activate",
00476 GTK_SIGNAL_FUNC (activate_cb),
00477 NULL,
00478 md,
00479 g_free, FALSE, FALSE);
00480 }
00481
00482 if (mi)
00483 {
00484 gtk_menu_shell_append (GTK_MENU_SHELL (menu->menu), mi);
00485
00486 gtk_widget_show (mi);
00487 }
00488 }
00489 }
00490
00491
00492 g_signal_connect (menu->menu, "selection_done",
00493 G_CALLBACK (menu_closed), menu);
00494
00495 return menu;
00496 }
00497
00498 void
00499 meta_window_menu_popup (MetaWindowMenu *menu,
00500 int root_x,
00501 int root_y,
00502 int button,
00503 guint32 timestamp)
00504 {
00505 GdkPoint *pt;
00506
00507 pt = g_new (GdkPoint, 1);
00508
00509 g_object_set_data_full (G_OBJECT (menu->menu),
00510 "destroy-point",
00511 pt,
00512 g_free);
00513
00514 pt->x = root_x;
00515 pt->y = root_y;
00516
00517 gtk_menu_popup (GTK_MENU (menu->menu),
00518 NULL, NULL,
00519 popup_position_func, pt,
00520 button,
00521 timestamp);
00522
00523 if (!GTK_MENU_SHELL (menu->menu)->have_xgrab)
00524 meta_warning ("GtkMenu failed to grab the pointer\n");
00525 }
00526
00527 void
00528 meta_window_menu_free (MetaWindowMenu *menu)
00529 {
00530 gtk_widget_destroy (menu->menu);
00531 g_free (menu);
00532 }