/*
* 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"
#include "indicator-desktop-shortcuts.h"
#include
typedef ImMenuClass ImDesktopMenuClass;
struct _ImDesktopMenu
{
ImMenu parent;
gboolean status_section_visible;
GMenu *default_chat_client_section;
GMenu *default_mail_client_section;
GHashTable *source_sections;
};
G_DEFINE_TYPE (ImDesktopMenu, im_desktop_menu, IM_TYPE_MENU);
static void
menu_append_status (GMenu *menu,
const gchar *label,
const gchar *detailed_action,
const gchar *icon_name)
{
GMenuItem *item;
GIcon *icon;
icon = g_themed_icon_new (icon_name);
item = g_menu_item_new (label, detailed_action);
g_menu_item_set_icon (item, icon);
g_menu_append_item (menu, item);
g_object_unref (icon);
g_object_unref (item);
}
static void
im_desktop_menu_show_chat_section (ImDesktopMenu *menu)
{
GMenu *status_section;
if (menu->status_section_visible)
return;
status_section = g_menu_new ();
menu_append_status (status_section, _("Available"), "indicator.status::available", "user-available");
menu_append_status (status_section, _("Away"), "indicator.status::away", "user-away");
menu_append_status (status_section, _("Busy"), "indicator.status::busy", "user-busy");
menu_append_status (status_section, _("Invisible"), "indicator.status::invisible", "user-invisible");
menu_append_status (status_section, _("Offline"), "indicator.status::offline", "user-offline");
im_menu_prepend_section (IM_MENU (menu), G_MENU_MODEL (status_section));
menu->status_section_visible = TRUE;
g_object_unref (status_section);
}
static gboolean
g_app_info_is_default_for_uri_scheme (GAppInfo *info,
const gchar *uri_scheme)
{
GAppInfo *default_info;
gboolean is_default = FALSE;
default_info = g_app_info_get_default_for_uri_scheme (uri_scheme);
if (default_info)
{
is_default = g_app_info_equal (info, default_info);
g_object_unref (default_info);
}
return is_default;
}
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;
GMenuItem *item;
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-ayatana-type", "s", "org.ayatana.indicator.application");
icon = g_icon_serialize (g_app_info_get_icon (G_APP_INFO (app_info)));
if (icon)
{
g_menu_item_set_attribute_value (item, "icon", icon);
g_variant_unref (icon);
}
g_menu_append_item (app_section, item);
g_object_unref (item);
}
/* application actions */
{
const gchar * filename = NULL;
IndicatorDesktopShortcuts * shortcuts = NULL;
const gchar ** nicks = {NULL};
filename = g_desktop_app_info_get_filename(app_info);
if (filename != NULL)
shortcuts = indicator_desktop_shortcuts_new(filename, "Messaging Menu");
if (shortcuts != NULL)
for (nicks = indicator_desktop_shortcuts_get_nicks(shortcuts); *nicks; nicks++)
{
GMenuItem *item;
gchar *label;
label = indicator_desktop_shortcuts_nick_get_name (shortcuts, *nicks);
item = g_menu_item_new (label, *nicks);
g_menu_item_set_attribute (item, "x-ayatana-type", "s", "org.ayatana.indicator.application");
g_menu_append_item (app_section, item);
g_free (label);
g_object_unref (item);
}
g_clear_object(&shortcuts);
}
if (g_desktop_app_info_get_boolean (app_info, "X-MessagingMenu-UsesChatSection"))
im_desktop_menu_show_chat_section (menu);
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));
item = g_menu_item_new_section (NULL, G_MENU_MODEL (section));
namespace = g_strconcat ("indicator.", app_id, NULL);
g_menu_item_set_attribute (item, "action-namespace", "s", namespace);
/* The default chat client is not stored anywhere, so let's hardcode empathy. */
if (g_str_equal (app_id, "empathy"))
{
g_menu_remove_all (menu->default_chat_client_section);
g_menu_append_item (menu->default_chat_client_section, item);
}
else if (g_app_info_is_default_for_uri_scheme (G_APP_INFO (app_info), "mailto"))
{
g_menu_remove_all (menu->default_mail_client_section);
g_menu_append_item (menu->default_mail_client_section, item);
}
else
{
g_menu_item_set_attribute (item, "x-messaging-menu-sort-string", "s",
g_app_info_get_name(G_APP_INFO(app_info)));
im_menu_insert_item_sorted (IM_MENU (menu), item, menu->status_section_visible ? 3 : 2, -1);
}
g_hash_table_insert (menu->source_sections, g_strdup (app_id), source_section);
g_free (namespace);
g_object_unref (item);
g_object_unref (section);
g_object_unref (app_section);
}
static void
im_desktop_menu_source_section_insert_source (GMenu *source_section,
const gchar *source_id,
const gchar *label,
GVariant *serialized_icon,
gint pos)
{
GMenuItem *item;
gchar *action;
action = g_strconcat ("src.", source_id, NULL);
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-ayatana-type", "s", "org.ayatana.indicator.messages.source");
if (serialized_icon)
g_menu_item_set_attribute_value (item, "icon", serialized_icon);
if (pos >= 0)
g_menu_insert_item (source_section, pos, item);
else
g_menu_append_item (source_section, item);
g_free (action);
g_object_unref (item);
}
static gint
im_desktop_menu_source_section_find_source (GMenu *source_section,
const gchar *source_id)
{
gint n_items;
gchar *action;
gint i;
n_items = g_menu_model_get_n_items (G_MENU_MODEL (source_section));
action = g_strconcat ("src.", source_id, NULL);
for (i = 0; i < n_items; i++)
{
gchar *item_action;
if (g_menu_model_get_item_attribute (G_MENU_MODEL (source_section), i, "action", "s", &item_action))
{
gboolean equal;
equal = g_str_equal (action, item_action);
g_free (item_action);
if (equal)
break;
}
}
g_free (action);
return i < n_items ? i : -1;
}
static void
im_desktop_menu_source_added (ImApplicationList *applist,
const gchar *app_id,
const gchar *source_id,
const gchar *label,
GVariant *serialized_icon,
gboolean visible,
gpointer user_data)
{
ImDesktopMenu *menu = user_data;
GMenu *source_section;
source_section = g_hash_table_lookup (menu->source_sections, app_id);
g_return_if_fail (source_section != NULL);
if (visible)
im_desktop_menu_source_section_insert_source (source_section, source_id, label, serialized_icon, -1);
}
static void
im_desktop_menu_source_removed (ImApplicationList *applist,
const gchar *app_id,
const gchar *source_id,
gpointer user_data)
{
ImDesktopMenu *menu = user_data;
GMenu *source_section;
gint pos;
source_section = g_hash_table_lookup (menu->source_sections, app_id);
g_return_if_fail (source_section != NULL);
pos = im_desktop_menu_source_section_find_source (source_section, source_id);
if (pos >= 0)
g_menu_remove (source_section, pos);
}
static void
im_desktop_menu_source_changed (ImApplicationList *applist,
const gchar *app_id,
const gchar *source_id,
const gchar *label,
GVariant *serialized_icon,
gboolean visible,
gpointer user_data)
{
ImDesktopMenu *menu = user_data;
GMenu *section;
gint pos;
section = g_hash_table_lookup (menu->source_sections, app_id);
g_return_if_fail (section != NULL);
pos = im_desktop_menu_source_section_find_source (section, source_id);
if (pos >= 0)
g_menu_remove (section, pos);
if (visible)
im_desktop_menu_source_section_insert_source (section, source_id, label, serialized_icon, pos);
}
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_app_stopped (ImApplicationList *applist,
const gchar *app_id,
gpointer user_data)
{
ImDesktopMenu *menu = user_data;
GMenu *section;
section = g_hash_table_lookup (menu->source_sections, app_id);
g_return_if_fail (section != NULL);
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;
menu->default_chat_client_section = g_menu_new ();
im_menu_append_section (IM_MENU (menu), G_MENU_MODEL (menu->default_chat_client_section));
menu->default_mail_client_section = g_menu_new ();
im_menu_append_section (IM_MENU (menu), G_MENU_MODEL (menu->default_mail_client_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, "source-added", G_CALLBACK (im_desktop_menu_source_added), menu);
g_signal_connect (applist, "source-removed", G_CALLBACK (im_desktop_menu_source_removed), menu);
g_signal_connect (applist, "source-changed", G_CALLBACK (im_desktop_menu_source_changed), menu);
g_signal_connect (applist, "remove-all", G_CALLBACK (im_desktop_menu_remove_all), menu);
g_signal_connect (applist, "app-stopped", G_CALLBACK (im_desktop_menu_app_stopped), 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);
}