Back to index

indicator-appmenu  12.10.0
hudappindicatorsource.c
Go to the documentation of this file.
00001 /*
00002  * Copyright © 2012 Canonical Ltd.
00003  *
00004  * This program is free software: you can redistribute it and/or modify it
00005  * under the terms of the GNU General Public License version 3, as
00006  * published by the Free Software Foundation.
00007  *
00008  * This program is distributed in the hope that it will be useful, but
00009  * WITHOUT ANY WARRANTY; without even the implied warranties of
00010  * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
00011  * PURPOSE.  See the GNU General Public License for more details.
00012  *
00013  * You should have received a copy of the GNU General Public License along
00014  * with this program.  If not, see <http://www.gnu.org/licenses/>.
00015  *
00016  * Authors: Ryan Lortie <desrt@desrt.ca>
00017  *          Ted Gould <ted@canonical.com>
00018  */
00019 
00020 #define G_LOG_DOMAIN "hudappindicatorsource"
00021 
00022 #include "hudappindicatorsource.h"
00023 
00024 #include <glib/gi18n.h>
00025 #include <gio/gio.h>
00026 
00027 #include "hudsettings.h"
00028 #include "huddbusmenucollector.h"
00029 #include "hudsource.h"
00030 
00047 #define APP_INDICATOR_SERVICE_BUS_NAME    "com.canonical.indicator.application"
00048 #define APP_INDICATOR_SERVICE_OBJECT_PATH "/com/canonical/indicator/application/service"
00049 #define APP_INDICATOR_SERVICE_IFACE       "com.canonical.indicator.application.service"
00050 
00051 struct _HudAppIndicatorSource
00052 {
00053   GObject parent_instance;
00054 
00055   GSequence    *indicators;
00056   guint         subscription;
00057   GCancellable *cancellable;
00058   gint          use_count;
00059   gboolean      ready;
00060 };
00061 
00062 typedef GObjectClass HudAppIndicatorSourceClass;
00063 
00064 static void hud_app_indicator_source_iface_init (HudSourceInterface *iface);
00065 G_DEFINE_TYPE_WITH_CODE (HudAppIndicatorSource, hud_app_indicator_source, G_TYPE_OBJECT,
00066                          G_IMPLEMENT_INTERFACE (HUD_TYPE_SOURCE, hud_app_indicator_source_iface_init))
00067 
00068 static void
00069 hud_app_indicator_source_collector_changed (HudSource *collector,
00070                                             gpointer   user_data)
00071 {
00072   HudAppIndicatorSource *source = user_data;
00073 
00074   hud_source_changed (HUD_SOURCE (source));
00075 }
00076 
00077 static void
00078 hud_app_indicator_source_add_indicator (HudAppIndicatorSource *source,
00079                                         GVariant              *description)
00080 {
00081   HudDbusmenuCollector *collector;
00082   const gchar *dbus_name;
00083   const gchar *dbus_path;
00084   GSequenceIter *iter;
00085   const gchar *id;
00086   const gchar *icon_name;
00087   gint32 position;
00088   gchar *title;
00089 
00090   g_variant_get_child (description, 0, "&s", &icon_name);
00091   g_variant_get_child (description, 1, "i", &position);
00092   g_variant_get_child (description, 2, "&s", &dbus_name);
00093   g_variant_get_child (description, 3, "&o", &dbus_path);
00094   g_variant_get_child (description, 8, "&s", &id);
00095   g_variant_get_child (description, 9, "s", &title);
00096 
00097   if (title[0] == '\0')
00098     {
00099       g_free (title);
00100       /* TRANSLATORS:  This is used for Application indicators that
00101          are not providing a title string.  The '%s' represents the
00102          unique ID that the app indicator provides, but it is usually
00103          the package name and not generally human readable.  An example
00104          for Network Manager would be 'nm-applet'. */
00105       title = g_strdup_printf(_("Untitled Indicator (%s)"), id);
00106     }
00107   g_debug ("adding appindicator %s at %d ('%s', %s, %s, %s)", id, position, title, icon_name, dbus_name, dbus_path);
00108 
00109   collector = hud_dbusmenu_collector_new_for_endpoint (id, title, icon_name,
00110                                                        hud_settings.indicator_penalty,
00111                                                        dbus_name, dbus_path);
00112   g_signal_connect (collector, "changed", G_CALLBACK (hud_app_indicator_source_collector_changed), source);
00113 
00114   /* If query is active, mark new app indicator as used. */
00115   if (source->use_count)
00116     hud_source_use (HUD_SOURCE (collector));
00117 
00118   iter = g_sequence_get_iter_at_pos (source->indicators, position);
00119   g_sequence_insert_before (iter, collector);
00120   g_free (title);
00121 }
00122 
00123 static void
00124 hud_app_indicator_source_remove_indicator (HudAppIndicatorSource *source,
00125                                            GVariant              *description)
00126 {
00127   GSequenceIter *iter;
00128   gint32 position;
00129 
00130   g_variant_get_child (description, 0, "i", &position);
00131 
00132   g_debug ("removing appindicator at %d", position);
00133 
00134   iter = g_sequence_get_iter_at_pos (source->indicators, position);
00135   if (!g_sequence_iter_is_end (iter))
00136     {
00137       HudDbusmenuCollector *collector;
00138 
00139       collector = g_sequence_get (iter);
00140       g_signal_handlers_disconnect_by_func (collector, hud_app_indicator_source_collector_changed, source);
00141       /* Drop use count if we added one... */
00142       if (source->use_count)
00143         hud_source_unuse (HUD_SOURCE (collector));
00144       g_sequence_remove (iter);
00145     }
00146 }
00147 
00148 static void
00149 hud_app_indicator_source_dbus_signal (GDBusConnection *connection,
00150                                       const gchar     *sender_name,
00151                                       const gchar     *object_path,
00152                                       const gchar     *interface_name,
00153                                       const gchar     *signal_name,
00154                                       GVariant        *parameters,
00155                                       gpointer         user_data)
00156 {
00157   HudAppIndicatorSource *source = user_data;
00158 
00159   g_debug ("got signal");
00160 
00161   if (!source->ready)
00162     {
00163       g_debug ("not ready, so ignoring signal");
00164       return;
00165     }
00166 
00167   if (g_str_equal (signal_name, "ApplicationAdded"))
00168     {
00169       if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sisossssss)")))
00170         return;
00171 
00172       hud_app_indicator_source_add_indicator (source, parameters);
00173       hud_source_changed (HUD_SOURCE (source));
00174     }
00175 
00176   else if (g_str_equal (signal_name, "ApplicationRemoved"))
00177     {
00178       if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(i)")))
00179         return;
00180 
00181       hud_app_indicator_source_remove_indicator (source, parameters);
00182       hud_source_changed (HUD_SOURCE (source));
00183     }
00184 
00185   else if (g_str_equal (signal_name, "ApplicationTitleChanged"))
00186     {
00187       GSequenceIter *iter;
00188       const gchar *title;
00189       gint32 position;
00190 
00191       if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(is)")))
00192         return;
00193 
00194       g_variant_get (parameters, "(i&s)", &position, &title);
00195 
00196       g_debug ("changing title of appindicator at %d to '%s'", position, title);
00197 
00198       iter = g_sequence_get_iter_at_pos (source->indicators, position);
00199       if (!g_sequence_iter_is_end (iter))
00200         {
00201           HudDbusmenuCollector *collector;
00202 
00203           collector = g_sequence_get (iter);
00204           hud_dbusmenu_collector_set_prefix (collector, title);
00205         }
00206     }
00207 
00208   else if (g_str_equal (signal_name, "ApplicationIconChanged"))
00209     {
00210       GSequenceIter *iter;
00211       const gchar *icon;
00212       gint32 position;
00213 
00214       if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(iss)")))
00215         return;
00216 
00217       g_variant_get (parameters, "(i&ss)", &position, &icon, NULL);
00218 
00219       g_debug ("changing icon of appindicator at %d to '%s'", position, icon);
00220 
00221       iter = g_sequence_get_iter_at_pos (source->indicators, position);
00222       if (!g_sequence_iter_is_end (iter))
00223         {
00224           HudDbusmenuCollector *collector;
00225 
00226           collector = g_sequence_get (iter);
00227           hud_dbusmenu_collector_set_icon (collector, icon);
00228         }
00229     }
00230 }
00231 
00232 static void
00233 hud_app_indicator_source_ready (GObject      *connection,
00234                                 GAsyncResult *result,
00235                                 gpointer      user_data)
00236 {
00237   HudAppIndicatorSource *source = user_data;
00238   GError *error = NULL;
00239   GVariant *reply;
00240 
00241   g_debug ("GetApplications returned");
00242 
00243   g_clear_object (&source->cancellable);
00244 
00245   reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION (connection), result, &error);
00246 
00247   if (reply)
00248     {
00249       GVariant *description;
00250       GVariantIter *iter;
00251 
00252       g_assert (!source->ready);
00253       source->ready = TRUE;
00254 
00255       g_debug ("going ready");
00256 
00257       g_variant_get (reply, "(a(sisossssss))", &iter);
00258       while ((description = g_variant_iter_next_value (iter)))
00259         {
00260           hud_app_indicator_source_add_indicator (source, description);
00261           g_variant_unref (description);
00262         }
00263       g_variant_iter_free (iter);
00264       g_variant_unref (reply);
00265 
00266       hud_source_changed (HUD_SOURCE (source));
00267     }
00268   else
00269     {
00270       g_warning ("GetApplications returned an error: %s", error->message);
00271       g_error_free (error);
00272     }
00273 
00274   g_object_unref (source);
00275 
00276   g_debug ("done handling GetApplications reply");
00277 }
00278 
00279 static void
00280 hud_app_indicator_source_name_appeared (GDBusConnection *connection,
00281                                         const gchar     *name,
00282                                         const gchar     *name_owner,
00283                                         gpointer         user_data)
00284 {
00285   HudAppIndicatorSource *source = user_data;
00286 
00287   g_debug ("name appeared (owner is %s)", name_owner);
00288 
00289   g_assert (source->subscription == 0);
00290   source->subscription = g_dbus_connection_signal_subscribe (connection, name_owner,
00291                                                              APP_INDICATOR_SERVICE_IFACE, NULL,
00292                                                              APP_INDICATOR_SERVICE_OBJECT_PATH, NULL,
00293                                                              G_DBUS_SIGNAL_FLAGS_NONE,
00294                                                              hud_app_indicator_source_dbus_signal,
00295                                                              source, NULL);
00296 
00297   g_assert (source->cancellable == NULL);
00298   source->cancellable = g_cancellable_new ();
00299   g_dbus_connection_call (connection, name_owner, APP_INDICATOR_SERVICE_OBJECT_PATH, APP_INDICATOR_SERVICE_IFACE,
00300                           "GetApplications", NULL, G_VARIANT_TYPE ("(a(sisossssss))"),
00301                           G_DBUS_CALL_FLAGS_NONE, -1, source->cancellable,
00302                           hud_app_indicator_source_ready, g_object_ref (source));
00303 }
00304 
00305 static void
00306 hud_app_indicator_source_name_vanished (GDBusConnection *connection,
00307                                         const gchar     *name,
00308                                         gpointer         user_data)
00309 {
00310   HudAppIndicatorSource *source = user_data;
00311 
00312   g_debug ("name vanished");
00313 
00314   if (source->subscription > 0)
00315     {
00316       g_dbus_connection_signal_unsubscribe (connection, source->subscription);
00317       source->subscription = 0;
00318     }
00319 
00320   if (source->cancellable)
00321     {
00322       g_cancellable_cancel (source->cancellable);
00323       g_clear_object (&source->cancellable);
00324     }
00325 
00326   if (source->ready)
00327     {
00328       GSequenceIter *iter;
00329 
00330       source->ready = FALSE;
00331 
00332       iter = g_sequence_get_begin_iter (source->indicators);
00333       while (!g_sequence_iter_is_end (iter))
00334         {
00335           HudDbusmenuCollector *collector;
00336           GSequenceIter *next;
00337 
00338           collector = g_sequence_get (iter);
00339           g_signal_handlers_disconnect_by_func (collector, hud_app_indicator_source_collector_changed, source);
00340           next = g_sequence_iter_next (iter);
00341           g_sequence_remove (iter);
00342           iter = next;
00343         }
00344 
00345       hud_source_changed (HUD_SOURCE (source));
00346     }
00347 }
00348 
00349 static void
00350 hud_app_indicator_source_use (HudSource *hud_source)
00351 {
00352   HudAppIndicatorSource *source = HUD_APP_INDICATOR_SOURCE (hud_source);
00353 
00354   if (source->use_count == 0)
00355     g_sequence_foreach (source->indicators, (GFunc) hud_source_use, NULL);
00356 
00357   source->use_count++;
00358 }
00359 
00360 static void
00361 hud_app_indicator_source_unuse (HudSource *hud_source)
00362 {
00363   HudAppIndicatorSource *source = HUD_APP_INDICATOR_SOURCE (hud_source);
00364 
00365   g_return_if_fail (source->use_count > 0);
00366 
00367   source->use_count--;
00368 
00369   if (source->use_count == 0)
00370     g_sequence_foreach (source->indicators, (GFunc) hud_source_unuse, NULL);
00371 }
00372 
00373 static void
00374 hud_app_indicator_source_search (HudSource    *hud_source,
00375                                  GPtrArray    *results_array,
00376                                  HudTokenList *search_tokens)
00377 {
00378   HudAppIndicatorSource *source = HUD_APP_INDICATOR_SOURCE (hud_source);
00379   GSequenceIter *iter;
00380 
00381   iter = g_sequence_get_begin_iter (source->indicators);
00382 
00383   while (!g_sequence_iter_is_end (iter))
00384     {
00385       hud_source_search (g_sequence_get (iter), results_array, search_tokens);
00386       iter = g_sequence_iter_next (iter);
00387     }
00388 }
00389 
00390 static void
00391 hud_app_indicator_source_finalize (GObject *object)
00392 {
00393   g_assert_not_reached ();
00394 }
00395 
00396 static void
00397 hud_app_indicator_source_init (HudAppIndicatorSource *source)
00398 {
00399   g_debug ("online");
00400 
00401   source->indicators = g_sequence_new (g_object_unref);
00402   g_bus_watch_name (G_BUS_TYPE_SESSION, APP_INDICATOR_SERVICE_BUS_NAME, G_BUS_NAME_WATCHER_FLAGS_NONE,
00403                     hud_app_indicator_source_name_appeared, hud_app_indicator_source_name_vanished,
00404                     g_object_ref (source), g_object_unref);
00405 }
00406 
00407 static void
00408 hud_app_indicator_source_iface_init (HudSourceInterface *iface)
00409 {
00410   iface->use = hud_app_indicator_source_use;
00411   iface->unuse = hud_app_indicator_source_unuse;
00412   iface->search = hud_app_indicator_source_search;
00413 }
00414 
00415 static void
00416 hud_app_indicator_source_class_init (HudAppIndicatorSourceClass *class)
00417 {
00418   class->finalize = hud_app_indicator_source_finalize;
00419 }
00420 
00428 HudAppIndicatorSource *
00429 hud_app_indicator_source_new (void)
00430 {
00431   return g_object_new (HUD_TYPE_APP_INDICATOR_SOURCE, NULL);
00432 }