aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libmessaging-menu/messaging-menu.c11
-rw-r--r--src/app-section.c51
-rw-r--r--src/app-section.h3
-rw-r--r--src/ido-menu-item.c34
-rw-r--r--src/indicator-messages.c39
-rw-r--r--src/messages-service.c215
-rw-r--r--src/messages-service.xml1
7 files changed, 278 insertions, 76 deletions
diff --git a/libmessaging-menu/messaging-menu.c b/libmessaging-menu/messaging-menu.c
index 8066b35..8a95b0a 100644
--- a/libmessaging-menu/messaging-menu.c
+++ b/libmessaging-menu/messaging-menu.c
@@ -102,6 +102,7 @@ struct _MessagingMenuApp
GDesktopAppInfo *appinfo;
int registered; /* -1 for unknown */
MessagingMenuStatus status;
+ gboolean status_set;
GSimpleActionGroup *source_actions;
GMenu *menu;
@@ -370,7 +371,8 @@ created_messages_service (GObject *source_object,
messaging_menu_app_register (app);
else if (app->registered == FALSE)
messaging_menu_app_unregister (app);
- messaging_menu_app_set_status (app, app->status);
+ if (app->status_set)
+ messaging_menu_app_set_status (app, app->status);
}
static void
@@ -410,7 +412,7 @@ static void
messaging_menu_app_init (MessagingMenuApp *app)
{
app->registered = -1;
- app->status = MESSAGING_MENU_STATUS_OFFLINE;
+ app->status_set = FALSE;
app->cancellable = g_cancellable_new ();
@@ -538,12 +540,14 @@ messaging_menu_app_set_status (MessagingMenuApp *app,
status <= MESSAGING_MENU_STATUS_OFFLINE);
app->status = status;
+ app->status_set = TRUE;
/* state will be synced right after connecting to the service */
if (!app->messages_service)
return;
indicator_messages_service_call_set_status (app->messages_service,
+ g_app_info_get_id (G_APP_INFO (app->appinfo)),
status_ids [status],
app->cancellable,
NULL, NULL);
@@ -577,8 +581,7 @@ global_status_changed (IndicatorMessagesService *service,
status = status_from_string (status_str);
g_return_if_fail (status >= 0);
- app->status = (MessagingMenuStatus)status;
- g_signal_emit (app, signals[STATUS_CHANGED], 0, app->status);
+ g_signal_emit (app, signals[STATUS_CHANGED], 0, status);
}
static void
diff --git a/src/app-section.c b/src/app-section.c
index 523e249..6aac52a 100644
--- a/src/app-section.c
+++ b/src/app-section.c
@@ -50,6 +50,7 @@ struct _AppSectionPrivate
gboolean draws_attention;
gboolean uses_chat_status;
+ gchar *chat_status;
guint name_watch_id;
};
@@ -60,6 +61,7 @@ enum {
PROP_ACTIONS,
PROP_DRAWS_ATTENTION,
PROP_USES_CHAT_STATUS,
+ PROP_CHAT_STATUS,
NUM_PROPERTIES
};
@@ -78,6 +80,7 @@ static void app_section_set_property (GObject *object,
const GValue *value,
GParamSpec *pspec);
static void app_section_dispose (GObject *object);
+static void app_section_finalize (GObject *object);
static void activate_cb (GSimpleAction *action,
GVariant *param,
gpointer userdata);
@@ -118,6 +121,7 @@ app_section_class_init (AppSectionClass *klass)
object_class->get_property = app_section_get_property;
object_class->set_property = app_section_set_property;
object_class->dispose = app_section_dispose;
+ object_class->finalize = app_section_finalize;
properties[PROP_APPINFO] = g_param_spec_object ("app-info",
"AppInfo",
@@ -143,6 +147,13 @@ app_section_class_init (AppSectionClass *klass)
FALSE,
G_PARAM_READABLE);
+ properties[PROP_CHAT_STATUS] = g_param_spec_string ("chat-status",
+ "Chat status",
+ "Current chat status of the application",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS);
+
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
destroy_signal = g_signal_new ("destroy",
@@ -199,6 +210,10 @@ app_section_get_property (GObject *object,
g_value_set_boolean (value, app_section_get_uses_chat_status (self));
break;
+ case PROP_CHAT_STATUS:
+ g_value_set_string (value, app_section_get_status (self));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
@@ -218,6 +233,10 @@ app_section_set_property (GObject *object,
app_section_set_app_info (self, g_value_get_object (value));
break;
+ case PROP_CHAT_STATUS:
+ app_section_set_status (self, g_value_get_string (value));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
@@ -261,6 +280,16 @@ app_section_dispose (GObject *object)
G_OBJECT_CLASS (app_section_parent_class)->dispose (object);
}
+static void
+app_section_finalize (GObject *object)
+{
+ AppSection * self = APP_SECTION(object);
+
+ g_free (self->priv->chat_status);
+
+ G_OBJECT_CLASS (app_section_parent_class)->dispose (object);
+}
+
/* Respond to one of the shortcuts getting clicked on. */
static void
nick_activate_cb (GSimpleAction *action,
@@ -668,10 +697,12 @@ app_section_unset_object_path (AppSection *self)
}
priv->draws_attention = FALSE;
+ g_clear_pointer (&priv->chat_status, g_free);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ACTIONS]);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DRAWS_ATTENTION]);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_USES_CHAT_STATUS]);
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CHAT_STATUS]);
g_action_group_change_action_state (G_ACTION_GROUP (priv->static_shortcuts),
"launch", g_variant_new_boolean (FALSE));
@@ -763,3 +794,23 @@ app_section_get_uses_chat_status (AppSection *self)
return priv->uses_chat_status;
}
+
+const gchar *
+app_section_get_status (AppSection *self)
+{
+ AppSectionPrivate * priv = self->priv;
+
+ return priv->chat_status;
+}
+
+void
+app_section_set_status (AppSection *self,
+ const gchar *status)
+{
+ AppSectionPrivate * priv = self->priv;
+
+ g_free (priv->chat_status);
+ priv->chat_status = g_strdup (status);
+
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CHAT_STATUS]);
+}
diff --git a/src/app-section.h b/src/app-section.h
index 09697c5..7c39e8e 100644
--- a/src/app-section.h
+++ b/src/app-section.h
@@ -63,6 +63,9 @@ void app_section_set_object_path (AppSection *self,
const gchar *object_path);
void app_section_unset_object_path (AppSection *self);
gboolean app_section_get_uses_chat_status (AppSection *self);
+const gchar * app_section_get_status (AppSection *self);
+void app_section_set_status (AppSection *self,
+ const gchar *status);
G_END_DECLS
diff --git a/src/ido-menu-item.c b/src/ido-menu-item.c
index 6b19d2a..32044ff 100644
--- a/src/ido-menu-item.c
+++ b/src/ido-menu-item.c
@@ -99,8 +99,37 @@ ido_menu_item_set_state (IdoMenuItem *self,
if (priv->target)
{
ido_menu_item_set_has_indicator (self, TRUE);
+ ido_menu_item_set_active (self, FALSE);
gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (self), TRUE);
- ido_menu_item_set_active (self, g_variant_equal (priv->target, state));
+ gtk_check_menu_item_set_inconsistent (GTK_CHECK_MENU_ITEM (self), FALSE);
+
+ if (g_variant_is_of_type (state, G_VARIANT_TYPE_STRING))
+ {
+ ido_menu_item_set_active (self, g_variant_equal (priv->target, state));
+ }
+ else if (g_variant_is_of_type (state, G_VARIANT_TYPE ("as")) &&
+ g_variant_is_of_type (priv->target, G_VARIANT_TYPE_STRING))
+ {
+ const gchar *target_str;
+ const gchar **state_strs;
+ const gchar **it;
+
+ target_str = g_variant_get_string (priv->target, NULL);
+ state_strs = g_variant_get_strv (state, NULL);
+
+ it = state_strs;
+ while (*it != NULL && !g_str_equal (*it, target_str))
+ it++;
+
+ if (*it != NULL)
+ {
+ ido_menu_item_set_active (self, TRUE);
+ gtk_check_menu_item_set_inconsistent (GTK_CHECK_MENU_ITEM (self),
+ g_strv_length ((gchar **)state_strs) > 1);
+ }
+
+ g_free (state_strs);
+ }
}
else if (g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN))
{
@@ -258,7 +287,8 @@ ido_menu_item_activate (GtkMenuItem *item)
if (!priv->in_set_active && priv->action && priv->action_group)
g_action_group_activate_action (priv->action_group, priv->action, priv->target);
- GTK_MENU_ITEM_CLASS (ido_menu_item_parent_class)->activate (item);
+ if (priv->in_set_active)
+ GTK_MENU_ITEM_CLASS (ido_menu_item_parent_class)->activate (item);
}
static void
diff --git a/src/indicator-messages.c b/src/indicator-messages.c
index 2647a76..db4f634 100644
--- a/src/indicator-messages.c
+++ b/src/indicator-messages.c
@@ -87,6 +87,9 @@ static void menu_items_changed (GMenuModel *menu,
gint removed,
gint added,
gpointer user_data);
+static void messages_action_added (GActionGroup *action_group,
+ gchar *action_name,
+ gpointer user_data);
static void messages_state_changed (GActionGroup *action_group,
gchar *action_name,
GVariant *value,
@@ -174,6 +177,7 @@ static void service_connection_changed (IndicatorServiceManager *sm,
GError *error = NULL;
if (self->actions != NULL) {
+ g_signal_handlers_disconnect_by_func (self->actions, messages_action_added, self);
g_signal_handlers_disconnect_by_func (self->actions, messages_state_changed, self);
g_clear_object (&self->actions);
}
@@ -199,6 +203,8 @@ static void service_connection_changed (IndicatorServiceManager *sm,
gtk_widget_insert_action_group (self->gtkmenu,
get_name_hint (INDICATOR_OBJECT (self)),
self->actions);
+ g_signal_connect (self->actions, "action-added::messages",
+ G_CALLBACK (messages_action_added), self);
g_signal_connect (self->actions, "action-state-changed::messages",
G_CALLBACK (messages_state_changed), self);
@@ -331,6 +337,32 @@ menu_items_changed (GMenuModel *menu,
}
static void
+indicator_messages_update_icon (IndicatorMessages *self,
+ GVariant *state)
+{
+ g_return_if_fail (g_variant_is_of_type (state, G_VARIANT_TYPE_STRING));
+
+ gtk_image_set_from_icon_name (GTK_IMAGE (self->image),
+ g_variant_get_string (state, NULL),
+ GTK_ICON_SIZE_MENU);
+
+}
+
+static void
+messages_action_added (GActionGroup *action_group,
+ gchar *action_name,
+ gpointer user_data)
+{
+ IndicatorMessages *self = user_data;
+ GVariant *state;
+
+ state = g_action_group_get_action_state (action_group, "messages");
+ indicator_messages_update_icon (self, state);
+
+ g_variant_unref (state);
+}
+
+static void
messages_state_changed (GActionGroup *action_group,
gchar *action_name,
GVariant *value,
@@ -338,10 +370,5 @@ messages_state_changed (GActionGroup *action_group,
{
IndicatorMessages *self = user_data;
- g_return_if_fail (g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN));
-
- if (g_variant_get_boolean (value))
- gtk_image_set_from_icon_name (GTK_IMAGE (self->image), "indicator-messages-new", GTK_ICON_SIZE_MENU);
- else
- gtk_image_set_from_icon_name (GTK_IMAGE (self->image), "indicator-messages", GTK_ICON_SIZE_MENU);
+ indicator_messages_update_icon (self, value);
}
diff --git a/src/messages-service.c b/src/messages-service.c
index fd0bdcb..73f3d35 100644
--- a/src/messages-service.c
+++ b/src/messages-service.c
@@ -35,15 +35,63 @@ with this program. If not, see <http://www.gnu.org/licenses/>.
#include "gmenuutils.h"
#include "indicator-messages-service.h"
+#define NUM_STATUSES 5
+
static GHashTable *applications;
-IndicatorMessagesService *messages_service;
+static IndicatorMessagesService *messages_service;
static GSimpleActionGroup *actions;
static GActionMuxer *action_muxer;
static GMenu *toplevel_menu;
static GMenu *menu;
static GMenuModel *chat_section;
static GSettings *settings;
+static gboolean draws_attention;
+static const gchar *global_status[6]; /* max 5: available, away, busy, invisible, offline */
+
+static gchar *
+indicator_messages_get_icon_name ()
+{
+ GString *name;
+ GIcon *icon;
+ gchar *iconstr;
+
+ name = g_string_new ("indicator-messages");
+
+ if (global_status[0] != NULL)
+ {
+ if (global_status[1] != NULL)
+ g_string_append (name, "-mixed");
+ else
+ g_string_append_printf (name, "-%s", global_status[0]);
+ }
+
+ if (draws_attention)
+ g_string_append (name, "-new");
+
+ icon = g_themed_icon_new (name->str);
+ iconstr = g_icon_to_string (icon);
+
+ g_object_unref (icon);
+ g_string_free (name, TRUE);
+
+ return iconstr;
+}
+
+static void
+indicator_messages_update_icon ()
+{
+ GSimpleAction *messages;
+ gchar *icon;
+
+ messages = G_SIMPLE_ACTION (g_simple_action_group_lookup (actions, "messages"));
+ g_return_if_fail (messages != NULL);
+
+ icon = indicator_messages_get_icon_name ();
+ g_simple_action_set_state (messages, g_variant_new_string (icon));
+
+ g_free (icon);
+}
static gchar *
g_app_info_get_simple_id (GAppInfo *appinfo)
@@ -91,18 +139,16 @@ draws_attention_changed (GObject *object,
GParamSpec *pspec,
gpointer user_data)
{
- GSimpleAction *messages;
GSimpleAction *clear;
- gboolean attention;
- messages = G_SIMPLE_ACTION (g_simple_action_group_lookup (actions, "messages"));
clear = G_SIMPLE_ACTION (g_simple_action_group_lookup (actions, "clear"));
- g_return_if_fail (messages != NULL && clear != NULL);
+ g_return_if_fail (clear != NULL);
+
+ draws_attention = g_hash_table_find (applications, app_section_draws_attention, NULL) != NULL;
- attention = g_hash_table_find (applications, app_section_draws_attention, NULL) != NULL;
+ g_simple_action_set_enabled (clear, draws_attention);
- g_simple_action_set_state (messages, g_variant_new_boolean (attention));
- g_simple_action_set_enabled (clear, attention);
+ indicator_messages_update_icon ();
}
static gboolean
@@ -134,6 +180,8 @@ update_chat_section ()
if (first_section != NULL)
g_object_unref (first_section);
+
+ indicator_messages_update_icon ();
}
static void
@@ -144,6 +192,61 @@ uses_chat_status_changed (GObject *object,
update_chat_section ();
}
+static gboolean
+strv_contains (const gchar **strv,
+ const gchar *needle)
+{
+ const gchar **it;
+
+ it = strv;
+ while (*it != NULL && !g_str_equal (*it, needle))
+ it++;
+
+ return *it != NULL;
+}
+
+static void
+update_chat_status ()
+{
+ GHashTableIter iter;
+ AppSection *section;
+ int pos;
+ GAction *status;
+
+ for (pos = 0; pos < G_N_ELEMENTS (global_status); pos++)
+ global_status[pos] = NULL;
+
+ pos = 0;
+ g_hash_table_iter_init (&iter, applications);
+ while (g_hash_table_iter_next (&iter, NULL, (gpointer) &section) &&
+ pos < G_N_ELEMENTS (global_status))
+ {
+ const gchar *status_str = NULL;
+
+ status_str = app_section_get_status (section);
+ if (status_str != NULL && !strv_contains (global_status, status_str))
+ global_status[pos++] = status_str;
+ }
+
+ if (pos == 0)
+ global_status[0] = "offline";
+
+ status = g_simple_action_group_lookup (actions, "status");
+ g_return_if_fail (status != NULL);
+
+ g_simple_action_set_state (G_SIMPLE_ACTION (status), g_variant_new_strv (global_status, -1));
+
+ indicator_messages_update_icon ();
+}
+
+static void
+chat_status_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ update_chat_status ();
+}
+
static void
remove_section (AppSection *section,
const gchar *id)
@@ -156,6 +259,7 @@ remove_section (AppSection *section,
g_signal_handlers_disconnect_by_func (section, actions_changed, NULL);
g_signal_handlers_disconnect_by_func (section, draws_attention_changed, NULL);
g_signal_handlers_disconnect_by_func (section, uses_chat_status_changed, NULL);
+ g_signal_handlers_disconnect_by_func (section, chat_status_changed, NULL);
g_signal_handlers_disconnect_by_func (section, remove_section, NULL);
g_hash_table_remove (applications, id);
@@ -165,6 +269,7 @@ remove_section (AppSection *section,
g_menu_remove (toplevel_menu, 0);
}
+ update_chat_status ();
update_chat_section ();
}
@@ -197,6 +302,8 @@ add_application (const gchar *desktop_id)
G_CALLBACK (draws_attention_changed), NULL);
g_signal_connect (section, "notify::uses-chat-status",
G_CALLBACK (uses_chat_status_changed), NULL);
+ g_signal_connect (section, "notify::chat-status",
+ G_CALLBACK (chat_status_changed), NULL);
g_signal_connect_data (section, "destroy",
G_CALLBACK (remove_section),
g_strdup (id),
@@ -212,21 +319,13 @@ add_application (const gchar *desktop_id)
if (g_menu_model_get_n_items (G_MENU_MODEL (toplevel_menu)) == 0) {
GMenuItem *header;
- GIcon *icon;
- gchar *iconstr;
-
- icon = g_themed_icon_new ("indicator-messages");
- iconstr = g_icon_to_string (icon);
header = g_menu_item_new (NULL, "messages");
g_menu_item_set_submenu (header, G_MENU_MODEL (menu));
- g_menu_item_set_attribute (header, "x-canonical-icon", "s", iconstr);
g_menu_item_set_attribute (header, "x-canonical-accessible-description", "s", _("Messages"));
g_menu_append_item (toplevel_menu, header);
g_object_unref (header);
- g_free (iconstr);
- g_object_unref (icon);
}
g_free (id);
@@ -322,48 +421,15 @@ clear_action_activate (GSimpleAction *simple,
}
static void
-radio_item_activate (GSimpleAction *action,
- GVariant *parameter,
- gpointer user_data)
-{
- g_action_change_state (G_ACTION (action), parameter);
-}
-
-static gboolean
-g_action_state_equal (GAction *action,
- GVariant *value)
-{
- GVariant *state;
- gboolean eq;
-
- state = g_action_get_state (action);
- g_return_val_if_fail (state != NULL, FALSE);
-
- eq = g_variant_equal (state, value);
-
- g_variant_unref (state);
- return eq;
-}
-
-static void
-change_status_action (GSimpleAction *action,
- GVariant *value,
- gpointer user_data)
+status_action_activate (GSimpleAction *action,
+ GVariant *parameter,
+ gpointer user_data)
{
const gchar *status;
- g_variant_get (value, "&s", &status);
+ status = g_variant_get_string (parameter, NULL);
- g_return_if_fail (g_str_equal (status, "available") ||
- g_str_equal (status, "away")||
- g_str_equal (status, "busy") ||
- g_str_equal (status, "invisible") ||
- g_str_equal (status, "offline"));
-
- if (!g_action_state_equal (G_ACTION (action), value)) {
- g_simple_action_set_state (action, value);
- indicator_messages_service_emit_status_changed (messages_service, status);
- }
+ indicator_messages_service_emit_status_changed (messages_service, status);
}
static void
@@ -405,17 +471,35 @@ unregister_application (IndicatorMessagesService *service,
static void
set_status (IndicatorMessagesService *service,
GDBusMethodInvocation *invocation,
+ const gchar *desktop_id,
const gchar *status_str,
gpointer user_data)
{
- GAction *status;
+ GDesktopAppInfo *appinfo;
+ gchar *id;
+ AppSection *section;
- status = g_simple_action_group_lookup (actions, "status");
- g_return_if_fail (status != NULL);
+ g_return_if_fail (g_str_equal (status_str, "available") ||
+ g_str_equal (status_str, "away")||
+ g_str_equal (status_str, "busy") ||
+ g_str_equal (status_str, "invisible") ||
+ g_str_equal (status_str, "offline"));
- g_action_change_state (status, g_variant_new_string (status_str));
+ appinfo = g_desktop_app_info_new (desktop_id);
+ if (!appinfo) {
+ g_warning ("could not set status for '%s', there's no desktop file with that id", desktop_id);
+ return;
+ }
+
+ id = g_app_info_get_simple_id (G_APP_INFO (appinfo));
+ section = g_hash_table_lookup (applications, id);
+ if (section != NULL)
+ app_section_set_status (section, status_str);
indicator_messages_service_complete_set_status (service, invocation);
+
+ g_free (id);
+ g_object_unref (appinfo);
}
static GSimpleActionGroup *
@@ -425,17 +509,19 @@ create_action_group (void)
GSimpleAction *messages;
GSimpleAction *clear;
GSimpleAction *status;
+ const gchar *default_status[] = { "offline", NULL };
+ gchar *icon;
actions = g_simple_action_group_new ();
- /* state of the messages action mirrors "draws-attention" */
- messages = g_simple_action_new_stateful ("messages", G_VARIANT_TYPE ("b"),
- g_variant_new_boolean (FALSE));
+ /* state of the messages action is its icon name */
+ icon = indicator_messages_get_icon_name ();
+ messages = g_simple_action_new_stateful ("messages", G_VARIANT_TYPE ("s"),
+ g_variant_new_string (icon));
status = g_simple_action_new_stateful ("status", G_VARIANT_TYPE ("s"),
- g_variant_new ("s", "offline"));
- g_signal_connect (status, "activate", G_CALLBACK (radio_item_activate), NULL);
- g_signal_connect (status, "change-state", G_CALLBACK (change_status_action), NULL);
+ g_variant_new_strv (default_status, -1));
+ g_signal_connect (status, "activate", G_CALLBACK (status_action_activate), NULL);
clear = g_simple_action_new ("clear", NULL);
g_simple_action_set_enabled (clear, FALSE);
@@ -445,6 +531,7 @@ create_action_group (void)
g_simple_action_group_insert (actions, G_ACTION (status));
g_simple_action_group_insert (actions, G_ACTION (clear));
+ g_free (icon);
return actions;
}
diff --git a/src/messages-service.xml b/src/messages-service.xml
index edd47c7..00ae154 100644
--- a/src/messages-service.xml
+++ b/src/messages-service.xml
@@ -12,6 +12,7 @@
</method>
<method name="SetStatus">
+ <arg type="s" name="desktop_id" direction="in" />
<arg type="s" name="status" direction="in" />
</method>