aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Uebernickel <lars.uebernickel@canonical.com>2013-08-13 09:43:22 +0200
committerLars Uebernickel <lars.uebernickel@canonical.com>2013-08-13 09:43:22 +0200
commit046ef6f5581ab2634d5ef097e6c449316c2404bf (patch)
treebbcbc8b9943b39d10e055d9ea469bb51a385e2d0
parent0b24c6a91ff91400568cab9b4d192a0a85db918e (diff)
downloadayatana-indicator-messages-046ef6f5581ab2634d5ef097e6c449316c2404bf.tar.gz
ayatana-indicator-messages-046ef6f5581ab2634d5ef097e6c449316c2404bf.tar.bz2
ayatana-indicator-messages-046ef6f5581ab2634d5ef097e6c449316c2404bf.zip
Add desktop menu
Only shows application launchers right now.
-rw-r--r--data/com.canonical.indicator.messages3
-rw-r--r--src/Makefile.am2
-rw-r--r--src/im-application-list.c84
-rw-r--r--src/im-application-list.h6
-rw-r--r--src/im-desktop-menu.c174
-rw-r--r--src/im-desktop-menu.h35
-rw-r--r--src/im-menu.c29
-rw-r--r--src/im-menu.h5
-rw-r--r--src/messages-service.c2
9 files changed, 329 insertions, 11 deletions
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,
@@ -455,12 +468,29 @@ im_application_list_lookup (ImApplicationList *list,
}
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)
{
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 <gio/gio.h>
+#include <gio/gdesktopappinfo.h>
#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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Lars Uebernickel <lars.uebernickel@canonical.com>
+ */
+
+#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 *) &section))
+ {
+ 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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Lars Uebernickel <lars.uebernickel@canonical.com>
+ */
+
+#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 <http://www.gnu.org/licenses/>.
#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);