diff options
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | src/app-menu-item.c | 207 | ||||
-rw-r--r-- | src/app-menu-item.h | 7 | ||||
-rw-r--r-- | src/messages-service.c | 65 |
4 files changed, 276 insertions, 5 deletions
diff --git a/configure.ac b/configure.ac index 9a906ec..0932e1d 100644 --- a/configure.ac +++ b/configure.ac @@ -31,7 +31,7 @@ GIO_UNIX_REQUIRED_VERSION=2.18 PANEL_REQUIRED_VERSION=2.0.0 INDICATE_REQUIRED_VERSION=0.3.0 INDICATOR_REQUIRED_VERSION=0.3.3 -DBUSMENUGTK_REQUIRED_VERSION=0.2.2 +DBUSMENUGTK_REQUIRED_VERSION=0.2.5 PKG_CHECK_MODULES(APPLET, gtk+-2.0 >= $GTK_REQUIRED_VERSION gio-unix-2.0 >= $GIO_UNIX_REQUIRED_VERSION diff --git a/src/app-menu-item.c b/src/app-menu-item.c index 5fc2a9c..2fb24f6 100644 --- a/src/app-menu-item.c +++ b/src/app-menu-item.c @@ -26,12 +26,15 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #include <glib/gi18n.h> #include <gio/gdesktopappinfo.h> +#include <libdbusmenu-glib/client.h> +#include <libdbusmenu-glib/menuitem-proxy.h> #include "app-menu-item.h" #include "dbus-data.h" enum { COUNT_CHANGED, NAME_CHANGED, + SHORTCUTS_CHANGED, LAST_SIGNAL }; @@ -48,6 +51,10 @@ struct _AppMenuItemPrivate GAppInfo * appinfo; gchar * desktop; guint unreadcount; + + DbusmenuClient * client; + DbusmenuMenuitem * root; + GList * shortcuts; }; #define APP_MENU_ITEM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), APP_MENU_ITEM_TYPE, AppMenuItemPrivate)) @@ -60,6 +67,7 @@ static void app_menu_item_finalize (GObject *object); static void activate_cb (AppMenuItem * self, guint timestamp, gpointer data); static void count_changed (IndicateListener * listener, IndicateListenerServer * server, guint count, gpointer data); static void count_cb (IndicateListener * listener, IndicateListenerServer * server, guint value, gpointer data); +static void menu_cb (IndicateListener * listener, IndicateListenerServer * server, gchar * menupath, gpointer data); static void desktop_cb (IndicateListener * listener, IndicateListenerServer * server, gchar * value, gpointer data); static void update_label (AppMenuItem * self); @@ -90,6 +98,13 @@ app_menu_item_class_init (AppMenuItemClass *klass) NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); + signals[SHORTCUTS_CHANGED] = g_signal_new(APP_MENU_ITEM_SIGNAL_SHORTCUTS_CHANGED, + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (AppMenuItemClass, shortcuts_changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0, G_TYPE_NONE); return; } @@ -107,6 +122,18 @@ app_menu_item_init (AppMenuItem *self) priv->desktop = NULL; priv->unreadcount = 0; + priv->client = NULL; + priv->root = NULL; + priv->shortcuts = NULL; + + return; +} + +/* A wrapper to make the prototypes work for GFunc */ +static void +func_unref (gpointer data, gpointer user_data) +{ + g_object_unref(G_OBJECT(data)); return; } @@ -117,8 +144,28 @@ app_menu_item_dispose (GObject *object) AppMenuItem * self = APP_MENU_ITEM(object); AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(self); - g_signal_handlers_disconnect_by_func(G_OBJECT(priv->listener), count_changed, self); - g_object_unref(priv->listener); + if (priv->listener != NULL) { + g_signal_handlers_disconnect_by_func(G_OBJECT(priv->listener), count_changed, self); + g_object_unref(priv->listener); + priv->listener = NULL; + } + + if (priv->shortcuts != NULL) { + g_list_foreach(priv->shortcuts, func_unref, NULL); + g_list_free(priv->shortcuts); + priv->shortcuts = NULL; + g_signal_emit(object, signals[SHORTCUTS_CHANGED], 0, TRUE); + } + + if (priv->root != NULL) { + g_object_unref(priv->root); + priv->root = NULL; + } + + if (priv->client != NULL) { + g_object_unref(priv->client); + priv->client = NULL; + } G_OBJECT_CLASS (app_menu_item_parent_class)->dispose (object); } @@ -168,6 +215,7 @@ app_menu_item_new (IndicateListener * listener, IndicateListenerServer * server) /* Get the values we care about from the server */ indicate_listener_server_get_desktop(listener, server, desktop_cb, self); indicate_listener_server_get_count(listener, server, count_cb, self); + indicate_listener_server_get_menu(listener, server, menu_cb, self); g_signal_connect(G_OBJECT(self), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(activate_cb), NULL); @@ -206,6 +254,7 @@ update_label (AppMenuItem * self) static void count_changed (IndicateListener * listener, IndicateListenerServer * server, guint count, gpointer data) { + g_return_if_fail(IS_APP_MENU_ITEM(data)); AppMenuItem * self = APP_MENU_ITEM(data); AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(self); @@ -234,6 +283,7 @@ count_cb (IndicateListener * listener, IndicateListenerServer * server, guint va static void desktop_cb (IndicateListener * listener, IndicateListenerServer * server, gchar * value, gpointer data) { + g_return_if_fail(IS_APP_MENU_ITEM(data)); AppMenuItem * self = APP_MENU_ITEM(data); AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(self); @@ -262,6 +312,149 @@ desktop_cb (IndicateListener * listener, IndicateListenerServer * server, gchar return; } +/* Relay this signal into causing a rebuild of the shortcuts + from those above us. */ +static void +child_added_cb (DbusmenuMenuitem * root, DbusmenuMenuitem * child, guint position, gpointer data) +{ + g_return_if_fail(IS_APP_MENU_ITEM(data)); + AppMenuItem * self = APP_MENU_ITEM(data); + AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(self); + DbusmenuMenuitemProxy * mip = dbusmenu_menuitem_proxy_new(child); + + priv->shortcuts = g_list_insert(priv->shortcuts, mip, position); + + g_signal_emit(G_OBJECT(data), signals[SHORTCUTS_CHANGED], 0, TRUE); + return; +} + +/* Relay this signal into causing a rebuild of the shortcuts + from those above us. */ +static void +child_removed_cb (DbusmenuMenuitem * root, DbusmenuMenuitem * child, gpointer data) +{ + g_return_if_fail(IS_APP_MENU_ITEM(data)); + AppMenuItem * self = APP_MENU_ITEM(data); + AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(self); + + GList * pitems = priv->shortcuts; + while (pitems != NULL) { + DbusmenuMenuitemProxy * mip = DBUSMENU_MENUITEM_PROXY(pitems->data); + + if (dbusmenu_menuitem_proxy_get_wrapped(mip) == child) { + break; + } + + pitems = g_list_next(pitems); + } + + if (pitems != NULL) { + DbusmenuMenuitemProxy * mip = DBUSMENU_MENUITEM_PROXY(pitems->data); + g_object_unref(mip); + priv->shortcuts = g_list_remove(priv->shortcuts, mip); + + g_signal_emit(G_OBJECT(data), signals[SHORTCUTS_CHANGED], 0, TRUE); + } + + return; +} + +/* Relay this signal into causing a rebuild of the shortcuts + from those above us. */ +static void +child_moved_cb (DbusmenuMenuitem * root, DbusmenuMenuitem * child, guint newpos, guint oldpos, gpointer data) +{ + g_return_if_fail(IS_APP_MENU_ITEM(data)); + AppMenuItem * self = APP_MENU_ITEM(data); + AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(self); + + DbusmenuMenuitemProxy * mip = DBUSMENU_MENUITEM_PROXY(g_list_nth_data(priv->shortcuts, oldpos)); + + if (mip != NULL) { + if (dbusmenu_menuitem_proxy_get_wrapped(mip) != child) { + mip = NULL; + } + } + + if (mip != NULL) { + priv->shortcuts = g_list_remove(priv->shortcuts, mip); + priv->shortcuts = g_list_insert(priv->shortcuts, mip, newpos); + g_signal_emit(G_OBJECT(data), signals[SHORTCUTS_CHANGED], 0, TRUE); + } + + return; +} + +/* We've got a new root. We need to proxy it and handle it's children + if that's a relevant thing to do. */ +static void +root_changed (DbusmenuClient * client, DbusmenuMenuitem * newroot, gpointer data) +{ + g_debug("Root Changed"); + AppMenuItem * self = APP_MENU_ITEM(data); + AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(self); + gboolean change_time = FALSE; + + if (priv->root != NULL) { + if (dbusmenu_menuitem_get_children(DBUSMENU_MENUITEM(priv->root)) != NULL) { + change_time = TRUE; + g_list_foreach(priv->shortcuts, func_unref, NULL); + g_list_free(priv->shortcuts); + priv->shortcuts = NULL; + } + g_object_unref(priv->root); + priv->root = NULL; + } + + /* We need to proxy the new root across to the old + world of indicator land. */ + priv->root = newroot; + g_object_ref(priv->root); + g_signal_connect(G_OBJECT(priv->root), DBUSMENU_MENUITEM_SIGNAL_CHILD_ADDED, G_CALLBACK(child_added_cb), self); + g_signal_connect(G_OBJECT(priv->root), DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED, G_CALLBACK(child_removed_cb), self); + g_signal_connect(G_OBJECT(priv->root), DBUSMENU_MENUITEM_SIGNAL_CHILD_MOVED, G_CALLBACK(child_moved_cb), self); + + /* See if we have any menuitems to worry about, + otherwise we'll just move along. */ + GList * children = dbusmenu_menuitem_get_children(DBUSMENU_MENUITEM(priv->root)); + if (children != NULL) { + change_time = TRUE; + g_debug("\tProcessing %d children", g_list_length(children)); + while (children != NULL) { + DbusmenuMenuitemProxy * mip = dbusmenu_menuitem_proxy_new(DBUSMENU_MENUITEM(children->data)); + priv->shortcuts = g_list_append(priv->shortcuts, mip); + children = g_list_next(children); + } + } + + if (change_time) { + g_signal_emit(G_OBJECT(self), signals[SHORTCUTS_CHANGED], 0, TRUE); + } + + return; +} + +/* Gets the path to menuitems if there are some. Now we need to + make them special. */ +static void +menu_cb (IndicateListener * listener, IndicateListenerServer * server, gchar * menupath, gpointer data) +{ + g_debug("Got Menu: %s", menupath); + g_return_if_fail(IS_APP_MENU_ITEM(data)); + AppMenuItem * self = APP_MENU_ITEM(data); + AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(self); + + priv->client = dbusmenu_client_new(indicate_listener_server_get_dbusname(server), menupath); + g_signal_connect(G_OBJECT(priv->client), DBUSMENU_CLIENT_SIGNAL_ROOT_CHANGED, G_CALLBACK(root_changed), self); + + DbusmenuMenuitem * root = dbusmenu_client_get_root(priv->client); + if (root != NULL) { + root_changed(priv->client, root, self); + } + + return; +} + static void activate_cb (AppMenuItem * self, guint timestamp, gpointer data) { @@ -309,3 +502,13 @@ app_menu_item_get_desktop (AppMenuItem * appitem) AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(appitem); return priv->desktop; } + +/* Get the dynamic items added onto the end of + and app entry. */ +GList * +app_menu_item_get_items (AppMenuItem * appitem) +{ + g_return_val_if_fail(IS_APP_MENU_ITEM(appitem), NULL); + AppMenuItemPrivate * priv = APP_MENU_ITEM_GET_PRIVATE(appitem); + return priv->shortcuts; +} diff --git a/src/app-menu-item.h b/src/app-menu-item.h index 583d50d..48a7cfa 100644 --- a/src/app-menu-item.h +++ b/src/app-menu-item.h @@ -37,8 +37,9 @@ G_BEGIN_DECLS #define IS_APP_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), APP_MENU_ITEM_TYPE)) #define APP_MENU_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), APP_MENU_ITEM_TYPE, AppMenuItemClass)) -#define APP_MENU_ITEM_SIGNAL_COUNT_CHANGED "count-changed" -#define APP_MENU_ITEM_SIGNAL_NAME_CHANGED "name-changed" +#define APP_MENU_ITEM_SIGNAL_COUNT_CHANGED "count-changed" +#define APP_MENU_ITEM_SIGNAL_NAME_CHANGED "name-changed" +#define APP_MENU_ITEM_SIGNAL_SHORTCUTS_CHANGED "shortcuts-changed" typedef struct _AppMenuItem AppMenuItem; typedef struct _AppMenuItemClass AppMenuItemClass; @@ -48,6 +49,7 @@ struct _AppMenuItemClass { void (* count_changed) (guint count); void (* name_changed) (gchar * name); + void (* shortcuts_changed) (void); }; struct _AppMenuItem { @@ -60,6 +62,7 @@ guint app_menu_item_get_count (AppMenuItem * appitem); IndicateListenerServer * app_menu_item_get_server (AppMenuItem * appitem); const gchar * app_menu_item_get_name (AppMenuItem * appitem); const gchar * app_menu_item_get_desktop (AppMenuItem * appitem); +GList * app_menu_item_get_items (AppMenuItem * appitem); G_END_DECLS diff --git a/src/messages-service.c b/src/messages-service.c index cdfdb34..0ebab0b 100644 --- a/src/messages-service.c +++ b/src/messages-service.c @@ -31,6 +31,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #include <libdbusmenu-glib/client.h> #include <libdbusmenu-glib/server.h> +#include <libdbusmenu-glib/menuitem-proxy.h> #include "im-menu-item.h" #include "app-menu-item.h" @@ -51,6 +52,7 @@ static MessageServiceDbus * dbus_interface = NULL; #define DESKTOP_FILE_GROUP "Messaging Menu" #define DESKTOP_FILE_KEY_DESKTOP "DesktopFile" +static void server_shortcuts_changed (AppMenuItem * appitem, gpointer data); static void server_count_changed (AppMenuItem * appitem, guint count, gpointer data); static void server_name_changed (AppMenuItem * appitem, gchar * name, gpointer data); static void im_time_changed (ImMenuItem * imitem, glong seconds, gpointer data); @@ -577,11 +579,21 @@ server_added (IndicateListener * listener, IndicateListenerServer * server, gcha /* Connect the signals up to the menu item */ g_signal_connect(G_OBJECT(menuitem), APP_MENU_ITEM_SIGNAL_COUNT_CHANGED, G_CALLBACK(server_count_changed), sl_item); g_signal_connect(G_OBJECT(menuitem), APP_MENU_ITEM_SIGNAL_NAME_CHANGED, G_CALLBACK(server_name_changed), menushell); + g_signal_connect(G_OBJECT(menuitem), APP_MENU_ITEM_SIGNAL_SHORTCUTS_CHANGED, G_CALLBACK(server_shortcuts_changed), menushell); /* Put our new menu item in, with the separator behind it. resort_menu will take care of whether it should be hidden or not. */ dbusmenu_menuitem_child_append(menushell, DBUSMENU_MENUITEM(menuitem)); + + GList * shortcuts = app_menu_item_get_items(sl_item->menuitem); + while (shortcuts != NULL) { + DbusmenuMenuitem * mi = DBUSMENU_MENUITEM(shortcuts->data); + g_debug("\tAdding shortcut: %s", dbusmenu_menuitem_property_get(mi, DBUSMENU_MENUITEM_PROP_LABEL)); + dbusmenu_menuitem_child_append(menushell, mi); + shortcuts = g_list_next(shortcuts); + } + dbusmenu_menuitem_child_append(menushell, DBUSMENU_MENUITEM(sl_item->separator)); resort_menu(menushell); @@ -590,6 +602,50 @@ server_added (IndicateListener * listener, IndicateListenerServer * server, gcha return; } +/* The shortcuts have changed, let's just remove them and put + the back. */ +static void +server_shortcuts_changed (AppMenuItem * appitem, gpointer data) +{ + g_debug("Application Shortcuts changed"); + DbusmenuMenuitem * shell = DBUSMENU_MENUITEM(data); + gboolean appitemfound = FALSE; + GList * children = dbusmenu_menuitem_get_children(shell); + GList * removelist = NULL; + + while (children != NULL) { + if (!appitemfound && children->data != appitem) { + children = g_list_next(children); + continue; + } + appitemfound = TRUE; + + if (!DBUSMENU_IS_MENUITEM_PROXY(children->data)) { + break; + } + + removelist = g_list_prepend(removelist, children->data); + } + + GList * removeitem; + for (removeitem = removelist; removeitem != NULL; removeitem = g_list_next(removeitem)) { + dbusmenu_menuitem_child_delete(shell, DBUSMENU_MENUITEM(removeitem->data)); + } + g_list_free(removeitem); + + GList * shortcuts = app_menu_item_get_items(appitem); + while (shortcuts != NULL) { + DbusmenuMenuitem * mi = DBUSMENU_MENUITEM(shortcuts->data); + g_debug("\tAdding shortcut: %s", dbusmenu_menuitem_property_get(mi, DBUSMENU_MENUITEM_PROP_LABEL)); + dbusmenu_menuitem_child_append(shell, mi); + shortcuts = g_list_next(shortcuts); + } + + resort_menu(shell); + + return; +} + /* The name of a server has changed, we probably need to reorder the menu to keep it in alphabetical order. This happens often after we read the destkop @@ -826,6 +882,15 @@ resort_menu (DbusmenuMenuitem * menushell) g_debug("\tMoving app %s to position %d", INDICATE_LISTENER_SERVER_DBUS_NAME(si->server), position); dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(si->menuitem), position); position++; + + /* Inserting the shortcuts from the launcher */ + GList * shortcuts = app_menu_item_get_items(si->menuitem); + while (shortcuts != NULL) { + g_debug("\t\tMoving shortcut to position %d", position); + dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(shortcuts->data), position); + position++; + shortcuts = g_list_next(shortcuts); + } } /* Putting all the indicators that are related to this application |