Back to index

libappindicator  12.10.0
app-indicator.c
Go to the documentation of this file.
00001 /*
00002 An object to represent the application as an application indicator
00003 in the system panel.
00004 
00005 Copyright 2009 Canonical Ltd.
00006 
00007 Authors:
00008     Ted Gould <ted@canonical.com>
00009     Cody Russell <cody.russell@canonical.com>
00010 
00011 This program is free software: you can redistribute it and/or modify it
00012 under the terms of either or both of the following licenses:
00013 
00014 1) the GNU Lesser General Public License version 3, as published by the
00015    Free Software Foundation; and/or
00016 2) the GNU Lesser General Public License version 2.1, as published by
00017    the Free Software Foundation.
00018 
00019 This program is distributed in the hope that it will be useful, but
00020 WITHOUT ANY WARRANTY; without even the implied warranties of
00021 MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
00022 PURPOSE.  See the applicable version of the GNU Lesser General Public
00023 License for more details.
00024 
00025 You should have received a copy of both the GNU Lesser General Public
00026 License version 3 and version 2.1 along with this program.  If not, see
00027 <http://www.gnu.org/licenses/>
00028 */
00029 
00030 #ifdef HAVE_CONFIG_H
00031 #include "config.h"
00032 #endif
00033 
00034 #include <libdbusmenu-glib/menuitem.h>
00035 #include <libdbusmenu-glib/server.h>
00036 #include <libdbusmenu-gtk/client.h>
00037 #include <libdbusmenu-gtk/parser.h>
00038 
00039 #include <libindicator/indicator-desktop-shortcuts.h>
00040 
00041 #include "app-indicator.h"
00042 #include "app-indicator-enum-types.h"
00043 #include "application-service-marshal.h"
00044 
00045 #include "gen-notification-watcher.xml.h"
00046 #include "gen-notification-item.xml.h"
00047 
00048 #include "dbus-shared.h"
00049 #include "generate-id.h"
00050 
00051 #define PANEL_ICON_SUFFIX  "panel"
00052 
00066 struct _AppIndicatorPrivate {
00067        /*< Private >*/
00068        /* Properties */
00069        gchar                *id;
00070        gchar                *clean_id;
00071        AppIndicatorCategory  category;
00072        AppIndicatorStatus    status;
00073        gchar                *icon_name;
00074        gchar                *attention_icon_name;
00075        gchar                *icon_theme_path;
00076        DbusmenuServer       *menuservice;
00077        GtkWidget            *menu;
00078        GtkWidget            *sec_activate_target;
00079        gboolean              sec_activate_enabled;
00080        guint32               ordering_index;
00081        gchar *               title;
00082        gchar *               label;
00083        gchar *               label_guide;
00084        gchar *               accessible_desc;
00085        gchar *               att_accessible_desc;
00086        guint                 label_change_idle;
00087 
00088        GtkStatusIcon *       status_icon;
00089        gint                  fallback_timer;
00090 
00091        /* Fun stuff */
00092        GDBusProxy           *watcher_proxy;
00093        GDBusConnection      *connection;
00094        guint                 dbus_registration;
00095        gchar *               path;
00096 
00097        /* Might be used */
00098        IndicatorDesktopShortcuts * shorties;
00099 };
00100 
00101 /* Signals Stuff */
00102 enum {
00103        NEW_ICON,
00104        NEW_ATTENTION_ICON,
00105        NEW_STATUS,
00106        NEW_LABEL,
00107        CONNECTION_CHANGED,
00108        NEW_ICON_THEME_PATH,
00109        SCROLL_EVENT,
00110        LAST_SIGNAL
00111 };
00112 
00113 static guint signals[LAST_SIGNAL] = { 0 };
00114 
00115 /* Enum for the properties so that they can be quickly
00116    found and looked up. */
00117 enum {
00118        PROP_0,
00119        PROP_ID,
00120        PROP_CATEGORY,
00121        PROP_STATUS,
00122        PROP_ICON_NAME,
00123        PROP_ICON_DESC,
00124        PROP_ATTENTION_ICON_NAME,
00125        PROP_ATTENTION_ICON_DESC,
00126        PROP_ICON_THEME_PATH,
00127        PROP_CONNECTED,
00128        PROP_LABEL,
00129        PROP_LABEL_GUIDE,
00130        PROP_ORDERING_INDEX,
00131        PROP_DBUS_MENU_SERVER,
00132        PROP_TITLE
00133 };
00134 
00135 /* The strings so that they can be slowly looked up. */
00136 #define PROP_ID_S                    "id"
00137 #define PROP_CATEGORY_S              "category"
00138 #define PROP_STATUS_S                "status"
00139 #define PROP_ICON_NAME_S             "icon-name"
00140 #define PROP_ICON_DESC_S             "icon-desc"
00141 #define PROP_ATTENTION_ICON_NAME_S   "attention-icon-name"
00142 #define PROP_ATTENTION_ICON_DESC_S   "attention-icon-desc"
00143 #define PROP_ICON_THEME_PATH_S       "icon-theme-path"
00144 #define PROP_CONNECTED_S             "connected"
00145 #define PROP_LABEL_S                 "label"
00146 #define PROP_LABEL_GUIDE_S           "label-guide"
00147 #define PROP_ORDERING_INDEX_S        "ordering-index"
00148 #define PROP_DBUS_MENU_SERVER_S      "dbus-menu-server"
00149 #define PROP_TITLE_S                 "title"
00150 
00151 /* Private macro, shhhh! */
00152 #define APP_INDICATOR_GET_PRIVATE(o) \
00153                              (G_TYPE_INSTANCE_GET_PRIVATE ((o), APP_INDICATOR_TYPE, AppIndicatorPrivate))
00154 
00155 /* Default Path */
00156 #define DEFAULT_ITEM_PATH   "/org/ayatana/NotificationItem"
00157 
00158 /* More constants */
00159 #define DEFAULT_FALLBACK_TIMER  100 /* in milliseconds */
00160 
00161 /* Globals */
00162 static GDBusNodeInfo *            item_node_info = NULL;
00163 static GDBusInterfaceInfo *       item_interface_info = NULL;
00164 static GDBusNodeInfo *            watcher_node_info = NULL;
00165 static GDBusInterfaceInfo *       watcher_interface_info = NULL;
00166 
00167 /* Boiler plate */
00168 static void app_indicator_class_init (AppIndicatorClass *klass);
00169 static void app_indicator_init       (AppIndicator *self);
00170 static void app_indicator_dispose    (GObject *object);
00171 static void app_indicator_finalize   (GObject *object);
00172 /* Property functions */
00173 static void app_indicator_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
00174 static void app_indicator_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
00175 /* Other stuff */
00176 static void signal_label_change (AppIndicator * self);
00177 static void check_connect (AppIndicator * self);
00178 static void register_service_cb (GObject * obj, GAsyncResult * res, gpointer user_data);
00179 static void start_fallback_timer (AppIndicator * self, gboolean disable_timeout);
00180 static gboolean fallback_timer_expire (gpointer data);
00181 static GtkStatusIcon * fallback (AppIndicator * self);
00182 static void status_icon_status_wrapper (AppIndicator * self, const gchar * status, gpointer data);
00183 static gboolean scroll_event_wrapper(GtkWidget *status_icon, GdkEventScroll *event, gpointer user_data);
00184 static gboolean middle_click_wrapper(GtkWidget *status_icon, GdkEventButton *event, gpointer user_data);
00185 static void status_icon_changes (AppIndicator * self, gpointer data);
00186 static void status_icon_activate (GtkStatusIcon * icon, gpointer data);
00187 static void status_icon_menu_activate (GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data);
00188 static void unfallback (AppIndicator * self, GtkStatusIcon * status_icon);
00189 static gchar * append_panel_icon_suffix (const gchar * icon_name);
00190 static void watcher_owner_changed (GObject * obj, GParamSpec * pspec, gpointer user_data);
00191 static void theme_changed_cb (GtkIconTheme * theme, gpointer user_data);
00192 static void sec_activate_target_parent_changed(GtkWidget *menuitem, GtkWidget *old_parent, gpointer   user_data);
00193 static GVariant * bus_get_prop (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * property, GError ** error, gpointer user_data);
00194 static void bus_method_call (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * method, GVariant * params, GDBusMethodInvocation * invocation, gpointer user_data);
00195 static void bus_creation (GObject * obj, GAsyncResult * res, gpointer user_data);
00196 static void bus_watcher_ready (GObject * obj, GAsyncResult * res, gpointer user_data);
00197 
00198 static const GDBusInterfaceVTable item_interface_table = {
00199        method_call:    bus_method_call,
00200        get_property:   bus_get_prop,
00201        set_property:   NULL /* No properties that can be set */
00202 };
00203 
00204 /* GObject type */
00205 G_DEFINE_TYPE (AppIndicator, app_indicator, G_TYPE_OBJECT);
00206 
00207 static void
00208 app_indicator_class_init (AppIndicatorClass *klass)
00209 {
00210        GObjectClass *object_class = G_OBJECT_CLASS (klass);
00211 
00212        g_type_class_add_private (klass, sizeof (AppIndicatorPrivate));
00213 
00214        /* Clean up */
00215        object_class->dispose = app_indicator_dispose;
00216        object_class->finalize = app_indicator_finalize;
00217 
00218        /* Property funcs */
00219        object_class->set_property = app_indicator_set_property;
00220        object_class->get_property = app_indicator_get_property;
00221 
00222        /* Our own funcs */
00223        klass->fallback = fallback;
00224        klass->unfallback = unfallback;
00225 
00226        /* Properties */
00227 
00234        g_object_class_install_property (object_class,
00235                                          PROP_ID,
00236                                          g_param_spec_string(PROP_ID_S,
00237                                                              "The ID for this indicator",
00238                                                              "An ID that should be unique, but used consistently by this program and its indicator.",
00239                                                              NULL,
00240                                                              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
00241 
00248        g_object_class_install_property (object_class,
00249                                          PROP_CATEGORY,
00250                                          g_param_spec_string (PROP_CATEGORY_S,
00251                                                               "Indicator Category",
00252                                                               "The type of indicator that this represents.  Please don't use 'other'. Defaults to 'ApplicationStatus'.",
00253                                                               NULL,
00254                                                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY));
00255 
00262        g_object_class_install_property (object_class,
00263                                          PROP_STATUS,
00264                                          g_param_spec_string (PROP_STATUS_S,
00265                                                               "Indicator Status",
00266                                                               "Whether the indicator is shown or requests attention. Defaults to 'Passive'.",
00267                                                               NULL,
00268                                                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
00269 
00275        g_object_class_install_property(object_class,
00276                                        PROP_ICON_NAME,
00277                                        g_param_spec_string (PROP_ICON_NAME_S,
00278                                                             "An icon for the indicator",
00279                                                             "The default icon that is shown for the indicator.",
00280                                                             NULL,
00281                                                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
00287        g_object_class_install_property(object_class,
00288                                        PROP_ICON_DESC,
00289                                        g_param_spec_string (PROP_ICON_DESC_S,
00290                                                             "A description of the icon for the indicator",
00291                                                             "A description of the default icon that is shown for the indicator.",
00292                                                             NULL,
00293                                                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
00294 
00301        g_object_class_install_property (object_class,
00302                                          PROP_ATTENTION_ICON_NAME,
00303                                          g_param_spec_string (PROP_ATTENTION_ICON_NAME_S,
00304                                                               "An icon to show when the indicator request attention.",
00305                                                               "If the indicator sets it's status to 'attention' then this icon is shown.",
00306                                                               NULL,
00307                                                               G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
00314        g_object_class_install_property (object_class,
00315                                         PROP_ATTENTION_ICON_DESC,
00316                                         g_param_spec_string (PROP_ATTENTION_ICON_DESC_S,
00317                                                              "A description of the icon to show when the indicator request attention.",
00318                                                              "When the indicator is an attention mode this should describe the icon shown",
00319                                                              NULL,
00320                                                              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
00327        g_object_class_install_property(object_class,
00328                                        PROP_ICON_THEME_PATH,
00329                                        g_param_spec_string (PROP_ICON_THEME_PATH_S,
00330                                                              "An additional path for custom icons.",
00331                                                              "An additional place to look for icon names that may be installed by the application.",
00332                                                              NULL,
00333                                                              G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
00334        
00341        g_object_class_install_property (object_class,
00342                                          PROP_CONNECTED,
00343                                          g_param_spec_boolean (PROP_CONNECTED_S,
00344                                                                "Whether we're conneced to a watcher",
00345                                                                "Pretty simple, true if we have a reasonable expectation of being displayed through this object.  You should hide your TrayIcon if so.",
00346                                                                FALSE,
00347                                                                G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
00358        g_object_class_install_property(object_class,
00359                                        PROP_LABEL,
00360                                        g_param_spec_string (PROP_LABEL_S,
00361                                                             "A label next to the icon",
00362                                                             "A label to provide dynamic information.",
00363                                                             NULL,
00364                                                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
00376        g_object_class_install_property(object_class,
00377                                        PROP_LABEL_GUIDE,
00378                                        g_param_spec_string (PROP_LABEL_GUIDE_S,
00379                                                             "A string to size the space available for the label.",
00380                                                             "To ensure that the label does not cause the panel to 'jiggle' this string should provide information on how much space it could take.",
00381                                                             NULL,
00382                                                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
00395        g_object_class_install_property(object_class,
00396                                        PROP_ORDERING_INDEX,
00397                                        g_param_spec_uint (PROP_ORDERING_INDEX_S,
00398                                                           "The location that this app indicator should be in the list.",
00399                                                           "A way to override the default ordering of the applications by providing a very specific idea of where this entry should be placed.",
00400                                                           0, G_MAXUINT32, 0,
00401                                                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
00402 
00409        g_object_class_install_property(object_class,
00410                                        PROP_DBUS_MENU_SERVER,
00411                                        g_param_spec_object (PROP_DBUS_MENU_SERVER_S,
00412                                                             "The internal DBusmenu Server",
00413                                                             "DBusmenu server which is available for testing the application indicators.",
00414                                                             DBUSMENU_TYPE_SERVER,
00415                                                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
00424        g_object_class_install_property(object_class,
00425                                        PROP_TITLE,
00426                                        g_param_spec_string (PROP_TITLE_S,
00427                                                             "Title of the application indicator",
00428                                                             "A human readable way to refer to this application indicator in the UI.",
00429                                                             NULL,
00430                                                             G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
00431 
00432        /* Signals */
00433 
00440        signals[NEW_ICON] = g_signal_new (APP_INDICATOR_SIGNAL_NEW_ICON,
00441                                          G_TYPE_FROM_CLASS(klass),
00442                                          G_SIGNAL_RUN_LAST,
00443                                          G_STRUCT_OFFSET (AppIndicatorClass, new_icon),
00444                                          NULL, NULL,
00445                                          g_cclosure_marshal_VOID__VOID,
00446                                          G_TYPE_NONE, 0, G_TYPE_NONE);
00447 
00454        signals[NEW_ATTENTION_ICON] = g_signal_new (APP_INDICATOR_SIGNAL_NEW_ATTENTION_ICON,
00455                                                    G_TYPE_FROM_CLASS(klass),
00456                                                    G_SIGNAL_RUN_LAST,
00457                                                    G_STRUCT_OFFSET (AppIndicatorClass, new_attention_icon),
00458                                                    NULL, NULL,
00459                                                    g_cclosure_marshal_VOID__VOID,
00460                                                    G_TYPE_NONE, 0, G_TYPE_NONE);
00461 
00469        signals[NEW_STATUS] = g_signal_new (APP_INDICATOR_SIGNAL_NEW_STATUS,
00470                                            G_TYPE_FROM_CLASS(klass),
00471                                            G_SIGNAL_RUN_LAST,
00472                                            G_STRUCT_OFFSET (AppIndicatorClass, new_status),
00473                                            NULL, NULL,
00474                                            g_cclosure_marshal_VOID__STRING,
00475                                            G_TYPE_NONE, 1,
00476                                             G_TYPE_STRING);
00477 
00487        signals[NEW_LABEL] = g_signal_new (APP_INDICATOR_SIGNAL_NEW_LABEL,
00488                                            G_TYPE_FROM_CLASS(klass),
00489                                            G_SIGNAL_RUN_LAST,
00490                                            G_STRUCT_OFFSET (AppIndicatorClass, new_label),
00491                                            NULL, NULL,
00492                                            _application_service_marshal_VOID__STRING_STRING,
00493                                            G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
00494 
00502        signals[CONNECTION_CHANGED] = g_signal_new (APP_INDICATOR_SIGNAL_CONNECTION_CHANGED,
00503                                                    G_TYPE_FROM_CLASS(klass),
00504                                                    G_SIGNAL_RUN_LAST,
00505                                                    G_STRUCT_OFFSET (AppIndicatorClass, connection_changed),
00506                                                    NULL, NULL,
00507                                                    g_cclosure_marshal_VOID__BOOLEAN,
00508                                                    G_TYPE_NONE, 1, G_TYPE_BOOLEAN, G_TYPE_NONE);
00509 
00517        signals[NEW_ICON_THEME_PATH] = g_signal_new (APP_INDICATOR_SIGNAL_NEW_ICON_THEME_PATH,
00518                                          G_TYPE_FROM_CLASS(klass),
00519                                          G_SIGNAL_RUN_LAST,
00520                                          G_STRUCT_OFFSET (AppIndicatorClass, new_icon_theme_path),
00521                                          NULL, NULL,
00522                                          g_cclosure_marshal_VOID__STRING,
00523                                          G_TYPE_NONE, 1, G_TYPE_STRING);
00524 
00533        signals[SCROLL_EVENT] = g_signal_new (APP_INDICATOR_SIGNAL_SCROLL_EVENT,
00534                                          G_TYPE_FROM_CLASS(klass),
00535                                          G_SIGNAL_RUN_LAST,
00536                                          G_STRUCT_OFFSET (AppIndicatorClass, scroll_event),
00537                                          NULL, NULL,
00538                                          _application_service_marshal_VOID__INT_UINT,
00539                                          G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_UINT);
00540 
00541        /* DBus interfaces */
00542        if (item_node_info == NULL) {
00543               GError * error = NULL;
00544 
00545               item_node_info = g_dbus_node_info_new_for_xml(_notification_item, &error);
00546               if (error != NULL) {
00547                      g_error("Unable to parse Notification Item DBus interface: %s", error->message);
00548                      g_error_free(error);
00549               }
00550        }
00551 
00552        if (item_interface_info == NULL && item_node_info != NULL) {
00553               item_interface_info = g_dbus_node_info_lookup_interface(item_node_info, NOTIFICATION_ITEM_DBUS_IFACE);
00554 
00555               if (item_interface_info == NULL) {
00556                      g_error("Unable to find interface '" NOTIFICATION_ITEM_DBUS_IFACE "'");
00557               }
00558        }
00559 
00560        if (watcher_node_info == NULL) {
00561               GError * error = NULL;
00562 
00563               watcher_node_info = g_dbus_node_info_new_for_xml(_notification_watcher, &error);
00564               if (error != NULL) {
00565                      g_error("Unable to parse Notification Item DBus interface: %s", error->message);
00566                      g_error_free(error);
00567               }
00568        }
00569 
00570        if (watcher_interface_info == NULL && watcher_node_info != NULL) {
00571               watcher_interface_info = g_dbus_node_info_lookup_interface(watcher_node_info, NOTIFICATION_WATCHER_DBUS_IFACE);
00572 
00573               if (watcher_interface_info == NULL) {
00574                      g_error("Unable to find interface '" NOTIFICATION_WATCHER_DBUS_IFACE "'");
00575               }
00576        }
00577 
00578        return;
00579 }
00580 
00581 static void
00582 app_indicator_init (AppIndicator *self)
00583 {
00584        AppIndicatorPrivate * priv = APP_INDICATOR_GET_PRIVATE(self);
00585 
00586        priv->id = NULL;
00587        priv->clean_id = NULL;
00588        priv->category = APP_INDICATOR_CATEGORY_OTHER;
00589        priv->status = APP_INDICATOR_STATUS_PASSIVE;
00590        priv->icon_name = NULL;
00591        priv->attention_icon_name = NULL;
00592        priv->icon_theme_path = NULL;
00593        priv->menu = NULL;
00594        priv->menuservice = NULL;
00595        priv->ordering_index = 0;
00596        priv->title = NULL;
00597        priv->label = NULL;
00598        priv->label_guide = NULL;
00599        priv->label_change_idle = 0;
00600 
00601        priv->watcher_proxy = NULL;
00602        priv->connection = NULL;
00603        priv->dbus_registration = 0;
00604        priv->path = NULL;
00605 
00606        priv->status_icon = NULL;
00607        priv->fallback_timer = 0;
00608 
00609        priv->shorties = NULL;
00610 
00611        priv->sec_activate_target = NULL;
00612        priv->sec_activate_enabled = FALSE;
00613 
00614        /* Start getting the session bus */
00615        g_object_ref(self); /* ref for the bus creation callback */
00616        g_bus_get(G_BUS_TYPE_SESSION, NULL, bus_creation, self);
00617 
00618        g_signal_connect(G_OBJECT(gtk_icon_theme_get_default()),
00619               "changed", G_CALLBACK(theme_changed_cb), self);
00620 
00621        self->priv = priv;
00622 
00623        return;
00624 }
00625 
00626 /* Free all objects, make sure that all the dbus
00627    signals are sent out before we shut this down. */
00628 static void
00629 app_indicator_dispose (GObject *object)
00630 {
00631        AppIndicator *self = APP_INDICATOR (object);
00632        AppIndicatorPrivate *priv = self->priv;
00633 
00634        if (priv->shorties != NULL) {
00635               g_object_unref(G_OBJECT(priv->shorties));
00636               priv->shorties = NULL;
00637        }
00638 
00639        if (priv->status != APP_INDICATOR_STATUS_PASSIVE) {
00640               app_indicator_set_status(self, APP_INDICATOR_STATUS_PASSIVE);
00641        }
00642 
00643        if (priv->status_icon != NULL) {
00644               AppIndicatorClass * class = APP_INDICATOR_GET_CLASS(object);
00645               if (class->unfallback != NULL) {
00646                      class->unfallback(self, priv->status_icon);
00647               }
00648               priv->status_icon = NULL;
00649        }
00650 
00651        if (priv->fallback_timer != 0) {
00652               g_source_remove(priv->fallback_timer);
00653               priv->fallback_timer = 0;
00654        }
00655 
00656        if (priv->label_change_idle != 0) {
00657               g_source_remove(priv->label_change_idle);
00658               priv->label_change_idle = 0;
00659        }
00660 
00661        if (priv->menu != NULL) {
00662               g_object_unref(G_OBJECT(priv->menu));
00663               priv->menu = NULL;
00664        }
00665 
00666        if (priv->menuservice != NULL) {
00667               g_object_unref (priv->menuservice);
00668        }
00669 
00670        if (priv->watcher_proxy != NULL) {
00671               g_signal_handlers_disconnect_by_func(G_OBJECT(priv->watcher_proxy), watcher_owner_changed, self);
00672               g_object_unref(G_OBJECT(priv->watcher_proxy));
00673               priv->watcher_proxy = NULL;
00674 
00675            /* Emit the AppIndicator::connection-changed signal*/
00676         g_signal_emit (self, signals[CONNECTION_CHANGED], 0, FALSE);
00677        }
00678 
00679        if (priv->dbus_registration != 0) {
00680               g_dbus_connection_unregister_object(priv->connection, priv->dbus_registration);
00681               priv->dbus_registration = 0;
00682        }
00683 
00684        if (priv->connection != NULL) {
00685               g_object_unref(G_OBJECT(priv->connection));
00686               priv->connection = NULL;
00687        }
00688 
00689        if (priv->sec_activate_target != NULL) {
00690               g_signal_handlers_disconnect_by_func (priv->sec_activate_target, sec_activate_target_parent_changed, self);
00691               g_object_unref(G_OBJECT(priv->sec_activate_target));
00692               priv->sec_activate_target = NULL;
00693        }
00694 
00695        g_signal_handlers_disconnect_by_func(gtk_icon_theme_get_default(), G_CALLBACK(theme_changed_cb), self);
00696 
00697        G_OBJECT_CLASS (app_indicator_parent_class)->dispose (object);
00698        return;
00699 }
00700 
00701 /* Free all of the memory that we could be using in
00702    the object. */
00703 static void
00704 app_indicator_finalize (GObject *object)
00705 {
00706        AppIndicator * self = APP_INDICATOR(object);
00707        AppIndicatorPrivate *priv = self->priv;
00708 
00709        if (priv->status != APP_INDICATOR_STATUS_PASSIVE) {
00710               g_warning("Finalizing Application Status with the status set to: %d", priv->status);
00711        }
00712 
00713        if (priv->id != NULL) {
00714               g_free(priv->id);
00715               priv->id = NULL;
00716        }
00717 
00718        if (priv->clean_id != NULL) {
00719               g_free(priv->clean_id);
00720               priv->clean_id = NULL;
00721        }
00722 
00723        if (priv->icon_name != NULL) {
00724               g_free(priv->icon_name);
00725               priv->icon_name = NULL;
00726        }
00727 
00728        if (priv->attention_icon_name != NULL) {
00729               g_free(priv->attention_icon_name);
00730               priv->attention_icon_name = NULL;
00731        }
00732 
00733        if (priv->icon_theme_path != NULL) {
00734               g_free(priv->icon_theme_path);
00735               priv->icon_theme_path = NULL;
00736        }
00737        
00738        if (priv->title != NULL) {
00739               g_free(priv->title);
00740               priv->title = NULL;
00741        }
00742 
00743        if (priv->label != NULL) {
00744               g_free(priv->label);
00745               priv->label = NULL;
00746        }
00747 
00748        if (priv->label_guide != NULL) {
00749               g_free(priv->label_guide);
00750               priv->label_guide = NULL;
00751        }
00752 
00753        if (priv->accessible_desc != NULL) {
00754               g_free(priv->accessible_desc);
00755               priv->accessible_desc = NULL;
00756        }
00757 
00758        if (priv->att_accessible_desc != NULL) {
00759               g_free(priv->att_accessible_desc);
00760               priv->att_accessible_desc = NULL;
00761        }
00762 
00763        if (priv->path != NULL) {
00764               g_free(priv->path);
00765               priv->path = NULL;
00766        }
00767 
00768        G_OBJECT_CLASS (app_indicator_parent_class)->finalize (object);
00769        return;
00770 }
00771 
00772 #define WARN_BAD_TYPE(prop, value)  g_warning("Can not work with property '%s' with value of type '%s'.", prop, G_VALUE_TYPE_NAME(value))
00773 
00774 /* Set some properties */
00775 static void
00776 app_indicator_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec)
00777 {
00778         AppIndicator *self = APP_INDICATOR (object);
00779         AppIndicatorPrivate *priv = self->priv;
00780         GEnumValue *enum_val;
00781 
00782         switch (prop_id) {
00783         case PROP_ID:
00784           if (priv->id != NULL) {
00785             g_warning ("Resetting ID value when I already had a value of: %s", priv->id);
00786             break;
00787           }
00788 
00789           priv->id = g_strdup (g_value_get_string (value));
00790 
00791           priv->clean_id = g_strdup(priv->id);
00792           gchar * cleaner;
00793           for (cleaner = priv->clean_id; *cleaner != '\0'; cleaner++) {
00794             if (!g_ascii_isalnum(*cleaner)) {
00795               *cleaner = '_';
00796             }
00797           }
00798 
00799           check_connect (self);
00800           break;
00801 
00802         case PROP_CATEGORY:
00803           enum_val = g_enum_get_value_by_nick ((GEnumClass *) g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_CATEGORY),
00804                                                g_value_get_string (value));
00805 
00806           if (priv->category != enum_val->value)
00807             {
00808               priv->category = enum_val->value;
00809             }
00810 
00811           break;
00812 
00813         case PROP_STATUS:
00814           enum_val = g_enum_get_value_by_nick ((GEnumClass *) g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_STATUS),
00815                                                g_value_get_string (value));
00816 
00817           app_indicator_set_status (APP_INDICATOR (object),
00818                                     enum_val->value);
00819           break;
00820 
00821         case PROP_ICON_NAME:
00822           app_indicator_set_icon_full (APP_INDICATOR (object),
00823                                        g_value_get_string (value),
00824                                        priv->accessible_desc);
00825           check_connect (self);
00826           break;
00827 
00828         case PROP_ICON_DESC:
00829           app_indicator_set_icon_full (APP_INDICATOR (object),
00830                                        priv->icon_name,
00831                                        g_value_get_string (value));
00832           check_connect (self);
00833           break;
00834 
00835         case PROP_ATTENTION_ICON_NAME:
00836           app_indicator_set_attention_icon_full (APP_INDICATOR (object),
00837                                                  g_value_get_string (value),
00838                                                  priv->att_accessible_desc);
00839           break;
00840 
00841         case PROP_ATTENTION_ICON_DESC:
00842           app_indicator_set_attention_icon_full (APP_INDICATOR (object),
00843                                                  priv->attention_icon_name,
00844                                                  g_value_get_string (value));
00845           break;
00846 
00847         case PROP_ICON_THEME_PATH:
00848           app_indicator_set_icon_theme_path (APP_INDICATOR (object),
00849                                             g_value_get_string (value));
00850           check_connect (self);
00851           break;
00852 
00853               case PROP_LABEL: {
00854                 gchar * oldlabel = priv->label;
00855                 priv->label = g_value_dup_string(value);
00856 
00857                 if (priv->label != NULL && priv->label[0] == '\0') {
00858                      g_free(priv->label);
00859                      priv->label = NULL;
00860                 }
00861 
00862                 if (g_strcmp0(oldlabel, priv->label) != 0) {
00863                   signal_label_change(APP_INDICATOR(object));
00864                 }
00865 
00866                 if (oldlabel != NULL) {
00867                      g_free(oldlabel);
00868                 }
00869                 break;
00870               }
00871               case PROP_TITLE: {
00872                 gchar * oldtitle = priv->title;
00873                 priv->title = g_value_dup_string(value);
00874 
00875                 if (priv->title != NULL && priv->title[0] == '\0') {
00876                      g_free(priv->title);
00877                      priv->title = NULL;
00878                 }
00879 
00880                 if (g_strcmp0(oldtitle, priv->title) != 0 && self->priv->connection != NULL) {
00881                      GError * error = NULL;
00882 
00883                      g_dbus_connection_emit_signal(self->priv->connection,
00884                                                                         NULL,
00885                                                                         self->priv->path,
00886                                                                         NOTIFICATION_ITEM_DBUS_IFACE,
00887                                                                         "NewTitle",
00888                                                                         NULL,
00889                                                                         &error);
00890 
00891                      if (error != NULL) {
00892                             g_warning("Unable to send signal for NewTitle: %s", error->message);
00893                             g_error_free(error);
00894                      }
00895                 }
00896 
00897                 if (oldtitle != NULL) {
00898                      g_free(oldtitle);
00899                 }
00900 
00901                 if (priv->status_icon != NULL) {
00902                      gtk_status_icon_set_title(priv->status_icon, priv->title ? priv->title : "");
00903                 }
00904                 break;
00905               }
00906               case PROP_LABEL_GUIDE: {
00907                 gchar * oldguide = priv->label_guide;
00908                 priv->label_guide = g_value_dup_string(value);
00909 
00910                 if (g_strcmp0(oldguide, priv->label_guide) != 0) {
00911                   signal_label_change(APP_INDICATOR(object));
00912                 }
00913 
00914                 if (priv->label_guide != NULL && priv->label_guide[0] == '\0') {
00915                      g_free(priv->label_guide);
00916                      priv->label_guide = NULL;
00917                 }
00918 
00919                 if (oldguide != NULL) {
00920                      g_free(oldguide);
00921                 }
00922                 break;
00923               }
00924               case PROP_ORDERING_INDEX:
00925                 priv->ordering_index = g_value_get_uint(value);
00926                 break;
00927 
00928               case PROP_DBUS_MENU_SERVER:
00929                      g_clear_object (&priv->menuservice);
00930                      priv->menuservice = DBUSMENU_SERVER (g_value_dup_object(value));
00931                      break;
00932 
00933         default:
00934           G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
00935           break;
00936         }
00937 
00938        return;
00939 }
00940 
00941 /* Function to fill our value with the property it's requesting. */
00942 static void
00943 app_indicator_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec)
00944 {
00945         AppIndicator *self = APP_INDICATOR (object);
00946         AppIndicatorPrivate *priv = self->priv;
00947         GEnumValue *enum_value;
00948 
00949         switch (prop_id) {
00950         case PROP_ID:
00951           g_value_set_string (value, priv->id);
00952           break;
00953 
00954         case PROP_CATEGORY:
00955           enum_value = g_enum_get_value ((GEnumClass *) g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_CATEGORY), priv->category);
00956           g_value_set_string (value, enum_value->value_nick);
00957           break;
00958 
00959         case PROP_STATUS:
00960           enum_value = g_enum_get_value ((GEnumClass *) g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_STATUS), priv->status);
00961           g_value_set_string (value, enum_value->value_nick);
00962           break;
00963 
00964         case PROP_ICON_NAME:
00965           g_value_set_string (value, priv->icon_name);
00966           break;
00967 
00968         case PROP_ICON_DESC:
00969           g_value_set_string (value, priv->accessible_desc);
00970           break;
00971 
00972         case PROP_ATTENTION_ICON_NAME:
00973           g_value_set_string (value, priv->attention_icon_name);
00974           break;
00975 
00976         case PROP_ATTENTION_ICON_DESC:
00977           g_value_set_string (value, priv->att_accessible_desc);
00978           break;
00979 
00980         case PROP_ICON_THEME_PATH:
00981           g_value_set_string (value, priv->icon_theme_path);
00982           break;
00983 
00984               case PROP_CONNECTED: {
00985                      gboolean connected = FALSE;
00986 
00987                      if (priv->watcher_proxy != NULL) {
00988                             gchar * name = g_dbus_proxy_get_name_owner(priv->watcher_proxy);
00989                             if (name != NULL) {
00990                                    connected = TRUE;
00991                                    g_free(name);
00992                             }
00993                      }
00994 
00995                      g_value_set_boolean (value, connected);
00996                      break;
00997               }
00998 
00999         case PROP_LABEL:
01000           g_value_set_string (value, priv->label);
01001           break;
01002 
01003         case PROP_LABEL_GUIDE:
01004           g_value_set_string (value, priv->label_guide);
01005           break;
01006 
01007               case PROP_ORDERING_INDEX:
01008                 g_value_set_uint(value, priv->ordering_index);
01009                 break;
01010 
01011               case PROP_DBUS_MENU_SERVER:
01012                      g_value_set_object(value, priv->menuservice);
01013                      break;
01014 
01015               case PROP_TITLE:
01016                      g_value_set_string(value, priv->title);
01017                      break;
01018 
01019         default:
01020           G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
01021           break;
01022         }
01023 
01024        return;
01025 }
01026 
01027 /* DBus bus has been created, well maybe, but we got a call
01028    back about it so we need to check into it. */
01029 static void
01030 bus_creation (GObject * obj, GAsyncResult * res, gpointer user_data)
01031 {
01032        GError * error = NULL;
01033 
01034        GDBusConnection * connection = g_bus_get_finish(res, &error);
01035        if (error != NULL) {
01036               g_warning("Unable to get the session bus: %s", error->message);
01037               g_error_free(error);
01038               g_object_unref(G_OBJECT(user_data));
01039               return;
01040        }
01041 
01042        AppIndicator * app = APP_INDICATOR(user_data);
01043        app->priv->connection = connection;
01044 
01045        /* If the connection was blocking the exporting of the
01046           object this function will export everything. */
01047        check_connect(app);
01048 
01049        g_object_unref(G_OBJECT(app));
01050 
01051        return;
01052 }
01053 
01054 static void
01055 bus_method_call (GDBusConnection * connection, const gchar * sender,
01056                  const gchar * path, const gchar * interface,
01057                  const gchar * method, GVariant * params,
01058                  GDBusMethodInvocation * invocation, gpointer user_data)
01059 {
01060        g_return_if_fail(IS_APP_INDICATOR(user_data));
01061 
01062        AppIndicator * app = APP_INDICATOR(user_data);
01063        AppIndicatorPrivate * priv = app->priv;
01064        GVariant * retval = NULL;
01065 
01066        if (g_strcmp0(method, "Scroll") == 0) {
01067               guint direction;
01068               gint delta;
01069               const gchar *orientation;
01070 
01071               g_variant_get(params, "(i&s)", &delta, &orientation);
01072 
01073               if (g_strcmp0(orientation, "horizontal") == 0) {
01074                      direction = (delta >= 0) ? GDK_SCROLL_RIGHT : GDK_SCROLL_LEFT;
01075               } else if (g_strcmp0(orientation, "vertical") == 0) {
01076                      direction = (delta >= 0) ? GDK_SCROLL_DOWN : GDK_SCROLL_UP;
01077               } else {
01078                      g_dbus_method_invocation_return_value(invocation, retval);
01079                      return;
01080               }
01081 
01082               delta = ABS(delta);
01083               g_signal_emit(app, signals[SCROLL_EVENT], 0, delta, direction);
01084 
01085        } else if (g_strcmp0(method, "SecondaryActivate") == 0 ||
01086                   g_strcmp0(method, "XAyatanaSecondaryActivate") == 0) {
01087               GtkWidget *menuitem = priv->sec_activate_target;
01088               
01089               if (priv->sec_activate_enabled && menuitem &&
01090                   gtk_widget_get_visible (menuitem) &&
01091                   gtk_widget_get_sensitive (menuitem))
01092               {
01093                      gtk_widget_activate (menuitem);
01094               }
01095        } else {
01096               g_warning("Calling method '%s' on the app-indicator and it's unknown", method);
01097        }
01098 
01099        g_dbus_method_invocation_return_value(invocation, retval);
01100 }
01101 
01102 /* DBus is asking for a property so we should figure out what it
01103    wants and try and deliver. */
01104 static GVariant *
01105 bus_get_prop (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * property, GError ** error, gpointer user_data)
01106 {
01107        g_return_val_if_fail(IS_APP_INDICATOR(user_data), NULL);
01108        AppIndicator * app = APP_INDICATOR(user_data);
01109        AppIndicatorPrivate *priv = app->priv;
01110 
01111        if (g_strcmp0(property, "Id") == 0) {
01112               return g_variant_new_string(app->priv->id ? app->priv->id : "");
01113        } else if (g_strcmp0(property, "Category") == 0) {
01114         GEnumValue *enum_value;
01115               enum_value = g_enum_get_value ((GEnumClass *) g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_CATEGORY), priv->category);
01116               return g_variant_new_string(enum_value->value_nick ? enum_value->value_nick : "");
01117        } else if (g_strcmp0(property, "Status") == 0) {
01118         GEnumValue *enum_value;
01119               enum_value = g_enum_get_value ((GEnumClass *) g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_STATUS), priv->status);
01120               return g_variant_new_string(enum_value->value_nick ? enum_value->value_nick : "");
01121        } else if (g_strcmp0(property, "IconName") == 0) {
01122               return g_variant_new_string(priv->icon_name ? priv->icon_name : "");
01123        } else if (g_strcmp0(property, "AttentionIconName") == 0) {
01124               return g_variant_new_string(priv->attention_icon_name ? priv->attention_icon_name : "");
01125        } else if (g_strcmp0(property, "Title") == 0) {
01126               const gchar * output = NULL;
01127               if (priv->title == NULL) {
01128                      const gchar * name = g_get_application_name();
01129                      if (name != NULL) {
01130                             output = name;
01131                      } else {
01132                             output = "";
01133                      }
01134               } else {
01135                      output = priv->title;
01136               }
01137               return g_variant_new_string(output);
01138        } else if (g_strcmp0(property, "IconThemePath") == 0) {
01139               return g_variant_new_string(priv->icon_theme_path ? priv->icon_theme_path : "");
01140        } else if (g_strcmp0(property, "Menu") == 0) {
01141               if (priv->menuservice != NULL) {
01142                      GValue strval = { 0 };
01143                      g_value_init(&strval, G_TYPE_STRING);
01144                      g_object_get_property (G_OBJECT (priv->menuservice), DBUSMENU_SERVER_PROP_DBUS_OBJECT, &strval);
01145                      GVariant * var = g_variant_new("o", g_value_get_string(&strval));
01146                      g_value_unset(&strval);
01147                      return var;
01148               } else {
01149                      return g_variant_new("o", "/");
01150               }
01151        } else if (g_strcmp0(property, "XAyatanaLabel") == 0) {
01152               return g_variant_new_string(priv->label ? priv->label : "");
01153        } else if (g_strcmp0(property, "XAyatanaLabelGuide") == 0) {
01154               return g_variant_new_string(priv->label_guide ? priv->label_guide : "");
01155        } else if (g_strcmp0(property, "XAyatanaOrderingIndex") == 0) {
01156               return g_variant_new_uint32(priv->ordering_index);
01157        } else if (g_strcmp0(property, "IconAccessibleDesc") == 0) {
01158               return g_variant_new_string(priv->accessible_desc ? priv->accessible_desc : "");
01159        } else if (g_strcmp0(property, "AttentionAccessibleDesc") == 0) {
01160               return g_variant_new_string(priv->att_accessible_desc ? priv->att_accessible_desc : "");
01161        }
01162 
01163        *error = g_error_new(0, 0, "Unknown property: %s", property);
01164        return NULL;
01165 }
01166 
01167 /* Sends the label changed signal and resets the source ID */
01168 static gboolean
01169 signal_label_change_idle (gpointer user_data)
01170 {
01171        AppIndicator * self = (AppIndicator *)user_data;
01172        AppIndicatorPrivate *priv = self->priv;
01173 
01174        gchar * label = priv->label != NULL ? priv->label : "";
01175        gchar * guide = priv->label_guide != NULL ? priv->label_guide : "";
01176 
01177        g_signal_emit(G_OBJECT(self), signals[NEW_LABEL], 0,
01178                      label, guide, TRUE);
01179        if (priv->dbus_registration != 0 && priv->connection != NULL) {
01180               GError * error = NULL;
01181 
01182               g_dbus_connection_emit_signal(priv->connection,
01183                                             NULL,
01184                                             priv->path,
01185                                             NOTIFICATION_ITEM_DBUS_IFACE,
01186                                             "XAyatanaNewLabel",
01187                                             g_variant_new("(ss)", label, guide),
01188                                             &error);
01189 
01190               if (error != NULL) {
01191                      g_warning("Unable to send signal for NewIcon: %s", error->message);
01192                      g_error_free(error);
01193               }
01194        }
01195 
01196        priv->label_change_idle = 0;
01197 
01198        return FALSE;
01199 }
01200 
01201 /* Sets up an idle function to send the label changed signal
01202    so that we don't send it too many times. */
01203 static void
01204 signal_label_change (AppIndicator * self)
01205 {
01206        AppIndicatorPrivate *priv = self->priv;
01207 
01208        /* don't set it twice */
01209        if (priv->label_change_idle != 0) {
01210               return;
01211        }
01212 
01213        priv->label_change_idle = g_idle_add(signal_label_change_idle, self);
01214        return;
01215 }
01216 
01217 /* This function is used to see if we have enough information to
01218    connect to things.  If we do, and we're not connected, it
01219    connects for us. */
01220 static void
01221 check_connect (AppIndicator *self)
01222 {
01223        AppIndicatorPrivate *priv = self->priv;
01224 
01225        /* Do we have a connection? */
01226        if (priv->connection == NULL) return;
01227 
01228        /* If we already have a proxy, let's see if it has someone
01229           implementing it.  If not, we can't do much more than to
01230           do nothing. */
01231        if (priv->watcher_proxy != NULL) {
01232               gchar * name = g_dbus_proxy_get_name_owner(priv->watcher_proxy);
01233               if (name == NULL) {
01234                      return;
01235               }
01236               g_free(name);
01237        }
01238 
01239        /* Do we have enough information? */
01240        if (priv->menu == NULL) return;
01241        if (priv->icon_name == NULL) return;
01242        if (priv->id == NULL) return;
01243 
01244        if (priv->path == NULL) {
01245               priv->path = g_strdup_printf(DEFAULT_ITEM_PATH "/%s", priv->clean_id);
01246        }
01247 
01248        if (priv->dbus_registration == 0) {
01249               GError * error = NULL;
01250               priv->dbus_registration = g_dbus_connection_register_object(priv->connection,
01251                                                                           priv->path,
01252                                                                           item_interface_info,
01253                                                                           &item_interface_table,
01254                                                                           self,
01255                                                                           NULL,
01256                                                                           &error);
01257               if (error != NULL) {
01258                      g_warning("Unable to register object on path '%s': %s", priv->path, error->message);
01259                      g_error_free(error);
01260                      return;
01261               }
01262        }
01263 
01264        /* NOTE: It's really important the order here.  We make sure to *publish*
01265           the object on the bus and *then* get the proxy.  The reason is that we
01266           want to ensure all the filters are setup before talking to the watcher
01267           and that's where the order is important. */
01268 
01269        g_object_ref(G_OBJECT(self)); /* Unref in watcher_ready() */
01270        if (priv->watcher_proxy == NULL) {
01271               /* Build Watcher Proxy */
01272               g_dbus_proxy_new(priv->connection,
01273                                G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES|
01274                                G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, /* We don't use these, don't bother with them */
01275                                watcher_interface_info,
01276                                NOTIFICATION_WATCHER_DBUS_ADDR,
01277                                NOTIFICATION_WATCHER_DBUS_OBJ,
01278                                NOTIFICATION_WATCHER_DBUS_IFACE,
01279                                NULL, /* cancellable */
01280                                bus_watcher_ready,
01281                                self);
01282        } else {
01283               bus_watcher_ready(NULL, NULL, self);
01284        }
01285 
01286        return;
01287 }
01288 
01289 /* Callback for when the watcher proxy has been created, or not
01290    but we got called none-the-less. */
01291 static void
01292 bus_watcher_ready (GObject * obj, GAsyncResult * res, gpointer user_data)
01293 {
01294        GError * error = NULL;
01295 
01296        GDBusProxy * proxy = NULL;
01297        if (res != NULL) {
01298               proxy = g_dbus_proxy_new_finish(res, &error);
01299        }
01300 
01301        if (error != NULL) {
01302               /* Unable to get proxy, but we're handling that now so
01303                  it's not a warning anymore. */
01304               g_error_free(error);
01305 
01306               if (IS_APP_INDICATOR(user_data)) {
01307                      start_fallback_timer(APP_INDICATOR(user_data), FALSE);
01308               }
01309 
01310               g_object_unref(G_OBJECT(user_data));
01311               return;
01312        }
01313 
01314        AppIndicator * app = APP_INDICATOR(user_data);
01315 
01316        if (res != NULL) {
01317               app->priv->watcher_proxy = proxy;
01318 
01319               /* Setting up a signal to watch when the unique name
01320                  changes */
01321               g_signal_connect(G_OBJECT(app->priv->watcher_proxy), "notify::g-name-owner", G_CALLBACK(watcher_owner_changed), user_data);
01322        }
01323 
01324        /* Let's insure that someone is on the other side, else we're
01325           still in a fallback scenario. */
01326        gchar * name = g_dbus_proxy_get_name_owner(app->priv->watcher_proxy);
01327        if (name == NULL) {
01328               start_fallback_timer(APP_INDICATOR(user_data), FALSE);
01329               g_object_unref(G_OBJECT(user_data));
01330               return;
01331        }
01332        g_free(name);
01333 
01334        /* g_object_unref(G_OBJECT(user_data)); */
01335        /* Why is this commented out?  Oh, wait, we don't want to
01336           unref in this case because we need to ref again to do the
01337           register callback.  Let's not unref to ref again. */
01338 
01339        g_dbus_proxy_call(app->priv->watcher_proxy,
01340                          "RegisterStatusNotifierItem",
01341                          g_variant_new("(s)", app->priv->path),
01342                          G_DBUS_CALL_FLAGS_NONE,
01343                                      -1,
01344                          NULL, /* cancelable */
01345                          register_service_cb,
01346                          user_data);
01347 
01348        return;
01349 }
01350 
01351 /* Watching for when the name owner changes on the interface
01352    to know whether we should be connected or not. */
01353 static void
01354 watcher_owner_changed (GObject * obj, GParamSpec * pspec, gpointer user_data)
01355 {
01356        AppIndicator * self = APP_INDICATOR(user_data);
01357        g_return_if_fail(self != NULL);
01358        g_return_if_fail(self->priv->watcher_proxy != NULL);
01359 
01360        gchar * name = g_dbus_proxy_get_name_owner(self->priv->watcher_proxy);
01361 
01362        if (name == NULL) {
01363               /* Emit the AppIndicator::connection-changed signal*/
01364               g_signal_emit (self, signals[CONNECTION_CHANGED], 0, FALSE);
01365               
01366               start_fallback_timer(self, FALSE);
01367        } else {
01368               if (self->priv->fallback_timer != 0) {
01369                      /* Stop the timer */
01370                      g_source_remove(self->priv->fallback_timer);
01371                      self->priv->fallback_timer = 0;
01372               }
01373 
01374               check_connect(self);
01375        }
01376 
01377        return;
01378 }
01379 
01380 /* Responce from the DBus command to register a service
01381    with a NotificationWatcher. */
01382 static void
01383 register_service_cb (GObject * obj, GAsyncResult * res, gpointer user_data)
01384 {
01385        GError * error = NULL;
01386        GVariant * returns = g_dbus_proxy_call_finish(G_DBUS_PROXY(obj), res, &error);
01387 
01388        /* We don't care about any return values */
01389        if (returns != NULL) {
01390               g_variant_unref(returns);
01391        }
01392 
01393        if (error != NULL) {
01394               /* They didn't respond, ewww.  Not sure what they could
01395                  be doing */
01396               g_warning("Unable to connect to the Notification Watcher: %s", error->message);
01397               start_fallback_timer(APP_INDICATOR(user_data), TRUE);
01398               g_object_unref(G_OBJECT(user_data));
01399               return;
01400        }
01401 
01402        g_return_if_fail(IS_APP_INDICATOR(user_data));
01403        AppIndicator * app = APP_INDICATOR(user_data);
01404        AppIndicatorPrivate * priv = app->priv;
01405 
01406        /* Emit the AppIndicator::connection-changed signal*/
01407     g_signal_emit (app, signals[CONNECTION_CHANGED], 0, TRUE);
01408 
01409        if (priv->status_icon) {
01410               AppIndicatorClass * class = APP_INDICATOR_GET_CLASS(app);
01411               if (class->unfallback != NULL) {
01412                      class->unfallback(app, priv->status_icon);
01413                      priv->status_icon = NULL;
01414               } 
01415        }
01416 
01417        g_object_unref(G_OBJECT(user_data));
01418        return;
01419 }
01420 
01421 /* A helper function to get the nick out of a given
01422    category enum value. */
01423 static const gchar *
01424 category_from_enum (AppIndicatorCategory category)
01425 {
01426   GEnumValue *value;
01427 
01428   value = g_enum_get_value ((GEnumClass *)g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_CATEGORY), category);
01429   return value->value_nick;
01430 }
01431 
01432 /* A function that will start the fallback timer if it's not
01433    already started.  It sets up the DBus watcher to see if
01434    there is a change.  Also, provides an override mode for cases
01435    where it's unlikely that a timer will help anything. */
01436 static void
01437 start_fallback_timer (AppIndicator * self, gboolean disable_timeout)
01438 {
01439        g_return_if_fail(IS_APP_INDICATOR(self));
01440        AppIndicatorPrivate * priv = APP_INDICATOR(self)->priv;
01441 
01442        if (priv->fallback_timer != 0) {
01443               /* The timer is set, let's just be happy with the one
01444                  we've already got running */
01445               return;
01446        }
01447 
01448        if (priv->status_icon != NULL) {
01449               /* We're already fallen back.  Let's not do it again. */
01450               return;
01451        }
01452 
01453        if (disable_timeout) {
01454               fallback_timer_expire(self);
01455        } else {
01456               priv->fallback_timer = g_timeout_add(DEFAULT_FALLBACK_TIMER, fallback_timer_expire, self);
01457        }
01458 
01459        return;
01460 }
01461 
01462 /* A function that gets executed when we want to change the
01463    state of the fallback. */
01464 static gboolean
01465 fallback_timer_expire (gpointer data)
01466 {
01467        g_return_val_if_fail(IS_APP_INDICATOR(data), FALSE);
01468 
01469        AppIndicatorPrivate * priv = APP_INDICATOR(data)->priv;
01470        AppIndicatorClass * class = APP_INDICATOR_GET_CLASS(data);
01471 
01472        if (priv->status_icon == NULL) {
01473               if (class->fallback != NULL) {
01474                      priv->status_icon = class->fallback(APP_INDICATOR(data));
01475               } 
01476        } else {
01477               if (class->unfallback != NULL) {
01478                      class->unfallback(APP_INDICATOR(data), priv->status_icon);
01479                      priv->status_icon = NULL;
01480               } else {
01481                      g_warning("No 'unfallback' function but the 'fallback' function returned a non-NULL result.");
01482               }
01483        }
01484 
01485        priv->fallback_timer = 0;
01486        return FALSE;
01487 }
01488 
01489 /* emit a NEW_ICON signal in response for the theme change */
01490 static void
01491 theme_changed_cb (GtkIconTheme * theme, gpointer user_data)
01492 {
01493        g_signal_emit (user_data, signals[NEW_ICON], 0, TRUE);
01494 
01495        AppIndicator * self = (AppIndicator *)user_data;
01496        AppIndicatorPrivate *priv = self->priv;
01497 
01498        if (priv->dbus_registration != 0 && priv->connection != NULL) {
01499               GError * error = NULL;
01500 
01501               g_dbus_connection_emit_signal(priv->connection,
01502                                             NULL,
01503                                             priv->path,
01504                                             NOTIFICATION_ITEM_DBUS_IFACE,
01505                                             "NewIcon",
01506                                             NULL,
01507                                             &error);
01508 
01509               if (error != NULL) {
01510                      g_warning("Unable to send signal for NewIcon: %s", error->message);
01511                      g_error_free(error);
01512               }
01513        }
01514 
01515        return;
01516 }
01517 
01518 /* Creates a StatusIcon that can be used when the application
01519    indicator area isn't available. */
01520 static GtkStatusIcon *
01521 fallback (AppIndicator * self)
01522 {
01523        GtkStatusIcon * icon = gtk_status_icon_new();
01524 
01525        gtk_status_icon_set_name(icon, app_indicator_get_id(self));
01526        const gchar * title = app_indicator_get_title(self);
01527        if (title != NULL) {
01528               gtk_status_icon_set_title(icon, title);
01529        }
01530        
01531        g_signal_connect(G_OBJECT(self), APP_INDICATOR_SIGNAL_NEW_STATUS,
01532               G_CALLBACK(status_icon_status_wrapper), icon);
01533        g_signal_connect(G_OBJECT(self), APP_INDICATOR_SIGNAL_NEW_ICON,
01534               G_CALLBACK(status_icon_changes), icon);
01535        g_signal_connect(G_OBJECT(self), APP_INDICATOR_SIGNAL_NEW_ATTENTION_ICON,
01536               G_CALLBACK(status_icon_changes), icon);
01537 
01538        status_icon_changes(self, icon);
01539 
01540        g_signal_connect(G_OBJECT(icon), "activate", G_CALLBACK(status_icon_activate), self);
01541        g_signal_connect(G_OBJECT(icon), "popup-menu", G_CALLBACK(status_icon_menu_activate), self);
01542        g_signal_connect(G_OBJECT(icon), "scroll-event", G_CALLBACK(scroll_event_wrapper), self);
01543        g_signal_connect(G_OBJECT(icon), "button-release-event", G_CALLBACK(middle_click_wrapper), self);
01544 
01545        return icon;
01546 }
01547 
01548 /* A wrapper as the status update prototype is a little
01549    bit different, but we want to handle it the same. */
01550 static void
01551 status_icon_status_wrapper (AppIndicator * self, const gchar * status, gpointer data)
01552 {
01553        return status_icon_changes(self, data);
01554 }
01555 
01556 /* A wrapper for redirecting the scroll events to the app-indicator from status
01557    icon widget. */
01558 static gboolean
01559 scroll_event_wrapper (GtkWidget *status_icon, GdkEventScroll *event, gpointer data)
01560 {
01561        g_return_val_if_fail(IS_APP_INDICATOR(data), FALSE);
01562        AppIndicator * app = APP_INDICATOR(data);
01563        g_signal_emit(app, signals[SCROLL_EVENT], 0, 1, event->direction);
01564 
01565        return TRUE;
01566 }
01567 
01568 static gboolean
01569 middle_click_wrapper (GtkWidget *status_icon, GdkEventButton *event, gpointer data)
01570 {
01571        g_return_val_if_fail(IS_APP_INDICATOR(data), FALSE);
01572        AppIndicator * app = APP_INDICATOR(data);
01573        AppIndicatorPrivate *priv = app->priv;
01574 
01575        if (event->button == 2 && event->type == GDK_BUTTON_RELEASE) {
01576               GtkAllocation alloc;
01577               gint px = event->x;
01578               gint py = event->y;
01579               gtk_widget_get_allocation (status_icon, &alloc);
01580               GtkWidget *menuitem = priv->sec_activate_target;
01581 
01582               if (px >= 0 && px < alloc.width && py >= 0 && py < alloc.height &&
01583                   priv->sec_activate_enabled && menuitem &&
01584                   gtk_widget_get_visible (menuitem) &&
01585                   gtk_widget_get_sensitive (menuitem))
01586               {
01587                      gtk_widget_activate (menuitem);
01588                      return TRUE;
01589               }
01590        }
01591 
01592        return FALSE;
01593 }
01594 
01595 /* This tracks changes to either the status or the icons
01596    that are associated with the app indicator */
01597 static void
01598 status_icon_changes (AppIndicator * self, gpointer data)
01599 {
01600        GtkStatusIcon * icon = GTK_STATUS_ICON(data);
01601 
01602        /* add the icon_theme_path once if needed */
01603        GtkIconTheme *icon_theme = gtk_icon_theme_get_default();
01604        if (self->priv->icon_theme_path != NULL) {
01605               gchar **path;
01606               gint n_elements, i;
01607               gboolean found=FALSE;
01608               gtk_icon_theme_get_search_path(icon_theme, &path, &n_elements);
01609               for (i=0; i< n_elements || path[i] == NULL; i++) {
01610                      if(g_strcmp0(path[i], self->priv->icon_theme_path) == 0) {
01611                             found=TRUE;
01612                             break;
01613                      }
01614               }
01615               if(!found) {
01616                      gtk_icon_theme_append_search_path(icon_theme, self->priv->icon_theme_path);
01617               }
01618               g_strfreev (path);
01619        }
01620 
01621        const gchar * icon_name = NULL;
01622        switch (app_indicator_get_status(self)) {
01623        case APP_INDICATOR_STATUS_PASSIVE:
01624               /* hide first to avoid that the change is visible to the user */
01625               gtk_status_icon_set_visible(icon, FALSE);
01626               icon_name = app_indicator_get_icon(self);
01627               break;
01628        case APP_INDICATOR_STATUS_ACTIVE:
01629               icon_name = app_indicator_get_icon(self);
01630               gtk_status_icon_set_visible(icon, TRUE);
01631               break;
01632        case APP_INDICATOR_STATUS_ATTENTION:
01633               /* get the _attention_ icon here */
01634               icon_name = app_indicator_get_attention_icon(self);
01635               gtk_status_icon_set_visible(icon, TRUE);
01636               break;
01637        };
01638 
01639        if (icon_name != NULL) {
01640               if (g_file_test(icon_name, G_FILE_TEST_EXISTS)) {
01641                      gtk_status_icon_set_from_file(icon, icon_name);
01642               } else {
01643                      gchar *longname = append_panel_icon_suffix(icon_name);
01644 
01645                      if (longname != NULL && gtk_icon_theme_has_icon (icon_theme, longname)) {
01646                             gtk_status_icon_set_from_icon_name(icon, longname);
01647                      } else {
01648                             gtk_status_icon_set_from_icon_name(icon, icon_name);
01649                      }
01650 
01651                      g_free(longname);
01652               }
01653        }
01654 
01655        return;
01656 }
01657 
01658 /* Handles the activate action by the status icon by showing
01659    the menu in a popup. */
01660 static void
01661 status_icon_activate (GtkStatusIcon * icon, gpointer data)
01662 {
01663        GtkMenu * menu = app_indicator_get_menu(APP_INDICATOR(data));
01664        if (menu == NULL)
01665               return;
01666        
01667        gtk_menu_popup(menu,
01668                       NULL, /* Parent Menu */
01669                       NULL, /* Parent item */
01670                       gtk_status_icon_position_menu,
01671                       icon,
01672                       1, /* Button */
01673                       gtk_get_current_event_time());
01674 
01675        return;
01676 }
01677 
01678 /* Handles the right-click action by the status icon by showing
01679    the menu in a popup. */
01680 static void
01681 status_icon_menu_activate (GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data)
01682 {
01683        status_icon_activate(status_icon, user_data);
01684 }
01685 
01686 /* Removes the status icon as the application indicator area
01687    is now up and running again. */
01688 static void
01689 unfallback (AppIndicator * self, GtkStatusIcon * status_icon)
01690 {
01691        g_signal_handlers_disconnect_by_func(G_OBJECT(self), status_icon_status_wrapper, status_icon);
01692        g_signal_handlers_disconnect_by_func(G_OBJECT(self), status_icon_changes, status_icon);
01693        g_signal_handlers_disconnect_by_func(G_OBJECT(self), scroll_event_wrapper, status_icon);
01694        g_signal_handlers_disconnect_by_func(G_OBJECT(self), middle_click_wrapper, status_icon);
01695        gtk_status_icon_set_visible(status_icon, FALSE);
01696        g_object_unref(G_OBJECT(status_icon));
01697        return;
01698 }
01699 
01700 /* A helper function that appends PANEL_ICON_SUFFIX to the given icon name
01701    if it's missing. */
01702 static gchar *
01703 append_panel_icon_suffix (const gchar *icon_name)
01704 {
01705        gchar * long_name = NULL;
01706 
01707        if (!g_str_has_suffix (icon_name, PANEL_ICON_SUFFIX)) {
01708               long_name =
01709                   g_strdup_printf("%s-%s", icon_name, PANEL_ICON_SUFFIX);
01710         } else {
01711               long_name = g_strdup (icon_name);
01712         }
01713 
01714        return long_name;    
01715 }
01716 
01717 static gboolean
01718 widget_is_menu_child(AppIndicator * self, GtkWidget *child)
01719 {
01720        g_return_val_if_fail(IS_APP_INDICATOR(self), FALSE);
01721 
01722        if (!self->priv->menu) return FALSE;
01723        if (!child) return FALSE;
01724 
01725        GtkWidget *parent;
01726 
01727        while ((parent = gtk_widget_get_parent(child))) {
01728               if (parent == self->priv->menu)
01729                      return TRUE;
01730 
01731               if (GTK_IS_MENU(parent))
01732                      child = gtk_menu_get_attach_widget(GTK_MENU(parent));
01733               else
01734                      child = parent;
01735        }
01736 
01737        return FALSE;
01738 }
01739 
01740 static void
01741 sec_activate_target_parent_changed(GtkWidget *menuitem, GtkWidget *old_parent,
01742                                    gpointer data)
01743 {
01744        g_return_if_fail(IS_APP_INDICATOR(data));
01745        AppIndicator *self = data;
01746        self->priv->sec_activate_enabled = widget_is_menu_child(self, menuitem);
01747 }
01748 
01749 
01750 /* ************************* */
01751 /*    Public Functions       */
01752 /* ************************* */
01753 
01766 AppIndicator *
01767 app_indicator_new (const gchar          *id,
01768                    const gchar          *icon_name,
01769                    AppIndicatorCategory  category)
01770 {
01771   AppIndicator *indicator = g_object_new (APP_INDICATOR_TYPE,
01772                                           PROP_ID_S, id,
01773                                           PROP_CATEGORY_S, category_from_enum (category),
01774                                           PROP_ICON_NAME_S, icon_name,
01775                                           NULL);
01776 
01777   return indicator;
01778 }
01779 
01794 AppIndicator *
01795 app_indicator_new_with_path (const gchar          *id,
01796                              const gchar          *icon_name,
01797                              AppIndicatorCategory  category,
01798                              const gchar          *icon_theme_path)
01799 {
01800        AppIndicator *indicator = g_object_new (APP_INDICATOR_TYPE,
01801                                                PROP_ID_S, id,
01802                                                PROP_CATEGORY_S, category_from_enum (category),
01803                                                PROP_ICON_NAME_S, icon_name,
01804                                                PROP_ICON_THEME_PATH_S, icon_theme_path,
01805                                                NULL);
01806 
01807        return indicator;
01808 }
01809 
01825 void
01826 app_indicator_set_status (AppIndicator *self, AppIndicatorStatus status)
01827 {
01828        g_return_if_fail (IS_APP_INDICATOR (self));
01829 
01830        if (self->priv->status != status) {
01831               GEnumValue *value = g_enum_get_value ((GEnumClass *) g_type_class_ref (APP_INDICATOR_TYPE_INDICATOR_STATUS), status);
01832 
01833               self->priv->status = status;
01834               g_signal_emit (self, signals[NEW_STATUS], 0, value->value_nick);
01835 
01836               if (self->priv->dbus_registration != 0 && self->priv->connection != NULL) {
01837                      GError * error = NULL;
01838 
01839                      g_dbus_connection_emit_signal(self->priv->connection,
01840                                                                         NULL,
01841                                                                         self->priv->path,
01842                                                                         NOTIFICATION_ITEM_DBUS_IFACE,
01843                                                                         "NewStatus",
01844                                                                         g_variant_new("(s)", value->value_nick),
01845                                                                         &error);
01846 
01847                      if (error != NULL) {
01848                             g_warning("Unable to send signal for NewStatus: %s", error->message);
01849                             g_error_free(error);
01850                      }
01851               }
01852        }
01853 
01854        return;
01855 }
01856 
01867 void
01868 app_indicator_set_attention_icon (AppIndicator *self, const gchar *icon_name)
01869 {
01870        return app_indicator_set_attention_icon_full(self, icon_name, NULL);
01871 }
01872 
01881 void
01882 app_indicator_set_attention_icon_full (AppIndicator *self, const gchar *icon_name, const gchar * icon_desc)
01883 {
01884        g_return_if_fail (IS_APP_INDICATOR (self));
01885        g_return_if_fail (icon_name != NULL);
01886        gboolean changed = FALSE;
01887 
01888        if (g_strcmp0 (self->priv->attention_icon_name, icon_name) != 0) {
01889               if (self->priv->attention_icon_name) {
01890                      g_free (self->priv->attention_icon_name);
01891               }
01892 
01893               self->priv->attention_icon_name = g_strdup(icon_name);
01894               changed = TRUE;
01895        }
01896 
01897        if (g_strcmp0(self->priv->att_accessible_desc, icon_desc) != 0) {
01898               if (self->priv->att_accessible_desc) {
01899                      g_free (self->priv->att_accessible_desc);
01900               }
01901 
01902               self->priv->att_accessible_desc = g_strdup(icon_name);
01903               changed = TRUE;
01904        }
01905 
01906        if (changed) {
01907               g_signal_emit (self, signals[NEW_ATTENTION_ICON], 0, TRUE);
01908 
01909               if (self->priv->dbus_registration != 0 && self->priv->connection != NULL) {
01910                      GError * error = NULL;
01911 
01912                      g_dbus_connection_emit_signal(self->priv->connection,
01913                                                                         NULL,
01914                                                                         self->priv->path,
01915                                                                         NOTIFICATION_ITEM_DBUS_IFACE,
01916                                                                         "NewAttentionIcon",
01917                                                                         NULL,
01918                                                                         &error);
01919 
01920                      if (error != NULL) {
01921                             g_warning("Unable to send signal for NewAttentionIcon: %s", error->message);
01922                             g_error_free(error);
01923                      }
01924               }
01925        }
01926 
01927        return;
01928 }
01929 
01940 void
01941 app_indicator_set_icon (AppIndicator *self, const gchar *icon_name)
01942 {
01943        return app_indicator_set_icon_full(self, icon_name, NULL);
01944 }
01945 
01959 void
01960 app_indicator_set_icon_full (AppIndicator *self, const gchar *icon_name, const gchar * icon_desc)
01961 {
01962        g_return_if_fail (IS_APP_INDICATOR (self));
01963        g_return_if_fail (icon_name != NULL);
01964        gboolean changed = FALSE;
01965 
01966        if (g_strcmp0 (self->priv->icon_name, icon_name) != 0) {
01967               if (self->priv->icon_name) {
01968                      g_free (self->priv->icon_name);
01969               }
01970 
01971               self->priv->icon_name = g_strdup(icon_name);
01972               changed = TRUE;
01973        }
01974 
01975        if (g_strcmp0(self->priv->accessible_desc, icon_desc) != 0) {
01976               if (self->priv->accessible_desc != NULL) {
01977                      g_free(self->priv->accessible_desc);
01978               }
01979 
01980               self->priv->accessible_desc = g_strdup(icon_desc);
01981               changed = TRUE;
01982        }
01983 
01984        if (changed) {
01985               g_signal_emit (self, signals[NEW_ICON], 0, TRUE);
01986 
01987               if (self->priv->dbus_registration != 0 && self->priv->connection != NULL) {
01988                      GError * error = NULL;
01989 
01990                      g_dbus_connection_emit_signal(self->priv->connection,
01991                                                                         NULL,
01992                                                                         self->priv->path,
01993                                                                         NOTIFICATION_ITEM_DBUS_IFACE,
01994                                                                         "NewIcon",
01995                                                                         NULL,
01996                                                                         &error);
01997 
01998                      if (error != NULL) {
01999                             g_warning("Unable to send signal for NewIcon: %s", error->message);
02000                             g_error_free(error);
02001                      }
02002               }
02003        }
02004 
02005        return;
02006 }
02007 
02018 void
02019 app_indicator_set_label (AppIndicator *self, const gchar * label, const gchar * guide)
02020 {
02021        g_return_if_fail (IS_APP_INDICATOR (self));
02022        /* Note: The label can be NULL, it's okay */
02023        /* Note: The guide can be NULL, it's okay */
02024 
02025        g_object_set(G_OBJECT(self),
02026                     PROP_LABEL_S,       label == NULL ? "" : label,
02027                     PROP_LABEL_GUIDE_S, guide == NULL ? "" : guide,
02028                     NULL);
02029 
02030        return;
02031 }
02032 
02040 void
02041 app_indicator_set_icon_theme_path (AppIndicator *self, const gchar *icon_theme_path)
02042 {
02043        g_return_if_fail (IS_APP_INDICATOR (self));
02044 
02045        if (g_strcmp0 (self->priv->icon_theme_path, icon_theme_path) != 0) {
02046               if (self->priv->icon_theme_path != NULL)
02047                      g_free(self->priv->icon_theme_path);
02048 
02049               self->priv->icon_theme_path = g_strdup(icon_theme_path);
02050 
02051               g_signal_emit (self, signals[NEW_ICON_THEME_PATH], 0, self->priv->icon_theme_path, TRUE);
02052 
02053               if (self->priv->dbus_registration != 0 && self->priv->connection != NULL) {
02054                      GError * error = NULL;
02055 
02056                      g_dbus_connection_emit_signal(self->priv->connection,
02057                                                                         NULL,
02058                                                                         self->priv->path,
02059                                                                         NOTIFICATION_ITEM_DBUS_IFACE,
02060                                                                         "NewIconThemePath",
02061                                                                         g_variant_new("(s)", self->priv->icon_theme_path),
02062                                                                         &error);
02063 
02064                      if (error != NULL) {
02065                             g_warning("Unable to send signal for NewIconThemePath: %s", error->message);
02066                             g_error_free(error);
02067                      }
02068               }
02069        }
02070 
02071        return;
02072 }
02073 
02074 /* Does the dbusmenu related work.  If there isn't a server, it builds
02075    one and if there are menus it runs the parse to put those menus into
02076    the server. */
02077 static void
02078 setup_dbusmenu (AppIndicator *self)
02079 {
02080        AppIndicatorPrivate *priv;
02081        DbusmenuMenuitem *root = NULL;
02082 
02083        priv = self->priv;
02084 
02085        if (priv->menu) {
02086               root = dbusmenu_gtk_parse_menu_structure(priv->menu);
02087        }
02088 
02089        if (priv->menuservice == NULL) {
02090               gchar * path = g_strdup_printf(DEFAULT_ITEM_PATH "/%s/Menu", priv->clean_id);
02091               priv->menuservice = dbusmenu_server_new (path);
02092               g_free(path);
02093        }
02094 
02095        dbusmenu_server_set_root (priv->menuservice, root);
02096 
02097        /* Drop our local ref as set_root should get it's own. */
02098        if (root != NULL) {
02099               g_object_unref(root);
02100        }
02101 
02102        return;
02103 }
02104 
02116 void
02117 app_indicator_set_menu (AppIndicator *self, GtkMenu *menu)
02118 {
02119   AppIndicatorPrivate *priv;
02120 
02121   g_return_if_fail (IS_APP_INDICATOR (self));
02122   g_return_if_fail (GTK_IS_MENU (menu));
02123   g_return_if_fail (self->priv->clean_id != NULL);
02124 
02125   priv = self->priv;
02126 
02127   if (priv->menu != NULL)
02128     {
02129       g_object_unref (priv->menu);
02130     }
02131 
02132   priv->menu = GTK_WIDGET (menu);
02133   g_object_ref_sink (priv->menu);
02134 
02135   setup_dbusmenu (self);
02136 
02137   priv->sec_activate_enabled = widget_is_menu_child (self, priv->sec_activate_target);
02138 
02139   check_connect (self);
02140 
02141   return;
02142 }
02143 
02155 void
02156 app_indicator_set_ordering_index (AppIndicator *self, guint32 ordering_index)
02157 {
02158        g_return_if_fail (IS_APP_INDICATOR (self));
02159 
02160        self->priv->ordering_index = ordering_index;
02161 
02162        return;
02163 }
02164 
02179 void
02180 app_indicator_set_secondary_activate_target (AppIndicator *self, GtkWidget *menuitem)
02181 {
02182        g_return_if_fail (IS_APP_INDICATOR (self));
02183        AppIndicatorPrivate *priv = self->priv;
02184 
02185        if (priv->sec_activate_target) {
02186               g_signal_handlers_disconnect_by_func (priv->sec_activate_target,
02187                                                     sec_activate_target_parent_changed,
02188                                                     self);
02189               g_object_unref(G_OBJECT(priv->sec_activate_target));
02190               priv->sec_activate_target = NULL;
02191        }
02192 
02193        if (menuitem == NULL) {
02194               return;
02195        }
02196 
02197        g_return_if_fail (GTK_IS_WIDGET (menuitem));
02198 
02199        priv->sec_activate_target = g_object_ref(G_OBJECT(menuitem));
02200        priv->sec_activate_enabled = widget_is_menu_child(self, menuitem);
02201        g_signal_connect(menuitem, "parent-set", G_CALLBACK(sec_activate_target_parent_changed), self);
02202 }
02203 
02222 void
02223 app_indicator_set_title (AppIndicator *self, const gchar * title)
02224 {
02225        g_return_if_fail (IS_APP_INDICATOR (self));
02226 
02227        g_object_set(G_OBJECT(self),
02228                     PROP_TITLE_S, title == NULL ? "": title,
02229                     NULL);
02230 
02231        return;
02232 }
02233 
02242 const gchar *
02243 app_indicator_get_id (AppIndicator *self)
02244 {
02245   g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
02246 
02247   return self->priv->id;
02248 }
02249 
02258 AppIndicatorCategory
02259 app_indicator_get_category (AppIndicator *self)
02260 {
02261   g_return_val_if_fail (IS_APP_INDICATOR (self), APP_INDICATOR_CATEGORY_APPLICATION_STATUS);
02262 
02263   return self->priv->category;
02264 }
02265 
02274 AppIndicatorStatus
02275 app_indicator_get_status (AppIndicator *self)
02276 {
02277   g_return_val_if_fail (IS_APP_INDICATOR (self), APP_INDICATOR_STATUS_PASSIVE);
02278 
02279   return self->priv->status;
02280 }
02281 
02290 const gchar *
02291 app_indicator_get_icon (AppIndicator *self)
02292 {
02293   g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
02294 
02295   return self->priv->icon_name;
02296 }
02297 
02306 const gchar *
02307 app_indicator_get_icon_desc (AppIndicator *self)
02308 {
02309   g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
02310 
02311   return self->priv->accessible_desc;
02312 }
02313 
02322 const gchar *
02323 app_indicator_get_icon_theme_path (AppIndicator *self)
02324 {
02325   g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
02326 
02327   return self->priv->icon_theme_path;
02328 }
02329 
02338 const gchar *
02339 app_indicator_get_attention_icon (AppIndicator *self)
02340 {
02341   g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
02342 
02343   return self->priv->attention_icon_name;
02344 }
02345 
02354 const gchar *
02355 app_indicator_get_attention_icon_desc (AppIndicator *self)
02356 {
02357   g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
02358 
02359   return self->priv->att_accessible_desc;
02360 }
02361 
02374 const gchar *
02375 app_indicator_get_title (AppIndicator *self)
02376 {
02377        g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
02378 
02379        return self->priv->title;
02380 }
02381 
02382 
02392 GtkMenu *
02393 app_indicator_get_menu (AppIndicator *self)
02394 {
02395        AppIndicatorPrivate *priv;
02396 
02397        g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
02398 
02399        priv = self->priv;
02400 
02401        return GTK_MENU(priv->menu);
02402 }
02403 
02412 const gchar *
02413 app_indicator_get_label (AppIndicator *self)
02414 {
02415   g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
02416 
02417   return self->priv->label;
02418 }
02419 
02428 const gchar *
02429 app_indicator_get_label_guide (AppIndicator *self)
02430 {
02431   g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
02432 
02433   return self->priv->label_guide;
02434 }
02435 
02444 guint32
02445 app_indicator_get_ordering_index (AppIndicator *self)
02446 {
02447        g_return_val_if_fail (IS_APP_INDICATOR (self), 0);
02448 
02449        if (self->priv->ordering_index == 0) {
02450               return _generate_id(self->priv->category, self->priv->id);
02451        } else {
02452               return self->priv->ordering_index;
02453        }
02454 }
02455 
02464 GtkWidget *
02465 app_indicator_get_secondary_activate_target (AppIndicator *self)
02466 {
02467        g_return_val_if_fail (IS_APP_INDICATOR (self), NULL);
02468 
02469        return GTK_WIDGET(self->priv->sec_activate_target);
02470 }
02471 
02472 #define APP_INDICATOR_SHORTY_NICK "app-indicator-shorty-nick"
02473 
02474 /* Callback when an item from the desktop shortcuts gets
02475    called. */
02476 static void
02477 shorty_activated_cb (DbusmenuMenuitem * mi, guint timestamp, gpointer user_data)
02478 {
02479        gchar * nick = g_object_get_data(G_OBJECT(mi), APP_INDICATOR_SHORTY_NICK);
02480        g_return_if_fail(nick != NULL);
02481 
02482        g_return_if_fail(IS_APP_INDICATOR(user_data));
02483        AppIndicator * self = APP_INDICATOR(user_data);
02484        AppIndicatorPrivate *priv = self->priv;
02485 
02486        g_return_if_fail(priv->shorties != NULL);
02487 
02488        indicator_desktop_shortcuts_nick_exec(priv->shorties, nick);
02489 
02490        return;
02491 }
02492 
02502 void
02503 app_indicator_build_menu_from_desktop (AppIndicator * self, const gchar * desktop_file, const gchar * desktop_profile)
02504 {
02505        g_return_if_fail(IS_APP_INDICATOR(self));
02506        AppIndicatorPrivate *priv = self->priv;
02507 
02508        /* Build a new shortcuts object */
02509        if (priv->shorties != NULL) {
02510               g_object_unref(priv->shorties);
02511               priv->shorties = NULL;
02512        }
02513        priv->shorties = indicator_desktop_shortcuts_new(desktop_file, desktop_profile);
02514        g_return_if_fail(priv->shorties != NULL);
02515 
02516        const gchar ** nicks = indicator_desktop_shortcuts_get_nicks(priv->shorties);
02517        int nick_num;
02518 
02519        /* Place the items on a dbusmenu */
02520        DbusmenuMenuitem * root = dbusmenu_menuitem_new();
02521 
02522        for (nick_num = 0; nicks[nick_num] != NULL; nick_num++) {
02523               DbusmenuMenuitem * item = dbusmenu_menuitem_new();
02524               g_object_set_data(G_OBJECT(item), APP_INDICATOR_SHORTY_NICK, (gpointer)nicks[nick_num]);
02525 
02526               gchar * name = indicator_desktop_shortcuts_nick_get_name(priv->shorties, nicks[nick_num]);
02527               dbusmenu_menuitem_property_set(item, DBUSMENU_MENUITEM_PROP_LABEL, name);
02528               g_free(name);
02529 
02530               g_signal_connect(G_OBJECT(item), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(shorty_activated_cb), self);
02531 
02532               dbusmenu_menuitem_child_append(root, item);
02533        }
02534 
02535        /* Swap it if needed */
02536        if (priv->menuservice == NULL) {
02537               gchar * path = g_strdup_printf(DEFAULT_ITEM_PATH "/%s/Menu", priv->clean_id);
02538               priv->menuservice = dbusmenu_server_new (path);
02539               g_free(path);
02540        }
02541 
02542        dbusmenu_server_set_root (priv->menuservice, root);
02543 
02544        if (priv->menu != NULL) {
02545               g_object_unref(G_OBJECT(priv->menu));
02546               priv->menu = NULL;
02547        }
02548 
02549        return;
02550 }