diff options
Diffstat (limited to 'src/messages-service.c')
-rw-r--r-- | src/messages-service.c | 1727 |
1 files changed, 335 insertions, 1392 deletions
diff --git a/src/messages-service.c b/src/messages-service.c index 078cee3..7654e1f 100644 --- a/src/messages-service.c +++ b/src/messages-service.c @@ -20,1512 +20,461 @@ You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <string.h> -#include <locale.h> -#include <libintl.h> #include <config.h> -#include <pango/pango-utils.h> -#include <libindicate/listener.h> +#include <locale.h> #include <libindicator/indicator-service.h> #include <gio/gio.h> +#include <gio/gdesktopappinfo.h> #include <glib/gi18n.h> -#include <libdbusmenu-glib/client.h> -#include <libdbusmenu-glib/server.h> - -#include "im-menu-item.h" -#include "app-menu-item.h" -#include "launcher-menu-item.h" +#include "app-section.h" #include "dbus-data.h" -#include "dirs.h" -#include "messages-service-dbus.h" -#include "seen-db.h" -#include "status-items.h" - -static IndicatorService * service = NULL; -static IndicateListener * listener = NULL; -static GList * serverList = NULL; -static GList * launcherList = NULL; - -static DbusmenuMenuitem * root_menuitem = NULL; -static DbusmenuMenuitem * status_separator = NULL; -static DbusmenuMenuitem * clear_attention = NULL; -static GMainLoop * mainloop = NULL; - -static MessageServiceDbus * dbus_interface = NULL; - -#define DESKTOP_FILE_GROUP "Messaging Menu" -#define DESKTOP_FILE_KEY_DESKTOP "DesktopFile" - -static void server_shortcut_added (AppMenuItem * appitem, DbusmenuMenuitem * mi, gpointer data); -static void server_shortcut_removed (AppMenuItem * appitem, DbusmenuMenuitem * mi, gpointer data); -static void server_count_changed (AppMenuItem * appitem, guint count, gpointer data); -static void server_name_changed (AppMenuItem * appitem, gchar * name, gpointer data); -static void im_time_changed (ImMenuItem * imitem, glong seconds, gpointer data); -static void resort_menu (DbusmenuMenuitem * menushell); -static void indicator_removed (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gpointer data); -static void check_eclipses (AppMenuItem * ai); -static void remove_eclipses (AppMenuItem * ai); -static gboolean build_launcher (gpointer data); -static gboolean build_launcher_keyfile (gpointer data); -static void build_launcher_core (const gchar * desktop); -static gboolean build_launchers (gpointer data); -static gboolean blacklist_init (gpointer data); -static gboolean blacklist_add (gpointer data); -static gchar * desktop_file_from_keyfile (const gchar * definition_file); -static gboolean blacklist_keyfile_add (gpointer udata); -static void blacklist_add_core (gchar * desktop, gchar * definition); -static gboolean blacklist_remove (gpointer data); -static void blacklist_dir_changed (GFileMonitor * monitor, GFile * file, GFile * other_file, GFileMonitorEvent event_type, gpointer user_data); -static void app_dir_changed (GFileMonitor * monitor, GFile * file, GFile * other_file, GFileMonitorEvent event_type, gpointer user_data); -static gboolean destroy_launcher (gpointer data); -static void check_hidden (void); +#include "gactionmuxer.h" +#include "gsettingsstrv.h" +#include "gmenuutils.h" +#include "indicator-messages-service.h" +static GHashTable *applications; -/* - * Server List - */ - -typedef struct _serverList_t serverList_t; -struct _serverList_t { - IndicateListenerServer * server; - AppMenuItem * menuitem; - DbusmenuMenuitem * separator; - gboolean attention; - guint count; - GList * imList; -}; +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 gint -serverList_equal (gconstpointer a, gconstpointer b) -{ - serverList_t * pa, * pb; - pa = (serverList_t *)a; - pb = (serverList_t *)b; +static gchar * +g_app_info_get_simple_id (GAppInfo *appinfo) +{ + const gchar *id; - const gchar * pan = INDICATE_LISTENER_SERVER_DBUS_NAME(pa->server); - const gchar * pbn = INDICATE_LISTENER_SERVER_DBUS_NAME(pb->server); - const gchar * pap = indicate_listener_server_get_dbuspath(pa->server); - const gchar * pbp = indicate_listener_server_get_dbuspath(pb->server); + id = g_app_info_get_id (appinfo); + if (!id) + return NULL; - if (g_strcmp0(pan, pbn) == 0) - return g_strcmp0(pap, pbp); + if (g_str_has_suffix (id, ".desktop")) + return g_strndup (id, strlen (id) - 8); else - return 1; + return g_strdup (id); } -static gint -serverList_sort (gconstpointer a, gconstpointer b) +static void +actions_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) { - serverList_t * pa, * pb; + AppSection *section = APP_SECTION (object); + gchar *id; + GActionGroup *actions; - pa = (serverList_t *)a; - pb = (serverList_t *)b; + id = g_app_info_get_simple_id (app_section_get_app_info (section)); + actions = app_section_get_actions (section); - const gchar * pan = app_menu_item_get_name(pa->menuitem); - const gchar * pbn = app_menu_item_get_name(pb->menuitem); - - return g_strcmp0(pan, pbn); + g_action_muxer_insert (action_muxer, id, actions); + g_free (id); } -/* - * Item List - */ - -typedef struct _imList_t imList_t; -struct _imList_t { - IndicateListenerServer * server; - IndicateListenerIndicator * indicator; - DbusmenuMenuitem * menuitem; - gulong timechange_cb; - gulong attentionchange_cb; -}; static gboolean -imList_equal (gconstpointer a, gconstpointer b) -{ - imList_t * pa, * pb; - - pa = (imList_t *)a; - pb = (imList_t *)b; - - const gchar * pas = INDICATE_LISTENER_SERVER_DBUS_NAME(pa->server); - const gchar * pbs = INDICATE_LISTENER_SERVER_DBUS_NAME(pb->server); - - guint pai = INDICATE_LISTENER_INDICATOR_ID(pa->indicator); - guint pbi = INDICATE_LISTENER_INDICATOR_ID(pb->indicator); - - g_debug("\tComparing (%s %d) to (%s %d)", pas, pai, pbs, pbi); - - return !((!g_strcmp0(pas, pbs)) && (pai == pbi)); -} - -static gint -imList_sort (gconstpointer a, gconstpointer b) -{ - imList_t * pa, * pb; - - pa = (imList_t *)a; - pb = (imList_t *)b; - - return (gint)(im_menu_item_get_seconds(IM_MENU_ITEM(pb->menuitem)) - im_menu_item_get_seconds(IM_MENU_ITEM(pa->menuitem))); -} - -/* - * Launcher List - */ - -typedef struct _launcherList_t launcherList_t; -struct _launcherList_t { - LauncherMenuItem * menuitem; - DbusmenuMenuitem * separator; - GList * appdiritems; -}; - -static gint -launcherList_sort (gconstpointer a, gconstpointer b) +app_section_draws_attention (gpointer key, + gpointer value, + gpointer user_data) { - launcherList_t * pa, * pb; - - pa = (launcherList_t *)a; - pb = (launcherList_t *)b; - - const gchar * pan = launcher_menu_item_get_name(pa->menuitem); - const gchar * pbn = launcher_menu_item_get_name(pb->menuitem); - - return g_strcmp0(pan, pbn); + AppSection *section = value; + return app_section_get_draws_attention (section); } static void -launcherList_count_helper (gpointer data, gpointer user_data) +draws_attention_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) { - guint * count = (guint *)user_data; - launcherList_t * li = (launcherList_t *)data; - - if (!launcher_menu_item_get_eclipsed(li->menuitem)) { - *count = *count + 1; - } - - return; -} - -static guint -launcherList_count (void) -{ - guint count = 0; - - g_list_foreach(launcherList, launcherList_count_helper, &count); - - return count; -} - -/* - * Black List - */ - -static GHashTable * blacklist = NULL; -static GFileMonitor * blacklistdirmon = NULL; - -/* Initialize the black list and start to setup - handlers for it. */ -static gboolean -blacklist_init (gpointer data) -{ - blacklist = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, g_free); - - gchar * blacklistdir = g_build_filename(g_get_user_config_dir(), USER_BLACKLIST_DIR, NULL); - g_debug("Looking at blacklist: %s", blacklistdir); - if (!g_file_test(blacklistdir, G_FILE_TEST_IS_DIR)) { - g_free(blacklistdir); - return FALSE; - } - - GFile * filedir = g_file_new_for_path(blacklistdir); - blacklistdirmon = g_file_monitor_directory(filedir, G_FILE_MONITOR_NONE, NULL, NULL); - if (blacklistdirmon != NULL) { - g_signal_connect(G_OBJECT(blacklistdirmon), "changed", G_CALLBACK(blacklist_dir_changed), NULL); - } - - GError * error = NULL; - GDir * dir = g_dir_open(blacklistdir, 0, &error); - if (dir == NULL) { - g_warning("Unable to open blacklist directory (%s): %s", blacklistdir, error == NULL ? "No Message" : error->message); - g_error_free(error); - g_free(blacklistdir); - return FALSE; - } + GSimpleAction *messages; + GSimpleAction *clear; + gboolean attention; - const gchar * filename = NULL; - while ((filename = g_dir_read_name(dir)) != NULL) { - g_debug("Found file: %s", filename); - gchar * path = g_build_filename(blacklistdir, filename, NULL); - if (g_str_has_suffix(path, "keyfile")) { - g_idle_add(blacklist_keyfile_add, path); - } else { - g_idle_add(blacklist_add, path); - } - } + 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_dir_close(dir); - g_free(blacklistdir); + attention = g_hash_table_find (applications, app_section_draws_attention, NULL) != NULL; - return FALSE; + g_simple_action_set_state (messages, g_variant_new_boolean (attention)); + g_simple_action_set_enabled (clear, attention); } -/* Parses through a keyfile to find the desktop file entry and - pushes them into the blacklist. */ static gboolean -blacklist_keyfile_add (gpointer udata) +app_section_uses_chat (gpointer key, + gpointer value, + gpointer user_data) { - gchar * definition_file = (gchar *)udata; - gchar * desktopfile = desktop_file_from_keyfile(definition_file); - if (desktopfile != NULL) { - blacklist_add_core(desktopfile, definition_file); - g_free(desktopfile); - } - return FALSE; + AppSection *section = value; + return app_section_get_uses_chat_status (section); } -/* Takes a keyfile and finds the desktop file in it for - us. With some error handling. */ -static gchar * -desktop_file_from_keyfile (const gchar * definition_file) +static void +uses_chat_status_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) { - GKeyFile * keyfile = g_key_file_new(); - GError * error = NULL; + gboolean show_chat; + GMenuModel *first_section; - if (!g_key_file_load_from_file(keyfile, definition_file, G_KEY_FILE_NONE, &error)) { - g_warning("Unable to load keyfile '%s' because: %s", definition_file, error == NULL ? "unknown" : error->message); - g_error_free(error); - g_key_file_free(keyfile); - return NULL; - } + show_chat = g_hash_table_find (applications, app_section_uses_chat, NULL) != NULL; - if (!g_key_file_has_group(keyfile, DESKTOP_FILE_GROUP)) { - g_warning("Unable to use keyfile '%s' as it has no '" DESKTOP_FILE_GROUP "' group.", definition_file); - g_key_file_free(keyfile); - return NULL; + first_section = g_menu_model_get_item_link (G_MENU_MODEL (menu), 0, G_MENU_LINK_SECTION); + if (first_section == chat_section) { + if (!show_chat) + g_menu_remove (menu, 0); } - - if (!g_key_file_has_key(keyfile, DESKTOP_FILE_GROUP, DESKTOP_FILE_KEY_DESKTOP, &error)) { - g_warning("Unable to use keyfile '%s' as there is no key '" DESKTOP_FILE_KEY_DESKTOP "' in the group '" DESKTOP_FILE_GROUP "' because: %s", definition_file, error == NULL ? "unknown" : error->message); - g_error_free(error); - g_key_file_free(keyfile); - return NULL; + else { + if (show_chat) + g_menu_insert_section (menu, 0, NULL, chat_section); } - - gchar * desktopfile = g_key_file_get_string(keyfile, DESKTOP_FILE_GROUP, DESKTOP_FILE_KEY_DESKTOP, &error); - g_key_file_free(keyfile); - return desktopfile; } -/* Check if path is a symlink and return its target if it is */ -static gchar * -get_symlink_target (const gchar *path) +static AppSection * +add_application (const gchar *desktop_id) { - GFile *file; - GFileInfo *fileinfo; - gchar *target = NULL; - - file = g_file_new_for_path (path); + GDesktopAppInfo *appinfo; + gchar *id; + AppSection *section; - fileinfo = g_file_query_info (file, "standard::is-symlink", - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - NULL, NULL); - g_object_unref (file); - - if (!fileinfo) + appinfo = g_desktop_app_info_new (desktop_id); + if (!appinfo) { + g_warning ("could not add '%s', there's no desktop file with that id", desktop_id); return NULL; + } - if (g_file_info_get_is_symlink (fileinfo)) - target = g_strdup (g_file_info_get_symlink_target (fileinfo)); + id = g_app_info_get_simple_id (G_APP_INFO (appinfo)); + section = g_hash_table_lookup (applications, id); - g_object_unref (fileinfo); - return target; -} + if (!section) { + GMenuItem *menuitem; -/* Add a definition file into the black list and eclipse - any launchers that have the same file. */ -static gboolean -blacklist_add (gpointer udata) -{ - gchar * definition_file = (gchar *)udata; - gchar * symlink_target = get_symlink_target (definition_file); - gchar * contents = NULL; + section = app_section_new(appinfo); + g_hash_table_insert (applications, g_strdup (id), section); - if (symlink_target) - { - blacklist_add_core (symlink_target, definition_file); - g_free (symlink_target); - } - else if (g_str_has_suffix (definition_file, ".desktop")) - { - blacklist_add_core(definition_file, definition_file); - } - else if (g_file_get_contents (definition_file, &contents, NULL, NULL)) - { - gchar *trimmed = pango_trim_string (contents); - blacklist_add_core (trimmed, definition_file); - g_free (trimmed); - g_free (contents); + g_action_muxer_insert (action_muxer, id, app_section_get_actions (section)); + g_signal_connect (section, "notify::actions", + G_CALLBACK (actions_changed), NULL); + g_signal_connect (section, "notify::draws-attention", + G_CALLBACK (draws_attention_changed), NULL); + g_signal_connect (section, "notify::uses-chat-status", + G_CALLBACK (uses_chat_status_changed), NULL); + + /* TODO insert it at the right position (alphabetically by application name) */ + menuitem = g_menu_item_new_section (NULL, app_section_get_menu (section)); + g_menu_item_set_attribute (menuitem, "action-namespace", "s", id); + g_menu_insert_item (menu, g_menu_model_get_n_items (G_MENU_MODEL (menu)) -1, menuitem); + g_object_unref (menuitem); } - else - g_warning ("invalid blacklist entry: %s", definition_file); - return FALSE; + g_free (id); + g_object_unref (appinfo); + return section; } -/* This takes a desktop file and tries to add it to the black - list for applications in the messaging menu. If it can, - then the launcher item gets marked as eclipsed and hidden - from the user. */ static void -blacklist_add_core (gchar * desktop, gchar * definition) +remove_application (const char *desktop_id) { - gchar *basename = g_path_get_basename(desktop); - - /* Check for conflicts */ - gpointer data = g_hash_table_lookup(blacklist, basename); - if (data != NULL) { - gchar * oldfile = (gchar *)data; - if (!g_strcmp0(oldfile, definition)) { - g_warning("Already added file '%s'", oldfile); - } else { - g_warning("Already have desktop file '%s' in blacklist file '%s' not adding from '%s'", desktop, oldfile, definition); - } - - g_free(basename); - return; - } + GDesktopAppInfo *appinfo; + gchar *id; + AppSection *section; - /* Actually blacklist this thing */ - g_hash_table_insert(blacklist, g_strdup(basename), g_strdup(definition)); - g_debug("Adding Blacklist item '%s' for desktop '%s'", definition, desktop); - - /* Go through and eclipse folks */ - GList * launcher; - for (launcher = launcherList; launcher != NULL; launcher = launcher->next) { - launcherList_t * item = (launcherList_t *)launcher->data; - gchar * item_basename = g_path_get_basename(launcher_menu_item_get_desktop(item->menuitem)); - if (!g_strcmp0(basename, item_basename)) { - launcher_menu_item_set_eclipsed(item->menuitem, TRUE); - dbusmenu_menuitem_property_set_bool(item->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - } - g_free(item_basename); + appinfo = g_desktop_app_info_new (desktop_id); + if (!appinfo) { + g_warning ("could not remove '%s', there's no desktop file with that id", desktop_id); + return; } - check_hidden(); - /* Shouldn't need a resort here as hiding shouldn't cause things to - move other than this item disappearing. */ + id = g_app_info_get_simple_id (G_APP_INFO (appinfo)); - g_free(basename); - return; -} + section = g_hash_table_lookup (applications, id); + if (section) { + int pos = g_menu_find_section (menu, app_section_get_menu (section)); + if (pos >= 0) + g_menu_remove (menu, pos); + g_action_muxer_remove (action_muxer, id); -/* Remove a black list item based on the definition file - and uneclipse those launchers blocked by it. */ -static gboolean -blacklist_remove (gpointer data) -{ - gchar * definition_file = (gchar *)data; - g_debug("Removing: %s", definition_file); - - GHashTableIter iter; - gpointer key, value; - gboolean found = FALSE; - - g_hash_table_iter_init(&iter, blacklist); - while (g_hash_table_iter_next(&iter, &key, &value)) { - if (!g_strcmp0((gchar *)value, definition_file)) { - found = TRUE; - break; - } + 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); } - - if (!found) { - g_debug("\tNot found!"); - return FALSE; + else { + g_warning ("could not remove '%s', it's not registered", desktop_id); } - - GList * launcheritem; - for (launcheritem = launcherList; launcheritem != NULL; launcheritem = launcheritem->next) { - launcherList_t * li = (launcherList_t *)launcheritem->data; - if (!g_strcmp0(launcher_menu_item_get_desktop(li->menuitem), (gchar *)key)) { - GList * serveritem; - for (serveritem = serverList; serveritem != NULL; serveritem = serveritem->next) { - serverList_t * si = (serverList_t *)serveritem->data; - if (!g_strcmp0(app_menu_item_get_desktop(si->menuitem), (gchar *)key)) { - break; - } - } - if (serveritem == NULL) { - launcher_menu_item_set_eclipsed(li->menuitem, FALSE); - dbusmenu_menuitem_property_set_bool(li->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); - } - } - } - - if (!g_hash_table_remove(blacklist, key)) { - g_warning("Unable to remove '%s' with value '%s'", definition_file, (gchar *)key); - } - - check_hidden(); - resort_menu(root_menuitem); - - return FALSE; + + g_hash_table_remove (applications, id); + g_free (id); + g_object_unref (appinfo); } -/* Check to see if a particular desktop file is - in the blacklist. */ +/* This function turns a specific desktop id into a menu + item and registers it appropriately with everyone */ static gboolean -blacklist_check (const gchar * desktop_file) +build_launcher (gpointer data) { - gchar *basename = g_path_get_basename(desktop_file); - gboolean found; + gchar *desktop_id = data; - g_debug("Checking blacklist for: %s", basename); + add_application (desktop_id); - if (blacklist && g_hash_table_lookup(blacklist, basename)) { - g_debug("\tFound!"); - found = TRUE; - } - else - found = FALSE; - - g_free(basename); - return found; -} - -/* A callback everytime the blacklist directory changes - in some way. It needs to handle that. */ -static void -blacklist_dir_changed (GFileMonitor * monitor, GFile * file, GFile * other_file, GFileMonitorEvent event_type, gpointer user_data) -{ - g_debug("Blacklist directory changed!"); - - switch (event_type) { - case G_FILE_MONITOR_EVENT_DELETED: { - gchar * path = g_file_get_path(file); - g_debug("\tDelete: %s", path); - g_idle_add(blacklist_remove, path); - break; - } - case G_FILE_MONITOR_EVENT_CREATED: { - gchar * path = g_file_get_path(file); - g_debug("\tCreate: %s", path); - g_idle_add(blacklist_add, path); - break; - } - default: - break; - } - - return; + g_free (desktop_id); + return FALSE; } -/* - * More code - */ - -/* Goes through all the servers and sees if any of them - want attention. If they do, then well we'll give it - to them. If they don't, let's not bother the user - any, shall we? */ -static void -check_attention (void) +/* This function goes through all the launchers that we're + supposed to be grabbing and decides to show turn them + into menu items or not. It doens't do the work, but it + makes the decision. */ +static gboolean +build_launchers (gpointer data) { - GList * pointer; - for (pointer = serverList; pointer != NULL; pointer = g_list_next(pointer)) { - serverList_t * slt = (serverList_t *)pointer->data; - if (slt->attention) { - message_service_dbus_set_attention(dbus_interface, TRUE); - return; - } - } - message_service_dbus_set_attention(dbus_interface, FALSE); - return; -} + gchar **applications = g_settings_get_strv (settings, "applications"); + gchar **app; -/* This checks a server listing to see if it should - have attention. It can get attention through it's - count or by having an indicator that is requestion - attention. */ -static void -server_attention (serverList_t * slt) -{ - /* Count, easy yes and out. */ - if (slt->count > 0) { - slt->attention = TRUE; - return; - } + g_return_val_if_fail (applications != NULL, FALSE); - /* Check to see if any of the indicators want attention */ - GList * pointer; - for (pointer = slt->imList; pointer != NULL; pointer = g_list_next(pointer)) { - imList_t * ilt = (imList_t *)pointer->data; - if (im_menu_item_get_attention(IM_MENU_ITEM(ilt->menuitem))) { - slt->attention = TRUE; - return; - } + for (app = applications; *app; app++) + { + g_idle_add(build_launcher, g_strdup (*app)); } - /* Nope, no one */ - slt->attention = FALSE; - return; + g_strfreev (applications); + return FALSE; } -/* A new server has been created on the indicate bus. - We need to check to see if we like it. And build - structures for it if so. */ static void -server_added (IndicateListener * listener, IndicateListenerServer * server, gchar * type, gpointer data) -{ - g_debug("Server Added '%s' of type '%s'.", INDICATE_LISTENER_SERVER_DBUS_NAME(server), type); - if (type == NULL) { - return; - } - - if (type[0] == '\0') { - return; - } - - if (strncmp(type, "message", strlen("message"))) { - g_debug("\tServer type '%s' is not a message based type.", type); - return; - } - - DbusmenuMenuitem * menushell = DBUSMENU_MENUITEM(data); - if (menushell == NULL) { - g_error("\tData in callback is not a menushell"); - return; - } - - /* Build the Menu item */ - AppMenuItem * menuitem = app_menu_item_new(listener, server); - - /* Build a possible server structure */ - serverList_t * sl_item = g_new0(serverList_t, 1); - sl_item->server = server; - sl_item->menuitem = menuitem; - sl_item->imList = NULL; - sl_item->attention = FALSE; - sl_item->count = 0; - - /* Incase we got an indicator first */ - GList * alreadythere = g_list_find_custom(serverList, sl_item, serverList_equal); - if (alreadythere != NULL) { - /* Use the one we already had */ - g_free(sl_item); - sl_item = (serverList_t *)alreadythere->data; - sl_item->menuitem = menuitem; - serverList = g_list_sort(serverList, serverList_sort); - } else { - /* Insert the new one in the list */ - serverList = g_list_insert_sorted(serverList, sl_item, serverList_sort); - } - - /* Build a separator */ - sl_item->separator = dbusmenu_menuitem_new(); - dbusmenu_menuitem_property_set(sl_item->separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR); - - /* Connect the signals up to the menu item */ - g_signal_connect(G_OBJECT(menuitem), APP_MENU_ITEM_SIGNAL_COUNT_CHANGED, G_CALLBACK(server_count_changed), sl_item); - g_signal_connect(G_OBJECT(menuitem), APP_MENU_ITEM_SIGNAL_NAME_CHANGED, G_CALLBACK(server_name_changed), menushell); - g_signal_connect(G_OBJECT(menuitem), APP_MENU_ITEM_SIGNAL_SHORTCUT_ADDED, G_CALLBACK(server_shortcut_added), menushell); - g_signal_connect(G_OBJECT(menuitem), APP_MENU_ITEM_SIGNAL_SHORTCUT_REMOVED, G_CALLBACK(server_shortcut_removed), menushell); - - /* Put our new menu item in, with the separator behind it. - resort_menu will take care of whether it should be hidden - or not. */ - dbusmenu_menuitem_child_append(menushell, DBUSMENU_MENUITEM(menuitem)); - - GList * shortcuts = app_menu_item_get_items(sl_item->menuitem); - while (shortcuts != NULL) { - DbusmenuMenuitem * mi = DBUSMENU_MENUITEM(shortcuts->data); - g_debug("\tAdding shortcut: %s", dbusmenu_menuitem_property_get(mi, DBUSMENU_MENUITEM_PROP_LABEL)); - dbusmenu_menuitem_child_append(menushell, mi); - shortcuts = g_list_next(shortcuts); - } - - dbusmenu_menuitem_child_append(menushell, DBUSMENU_MENUITEM(sl_item->separator)); - - resort_menu(menushell); - check_hidden(); - - return; -} - -/* Server shortcut has been added */ -static void -server_shortcut_added (AppMenuItem * appitem, DbusmenuMenuitem * mi, gpointer data) -{ - g_debug("Application Shortcut added: %s", mi != NULL ? dbusmenu_menuitem_property_get(mi, DBUSMENU_MENUITEM_PROP_LABEL) : "none"); - DbusmenuMenuitem * shell = DBUSMENU_MENUITEM(data); - if (mi != NULL) { - dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_ICON_NAME, DBUSMENU_MENUITEM_ICON_NAME_BLANK); - dbusmenu_menuitem_child_append(shell, mi); - } - resort_menu(shell); - return; -} - -/* Server shortcut has been removed */ -static void -server_shortcut_removed (AppMenuItem * appitem, DbusmenuMenuitem * mi, gpointer data) +service_shutdown (IndicatorService * service, gpointer user_data) { - g_debug("Application Shortcut removed: %s", mi != NULL ? dbusmenu_menuitem_property_get(mi, DBUSMENU_MENUITEM_PROP_LABEL) : "none"); - DbusmenuMenuitem * shell = DBUSMENU_MENUITEM(data); - dbusmenu_menuitem_child_delete(shell, mi); - return; -} + GMainLoop *mainloop = user_data; -/* The name of a server has changed, we probably - need to reorder the menu to keep it in alphabetical - order. This happens often after we read the destkop - file from disk. */ -static void -server_name_changed (AppMenuItem * appitem, gchar * name, gpointer data) -{ - serverList = g_list_sort(serverList, serverList_sort); - check_eclipses(appitem); - resort_menu(DBUSMENU_MENUITEM(data)); - return; + g_warning("Shutting down service!"); + g_main_loop_quit(mainloop); } -/* If the count on the server changes, we need to know - whether that should be grabbing attention or not. If - it is, we need to reevaluate whether the whole thing - should be grabbing attention or not. */ static void -server_count_changed (AppMenuItem * appitem, guint count, gpointer data) +app_section_remove_attention (gpointer key, + gpointer value, + gpointer user_data) { - serverList_t * slt = (serverList_t *)data; - slt->count = count; - - if (count == 0 && slt->attention) { - /* Regen based on indicators if the count isn't going to cause it. */ - server_attention(slt); - /* If we're dropping let's see if we're the last. */ - if (!slt->attention) { - check_attention(); - } - } - - if (count != 0 && !slt->attention) { - slt->attention = TRUE; - /* Let's tell everyone about us! */ - message_service_dbus_set_attention(dbus_interface, TRUE); - } - - return; + AppSection *section = value; + app_section_clear_draws_attention (section); } -/* Respond to the IM entrie's time changing - which results in it needing to resort the list - and rebuild the menu to match. */ static void -im_time_changed (ImMenuItem * imitem, glong seconds, gpointer data) +clear_action_activate (GSimpleAction *simple, + GVariant *param, + gpointer user_data) { - serverList_t * sl = (serverList_t *)data; - sl->imList = g_list_sort(sl->imList, imList_sort); - resort_menu(root_menuitem); - return; + g_hash_table_foreach (applications, app_section_remove_attention, NULL); } -/* The IM entrie's request for attention has changed - so we need to pass that up the stack. */ static void -im_attention_changed (ImMenuItem * imitem, gboolean requestit, gpointer data) +radio_item_activate (GSimpleAction *action, + GVariant *parameter, + gpointer user_data) { - serverList_t * sl = (serverList_t *)data; - - if (requestit) { - sl->attention = TRUE; - message_service_dbus_set_attention(dbus_interface, TRUE); - } else { - server_attention(sl); - if (!sl->attention) { - check_attention(); - } - } - - return; + g_action_change_state (G_ACTION (action), parameter); } -/* Run when a server is removed from the indicator bus. It figures - out if we have it somewhere, and if so then we dump it out and - clean up all of it's entries. */ -static void -server_removed (IndicateListener * listener, IndicateListenerServer * server, gchar * type, gpointer data) +static gboolean +g_action_state_equal (GAction *action, + GVariant *value) { - /* Look for the server */ - g_debug("Removing server: %s", INDICATE_LISTENER_SERVER_DBUS_NAME(server)); - serverList_t slt = {0}; - slt.server = server; - GList * lookup = g_list_find_custom(serverList, &slt, serverList_equal); - - /* If we don't have it, exit */ - if (lookup == NULL) { - g_debug("\tUnable to find server: %s", INDICATE_LISTENER_SERVER_DBUS_NAME(server)); - return; - } - - serverList_t * sltp = (serverList_t *)lookup->data; - - /* Removing indicators from this server */ - while (sltp->imList) { - imList_t * imitem = (imList_t *)sltp->imList->data; - indicator_removed(listener, server, imitem->indicator, data); - } + GVariant *state; + gboolean eq; - /* Remove from the server list */ - serverList = g_list_remove(serverList, sltp); - - /* Remove launchers this could be eclipsing */ - remove_eclipses(sltp->menuitem); - - /* If there is a menu item, let's get rid of it. */ - if (sltp->menuitem != NULL) { - /* If there are shortcuts remove them */ - GList * shortcuts = app_menu_item_get_items(sltp->menuitem); - while (shortcuts != NULL) { - g_debug("\tRemoving shortcut: %s", dbusmenu_menuitem_property_get(DBUSMENU_MENUITEM(shortcuts->data), DBUSMENU_MENUITEM_PROP_LABEL)); - dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(shortcuts->data), DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - dbusmenu_menuitem_child_delete(DBUSMENU_MENUITEM(data), DBUSMENU_MENUITEM(shortcuts->data)); - shortcuts = g_list_next(shortcuts); - } - - g_debug("\tRemoving item"); - dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(sltp->menuitem), DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - dbusmenu_menuitem_child_delete(DBUSMENU_MENUITEM(data), DBUSMENU_MENUITEM(sltp->menuitem)); - g_object_unref(G_OBJECT(sltp->menuitem)); - } else { - g_debug("\tNo menuitem"); - } - - /* If there is a separator, let's get rid of it. */ - if (sltp->separator != NULL) { - g_debug("\tRemoving separator"); - dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(sltp->separator), DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - dbusmenu_menuitem_child_delete(DBUSMENU_MENUITEM(data), DBUSMENU_MENUITEM(sltp->separator)); - g_object_unref(G_OBJECT(sltp->separator)); - } else { - g_debug("\tNo separator"); - } - - if (sltp->attention) { - /* Check to see if this was the server causing the menu item to - be lit up. */ - check_attention(); - } + state = g_action_get_state (action); + g_return_val_if_fail (state != NULL, FALSE); - g_free(sltp); + eq = g_variant_equal (state, value); - check_hidden(); - - return; + g_variant_unref (state); + return eq; } -typedef struct _menushell_location menushell_location_t; -struct _menushell_location { - const IndicateListenerServer * server; - gint position; - gboolean found; -}; - static void -menushell_foreach_cb (DbusmenuMenuitem * data_mi, gpointer data_ms) { - menushell_location_t * msl = (menushell_location_t *)data_ms; - - if (msl->found) return; - - if (!IS_APP_MENU_ITEM(data_mi)) { - msl->position++; - return; - } - - AppMenuItem * appmenu = APP_MENU_ITEM(data_mi); - if (!g_strcmp0(INDICATE_LISTENER_SERVER_DBUS_NAME((IndicateListenerServer*)msl->server), INDICATE_LISTENER_SERVER_DBUS_NAME(app_menu_item_get_server(appmenu)))) { - msl->found = TRUE; - /* Return a position at the end of our shortcuts */ - msl->position += g_list_length(app_menu_item_get_items(appmenu)); - } else { - msl->position++; - } - - return; -} - -static void -check_hidden (void) +change_status_action (GSimpleAction *action, + GVariant *value, + gpointer user_data) { - g_debug("Checking Hidden..."); - gboolean hide = FALSE; - if (launcherList_count() == 0) { - g_debug("\tZero Launchers"); - /* If we don't have visible launchers we need to look more */ - if (g_list_length(serverList) == 0) { - g_debug("\tZero Applications"); - hide = TRUE; - } - } + const gchar *status; - message_service_dbus_set_icon(dbus_interface, hide); - return; -} + g_variant_get (value, "&s", &status); -/* This function takes care of putting the menu in the right order. - It basically it rebuilds the order by looking through all the - applications and launchers and puts them in the right place. The - menu functions will handle the cases where they don't move so this - is a good way to ensure everything is right. */ -static void -resort_menu (DbusmenuMenuitem * menushell) -{ - guint position = 0; - GList * serverentry; - GList * launcherentry = launcherList; - - g_debug("Reordering Menu:"); - - if (DBUSMENU_IS_MENUITEM(status_separator)) { - position = dbusmenu_menuitem_get_position(status_separator, root_menuitem) + 1; - g_debug("\tPriming with location of status separator: %d", position); - } + 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")); - for (serverentry = serverList; serverentry != NULL; serverentry = serverentry->next) { - serverList_t * si = (serverList_t *)serverentry->data; - - /* Looking to see if there are any launchers we need to insert - into the menu structure. We put as many as we need to. */ - if (launcherentry != NULL) { - launcherList_t * li = (launcherList_t *)launcherentry->data; - while (launcherentry != NULL && g_strcmp0(launcher_menu_item_get_name(li->menuitem), app_menu_item_get_name(si->menuitem)) < 0) { - /* Putting the launcher item in */ - g_debug("\tMoving launcher '%s' to position %d", launcher_menu_item_get_name(li->menuitem), position); - dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(li->menuitem), position); - position++; - - /* Inserting the shortcuts from the launcher */ - GList * shortcuts = launcher_menu_item_get_items(li->menuitem); - while (shortcuts != NULL) { - g_debug("\t\tMoving shortcut to position %d", position); - dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(shortcuts->data), position); - position++; - shortcuts = g_list_next(shortcuts); - } - - /* Putting the launcher separator in */ - g_debug("\tMoving launcher separator to position %d", position); - dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(li->separator), position); - if (!launcher_menu_item_get_eclipsed(li->menuitem)) { - /* Only clear the visiblity if we're not eclipsed */ - dbusmenu_menuitem_property_set_bool(li->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); - } - position++; - - launcherentry = launcherentry->next; - if (launcherentry != NULL) { - li = (launcherList_t *)launcherentry->data; - } - } - } - - /* Putting the app menu item in */ - if (si->menuitem != NULL) { - g_debug("\tMoving app %s to position %d", INDICATE_LISTENER_SERVER_DBUS_NAME(si->server), position); - dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(si->menuitem), position); - position++; - - /* Inserting the shortcuts from the launcher */ - GList * shortcuts = app_menu_item_get_items(si->menuitem); - while (shortcuts != NULL) { - g_debug("\t\tMoving shortcut to position %d", position); - dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(shortcuts->data), position); - position++; - shortcuts = g_list_next(shortcuts); - } - } - - /* Putting all the indicators that are related to this application - after it. */ - GList * imentry; - for (imentry = si->imList; imentry != NULL; imentry = imentry->next) { - imList_t * imi = (imList_t *)imentry->data; - - if (imi->menuitem != NULL) { - g_debug("\tMoving indicator on %s id %d to position %d", INDICATE_LISTENER_SERVER_DBUS_NAME(imi->server), INDICATE_LISTENER_INDICATOR_ID(imi->indicator), position); - - if (si->menuitem == NULL || !dbusmenu_menuitem_property_get_bool(DBUSMENU_MENUITEM(si->menuitem), DBUSMENU_MENUITEM_PROP_VISIBLE)) { - dbusmenu_menuitem_property_set_bool(imi->menuitem, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - } else { - dbusmenu_menuitem_property_set_bool(imi->menuitem, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); - } - - dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(imi->menuitem), position); - position++; - } - } - - /* Lastly putting the separator in */ - if (si->separator != NULL) { - g_debug("\tMoving app %s separator to position %d", INDICATE_LISTENER_SERVER_DBUS_NAME(si->server), position); - - if (si->menuitem == NULL || !dbusmenu_menuitem_property_get_bool(DBUSMENU_MENUITEM(si->menuitem), DBUSMENU_MENUITEM_PROP_VISIBLE)) { - dbusmenu_menuitem_property_set_bool(si->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - /* Note, this isn't the last if we can't see it */ - } else { - dbusmenu_menuitem_property_set_bool(si->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); - } - - dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(si->separator), position); - position++; - } + if (!g_action_state_equal (G_ACTION (action), value)) { + g_message ("%s", status); + g_simple_action_set_state (action, value); + indicator_messages_service_emit_status_changed (messages_service, status); } - - /* Put any leftover launchers in at the end of the list. */ - while (launcherentry != NULL) { - launcherList_t * li = (launcherList_t *)launcherentry->data; - - /* Putting the launcher in */ - g_debug("\tMoving launcher '%s' to position %d", launcher_menu_item_get_name(li->menuitem), position); - dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(li->menuitem), position); - position++; - - /* Inserting the shortcuts from the launcher */ - GList * shortcuts = launcher_menu_item_get_items(li->menuitem); - while (shortcuts != NULL) { - g_debug("\t\tMoving shortcut to position %d", position); - dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(shortcuts->data), position); - position++; - shortcuts = g_list_next(shortcuts); - } - - /* Putting the launcher separator in */ - g_debug("\tMoving launcher separator to position %d", position); - dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), DBUSMENU_MENUITEM(li->separator), position); - if (!launcher_menu_item_get_eclipsed(li->menuitem)) { - /* Only clear the visiblity if we're not eclipsed */ - dbusmenu_menuitem_property_set_bool(li->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); - } - position++; - - launcherentry = launcherentry->next; - } - - if (clear_attention != NULL) { - dbusmenu_menuitem_child_reorder(DBUSMENU_MENUITEM(menushell), clear_attention, position); - position++; /* Not needed, but reduce bugs on code tacked on here, compiler will remove */ - } - - return; } -/* Responding to a new indicator showing up on the bus. We - need to create a menuitem for it and start populating the - internal structures to track it. */ static void -indicator_added (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gpointer data) -{ - DbusmenuMenuitem * menushell = DBUSMENU_MENUITEM(data); - if (menushell == NULL) { - g_error("Data in callback is not a menushell"); +register_application (IndicatorMessagesService *service, + GDBusMethodInvocation *invocation, + const gchar *desktop_id, + const gchar *menu_path, + gpointer user_data) +{ + AppSection *section; + GDBusConnection *bus; + const gchar *sender; + + section = add_application (desktop_id); + if (!section) return; - } - imList_t * listItem = g_new0(imList_t, 1); - listItem->server = server; - listItem->indicator = indicator; - - /* Building the IM Menu Item which is a subclass - of DBus Menuitem */ - ImMenuItem * menuitem = im_menu_item_new(listener, server, indicator); - listItem->menuitem = DBUSMENU_MENUITEM(menuitem); - - /* Looking for a server entry to attach this indicator - to. If we can't find one then we have to build one - and attach the indicator to it. */ - serverList_t sl_item_local = {0}; - serverList_t * sl_item = NULL; - sl_item_local.server = server; - GList * serverentry = g_list_find_custom(serverList, &sl_item_local, serverList_equal); - - if (serverentry == NULL) { - /* This sucks, we got an indicator before the server. I guess - that's the joy of being asynchronous */ - sl_item = g_new0(serverList_t, 1); - sl_item->server = server; - sl_item->menuitem = NULL; - sl_item->imList = NULL; - sl_item->attention = FALSE; - sl_item->count = 0; - sl_item->separator = NULL; - - serverList = g_list_insert_sorted(serverList, sl_item, serverList_sort); - } else { - sl_item = (serverList_t *)serverentry->data; - } + bus = g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (service)); + sender = g_dbus_method_invocation_get_sender (invocation); - /* Added a this entry into the IM list */ - sl_item->imList = g_list_insert_sorted(sl_item->imList, listItem, imList_sort); - listItem->timechange_cb = g_signal_connect(G_OBJECT(menuitem), IM_MENU_ITEM_SIGNAL_TIME_CHANGED, G_CALLBACK(im_time_changed), sl_item); - listItem->attentionchange_cb = g_signal_connect(G_OBJECT(menuitem), IM_MENU_ITEM_SIGNAL_ATTENTION_CHANGED, G_CALLBACK(im_attention_changed), sl_item); - - /* Check the length of the list. If we've got more inidactors - than we allow. Well. Someone's gotta pay. Sorry. I didn't - want to do this, but you did it to yourself. */ - if (g_list_length(sl_item->imList) > MAX_NUMBER_OF_INDICATORS) { - GList * indicatoritem; - gint count; - for (indicatoritem = sl_item->imList, count = 0; indicatoritem != NULL; indicatoritem = g_list_next(indicatoritem), count++) { - imList_t * im = (imList_t *)indicatoritem->data; - im_menu_item_show(IM_MENU_ITEM(im->menuitem), count < MAX_NUMBER_OF_INDICATORS); - } - } + app_section_set_object_path (section, bus, sender, menu_path); + g_settings_strv_append_unique (settings, "applications", desktop_id); - /* Placing the item into the shell. Look to see if - we can find our server and slip in there. Otherwise - we'll just append. */ - menushell_location_t msl; - msl.found = FALSE; - msl.position = 0; - msl.server = server; - - dbusmenu_menuitem_foreach(DBUSMENU_MENUITEM(menushell), menushell_foreach_cb, &msl); - if (msl.found) { - dbusmenu_menuitem_child_add_position(menushell, DBUSMENU_MENUITEM(menuitem), msl.position); - } else { - g_warning("Unable to find server menu item"); - dbusmenu_menuitem_child_append(menushell, DBUSMENU_MENUITEM(menuitem)); - resort_menu (root_menuitem); - } - - return; + indicator_messages_service_complete_register_application (service, invocation); } -/* Process and indicator getting removed from the system. We - first need to ensure that it's one of ours and figure out - where we put it. When we find all that out we can go through - the process of removing the effect it had on the system. */ static void -indicator_removed (IndicateListener * listener, IndicateListenerServer * server, IndicateListenerIndicator * indicator, gpointer data) +unregister_application (IndicatorMessagesService *service, + GDBusMethodInvocation *invocation, + const gchar *desktop_id, + gpointer user_data) { - g_debug("Removing %s %d", INDICATE_LISTENER_SERVER_DBUS_NAME(server), INDICATE_LISTENER_INDICATOR_ID(indicator)); - - gboolean removed = FALSE; + remove_application (desktop_id); + g_settings_strv_remove (settings, "applications", desktop_id); - /* Find the server that was related to this item */ - serverList_t sl_item_local = {0}; - serverList_t * sl_item = NULL; - sl_item_local.server = server; - GList * serverentry = g_list_find_custom(serverList, &sl_item_local, serverList_equal); - if (serverentry == NULL) { - /* We didn't care about that server */ - return; - } - sl_item = (serverList_t *)serverentry->data; - - /* Look in the IM Hash Table */ - imList_t listData = {0}; - listData.server = server; - listData.indicator = indicator; - - GList * listItem = g_list_find_custom(sl_item->imList, &listData, imList_equal); - DbusmenuMenuitem * menuitem = NULL; - imList_t * ilt = NULL; - if (listItem != NULL) { - ilt = (imList_t *)listItem->data; - menuitem = ilt->menuitem; - } - - /* If we found a menu item and an imList_t item then - we can go ahead and remove it. Otherwise we can - skip this and exit. */ - if (!removed && menuitem != NULL) { - sl_item->imList = g_list_remove(sl_item->imList, ilt); - g_signal_handler_disconnect(menuitem, ilt->timechange_cb); - g_signal_handler_disconnect(menuitem, ilt->attentionchange_cb); - g_free(ilt); - - if (im_menu_item_get_attention(IM_MENU_ITEM(menuitem)) && im_menu_item_shown(IM_MENU_ITEM(menuitem))) { - /* If the removed indicator menu item was asking for - attention we need to see if this server should still - be asking for attention. */ - server_attention(sl_item); - /* If the server is no longer asking for attention then - we need to check if the whole system should be. */ - if (!sl_item->attention) { - check_attention(); - } - } - - if (im_menu_item_shown(IM_MENU_ITEM(menuitem)) && g_list_length(sl_item->imList) >= MAX_NUMBER_OF_INDICATORS) { - /* In this case we need to show a different indicator - becasue a shown one has left. But we're going to be - easy and set all the values. */ - GList * indicatoritem; - gint count; - for (indicatoritem = sl_item->imList, count = 0; indicatoritem != NULL; indicatoritem = g_list_next(indicatoritem), count++) { - imList_t * im = (imList_t *)indicatoritem->data; - im_menu_item_show(IM_MENU_ITEM(im->menuitem), count < MAX_NUMBER_OF_INDICATORS); - } - } - - /* Hide the item immediately, and then remove it - which might take a little longer. */ - dbusmenu_menuitem_property_set_bool(menuitem, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - dbusmenu_menuitem_child_delete(DBUSMENU_MENUITEM(data), menuitem); - removed = TRUE; - } - - if (!removed) { - g_warning("We were asked to remove %s %d but we didn't.", INDICATE_LISTENER_SERVER_DBUS_NAME(server), INDICATE_LISTENER_INDICATOR_ID(indicator)); - } - - return; + indicator_messages_service_complete_unregister_application (service, invocation); } static void -app_dir_changed (GFileMonitor * monitor, GFile * file, GFile * other_file, GFileMonitorEvent event_type, gpointer user_data) +set_status (IndicatorMessagesService *service, + GDBusMethodInvocation *invocation, + const gchar *status_str, + gpointer user_data) { - gchar * directory = (gchar *)user_data; - g_debug("Application directory changed: %s", directory); - - switch (event_type) { - case G_FILE_MONITOR_EVENT_DELETED: { - gchar * path = g_file_get_path(file); - g_debug("\tDelete: %s", path); - g_idle_add(destroy_launcher, path); - break; - } - case G_FILE_MONITOR_EVENT_CREATED: { - gchar * path = g_file_get_path(file); - g_debug("\tCreate: %s", path); - if (g_str_has_suffix(path, "keyfile")) { - g_idle_add(build_launcher_keyfile, path); - } else { - g_idle_add(build_launcher, path); - } - break; - } - default: - break; - } + GAction *status; - return; -} + status = g_simple_action_group_lookup (actions, "status"); + g_return_if_fail (status != NULL); -/* Check to see if a new desktop file causes - any of the launchers to be eclipsed by a running - process */ -static void -check_eclipses (AppMenuItem * ai) -{ - g_debug("Checking eclipsing"); - const gchar * aidesktop = app_menu_item_get_desktop(ai); - if (aidesktop == NULL) return; - g_debug("\tApp desktop: %s", aidesktop); - - GList * llitem; - for (llitem = launcherList; llitem != NULL; llitem = llitem->next) { - launcherList_t * ll = (launcherList_t *)llitem->data; - const gchar * lidesktop = launcher_menu_item_get_desktop(ll->menuitem); - g_debug("\tLauncher desktop: %s", lidesktop); - - if (!g_strcmp0(aidesktop, lidesktop)) { - launcher_menu_item_set_eclipsed(ll->menuitem, TRUE); - dbusmenu_menuitem_property_set_bool(ll->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - break; - } - } + g_action_change_state (status, g_variant_new_string (status_str)); - return; + indicator_messages_service_complete_set_status (service, invocation); } -/* Remove any eclipses that might have been caused - by this app item that is now retiring */ -static void -remove_eclipses (AppMenuItem * ai) +static GSimpleActionGroup * +create_action_group (void) { - const gchar * aidesktop = app_menu_item_get_desktop(ai); - if (aidesktop == NULL) return; - - if (blacklist_check(aidesktop)) return; - - GList * llitem; - for (llitem = launcherList; llitem != NULL; llitem = llitem->next) { - launcherList_t * ll = (launcherList_t *)llitem->data; - const gchar * lidesktop = launcher_menu_item_get_desktop(ll->menuitem); - - if (!g_strcmp0(aidesktop, lidesktop)) { - launcher_menu_item_set_eclipsed(ll->menuitem, FALSE); - dbusmenu_menuitem_property_set_bool(ll->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, TRUE); - break; - } - } + GSimpleActionGroup *actions; + GSimpleAction *messages; + GSimpleAction *clear; + GSimpleAction *status; - return; -} - -/* Remove a launcher from the system. We need to figure - out what it's up to! */ -static gboolean -destroy_launcher (gpointer data) -{ - gchar * appdirentry = (gchar *)data; - - GList * listitem; - GList * direntry; - launcherList_t * li; - gchar * appdir; - - for (listitem = launcherList; listitem != NULL; listitem = listitem->next) { - li = (launcherList_t *)listitem->data; - for (direntry = li->appdiritems; direntry != NULL; direntry = direntry->next) { - appdir = (gchar *)direntry->data; - if (!g_strcmp0(appdir, appdirentry)) { - break; - } - } - - if (direntry != NULL) { - break; - } - } + actions = g_simple_action_group_new (); - if (listitem == NULL) { - g_warning("Removed '%s' by the way of it not seeming to exist anywhere.", appdirentry); - return FALSE; - } + /* state of the messages action mirrors "draws-attention" */ + messages = g_simple_action_new_stateful ("messages", G_VARIANT_TYPE ("b"), + g_variant_new_boolean (FALSE)); - if (g_list_length(li->appdiritems) > 1) { - /* Just remove this item, and we can move on */ - g_debug("Just removing file entry: %s", appdir); - li->appdiritems = g_list_remove(li->appdiritems, appdir); - g_free(appdir); - return FALSE; - } + 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); - /* Full Destroy */ - g_free(appdir); - g_list_free(li->appdiritems); - - if (li->menuitem != NULL) { - /* If there are shortcuts remove them */ - GList * shortcuts = launcher_menu_item_get_items(li->menuitem); - while (shortcuts != NULL) { - dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(shortcuts->data), DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - dbusmenu_menuitem_child_delete(DBUSMENU_MENUITEM(data), DBUSMENU_MENUITEM(shortcuts->data)); - shortcuts = g_list_next(shortcuts); - } - - dbusmenu_menuitem_property_set_bool(DBUSMENU_MENUITEM(li->menuitem), DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - dbusmenu_menuitem_child_delete(root_menuitem, DBUSMENU_MENUITEM(li->menuitem)); - g_object_unref(G_OBJECT(li->menuitem)); - li->menuitem = NULL; - } + clear = g_simple_action_new ("clear", NULL); + g_simple_action_set_enabled (clear, FALSE); + g_signal_connect (clear, "activate", G_CALLBACK (clear_action_activate), NULL); - launcherList = g_list_remove(launcherList, li); - g_free(li); + g_simple_action_group_insert (actions, G_ACTION (messages)); + g_simple_action_group_insert (actions, G_ACTION (status)); + g_simple_action_group_insert (actions, G_ACTION (clear)); - return FALSE; + return actions; } -/* This function turns a specific file into a menu - item and registers it appropriately with everyone */ -static gboolean -build_launcher (gpointer data) +static GMenuModel * +create_status_section (void) { - /* Read the file get the data */ - gchar * path = (gchar *)data; - g_debug("\tpath: %s", path); - gchar * desktop = NULL; - - g_file_get_contents(path, &desktop, NULL, NULL); - - if (desktop == NULL) { - g_free(path); - return FALSE; - } + GMenu *menu; - gchar * trimdesktop = pango_trim_string(desktop); - g_free(desktop); - g_debug("\tcontents: %s", trimdesktop); + menu = g_menu_new (); + g_menu_append_with_icon_name (menu, _("Available"), "user-available", "status::available"); + g_menu_append_with_icon_name (menu, _("Away"), "user-away", "status::away"); + g_menu_append_with_icon_name (menu, _("Busy"), "user-busy", "status::busy"); + g_menu_append_with_icon_name (menu, _("Invisible"), "user-invisible", "status::invisible"); + g_menu_append_with_icon_name (menu, _("Offline"), "user-offline", "status::offline"); - build_launcher_core(trimdesktop); - g_free(trimdesktop); - g_free(path); - return FALSE; -} - -/* Use a key file to find the desktop file. */ -static gboolean -build_launcher_keyfile (gpointer data) -{ - gchar * path = (gchar *)data; - gchar * desktop = desktop_file_from_keyfile (path); - if (desktop != NULL) { - build_launcher_core(desktop); - g_free(desktop); - } - g_free(path); - return FALSE; + return G_MENU_MODEL (menu); } -/* The core action of dealing with a desktop file that should - be a launcher */ static void -build_launcher_core (const gchar * desktop) +got_bus (GObject *object, + GAsyncResult * res, + gpointer user_data) { - /* Check to see if we already have a launcher */ - GList * listitem; - for (listitem = launcherList; listitem != NULL; listitem = listitem->next) { - launcherList_t * li = (launcherList_t *)listitem->data; - if (!g_strcmp0(launcher_menu_item_get_desktop(li->menuitem), desktop)) { - break; - } - } - - if (listitem == NULL) { - /* If not */ - /* Build the item */ - launcherList_t * ll = g_new0(launcherList_t, 1); - ll->menuitem = launcher_menu_item_new(desktop); - ll->appdiritems = g_list_append(NULL, g_strdup(desktop)); - - /* Build a separator */ - ll->separator = dbusmenu_menuitem_new(); - dbusmenu_menuitem_property_set(ll->separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR); - - /* Add it to the list */ - launcherList = g_list_insert_sorted(launcherList, ll, launcherList_sort); - - /* Add it to the menu */ - dbusmenu_menuitem_property_set(DBUSMENU_MENUITEM(ll->menuitem), DBUSMENU_MENUITEM_PROP_TYPE, APPLICATION_MENUITEM_TYPE); - dbusmenu_menuitem_child_append(root_menuitem, DBUSMENU_MENUITEM(ll->menuitem)); - GList * shortcuts = launcher_menu_item_get_items(ll->menuitem); - while (shortcuts != NULL) { - dbusmenu_menuitem_child_append(root_menuitem, DBUSMENU_MENUITEM(shortcuts->data)); - shortcuts = g_list_next(shortcuts); - } - dbusmenu_menuitem_child_append(root_menuitem, DBUSMENU_MENUITEM(ll->separator)); - - /* If we're in the black list or we've gotten eclipsed - by something else, hide the item and the separator. */ - if (blacklist_check(launcher_menu_item_get_desktop(ll->menuitem)) || - launcher_menu_item_get_eclipsed(ll->menuitem)) { - launcher_menu_item_set_eclipsed(ll->menuitem, TRUE); - dbusmenu_menuitem_property_set_bool(ll->separator, DBUSMENU_MENUITEM_PROP_VISIBLE, FALSE); - } - - /* Check to see if any of the current applications should - be eclipsing us. */ - GList * server; - for (server = serverList; server != NULL; server = g_list_next(server)) { - serverList_t * slt = (serverList_t *)server->data; - check_eclipses(slt->menuitem); - } - - resort_menu(root_menuitem); - check_hidden(); - } else { - /* If so add ourselves */ - launcherList_t * ll = (launcherList_t *)listitem->data; - ll->appdiritems = g_list_append(ll->appdiritems, g_strdup(desktop)); - } - - return; -} + GDBusConnection *bus; + GError *error = NULL; -/* This function goes through all the launchers that we're - supposed to be grabbing and decides to show turn them - into menu items or not. It doens't do the work, but it - makes the decision. */ -static gboolean -build_launchers (gpointer data) -{ - gchar * directory = (gchar *)data; - - if (!g_file_test(directory, G_FILE_TEST_IS_DIR)) { - return FALSE; + bus = g_bus_get_finish (res, &error); + if (!bus) { + g_warning ("unable to connect to the session bus: %s", error->message); + g_error_free (error); + return; } - GFile * filedir = g_file_new_for_path(directory); - GFileMonitor * dirmon = g_file_monitor_directory(filedir, G_FILE_MONITOR_NONE, NULL, NULL); - if (dirmon != NULL) { - g_signal_connect(G_OBJECT(dirmon), "changed", G_CALLBACK(app_dir_changed), directory); + g_dbus_connection_export_action_group (bus, INDICATOR_MESSAGES_DBUS_OBJECT, + G_ACTION_GROUP (action_muxer), &error); + if (error) { + g_warning ("unable to export action group on dbus: %s", error->message); + g_error_free (error); + return; } - GError * error = NULL; - GDir * dir = g_dir_open(directory, 0, &error); - if (dir == NULL) { - g_warning("Unable to open system apps directory: %s", error->message); - g_error_free(error); - return FALSE; + g_dbus_connection_export_menu_model (bus, INDICATOR_MESSAGES_DBUS_OBJECT, + G_MENU_MODEL (toplevel_menu), &error); + if (error) { + g_warning ("unable to export menu on dbus: %s", error->message); + g_error_free (error); + return; } - const gchar * filename = NULL; - while ((filename = g_dir_read_name(dir)) != NULL) { - g_debug("Found file: %s", filename); - gchar * path = g_build_filename(directory, filename, NULL); - if (g_str_has_suffix(path, "keyfile")) { - g_idle_add(build_launcher_keyfile, path); - } else { - g_idle_add(build_launcher, path); - } + g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (messages_service), + bus, INDICATOR_MESSAGES_DBUS_SERVICE_OBJECT, + &error); + if (error) { + g_warning ("unable to export messages service on dbus: %s", error->message); + g_error_free (error); + return; } - g_dir_close(dir); - launcherList = g_list_sort(launcherList, launcherList_sort); - return FALSE; -} - -static void -service_shutdown (IndicatorService * service, gpointer user_data) -{ - g_warning("Shutting down service!"); - g_main_loop_quit(mainloop); - return; + g_object_unref (bus); } -/* Respond to changing status by updating the icon that - is on the panel */ -static void -status_update_callback (void) -{ - return; -} - -/* The clear attention item has been clicked on, what to do? */ -static void -clear_attention_activate (DbusmenuMenuitem * mi, guint timestamp, MessageServiceDbus * dbus) -{ - message_service_dbus_set_attention(dbus, FALSE); - return; -} - -/* Handle an update of the active state to ensure that we're - only enabled when we could do something. */ -static void -clear_attention_handler (MessageServiceDbus * msd, gboolean attention, DbusmenuMenuitem * clearitem) -{ - dbusmenu_menuitem_property_set_bool(clearitem, DBUSMENU_MENUITEM_PROP_ENABLED, attention); - return; -} - -/* Oh, if you don't know what main() is for - we really shouldn't be talking. */ int main (int argc, char ** argv) { + GMainLoop * mainloop = NULL; + IndicatorService * service = NULL; + GMenuItem *header; + GIcon *icon; + gchar *iconstr; + /* Glib init */ g_type_init(); + mainloop = g_main_loop_new (NULL, FALSE); + /* Create the Indicator Service interface */ service = indicator_service_new_version(INDICATOR_MESSAGES_DBUS_NAME, 1); - g_signal_connect(service, INDICATOR_SERVICE_SIGNAL_SHUTDOWN, G_CALLBACK(service_shutdown), NULL); + g_signal_connect(service, INDICATOR_SERVICE_SIGNAL_SHUTDOWN, G_CALLBACK(service_shutdown), mainloop); /* Setting up i18n and gettext. Apparently, we need all of these. */ @@ -1533,58 +482,52 @@ main (int argc, char ** argv) bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); textdomain (GETTEXT_PACKAGE); - /* Create the Seen DB */ - seen_db_init(); - /* Bring up the service DBus interface */ - dbus_interface = message_service_dbus_new(); - - /* Build the base menu */ - root_menuitem = dbusmenu_menuitem_new(); - DbusmenuServer * server = dbusmenu_server_new(INDICATOR_MESSAGES_DBUS_OBJECT); - dbusmenu_server_set_root(server, root_menuitem); - - /* Add status items */ - GList * statusitems = status_items_build(&status_update_callback); - while (statusitems != NULL) { - dbusmenu_menuitem_child_append(root_menuitem, DBUSMENU_MENUITEM(statusitems->data)); - statusitems = g_list_next(statusitems); - } - status_separator = dbusmenu_menuitem_new(); - dbusmenu_menuitem_property_set(status_separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR); - dbusmenu_menuitem_child_append(root_menuitem, status_separator); - - /* Add in the clear attention item */ - clear_attention = dbusmenu_menuitem_new(); - dbusmenu_menuitem_property_set(clear_attention, DBUSMENU_MENUITEM_PROP_LABEL, _("Clear")); - dbusmenu_menuitem_child_append(root_menuitem, clear_attention); - g_signal_connect(G_OBJECT(dbus_interface), MESSAGE_SERVICE_DBUS_SIGNAL_ATTENTION_CHANGED, G_CALLBACK(clear_attention_handler), clear_attention); - g_signal_connect(G_OBJECT(clear_attention), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(clear_attention_activate), dbus_interface); - - /* Start up the libindicate listener */ - listener = indicate_listener_ref_default(); - serverList = NULL; - - g_signal_connect(listener, INDICATE_LISTENER_SIGNAL_INDICATOR_ADDED, G_CALLBACK(indicator_added), root_menuitem); - g_signal_connect(listener, INDICATE_LISTENER_SIGNAL_INDICATOR_REMOVED, G_CALLBACK(indicator_removed), root_menuitem); - g_signal_connect(listener, INDICATE_LISTENER_SIGNAL_SERVER_ADDED, G_CALLBACK(server_added), root_menuitem); - g_signal_connect(listener, INDICATE_LISTENER_SIGNAL_SERVER_REMOVED, G_CALLBACK(server_removed), root_menuitem); - - /* Find launchers by looking through the config directories - in the idle loop */ - g_idle_add(blacklist_init, NULL); - g_idle_add(build_launchers, SYSTEM_APPS_DIR); - g_idle_add(build_launchers, SYSTEM_APPS_DIR_OLD); - gchar * userdir = g_build_filename(g_get_user_config_dir(), USER_APPS_DIR, NULL); - g_idle_add(build_launchers, userdir); - - /* Let's run a mainloop */ - mainloop = g_main_loop_new(NULL, FALSE); + messages_service = indicator_messages_service_skeleton_new (); + + g_bus_get (G_BUS_TYPE_SESSION, NULL, got_bus, NULL); + + actions = create_action_group (); + + action_muxer = g_action_muxer_new (); + g_action_muxer_insert (action_muxer, NULL, G_ACTION_GROUP (actions)); + + g_signal_connect (messages_service, "handle-register-application", + G_CALLBACK (register_application), NULL); + g_signal_connect (messages_service, "handle-unregister-application", + G_CALLBACK (unregister_application), NULL); + g_signal_connect (messages_service, "handle-set-status", + G_CALLBACK (set_status), NULL); + + menu = g_menu_new (); + chat_section = create_status_section (); + g_menu_append (menu, _("Clear"), "clear"); + + icon = g_themed_icon_new ("indicator-messages"); + iconstr = g_icon_to_string (icon); + + toplevel_menu = g_menu_new (); + 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); + + settings = g_settings_new ("com.canonical.indicator.messages"); + + applications = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + + g_idle_add(build_launchers, NULL); + g_main_loop_run(mainloop); /* Clean up */ - status_items_cleanup(); - g_free(userdir); - + g_free (iconstr); + g_object_unref (icon); + g_object_unref (messages_service); + g_object_unref (chat_section); + g_object_unref (settings); + g_hash_table_unref (applications); return 0; } |