diff --git a/plugins/windowmenu/windowmenu.c b/plugins/windowmenu/windowmenu.c index f61fdac..95a64ea 100644 --- a/plugins/windowmenu/windowmenu.c +++ b/plugins/windowmenu/windowmenu.c @@ -138,6 +138,7 @@ XFCE_PANEL_DEFINE_PLUGIN_RESIDENT (WindowMenuPlugin, window_menu_plugin) static GQuark window_quark = 0; +static GQuark active_item_quark = 0; static GtkIconSize menu_icon_size = GTK_ICON_SIZE_INVALID; @@ -226,6 +227,7 @@ window_menu_plugin_class_init (WindowMenuPluginClass *klass) EXO_PARAM_READABLE)); window_quark = g_quark_from_static_string ("window-list-window-quark"); + active_item_quark = g_quark_from_static_string ("window-list-menu-active-item-quark"); menu_icon_size = gtk_icon_size_from_name ("panel-window-menu"); if (menu_icon_size == GTK_ICON_SIZE_INVALID) @@ -877,13 +879,14 @@ window_menu_plugin_menu_workspace_item_new (WnckWorkspace *workspace, static void -window_menu_plugin_menu_actions_selection_done (GtkWidget *action_menu, +window_menu_plugin_menu_actions_hide (GtkWidget *action_menu, GtkMenuShell *menu) { panel_return_if_fail (GTK_IS_MENU_SHELL (menu)); panel_return_if_fail (WNCK_IS_ACTION_MENU (action_menu)); - gtk_widget_destroy (action_menu); + /* delay destruction so we can handle the activate event first */ + exo_gtk_object_destroy_later (GTK_OBJECT (action_menu)); /* deactive the window list menu */ gtk_menu_shell_cancel (menu); @@ -891,44 +894,100 @@ window_menu_plugin_menu_actions_selection_done (GtkWidget *action_menu, +static void +window_menu_plugin_menu_actions_position_menu (GtkMenu *action_menu, + gint *x, + gint *y, + gboolean *push_in, + GtkMenuItem *menu_item) +{ + GtkWidget *widget; + GtkRequisition requisition; + GdkScreen *screen; + GdkRectangle monitor; + gint monitor_num; + gint wx, wy; + gint space_above, space_below; + GtkTextDirection direction; + + panel_return_if_fail (GTK_IS_MENU (action_menu)); + panel_return_if_fail (GTK_IS_MENU_ITEM (menu_item)); + + widget = GTK_WIDGET (menu_item); + + if (!gdk_window_get_origin (gtk_widget_get_window (widget), &wx, &wy)) + return; + + wx += widget->allocation.x; + wy += widget->allocation.y; + + gtk_widget_size_request (GTK_WIDGET (action_menu), &requisition); + + screen = gtk_widget_get_screen (widget); + monitor_num = gdk_screen_get_monitor_at_window (screen, gtk_widget_get_window (widget)); + gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor); + + space_above = wy - monitor.y; + space_below = (monitor.y + monitor.height) - (wy + widget->allocation.height); + + if (space_below >= requisition.height + || space_below >= space_above) + { + wy += widget->allocation.height; + } + else + { + wy -= requisition.height; + } + + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) + wx += widget->allocation.width - requisition.width; + + *x = MAX (MIN (wx, monitor.x + monitor.width - requisition.width), monitor.x); + *y = wy; + + *push_in = FALSE; +} + + + static gboolean -window_menu_plugin_menu_window_item_activate (GtkWidget *mi, - GdkEventButton *event, - WnckWindow *window) +window_menu_plugin_menu_window_item_activate (GtkWidget *mi, + WnckWindow *window, + guint button, + guint32 event_time, + gboolean menu_at_pointer) { WnckWorkspace *workspace; GtkWidget *menu; panel_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE); panel_return_val_if_fail (GTK_IS_MENU_ITEM (mi), FALSE); panel_return_val_if_fail (GTK_IS_MENU_SHELL (mi->parent), FALSE); - /* only respond to a button releases */ - if (event->type != GDK_BUTTON_RELEASE) - return FALSE; - - if (event->button == 1) + if (button == 1) { /* go to workspace and activate window */ workspace = wnck_window_get_workspace (window); if (workspace != NULL) - wnck_workspace_activate (workspace, event->time - 1); - wnck_window_activate (window, event->time); + wnck_workspace_activate (workspace, event_time - 1); + wnck_window_activate (window, event_time); } - else if (event->button == 2) + else if (button == 2) { /* active the window (bring it to this workspace) */ - wnck_window_activate (window, event->time); + wnck_window_activate (window, event_time); } - else if (event->button == 3) + else if (button == 3) { /* popup the window action menu */ menu = wnck_action_menu_new (window); - g_signal_connect (G_OBJECT (menu), "selection-done", - G_CALLBACK (window_menu_plugin_menu_actions_selection_done), + g_signal_connect (G_OBJECT (menu), "hide", + G_CALLBACK (window_menu_plugin_menu_actions_hide), gtk_widget_get_parent (mi)); - gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, - NULL, event->button, event->time); + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, + menu_at_pointer ? NULL : window_menu_plugin_menu_actions_position_menu, + GTK_MENU_ITEM (mi), 0, event_time); return TRUE; } @@ -938,6 +997,57 @@ window_menu_plugin_menu_window_item_activate (GtkWidget *mi, +static gboolean +window_menu_plugin_menu_window_item_button_release (GtkWidget *mi, + GdkEventButton *event, + WnckWindow *window) +{ + panel_return_val_if_fail (WNCK_IS_WINDOW (window), FALSE); + panel_return_val_if_fail (GTK_IS_MENU_ITEM (mi), FALSE); + panel_return_val_if_fail (GTK_IS_MENU_SHELL (mi->parent), FALSE); + + /* only respond to a button releases */ + if (event->type != GDK_BUTTON_RELEASE) + return FALSE; + + return window_menu_plugin_menu_window_item_activate (mi, window, + event->button, event->time, TRUE); +} + + + +static gboolean +window_menu_plugin_menu_window_item_select (GtkWidget *mi) +{ + GtkWidget *menu; + + panel_return_val_if_fail (GTK_IS_MENU_ITEM (mi), FALSE); + panel_return_val_if_fail (GTK_IS_MENU_SHELL (mi->parent), FALSE); + + menu = gtk_widget_get_parent (mi); + g_object_set_qdata (G_OBJECT (menu), active_item_quark, mi); + + return FALSE; +} + + + +static gboolean +window_menu_plugin_menu_window_item_deselect (GtkWidget *mi) +{ + GtkWidget *menu; + + panel_return_val_if_fail (GTK_IS_MENU_ITEM (mi), FALSE); + panel_return_val_if_fail (GTK_IS_MENU_SHELL (mi->parent), FALSE); + + menu = gtk_widget_get_parent (mi); + g_object_set_qdata (G_OBJECT (menu), active_item_quark, NULL); + + return FALSE; +} + + + static GtkWidget * window_menu_plugin_menu_window_item_new (WnckWindow *window, WindowMenuPlugin *plugin, @@ -976,7 +1086,11 @@ window_menu_plugin_menu_window_item_new (WnckWindow *window, gtk_widget_set_tooltip_text (mi, tooltip); g_object_set_qdata (G_OBJECT (mi), window_quark, window); g_signal_connect (G_OBJECT (mi), "button-release-event", - G_CALLBACK (window_menu_plugin_menu_window_item_activate), window); + G_CALLBACK (window_menu_plugin_menu_window_item_button_release), window); + g_signal_connect (G_OBJECT (mi), "select", + G_CALLBACK (window_menu_plugin_menu_window_item_select), NULL); + g_signal_connect (G_OBJECT (mi), "deselect", + G_CALLBACK (window_menu_plugin_menu_window_item_deselect), NULL); g_free (utf8); g_free (decorated); @@ -1059,10 +1173,10 @@ static gboolean window_menu_plugin_menu_key_press_event (GtkWidget *menu, GdkEventKey *event) { - GtkWidget *mi = NULL; - GdkEventButton fake_event = { 0, }; - guint modifiers; - WnckWindow *window; + GtkWidget *mi = NULL; + guint button = 0; + guint modifiers; + WnckWindow *window; panel_return_val_if_fail (GTK_IS_MENU (menu), FALSE); @@ -1074,49 +1188,54 @@ window_menu_plugin_menu_key_press_event (GtkWidget *menu, case GDK_KP_Space: case GDK_KP_Enter: /* active the menu item */ - fake_event.button = 1; + button = 1; break; case GDK_Menu: /* popup the window actions menu */ - fake_event.button = 3; + button = 3; break; + case GDK_F10: + modifiers = event->state & gtk_accelerator_get_default_mod_mask (); + + if (modifiers == GDK_SHIFT_MASK) + /* popup the window actions menu */ + button = 3; + break; + default: return FALSE; } - /* popdown the menu, this will also update the active item */ - gtk_menu_popdown (GTK_MENU (menu)); - - /* get the active menu item leave when no item if found */ - mi = gtk_menu_get_active (GTK_MENU (menu)); + /* get the active window menu item leave when no item if found */ + mi = g_object_get_qdata (G_OBJECT (menu), active_item_quark); panel_return_val_if_fail (mi == NULL || GTK_IS_MENU_ITEM (mi), FALSE); if (mi == NULL) return FALSE; - if (fake_event.button == 1) + if (button == 1) { /* get the modifiers */ modifiers = event->state & gtk_accelerator_get_default_mod_mask (); if (modifiers == GDK_SHIFT_MASK) - fake_event.button = 2; + button = 2; else if (modifiers == GDK_CONTROL_MASK) - fake_event.button = 3; + button = 3; } - /* complete the event */ - fake_event.type = GDK_BUTTON_RELEASE; - fake_event.time = event->time; - - /* try the get the window and active an item */ + /* try the get the window and activate an item */ window = g_object_get_qdata (G_OBJECT (mi), window_quark); if (window != NULL) - window_menu_plugin_menu_window_item_activate (mi, &fake_event, window); + window_menu_plugin_menu_window_item_activate (mi, window, button, + event->time, FALSE); else gtk_menu_item_activate (GTK_MENU_ITEM (mi)); + if (button != 3) + gtk_menu_shell_cancel (GTK_MENU_SHELL (menu)); + return FALSE; }