[Xfce4-commits] <xfce4-taskbar-plugin:master> Hover menu implementation largely complete, still some conflicts between the old focus grabbing menu and the new mouse-over-focus only menu, will get to those soon :)

Gearoid Murphy noreply at xfce.org
Mon Oct 8 21:22:01 CEST 2012


Updating branch refs/heads/master
         to e6823823cecfc858af31ac621af242b2b06fc105 (commit)
       from 1ab8f1f5c2f6bfeeb38ca06ba5ee335ffaf180e9 (commit)

commit e6823823cecfc858af31ac621af242b2b06fc105
Author: Gearoid Murphy <gearoid at murphy.com>
Date:   Mon Oct 8 20:19:06 2012 +0100

    Hover menu implementation largely complete, still some conflicts between the old focus grabbing menu
    and the new mouse-over-focus only menu, will get to those soon :)

 Makefile         |   19 ++-
 README           |   39 +++++-
 taskbar-widget.c |  414 +++++++++++++++++++++++++++++++++---------------------
 3 files changed, 307 insertions(+), 165 deletions(-)

diff --git a/Makefile b/Makefile
index 0c7aee3..82101e5 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,5 @@
-CFLAGS=-fPIC -I. `pkg-config --cflags-only-I gtk+-2.0 exo-1 libwnck-1.0 libxfce4panel-1.0 libxfce4ui-1 libxfconf-0 gtkhotkey-1.0`
+#CFLAGS=-fPIC -g -I. `pkg-config --cflags-only-I gtk+-2.0 exo-1 libwnck-1.0 libxfce4panel-1.0 libxfce4ui-1 libxfconf-0 gtkhotkey-1.0`
+CFLAGS=-fPIC -O3 -I. `pkg-config --cflags-only-I gtk+-2.0 exo-1 libwnck-1.0 libxfce4panel-1.0 libxfce4ui-1 libxfconf-0 gtkhotkey-1.0`
 LINKFLAGS=`pkg-config --libs gtk+-2.0 exo-1 libwnck-1.0 libxfce4panel-1.0 libxfce4ui-1 libxfconf-0 gobject-2.0 gtk+-x11-2.0 glib-2.0 gtkhotkey-1.0`
 
 OBJ=taskbar.o taskbar-widget.o hotkeys.o
@@ -9,12 +10,20 @@ libtaskbar:$(OBJ)
 	g++ -shared  -o libtaskbar.so $(OBJ) $(LINKFLAGS)
 
 install:
-	ln -s `pwd`/libtaskbar.so /usr/lib/xfce4/panel-plugins/libtaskbar.so
-	ln -s `pwd`/taskbar.desktop /usr/share/xfce4/panel/plugins/taskbar.desktop
+	sudo ln -s `pwd`/libtaskbar.so /usr/lib/xfce4/panel-plugins/libtaskbar.so
+	sudo ln -s `pwd`/taskbar.desktop /usr/share/xfce4/panel/plugins/taskbar.desktop
 
 uninstall:
-	rm -f /usr/lib/xfce4/panel-plugins/libtaskbar.so
-	rm -f /usr/share/xfce4/panel/plugins/taskbar.desktop
+	sudo rm -f /usr/lib/xfce4/panel-plugins/libtaskbar.so
+	sudo rm -f /usr/share/xfce4/panel/plugins/taskbar.desktop
 
 clean:
 	rm -f *.o *.so
+
+devenv:
+	sudo apt-get install libgtk2.0-dev libexo-1-dev libxfce4ui-1-dev libxfce4util-dev libxfcegui4-dev libxfconf-0-dev xfce4-panel-dev libwnck-dev libgtkhotkey-dev
+
+restart:
+	xfce4-panel -r
+
+	
\ No newline at end of file
diff --git a/README b/README
index 406855f..c2b9676 100644
--- a/README
+++ b/README
@@ -1,13 +1,46 @@
-1) To build and install the plugin:
+1) Make sure you've got the development packages
+    
+    # Valid for XUbuntu 12.04
+    make devenv
+
+2) Build and install the plugin:
 
     make
     sudo make install
 
-2) To remove the plugin
+3) To remove the plugin
 
     sudo make uninstall
 
-** Known Issues / Notes ***
+4) For development
+
+    #uninstall, install, restart the panel
+    make restart
+
+** Usage Notes **
+
+    <Super> + number will activate the pinned application indexed by the
+    number (Super is the so-called 'Windows' key)
+    
+    Right-click on an application icon will give you the option to pin/unpin
+    the app
+    
+    Left-click on the application icon has context dependent behaviour:
+    
+        1) If it is a pinned icon and there are no application instances
+        running, the associated application is started
+        
+        2) If there is a single application instance running, it is raised
+        or lowered, depending on it's current state
+        
+        3) If there are multiple application instances running, a list to
+        the application windows is shown
+    
+
+** Known Issues ***
+    
+    Drag and drop of applications from the applications menu or the desktop
+    does not work (yet)
     
     The windows are grouped by wnck class resource name
     This appears to be a somewhat volatile property as some applications 
