From 046ef6f5581ab2634d5ef097e6c449316c2404bf Mon Sep 17 00:00:00 2001 From: Lars Uebernickel Date: Tue, 13 Aug 2013 09:43:22 +0200 Subject: Add desktop menu Only shows application launchers right now. --- data/com.canonical.indicator.messages | 3 + src/Makefile.am | 2 + src/im-application-list.c | 84 +++++++++++++--- src/im-application-list.h | 6 ++ src/im-desktop-menu.c | 174 ++++++++++++++++++++++++++++++++++ src/im-desktop-menu.h | 35 +++++++ src/im-menu.c | 29 ++++++ src/im-menu.h | 5 + src/messages-service.c | 2 + 9 files changed, 329 insertions(+), 11 deletions(-) create mode 100644 src/im-desktop-menu.c create mode 100644 src/im-desktop-menu.h diff --git a/data/com.canonical.indicator.messages b/data/com.canonical.indicator.messages index dc37549..fa0ac50 100644 --- a/data/com.canonical.indicator.messages +++ b/data/com.canonical.indicator.messages @@ -3,5 +3,8 @@ Name=indicator-messages ObjectPath=/com/canonical/indicator/messages Position=50 +[desktop] +ObjectPath=/com/canonical/indicator/messages/desktop + [phone] ObjectPath=/com/canonical/indicator/messages/phone diff --git a/src/Makefile.am b/src/Makefile.am index 74a1142..e03406a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,6 +18,8 @@ indicator_messages_service_SOURCES = \ im-menu.h \ im-phone-menu.c \ im-phone-menu.h \ + im-desktop-menu.c \ + im-desktop-menu.h \ im-application-list.c \ im-application-list.h diff --git a/src/im-application-list.c b/src/im-application-list.c index 3944f3f..8eaa601 100644 --- a/src/im-application-list.c +++ b/src/im-application-list.c @@ -44,6 +44,7 @@ enum SOURCE_REMOVED, MESSAGE_ADDED, MESSAGE_REMOVED, + APP_ADDED, APP_STOPPED, REMOVE_ALL, N_SIGNALS @@ -57,7 +58,8 @@ typedef struct GDesktopAppInfo *info; gchar *id; IndicatorMessagesApplication *proxy; - GActionMuxer *actions; + GActionMuxer *muxer; + GSimpleActionGroup *actions; GSimpleActionGroup *source_actions; GSimpleActionGroup *message_actions; GActionMuxer *message_sub_actions; @@ -84,9 +86,9 @@ application_free (gpointer data) if (app->proxy) g_object_unref (app->proxy); - if (app->actions) + if (app->muxer) { - g_object_unref (app->actions); + g_object_unref (app->muxer); g_object_unref (app->source_actions); g_object_unref (app->message_actions); g_object_unref (app->message_sub_actions); @@ -371,6 +373,17 @@ im_application_list_class_init (ImApplicationListClass *klass) G_TYPE_STRING, G_TYPE_STRING); + signals[APP_ADDED] = g_signal_new ("app-added", + IM_TYPE_APPLICATION_LIST, + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + g_cclosure_marshal_generic, + G_TYPE_NONE, + 2, + G_TYPE_STRING, + G_TYPE_DESKTOP_APP_INFO); + signals[APP_STOPPED] = g_signal_new ("app-stopped", IM_TYPE_APPLICATION_LIST, G_SIGNAL_RUN_FIRST, @@ -454,6 +467,21 @@ im_application_list_lookup (ImApplicationList *list, return app; } +void +im_application_list_activate_launch (ImApplicationList *list, + GVariant *parameter, + gpointer user_data) +{ + Application *app = user_data; + GError *error = NULL; + + if (!g_app_info_launch (G_APP_INFO (app->info), NULL, NULL, &error)) + { + g_warning ("unable to launch application: %s", error->message); + g_error_free (error); + } +} + void im_application_list_add (ImApplicationList *list, const gchar *desktop_id) @@ -461,6 +489,8 @@ im_application_list_add (ImApplicationList *list, GDesktopAppInfo *info; Application *app; const gchar *id; + GSimpleActionGroup *actions; + GSimpleAction *launch_action; g_return_if_fail (IM_IS_APPLICATION_LIST (list)); g_return_if_fail (desktop_id != NULL); @@ -482,17 +512,29 @@ im_application_list_add (ImApplicationList *list, app->info = info; app->id = im_application_list_canonical_id (id); app->list = list; - app->actions = g_action_muxer_new (); + app->muxer = g_action_muxer_new (); app->source_actions = g_simple_action_group_new (); app->message_actions = g_simple_action_group_new (); app->message_sub_actions = g_action_muxer_new (); - g_action_muxer_insert (app->actions, "src", G_ACTION_GROUP (app->source_actions)); - g_action_muxer_insert (app->actions, "msg", G_ACTION_GROUP (app->message_actions)); - g_action_muxer_insert (app->actions, "msg-actions", G_ACTION_GROUP (app->message_sub_actions)); + launch_action = g_simple_action_new_stateful ("launch", NULL, g_variant_new_boolean (FALSE)); + g_signal_connect (launch_action, "activate", G_CALLBACK (im_application_list_activate_launch), app); + + actions = g_simple_action_group_new (); + g_action_map_add_action (G_ACTION_MAP (actions), G_ACTION (launch_action)); + + g_action_muxer_insert (app->muxer, NULL, G_ACTION_GROUP (actions)); + g_action_muxer_insert (app->muxer, "src", G_ACTION_GROUP (app->source_actions)); + g_action_muxer_insert (app->muxer, "msg", G_ACTION_GROUP (app->message_actions)); + g_action_muxer_insert (app->muxer, "msg-actions", G_ACTION_GROUP (app->message_sub_actions)); g_hash_table_insert (list->applications, (gpointer) app->id, app); - g_action_muxer_insert (list->muxer, app->id, G_ACTION_GROUP (app->actions)); + g_action_muxer_insert (list->muxer, app->id, G_ACTION_GROUP (app->muxer)); + + g_signal_emit (app->list, signals[APP_ADDED], 0, app->id, app->info); + + g_object_unref (launch_action); + g_object_unref (actions); } void @@ -779,9 +821,9 @@ im_application_list_unset_remote (Application *app) app->source_actions = g_simple_action_group_new (); app->message_actions = g_simple_action_group_new (); app->message_sub_actions = g_action_muxer_new (); - g_action_muxer_insert (app->actions, "src", G_ACTION_GROUP (app->source_actions)); - g_action_muxer_insert (app->actions, "msg", G_ACTION_GROUP (app->message_actions)); - g_action_muxer_insert (app->actions, "msg-actions", G_ACTION_GROUP (app->message_sub_actions)); + g_action_muxer_insert (app->muxer, "src", G_ACTION_GROUP (app->source_actions)); + g_action_muxer_insert (app->muxer, "msg", G_ACTION_GROUP (app->message_actions)); + g_action_muxer_insert (app->muxer, "msg-actions", G_ACTION_GROUP (app->message_sub_actions)); im_application_list_update_draws_attention (app->list); @@ -878,3 +920,23 @@ im_application_list_get_action_group (ImApplicationList *list) return G_ACTION_GROUP (list->muxer); } + +GList * +im_application_list_get_applications (ImApplicationList *list) +{ + g_return_val_if_fail (IM_IS_APPLICATION_LIST (list), NULL); + + return g_hash_table_get_keys (list->applications); +} + +GDesktopAppInfo * +im_application_list_get_application (ImApplicationList *list, + const gchar *id) +{ + Application *app; + + g_return_val_if_fail (IM_IS_APPLICATION_LIST (list), NULL); + + app = g_hash_table_lookup (list->applications, id); + return app ? app->info : NULL; +} diff --git a/src/im-application-list.h b/src/im-application-list.h index ecba312..53dac50 100644 --- a/src/im-application-list.h +++ b/src/im-application-list.h @@ -21,6 +21,7 @@ #define __IM_APPLICATION_LIST_H__ #include +#include #define IM_TYPE_APPLICATION_LIST (im_application_list_get_type ()) #define IM_APPLICATION_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IM_TYPE_APPLICATION_LIST, ImApplicationList)) @@ -49,4 +50,9 @@ void im_application_list_set_remote (ImApplicationLi GActionGroup * im_application_list_get_action_group (ImApplicationList *list); +GList * im_application_list_get_applications (ImApplicationList *list); + +GDesktopAppInfo * im_application_list_get_application (ImApplicationList *list, + const gchar *id); + #endif diff --git a/src/im-desktop-menu.c b/src/im-desktop-menu.c new file mode 100644 index 0000000..ee25f02 --- /dev/null +++ b/src/im-desktop-menu.c @@ -0,0 +1,174 @@ +/* + * Copyright 2012 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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, see . + * + * Authors: + * Lars Uebernickel + */ + +#include "im-desktop-menu.h" + +typedef ImMenuClass ImDesktopMenuClass; + +struct _ImDesktopMenu +{ + ImMenu parent; + + GHashTable *source_sections; +}; + +G_DEFINE_TYPE (ImDesktopMenu, im_desktop_menu, IM_TYPE_MENU); + +static void +im_desktop_menu_app_added (ImApplicationList *applist, + const gchar *app_id, + GDesktopAppInfo *app_info, + gpointer user_data) +{ + ImDesktopMenu *menu = user_data; + GMenu *section; + GMenu *app_section; + GMenu *source_section; + gchar *namespace; + + app_section = g_menu_new (); + + /* application launcher */ + { + GMenuItem *item; + GVariant *icon; + + item = g_menu_item_new (g_app_info_get_name (G_APP_INFO (app_info)), "launch"); + g_menu_item_set_attribute (item, "x-canonical-type", "s", "com.canonical.application"); + + icon = g_icon_serialize (g_app_info_get_icon (G_APP_INFO (app_info))); + if (icon) + { + g_menu_item_set_attribute_value (item, "x-canonical-icon", icon); + g_variant_unref (icon); + } + + g_menu_append_item (app_section, item); + + g_object_unref (item); + } + + /* TODO application actions */ + + source_section = g_menu_new (); + + section = g_menu_new (); + g_menu_append_section (section, NULL, G_MENU_MODEL (app_section)); + g_menu_append_section (section, NULL, G_MENU_MODEL (source_section)); + + namespace = g_strconcat ("indicator.", app_id, NULL); + im_menu_insert_section (IM_MENU (menu), -1, namespace, G_MENU_MODEL (section)); + g_hash_table_insert (menu->source_sections, g_strdup (app_id), source_section); + + g_free (namespace); + g_object_unref (section); + g_object_unref (app_section); +} + +static void +im_desktop_menu_remove_all (ImApplicationList *applist, + gpointer user_data) +{ + ImDesktopMenu *menu = user_data; + GHashTableIter it; + GMenu *section; + + g_hash_table_iter_init (&it, menu->source_sections); + while (g_hash_table_iter_next (&it, NULL, (gpointer *) §ion)) + { + while (g_menu_model_get_n_items (G_MENU_MODEL (section)) > 0) + g_menu_remove (section, 0); + } +} + +static void +im_desktop_menu_constructed (GObject *object) +{ + ImDesktopMenu *menu = IM_DESKTOP_MENU (object); + ImApplicationList *applist; + + /* TODO: chat section */ + + { + GMenu *clear_section; + + clear_section = g_menu_new (); + g_menu_append (clear_section, "Clear", "indicator.remove-all"); + im_menu_append_section (IM_MENU (menu), G_MENU_MODEL (clear_section)); + + g_object_unref (clear_section); + } + + applist = im_menu_get_application_list (IM_MENU (menu)); + + { + GList *apps; + GList *it; + + apps = im_application_list_get_applications (applist); + for (it = apps; it != NULL; it = it->next) + { + const gchar *id = it->data; + im_desktop_menu_app_added (applist, id, im_application_list_get_application (applist, id), menu); + } + + g_list_free (apps); + } + + + g_signal_connect (applist, "app-added", G_CALLBACK (im_desktop_menu_app_added), menu); + g_signal_connect (applist, "remove-all", G_CALLBACK (im_desktop_menu_remove_all), menu); + + G_OBJECT_CLASS (im_desktop_menu_parent_class)->constructed (object); +} + +static void +im_desktop_menu_finalize (GObject *object) +{ + ImDesktopMenu *menu = IM_DESKTOP_MENU (object); + + g_hash_table_unref (menu->source_sections); + + G_OBJECT_CLASS (im_desktop_menu_parent_class)->finalize (object); +} + +static void +im_desktop_menu_class_init (ImDesktopMenuClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructed = im_desktop_menu_constructed; + object_class->finalize = im_desktop_menu_finalize; +} + +static void +im_desktop_menu_init (ImDesktopMenu *menu) +{ + menu->source_sections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); +} + +ImDesktopMenu * +im_desktop_menu_new (ImApplicationList *applist) +{ + g_return_val_if_fail (IM_IS_APPLICATION_LIST (applist), NULL); + + return g_object_new (IM_TYPE_DESKTOP_MENU, + "application-list", applist, + NULL); +} diff --git a/src/im-desktop-menu.h b/src/im-desktop-menu.h new file mode 100644 index 0000000..9469ea6 --- /dev/null +++ b/src/im-desktop-menu.h @@ -0,0 +1,35 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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, see . + * + * Authors: + * Lars Uebernickel + */ + +#ifndef __IM_DESKTOP_MENU_H__ +#define __IM_DESKTOP_MENU_H__ + +#include "im-menu.h" + +#define IM_TYPE_DESKTOP_MENU (im_desktop_menu_get_type ()) +#define IM_DESKTOP_MENU(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IM_TYPE_DESKTOP_MENU, ImDesktopMenu)) +#define IM_IS_DESKTOP_MENU(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IM_TYPE_DESKTOP_MENU)) + +typedef struct _ImDesktopMenu ImDesktopMenu; + +GType im_desktop_menu_get_type (void); + +ImDesktopMenu * im_desktop_menu_new (ImApplicationList *applist); + +#endif diff --git a/src/im-menu.c b/src/im-menu.c index 4c660e5..ac23a29 100644 --- a/src/im-menu.c +++ b/src/im-menu.c @@ -113,6 +113,7 @@ im_menu_init (ImMenu *menu) root = g_menu_item_new (NULL, "indicator.messages"); g_menu_item_set_attribute (root, "x-canonical-type", "s", "com.canonical.indicator.root"); + g_menu_item_set_attribute (root, "action-namespace", "s", "indicator"); g_menu_item_set_submenu (root, G_MENU_MODEL (priv->menu)); g_menu_append_item (priv->toplevel_menu, root); @@ -160,3 +161,31 @@ im_menu_append_section (ImMenu *menu, g_menu_append_section (priv->menu, NULL, section); } + +void +im_menu_insert_section (ImMenu *menu, + gint position, + const gchar *namespace, + GMenuModel *section) +{ + ImMenuPrivate *priv; + GMenuItem *item; + + g_return_if_fail (IM_IS_MENU (menu)); + g_return_if_fail (G_IS_MENU_MODEL (section)); + + priv = im_menu_get_instance_private (menu); + + /* count from the back if position is < 0 */ + if (position < 0) + position = g_menu_model_get_n_items (G_MENU_MODEL (priv->menu)) + position; + + item = g_menu_item_new_section (NULL, section); + + if (namespace) + g_menu_item_set_attribute (item, "action-namespace", "s", namespace); + + g_menu_insert_item (priv->menu, position, item); + + g_object_unref (item); +} diff --git a/src/im-menu.h b/src/im-menu.h index ec41bff..d3775ad 100644 --- a/src/im-menu.h +++ b/src/im-menu.h @@ -56,4 +56,9 @@ gboolean im_menu_export (ImMenu void im_menu_append_section (ImMenu *menu, GMenuModel *section); +void im_menu_insert_section (ImMenu *menu, + gint position, + const gchar *namespace, + GMenuModel *section); + #endif diff --git a/src/messages-service.c b/src/messages-service.c index 781e261..efe9602 100644 --- a/src/messages-service.c +++ b/src/messages-service.c @@ -32,6 +32,7 @@ with this program. If not, see . #include "indicator-messages-service.h" #include "indicator-messages-application.h" #include "im-phone-menu.h" +#include "im-desktop-menu.h" #include "im-application-list.h" #define NUM_STATUSES 5 @@ -169,6 +170,7 @@ main (int argc, char ** argv) menus = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref); g_hash_table_insert (menus, "phone", im_phone_menu_new (applications)); + g_hash_table_insert (menus, "desktop", im_desktop_menu_new (applications)); g_main_loop_run(mainloop); -- cgit v1.2.3