summaryrefslogtreecommitdiff
path: root/src/ui/menu.c
diff options
context:
space:
mode:
authorPerberos <[email protected]>2011-12-01 23:52:01 -0300
committerPerberos <[email protected]>2011-12-01 23:52:01 -0300
commit28a029a4990d2a84f9d6a0b890eba812ea503998 (patch)
tree7a69477d0dd6bf351801fa9698d95224e4fe47b6 /src/ui/menu.c
downloadmarco-28a029a4990d2a84f9d6a0b890eba812ea503998.tar.bz2
marco-28a029a4990d2a84f9d6a0b890eba812ea503998.tar.xz
moving from https://github.com/perberos/mate-desktop-environment
Diffstat (limited to 'src/ui/menu.c')
-rw-r--r--src/ui/menu.c509
1 files changed, 509 insertions, 0 deletions
diff --git a/src/ui/menu.c b/src/ui/menu.c
new file mode 100644
index 00000000..5207876f
--- /dev/null
+++ b/src/ui/menu.c
@@ -0,0 +1,509 @@
+/* Marco window menu */
+
+/*
+ * Copyright (C) 2001 Havoc Pennington
+ * Copyright (C) 2004 Rob Adams
+ * Copyright (C) 2005 Elijah Newren
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include "menu.h"
+#include "main.h"
+#include "util.h"
+#include "core.h"
+#include "themewidget.h"
+#include "metaaccellabel.h"
+#include "ui.h"
+
+typedef struct _MenuItem MenuItem;
+typedef struct _MenuData MenuData;
+
+typedef enum {
+ MENU_ITEM_SEPARATOR = 0,
+ MENU_ITEM_NORMAL,
+ MENU_ITEM_IMAGE,
+ MENU_ITEM_CHECKBOX,
+ MENU_ITEM_RADIOBUTTON,
+ MENU_ITEM_WORKSPACE_LIST,
+} MetaMenuItemType;
+
+struct _MenuItem {
+ MetaMenuOp op;
+ MetaMenuItemType type;
+ const char* stock_id;
+ const gboolean checked;
+ const char* label;
+};
+
+
+struct _MenuData {
+ MetaWindowMenu* menu;
+ MetaMenuOp op;
+};
+
+static void activate_cb(GtkWidget* menuitem, gpointer data);
+
+static MenuItem menuitems[] = {
+ /* Translators: Translate this string the same way as you do in libwnck! */
+ {META_MENU_OP_MINIMIZE, MENU_ITEM_IMAGE, MARCO_STOCK_MINIMIZE, FALSE, N_("Mi_nimize")},
+ /* Translators: Translate this string the same way as you do in libwnck! */
+ {META_MENU_OP_MAXIMIZE, MENU_ITEM_IMAGE, MARCO_STOCK_MAXIMIZE, FALSE, N_("Ma_ximize")},
+ /* Translators: Translate this string the same way as you do in libwnck! */
+ {META_MENU_OP_UNMAXIMIZE, MENU_ITEM_IMAGE, MARCO_STOCK_RESTORE, FALSE, N_("Unma_ximize")},
+ /* Translators: Translate this string the same way as you do in libwnck! */
+ {META_MENU_OP_SHADE, MENU_ITEM_NORMAL, NULL, FALSE, N_("Roll _Up")},
+ /* Translators: Translate this string the same way as you do in libwnck! */
+ {META_MENU_OP_UNSHADE, MENU_ITEM_NORMAL, NULL, FALSE, N_("_Unroll")},
+ /* Translators: Translate this string the same way as you do in libwnck! */
+ {META_MENU_OP_MOVE, MENU_ITEM_NORMAL, NULL, FALSE, N_("_Move") },
+ /* Translators: Translate this string the same way as you do in libwnck! */
+ {META_MENU_OP_RESIZE, MENU_ITEM_NORMAL, NULL, FALSE, N_("_Resize")},
+ /* Translators: Translate this string the same way as you do in libwnck! */
+ {META_MENU_OP_RECOVER, MENU_ITEM_NORMAL, NULL, FALSE, N_("Move Titlebar On_screen")},
+ {META_MENU_OP_WORKSPACES, MENU_ITEM_SEPARATOR, NULL, FALSE, NULL}, /* separator */
+ /* Translators: Translate this string the same way as you do in libwnck! */
+ {META_MENU_OP_ABOVE, MENU_ITEM_CHECKBOX, NULL, FALSE, N_("Always on _Top")},
+ /* Translators: Translate this string the same way as you do in libwnck! */
+ {META_MENU_OP_UNABOVE, MENU_ITEM_CHECKBOX, NULL, TRUE, N_("Always on _Top")},
+ /* Translators: Translate this string the same way as you do in libwnck! */
+ {META_MENU_OP_STICK, MENU_ITEM_RADIOBUTTON, NULL, FALSE, N_("_Always on Visible Workspace")},
+ /* Translators: Translate this string the same way as you do in libwnck! */
+ {META_MENU_OP_UNSTICK, MENU_ITEM_RADIOBUTTON, NULL, FALSE, N_("_Only on This Workspace")},
+ /* Translators: Translate this string the same way as you do in libwnck! */
+ {META_MENU_OP_MOVE_LEFT, MENU_ITEM_NORMAL, NULL, FALSE, N_("Move to Workspace _Left")},
+ /* Translators: Translate this string the same way as you do in libwnck! */
+ {META_MENU_OP_MOVE_RIGHT, MENU_ITEM_NORMAL, NULL, FALSE, N_("Move to Workspace R_ight")},
+ /* Translators: Translate this string the same way as you do in libwnck! */
+ {META_MENU_OP_MOVE_UP, MENU_ITEM_NORMAL, NULL, FALSE, N_("Move to Workspace _Up")},
+ /* Translators: Translate this string the same way as you do in libwnck! */
+ {META_MENU_OP_MOVE_DOWN, MENU_ITEM_NORMAL, NULL, FALSE, N_("Move to Workspace _Down")},
+ {0, MENU_ITEM_WORKSPACE_LIST, NULL, FALSE, NULL},
+ {0, MENU_ITEM_SEPARATOR, NULL, FALSE, NULL}, /* separator */
+ /* Translators: Translate this string the same way as you do in libwnck! */
+ {META_MENU_OP_DELETE, MENU_ITEM_IMAGE, MARCO_STOCK_DELETE, FALSE, N_("_Close")}
+};
+
+static void popup_position_func(GtkMenu* menu, gint* x, gint* y, gboolean* push_in, gpointer user_data)
+{
+ GtkRequisition req;
+ GdkPoint* pos;
+
+ pos = user_data;
+
+ gtk_widget_size_request(GTK_WIDGET(menu), &req);
+
+ *x = pos->x;
+ *y = pos->y;
+
+ if (meta_ui_get_direction() == META_UI_DIRECTION_RTL)
+ {
+ *x = MAX (0, *x - req.width);
+ }
+
+ /* Ensure onscreen */
+ *x = CLAMP (*x, 0, MAX(0, gdk_screen_width() - req.width));
+ *y = CLAMP (*y, 0, MAX(0, gdk_screen_height() - req.height));
+}
+
+static void menu_closed(GtkMenu* widget, gpointer data)
+{
+ MetaWindowMenu *menu;
+
+ menu = data;
+
+ meta_frames_notify_menu_hide (menu->frames);
+
+ (*menu->func)(
+ menu,
+ GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
+ menu->client_xwindow,
+ gtk_get_current_event_time (),
+ 0, 0,
+ menu->data);
+
+ /* menu may now be freed */
+}
+
+static void activate_cb(GtkWidget* menuitem, gpointer data)
+{
+ MenuData* md;
+
+ g_return_if_fail (GTK_IS_WIDGET (menuitem));
+
+ md = data;
+
+ meta_frames_notify_menu_hide(md->menu->frames);
+
+ (*md->menu->func)(
+ md->menu,
+ GDK_DISPLAY_XDISPLAY (gdk_display_get_default()),
+ md->menu->client_xwindow,
+ gtk_get_current_event_time(),
+ md->op,
+ GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem), "workspace")),
+ md->menu->data);
+
+ /* menu may now be freed */
+}
+
+/*
+ * Given a Display and an index, get the workspace name and add any
+ * accelerators. At the moment this means adding a _ if the name is of
+ * the form "Workspace n" where n is less than 10, and escaping any
+ * other '_'s so they do not create inadvertant accelerators.
+ *
+ * The calling code owns the string, and is reponsible to free the
+ * memory after use.
+ *
+ * See also http://mail.gnome.org/archives/mate-i18n/2008-March/msg00380.html
+ * which discusses possible i18n concerns.
+ */
+static char*
+get_workspace_name_with_accel (Display *display,
+ Window xroot,
+ int index)
+{
+ const char *name;
+ int number;
+ int charcount=0;
+
+ name = meta_core_get_workspace_name_with_index (display, xroot, index);
+
+ g_assert (name != NULL);
+
+ /*
+ * If the name is of the form "Workspace x" where x is an unsigned
+ * integer, insert a '_' before the number if it is less than 10 and
+ * return it
+ */
+ number = 0;
+ if (sscanf (name, _("Workspace %d%n"), &number, &charcount) != 0 &&
+ *(name + charcount)=='\0')
+ {
+ char *new_name;
+
+ /*
+ * Above name is a pointer into the Workspace struct. Here we make
+ * a copy copy so we can have our wicked way with it.
+ */
+ if (number == 10)
+ new_name = g_strdup_printf (_("Workspace 1_0"));
+ else
+ new_name = g_strdup_printf (_("Workspace %s%d"),
+ number < 10 ? "_" : "",
+ number);
+ return new_name;
+ }
+ else
+ {
+ /*
+ * Otherwise this is just a normal name. Escape any _ characters so that
+ * the user's workspace names do not get mangled. If the number is less
+ * than 10 we provide an accelerator.
+ */
+ char *new_name;
+ const char *source;
+ char *dest;
+
+ /*
+ * Assume the worst case, that every character is a _. We also
+ * provide memory for " (_#)"
+ */
+ new_name = g_malloc0 (strlen (name) * 2 + 6 + 1);
+
+ /*
+ * Now iterate down the strings, adding '_' to escape as we go
+ */
+ dest = new_name;
+ source = name;
+ while (*source != '\0')
+ {
+ if (*source == '_')
+ *dest++ = '_';
+ *dest++ = *source++;
+ }
+
+ /* People don't start at workspace 0, but workspace 1 */
+ if (index < 9)
+ {
+ g_snprintf (dest, 6, " (_%d)", index + 1);
+ }
+ else if (index == 9)
+ {
+ g_snprintf (dest, 6, " (_0)");
+ }
+
+ return new_name;
+ }
+}
+
+static GtkWidget* menu_item_new(MenuItem* menuitem, int workspace_id)
+{
+ unsigned int key;
+ MetaVirtualModifier mods;
+ const char* i18n_label;
+ GtkWidget* mi;
+ GtkWidget* accel_label;
+
+ if (menuitem->type == MENU_ITEM_NORMAL)
+ {
+ mi = gtk_menu_item_new ();
+ }
+ else if (menuitem->type == MENU_ITEM_IMAGE)
+ {
+ GtkWidget* image = gtk_image_new_from_icon_name(menuitem->stock_id, GTK_ICON_SIZE_MENU);
+
+ mi = gtk_image_menu_item_new();
+
+ gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(mi), image);
+ gtk_widget_show(image);
+ }
+ else if (menuitem->type == MENU_ITEM_CHECKBOX)
+ {
+ mi = gtk_check_menu_item_new ();
+
+ gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mi), menuitem->checked);
+ }
+ else if (menuitem->type == MENU_ITEM_RADIOBUTTON)
+ {
+ mi = gtk_check_menu_item_new ();
+
+ gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (mi), TRUE);
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mi), menuitem->checked);
+ }
+ else if (menuitem->type == MENU_ITEM_WORKSPACE_LIST)
+ {
+ return NULL;
+ }
+ else
+ {
+ return gtk_separator_menu_item_new();
+ }
+
+ i18n_label = _(menuitem->label);
+ meta_core_get_menu_accelerator (menuitem->op, workspace_id, &key, &mods);
+
+ accel_label = meta_accel_label_new_with_mnemonic (i18n_label);
+ gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
+
+ gtk_container_add (GTK_CONTAINER (mi), accel_label);
+ gtk_widget_show (accel_label);
+
+ meta_accel_label_set_accelerator (META_ACCEL_LABEL (accel_label), key, mods);
+
+ return mi;
+}
+
+MetaWindowMenu*
+meta_window_menu_new (MetaFrames *frames,
+ MetaMenuOp ops,
+ MetaMenuOp insensitive,
+ Window client_xwindow,
+ unsigned long active_workspace,
+ int n_workspaces,
+ MetaWindowMenuFunc func,
+ gpointer data)
+{
+ int i;
+ MetaWindowMenu *menu;
+
+ /* FIXME: Modifications to 'ops' should happen in meta_window_show_menu */
+ if (n_workspaces < 2)
+ ops &= ~(META_MENU_OP_STICK | META_MENU_OP_UNSTICK | META_MENU_OP_WORKSPACES);
+ else if (n_workspaces == 2)
+ /* #151183: If we only have two workspaces, disable the menu listing them. */
+ ops &= ~(META_MENU_OP_WORKSPACES);
+
+ menu = g_new (MetaWindowMenu, 1);
+ menu->frames = frames;
+ menu->client_xwindow = client_xwindow;
+ menu->func = func;
+ menu->data = data;
+ menu->ops = ops;
+ menu->insensitive = insensitive;
+
+ menu->menu = gtk_menu_new ();
+
+ gtk_menu_set_screen (GTK_MENU (menu->menu),
+ gtk_widget_get_screen (GTK_WIDGET (frames)));
+
+ for (i = 0; i < (int) G_N_ELEMENTS (menuitems); i++)
+ {
+ MenuItem menuitem = menuitems[i];
+ if (ops & menuitem.op || menuitem.op == 0)
+ {
+ GtkWidget *mi;
+ MenuData *md;
+ unsigned int key;
+ MetaVirtualModifier mods;
+
+ mi = menu_item_new (&menuitem, -1);
+
+ /* Set the activeness of radiobuttons. */
+ switch (menuitem.op)
+ {
+ case META_MENU_OP_STICK:
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mi),
+ active_workspace == 0xFFFFFFFF);
+ break;
+ case META_MENU_OP_UNSTICK:
+ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mi),
+ active_workspace != 0xFFFFFFFF);
+ break;
+ default:
+ break;
+ }
+
+ if (menuitem.type == MENU_ITEM_WORKSPACE_LIST)
+ {
+ if (ops & META_MENU_OP_WORKSPACES)
+ {
+ Display *display;
+ Window xroot;
+ GdkScreen *screen;
+ GtkWidget *submenu;
+ int j;
+
+ MenuItem to_another_workspace = {
+ 0, MENU_ITEM_NORMAL,
+ NULL, FALSE,
+ N_("Move to Another _Workspace")
+ };
+
+ meta_verbose ("Creating %d-workspace menu current space %lu\n",
+ n_workspaces, active_workspace);
+
+ display = gdk_x11_drawable_get_xdisplay (GTK_WIDGET (frames)->window);
+
+ screen = gdk_drawable_get_screen (GTK_WIDGET (frames)->window);
+ xroot = GDK_DRAWABLE_XID (gdk_screen_get_root_window (screen));
+
+ submenu = gtk_menu_new ();
+
+ g_assert (mi==NULL);
+ mi = menu_item_new (&to_another_workspace, -1);
+ gtk_menu_item_set_submenu (GTK_MENU_ITEM (mi), submenu);
+
+ for (j = 0; j < n_workspaces; j++)
+ {
+ char *label;
+ MenuData *md;
+ unsigned int key;
+ MetaVirtualModifier mods;
+ MenuItem moveitem;
+ GtkWidget *submi;
+
+ meta_core_get_menu_accelerator (META_MENU_OP_WORKSPACES,
+ j + 1,
+ &key, &mods);
+
+ label = get_workspace_name_with_accel (display, xroot, j);
+
+ moveitem.type = MENU_ITEM_NORMAL;
+ moveitem.op = META_MENU_OP_WORKSPACES;
+ moveitem.label = label;
+ submi = menu_item_new (&moveitem, j + 1);
+
+ g_free (label);
+
+ if ((active_workspace == (unsigned)j) && (ops & META_MENU_OP_UNSTICK))
+ gtk_widget_set_sensitive (submi, FALSE);
+
+ md = g_new (MenuData, 1);
+
+ md->menu = menu;
+ md->op = META_MENU_OP_WORKSPACES;
+
+ g_object_set_data (G_OBJECT (submi),
+ "workspace",
+ GINT_TO_POINTER (j));
+
+ gtk_signal_connect_full (GTK_OBJECT (submi),
+ "activate",
+ G_CALLBACK (activate_cb),
+ NULL,
+ md,
+ g_free, FALSE, FALSE);
+
+ gtk_menu_shell_append (GTK_MENU_SHELL (submenu), submi);
+
+ gtk_widget_show (submi);
+ }
+ }
+ else
+ meta_verbose ("not creating workspace menu\n");
+ }
+ else if (menuitem.type != MENU_ITEM_SEPARATOR)
+ {
+ meta_core_get_menu_accelerator (menuitems[i].op, -1,
+ &key, &mods);
+
+ if (insensitive & menuitem.op)
+ gtk_widget_set_sensitive (mi, FALSE);
+
+ md = g_new (MenuData, 1);
+
+ md->menu = menu;
+ md->op = menuitem.op;
+
+ gtk_signal_connect_full (GTK_OBJECT (mi),
+ "activate",
+ G_CALLBACK (activate_cb),
+ NULL,
+ md,
+ g_free, FALSE, FALSE);
+ }
+
+ if (mi)
+ {
+ gtk_menu_shell_append (GTK_MENU_SHELL (menu->menu), mi);
+
+ gtk_widget_show (mi);
+ }
+ }
+ }
+
+ g_signal_connect (menu->menu, "selection_done", G_CALLBACK(menu_closed), menu);
+
+ return menu;
+}
+
+void meta_window_menu_popup(MetaWindowMenu* menu, int root_x, int root_y, int button, guint32 timestamp)
+{
+ GdkPoint* pt = g_new(GdkPoint, 1);
+
+ g_object_set_data_full(G_OBJECT(menu->menu), "destroy-point", pt, g_free);
+
+ pt->x = root_x;
+ pt->y = root_y;
+
+ gtk_menu_popup(GTK_MENU (menu->menu), NULL, NULL, popup_position_func, pt, button, timestamp);
+
+ if (!GTK_MENU_SHELL(menu->menu)->have_xgrab)
+ {
+ meta_warning("GtkMenu failed to grab the pointer\n");
+ }
+}
+
+void meta_window_menu_free(MetaWindowMenu* menu)
+{
+ gtk_widget_destroy(menu->menu);
+ g_free(menu);
+}