Back to index

indicator-appmenu  12.10.0
indicator-appmenu.c
Go to the documentation of this file.
00001 /*
00002 An implementation of indicator object showing menus from applications.
00003 
00004 Copyright 2010 Canonical Ltd.
00005 
00006 Authors:
00007     Ted Gould <ted@canonical.com>
00008 
00009 This program is free software: you can redistribute it and/or modify it 
00010 under the terms of the GNU General Public License version 3, as published 
00011 by the Free Software Foundation.
00012 
00013 This program is distributed in the hope that it will be useful, but 
00014 WITHOUT ANY WARRANTY; without even the implied warranties of 
00015 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 
00016 PURPOSE.  See the GNU General Public License for more details.
00017 
00018 You should have received a copy of the GNU General Public License along 
00019 with this program.  If not, see <http://www.gnu.org/licenses/>.
00020 */
00021 
00022 #ifdef HAVE_CONFIG_H
00023 #include "config.h"
00024 #endif
00025 
00026 #include <X11/Xlib.h>
00027 #include <gdk/gdkx.h>
00028 #include <gio/gio.h>
00029 
00030 #include <libindicator/indicator.h>
00031 #include <libindicator/indicator-object.h>
00032 
00033 #include <libdbusmenu-glib/menuitem.h>
00034 #include <libdbusmenu-glib/client.h>
00035 
00036 #include <libbamf/bamf-matcher.h>
00037 
00038 #include "gen-application-menu-registrar.xml.h"
00039 #include "gen-application-menu-renderer.xml.h"
00040 #include "indicator-appmenu-marshal.h"
00041 #include "window-menu.h"
00042 #include "window-menu-dbusmenu.h"
00043 #include "window-menu-model.h"
00044 #include "dbus-shared.h"
00045 #include "gdk-get-func.h"
00046 
00047 /**********************
00048   Indicator Object
00049  **********************/
00050 #define INDICATOR_APPMENU_TYPE            (indicator_appmenu_get_type ())
00051 #define INDICATOR_APPMENU(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_APPMENU_TYPE, IndicatorAppmenu))
00052 #define INDICATOR_APPMENU_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), INDICATOR_APPMENU_TYPE, IndicatorAppmenuClass))
00053 #define IS_INDICATOR_APPMENU(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_APPMENU_TYPE))
00054 #define IS_INDICATOR_APPMENU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), INDICATOR_APPMENU_TYPE))
00055 #define INDICATOR_APPMENU_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), INDICATOR_APPMENU_TYPE, IndicatorAppmenuClass))
00056 
00057 GType indicator_appmenu_get_type (void);
00058 
00059 INDICATOR_SET_VERSION
00060 INDICATOR_SET_TYPE(INDICATOR_APPMENU_TYPE)
00061 
00062 typedef struct _IndicatorAppmenu      IndicatorAppmenu;
00063 typedef struct _IndicatorAppmenuClass IndicatorAppmenuClass;
00064 typedef struct _IndicatorAppmenuDebug      IndicatorAppmenuDebug;
00065 typedef struct _IndicatorAppmenuDebugClass IndicatorAppmenuDebugClass;
00066 
00067 typedef enum _ActiveStubsState ActiveStubsState;
00068 enum _ActiveStubsState {
00069        STUBS_UNKNOWN,
00070        STUBS_SHOW,
00071        STUBS_HIDE
00072 };
00073 
00074 struct _IndicatorAppmenuClass {
00075        IndicatorObjectClass parent_class;
00076 
00077        void (*window_registered) (IndicatorAppmenu * iapp, guint wid, gchar * address, gpointer path, gpointer user_data);
00078        void (*window_unregistered) (IndicatorAppmenu * iapp, guint wid, gpointer user_data);
00079 };
00080 
00081 struct _IndicatorAppmenu {
00082        IndicatorObject parent;
00083 
00084        gulong retry_registration;
00085 
00086        WindowMenu * default_app;
00087        GHashTable * apps;
00088 
00089        BamfMatcher * matcher;
00090        BamfWindow * active_window;
00091        ActiveStubsState active_stubs;
00092 
00093        gulong sig_entry_added;
00094        gulong sig_entry_removed;
00095        gulong sig_status_changed;
00096        gulong sig_show_menu;
00097        gulong sig_a11y_update;
00098 
00099        GtkMenuItem * close_item;
00100 
00101        GArray * window_menus;
00102 
00103        GHashTable * desktop_windows;
00104        WindowMenu * desktop_menu;
00105 
00106        GDBusConnection * bus;
00107        guint owner_id;
00108        guint dbus_registration;
00109 
00110        GHashTable * destruction_timers;
00111 };
00112 
00113 
00114 /**********************
00115   Debug Proxy
00116  **********************/
00117 #define INDICATOR_APPMENU_DEBUG_TYPE            (indicator_appmenu_debug_get_type ())
00118 #define INDICATOR_APPMENU_DEBUG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_APPMENU_DEBUG_TYPE, IndicatorAppmenuDebug))
00119 #define INDICATOR_APPMENU_DEBUG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), INDICATOR_APPMENU_DEBUG_TYPE, IndicatorAppmenuDebugClass))
00120 #define IS_INDICATOR_APPMENU_DEBUG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_APPMENU_DEBUG_TYPE))
00121 #define IS_INDICATOR_APPMENU_DEBUG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), INDICATOR_APPMENU_DEBUG_TYPE))
00122 #define INDICATOR_APPMENU_DEBUG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), INDICATOR_APPMENU_DEBUG_TYPE, IndicatorAppmenuDebugClass))
00123 
00124 GType indicator_appmenu_debug_get_type (void);
00125 
00126 struct _IndicatorAppmenuDebugClass {
00127        GObjectClass parent_class;
00128 };
00129 
00130 struct _IndicatorAppmenuDebug {
00131        GObject parent;
00132        IndicatorAppmenu * appmenu;
00133        GCancellable * bus_cancel;
00134        GDBusConnection * bus;
00135        guint dbus_registration;
00136 };
00137 
00138 
00139 /**********************
00140   Prototypes
00141  **********************/
00142 static void indicator_appmenu_dispose                                (GObject *object);
00143 static void indicator_appmenu_finalize                               (GObject *object);
00144 static void build_window_menus                                       (IndicatorAppmenu * iapp);
00145 static GList * get_entries                                           (IndicatorObject * io);
00146 static guint get_location                                            (IndicatorObject * io,
00147                                                                       IndicatorObjectEntry * entry);
00148 static void entry_activate                                           (IndicatorObject * io,
00149                                                                       IndicatorObjectEntry * entry,
00150                                                                       guint timestamp);
00151 static void entry_activate_window                                    (IndicatorObject * io,
00152                                                                       IndicatorObjectEntry * entry,
00153                                                                       guint windowid,
00154                                                                       guint timestamp);
00155 static void switch_default_app                                       (IndicatorAppmenu * iapp,
00156                                                                       WindowMenu * newdef,
00157                                                                       BamfWindow * active_window);
00158 static void find_desktop_windows                                     (IndicatorAppmenu * iapp);
00159 static void new_window                                               (BamfMatcher * matcher,
00160                                                                       BamfView * view,
00161                                                                       gpointer user_data);
00162 static void old_window                                               (BamfMatcher * matcher,
00163                                                                       BamfView * view,
00164                                                                       gpointer user_data);
00165 static void window_entry_added                                       (WindowMenu * mw,
00166                                                                       IndicatorObjectEntry * entry,
00167                                                                       gpointer user_data);
00168 static void window_entry_removed                                     (WindowMenu * mw,
00169                                                                       IndicatorObjectEntry * entry,
00170                                                                       gpointer user_data);
00171 static void window_status_changed                                    (WindowMenu * mw,
00172                                                                       DbusmenuStatus status,
00173                                                                       IndicatorAppmenu * iapp);
00174 static void window_show_menu                                         (WindowMenu * mw,
00175                                                                       IndicatorObjectEntry * entry,
00176                                                                       guint timestamp,
00177                                                                       gpointer user_data);
00178 static void window_a11y_update                                       (WindowMenu * mw,
00179                                                                       IndicatorObjectEntry * entry,
00180                                                                       gpointer user_data);
00181 static void active_window_changed                                    (BamfMatcher * matcher,
00182                                                                       BamfView * oldview,
00183                                                                       BamfView * newview,
00184                                                                       gpointer user_data);
00185 static GQuark error_quark                                            (void);
00186 static gboolean retry_registration                                   (gpointer user_data);
00187 static void bus_method_call                                          (GDBusConnection * connection,
00188                                                                       const gchar * sender,
00189                                                                       const gchar * object_path,
00190                                                                       const gchar * interface,
00191                                                                       const gchar * method,
00192                                                                       GVariant * params,
00193                                                                       GDBusMethodInvocation * invocation,
00194                                                                       gpointer user_data);
00195 static void on_bus_acquired                                          (GDBusConnection * connection,
00196                                                                       const gchar * name,
00197                                                                       gpointer user_data);
00198 static void on_name_acquired                                         (GDBusConnection * connection,
00199                                                                       const gchar * name,
00200                                                                       gpointer user_data);
00201 static void on_name_lost                                             (GDBusConnection * connection,
00202                                                                       const gchar * name,
00203                                                                       gpointer user_data);
00204 static void menus_destroyed                                          (GObject * menus,
00205                                                                       gpointer user_data);
00206 static void source_unregister                                        (gpointer user_data);
00207 static GVariant * unregister_window                                  (IndicatorAppmenu * iapp,
00208                                                                       guint windowid);
00209 
00210 /* Unique error codes for debug interface */
00211 enum {
00212        ERROR_NO_APPLICATIONS,
00213        ERROR_NO_DEFAULT_APP,
00214        ERROR_WINDOW_NOT_FOUND
00215 };
00216 
00217 /**********************
00218   DBus Interfaces
00219  **********************/
00220 static GDBusNodeInfo *      node_info = NULL;
00221 static GDBusInterfaceInfo * interface_info = NULL;
00222 static GDBusInterfaceVTable interface_table = {
00223        method_call:    bus_method_call,
00224        get_property:   NULL, /* No properties */
00225        set_property:   NULL  /* No properties */
00226 };
00227 
00228 G_DEFINE_TYPE (IndicatorAppmenu, indicator_appmenu, INDICATOR_OBJECT_TYPE);
00229 
00230 /* One time init */
00231 static void
00232 indicator_appmenu_class_init (IndicatorAppmenuClass *klass)
00233 {
00234        GObjectClass *object_class = G_OBJECT_CLASS (klass);
00235 
00236        object_class->dispose = indicator_appmenu_dispose;
00237        object_class->finalize = indicator_appmenu_finalize;
00238 
00239        IndicatorObjectClass * ioclass = INDICATOR_OBJECT_CLASS(klass);
00240 
00241        ioclass->get_entries = get_entries;
00242        ioclass->get_location = get_location;
00243        ioclass->entry_activate = entry_activate;
00244        ioclass->entry_activate_window = entry_activate_window;
00245 
00246        /* Setting up the DBus interfaces */
00247        if (node_info == NULL) {
00248               GError * error = NULL;
00249 
00250               node_info = g_dbus_node_info_new_for_xml(_application_menu_registrar, &error);
00251               if (error != NULL) {
00252                      g_critical("Unable to parse Application Menu Interface description: %s", error->message);
00253                      g_error_free(error);
00254               }
00255        }
00256 
00257        if (interface_info == NULL) {
00258               interface_info = g_dbus_node_info_lookup_interface(node_info, REG_IFACE);
00259 
00260               if (interface_info == NULL) {
00261                      g_critical("Unable to find interface '" REG_IFACE "'");
00262               }
00263        }
00264 
00265        return;
00266 }
00267 
00268 /* Per instance Init */
00269 static void
00270 indicator_appmenu_init (IndicatorAppmenu *self)
00271 {
00272        self->default_app = NULL;
00273        self->apps = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_object_unref);
00274        self->matcher = NULL;
00275        self->active_window = NULL;
00276        self->active_stubs = STUBS_UNKNOWN;
00277        self->close_item = NULL;
00278        self->retry_registration = 0;
00279        self->bus = NULL;
00280        self->owner_id = 0;
00281        self->dbus_registration = 0;
00282 
00283        /* Setup the entries for the fallbacks */
00284        self->window_menus = g_array_sized_new(FALSE, FALSE, sizeof(IndicatorObjectEntry), 2);
00285 
00286        /* Setup the cache of windows with possible desktop entries */
00287        self->desktop_windows = g_hash_table_new(g_direct_hash, g_direct_equal);
00288        self->desktop_menu = NULL; /* Starts NULL until found */
00289 
00290        /* Set up the hashtable of destruction timers */
00291        self->destruction_timers = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, source_unregister);
00292 
00293        build_window_menus(self);
00294 
00295        /* Get the default BAMF matcher */
00296        self->matcher = bamf_matcher_get_default();
00297        if (self->matcher == NULL) {
00298               /* we don't want to exit out of Unity -- but this
00299                  should really never happen */
00300               g_warning("Unable to get BAMF matcher, can not watch applications switch!");
00301        } else {
00302               g_signal_connect(G_OBJECT(self->matcher), "active-window-changed", G_CALLBACK(active_window_changed), self);
00303 
00304               /* Desktop window tracking */
00305               g_signal_connect(G_OBJECT(self->matcher), "view-opened", G_CALLBACK(new_window), self);
00306               g_signal_connect(G_OBJECT(self->matcher), "view-closed", G_CALLBACK(old_window), self);
00307        }
00308 
00309        find_desktop_windows(self);
00310 
00311        /* Request a name so others can find us */
00312        retry_registration(self);
00313 
00314        return;
00315 }
00316 
00317 /* If we weren't able to register on the bus, then we need
00318    to try it all again. */
00319 static gboolean
00320 retry_registration (gpointer user_data)
00321 {
00322        g_return_val_if_fail(IS_INDICATOR_APPMENU(user_data), FALSE);
00323        IndicatorAppmenu * iapp = INDICATOR_APPMENU(user_data);
00324 
00325        iapp->owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
00326                                         DBUS_NAME,
00327                                         G_BUS_NAME_OWNER_FLAGS_NONE,
00328                                         iapp->dbus_registration == 0 ? on_bus_acquired : NULL,
00329                                         on_name_acquired,
00330                                         on_name_lost,
00331                                         g_object_ref(iapp),
00332                                         g_object_unref);
00333 
00334        return TRUE;
00335 }
00336 
00337 static void
00338 on_bus_acquired (GDBusConnection * connection, const gchar * name,
00339                  gpointer user_data)
00340 {
00341        IndicatorAppmenu * iapp = INDICATOR_APPMENU(user_data);
00342        GError * error = NULL;
00343 
00344        iapp->bus = connection;
00345 
00346        /* Now register our object on our new connection */
00347        iapp->dbus_registration = g_dbus_connection_register_object(connection,
00348                                                                    REG_OBJECT,
00349                                                                    interface_info,
00350                                                                    &interface_table,
00351                                                                    user_data,
00352                                                                    NULL,
00353                                                                    &error);
00354 
00355        if (error != NULL) {
00356               g_critical("Unable to register the object to DBus: %s", error->message);
00357               g_error_free(error);
00358               g_bus_unown_name(iapp->owner_id);
00359               iapp->owner_id = 0;
00360               iapp->retry_registration = g_timeout_add_seconds(1, retry_registration, iapp);
00361               return;
00362        }
00363 
00364        return;       
00365 }
00366 
00367 static void
00368 on_name_acquired (GDBusConnection * connection, const gchar * name,
00369                   gpointer user_data)
00370 {
00371 }
00372 
00373 static void
00374 on_name_lost (GDBusConnection * connection, const gchar * name,
00375               gpointer user_data)
00376 {
00377        IndicatorAppmenu * iapp = INDICATOR_APPMENU(user_data);
00378 
00379        if (connection == NULL) {
00380               g_critical("OMG! Unable to get a connection to DBus");
00381        }
00382        else {
00383               g_critical("Unable to claim the name %s", DBUS_NAME);
00384        }
00385 
00386        /* We can rest assured no one will register with us, but let's
00387           just ensure we're not showing anything. */
00388        switch_default_app(iapp, NULL, NULL);
00389 
00390        iapp->owner_id = 0;
00391 }
00392 
00393 /* Object refs decrement */
00394 static void
00395 indicator_appmenu_dispose (GObject *object)
00396 {
00397        IndicatorAppmenu * iapp = INDICATOR_APPMENU(object);
00398 
00399        /* Don't register if we're dying! */
00400        if (iapp->retry_registration != 0) {
00401               g_source_remove(iapp->retry_registration);
00402               iapp->retry_registration = 0;
00403        }
00404 
00405        if (iapp->dbus_registration != 0) {
00406               g_dbus_connection_unregister_object(iapp->bus, iapp->dbus_registration);
00407               /* Don't care if it fails, there's nothing we can do */
00408               iapp->dbus_registration = 0;
00409        }
00410 
00411        if (iapp->destruction_timers != NULL) {
00412               /* These are in dispose and not finalize becuase the dereference
00413                  function removes timers that could need the object to be in
00414                  a valid state, so it's better to have them in dispose */
00415               g_hash_table_destroy(iapp->destruction_timers);
00416               iapp->destruction_timers = NULL;
00417        }
00418 
00419        if (iapp->bus != NULL) {
00420               g_object_unref(iapp->bus);
00421               iapp->bus = NULL;
00422        }
00423 
00424        if (iapp->owner_id != 0) {
00425               g_bus_unown_name(iapp->owner_id);
00426               iapp->owner_id = 0;
00427        }
00428 
00429        /* bring down the matcher before resetting to no menu so we don't
00430           get match signals */
00431        if (iapp->matcher != NULL) {
00432               g_object_unref(iapp->matcher);
00433               iapp->matcher = NULL;
00434        }
00435 
00436        /* No specific ref */
00437        switch_default_app (iapp, NULL, NULL);
00438 
00439        if (iapp->apps != NULL) {
00440               g_hash_table_destroy(iapp->apps);
00441               iapp->apps = NULL;
00442        }
00443 
00444        if (iapp->desktop_windows != NULL) {
00445               g_hash_table_destroy(iapp->desktop_windows);
00446               iapp->desktop_windows = NULL;
00447        }
00448 
00449        if (iapp->desktop_menu != NULL) {
00450               /* Wait, nothing here?  Yup.  We're not referencing the
00451                  menus here they're already attached to the window ID.
00452                  We're just keeping an efficient pointer to them. */
00453               iapp->desktop_menu = NULL;
00454        }
00455 
00456        G_OBJECT_CLASS (indicator_appmenu_parent_class)->dispose (object);
00457        return;
00458 }
00459 
00460 /* Free memory */
00461 static void
00462 indicator_appmenu_finalize (GObject *object)
00463 {
00464        IndicatorAppmenu * iapp = INDICATOR_APPMENU(object);
00465 
00466        if (iapp->window_menus != NULL) {
00467               if (iapp->window_menus->len != 0) {
00468                      g_warning("Window menus weren't free'd in dispose!");
00469               }
00470               g_array_free(iapp->window_menus, TRUE);
00471               iapp->window_menus = NULL;
00472        }
00473 
00474        G_OBJECT_CLASS (indicator_appmenu_parent_class)->finalize (object);
00475        return;
00476 }
00477 
00478 static void
00479 emit_signal (IndicatorAppmenu * iapp, const gchar * name, GVariant * variant)
00480 {
00481        GError * error = NULL;
00482 
00483        g_dbus_connection_emit_signal (iapp->bus,
00484                                      NULL,
00485                                      REG_OBJECT,
00486                                      REG_IFACE,
00487                                      name,
00488                                      variant,
00489                                      &error);
00490 
00491        if (error != NULL) {
00492               g_critical("Unable to send %s signal: %s", name, error->message);
00493               g_error_free(error);
00494               return;
00495        }
00496 
00497        return;
00498 }
00499 
00500 /* Close the current application using magic */
00501 static void
00502 close_current (GtkMenuItem * mi, gpointer user_data)
00503 {
00504        IndicatorAppmenu * iapp = INDICATOR_APPMENU(user_data);
00505 
00506        if (!BAMF_IS_WINDOW (iapp->active_window) || bamf_view_is_closed (BAMF_VIEW (iapp->active_window))) {
00507               g_warning("Can't close a window we don't have. Window is either non-existent or recently closed.");
00508               return;
00509        }
00510 
00511        guint32 xid = bamf_window_get_xid(iapp->active_window);
00512        guint timestamp = gdk_event_get_time(NULL);
00513 
00514        XEvent xev;
00515 
00516        xev.xclient.type = ClientMessage;
00517        xev.xclient.serial = 0;
00518        xev.xclient.send_event = True;
00519        xev.xclient.display = gdk_x11_get_default_xdisplay ();
00520        xev.xclient.window = xid;
00521        xev.xclient.message_type = gdk_x11_atom_to_xatom (gdk_atom_intern ("_NET_CLOSE_WINDOW", TRUE));
00522        xev.xclient.format = 32;
00523        xev.xclient.data.l[0] = timestamp;
00524        xev.xclient.data.l[1] = 2; /* Client type pager, so it listens to us */
00525        xev.xclient.data.l[2] = 0;
00526        xev.xclient.data.l[3] = 0;
00527        xev.xclient.data.l[4] = 0;
00528 
00529        gdk_error_trap_push ();
00530        XSendEvent (gdk_x11_get_default_xdisplay (),
00531                    gdk_x11_get_default_root_xwindow (),
00532                    False,
00533                    SubstructureRedirectMask | SubstructureNotifyMask,
00534                    &xev);
00535        gdk_flush ();
00536        gdk_error_trap_pop_ignored ();
00537 
00538        return;
00539 }
00540 
00541 /* Create the default window menus */
00542 static void
00543 build_window_menus (IndicatorAppmenu * iapp)
00544 {
00545        IndicatorObjectEntry entries[1] = {{0}};
00546        GtkAccelGroup * agroup = gtk_accel_group_new();
00547        GtkMenuItem * mi = NULL;
00548        GtkStockItem stockitem;
00549 
00550        /* File Menu */
00551        if (!gtk_stock_lookup(GTK_STOCK_FILE, &stockitem)) {
00552               g_warning("Unable to find the file menu stock item");
00553               stockitem.label = "_File";
00554        }
00555        entries[0].label = GTK_LABEL(gtk_label_new_with_mnemonic(stockitem.label));
00556        g_object_ref(G_OBJECT(entries[0].label));
00557        gtk_widget_show(GTK_WIDGET(entries[0].label));
00558 
00559        entries[0].menu = GTK_MENU(gtk_menu_new());
00560        g_object_ref(G_OBJECT(entries[0].menu));
00561 
00562        mi = GTK_MENU_ITEM(gtk_image_menu_item_new_from_stock(GTK_STOCK_CLOSE, agroup));
00563        gtk_widget_set_sensitive(GTK_WIDGET(mi), FALSE);
00564        g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(close_current), iapp);
00565        gtk_widget_show(GTK_WIDGET(mi));
00566        gtk_menu_shell_append(GTK_MENU_SHELL(entries[0].menu), GTK_WIDGET(mi));
00567        iapp->close_item = mi;
00568 
00569        gtk_widget_show(GTK_WIDGET(entries[0].menu));
00570 
00571        /* Copy the entries on the stack into the array */
00572        g_array_insert_vals(iapp->window_menus, 0, entries, 1);
00573 
00574        return;
00575 }
00576 
00577 /* Determine which windows should be used as the desktop
00578    menus. */
00579 static void
00580 determine_new_desktop (IndicatorAppmenu * iapp)
00581 {
00582        GList * keys = g_hash_table_get_keys(iapp->desktop_windows);
00583        GList * key;
00584 
00585        for (key = keys; key != NULL; key = g_list_next(key)) {
00586               guint xid = GPOINTER_TO_UINT(key->data);
00587               gpointer pwm = g_hash_table_lookup(iapp->apps, GUINT_TO_POINTER(xid));
00588               if (pwm != NULL) {
00589                      g_debug("Setting Desktop Menus to: %X", xid);
00590                      iapp->desktop_menu = WINDOW_MENU(pwm);
00591               }
00592        }
00593 
00594        g_list_free(keys);
00595 
00596        return;
00597 }
00598 
00599 /* Puts all the desktop windows into the hash table so that we
00600    can have a nice list of them. */
00601 static void
00602 find_desktop_windows (IndicatorAppmenu * iapp)
00603 {
00604        GList * windows = bamf_matcher_get_windows(iapp->matcher);
00605        GList * lwindow;
00606 
00607        for (lwindow = windows; lwindow != NULL; lwindow = g_list_next(lwindow)) {
00608               BamfView * view = BAMF_VIEW(lwindow->data);
00609               new_window(iapp->matcher, view, iapp);
00610        }
00611 
00612        g_list_free(windows);
00613 
00614        return;
00615 }
00616 
00617 /* When new windows are born, we check to see if they're desktop
00618    windows. */
00619 static void
00620 new_window (BamfMatcher * matcher, BamfView * view, gpointer user_data)
00621 {
00622        if (view == NULL || !BAMF_IS_WINDOW(view)) {
00623               return;
00624        }
00625 
00626        BamfWindow * window = BAMF_WINDOW(view);
00627        IndicatorAppmenu * iapp = INDICATOR_APPMENU(user_data);
00628        guint32 xid = bamf_window_get_xid(window);
00629 
00630        /* Make sure we don't destroy it later */
00631        g_hash_table_remove(iapp->destruction_timers, GUINT_TO_POINTER(xid));
00632 
00633        if (bamf_window_get_window_type(window) != BAMF_WINDOW_DESKTOP) {
00634               return;
00635        }
00636 
00637        g_hash_table_insert(iapp->desktop_windows, GUINT_TO_POINTER(xid), GINT_TO_POINTER(TRUE));
00638 
00639        g_debug("New Desktop Window: %X", xid);
00640 
00641        gpointer pwm = g_hash_table_lookup(iapp->apps, GUINT_TO_POINTER(xid));
00642        if (pwm != NULL) {
00643               WindowMenu * wm = WINDOW_MENU(pwm);
00644               iapp->desktop_menu = wm;
00645               g_debug("Setting Desktop Menus to: %X", xid);
00646               if (iapp->active_window == NULL && iapp->default_app == NULL) {
00647                      switch_default_app(iapp, NULL, NULL);
00648               }
00649        }
00650 
00651        return;
00652 }
00653 
00654 typedef struct _destroy_data_t destroy_data_t;
00655 struct _destroy_data_t {
00656        IndicatorAppmenu * iapp;
00657        guint32 xid;
00658 };
00659 
00660 /* Timeout to finally cleanup the window.  Causes is to ignore glitches that
00661    come from BAMF/WNCK. */
00662 static gboolean
00663 destroy_window_timeout (gpointer user_data)
00664 {
00665        destroy_data_t * destroy_data = (destroy_data_t *)user_data;
00666        g_hash_table_steal(destroy_data->iapp->destruction_timers, GUINT_TO_POINTER(destroy_data->xid));
00667        unregister_window(destroy_data->iapp, destroy_data->xid);
00668        return FALSE; /* free's data through source deregistration */
00669 }
00670 
00671 /* Unregisters the source in the hash table when it gets removed.  This ensure
00672    we don't leave any timeouts around */
00673 static void
00674 source_unregister (gpointer user_data)
00675 {
00676        g_source_remove(GPOINTER_TO_UINT(user_data));
00677        return;
00678 }
00679 
00680 /* When windows leave us, this function gets called */
00681 static void
00682 old_window (BamfMatcher * matcher, BamfView * view, gpointer user_data)
00683 {
00684        if (view == NULL || !BAMF_IS_WINDOW(view)) {
00685               return;
00686        }
00687 
00688        IndicatorAppmenu * iapp = INDICATOR_APPMENU(user_data);
00689        BamfWindow * window = BAMF_WINDOW(view);
00690        guint32 xid = bamf_window_get_xid(window);
00691 
00692        destroy_data_t * destroy_data = g_new0(destroy_data_t, 1);
00693        destroy_data->iapp = iapp;
00694        destroy_data->xid = xid;
00695 
00696        guint source_id = g_timeout_add_seconds_full(G_PRIORITY_LOW, 5, destroy_window_timeout, destroy_data, g_free);
00697        g_hash_table_replace(iapp->destruction_timers, GUINT_TO_POINTER(xid), GUINT_TO_POINTER(source_id));
00698 
00699        return;
00700 }
00701 
00702 /* List of desktop files that shouldn't have menu stubs. */
00703 const static gchar * stubs_blacklist[] = {
00704        /* Firefox */
00705        "/usr/share/applications/firefox.desktop",
00706        /* Thunderbird */
00707        "/usr/share/applications/thunderbird.desktop",
00708        /* Open Office */
00709        "/usr/share/applications/openoffice.org-base.desktop",
00710        "/usr/share/applications/openoffice.org-impress.desktop",
00711        "/usr/share/applications/openoffice.org-calc.desktop",
00712        "/usr/share/applications/openoffice.org-math.desktop",
00713        "/usr/share/applications/openoffice.org-draw.desktop",
00714        "/usr/share/applications/openoffice.org-writer.desktop",
00715        /* Blender */
00716        "/usr/share/applications/blender-fullscreen.desktop",
00717        "/usr/share/applications/blender-windowed.desktop",
00718        /* Eclipse */
00719        "/usr/share/applications/eclipse.desktop",
00720 
00721        NULL
00722 };
00723 
00724 /* Check with BAMF, and then check the blacklist of desktop files
00725    to see if any are there.  Otherwise, show the stubs. */
00726 gboolean
00727 show_menu_stubs (BamfApplication * app)
00728 {
00729        if (bamf_application_get_show_menu_stubs(app) == FALSE) {
00730               return FALSE;
00731        }
00732 
00733        const gchar * desktop_file = bamf_application_get_desktop_file(app);
00734        if (desktop_file == NULL || desktop_file[0] == '\0') {
00735               return TRUE;
00736        }
00737 
00738        int i;
00739        for (i = 0; stubs_blacklist[i] != NULL; i++) {
00740               if (g_strcmp0(stubs_blacklist[i], desktop_file) == 0) {
00741                      return FALSE;
00742               }
00743        }
00744 
00745        return TRUE;
00746 }
00747 
00748 /* Get the current set of entries */
00749 static GList *
00750 get_entries (IndicatorObject * io)
00751 {
00752        g_return_val_if_fail(IS_INDICATOR_APPMENU(io), NULL);
00753        IndicatorAppmenu * iapp = INDICATOR_APPMENU(io);
00754 
00755        /* If we have a focused app with menus, use it's windows */
00756        if (iapp->default_app != NULL) {
00757               return window_menu_get_entries(iapp->default_app);
00758        }
00759 
00760        /* Else, let's go with desktop windows if there isn't a focused window */
00761        if (iapp->active_window == NULL) {
00762               if (iapp->desktop_menu == NULL) {
00763                      return NULL;
00764               } else {
00765                      return window_menu_get_entries(iapp->desktop_menu);
00766               }
00767        }
00768 
00769        /* Oh, now we're looking at stubs. */
00770 
00771        if (iapp->active_stubs == STUBS_UNKNOWN) {
00772               iapp->active_stubs = STUBS_SHOW;
00773 
00774               BamfApplication * app = bamf_matcher_get_application_for_window(iapp->matcher, iapp->active_window);
00775               if (app != NULL) {
00776                      /* First check to see if we can find an app, then if we can
00777                         check to see if it has an opinion on whether we should
00778                         show the stubs or not. */
00779                      if (show_menu_stubs(app) == FALSE) {
00780                             /* If it blocks them, fall out. */
00781                             iapp->active_stubs = STUBS_HIDE;
00782                      }
00783               }
00784        }
00785 
00786        if (iapp->active_stubs == STUBS_HIDE) {
00787               return NULL;
00788        }
00789 
00790        if (indicator_object_check_environment(INDICATOR_OBJECT(iapp), "unity")) {
00791               return NULL;
00792        }
00793 
00794        GList * output = NULL;
00795        int i;
00796 
00797        /* There is only one item in window_menus now, but there
00798           was more, and there is likely to be more in the future
00799           so we're leaving this here to avoid a possible bug. */
00800        for (i = 0; i < iapp->window_menus->len; i++) {
00801               output = g_list_append(output, &g_array_index(iapp->window_menus, IndicatorObjectEntry, i));
00802        }
00803 
00804        return output;
00805 }
00806 
00807 /* Grabs the location of the entry */
00808 static guint
00809 get_location (IndicatorObject * io, IndicatorObjectEntry * entry)
00810 {
00811        guint count = 0;
00812        IndicatorAppmenu * iapp = INDICATOR_APPMENU(io);
00813        if (iapp->default_app != NULL) {
00814               /* Find the location in the app */
00815               count = window_menu_get_location(iapp->default_app, entry);
00816        } else if (iapp->active_window != NULL) {
00817               /* Find the location in the window menus */
00818               for (count = 0; count < iapp->window_menus->len; count++) {
00819                      if (entry == &g_array_index(iapp->window_menus, IndicatorObjectEntry, count)) {
00820                             break;
00821                      }
00822               }
00823               if (count == iapp->window_menus->len) {
00824                      g_warning("Unable to find entry in default window menus");
00825                      count = 0;
00826               }
00827        } else {
00828               /* Find the location in the desktop menu */
00829               if (iapp->desktop_menu != NULL) {
00830                      count = window_menu_get_location(iapp->desktop_menu, entry);
00831               }
00832        }
00833        return count;
00834 }
00835 
00836 /* Responds to a menuitem being activated on the panel. */
00837 static void
00838 entry_activate (IndicatorObject * io, IndicatorObjectEntry * entry, guint timestamp)
00839 {
00840        return entry_activate_window(io, entry, 0, timestamp);
00841 }
00842 
00843 /* Find the BAMF Window that is associated with that XID.  Unfortunately
00844    this requires a bit of searching, don't do it too often */
00845 static BamfWindow *
00846 xid_to_bamf_window (IndicatorAppmenu * iapp, guint xid)
00847 {
00848        GList * windows = bamf_matcher_get_windows(iapp->matcher);
00849        GList * window;
00850        BamfWindow * newwindow = NULL;
00851 
00852        for (window = windows; window != NULL; window = g_list_next(window)) {
00853               if (!BAMF_IS_WINDOW(window->data)) {
00854                      continue;
00855               }
00856 
00857               BamfWindow * testwindow = BAMF_WINDOW(window->data);
00858 
00859               if (xid == bamf_window_get_xid(testwindow)) {
00860                      newwindow = testwindow;
00861                      break;
00862               }
00863        }
00864        g_list_free(windows);
00865 
00866        return newwindow;
00867 }
00868 
00869 /* Responds to a menuitem being activated on the panel. */
00870 static void
00871 entry_activate_window (IndicatorObject * io, IndicatorObjectEntry * entry, guint windowid, guint timestamp)
00872 {
00873        IndicatorAppmenu * iapp = INDICATOR_APPMENU(io);
00874 
00875        /* We need to force a focus change in this case as we probably
00876           just haven't gotten the signal from BAMF yet */
00877        if (windowid != 0) {
00878               BamfView * newwindow = BAMF_VIEW(xid_to_bamf_window(iapp, windowid));
00879 
00880               if (newwindow != NULL) {
00881                      active_window_changed(iapp->matcher, BAMF_VIEW(iapp->active_window), newwindow, iapp);
00882               }
00883        }
00884 
00885        if (iapp->default_app != NULL) {
00886               window_menu_entry_activate(iapp->default_app, entry, timestamp);
00887               return;
00888        }
00889 
00890        if (iapp->active_window == NULL) {
00891               if (iapp->desktop_menu != NULL) {
00892                      window_menu_entry_activate(iapp->desktop_menu, entry, timestamp);
00893               }
00894               return;
00895        }
00896 
00897        /* Else we've got stubs, and the stubs don't care. */
00898 
00899        return;
00900 }
00901 
00902 /* Checks to see we cared about a window that's going
00903    away, so that we can deal with that */
00904 static void
00905 window_finalized_is_active (gpointer user_data, GObject * old_window)
00906 {
00907        g_return_if_fail(IS_INDICATOR_APPMENU(user_data));
00908        IndicatorAppmenu * iapp = INDICATOR_APPMENU(user_data);
00909 
00910        /* Pointer comparison as we can't really trust any of the
00911           pointers to do any dereferencing */
00912        if ((gpointer)iapp->active_window != (gpointer)old_window) {
00913               /* Ah, no issue, we weren't caring about this one
00914                  anyway. */
00915               return;
00916        }
00917 
00918        /* We're going to a state where we don't know what the active
00919           window is, hopefully BAMF will save us */
00920        active_window_changed (iapp->matcher, NULL, NULL, iapp);
00921 
00922        return;
00923 }
00924 
00925 /* A helper for switch_default_app that takes care of the
00926    switching of the active window variable */
00927 static void
00928 switch_active_window (IndicatorAppmenu * iapp, BamfWindow * active_window)
00929 {
00930        if (iapp->active_window == active_window) {
00931               return;
00932        }
00933 
00934        if (iapp->active_window != NULL) {
00935               g_object_weak_unref(G_OBJECT(iapp->active_window), window_finalized_is_active, iapp);
00936        }
00937 
00938        iapp->active_window = active_window;
00939        iapp->active_stubs = STUBS_UNKNOWN;
00940 
00941        /* Close any existing open menu by showing a null entry */
00942        window_show_menu(iapp->default_app, NULL, gtk_get_current_event_time(), iapp);
00943 
00944        if (active_window != NULL) {
00945               g_object_weak_ref(G_OBJECT(active_window), window_finalized_is_active, iapp);
00946        }
00947 
00948        if (iapp->close_item == NULL) {
00949               g_warning("No close item!?!?!");
00950               return;
00951        }
00952 
00953        gtk_widget_set_sensitive(GTK_WIDGET(iapp->close_item), FALSE);
00954 
00955        if (iapp->active_window == NULL) {
00956               return;
00957        }
00958 
00959        guint32 xid = bamf_window_get_xid(iapp->active_window);
00960        if (xid == 0 || bamf_view_is_closed (BAMF_VIEW (iapp->active_window))) {
00961               return;
00962        }
00963  
00964        GdkWMFunction functions;
00965        if (!egg_xid_get_functions(xid, &functions)) {
00966               g_debug("Unable to get MWM functions for: %d", xid);
00967               functions = GDK_FUNC_ALL;
00968        }
00969 
00970        if (functions & GDK_FUNC_ALL || functions & GDK_FUNC_CLOSE) {
00971               gtk_widget_set_sensitive(GTK_WIDGET(iapp->close_item), TRUE);
00972        }
00973 
00974        return;
00975 }
00976 
00977 /* Switch applications, remove all the entires for the previous
00978    one and add them for the new application */
00979 static void
00980 switch_default_app (IndicatorAppmenu * iapp, WindowMenu * newdef, BamfWindow * active_window)
00981 {
00982        if (iapp->default_app == newdef && iapp->default_app != NULL) {
00983               /* We've got an app with menus and it hasn't changed. */
00984 
00985               /* Keep active window up-to-date, though we're probably not
00986                  using it much. */
00987               switch_active_window(iapp, active_window);
00988               return;
00989        }
00990 
00991        if (iapp->default_app == NULL && iapp->active_window == active_window && newdef == NULL) {
00992               /* There's no application menus, but the active window hasn't
00993                  changed.  So there's no change. */
00994               return;
00995        }
00996 
00997        /* hide the entries that we're swapping out */
00998        indicator_object_set_visible (INDICATOR_OBJECT(iapp), FALSE);
00999        
01000        /* Disconnect signals */
01001        if (iapp->sig_entry_added != 0) {
01002               g_signal_handler_disconnect(G_OBJECT(iapp->default_app), iapp->sig_entry_added);
01003               iapp->sig_entry_added = 0;
01004        }
01005        if (iapp->sig_entry_removed != 0) {
01006               g_signal_handler_disconnect(G_OBJECT(iapp->default_app), iapp->sig_entry_removed);
01007               iapp->sig_entry_removed = 0;
01008        }
01009        if (iapp->sig_status_changed != 0) {
01010               g_signal_handler_disconnect(G_OBJECT(iapp->default_app), iapp->sig_status_changed);
01011               iapp->sig_status_changed = 0;
01012        }
01013        if (iapp->sig_show_menu != 0) {
01014               g_signal_handler_disconnect(G_OBJECT(iapp->default_app), iapp->sig_show_menu);
01015               iapp->sig_show_menu = 0;
01016        }
01017        if (iapp->sig_a11y_update != 0) {
01018               g_signal_handler_disconnect(G_OBJECT(iapp->default_app), iapp->sig_a11y_update);
01019               iapp->sig_a11y_update = 0;
01020        }
01021 
01022        /* Default App is NULL, let's see if it needs replacement */
01023        iapp->default_app = NULL;
01024 
01025        /* Update the active window pointer -- may be NULL */
01026        switch_active_window(iapp, active_window);
01027 
01028        /* If we're putting up a new window, let's do that now. */
01029        if (newdef != NULL) {
01030               /* Switch */
01031               iapp->default_app = newdef;
01032 
01033               /* Connect signals */
01034               iapp->sig_entry_added =   g_signal_connect(G_OBJECT(iapp->default_app),
01035                                                          WINDOW_MENU_SIGNAL_ENTRY_ADDED,
01036                                                          G_CALLBACK(window_entry_added),
01037                                                          iapp);
01038               iapp->sig_entry_removed = g_signal_connect(G_OBJECT(iapp->default_app),
01039                                                          WINDOW_MENU_SIGNAL_ENTRY_REMOVED,
01040                                                          G_CALLBACK(window_entry_removed),
01041                                                          iapp);
01042               iapp->sig_status_changed = g_signal_connect(G_OBJECT(iapp->default_app),
01043                                                          WINDOW_MENU_SIGNAL_STATUS_CHANGED,
01044                                                          G_CALLBACK(window_status_changed),
01045                                                          iapp);
01046               iapp->sig_show_menu     = g_signal_connect(G_OBJECT(iapp->default_app),
01047                                                          WINDOW_MENU_SIGNAL_SHOW_MENU,
01048                                                          G_CALLBACK(window_show_menu),
01049                                                          iapp);
01050               iapp->sig_a11y_update   = g_signal_connect(G_OBJECT(iapp->default_app),
01051                                                          WINDOW_MENU_SIGNAL_A11Y_UPDATE,
01052                                                          G_CALLBACK(window_a11y_update),
01053                                                          iapp);
01054        }
01055 
01056        /* show the entries that we're swapping in */
01057        indicator_object_set_visible (INDICATOR_OBJECT(iapp), TRUE);
01058 
01059        /* Set up initial state for new entries if needed */
01060        if (iapp->default_app != NULL &&
01061             window_menu_get_status (iapp->default_app) != WINDOW_MENU_STATUS_NORMAL) {
01062               window_status_changed (iapp->default_app,
01063                                      window_menu_get_status (iapp->default_app),
01064                                      iapp);
01065        }
01066 
01067        return;
01068 }
01069 
01070 /* Recieve the signal that the window being shown
01071    has now changed. */
01072 static void
01073 active_window_changed (BamfMatcher * matcher, BamfView * oldview, BamfView * newview, gpointer user_data)
01074 {
01075        BamfWindow * window = NULL;
01076 
01077        if (newview != NULL) {
01078               window = BAMF_WINDOW(newview);
01079               if (window == NULL) {
01080                      g_warning("Active window changed to View thats not a window.");
01081               }
01082        } else {
01083               g_debug("Active window is: NULL");
01084        }
01085 
01086        IndicatorAppmenu * appmenu = INDICATOR_APPMENU(user_data);
01087 
01088        if (window != NULL && bamf_window_get_window_type(window) == BAMF_WINDOW_DESKTOP) {
01089               g_debug("Switching to menus from desktop");
01090               switch_default_app(appmenu, NULL, NULL);
01091               return;
01092        }
01093 
01094        WindowMenu * menus = NULL;
01095        guint32 xid = 0;
01096 
01097        while (window != NULL && menus == NULL) {
01098               xid = bamf_window_get_xid(window);
01099        
01100               menus = g_hash_table_lookup(appmenu->apps, GUINT_TO_POINTER(xid));
01101 
01102               /* First look to see if we can get these from the
01103                  GMenuModel access */
01104               if (menus == NULL) {
01105                      gchar * uniquename = bamf_window_get_utf8_prop (window, "_GTK_UNIQUE_BUS_NAME");
01106 
01107                      if (uniquename != NULL) {
01108                             BamfApplication * app = bamf_matcher_get_application_for_window(matcher, window);
01109 
01110                             menus = WINDOW_MENU(window_menu_model_new(app, window));
01111 
01112                             g_hash_table_insert(appmenu->apps, GUINT_TO_POINTER(xid), menus);
01113                      }
01114 
01115                      g_free(uniquename);
01116               }
01117 
01118               if (menus == NULL) {
01119                      g_debug("Looking for parent window on XID %d", xid);
01120                      window = bamf_window_get_transient(window);
01121               }
01122        }
01123 
01124        /* Note: We're not using window here, but re-casting the
01125           newwindow variable.  Which means we stay where we were
01126           but get the menus from parents. */
01127        g_debug("Switching to menus from XID %d", xid);
01128        if (newview != NULL) {
01129               switch_default_app(appmenu, menus, BAMF_WINDOW(newview));
01130        } else {
01131               switch_default_app(appmenu, menus, NULL);
01132        }
01133 
01134        return;
01135 }
01136 
01137 /* Respond to the menus being destroyed.  We need to deregister
01138    and make sure we weren't being shown.  */
01139 static void
01140 menus_destroyed (GObject * menus, gpointer user_data)
01141 {
01142        gboolean reload_menus = FALSE;
01143        WindowMenu * wm = WINDOW_MENU(menus);
01144        IndicatorAppmenu * iapp = INDICATOR_APPMENU(user_data);
01145 
01146        guint32 xid = window_menu_get_xid(wm);
01147        g_return_if_fail(xid != 0);
01148 
01149        g_hash_table_steal(iapp->apps, GUINT_TO_POINTER(xid));
01150 
01151        g_debug("Removing menus for %d", xid);
01152 
01153        if (iapp->desktop_menu == wm) {
01154               iapp->desktop_menu = NULL;
01155               determine_new_desktop(iapp);
01156               if (iapp->default_app == NULL && iapp->active_window == NULL) {
01157                      reload_menus = TRUE;
01158               }
01159        }
01160 
01161        /* If we're it, let's remove ourselves and BAMF will probably
01162           give us a new entry in a bit. */
01163        if (iapp->default_app == wm) {
01164               reload_menus = TRUE;
01165        }
01166 
01167        if (reload_menus) {
01168               switch_default_app(iapp, NULL, NULL);
01169        }
01170 
01171        return;
01172 }
01173 
01174 /* A new window wishes to register it's windows with us */
01175 static GVariant *
01176 register_window (IndicatorAppmenu * iapp, guint windowid, const gchar * objectpath,
01177                  const gchar * sender)
01178 {
01179        g_debug("Registering window ID %d with path %s from %s", windowid, objectpath, sender);
01180 
01181        /* Shouldn't do anything, but let's be sure */
01182        g_hash_table_remove(iapp->destruction_timers, GUINT_TO_POINTER(windowid));
01183 
01184        if (g_hash_table_lookup(iapp->apps, GUINT_TO_POINTER(windowid)) == NULL && windowid != 0) {
01185               WindowMenu * wm = WINDOW_MENU(window_menu_dbusmenu_new(windowid, sender, objectpath));
01186               g_return_val_if_fail(wm != NULL, FALSE);
01187 
01188               g_hash_table_insert(iapp->apps, GUINT_TO_POINTER(windowid), wm);
01189 
01190               emit_signal(iapp, "WindowRegistered",
01191                           g_variant_new("(uso)", windowid, sender, objectpath));
01192 
01193               gpointer pdesktop = g_hash_table_lookup(iapp->desktop_windows, GUINT_TO_POINTER(windowid));
01194               if (pdesktop != NULL) {
01195                      determine_new_desktop(iapp);
01196               }
01197 
01198               /* Note: Does not cause ref */
01199               BamfWindow * win = bamf_matcher_get_active_window(iapp->matcher);
01200 
01201               active_window_changed(iapp->matcher, NULL, BAMF_VIEW(win), iapp);
01202        } else {
01203               if (windowid == 0) {
01204                      g_warning("Can't build windows for a NULL window ID %d with path %s from %s", windowid, objectpath, sender);
01205               } else {
01206                      g_warning("Already have a menu for window ID %d with path %s from %s, unregistering that one", windowid, objectpath, sender);
01207                      unregister_window(iapp, windowid);
01208 
01209                      /* NOTE: So we're doing a lookup here.  That seems pretty useless
01210                         now doesn't it.  It's for a good reason.  We're going recursive
01211                         with a pretty complex set of functions we want to ensure that
01212                         we're not going to end up infinitely recursive otherwise things
01213                         could go really bad. */
01214                      if (g_hash_table_lookup(iapp->apps, GUINT_TO_POINTER(windowid)) == NULL) {
01215                             return register_window(iapp, windowid, objectpath, sender);
01216                      }
01217 
01218                      g_warning("Unable to unregister window!");
01219               }
01220        }
01221 
01222        return g_variant_new("()");
01223 }
01224 
01225 /* Kindly remove an entry from our DB */
01226 static GVariant *
01227 unregister_window (IndicatorAppmenu * iapp, guint windowid)
01228 {
01229        g_debug("Unregistering: %d", windowid);
01230        g_return_val_if_fail(IS_INDICATOR_APPMENU(iapp), NULL);
01231        g_return_val_if_fail(iapp->matcher != NULL, NULL);
01232 
01233        /* Make sure we don't destroy it later */
01234        g_hash_table_remove(iapp->destruction_timers, GUINT_TO_POINTER(windowid));
01235 
01236        /* If it's a desktop window remove it from that table as well */
01237        g_hash_table_remove(iapp->desktop_windows, GUINT_TO_POINTER(windowid));
01238 
01239        emit_signal(iapp, "WindowUnregistered", g_variant_new ("(u)", windowid));
01240 
01241        /* Now let's see if we've got a WM object for it then
01242           we need to mark it as destroyed and unreference to
01243           actually destroy it. */
01244        gpointer wm = g_hash_table_lookup(iapp->apps, GUINT_TO_POINTER(windowid));
01245        if (wm != NULL) {
01246               GObject * wmo = G_OBJECT(wm);
01247 
01248               /* Using destroyed so that if the menus are shown
01249                  they'll be switch and the current window gets
01250                  updated as well. */
01251               menus_destroyed(wmo, iapp);
01252               g_object_unref(wmo);
01253        }
01254 
01255        return NULL;
01256 }
01257 
01258 /* Grab the menu information for a specific window */
01259 static GVariant *
01260 get_menu_for_window (IndicatorAppmenu * iapp, guint windowid, GError ** error)
01261 {
01262        WindowMenu * wm = NULL;
01263 
01264        if (windowid == 0) {
01265               wm = iapp->default_app;
01266        } else {
01267               wm = WINDOW_MENU(g_hash_table_lookup(iapp->apps, GUINT_TO_POINTER(windowid)));
01268        }
01269 
01270        if (wm == NULL) {
01271               g_set_error_literal(error, error_quark(), ERROR_WINDOW_NOT_FOUND, "Window not found");
01272               return NULL;
01273        }
01274 
01275        GVariantBuilder builder;
01276        g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE);
01277 
01278        if (IS_WINDOW_MENU_DBUSMENU(wm)) {
01279               gchar * address = window_menu_dbusmenu_get_address(WINDOW_MENU_DBUSMENU(wm));
01280               gchar * path = window_menu_dbusmenu_get_path(WINDOW_MENU_DBUSMENU(wm));
01281               g_variant_builder_add_value(&builder, g_variant_new_string(address));
01282               g_variant_builder_add_value(&builder, g_variant_new_object_path(path));
01283               g_free(path);
01284               g_free(address);
01285        } else {
01286               g_variant_builder_add_value(&builder, g_variant_new_string(""));
01287               g_variant_builder_add_value(&builder, g_variant_new_object_path("/"));
01288        }
01289 
01290        return g_variant_builder_end(&builder);
01291 }
01292 
01293 /* Get all the menus we have */
01294 static GVariant *
01295 get_menus (IndicatorAppmenu * iapp, GError ** error)
01296 {
01297        if (iapp->apps == NULL) {
01298               g_set_error_literal(error, error_quark(), ERROR_NO_APPLICATIONS, "No applications are registered");
01299               return NULL;
01300        }
01301 
01302        GVariantBuilder builder;
01303        GHashTableIter hash_iter;
01304        gpointer value;
01305 
01306        g_variant_builder_init (&builder, G_VARIANT_TYPE("a(uso)"));
01307        g_hash_table_iter_init (&hash_iter, iapp->apps);
01308        while (g_hash_table_iter_next (&hash_iter, NULL, &value)) {
01309               if (value != NULL) {
01310                      WindowMenu * wm = WINDOW_MENU(value);
01311                      if (IS_WINDOW_MENU_DBUSMENU(wm)) {
01312                             gchar * address = window_menu_dbusmenu_get_address(WINDOW_MENU_DBUSMENU(wm));
01313                             gchar * path = window_menu_dbusmenu_get_path(WINDOW_MENU_DBUSMENU(wm));
01314                             g_variant_builder_add (&builder, "(uso)",
01315                                                    window_menu_get_xid(wm),
01316                                                    address,
01317                                                    path);
01318                             g_free(path);
01319                             g_free(address);
01320                      } else {
01321                             g_variant_builder_add (&builder, "(uso)",
01322                                                    window_menu_get_xid(wm),
01323                                                    "",
01324                                                    "/");
01325                      }
01326               }
01327        }
01328 
01329        return g_variant_new ("(a(uso))", &builder);
01330 }
01331 
01332 /* A method has been called from our dbus inteface.  Figure out what it
01333    is and dispatch it. */
01334 static void
01335 bus_method_call (GDBusConnection * connection, const gchar * sender,
01336                  const gchar * object_path, const gchar * interface,
01337                  const gchar * method, GVariant * params,
01338                  GDBusMethodInvocation * invocation, gpointer user_data)
01339 {
01340        IndicatorAppmenu * iapp = INDICATOR_APPMENU(user_data);
01341        GVariant * retval = NULL;
01342        GError * error = NULL;
01343 
01344        if (g_strcmp0(method, "RegisterWindow") == 0) {
01345               guint32 xid;
01346               const gchar * path;
01347               g_variant_get(params, "(u&o)", &xid, &path);
01348               retval = register_window(iapp, xid, path, sender);
01349        } else if (g_strcmp0(method, "UnregisterWindow") == 0) {
01350               guint32 xid;
01351               g_variant_get(params, "(u)", &xid);
01352               retval = unregister_window(iapp, xid);
01353        } else if (g_strcmp0(method, "GetMenuForWindow") == 0) {
01354               guint32 xid;
01355               g_variant_get(params, "(u)", &xid);
01356               retval = get_menu_for_window(iapp, xid, &error);
01357        } else if (g_strcmp0(method, "GetMenus") == 0) {
01358               retval = get_menus(iapp, &error);
01359        } else {
01360               g_warning("Calling method '%s' on the indicator service and it's unknown", method);
01361        }
01362 
01363        if (error != NULL) {
01364               g_dbus_method_invocation_return_dbus_error(invocation,
01365                                                          "com.canonical.AppMenu.Error",
01366                                                          error->message);
01367               g_error_free(error);
01368        } else {
01369               g_dbus_method_invocation_return_value(invocation, retval);
01370        }
01371        return;
01372 }
01373 
01374 /* Pass up the entry added event */
01375 static void
01376 window_entry_added (WindowMenu * mw, IndicatorObjectEntry * entry, gpointer user_data)
01377 {
01378        entry->parent_object = user_data;
01379        g_signal_emit_by_name(G_OBJECT(user_data), INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED, entry);
01380        return;
01381 }
01382 
01383 /* Pass up the entry removed event */
01384 static void
01385 window_entry_removed (WindowMenu * mw, IndicatorObjectEntry * entry, gpointer user_data)
01386 {
01387        entry->parent_object = user_data;
01388        g_signal_emit_by_name(G_OBJECT(user_data), INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED, entry);
01389        return;
01390 }
01391 
01392 /* Pass up the status changed event */
01393 static void
01394 window_status_changed (WindowMenu * mw, DbusmenuStatus status, IndicatorAppmenu * iapp)
01395 {
01396        gboolean show_now = (status == DBUSMENU_STATUS_NOTICE);
01397        GList * entry_head, * entries;
01398 
01399        entry_head = indicator_object_get_entries(INDICATOR_OBJECT(iapp));
01400 
01401        for (entries = entry_head; entries != NULL; entries = g_list_next(entries)) {
01402               IndicatorObjectEntry * entry = (IndicatorObjectEntry *)entries->data;
01403               g_signal_emit(G_OBJECT(iapp), INDICATOR_OBJECT_SIGNAL_SHOW_NOW_CHANGED_ID, 0, entry, show_now);
01404        }
01405 
01406        return;
01407 }
01408 
01409 /* Pass up the show menu event */
01410 static void
01411 window_show_menu (WindowMenu * mw, IndicatorObjectEntry * entry, guint timestamp, gpointer user_data)
01412 {
01413        g_signal_emit_by_name(G_OBJECT(user_data), INDICATOR_OBJECT_SIGNAL_MENU_SHOW, entry, timestamp);
01414        return;
01415 }
01416 
01417 /* Pass up the accessible string update */
01418 static void
01419 window_a11y_update (WindowMenu * mw, IndicatorObjectEntry * entry, gpointer user_data)
01420 {
01421        g_signal_emit_by_name(G_OBJECT(user_data), INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE, entry);
01422        return;
01423 }
01424 
01425 /**********************
01426   DEBUG INTERFACE
01427  **********************/
01428 
01429 /* Builds the error quark if we need it, otherwise just
01430    returns the same value */
01431 static GQuark
01432 error_quark (void)
01433 {
01434        static GQuark error_quark = 0;
01435 
01436        if (error_quark == 0) {
01437               error_quark = g_quark_from_static_string("indicator-appmenu");
01438        }
01439 
01440        return error_quark;
01441 }
01442