diff --git a/taskbar-widget.c b/taskbar-widget.c
index 1c85389..2dfcac3 100644
--- a/taskbar-widget.c
+++ b/taskbar-widget.c
@@ -71,6 +71,9 @@ enum
     PROP_SHOW_HANDLE,
 };
 
+
+enum {LEFTMOUSE=1, MIDMOUSE=2, RIGHTMOUSE=3} ;
+
 struct _XfceTaskBarClass
 {
     GtkContainerClass __parent__;
@@ -184,8 +187,19 @@ struct _XfceTaskBarGroup
     gboolean      dragactive ;
     gboolean      pinned ;
     gchar         *command ;
+    guint         popup_timeout ;
+    gulong        enter_signal ;
+    gulong        leave_signal ;
 };
 
+#define DISABLE_HOVER_TIMEOUT(group) if(group->popup_timeout != 0) {g_source_remove(group->popup_timeout); group->popup_timeout=0;}
+
+#define ENABLE_ENTER_SIGNAL(group) { group->enter_signal = g_signal_connect(G_OBJECT(group->button), "enter-notify-event", G_CALLBACK(xfce_taskbar_group_button_enter_event), group); }
+#define DISABLE_ENTER_SIGNAL(group) if(group->enter_signal != 0) {g_signal_handler_disconnect(G_OBJECT(group->button), group->enter_signal); group->enter_signal=0;}
+
+#define ENABLE_EXIT_SIGNAL(group) { group->leave_signal = g_signal_connect(G_OBJECT(group->button),"leave-notify-event", G_CALLBACK(xfce_taskbar_group_button_leave_event), group); }
+#define DISABLE_EXIT_SIGNAL(group) if(group->leave_signal != 0) {g_signal_handler_disconnect(G_OBJECT(group->button), group->leave_signal); group->leave_signal=0;}
+
 static const GtkTargetEntry source_targets[] =
 {
     { "application/x-wnck-window-id", 0, 0 }
@@ -230,10 +244,12 @@ static XfceTaskBarWNode*    xfce_taskbar_wnode_new              (WnckWindow *win
 static void                 xfce_taskbar_wnode_del              (XfceTaskBarWNode *wnode);
 
 /* taskbar group buttons */
-static int      xfce_taskbar_group_visible_count            (XfceTaskBarGroup *group, WnckWorkspace *active_ws);
-static void     xfce_taskbar_group_update_visibility        (XfceTaskBarGroup *group);
-static void     xfce_taskbar_group_button_remove            (XfceTaskBarGroup *group);
-static void     xfce_taskbar_group_button_add_window        (XfceTaskBarGroup *group, XfceTaskBarWNode *window_child);
+static int          xfce_taskbar_group_visible_count            (XfceTaskBarGroup *group, WnckWorkspace *active_ws);
+static void         xfce_taskbar_group_update_visibility        (XfceTaskBarGroup *group);
+static void         xfce_taskbar_group_button_remove            (XfceTaskBarGroup *group);
+static void         xfce_taskbar_group_button_add_window        (XfceTaskBarGroup *group, XfceTaskBarWNode *window_child);
+static gboolean     xfce_taskbar_group_button_enter_event       (GtkWidget *button, GdkEvent *event, XfceTaskBarGroup *group);
+static gboolean     xfce_taskbar_group_button_leave_event       (GtkWidget *button, GdkEvent *event, XfceTaskBarGroup *group);
 
 static XfceTaskBarGroup* 
                 xfce_taskbar_group_button_new               (const char *, XfceTaskBar *taskbar);
@@ -245,8 +261,6 @@ static void     xfce_taskbar_group_button_build_launch_menu (XfceTaskBarGroup *g
 static void     xfce_taskbar_group_button_build_pin_menu    (XfceTaskBarGroup *group, GtkWidget *menu);
 static void     cache_pinned_configuration                  (XfceTaskBar *taskbar);
 
-
-
 /* potential public functions */
 static void xfce_taskbar_set_include_all_workspaces     (XfceTaskBar *taskbar, gboolean all_workspaces);
 static void xfce_taskbar_set_include_all_monitors       (XfceTaskBar *taskbar, gboolean all_monitors);
@@ -1407,7 +1421,7 @@ static void xfce_taskbar_button_icon_changed (WnckWindow *window, XfceTaskBarWNo
         g_object_unref (G_OBJECT (lucent));
 }
 
-static void xfce_taskbar_button_name_changed (WnckWindow *window, XfceTaskBarWNode *child)
+static void xfce_taskbar_button_name_changed(WnckWindow *window, XfceTaskBarWNode *child)
 {
     const gchar *name;
     gchar *label = NULL;
@@ -1562,59 +1576,52 @@ static gboolean xfce_taskbar_button_enter_notify_event (GtkWidget *button, GdkEv
     return FALSE;
 }
 
-//app buttons are the buttons associated with a single instance of a specific application group, exposed by clicking on a group button
-static gboolean xfce_taskbar_app_button_press_event (GtkWidget *button, GdkEventButton *event, XfceTaskBarWNode *child)
+//This function handles mouse release events on the sub-menu of active windows associated with an application group
+//Returning FALSE will close the menu that generated the event, return TRUE will persist it
+static gboolean xfce_taskbar_app_button_release_event (GtkWidget *button, GdkEventButton *event, XfceTaskBarWNode *child)
 {
-    GtkWidget *menu, *panel_plugin;
-
     panel_return_val_if_fail (XFCE_IS_taskbar (child->taskbar), FALSE);
 
-    if (event->type != GDK_BUTTON_PRESS || xfce_taskbar_is_locked (child->taskbar))
+    /* only respond to in-button events */
+    if (
+        ((event->type == GDK_BUTTON_RELEASE &&
+        !xfce_taskbar_is_locked (child->taskbar) &&
+        !(event->x == 0 && event->y == 0) && /* 0,0 = outside the widget in Gtk */
+        event->x >= 0 && event->x < button->allocation.width &&
+        event->y >= 0 && event->y < button->allocation.height))
+        == FALSE
+        )
     {
-        return FALSE;
+        return FALSE ;
     }
-
-    /* send the event to the panel plugin if control is pressed */
-    if (PANEL_HAS_FLAG (event->state, GDK_CONTROL_MASK))
+    
+    //Sanity check the buttons, to be sure to be sure :)
+    if(event->button < LEFTMOUSE || event->button > RIGHTMOUSE)
+        return FALSE ;
+    
+    if(event->button == LEFTMOUSE)
     {
-        /* send the event to the panel plugin */
-        panel_plugin = xfce_taskbar_get_panel_plugin (child->taskbar);
-        if (G_LIKELY (panel_plugin != NULL))
+        xfce_taskbar_button_activate (child, event->time);
+        return FALSE ;
+    }
+    else if(event->button == MIDMOUSE)
+    {
+        if(WNCK_IS_WINDOW (child->window) == TRUE)
         {
-            gtk_widget_event (panel_plugin, (GdkEvent *) event);
+            wnck_window_close (child->window, gtk_get_current_event_time ());
+            return FALSE ;
         }
-
-        return TRUE;
     }
-
-    if (event->button == 3)
+    else// if(event->button == 3)
     {
+        GtkWidget *menu ;
         menu = wnck_action_menu_new (child->window); g_signal_connect (G_OBJECT (menu), "selection-done", G_CALLBACK (gtk_widget_destroy), NULL);
         gtk_menu_attach_to_widget (GTK_MENU (menu), button, NULL);
         gtk_menu_popup (GTK_MENU (menu), NULL, NULL, xfce_panel_plugin_position_menu, xfce_taskbar_get_panel_plugin (child->taskbar), event->button, event->time);
-        return TRUE;
+        return TRUE ;
     }
-
-    return FALSE;
-}
-
-//app buttons are the buttons associated with a single instance of a specific application group, exposed by clicking on a group button
-static gboolean xfce_taskbar_app_button_release_event (GtkWidget *button, GdkEventButton *event, XfceTaskBarWNode *child)
-{
-    panel_return_val_if_fail (XFCE_IS_taskbar (child->taskbar), FALSE);
-
-    /* only respond to in-button events */
-    if (event->type == GDK_BUTTON_RELEASE
-            && !xfce_taskbar_is_locked (child->taskbar)
-            && event->button == 1
-            && !(event->x == 0 && event->y == 0) /* 0,0 = outside the widget in Gtk */
-            && event->x >= 0 && event->x < button->allocation.width
-            && event->y >= 0 && event->y < button->allocation.height)
-        {
-            xfce_taskbar_button_activate (child, event->time);
-        }
-
-    return FALSE;
+    
+    return FALSE ;
 }
 
 static void xfce_taskbar_button_enter_notify_event_disconnected (gpointer data, GClosure *closure)
@@ -1667,25 +1674,22 @@ static GtkWidget *xfce_taskbar_button_proxy_menu_item (XfceTaskBarWNode *child,
                 G_CALLBACK (xfce_taskbar_button_enter_notify_event), child,
                 xfce_taskbar_button_enter_notify_event_disconnected, 0);
     }
-
-    g_signal_connect (G_OBJECT (mi), "button-press-event", G_CALLBACK (xfce_taskbar_app_button_press_event), child);
+    
     g_signal_connect (G_OBJECT (mi), "button-release-event", G_CALLBACK (xfce_taskbar_app_button_release_event), child);
-
-    /* TODO item dnd */
-
+    
     return mi;
 }
 
 void xfce_taskbar_selgrp_cmd(XfceTaskBar *taskbar, char id) {
-	XfceTaskBarGroup *group = g_list_nth_data(taskbar->wgroups, id-1);
-	if(!group)
-		return;
-	XfceTaskBarWNode *first = g_slist_nth_data(group->wnodes, 0);
-	if(!first) {
-		xfce_taskbar_group_button_launch_pinned(group);
-	} else if (!wnck_window_is_active(first->window)) {
-		xfce_taskbar_button_activate(first, 0);
-	}
+    XfceTaskBarGroup *group = g_list_nth_data(taskbar->wgroups, id-1);
+    if(!group)
+        return;
+    XfceTaskBarWNode *first = g_slist_nth_data(group->wnodes, 0);
+    if(!first) {
+        xfce_taskbar_group_button_launch_pinned(group);
+    } else if (!wnck_window_is_active(first->window)) {
+        xfce_taskbar_button_activate(first, 0);
+    }
 }
 
 static void xfce_taskbar_button_activate (XfceTaskBarWNode *wnode, guint32 timestamp)
@@ -1707,39 +1711,39 @@ static void xfce_taskbar_button_activate (XfceTaskBarWNode *wnode, guint32 times
     }
     else
     {
-        /* we only change worksapces/viewports for non-pinned windows
-         * and if all workspaces/viewports are shown */
+        // we only change worksapces/viewports for non-pinned windows
+        // and if all workspaces/viewports are shown
         if (wnode->taskbar->all_workspaces && !wnck_window_is_pinned (wnode->window))
         {
             workspace = wnck_window_get_workspace (wnode->window);
-            /* only switch workspaces/viewports if switch_workspace is enabled or
-             * we want to restore a minimized window to the current workspace/viewport */
+            // only switch workspaces/viewports if switch_workspace is enabled or
+            // we want to restore a minimized window to the current workspace/viewport
             if (workspace != NULL && (wnode->taskbar->switch_workspace || !wnck_window_is_minimized (wnode->window)))
             {
                 if (G_UNLIKELY (wnck_workspace_is_virtual (workspace)))
                 {
                     if (!wnck_window_is_in_viewport (wnode->window, workspace))
                     {
-                        /* viewport info */
+                        // viewport info
                         workspace_width = wnck_workspace_get_width (workspace);
                         workspace_height = wnck_workspace_get_height (workspace);
                         screen_width = wnck_screen_get_width (wnode->taskbar->screen);
                         screen_height = wnck_screen_get_height (wnode->taskbar->screen);
 
-                        /* we only support multiple viewports like compiz has
-                        * (all equally spread across the screen) */
+                        // we only support multiple viewports like compiz has
+                        // (all equally spread across the screen)
                         if ((workspace_width % screen_width) == 0 && (workspace_height % screen_height) == 0)
                         {
                            wnck_window_get_geometry (wnode->window, &window_x, &window_y, NULL, NULL);
 
-                           /* lookup nearest workspace edge */
+                           // lookup nearest workspace edge
                            viewport_x = window_x - (window_x % screen_width);
                            viewport_x = CLAMP (viewport_x, 0, workspace_width - screen_width);
 
                            viewport_y = window_y - (window_y % screen_height);
                            viewport_y = CLAMP (viewport_y, 0, workspace_height - screen_height);
 
-                           /* move to the other viewport */
+                           // move to the other viewport
                            wnck_screen_move_viewport (wnode->taskbar->screen, viewport_x, viewport_y);
                         }
                         else
@@ -1750,35 +1754,35 @@ static void xfce_taskbar_button_activate (XfceTaskBarWNode *wnode, guint32 times
                 }
                 else if (wnck_screen_get_active_workspace (wnode->taskbar->screen) != workspace)
                 {
-                    /* switch to the other workspace before we activate the window */
+                    // switch to the other workspace before we activate the window
                     wnck_workspace_activate (workspace, timestamp - 1);
                 }
             }
             else if (workspace != NULL && wnck_workspace_is_virtual (workspace) && !wnck_window_is_in_viewport (wnode->window, workspace))
             {
-                /* viewport info */
+                // viewport info
                 workspace_width = wnck_workspace_get_width (workspace);
                 workspace_height = wnck_workspace_get_height (workspace);
                 screen_width = wnck_screen_get_width (wnode->taskbar->screen);
                 screen_height = wnck_screen_get_height (wnode->taskbar->screen);
 
-                /* we only support multiple viewports like compiz has
-                 * (all equaly spread across the screen) */
+                // we only support multiple viewports like compiz has
+                // (all equaly spread across the screen)
                 if ((workspace_width % screen_width) == 0 && (workspace_height % screen_height) == 0)
                 {
                    viewport_x = wnck_workspace_get_viewport_x (workspace);
                    viewport_y = wnck_workspace_get_viewport_y (workspace);
 
-                   /* note that the x and y might be negative numbers, since they are relative
-                    * to the current screen, not to the edge of the screen they are on. this is
-                    * not a problem since the mod result will always be positive */
+                   // note that the x and y might be negative numbers, since they are relative
+                   // to the current screen, not to the edge of the screen they are on. this is
+                   // not a problem since the mod result will always be positive
                    wnck_window_get_geometry (wnode->window, &window_x, &window_y, NULL, NULL);
 
-                   /* get the new screen position, with the same screen offset */
+                   // get the new screen position, with the same screen offset
                    window_x = viewport_x + (window_x % screen_width);
                    window_y = viewport_y + (window_y % screen_height);
 
-                   /* move the window */
+                   // move the window
                    wnck_window_set_geometry (wnode->window, WNCK_WINDOW_GRAVITY_CURRENT, WNCK_WINDOW_CHANGE_X | WNCK_WINDOW_CHANGE_Y, window_x, window_y, -1, -1);
                 }
                 else
@@ -1798,7 +1802,7 @@ static void xfce_taskbar_button_drag_data_get(GtkWidget *button, GdkDragContext
 
 static void xfce_taskbar_button_drag_begin (GtkWidget *button, GdkDragContext *context, XfceTaskBarGroup *group)
 {
-    // Hacky workaround for DnD crashes
+    // Gruesomely hacky workaround for DnD crashes :(
     usleep(20000);
     group->dragactive = TRUE ;
     gtk_drag_set_icon_pixmap(context, gtk_widget_get_colormap(group->icon), gtk_widget_get_snapshot(group->icon, NULL), NULL, 0, 0);
@@ -1871,14 +1875,14 @@ static XfceTaskBarWNode * xfce_taskbar_wnode_new (WnckWindow *window, XfceTaskBa
     wnode->group_name = g_strdup(wnck_application_get_name(wnck_window_get_application (window)));
     wnode->visible = xfce_taskbar_button_visible(wnode, active_ws);
     
-    /* monitor window changes */
+    // monitor window changes
     g_signal_connect (G_OBJECT (window), "icon-changed", G_CALLBACK (xfce_taskbar_button_icon_changed), wnode);
     g_signal_connect (G_OBJECT (window), "name-changed", G_CALLBACK (xfce_taskbar_button_name_changed), wnode);
     g_signal_connect (G_OBJECT (window), "state-changed", G_CALLBACK (xfce_taskbar_button_state_changed), wnode);
     g_signal_connect (G_OBJECT (window), "workspace-changed", G_CALLBACK (xfce_taskbar_button_workspace_changed), wnode);
     g_signal_connect (G_OBJECT (window), "geometry-changed", G_CALLBACK (xfce_taskbar_button_geometry_changed2), wnode);
 
-    /* poke functions */
+    // poke functions
     xfce_taskbar_button_icon_changed (window, wnode);
     xfce_taskbar_button_name_changed (NULL, wnode);
 
@@ -2006,14 +2010,12 @@ static GtkWidget * xfce_taskbar_group_button_menu_launcher(XfceTaskBarGroup *gro
 {
     GtkWidget *menu;
     GtkWidget *mi;
-    
     menu = gtk_menu_new ();
-    
     xfce_taskbar_group_button_build_launch_menu(group, menu, FALSE);
-    
     return menu ;
 }
 
+// Helper function for building the list of windows associated with a group icon
 static GtkWidget * xfce_taskbar_group_button_menu_show_active(XfceTaskBarGroup *group)
 {
     GSList *li;
@@ -2038,7 +2040,7 @@ static GtkWidget * xfce_taskbar_group_button_menu_show_active(XfceTaskBarGroup *
     return menu ;
 }
 
-/*** Copied most of this code from the verve plugin ***/
+// Copied most of this code from the verve plugin ;]
 static void xfce_taskbar_group_button_launch_pinned (XfceTaskBarGroup *group)
 {
     gint         argc;
@@ -2050,10 +2052,10 @@ static void xfce_taskbar_group_button_launch_pinned (XfceTaskBarGroup *group)
 
     panel_return_if_fail((group->pinned == TRUE));
 
-    /* Parse command line arguments */
+    // Parse command line arguments
     success = g_shell_parse_argv (group->command, &argc, &argv, &error);
 
-    /* Return false if command line arguments failed to be arsed */
+    // Return false if command line arguments failed to be parsed
     if (G_UNLIKELY (error != NULL))
     {
         xfce_dialog_show_error (NULL, error, _("Failed to parse the command \"%s\""), group->command);
@@ -2061,15 +2063,15 @@ static void xfce_taskbar_group_button_launch_pinned (XfceTaskBarGroup *group)
         return ;
     }
 
-    /* Get user's home directory */
+    // Get user's home directory
     home_dir = xfce_get_homedir ();
 
-    /* Set up spawn flags */
+    // Set up spawn flags
     flags = G_SPAWN_STDOUT_TO_DEV_NULL;
     flags |= G_SPAWN_STDERR_TO_DEV_NULL;
     flags |= G_SPAWN_SEARCH_PATH;
 
-    /* Spawn subprocess */
+    // Spawn subprocess
     success = g_spawn_async (home_dir, argv, NULL, flags, NULL, NULL, NULL, &error);
     g_strfreev (argv);
     if(!success)
@@ -2079,8 +2081,6 @@ static void xfce_taskbar_group_button_launch_pinned (XfceTaskBarGroup *group)
         return ;
     }
     
-    
-    
 }
 
 static void xfce_taskbar_group_button_toggle_pinned (XfceTaskBarGroup *group)
@@ -2132,64 +2132,38 @@ static void xfce_taskbar_group_button_toggle_pinned (XfceTaskBarGroup *group)
         }
     }
     
-    /* Cache the icon and path data for the pinned items */
+    // Cache the icon and path data for the pinned items
     xfce_taskbar_save_pinned_config (group->taskbar);
 }
 
-/* Create the wnck_action_menu first, then *prepend* the pinning functions to the menu */
-static GtkWidget * xfce_taskbar_group_button_menu_single_actions (XfceTaskBarGroup *group, XfceTaskBarWNode *wnode)
-{
-    GtkWidget *menu ;
-    GtkWidget *mi;
-    
-    menu = wnck_action_menu_new (wnode->window) ;
-    //Only offer to pin windows with detectable pids
-    if(wnck_window_get_pid(wnode->window) != 0)
-    {
-        if(group->pinned == TRUE)
-        {
-            xfce_taskbar_group_button_build_launch_menu(group, menu, TRUE);
-        }
-        else
-        {
-            xfce_taskbar_group_button_build_pin_menu(group, menu);
-        }
-    }
-    return menu ;
-}
-
-/* Create the pinning functions first, then *append* the group actions to the menu */
+// Create the pinning functions first, then *append* the group actions to the menu
 static GtkWidget * xfce_taskbar_group_button_menu_group_actions (XfceTaskBarGroup *group)
 {
     GSList *li;
-    XfceTaskBarWNode *wnode;
     GtkWidget *mi;
     GtkWidget *menu;
     GtkWidget *image;
+    XfceTaskBarWNode *wnode;
     gint visibility_count = 0 ;
-
+    
     panel_return_val_if_fail (XFCE_IS_taskbar (group->taskbar), NULL);
     
     menu = gtk_menu_new ();
     
+    if(group->pinned == TRUE)
+    {
+        xfce_taskbar_group_button_build_launch_menu(group, menu, TRUE);
+    }
     //Only offer to pin windows with detectable pids
-    if(group->wnodes && wnck_window_get_pid(((XfceTaskBarWNode *)group->wnodes->data)->window) != 0)
+    else if(group->wnodes && wnck_window_get_pid(((XfceTaskBarWNode *)group->wnodes->data)->window) != 0)
     {
-        if(group->pinned == TRUE)
-        {
-            xfce_taskbar_group_button_build_launch_menu(group, menu, TRUE);
-        }
-        else
-        {
-            xfce_taskbar_group_button_build_pin_menu(group, menu);
-        }
-        
+    
+        xfce_taskbar_group_button_build_pin_menu(group, menu);
     }
     
     for (li = group->wnodes; li != NULL; li = li->next)
     {
         wnode = li->data;
-        
         if(wnode->visible)
         {
             visibility_count += 1 ;
@@ -2238,7 +2212,7 @@ static GtkWidget * xfce_taskbar_group_button_menu_group_actions (XfceTaskBarGrou
     gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
     g_signal_connect_swapped (G_OBJECT (mi), "activate", G_CALLBACK (xfce_taskbar_group_button_menu_close_all), group);
     gtk_widget_show (mi);
-
+    
     image = gtk_image_new_from_stock ("wnck-stock-delete", GTK_ICON_SIZE_MENU);
     gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mi), image);
     gtk_widget_show (image);
@@ -2246,7 +2220,7 @@ static GtkWidget * xfce_taskbar_group_button_menu_group_actions (XfceTaskBarGrou
     return menu;
 }
 
-static void xfce_taskbar_group_button_menu_destroy (GtkWidget *menu, XfceTaskBarGroup *group)
+static void xfce_taskbar_group_button_menu_destroy(GtkWidget *menu, XfceTaskBarGroup *group)
 {
     panel_return_if_fail (XFCE_IS_taskbar (group->taskbar));
     panel_return_if_fail (GTK_IS_TOGGLE_BUTTON (group->button));
@@ -2262,17 +2236,22 @@ static void xfce_taskbar_group_button_menu_destroy (GtkWidget *menu, XfceTaskBar
 #endif
 }
 
+// This function handles all the logic for mouse button relase on the taskbar icons
 static gboolean xfce_taskbar_group_button_release_event
 (GtkWidget *button, GdkEventButton *event, XfceTaskBarGroup *group)
 {
     GtkWidget *panel_plugin;
     GtkWidget *menu;
 
+    //Disable the timeout, if active
+    DISABLE_HOVER_TIMEOUT(group);
+    
     panel_return_val_if_fail (XFCE_IS_taskbar (group->taskbar), FALSE);
 
     if (event->type != GDK_BUTTON_RELEASE || xfce_taskbar_is_locked (group->taskbar))
         return FALSE;
 
+    //Some hacky stuff for drag and drop
     if(group->dragactive == TRUE)
     {
         group->dragactive = FALSE ;
@@ -2290,10 +2269,17 @@ static gboolean xfce_taskbar_group_button_release_event
         }
         return TRUE;
     }
-
-    if (event->button == 1 || event->button == 3)
+    
+    //Middle mouse click on the taskbar icon launchs the icon
+    if(event->button == MIDMOUSE)
+    {
+        xfce_taskbar_group_button_launch_pinned(group);
+        return TRUE ;
+    }
+    
+    //This is trying to do too much, need to prune it
+    if (event->button == LEFTMOUSE || event->button == RIGHTMOUSE)
     {
-
         GSList *li;
         XfceTaskBarWNode *child, *active_child=0x0 ;
         int visible_count=0x0 ;
@@ -2316,7 +2302,7 @@ static gboolean xfce_taskbar_group_button_release_event
             activate that instance instead of showing the associated group menus,
             when a left mouse click falls on the icon
         */
-        if(active_child && event->button == 1)
+        if(active_child && event->button == LEFTMOUSE)
         {
             xfce_taskbar_button_activate (active_child, event->time);
             return TRUE ;
@@ -2326,7 +2312,7 @@ static gboolean xfce_taskbar_group_button_release_event
         {
             panel_return_val_if_fail ((group->pinned == TRUE), FALSE);
             
-            if(event->button == 1)
+            if(event->button == LEFTMOUSE)
             {
                 xfce_taskbar_group_button_launch_pinned(group);
                 return TRUE ;
@@ -2339,38 +2325,149 @@ static gboolean xfce_taskbar_group_button_release_event
         else
         {
             /* Split the particular menu use cases into seperate functions*/
-            if(event->button == 1)
+            if(event->button == LEFTMOUSE)
             {
                 menu = xfce_taskbar_group_button_menu_show_active (group);
             }
             else
             {
-                if(active_child)
-                {
-                    menu = xfce_taskbar_group_button_menu_single_actions (group,active_child);
-                }
-                else
-                {
-                    menu = xfce_taskbar_group_button_menu_group_actions (group);
-                }
+                menu = xfce_taskbar_group_button_menu_group_actions (group);
             }
         }
         
         g_signal_connect (G_OBJECT (menu), "selection-done", G_CALLBACK (xfce_taskbar_group_button_menu_destroy), group);
-
         gtk_menu_attach_to_widget (GTK_MENU (menu), button, NULL);
         gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
-           xfce_panel_plugin_position_menu,
-           xfce_taskbar_get_panel_plugin (group->taskbar),
-           event->button,
-           event->time);
-
+                        xfce_panel_plugin_position_menu,
+                        xfce_taskbar_get_panel_plugin(group->taskbar),
+                        event->button,
+                        event->time);
+        
         return TRUE;
     }
 
     return FALSE;
 }
 
+static gboolean trigger_hover_menu_timeout(GtkWidget *widget, GdkEvent  *event, gpointer menu_ptr);
+
+//This callback gets triggered if the mouse has left the group icon but has not entered the hover menu
+static gboolean xfce_taskbar_hover_menu_timeout(gpointer menu_ptr)
+{
+    GtkWidget *menu_widget = (GtkWidget *)menu_ptr;
+    
+    if((GTK_MENU_SHELL (menu_widget))->active == FALSE)
+    {
+        GtkWidget *button_widget = gtk_menu_get_attach_widget(GTK_MENU(menu_widget));
+        g_signal_handlers_disconnect_by_func(button_widget, trigger_hover_menu_timeout, menu_ptr);
+        gtk_widget_destroy (menu_widget);
+    }
+    
+    return FALSE ;
+}
+
+//Triggered when mouse focus enters the hover menu
+static gboolean hover_menu_enter(GtkWidget *widget, GdkEvent  *event, gpointer menu_ptr)
+{
+    void *counter;
+    GtkWidget *menu_widget = (GtkWidget *)menu_ptr;
+    
+    //We leave the menu shell in an inactive state until the user transfers mouse focus to it
+    //When mouse focus is recv'd, we disanle the pending timeout and activate the hover menu shell
+    if((GTK_MENU_SHELL (menu_widget))->active == FALSE)
+    {
+        GtkWidget *button_widget = gtk_menu_get_attach_widget(GTK_MENU(menu_widget));
+        g_signal_handlers_disconnect_by_func(button_widget, trigger_hover_menu_timeout, menu_ptr);
+        (GTK_MENU_SHELL (menu_widget))->active = TRUE;
+    }
+    
+    return FALSE ;
+}
+
+//Triggered when mouse focus leaves the hover menu
+static gboolean hover_menu_leave(GtkWidget *widget, GdkEvent  *event, gpointer menu_ptr)
+{
+    void *counter;
+    GtkWidget *menu_widget = (GtkWidget *)menu_ptr;
+    gtk_widget_destroy (menu_widget);
+    
+    return FALSE ;
+}
+
+//When the mouse moves away from the group icon, we trigger a timeout, during which the hover menu will remain
+//if the user moves into the hover menu scope, we disable the timeout, otherwise the timeout triggers
+//and removes the hover menu
+static gboolean trigger_hover_menu_timeout(GtkWidget *widget, GdkEvent  *event, gpointer menu_ptr)
+{
+    GtkWidget *menu_widget = (GtkWidget *)menu_ptr;
+    
+    //We don't want to kill the hover menu immediately, so we wait 100ms
+    g_timeout_add(100, xfce_taskbar_hover_menu_timeout, menu_widget);
+    
+    g_signal_connect (GTK_WINDOW (GTK_MENU(menu_widget)->toplevel), "enter-notify-event", G_CALLBACK (hover_menu_enter), menu_widget);
+    g_signal_connect (GTK_WINDOW (GTK_MENU(menu_widget)->toplevel), "leave-notify-event", G_CALLBACK (hover_menu_leave), menu_widget);
+    
+    return FALSE ;
+}
+
+//Triggered when the mouse has hovered over the group icon for duration of time
+static gboolean xfce_taskbar_group_button_hover_timeout(gpointer group_ptr)
+{
+    GtkWidget *menu_widget;
+    XfceTaskBarGroup *group = (XfceTaskBarGroup *)group_ptr ;
+    
+    //printf("xfce_taskbar_group_button_hover_timeout %s\n", group->window_class_name);
+    
+    //Disable the hover timeout
+    DISABLE_HOVER_TIMEOUT(group);
+    
+    //Create and raise the menu
+    menu_widget = xfce_taskbar_group_button_menu_show_active (group);
+    //Attach the menu widget to the button widget
+    gtk_menu_attach_to_widget (GTK_MENU (menu_widget), group->button, NULL);
+    
+    g_signal_connect (G_OBJECT (menu_widget), "selection-done", G_CALLBACK (xfce_taskbar_group_button_menu_destroy), group);
+    g_signal_connect_after (G_OBJECT (group->button), "leave-notify-event", G_CALLBACK (trigger_hover_menu_timeout), menu_widget);
+    
+    {
+        gint x, y ;
+        gboolean push_in=TRUE;
+        GtkMenu *menu = GTK_MENU(menu_widget);
+        GtkWindow *menu_window = GTK_WINDOW (menu->toplevel);
+        
+        xfce_panel_plugin_position_menu(menu, &x, &y, &push_in, xfce_taskbar_get_panel_plugin(group->taskbar));
+        
+        menu->position_func = xfce_panel_plugin_position_menu;
+        menu->position_func_data = xfce_taskbar_get_panel_plugin(group->taskbar);
+        
+        gtk_widget_show (menu->toplevel);
+        gtk_window_move(menu_window, x, y);
+        gtk_widget_show (menu_widget);
+    }
+    
+    return FALSE ;
+}
+
+//Triggered when the mouse exits the group button icon
+static gboolean xfce_taskbar_group_button_leave_event(GtkWidget *button, GdkEvent *event, XfceTaskBarGroup *group)
+{
+    //printf("xfce_taskbar_group_button_leave_event\n");
+    DISABLE_HOVER_TIMEOUT(group);
+    return FALSE ;
+}
+
+//Triggered when the mouse enters the group button icon
+static gboolean xfce_taskbar_group_button_enter_event(GtkWidget *button, GdkEvent *event, XfceTaskBarGroup *group)
+{
+    //printf("xfce_taskbar_group_button_enter_event\n");
+    if(xfce_taskbar_group_visible_count(group, wnck_screen_get_active_workspace (group->taskbar->screen)) > 0)
+    {
+        group->popup_timeout = g_timeout_add(250, xfce_taskbar_group_button_hover_timeout, group);
+    }
+    
+    return TRUE ;
+}
+
 static int xfce_taskbar_group_visible_count(XfceTaskBarGroup *group, WnckWorkspace *active_ws)
 {
     GSList *wi ;
@@ -2494,6 +2591,12 @@ static XfceTaskBarGroup * xfce_taskbar_group_button_new (const char *group_name,
     group->pinned = FALSE;
     group->command = NULL ;
     
+    //The timeout id, used when tracking a mouse hovering over the button
+    group->popup_timeout = 0 ;
+    //Signals ids for quickly enabling and disabling mouse hover events
+    group->enter_signal = 0 ;
+    group->leave_signal = 0 ;
+    
     group->button = xfce_arrow_button_new (GTK_ARROW_NONE);
     gtk_widget_set_parent (group->button, GTK_WIDGET (taskbar));
     gtk_widget_set_tooltip_text (group->button, group_name);
@@ -2502,11 +2605,10 @@ static XfceTaskBarGroup * xfce_taskbar_group_button_new (const char *group_name,
     gtk_container_add( GTK_CONTAINER(group->align), group->icon);
     gtk_container_add (GTK_CONTAINER (group->button), group->align);
     
-    //gtk_button_set_image((GtkButton *)group->button, group->icon);
-    
-    /* note that the same signals should be in the proxy menu item too */
     g_signal_connect (G_OBJECT (group->button), "button-release-event", G_CALLBACK (xfce_taskbar_group_button_release_event), group);
-
+    ENABLE_ENTER_SIGNAL(group);
+    ENABLE_EXIT_SIGNAL(group);
+    
     /* insert */
     taskbar->wgroups = g_list_append(taskbar->wgroups, group);
     
@@ -2547,8 +2649,6 @@ static void xfce_taskbar_set_include_all_workspaces (XfceTaskBar *taskbar, gbool
 
 }
 
-
-
 static void xfce_taskbar_set_include_all_monitors (XfceTaskBar *taskbar, gboolean all_monitors)
 {
     panel_return_if_fail (XFCE_IS_taskbar (taskbar));


More information about the Xfce4-commits mailing list