/*
* 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: Ryan Lortie
* Ted Gould
*/
#define G_LOG_DOMAIN "hudappindicatorsource"
#include "appindicator-source.h"
#include
#include
#include "settings.h"
#include "dbusmenu-collector.h"
#include "source.h"
/**
* SECTION:hudappindicatorsource
* @title: HudAppIndicatorSource
* @short_description: a #HudSource to search through the application
* indicators
*
* #HudAppIndicatorSource searches through the menus of application
* indicators.
**/
/**
* HudAppIndicatorSource:
*
* This is an opaque structure type.
**/
struct _HudAppIndicatorSource
{
GObject parent_instance;
GSequence *indicators;
guint subscription;
GCancellable *cancellable;
gint use_count;
gboolean ready;
guint watch_id;
GDBusConnection *connection;
};
typedef GObjectClass HudAppIndicatorSourceClass;
static void hud_app_indicator_source_iface_init (HudSourceInterface *iface);
G_DEFINE_TYPE_WITH_CODE (HudAppIndicatorSource, hud_app_indicator_source, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (HUD_TYPE_SOURCE, hud_app_indicator_source_iface_init))
static void
hud_app_indicator_source_collector_changed (HudSource *collector,
gpointer user_data)
{
HudAppIndicatorSource *source = user_data;
hud_source_changed (HUD_SOURCE (source));
}
static void
hud_app_indicator_source_add_indicator (HudAppIndicatorSource *source,
GVariant *description)
{
HudDbusmenuCollector *collector;
const gchar *dbus_name;
const gchar *dbus_path;
GSequenceIter *iter;
const gchar *id;
const gchar *icon_name;
gint32 position;
gchar *title;
g_variant_get_child (description, 0, "&s", &icon_name);
g_variant_get_child (description, 1, "i", &position);
g_variant_get_child (description, 2, "&s", &dbus_name);
g_variant_get_child (description, 3, "&o", &dbus_path);
g_variant_get_child (description, 8, "&s", &id);
g_variant_get_child (description, 9, "s", &title);
if (title[0] == '\0')
{
g_free (title);
/* TRANSLATORS: This is used for Application indicators that
are not providing a title string. The '%s' represents the
unique ID that the app indicator provides, but it is usually
the package name and not generally human readable. An example
for Network Manager would be 'nm-applet'. */
title = g_strdup_printf(_("Untitled Indicator (%s)"), id);
}
g_debug ("adding appindicator %s at %d ('%s', %s, %s, %s)", id, position, title, icon_name, dbus_name, dbus_path);
collector = hud_dbusmenu_collector_new_for_endpoint (id, title, icon_name,
hud_settings.indicator_penalty,
dbus_name, dbus_path,
HUD_SOURCE_ITEM_TYPE_INDICATOR);
g_signal_connect (collector, "changed", G_CALLBACK (hud_app_indicator_source_collector_changed), source);
/* If query is active, mark new app indicator as used. */
if (source->use_count)
hud_source_use (HUD_SOURCE (collector));
iter = g_sequence_get_iter_at_pos (source->indicators, position);
g_sequence_insert_before (iter, collector);
g_free (title);
}
static void
hud_app_indicator_source_remove_indicator (HudAppIndicatorSource *source,
GVariant *description)
{
GSequenceIter *iter;
gint32 position;
g_variant_get_child (description, 0, "i", &position);
g_debug ("removing appindicator at %d", position);
iter = g_sequence_get_iter_at_pos (source->indicators, position);
if (!g_sequence_iter_is_end (iter))
{
HudDbusmenuCollector *collector;
collector = g_sequence_get (iter);
g_signal_handlers_disconnect_by_func (collector, hud_app_indicator_source_collector_changed, source);
/* Drop use count if we added one... */
if (source->use_count)
hud_source_unuse (HUD_SOURCE (collector));
g_sequence_remove (iter);
}
}
static void
hud_app_indicator_source_dbus_signal (GDBusConnection *connection,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
HudAppIndicatorSource *source = user_data;
g_debug ("got signal");
if (!source->ready)
{
g_debug ("not ready, so ignoring signal");
return;
}
if (g_str_equal (signal_name, "ApplicationAdded"))
{
if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sisossssss)")))
return;
hud_app_indicator_source_add_indicator (source, parameters);
hud_source_changed (HUD_SOURCE (source));
}
else if (g_str_equal (signal_name, "ApplicationRemoved"))
{
if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(i)")))
return;
hud_app_indicator_source_remove_indicator (source, parameters);
hud_source_changed (HUD_SOURCE (source));
}
else if (g_str_equal (signal_name, "ApplicationTitleChanged"))
{
GSequenceIter *iter;
const gchar *title;
gint32 position;
if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(is)")))
return;
g_variant_get (parameters, "(i&s)", &position, &title);
g_debug ("changing title of appindicator at %d to '%s'", position, title);
iter = g_sequence_get_iter_at_pos (source->indicators, position);
if (!g_sequence_iter_is_end (iter))
{
HudDbusmenuCollector *collector;
collector = g_sequence_get (iter);
hud_dbusmenu_collector_set_prefix (collector, title);
}
}
else if (g_str_equal (signal_name, "ApplicationIconChanged"))
{
GSequenceIter *iter;
const gchar *icon;
gint32 position;
if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(iss)")))
return;
g_variant_get (parameters, "(i&ss)", &position, &icon, NULL);
g_debug ("changing icon of appindicator at %d to '%s'", position, icon);
iter = g_sequence_get_iter_at_pos (source->indicators, position);
if (!g_sequence_iter_is_end (iter))
{
HudDbusmenuCollector *collector;
collector = g_sequence_get (iter);
hud_dbusmenu_collector_set_icon (collector, icon);
}
}
}
static void
hud_app_indicator_source_ready (GObject *connection,
GAsyncResult *result,
gpointer user_data)
{
HudAppIndicatorSource *source = user_data;
GError *error = NULL;
GVariant *reply;
g_debug ("GetApplications returned");
g_clear_object (&source->cancellable);
if (source == NULL)
{
g_debug("Callback invoked with null connection");
return;
}
reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION (connection), result, &error);
if (reply)
{
GVariant *description;
GVariantIter *iter;
g_assert (!source->ready);
source->ready = TRUE;
g_debug ("going ready");
g_variant_get (reply, "(a(sisossssss))", &iter);
while ((description = g_variant_iter_next_value (iter)))
{
hud_app_indicator_source_add_indicator (source, description);
g_variant_unref (description);
}
g_variant_iter_free (iter);
g_variant_unref (reply);
hud_source_changed (HUD_SOURCE (source));
}
else
{
g_warning ("GetApplications returned an error: %s", error->message);
g_error_free (error);
}
g_object_unref (source);
g_debug ("done handling GetApplications reply");
}
static void
hud_app_indicator_source_name_appeared (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
gpointer user_data)
{
HudAppIndicatorSource *source = user_data;
g_debug ("name appeared (owner is %s)", name_owner);
g_assert (source->subscription == 0);
source->subscription = g_dbus_connection_signal_subscribe (connection, name_owner,
APP_INDICATOR_SERVICE_IFACE, NULL,
APP_INDICATOR_SERVICE_OBJECT_PATH, NULL,
G_DBUS_SIGNAL_FLAGS_NONE,
hud_app_indicator_source_dbus_signal,
source, NULL);
g_assert (source->cancellable == NULL);
source->cancellable = g_cancellable_new ();
g_dbus_connection_call (connection, name_owner, APP_INDICATOR_SERVICE_OBJECT_PATH, APP_INDICATOR_SERVICE_IFACE,
"GetApplications", NULL, G_VARIANT_TYPE ("(a(sisossssss))"),
G_DBUS_CALL_FLAGS_NONE, -1, source->cancellable,
hud_app_indicator_source_ready, g_object_ref(source));
}
static void
hud_app_indicator_source_name_vanished (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
HudAppIndicatorSource *source = user_data;
g_debug ("name vanished");
if (source->subscription > 0)
{
g_dbus_connection_signal_unsubscribe (connection, source->subscription);
source->subscription = 0;
}
if (source->cancellable)
{
g_cancellable_cancel (source->cancellable);
g_clear_object (&source->cancellable);
}
if (source->ready)
{
GSequenceIter *iter;
source->ready = FALSE;
iter = g_sequence_get_begin_iter (source->indicators);
while (!g_sequence_iter_is_end (iter))
{
HudDbusmenuCollector *collector;
GSequenceIter *next;
collector = g_sequence_get (iter);
g_signal_handlers_disconnect_by_func (collector, hud_app_indicator_source_collector_changed, source);
next = g_sequence_iter_next (iter);
g_sequence_remove (iter);
iter = next;
}
hud_source_changed (HUD_SOURCE (source));
}
}
static void
hud_app_indicator_source_use (HudSource *hud_source)
{
HudAppIndicatorSource *source = HUD_APP_INDICATOR_SOURCE (hud_source);
if (source->use_count == 0)
g_sequence_foreach (source->indicators, (GFunc) hud_source_use, NULL);
source->use_count++;
}
static void
hud_app_indicator_source_unuse (HudSource *hud_source)
{
HudAppIndicatorSource *source = HUD_APP_INDICATOR_SOURCE (hud_source);
g_return_if_fail (source->use_count > 0);
source->use_count--;
if (source->use_count == 0)
g_sequence_foreach (source->indicators, (GFunc) hud_source_unuse, NULL);
}
static void
hud_app_indicator_source_search (HudSource *hud_source,
HudTokenList *search_tokens,
void (*append_func) (HudResult * result, gpointer user_data),
gpointer user_data)
{
HudAppIndicatorSource *source = HUD_APP_INDICATOR_SOURCE (hud_source);
GSequenceIter *iter;
iter = g_sequence_get_begin_iter (source->indicators);
while (!g_sequence_iter_is_end (iter))
{
hud_source_search (g_sequence_get (iter), search_tokens, append_func, user_data);
iter = g_sequence_iter_next (iter);
}
}
static void
hud_app_indicator_source_list_applications (HudSource *hud_source,
HudTokenList *search_tokens,
void (*append_func) (const gchar *application_id, const gchar *application_icon, HudSourceItemType type, gpointer user_data),
gpointer user_data)
{
HudAppIndicatorSource *source = HUD_APP_INDICATOR_SOURCE (hud_source);
GSequenceIter *iter;
iter = g_sequence_get_begin_iter (source->indicators);
while (!g_sequence_iter_is_end (iter))
{
hud_source_list_applications (g_sequence_get (iter), search_tokens, append_func, user_data);
iter = g_sequence_iter_next (iter);
}
}
static HudSource *
hud_app_indicator_source_get (HudSource *hud_source,
const gchar *application_id)
{
HudAppIndicatorSource *source = HUD_APP_INDICATOR_SOURCE (hud_source);
GSequenceIter *iter;
iter = g_sequence_get_begin_iter (source->indicators);
while (!g_sequence_iter_is_end (iter))
{
HudSource *result = hud_source_get (g_sequence_get (iter), application_id);
if (result != NULL)
return result;
iter = g_sequence_iter_next (iter);
}
return NULL;
}
static void
hud_app_indicator_source_finalize (GObject *object)
{
g_debug("hud_app_indicator_source_finalize");
HudAppIndicatorSource *source = HUD_APP_INDICATOR_SOURCE(object);
if (source->subscription)
{
g_dbus_connection_signal_unsubscribe(source->connection, source->subscription);
source->subscription = 0;
}
g_bus_unwatch_name(source->watch_id);
g_sequence_free(source->indicators);
g_object_unref(source->connection);
G_OBJECT_CLASS(hud_app_indicator_source_parent_class)->finalize(object);
}
static void
hud_app_indicator_source_init (HudAppIndicatorSource *source)
{
g_debug ("online");
source->indicators = g_sequence_new (g_object_unref);
}
static void
hud_app_indicator_source_iface_init (HudSourceInterface *iface)
{
iface->use = hud_app_indicator_source_use;
iface->unuse = hud_app_indicator_source_unuse;
iface->search = hud_app_indicator_source_search;
iface->list_applications = hud_app_indicator_source_list_applications;
iface->get = hud_app_indicator_source_get;
}
static void
hud_app_indicator_source_class_init (HudAppIndicatorSourceClass *class)
{
class->finalize = hud_app_indicator_source_finalize;
}
/**
* hud_app_indicator_source_new:
*
* Creates a #HudAppIndicatorSource.
*
* Returns: a new #HudAppIndicatorSource
**/
HudAppIndicatorSource *
hud_app_indicator_source_new (GDBusConnection *connection)
{
HudAppIndicatorSource *source = g_object_new (HUD_TYPE_APP_INDICATOR_SOURCE, NULL);
source->connection = g_object_ref(connection);
source->watch_id = g_bus_watch_name_on_connection(connection, APP_INDICATOR_SERVICE_BUS_NAME, G_BUS_NAME_WATCHER_FLAGS_NONE,
hud_app_indicator_source_name_appeared, hud_app_indicator_source_name_vanished,
source, NULL);
return source;
}