/*
* 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-application-list.h"
#include "indicator-messages-application.h"
#include "gactionmuxer.h"
#include
typedef GObjectClass ImApplicationListClass;
struct _ImApplicationList
{
GObject parent;
GHashTable *applications;
GActionMuxer *muxer;
};
G_DEFINE_TYPE (ImApplicationList, im_application_list, G_TYPE_OBJECT);
enum
{
SOURCE_ADDED,
MESSAGE_ADDED,
N_SIGNALS
};
static guint signals[N_SIGNALS];
typedef struct
{
ImApplicationList *list;
GDesktopAppInfo *info;
IndicatorMessagesApplication *proxy;
GSimpleActionGroup *actions;
GCancellable *cancellable;
} Application;
static void
application_free (gpointer data)
{
Application *app = data;
if (!app)
return;
g_clear_object (&app->info);
if (app->cancellable)
{
g_cancellable_cancel (app->cancellable);
g_clear_object (&app->cancellable);
}
if (app->proxy)
g_object_unref (app->proxy);
if (app->actions)
g_object_unref (app->actions);
g_slice_free (Application, app);
}
static void
im_application_list_dispose (GObject *object)
{
ImApplicationList *list = IM_APPLICATION_LIST (object);
g_clear_pointer (&list->applications, g_hash_table_unref);
g_clear_object (&list->muxer);
G_OBJECT_CLASS (im_application_list_parent_class)->dispose (object);
}
static void
im_application_list_finalize (GObject *object)
{
G_OBJECT_CLASS (im_application_list_parent_class)->finalize (object);
}
static void
im_application_list_class_init (ImApplicationListClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = im_application_list_dispose;
object_class->finalize = im_application_list_finalize;
signals[SOURCE_ADDED] = g_signal_new ("source-added",
IM_TYPE_APPLICATION_LIST,
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL,
g_cclosure_marshal_generic,
G_TYPE_NONE,
8,
G_TYPE_DESKTOP_APP_INFO,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_UINT,
G_TYPE_INT64,
G_TYPE_STRING,
G_TYPE_BOOLEAN);
signals[MESSAGE_ADDED] = g_signal_new ("message-added",
IM_TYPE_APPLICATION_LIST,
G_SIGNAL_RUN_FIRST,
0,
NULL, NULL,
g_cclosure_marshal_generic,
G_TYPE_NONE,
8,
G_TYPE_DESKTOP_APP_INFO,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_STRING,
G_TYPE_INT64,
G_TYPE_BOOLEAN);
}
static void
im_application_list_init (ImApplicationList *list)
{
list->applications = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, application_free);
list->muxer = g_action_muxer_new ();
}
ImApplicationList *
im_application_list_new (void)
{
return g_object_new (IM_TYPE_APPLICATION_LIST, NULL);
}
gboolean
im_application_list_add (ImApplicationList *list,
const gchar *desktop_id)
{
GDesktopAppInfo *info;
Application *app;
const gchar *id;
g_return_val_if_fail (IM_IS_APPLICATION_LIST (list), FALSE);
g_return_val_if_fail (desktop_id != NULL, FALSE);
if (g_hash_table_contains (list->applications, desktop_id))
{
g_warning ("an application with id '%s' already exists", desktop_id);
return FALSE;
}
info = g_desktop_app_info_new (desktop_id);
if (!info)
{
g_warning ("application with id '%s' already exists", desktop_id);
return FALSE;
}
id = g_app_info_get_id (G_APP_INFO (info));
app = g_slice_new0 (Application);
app->list = list;
app->info = info;
app->actions = g_simple_action_group_new ();
g_hash_table_insert (list->applications, (gpointer) id, app);
g_action_muxer_insert (list->muxer, id, G_ACTION_GROUP (app->actions));
return TRUE;
}
void
im_application_list_remove (ImApplicationList *list,
const gchar *id)
{
g_return_if_fail (IM_IS_APPLICATION_LIST (list));
g_hash_table_remove (list->applications, id);
g_action_muxer_remove (list->muxer, id);
}
static void
im_application_list_sources_listed (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
Application *app = user_data;
GVariant *sources;
GError *error = NULL;
if (indicator_messages_application_call_list_sources_finish (app->proxy, &sources, result, &error))
{
GVariantIter iter;
const gchar *id;
const gchar *label;
const gchar *iconstr;
guint32 count;
gint64 time;
const gchar *string;
gboolean draws_attention;
g_variant_iter_init (&iter, sources);
while (g_variant_iter_next (&iter, "(&s&s&sux&sb)", &id, &label, &iconstr, &count,
&time, &string, &draws_attention))
{
GSimpleAction *action;
action = g_simple_action_new_stateful (id, NULL, g_variant_new_uint32 (count));
g_simple_action_group_insert (app->actions, G_ACTION (action));
g_signal_emit (app->list, signals[SOURCE_ADDED], 0,
app->info, id, label, iconstr, count, time, string, draws_attention);
g_object_unref (action);
}
g_variant_unref (sources);
}
else
{
g_warning ("could not fetch the list of sources: %s", error->message);
g_error_free (error);
}
}
static void
im_application_list_messages_listed (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
Application *app = user_data;
GVariant *messages;
GError *error = NULL;
if (indicator_messages_application_call_list_messages_finish (app->proxy, &messages, result, &error))
{
GVariantIter iter;
const gchar *id;
const gchar *iconstr;
const gchar *title;
const gchar *subtitle;
const gchar *body;
gint64 time;
gboolean draws_attention;
g_variant_iter_init (&iter, messages);
while (g_variant_iter_next (&iter, "(&s&s&s&s&sxb)", &id, &iconstr, &title, &subtitle, &body,
&time, &draws_attention))
{
GSimpleAction *action;
action = g_simple_action_new (id, NULL);
g_simple_action_group_insert (G_SIMPLE_ACTION_GROUP (app->actions), G_ACTION (action));
g_signal_emit (app->list, signals[MESSAGE_ADDED], 0,
app->info, id, iconstr, title, subtitle, body, time, draws_attention);
g_object_unref (action);
}
g_variant_unref (messages);
}
else
{
g_warning ("could not fetch the list of messages: %s", error->message);
g_error_free (error);
}
}
static void
im_application_list_proxy_created (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
Application *app = user_data;
GError *error = NULL;
app->proxy = indicator_messages_application_proxy_new_finish (result, &error);
if (!app)
{
g_warning ("could not create application proxy: %s", error->message);
g_error_free (error);
return;
}
indicator_messages_application_call_list_sources (app->proxy, app->cancellable,
im_application_list_sources_listed, app);
indicator_messages_application_call_list_messages (app->proxy, app->cancellable,
im_application_list_messages_listed, app);
}
void
im_application_list_set_remote (ImApplicationList *list,
const gchar *id,
GDBusConnection *connection,
const gchar *unique_bus_name,
const gchar *object_path)
{
Application *app;
g_return_if_fail (IM_IS_APPLICATION_LIST (list));
app = g_hash_table_lookup (list->applications, id);
if (!app)
{
g_warning ("'%s' is not a registered appliction", id);
return;
}
if (app->proxy || app->cancellable)
{
g_warning ("application '%s' is already running on '%s'",
id, g_dbus_proxy_get_name_owner (G_DBUS_PROXY (app->proxy)));
return;
}
app->cancellable = g_cancellable_new ();
indicator_messages_application_proxy_new (connection, G_DBUS_PROXY_FLAGS_NONE,
unique_bus_name, object_path, app->cancellable,
im_application_list_proxy_created, app);
}
GActionGroup *
im_application_list_get_action_group (ImApplicationList *list)
{
g_return_val_if_fail (IM_IS_APPLICATION_LIST (list), NULL);
return G_ACTION_GROUP (list->muxer);
}