aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--debian/changelog108
-rw-r--r--debian/libmessaging-menu0.symbols2
-rw-r--r--src/Makefile.am4
-rw-r--r--src/im-application-list.c128
-rw-r--r--src/im-desktop-menu.c35
-rw-r--r--src/im-menu.c15
-rw-r--r--src/im-menu.h2
-rw-r--r--src/im-phone-menu.c3
-rw-r--r--src/indicator-desktop-shortcuts.c680
-rw-r--r--src/indicator-desktop-shortcuts.h80
-rw-r--r--src/messages-service.c13
11 files changed, 937 insertions, 133 deletions
diff --git a/debian/changelog b/debian/changelog
index 6544d6e..e409bdc 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,14 +1,19 @@
-indicator-messages (13.10.1-0ubuntu1) UNRELEASED; urgency=low
+indicator-messages (13.10.1+13.10.20130820.2-0ubuntu1) saucy; urgency=low
- * Bumping version to ensure we override all PPAs when this lands
+ * Automatic snapshot from revision 354
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 20 Aug 2013 08:59:34 +0000
- -- Ted Gould <ted@ubuntu.com> Mon, 19 Aug 2013 10:09:40 -0500
+indicator-messages (13.10.1+13.10.20130820-0ubuntu1) saucy; urgency=low
-indicator-messages (13.10.0phablet1) raring; urgency=low
+ [ Ted Gould ]
+ * Bumping version to ensure we override all PPAs when this lands
- * Version bump to not pull from archives
+ [ Ubuntu daily release ]
+ * debian/*symbols: auto-update new symbols to released version
+ * Automatic snapshot from revision 352
- -- Sergio Schvezov <sergio.schvezov@canonical.com> Fri, 26 Apr 2013 13:53:53 -0300
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 20 Aug 2013 02:06:40 +0000
indicator-messages (12.10.6+13.10.20130702-0ubuntu1) saucy; urgency=low
@@ -20,12 +25,6 @@ indicator-messages (12.10.6+13.10.20130702-0ubuntu1) saucy; urgency=low
-- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 02 Jul 2013 02:02:36 +0000
-indicator-messages (12.10.6phablet1) quantal; urgency=low
-
- * Adding guards for g_type_init
-
- -- Sergio Schvezov <sergio.schvezov@canonical.com> Fri, 22 Mar 2013 17:23:45 -0300
-
indicator-messages (12.10.6daily13.06.19-0ubuntu1) saucy; urgency=low
[ Sebastien Bacher ]
@@ -139,91 +138,6 @@ indicator-messages (12.10.6daily12.11.21.1-0ubuntu1) raring; urgency=low
-- Automatic PS uploader <ps-jenkins@lists.canonical.com> Wed, 21 Nov 2012 10:41:37 +0000
-indicator-messages (12.10.6-0ubuntu1phablet9) quantal; urgency=low
-
- * add "remove-all" signal to imapplicationlist (temporarily)
-
- -- Lars Uebernickel <lars.uebernickel@ubuntu.com> Thu, 20 Dec 2012 18:49:50 +0100
-
-indicator-messages (12.10.6-0ubuntu1phablet8) quantal; urgency=low
-
- * Make messaging_menu_app_remove_message() work for messages with a ref count of 1
- * Make handling of multiple processes with the same desktop id more robust
- * ImPhoneMenu: sort messages by time (fixes LP #1090266)
- * Don't show message sources
-
- -- Lars Uebernickel <lars.uebernickel@ubuntu.com> Mon, 17 Dec 2012 14:42:03 +0100
-
-indicator-messages (12.10.6-0ubuntu1phablet7) quantal; urgency=low
-
- * Remove variant wrapper from 'parameter' argument of the "activate" signal
-
- -- Lars Uebernickel <lars.uebernickel@ubuntu.com> Tue, 11 Dec 2012 14:28:15 +0100
-
-indicator-messages (12.10.6-0ubuntu1phablet6) quantal; urgency=low
-
- * Don't show sources
- * Always use the "messageitem" widget type for messages
-
- -- Lars Uebernickel <lars.uebernickel@ubuntu.com> Mon, 10 Dec 2012 14:38:16 +0100
-
-indicator-messages (12.10.6-0ubuntu1phablet5) quantal; urgency=low
-
- * Don't shorten the app id to seven characters (fixes LP #1086729)
- * Add messaging_menu_app_get_message
-
- -- Lars Uebernickel <lars.uebernickel@ubuntu.com> Thu, 06 Dec 2012 15:06:34 +0000
-
-indicator-messages (12.10.6-0ubuntu1phablet4) quantal; urgency=low
-
- [Lars Uebernickel]
- * Add the concept of actions to messages
- * Stop using IndicatorService
- * Reverse order of messages
- * Expose symbolic application icon
- * Change icon when there are any messages in the menu
-
- -- Lars Uebernickel <lars.uebernickel@ubuntu.com> Tue, 04 Dec 2012 21:10:26 +0000
-
-indicator-messages (12.10.6-0ubuntu1phablet3) quantal; urgency=low
-
- [Lars Uebernickel]
- * expose root menu item of which the indicator menu is a submenu
- * fix crash in im-application-list on arm
-
- -- Lars Uebernickel <lars.uebernickel@ubuntu.com> Thu, 29 Nov 2012 21:44:19 +0100
-
-indicator-messages (12.10.6-0ubuntu1phablet2) quantal; urgency=low
-
- [Lars Uebernickel]
- * refactor messages-service.c
- * notify applications about message and source activations
- * add "Clear All" menu item
- * allow dismissing of messages
- * include application icons on message items
-
- -- Lars Uebernickel <lars.uebernickel@ubuntu.com> Mon, 26 Nov 2012 22:19:51 +0100
-
-indicator-messages (12.10.6-0ubuntu1phablet1) quantal; urgency=low
-
- [ Mathieu Trudel-Lapierre ]
- * debian/rules:
- - Use autogen.sh for dh_autoreconf.
- - Drop the override for dh_makeshlibs.
- - Add DPKG_GENSYMBOLS_CHECK_LEVEL=4.
- * debian/control:
- - Fix styling: add trailing commas at the end of lists.
- - Update Vcs-Bzr, Vcs-Browser fields and add a notice for developers.
- - Add libgtest-dev to Build-Depends.
- * Automatic snapshot from revision 329 (bootstrap):
- - Clear the detail (count or time) of a source when another type of detail
- is set. (LP: #1071640)
-
- [ Ricardo Mendoza ]
- * Releasing upstream for phablet
-
- -- Ricardo Mendoza <ricardo.mendoza@canonical.com> Tue, 20 Nov 2012 10:08:59 -0430
-
indicator-messages (12.10.5-0ubuntu2) raring; urgency=low
* Upload to raring
diff --git a/debian/libmessaging-menu0.symbols b/debian/libmessaging-menu0.symbols
index d5eaed1..6a54911 100644
--- a/debian/libmessaging-menu0.symbols
+++ b/debian/libmessaging-menu0.symbols
@@ -5,7 +5,7 @@ libmessaging-menu.so.0 libmessaging-menu0 #MINVER#
messaging_menu_app_append_source_with_string@Base 12.10.0
messaging_menu_app_append_source_with_time@Base 12.10.0
messaging_menu_app_draw_attention@Base 12.10.0
- messaging_menu_app_get_message@Base 0replaceme
+ messaging_menu_app_get_message@Base 13.10.1+13.10.20130820
messaging_menu_app_get_type@Base 12.10.0
messaging_menu_app_has_source@Base 12.10.0
messaging_menu_app_insert_source@Base 12.10.0
diff --git a/src/Makefile.am b/src/Makefile.am
index e03406a..9b1cc9a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -21,7 +21,9 @@ indicator_messages_service_SOURCES = \
im-desktop-menu.c \
im-desktop-menu.h \
im-application-list.c \
- im-application-list.h
+ im-application-list.h \
+ indicator-desktop-shortcuts.c \
+ indicator-desktop-shortcuts.h
indicator_messages_service_CFLAGS = \
$(APPLET_CFLAGS) \
diff --git a/src/im-application-list.c b/src/im-application-list.c
index 14af69b..684a2e4 100644
--- a/src/im-application-list.c
+++ b/src/im-application-list.c
@@ -21,10 +21,13 @@
#include "indicator-messages-application.h"
#include "gactionmuxer.h"
+#include "indicator-desktop-shortcuts.h"
#include <gio/gdesktopappinfo.h>
#include <string.h>
+#include "glib/gi18n.h"
+
typedef GObjectClass ImApplicationListClass;
struct _ImApplicationList
@@ -66,12 +69,12 @@ typedef struct
gchar *id;
IndicatorMessagesApplication *proxy;
GActionMuxer *muxer;
- GSimpleActionGroup *actions;
GSimpleActionGroup *source_actions;
GSimpleActionGroup *message_actions;
GActionMuxer *message_sub_actions;
GCancellable *cancellable;
gboolean draws_attention;
+ IndicatorDesktopShortcuts * shortcuts;
} Application;
@@ -108,9 +111,39 @@ application_free (gpointer data)
g_object_unref (app->message_sub_actions);
}
+ g_clear_object (&app->shortcuts);
+
g_slice_free (Application, app);
}
+/* Check to see if we have actions by getting the full list of
+ names and see if there is one. Not exactly efficient :-/ */
+static gboolean
+_g_action_group_has_actions (GActionGroup * ag)
+{
+ gchar ** list = NULL;
+ gboolean retval = FALSE;
+
+ list = g_action_group_list_actions(ag);
+ retval = (list[0] != NULL);
+ g_strfreev(list);
+
+ return retval;
+}
+
+/* Check to see if either of our action groups has any actions, if
+ so return TRUE so we get chosen! */
+static gboolean
+application_has_items (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ Application *app = value;
+
+ return _g_action_group_has_actions(G_ACTION_GROUP(app->source_actions)) ||
+ _g_action_group_has_actions(G_ACTION_GROUP(app->message_actions));
+}
+
static gboolean
application_draws_attention (gpointer key,
gpointer value,
@@ -124,18 +157,61 @@ application_draws_attention (gpointer key,
static void
im_application_list_update_draws_attention (ImApplicationList *list)
{
+ const gchar *base_icon_name;
+ const gchar *accessible_name;
const gchar *icon_name;
+ GIcon * icon;
+ GVariantBuilder builder;
GVariant *state;
- GActionGroup *main_actions;
- if (g_hash_table_find (list->applications, application_draws_attention, NULL))
- icon_name = "indicator-messages-new";
- else
- icon_name = "indicator-messages";
+ /* Figure out what type of icon we should be drawing */
+ if (g_hash_table_find (list->applications, application_draws_attention, NULL)) {
+ base_icon_name = "indicator-messages-new-%s";
+ accessible_name = _("New Messages");
+ } else {
+ base_icon_name = "indicator-messages-%s";
+ accessible_name = _("Messages");
+ }
- main_actions = g_action_muxer_get_group (list->muxer, NULL);
- state = g_variant_new ("(sssb)", "", icon_name, "Messages", TRUE);
- g_action_group_change_action_state (main_actions, "messages", state);
+ /* Include the IM state in the icon */
+ state = g_action_group_get_action_state(G_ACTION_GROUP(list->globalactions), "status");
+ icon_name = g_strdup_printf(base_icon_name, g_variant_get_string(state, NULL));
+ g_variant_unref(state);
+
+ /* Build up the dictionary of values for the state */
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_DICTIONARY);
+
+ /* icon */
+ g_variant_builder_open(&builder, G_VARIANT_TYPE_DICT_ENTRY);
+ g_variant_builder_add_value(&builder, g_variant_new_string("icon"));
+ icon = g_themed_icon_new_with_default_fallbacks(icon_name);
+ g_variant_builder_add_value(&builder, g_variant_new_variant(g_icon_serialize(icon)));
+ g_object_unref(icon);
+ g_variant_builder_close(&builder);
+
+ /* accessible description */
+ g_variant_builder_open(&builder, G_VARIANT_TYPE_DICT_ENTRY);
+ g_variant_builder_add_value(&builder, g_variant_new_string("accessible-desc"));
+ g_variant_builder_add_value(&builder, g_variant_new_variant(g_variant_new_string(accessible_name)));
+ g_variant_builder_close(&builder);
+
+ /* visibility */
+ g_variant_builder_open(&builder, G_VARIANT_TYPE_DICT_ENTRY);
+ g_variant_builder_add_value(&builder, g_variant_new_string("visible"));
+ g_variant_builder_add_value(&builder, g_variant_new_variant(g_variant_new_boolean(TRUE)));
+ g_variant_builder_close(&builder);
+
+ /* Set the state */
+ g_action_group_change_action_state (G_ACTION_GROUP(list->globalactions), "messages", g_variant_builder_end(&builder));
+
+ GAction * remove_action = g_simple_action_group_lookup(list->globalactions, "remove-all");
+ if (g_hash_table_find (list->applications, application_has_items, NULL)) {
+ g_debug("Enabling remove-all");
+ g_simple_action_set_enabled(G_SIMPLE_ACTION(remove_action), TRUE);
+ } else {
+ g_debug("Disabling remove-all");
+ g_simple_action_set_enabled(G_SIMPLE_ACTION(remove_action), FALSE);
+ }
}
/* Check a source action to see if it draws */
@@ -490,7 +566,6 @@ static void
im_application_list_init (ImApplicationList *list)
{
const GActionEntry action_entries[] = {
- { "messages", NULL, NULL, "('', 'indicator-messages', 'Messages', true)", NULL },
{ "remove-all", im_application_list_remove_all }
};
@@ -498,6 +573,10 @@ im_application_list_init (ImApplicationList *list)
list->app_status = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
list->globalactions = g_simple_action_group_new ();
+ {
+ GSimpleAction * messages = g_simple_action_new_stateful("messages", G_VARIANT_TYPE("a{sv}"), g_variant_new_array(G_VARIANT_TYPE("{sv}"), NULL, 0));
+ g_simple_action_group_insert(list->globalactions, G_ACTION(messages));
+ }
g_simple_action_group_add_entries (list->globalactions, action_entries, G_N_ELEMENTS (action_entries), list);
list->statusaction = g_simple_action_new_stateful("status", G_VARIANT_TYPE_STRING, g_variant_new_string("offline"));
@@ -507,6 +586,7 @@ im_application_list_init (ImApplicationList *list)
list->muxer = g_action_muxer_new ();
g_action_muxer_insert (list->muxer, NULL, G_ACTION_GROUP (list->globalactions));
+ im_application_list_update_draws_attention (list);
}
ImApplicationList *
@@ -573,7 +653,7 @@ im_application_list_activate_app_action (GSimpleAction *action,
{
Application *app = user_data;
- g_desktop_app_info_launch_action (app->info, g_action_get_name (G_ACTION (action)), NULL);
+ indicator_desktop_shortcuts_nick_exec_with_context (app->shortcuts, g_action_get_name (G_ACTION (action)), NULL);
}
gboolean
@@ -585,6 +665,7 @@ im_application_list_add (ImApplicationList *list,
const gchar *id;
GSimpleActionGroup *actions;
GSimpleAction *launch_action;
+ IndicatorDesktopShortcuts * shortcuts = NULL;
g_return_if_fail (IM_IS_APPLICATION_LIST (list));
g_return_if_fail (desktop_id != NULL);
@@ -602,6 +683,12 @@ im_application_list_add (ImApplicationList *list,
id = g_app_info_get_id (G_APP_INFO (info));
g_return_if_fail (id != NULL);
+ {
+ const char * filename = g_desktop_app_info_get_filename(info);
+ if (filename != NULL)
+ shortcuts = indicator_desktop_shortcuts_new(filename, "Messaging Menu");
+ }
+
app = g_slice_new0 (Application);
app->info = info;
app->id = im_application_list_canonical_id (id);
@@ -611,6 +698,7 @@ im_application_list_add (ImApplicationList *list,
app->message_actions = g_simple_action_group_new ();
app->message_sub_actions = g_action_muxer_new ();
app->draws_attention = FALSE;
+ app->shortcuts = shortcuts;
actions = g_simple_action_group_new ();
@@ -618,14 +706,14 @@ im_application_list_add (ImApplicationList *list,
g_signal_connect (launch_action, "activate", G_CALLBACK (im_application_list_activate_launch), app);
g_action_map_add_action (G_ACTION_MAP (actions), G_ACTION (launch_action));
- {
- const gchar *const *app_actions;
+ if (app->shortcuts != NULL) {
+ const gchar ** nicks;
- for (app_actions = g_desktop_app_info_list_actions (app->info); *app_actions; app_actions++)
+ for (nicks = indicator_desktop_shortcuts_get_nicks (app->shortcuts); *nicks; nicks++)
{
GSimpleAction *action;
- action = g_simple_action_new (*app_actions, NULL);
+ action = g_simple_action_new (*nicks, NULL);
g_signal_connect (action, "activate", G_CALLBACK (im_application_list_activate_app_action), app);
g_action_map_add_action (G_ACTION_MAP (actions), G_ACTION (action));
@@ -715,15 +803,21 @@ im_application_list_source_changed (Application *app,
gint64 time;
const gchar *string;
gboolean draws_attention;
+ gboolean old_draw;
g_variant_get (source, "(&s&s&sux&sb)",
&id, &label, &iconstr, &count, &time, &string, &draws_attention);
+ old_draw = app_source_action_check_draw(app, id);
+
g_action_group_change_action_state (G_ACTION_GROUP (app->source_actions), id,
g_variant_new ("(uxsb)", count, time, string, draws_attention));
g_signal_emit (app->list, signals[SOURCE_CHANGED], 0, app->id, id, label, iconstr);
+ if (!old_draw && draws_attention)
+ app->draws_attention = TRUE;
+
im_application_list_update_draws_attention (app->list);
}
@@ -1091,6 +1185,8 @@ status_activated (GSimpleAction * action, GVariant * param, gpointer user_data)
g_signal_emit (list, signals[STATUS_SET], 0, status);
+ im_application_list_update_draws_attention(list);
+
return;
}
@@ -1136,6 +1232,8 @@ im_application_list_set_status (ImApplicationList * list, const gchar * id, cons
g_simple_action_set_state(list->statusaction, g_variant_new_string(status_ids[final_status]));
+ im_application_list_update_draws_attention(list);
+
return;
}
diff --git a/src/im-desktop-menu.c b/src/im-desktop-menu.c
index 1040d62..834a8c9 100644
--- a/src/im-desktop-menu.c
+++ b/src/im-desktop-menu.c
@@ -18,6 +18,7 @@
*/
#include "im-desktop-menu.h"
+#include "indicator-desktop-shortcuts.h"
#include <glib/gi18n.h>
typedef ImMenuClass ImDesktopMenuClass;
@@ -66,21 +67,28 @@ im_desktop_menu_app_added (ImApplicationList *applist,
}
/* application actions */
-#if 0
{
- const gchar *const *actions;
+ const gchar * filename = NULL;
+ IndicatorDesktopShortcuts * shortcuts = NULL;
+ const gchar ** nicks = {NULL};
- for (actions = g_desktop_app_info_list_actions (app_info); *actions; actions++)
- {
- gchar *label;
+ filename = g_desktop_app_info_get_filename(app_info);
+ if (filename != NULL)
+ shortcuts = indicator_desktop_shortcuts_new(filename, "Messaging Menu");
- label = g_desktop_app_info_get_action_name (app_info, *actions);
- g_menu_append (app_section, label, *actions);
+ if (shortcuts != NULL)
+ for (nicks = indicator_desktop_shortcuts_get_nicks(shortcuts); *nicks; nicks++)
+ {
+ gchar *label;
- g_free (label);
- }
+ label = indicator_desktop_shortcuts_nick_get_name (shortcuts, *nicks);
+ g_menu_append (app_section, label, *nicks);
+
+ g_free (label);
+ }
+
+ g_clear_object(&shortcuts);
}
-#endif
source_section = g_menu_new ();
@@ -89,7 +97,7 @@ im_desktop_menu_app_added (ImApplicationList *applist,
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));
+ im_menu_insert_section (IM_MENU (menu), g_app_info_get_name(G_APP_INFO(app_info)), namespace, G_MENU_MODEL (section));
g_hash_table_insert (menu->source_sections, g_strdup (app_id), source_section);
g_free (namespace);
@@ -114,7 +122,8 @@ im_desktop_menu_source_added (ImApplicationList *applist,
g_return_if_fail (source_section != NULL);
action = g_strconcat ("src.", source_id, NULL);
- item = g_menu_item_new (label, action);
+ item = g_menu_item_new (label, NULL);
+ g_menu_item_set_action_and_target_value(item, action, NULL);
g_menu_item_set_attribute (item, "x-canonical-type", "s", "com.canonical.indicator.messages.source");
if (icon && *icon)
g_menu_item_set_attribute (item, "icon", "s", icon);
@@ -227,7 +236,7 @@ im_desktop_menu_constructed (GObject *object)
GMenu *clear_section;
clear_section = g_menu_new ();
- g_menu_append (clear_section, "Clear", "indicator.remove-all");
+ 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);
diff --git a/src/im-menu.c b/src/im-menu.c
index ac23a29..1aaffe3 100644
--- a/src/im-menu.c
+++ b/src/im-menu.c
@@ -164,10 +164,11 @@ im_menu_append_section (ImMenu *menu,
void
im_menu_insert_section (ImMenu *menu,
- gint position,
+ const gchar *sort_string,
const gchar *namespace,
GMenuModel *section)
{
+ int position;
ImMenuPrivate *priv;
GMenuItem *item;
@@ -176,11 +177,17 @@ im_menu_insert_section (ImMenu *menu,
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;
+ for (position = 1; position < g_menu_model_get_n_items(G_MENU_MODEL (priv->menu)) - 1; position++)
+ {
+ gchar * item_sort = NULL;
+ if (g_menu_model_get_item_attribute(G_MENU_MODEL(priv->menu), position, "x-messaging-menu-sort-string", "s", &item_sort))
+ if (g_utf8_collate(sort_string, item_sort) < 0)
+ break;
+ g_free(item_sort);
+ }
item = g_menu_item_new_section (NULL, section);
+ g_menu_item_set_attribute (item, "x-messaging-menu-sort-string", "s", sort_string);
if (namespace)
g_menu_item_set_attribute (item, "action-namespace", "s", namespace);
diff --git a/src/im-menu.h b/src/im-menu.h
index d3775ad..7c15eb7 100644
--- a/src/im-menu.h
+++ b/src/im-menu.h
@@ -57,7 +57,7 @@ void im_menu_append_section (ImMenu
GMenuModel *section);
void im_menu_insert_section (ImMenu *menu,
- gint position,
+ const gchar *sort_string,
const gchar *namespace,
GMenuModel *section);
diff --git a/src/im-phone-menu.c b/src/im-phone-menu.c
index 7381097..0ea6f76 100644
--- a/src/im-phone-menu.c
+++ b/src/im-phone-menu.c
@@ -20,6 +20,7 @@
#include "im-phone-menu.h"
#include <string.h>
+#include <glib/gi18n.h>
typedef ImMenuClass ImPhoneMenuClass;
@@ -72,7 +73,7 @@ im_phone_menu_constructed (GObject *object)
clear_section = g_menu_new ();
- item = g_menu_item_new ("Clear All", "remove-all");
+ item = g_menu_item_new (_("Clear All"), "remove-all");
g_menu_item_set_attribute (item, "x-canonical-type", "s", "com.canonical.indicator.button");
g_menu_append_item (clear_section, item);
diff --git a/src/indicator-desktop-shortcuts.c b/src/indicator-desktop-shortcuts.c
new file mode 100644
index 0000000..7b43630
--- /dev/null
+++ b/src/indicator-desktop-shortcuts.c
@@ -0,0 +1,680 @@
+/*
+A small file to parse through the actions that are available
+in the desktop file and making those easily usable.
+
+Copyright 2010 Canonical Ltd.
+
+Authors:
+ Ted Gould <ted@canonical.com>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+version 3.0 as published by the Free Software Foundation.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License version 3.0 for more details.
+
+You should have received a copy of the GNU General Public
+License along with this library. If not, see
+<http://www.gnu.org/licenses/>.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gio/gdesktopappinfo.h>
+#include "indicator-desktop-shortcuts.h"
+
+#define ACTIONS_KEY "Actions"
+#define ACTION_GROUP_PREFIX "Desktop Action"
+
+#define OLD_GROUP_SUFFIX "Shortcut Group"
+#define OLD_SHORTCUTS_KEY "X-Ayatana-Desktop-Shortcuts"
+#define OLD_ENVIRON_KEY "TargetEnvironment"
+
+#define PROP_DESKTOP_FILE_S "desktop-file"
+#define PROP_IDENTITY_S "identity"
+
+typedef enum _actions_t actions_t;
+enum _actions_t {
+ ACTIONS_NONE,
+ ACTIONS_XAYATANA,
+ ACTIONS_DESKTOP_SPEC
+};
+
+typedef struct _IndicatorDesktopShortcutsPrivate IndicatorDesktopShortcutsPrivate;
+struct _IndicatorDesktopShortcutsPrivate {
+ actions_t actions;
+ GKeyFile * keyfile;
+ gchar * identity;
+ GArray * nicks;
+ gchar * domain;
+};
+
+enum {
+ PROP_0,
+ PROP_DESKTOP_FILE,
+ PROP_IDENTITY
+};
+
+#define INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), INDICATOR_TYPE_DESKTOP_SHORTCUTS, IndicatorDesktopShortcutsPrivate))
+
+static void indicator_desktop_shortcuts_class_init (IndicatorDesktopShortcutsClass *klass);
+static void indicator_desktop_shortcuts_init (IndicatorDesktopShortcuts *self);
+static void indicator_desktop_shortcuts_dispose (GObject *object);
+static void indicator_desktop_shortcuts_finalize (GObject *object);
+static void set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
+static void get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
+static void parse_keyfile (IndicatorDesktopShortcuts * ids);
+static gboolean should_show (GKeyFile * keyfile, const gchar * group, const gchar * identity, gboolean should_have_target);
+
+G_DEFINE_TYPE (IndicatorDesktopShortcuts, indicator_desktop_shortcuts, G_TYPE_OBJECT);
+
+/* Build up the class */
+static void
+indicator_desktop_shortcuts_class_init (IndicatorDesktopShortcutsClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (IndicatorDesktopShortcutsPrivate));
+
+ object_class->dispose = indicator_desktop_shortcuts_dispose;
+ object_class->finalize = indicator_desktop_shortcuts_finalize;
+
+ /* Property funcs */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+
+ g_object_class_install_property(object_class, PROP_DESKTOP_FILE,
+ g_param_spec_string(PROP_DESKTOP_FILE_S,
+ "The path of the desktop file to read",
+ "A path to a desktop file that we'll look for shortcuts in.",
+ NULL,
+ G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property(object_class, PROP_IDENTITY,
+ g_param_spec_string(PROP_IDENTITY_S,
+ "The string that represents the identity that we're acting as.",
+ "Used to process ShowIn and NotShownIn fields of the desktop shortcust to get the proper list.",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
+
+ return;
+}
+
+/* Initialize instance data */
+static void
+indicator_desktop_shortcuts_init (IndicatorDesktopShortcuts *self)
+{
+ IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(self);
+
+ priv->keyfile = NULL;
+ priv->identity = NULL;
+ priv->domain = NULL;
+ priv->nicks = g_array_new(TRUE, TRUE, sizeof(gchar *));
+ priv->actions = ACTIONS_NONE;
+
+ return;
+}
+
+/* Clear object references */
+static void
+indicator_desktop_shortcuts_dispose (GObject *object)
+{
+ IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(object);
+
+ if (priv->keyfile) {
+ g_key_file_free(priv->keyfile);
+ priv->keyfile = NULL;
+ }
+
+ G_OBJECT_CLASS (indicator_desktop_shortcuts_parent_class)->dispose (object);
+ return;
+}
+
+/* Free all memory */
+static void
+indicator_desktop_shortcuts_finalize (GObject *object)
+{
+ IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(object);
+
+ if (priv->identity != NULL) {
+ g_free(priv->identity);
+ priv->identity = NULL;
+ }
+
+ if (priv->domain != NULL) {
+ g_free(priv->domain);
+ priv->domain = NULL;
+ }
+
+ if (priv->nicks != NULL) {
+ gint i;
+ for (i = 0; i < priv->nicks->len; i++) {
+ gchar * nick = g_array_index(priv->nicks, gchar *, i);
+ g_free(nick);
+ }
+ g_array_free(priv->nicks, TRUE);
+ priv->nicks = NULL;
+ }
+
+ G_OBJECT_CLASS (indicator_desktop_shortcuts_parent_class)->finalize (object);
+ return;
+}
+
+/* Sets one of the two properties we have, only at construction though */
+static void
+set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
+{
+ g_return_if_fail(INDICATOR_IS_DESKTOP_SHORTCUTS(object));
+ IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(object);
+
+ switch(prop_id) {
+ case PROP_DESKTOP_FILE: {
+ if (priv->keyfile != NULL) {
+ g_key_file_free(priv->keyfile);
+ priv->keyfile = NULL;
+ priv->actions = ACTIONS_NONE;
+ }
+
+ GError * error = NULL;
+ GKeyFile * keyfile = g_key_file_new();
+ g_key_file_load_from_file(keyfile, g_value_get_string(value), G_KEY_FILE_NONE, &error);
+
+ if (error != NULL) {
+ g_warning("Unable to load keyfile from file '%s': %s", g_value_get_string(value), error->message);
+ g_error_free(error);
+ g_key_file_free(keyfile);
+ break;
+ }
+
+ /* Always prefer the desktop spec if we can get it */
+ if (priv->actions == ACTIONS_NONE && g_key_file_has_key(keyfile, G_KEY_FILE_DESKTOP_GROUP, ACTIONS_KEY, NULL)) {
+ priv->actions = ACTIONS_DESKTOP_SPEC;
+ }
+
+ /* But fallback if we can't */
+ if (priv->actions == ACTIONS_NONE && g_key_file_has_key(keyfile, G_KEY_FILE_DESKTOP_GROUP, OLD_SHORTCUTS_KEY, NULL)) {
+ priv->actions = ACTIONS_XAYATANA;
+ g_warning("Desktop file '%s' is using a deprecated format for its actions that will be dropped soon.", g_value_get_string(value));
+ }
+
+ if (priv->actions == ACTIONS_NONE) {
+ g_key_file_free(keyfile);
+ break;
+ }
+
+ priv->keyfile = keyfile;
+ parse_keyfile(INDICATOR_DESKTOP_SHORTCUTS(object));
+ break;
+ }
+ case PROP_IDENTITY:
+ if (priv->identity != NULL) {
+ g_warning("Identity already set to '%s' and trying to set it to '%s'.", priv->identity, g_value_get_string(value));
+ return;
+ }
+ priv->identity = g_value_dup_string(value);
+ parse_keyfile(INDICATOR_DESKTOP_SHORTCUTS(object));
+ break;
+ /* *********************** */
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+
+ return;
+}
+
+/* Gets either the desktop file our the identity. */
+static void
+get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec)
+{
+ g_return_if_fail(INDICATOR_IS_DESKTOP_SHORTCUTS(object));
+ IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(object);
+
+ switch(prop_id) {
+ case PROP_IDENTITY:
+ g_value_set_string(value, priv->identity);
+ break;
+ /* *********************** */
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+
+ return;
+}
+
+/* Checks to see if we can, and if we can it goes through
+ and parses the keyfile entries. */
+static void
+parse_keyfile (IndicatorDesktopShortcuts * ids)
+{
+ IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(ids);
+
+ if (priv->keyfile == NULL) {
+ return;
+ }
+
+ if (priv->identity == NULL) {
+ return;
+ }
+
+ /* Remove a previous translation domain if we had one
+ from a previously parsed file. */
+ if (priv->domain != NULL) {
+ g_free(priv->domain);
+ priv->domain = NULL;
+ }
+
+ /* Check to see if there is a custom translation domain that
+ we should take into account. */
+ if (priv->domain == NULL &&
+ g_key_file_has_key(priv->keyfile, G_KEY_FILE_DESKTOP_GROUP, "X-GNOME-Gettext-Domain", NULL)) {
+ priv->domain = g_key_file_get_string(priv->keyfile, G_KEY_FILE_DESKTOP_GROUP, "X-GNOME-Gettext-Domain", NULL);
+ }
+
+ if (priv->domain == NULL &&
+ g_key_file_has_key(priv->keyfile, G_KEY_FILE_DESKTOP_GROUP, "X-Ubuntu-Gettext-Domain", NULL)) {
+ priv->domain = g_key_file_get_string(priv->keyfile, G_KEY_FILE_DESKTOP_GROUP, "X-Ubuntu-Gettext-Domain", NULL);
+ }
+
+ /* We need to figure out what we're looking for and what we want to
+ look for in the rest of the file */
+ const gchar * list_name = NULL;
+ const gchar * group_format = NULL;
+ gboolean should_have_target = FALSE;
+
+ switch (priv->actions) {
+ case ACTIONS_NONE:
+ /* None, let's just get outta here */
+ return;
+ case ACTIONS_XAYATANA:
+ list_name = OLD_SHORTCUTS_KEY;
+ group_format = "%s " OLD_GROUP_SUFFIX;
+ should_have_target = TRUE;
+ break;
+ case ACTIONS_DESKTOP_SPEC:
+ list_name = ACTIONS_KEY;
+ group_format = ACTION_GROUP_PREFIX " %s";
+ should_have_target = FALSE;
+ break;
+ default:
+ g_assert_not_reached();
+ return;
+ }
+
+ /* Okay, we've got everything we need. Let's get it on! */
+ gint i;
+ gsize num_nicks = 0;
+ gchar ** nicks = g_key_file_get_string_list(priv->keyfile, G_KEY_FILE_DESKTOP_GROUP, list_name, &num_nicks, NULL);
+
+ /* If there is an error from get_string_list num_nicks should still
+ be zero, so this loop will drop out. */
+ for (i = 0; i < num_nicks; i++) {
+ /* g_debug("Looking at group nick %s", nicks[i]); */
+ gchar * groupname = g_strdup_printf(group_format, nicks[i]);
+ if (!g_key_file_has_group(priv->keyfile, groupname)) {
+ g_warning("Unable to find group '%s'", groupname);
+ g_free(groupname);
+ continue;
+ }
+
+ if (!should_show(priv->keyfile, G_KEY_FILE_DESKTOP_GROUP, priv->identity, FALSE)) {
+ g_free(groupname);
+ continue;
+ }
+
+ if (!should_show(priv->keyfile, groupname, priv->identity, should_have_target)) {
+ g_free(groupname);
+ continue;
+ }
+
+ gchar * nickalloc = g_strdup(nicks[i]);
+ g_array_append_val(priv->nicks, nickalloc);
+ g_free(groupname);
+ }
+
+ if (nicks != NULL) {
+ g_strfreev(nicks);
+ }
+
+ return;
+}
+
+/* Checks the ONLY_SHOW_IN and NOT_SHOW_IN keys for a group to
+ see if we should be showing ourselves. */
+static gboolean
+should_show (GKeyFile * keyfile, const gchar * group, const gchar * identity, gboolean should_have_target)
+{
+ if (should_have_target && g_key_file_has_key(keyfile, group, OLD_ENVIRON_KEY, NULL)) {
+ /* If we've got this key, we're going to return here and not
+ process the deprecated keys. */
+ gint j;
+ gsize num_env = 0;
+ gchar ** envs = g_key_file_get_string_list(keyfile, group, OLD_ENVIRON_KEY, &num_env, NULL);
+
+ for (j = 0; j < num_env; j++) {
+ if (g_strcmp0(envs[j], identity) == 0) {
+ break;
+ }
+ }
+
+ if (envs != NULL) {
+ g_strfreev(envs);
+ }
+
+ if (j == num_env) {
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ /* If there is a list of OnlyShowIn entries we need to check
+ to see if we're in that list. If not, we drop this nick */
+ if (g_key_file_has_key(keyfile, group, G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN, NULL)) {
+ gint j;
+ gsize num_only = 0;
+ gchar ** onlies = g_key_file_get_string_list(keyfile, group, G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN, &num_only, NULL);
+
+ for (j = 0; j < num_only; j++) {
+ if (g_strcmp0(onlies[j], identity) == 0) {
+ break;
+ }
+ }
+
+ if (onlies != NULL) {
+ g_strfreev(onlies);
+ }
+
+ if (j == num_only) {
+ return FALSE;
+ }
+ }
+
+ /* If there is a NotShowIn entry we need to make sure that we're
+ not in that list. If we are, we need to drop out. */
+ if (g_key_file_has_key(keyfile, group, G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN, NULL)) {
+ gint j;
+ gsize num_not = 0;
+ gchar ** nots = g_key_file_get_string_list(keyfile, group, G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN, &num_not, NULL);
+
+ for (j = 0; j < num_not; j++) {
+ if (g_strcmp0(nots[j], identity) == 0) {
+ break;
+ }
+ }
+
+ if (nots != NULL) {
+ g_strfreev(nots);
+ }
+
+ if (j != num_not) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/* Looks through the nicks to see if this one is in the list,
+ and thus valid to use. */
+static gboolean
+is_valid_nick (gchar ** list, const gchar * nick)
+{
+ if (*list == NULL)
+ return FALSE;
+ /* g_debug("Checking Nick: %s", list[0]); */
+ if (g_strcmp0(list[0], nick) == 0)
+ return TRUE;
+ return is_valid_nick(&list[1], nick);
+}
+
+/* API */
+
+/**
+ indicator_desktop_shortcuts_new:
+ @file: The desktop file that would be opened to
+ find the actions.
+ @identity: This is a string that represents the identity
+ that should be used in searching those actions. It
+ relates to the ShowIn and NotShownIn properties.
+
+ This function creates the basic object. It involves opening
+ the file and parsing it. It could potentially block on IO. At
+ the end of the day you'll have a fully functional object.
+
+ Return value: A new #IndicatorDesktopShortcuts object.
+*/
+IndicatorDesktopShortcuts *
+indicator_desktop_shortcuts_new (const gchar * file, const gchar * identity)
+{
+ GObject * obj = g_object_new(INDICATOR_TYPE_DESKTOP_SHORTCUTS,
+ PROP_DESKTOP_FILE_S, file,
+ PROP_IDENTITY_S, identity,
+ NULL);
+ return INDICATOR_DESKTOP_SHORTCUTS(obj);
+}
+
+/**
+ indicator_desktop_shortcuts_get_nicks:
+ @ids: The #IndicatorDesktopShortcuts object to look in
+
+ Give you the list of commands that are available for this desktop
+ file given the identity that was passed in at creation. This will
+ filter out the various items in the desktop file. These nicks can
+ then be used as keys for working with the desktop file.
+
+ Return value: A #NULL terminated list of strings. This memory
+ is managed by the @ids object.
+*/
+const gchar **
+indicator_desktop_shortcuts_get_nicks (IndicatorDesktopShortcuts * ids)
+{
+ g_return_val_if_fail(INDICATOR_IS_DESKTOP_SHORTCUTS(ids), NULL);
+ IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(ids);
+ return (const gchar **)priv->nicks->data;
+}
+
+/**
+ indicator_desktop_shortcuts_nick_get_name:
+ @ids: The #IndicatorDesktopShortcuts object to look in
+ @nick: Which command that we're referencing.
+
+ This function looks in a desktop file for a nick to find the
+ user visible name for that shortcut. The @nick parameter
+ should be gotten from #indicator_desktop_shortcuts_get_nicks
+ though it's not required that the exact memory location
+ be the same.
+
+ Return value: A user visible string for the shortcut or
+ #NULL on error.
+*/
+gchar *
+indicator_desktop_shortcuts_nick_get_name (IndicatorDesktopShortcuts * ids, const gchar * nick)
+{
+ g_return_val_if_fail(INDICATOR_IS_DESKTOP_SHORTCUTS(ids), NULL);
+ IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(ids);
+
+ g_return_val_if_fail(priv->actions != ACTIONS_NONE, NULL);
+ g_return_val_if_fail(priv->keyfile != NULL, NULL);
+ g_return_val_if_fail(is_valid_nick((gchar **)priv->nicks->data, nick), NULL);
+
+ const gchar * group_format = NULL;
+
+ switch (priv->actions) {
+ case ACTIONS_XAYATANA:
+ group_format = "%s " OLD_GROUP_SUFFIX;
+ break;
+ case ACTIONS_DESKTOP_SPEC:
+ group_format = ACTION_GROUP_PREFIX " %s";
+ break;
+ default:
+ g_assert_not_reached();
+ return NULL;
+ }
+
+ gchar * groupheader = g_strdup_printf(group_format, nick);
+ if (!g_key_file_has_group(priv->keyfile, groupheader)) {
+ g_warning("The group for nick '%s' doesn't exist anymore.", nick);
+ g_free(groupheader);
+ return NULL;
+ }
+
+ if (!g_key_file_has_key(priv->keyfile, groupheader, G_KEY_FILE_DESKTOP_KEY_NAME, NULL)) {
+ g_warning("No name available for nick '%s'", nick);
+ g_free(groupheader);
+ return NULL;
+ }
+
+ gchar * name = NULL;
+ gchar * keyvalue = g_key_file_get_string(priv->keyfile,
+ groupheader,
+ G_KEY_FILE_DESKTOP_KEY_NAME,
+ NULL);
+ gchar * localeval = g_key_file_get_locale_string(priv->keyfile,
+ groupheader,
+ G_KEY_FILE_DESKTOP_KEY_NAME,
+ NULL,
+ NULL);
+ g_free(groupheader);
+
+ if (priv->domain != NULL && g_strcmp0(keyvalue, localeval) == 0) {
+ name = g_strdup(g_dgettext(priv->domain, keyvalue));
+ g_free(localeval);
+ } else {
+ name = localeval;
+ }
+
+ g_free(keyvalue);
+
+ return name;
+}
+
+/**
+ indicator_desktop_shortcuts_nick_exec_with_context:
+ @ids: The #IndicatorDesktopShortcuts object to look in
+ @nick: Which command that we're referencing.
+ @launch_context: The #GAppLaunchContext to use for launching the shortcut
+
+ Here we take a @nick and try and execute the action that is
+ associated with it. The @nick parameter should be gotten
+ from #indicator_desktop_shortcuts_get_nicks though it's not
+ required that the exact memory location be the same.
+
+ Return value: #TRUE on success or #FALSE on error.
+*/
+gboolean
+indicator_desktop_shortcuts_nick_exec_with_context (IndicatorDesktopShortcuts * ids, const gchar * nick, GAppLaunchContext * launch_context)
+{
+ GError * error = NULL;
+
+ g_return_val_if_fail(INDICATOR_IS_DESKTOP_SHORTCUTS(ids), FALSE);
+ IndicatorDesktopShortcutsPrivate * priv = INDICATOR_DESKTOP_SHORTCUTS_GET_PRIVATE(ids);
+
+ g_return_val_if_fail(priv->actions != ACTIONS_NONE, FALSE);
+ g_return_val_if_fail(priv->keyfile != NULL, FALSE);
+ g_return_val_if_fail(is_valid_nick((gchar **)priv->nicks->data, nick), FALSE);
+
+ const gchar * group_format = NULL;
+
+ switch (priv->actions) {
+ case ACTIONS_XAYATANA:
+ group_format = "%s " OLD_GROUP_SUFFIX;
+ break;
+ case ACTIONS_DESKTOP_SPEC:
+ group_format = ACTION_GROUP_PREFIX " %s";
+ break;
+ default:
+ g_assert_not_reached();
+ return FALSE;
+ }
+
+ gchar * groupheader = g_strdup_printf(group_format, nick);
+ if (!g_key_file_has_group(priv->keyfile, groupheader)) {
+ g_warning("The group for nick '%s' doesn't exist anymore.", nick);
+ g_free(groupheader);
+ return FALSE;
+ }
+
+ if (!g_key_file_has_key(priv->keyfile, groupheader, G_KEY_FILE_DESKTOP_KEY_NAME, NULL)) {
+ g_warning("No name available for nick '%s'", nick);
+ g_free(groupheader);
+ return FALSE;
+ }
+
+ if (!g_key_file_has_key(priv->keyfile, groupheader, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL)) {
+ g_warning("No exec available for nick '%s'", nick);
+ g_free(groupheader);
+ return FALSE;
+ }
+
+ /* Grab the name and the exec entries out of our current group */
+ gchar * name = g_key_file_get_locale_string(priv->keyfile,
+ groupheader,
+ G_KEY_FILE_DESKTOP_KEY_NAME,
+ NULL,
+ NULL);
+
+ gchar * exec = g_key_file_get_locale_string(priv->keyfile,
+ groupheader,
+ G_KEY_FILE_DESKTOP_KEY_EXEC,
+ NULL,
+ NULL);
+
+ g_free(groupheader);
+
+ GAppInfoCreateFlags flags = G_APP_INFO_CREATE_NONE;
+
+ if (launch_context) {
+ flags |= G_APP_INFO_CREATE_SUPPORTS_STARTUP_NOTIFICATION;
+ }
+
+ GAppInfo * appinfo = g_app_info_create_from_commandline(exec, name, flags, &error);
+ g_free(name); g_free(exec);
+
+ if (error != NULL) {
+ g_warning("Unable to build Command line App info: %s", error->message);
+ g_error_free(error);
+ return FALSE;
+ }
+
+ if (appinfo == NULL) {
+ g_warning("Unable to build Command line App info (unknown)");
+ return FALSE;
+ }
+
+ gboolean launched = g_app_info_launch(appinfo, NULL, launch_context, &error);
+
+ if (error != NULL) {
+ g_warning("Unable to launch file from nick '%s': %s", nick, error->message);
+ g_clear_error(&error);
+ }
+
+ g_object_unref(appinfo);
+
+ return launched;
+}
+
+/**
+ indicator_desktop_shortcuts_nick_exec:
+ @ids: The #IndicatorDesktopShortcuts object to look in
+ @nick: Which command that we're referencing.
+
+ Here we take a @nick and try and execute the action that is
+ associated with it. The @nick parameter should be gotten
+ from #indicator_desktop_shortcuts_get_nicks though it's not
+ required that the exact memory location be the same.
+ This function is deprecated and shouldn't be used in newly
+ written code.
+
+ Return value: #TRUE on success or #FALSE on error.
+*/
+gboolean
+indicator_desktop_shortcuts_nick_exec (IndicatorDesktopShortcuts * ids, const gchar * nick)
+{
+ return indicator_desktop_shortcuts_nick_exec_with_context (ids, nick, NULL);
+}
diff --git a/src/indicator-desktop-shortcuts.h b/src/indicator-desktop-shortcuts.h
new file mode 100644
index 0000000..fb997ea
--- /dev/null
+++ b/src/indicator-desktop-shortcuts.h
@@ -0,0 +1,80 @@
+/*
+A small file to parse through the actions that are available
+in the desktop file and making those easily usable.
+
+Copyright 2010 Canonical Ltd.
+
+Authors:
+ Ted Gould <ted@canonical.com>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+version 3.0 as published by the Free Software Foundation.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License version 3.0 for more details.
+
+You should have received a copy of the GNU General Public
+License along with this library. If not, see
+<http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __INDICATOR_DESKTOP_SHORTCUTS_H__
+#define __INDICATOR_DESKTOP_SHORTCUTS_H__
+
+#include <gio/gio.h>
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define INDICATOR_TYPE_DESKTOP_SHORTCUTS (indicator_desktop_shortcuts_get_type ())
+#define INDICATOR_DESKTOP_SHORTCUTS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_TYPE_DESKTOP_SHORTCUTS, IndicatorDesktopShortcuts))
+#define INDICATOR_DESKTOP_SHORTCUTS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), INDICATOR_TYPE_DESKTOP_SHORTCUTS, IndicatorDesktopShortcutsClass))
+#define INDICATOR_IS_DESKTOP_SHORTCUTS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_TYPE_DESKTOP_SHORTCUTS))
+#define INDICATOR_IS_DESKTOP_SHORTCUTS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), INDICATOR_TYPE_DESKTOP_SHORTCUTS))
+#define INDICATOR_DESKTOP_SHORTCUTS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), INDICATOR_TYPE_DESKTOP_SHORTCUTS, IndicatorDesktopShortcutsClass))
+
+typedef struct _IndicatorDesktopShortcuts IndicatorDesktopShortcuts;
+typedef struct _IndicatorDesktopShortcutsClass IndicatorDesktopShortcutsClass;
+
+/**
+ IndicatorDesktopShortcutsClass:
+ @parent_class: Space for #GObjectClass
+
+ The vtable for our precious #IndicatorDesktopShortcutsClass.
+*/
+struct _IndicatorDesktopShortcutsClass {
+ GObjectClass parent_class;
+};
+
+/**
+ IndicatorDesktopShortcuts:
+ @parent: The parent data from #GObject
+
+ The public data for an instance of the class
+ #IndicatorDesktopShortcuts.
+*/
+struct _IndicatorDesktopShortcuts {
+ GObject parent;
+};
+
+GType indicator_desktop_shortcuts_get_type (void);
+IndicatorDesktopShortcuts * indicator_desktop_shortcuts_new (const gchar * file,
+ const gchar * identity);
+const gchar ** indicator_desktop_shortcuts_get_nicks (IndicatorDesktopShortcuts * ids);
+gchar * indicator_desktop_shortcuts_nick_get_name (IndicatorDesktopShortcuts * ids,
+ const gchar * nick);
+gboolean indicator_desktop_shortcuts_nick_exec_with_context (IndicatorDesktopShortcuts * ids,
+ const gchar * nick,
+ GAppLaunchContext * launch_context);
+
+GLIB_DEPRECATED_FOR(indicator_desktop_shortcuts_nick_exec_with_context)
+gboolean indicator_desktop_shortcuts_nick_exec (IndicatorDesktopShortcuts * ids,
+ const gchar * nick);
+
+G_END_DECLS
+
+#endif
diff --git a/src/messages-service.c b/src/messages-service.c
index 5054ebc..a8deb6a 100644
--- a/src/messages-service.c
+++ b/src/messages-service.c
@@ -25,6 +25,7 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include <locale.h>
#include <gio/gio.h>
#include <glib/gi18n.h>
+#include <glib-unix.h>
#include "dbus-data.h"
#include "gsettingsstrv.h"
@@ -181,6 +182,16 @@ on_name_lost (GDBusConnection *bus,
g_main_loop_quit (mainloop);
}
+static gboolean
+sig_term_handler (gpointer user_data)
+{
+ GMainLoop *mainloop = user_data;
+
+ g_main_loop_quit (mainloop);
+
+ return FALSE;
+}
+
int
main (int argc, char ** argv)
{
@@ -237,6 +248,8 @@ main (int argc, char ** argv)
g_hash_table_insert (menus, "phone", im_phone_menu_new (applications));
g_hash_table_insert (menus, "desktop", im_desktop_menu_new (applications));
+ g_unix_signal_add(SIGTERM, sig_term_handler, mainloop);
+
g_main_loop_run(mainloop);
/* Clean up */