1 /*
2 An implementation of indicator object showing menus from applications.
3
4 Copyright 2010 Canonical Ltd.
5
6 Authors:
7 Ted Gould <ted@canonical.com>
8
9 This program is free software: you can redistribute it and/or modify it
10 under the terms of the GNU General Public License version 3, as published
11 by the Free Software Foundation.
12
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranties of
15 MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
16 PURPOSE. See the GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License along
19 with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <X11/Xlib.h>
27 #include <gdk/gdkx.h>
28 #include <gio/gio.h>
29
30 #include <libindicator/indicator.h>
31 #include <libindicator/indicator-object.h>
32
33 #include <libdbusmenu-glib/menuitem.h>
34 #include <libdbusmenu-glib/client.h>
35
36 #include <libbamf/bamf-matcher.h>
37
38 #include "gen-application-menu-registrar.xml.h"
39 #include "gen-application-menu-renderer.xml.h"
40 #include "indicator-appmenu-marshal.h"
41 #include "window-menus.h"
42 #include "dbus-shared.h"
43 #include "gdk-get-func.h"
44
45 /**********************
46 Indicator Object
47 **********************/
48 #define INDICATOR_APPMENU_TYPE (indicator_appmenu_get_type ())
49 #define INDICATOR_APPMENU(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_APPMENU_TYPE, IndicatorAppmenu))
50 #define INDICATOR_APPMENU_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), INDICATOR_APPMENU_TYPE, IndicatorAppmenuClass))
51 #define IS_INDICATOR_APPMENU(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_APPMENU_TYPE))
52 #define IS_INDICATOR_APPMENU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), INDICATOR_APPMENU_TYPE))
53 #define INDICATOR_APPMENU_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), INDICATOR_APPMENU_TYPE, IndicatorAppmenuClass))
54
55 GType indicator_appmenu_get_type (void);
56
57 INDICATOR_SET_VERSION
58 INDICATOR_SET_TYPE(INDICATOR_APPMENU_TYPE)
59
60 typedef struct _IndicatorAppmenu IndicatorAppmenu;
61 typedef struct _IndicatorAppmenuClass IndicatorAppmenuClass;
62 typedef struct _IndicatorAppmenuDebug IndicatorAppmenuDebug;
63 typedef struct _IndicatorAppmenuDebugClass IndicatorAppmenuDebugClass;
64
65 typedef enum _ActiveStubsState ActiveStubsState;
66 enum _ActiveStubsState {
67 STUBS_UNKNOWN,
68 STUBS_SHOW,
69 STUBS_HIDE
70 };
71
72 struct _IndicatorAppmenuClass {
73 IndicatorObjectClass parent_class;
74
75 void (*window_registered) (IndicatorAppmenu * iapp, guint wid, gchar * address, gpointer path, gpointer user_data);
76 void (*window_unregistered) (IndicatorAppmenu * iapp, guint wid, gpointer user_data);
77 };
78
79 struct _IndicatorAppmenu {
80 IndicatorObject parent;
81
82 gulong retry_registration;
83
84 WindowMenus * default_app;
85 GHashTable * apps;
86
87 BamfMatcher * matcher;
88 BamfWindow * active_window;
89 ActiveStubsState active_stubs;
90
91 gulong sig_entry_added;
92 gulong sig_entry_removed;
93 gulong sig_status_changed;
94 gulong sig_show_menu;
95 gulong sig_a11y_update;
96
97 GtkMenuItem * close_item;
98
99 GArray * window_menus;
100
101 GHashTable * desktop_windows;
102 WindowMenus * desktop_menu;
103
104 GDBusConnection * bus;
105 guint owner_id;
106 guint dbus_registration;
107
108 GHashTable * destruction_timers;
109 };
110
111
112 /**********************
113 Debug Proxy
114 **********************/
115 #define INDICATOR_APPMENU_DEBUG_TYPE (indicator_appmenu_debug_get_type ())
116 #define INDICATOR_APPMENU_DEBUG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), INDICATOR_APPMENU_DEBUG_TYPE, IndicatorAppmenuDebug))
117 #define INDICATOR_APPMENU_DEBUG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), INDICATOR_APPMENU_DEBUG_TYPE, IndicatorAppmenuDebugClass))
118 #define IS_INDICATOR_APPMENU_DEBUG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), INDICATOR_APPMENU_DEBUG_TYPE))
119 #define IS_INDICATOR_APPMENU_DEBUG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), INDICATOR_APPMENU_DEBUG_TYPE))
120 #define INDICATOR_APPMENU_DEBUG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), INDICATOR_APPMENU_DEBUG_TYPE, IndicatorAppmenuDebugClass))
121
122 GType indicator_appmenu_debug_get_type (void);
123
124 struct _IndicatorAppmenuDebugClass {
125 GObjectClass parent_class;
126 };
127
128 struct _IndicatorAppmenuDebug {
129 GObject parent;
130 IndicatorAppmenu * appmenu;
131 GCancellable * bus_cancel;
132 GDBusConnection * bus;
133 guint dbus_registration;
134 };
135
136
137 /**********************
138 Prototypes
139 **********************/
140 static void indicator_appmenu_dispose (GObject *object);
141 static void indicator_appmenu_finalize (GObject *object);
142 static void build_window_menus (IndicatorAppmenu * iapp);
143 static GList * get_entries (IndicatorObject * io);
144 static guint get_location (IndicatorObject * io,
145 IndicatorObjectEntry * entry);
146 static void entry_activate (IndicatorObject * io,
147 IndicatorObjectEntry * entry,
148 guint timestamp);
149 static void entry_activate_window (IndicatorObject * io,
150 IndicatorObjectEntry * entry,
151 guint windowid,
152 guint timestamp);
153 static void switch_default_app (IndicatorAppmenu * iapp,
154 WindowMenus * newdef,
155 BamfWindow * active_window);
156 static void find_desktop_windows (IndicatorAppmenu * iapp);
157 static void new_window (BamfMatcher * matcher,
158 BamfView * view,
159 gpointer user_data);
160 static void old_window (BamfMatcher * matcher,
161 BamfView * view,
162 gpointer user_data);
163 static void window_entry_added (WindowMenus * mw,
164 IndicatorObjectEntry * entry,
165 gpointer user_data);
166 static void window_entry_removed (WindowMenus * mw,
167 IndicatorObjectEntry * entry,
168 gpointer user_data);
169 static void window_status_changed (WindowMenus * mw,
170 DbusmenuStatus status,
171 IndicatorAppmenu * iapp);
172 static void window_show_menu (WindowMenus * mw,
173 IndicatorObjectEntry * entry,
174 guint timestamp,
175 gpointer user_data);
176 static void window_a11y_update (WindowMenus * mw,
177 IndicatorObjectEntry * entry,
178 gpointer user_data);
179 static void active_window_changed (BamfMatcher * matcher,
180 BamfView * oldview,
181 BamfView * newview,
182 gpointer user_data);
183 static GQuark error_quark (void);
184 static gboolean retry_registration (gpointer user_data);
185 static void bus_method_call (GDBusConnection * connection,
186 const gchar * sender,
187 const gchar * path,
188 const gchar * interface,
189 const gchar * method,
190 GVariant * params,
191 GDBusMethodInvocation * invocation,
192 gpointer user_data);
193 static void on_bus_acquired (GDBusConnection * connection,
194 const gchar * name,
195 gpointer user_data);
196 static void on_name_acquired (GDBusConnection * connection,
197 const gchar * name,
198 gpointer user_data);
199 static void on_name_lost (GDBusConnection * connection,
200 const gchar * name,
201 gpointer user_data);
202 static void menus_destroyed (GObject * menus,
203 gpointer user_data);
204 static void source_unregister (gpointer user_data);
205 static GVariant * unregister_window (IndicatorAppmenu * iapp,
206 guint windowid);
207
208 /* Unique error codes for debug interface */
209 enum {
210 ERROR_NO_APPLICATIONS,
211 ERROR_NO_DEFAULT_APP,
212 ERROR_WINDOW_NOT_FOUND
213 };
214
215 /**********************
216 DBus Interfaces
217 **********************/
218 static GDBusNodeInfo * node_info = NULL;
219 static GDBusInterfaceInfo * interface_info = NULL;
220 static GDBusInterfaceVTable interface_table = {
221 method_call: bus_method_call,
222 get_property: NULL, /* No properties */
223 set_property: NULL /* No properties */
224 };
225
226 G_DEFINE_TYPE (IndicatorAppmenu, indicator_appmenu, INDICATOR_OBJECT_TYPE);
227
228 /* One time init */
229 static void
230 indicator_appmenu_class_init (IndicatorAppmenuClass *klass)
231 {
232 GObjectClass *object_class = G_OBJECT_CLASS (klass);
233
234 object_class->dispose = indicator_appmenu_dispose;
235 object_class->finalize = indicator_appmenu_finalize;
236
237 IndicatorObjectClass * ioclass = INDICATOR_OBJECT_CLASS(klass);
238
239 ioclass->get_entries = get_entries;
240 ioclass->get_location = get_location;
241 ioclass->entry_activate = entry_activate;
242 ioclass->entry_activate_window = entry_activate_window;
243
244 /* Setting up the DBus interfaces */
245 if (node_info == NULL) {
246 GError * error = NULL;
247
248 node_info = g_dbus_node_info_new_for_xml(_application_menu_registrar, &error);
249 if (error != NULL) {
250 g_critical("Unable to parse Application Menu Interface description: %s", error->message);
251 g_error_free(error);
252 }
253 }
254
255 if (interface_info == NULL) {
256 interface_info = g_dbus_node_info_lookup_interface(node_info, REG_IFACE);
257
258 if (interface_info == NULL) {
259 g_critical("Unable to find interface '" REG_IFACE "'");
260 }
261 }
262
263 return;
264 }
265
266 /* Per instance Init */
267 static void
268 indicator_appmenu_init (IndicatorAppmenu *self)
269 {
270 self->default_app = NULL;
271 self->apps = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_object_unref);
272 self->matcher = NULL;
273 self->active_window = NULL;
274 self->active_stubs = STUBS_UNKNOWN;
275 self->close_item = NULL;
276 self->retry_registration = 0;
277 self->bus = NULL;
278 self->owner_id = 0;
279 self->dbus_registration = 0;
280
281 /* Setup the entries for the fallbacks */
282 self->window_menus = g_array_sized_new(FALSE, FALSE, sizeof(IndicatorObjectEntry), 2);
283
284 /* Setup the cache of windows with possible desktop entries */
285 self->desktop_windows = g_hash_table_new(g_direct_hash, g_direct_equal);
286 self->desktop_menu = NULL; /* Starts NULL until found */
287
288 /* Set up the hashtable of destruction timers */
289 self->destruction_timers = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, source_unregister);
290
291 build_window_menus(self);
292
293 /* Get the default BAMF matcher */
294 self->matcher = bamf_matcher_get_default();
295 if (self->matcher == NULL) {
296 /* we don't want to exit out of Unity -- but this
297 should really never happen */
298 g_warning("Unable to get BAMF matcher, can not watch applications switch!");
299 } else {
300 g_signal_connect(G_OBJECT(self->matcher), "active-window-changed", G_CALLBACK(active_window_changed), self);
301
302 /* Desktop window tracking */
303 g_signal_connect(G_OBJECT(self->matcher), "view-opened", G_CALLBACK(new_window), self);
304 g_signal_connect(G_OBJECT(self->matcher), "view-closed", G_CALLBACK(old_window), self);
305 }
306
307 find_desktop_windows(self);
308
309 /* Request a name so others can find us */
310 retry_registration(self);
311
312 return;
313 }
314
315 /* If we weren't able to register on the bus, then we need
316 to try it all again. */
317 static gboolean
318 retry_registration (gpointer user_data)
319 {
320 g_return_val_if_fail(IS_INDICATOR_APPMENU(user_data), FALSE);
321 IndicatorAppmenu * iapp = INDICATOR_APPMENU(user_data);
322
323 iapp->owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
324 DBUS_NAME,
325 G_BUS_NAME_OWNER_FLAGS_NONE,
326 iapp->dbus_registration == 0 ? on_bus_acquired : NULL,
327 on_name_acquired,
328 on_name_lost,
329 g_object_ref(iapp),
330 g_object_unref);
331
332 return TRUE;
333 }
334
335 static void
336 on_bus_acquired (GDBusConnection * connection, const gchar * name,
337 gpointer user_data)
338 {
339 IndicatorAppmenu * iapp = INDICATOR_APPMENU(user_data);
340 GError * error = NULL;
341
342 iapp->bus = connection;
343
344 /* Now register our object on our new connection */
345 iapp->dbus_registration = g_dbus_connection_register_object(connection,
346 REG_OBJECT,
347 interface_info,
348 &interface_table,
349 user_data,
350 NULL,
351 &error);
352
353 if (error != NULL) {
354 g_critical("Unable to register the object to DBus: %s", error->message);
355 g_error_free(error);
356 g_bus_unown_name(iapp->owner_id);
357 iapp->owner_id = 0;
358 iapp->retry_registration = g_timeout_add_seconds(1, retry_registration, iapp);
359 return;
360 }
361
362 return;
363 }
364
365 static void
366 on_name_acquired (GDBusConnection * connection, const gchar * name,
367 gpointer user_data)
368 {
369 }
370
371 static void
372 on_name_lost (GDBusConnection * connection, const gchar * name,
373 gpointer user_data)
374 {
375 IndicatorAppmenu * iapp = INDICATOR_APPMENU(user_data);
376
377 if (connection == NULL) {
378 g_critical("OMG! Unable to get a connection to DBus");
379 }
380 else {
381 g_critical("Unable to claim the name %s", DBUS_NAME);
382 }
383
384 /* We can rest assured no one will register with us, but let's
385 just ensure we're not showing anything. */
386 switch_default_app(iapp, NULL, NULL);
387
388 iapp->owner_id = 0;
389 }
390
391 /* Object refs decrement */
392 static void
393 indicator_appmenu_dispose (GObject *object)
394 {
395 IndicatorAppmenu * iapp = INDICATOR_APPMENU(object);
396
397 /* Don't register if we're dying! */
398 if (iapp->retry_registration != 0) {
399 g_source_remove(iapp->retry_registration);
400 iapp->retry_registration = 0;
401 }
402
403 if (iapp->dbus_registration != 0) {
404 g_dbus_connection_unregister_object(iapp->bus, iapp->dbus_registration);
405 /* Don't care if it fails, there's nothing we can do */
406 iapp->dbus_registration = 0;
407 }
408
409 if (iapp->destruction_timers != NULL) {
410 /* These are in dispose and not finalize becuase the dereference
411 function removes timers that could need the object to be in
412 a valid state, so it's better to have them in dispose */
413 g_hash_table_destroy(iapp->destruction_timers);
414 iapp->destruction_timers = NULL;
415 }
416
417 if (iapp->bus != NULL) {
418 g_object_unref(iapp->bus);
419 iapp->bus = NULL;
420 }
421
422 if (iapp->owner_id != 0) {
423 g_bus_unown_name(iapp->owner_id);
424 iapp->owner_id = 0;
425 }
426
427 /* bring down the matcher before resetting to no menu so we don't
428 get match signals */
429 if (iapp->matcher != NULL) {
430 g_object_unref(iapp->matcher);
431 iapp->matcher = NULL;
432 }
433
434 /* No specific ref */
435 switch_default_app (iapp, NULL, NULL);
436
437 if (iapp->apps != NULL) {
438 g_hash_table_destroy(iapp->apps);
439 iapp->apps = NULL;
440 }
441
442 if (iapp->desktop_windows != NULL) {
443 g_hash_table_destroy(iapp->desktop_windows);
444 iapp->desktop_windows = NULL;
445 }
446
447 if (iapp->desktop_menu != NULL) {
448 /* Wait, nothing here? Yup. We're not referencing the
449 menus here they're already attached to the window ID.
450 We're just keeping an efficient pointer to them. */
451 iapp->desktop_menu = NULL;
452 }
453
454 G_OBJECT_CLASS (indicator_appmenu_parent_class)->dispose (object);
455 return;
456 }
457
458 /* Free memory */
459 static void
460 indicator_appmenu_finalize (GObject *object)
461 {
462 IndicatorAppmenu * iapp = INDICATOR_APPMENU(object);
463
464 if (iapp->window_menus != NULL) {
465 if (iapp->window_menus->len != 0) {
466 g_warning("Window menus weren't free'd in dispose!");
467 }
468 g_array_free(iapp->window_menus, TRUE);
469 iapp->window_menus = NULL;
470 }
471
472 G_OBJECT_CLASS (indicator_appmenu_parent_class)->finalize (object);
473 return;
474 }
475
476 static void
477 emit_signal (IndicatorAppmenu * iapp, const gchar * name, GVariant * variant)
478 {
479 GError * error = NULL;
480
481 g_dbus_connection_emit_signal (iapp->bus,
482 NULL,
483 REG_OBJECT,
484 REG_IFACE,
485 name,
486 variant,
487 &error);
488
489 if (error != NULL) {
490 g_critical("Unable to send %s signal: %s", name, error->message);
491 g_error_free(error);
492 return;
493 }
494
495 return;
496 }
497
498 /* Close the current application using magic */
499 static void
500 close_current (GtkMenuItem * mi, gpointer user_data)
501 {
502 IndicatorAppmenu * iapp = INDICATOR_APPMENU(user_data);
503
504 if (!BAMF_IS_WINDOW (iapp->active_window) || bamf_view_is_closed (BAMF_VIEW (iapp->active_window))) {
505 g_warning("Can't close a window we don't have. Window is either non-existent or recently closed.");
506 return;
507 }
508
509 guint32 xid = bamf_window_get_xid(iapp->active_window);
510 guint timestamp = gdk_event_get_time(NULL);
511
512 XEvent xev;
513
514 xev.xclient.type = ClientMessage;
515 xev.xclient.serial = 0;
516 xev.xclient.send_event = True;
517 xev.xclient.display = gdk_x11_get_default_xdisplay ();
518 xev.xclient.window = xid;
519 xev.xclient.message_type = gdk_x11_atom_to_xatom (gdk_atom_intern ("_NET_CLOSE_WINDOW", TRUE));
520 xev.xclient.format = 32;
521 xev.xclient.data.l[0] = timestamp;
522 xev.xclient.data.l[1] = 2; /* Client type pager, so it listens to us */
523 xev.xclient.data.l[2] = 0;
524 xev.xclient.data.l[3] = 0;
525 xev.xclient.data.l[4] = 0;
526
527 gdk_error_trap_push ();
528 XSendEvent (gdk_x11_get_default_xdisplay (),
529 gdk_x11_get_default_root_xwindow (),
530 False,
531 SubstructureRedirectMask | SubstructureNotifyMask,
532 &xev);
533 gdk_flush ();
534 #if GTK_CHECK_VERSION(3, 0, 0)
535 gdk_error_trap_pop_ignored ();
536 #else
537 gdk_error_trap_pop ();
538 #endif
539
540 return;
541 }
542
543 /* Create the default window menus */
544 static void
545 build_window_menus (IndicatorAppmenu * iapp)
546 {
547 IndicatorObjectEntry entries[1] = {{0}};
548 GtkAccelGroup * agroup = gtk_accel_group_new();
549 GtkMenuItem * mi = NULL;
550 GtkStockItem stockitem;
551
552 /* File Menu */
553 if (!gtk_stock_lookup(GTK_STOCK_FILE, &stockitem)) {
554 g_warning("Unable to find the file menu stock item");
555 stockitem.label = "_File";
556 }
557 entries[0].label = GTK_LABEL(gtk_label_new_with_mnemonic(stockitem.label));
558 g_object_ref(G_OBJECT(entries[0].label));
559 gtk_widget_show(GTK_WIDGET(entries[0].label));
560
561 entries[0].menu = GTK_MENU(gtk_menu_new());
562 g_object_ref(G_OBJECT(entries[0].menu));
563
564 mi = GTK_MENU_ITEM(gtk_image_menu_item_new_from_stock(GTK_STOCK_CLOSE, agroup));
565 gtk_widget_set_sensitive(GTK_WIDGET(mi), FALSE);
566 g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(close_current), iapp);
567 gtk_widget_show(GTK_WIDGET(mi));
568 gtk_menu_shell_append(GTK_MENU_SHELL(entries[0].menu), GTK_WIDGET(mi));
569 iapp->close_item = mi;
570
571 gtk_widget_show(GTK_WIDGET(entries[0].menu));
572
573 /* Copy the entries on the stack into the array */
574 g_array_insert_vals(iapp->window_menus, 0, entries, 1);
575
576 return;
577 }
578
579 /* Determine which windows should be used as the desktop
580 menus. */
581 static void
582 determine_new_desktop (IndicatorAppmenu * iapp)
583 {
584 GList * keys = g_hash_table_get_keys(iapp->desktop_windows);
585 GList * key;
586
587 for (key = keys; key != NULL; key = g_list_next(key)) {
588 guint xid = GPOINTER_TO_UINT(key->data);
589 gpointer pwm = g_hash_table_lookup(iapp->apps, GUINT_TO_POINTER(xid));
590 if (pwm != NULL) {
591 g_debug("Setting Desktop Menus to: %X", xid);
592 iapp->desktop_menu = WINDOW_MENUS(pwm);
593 }
594 }
595
596 g_list_free(keys);
597
598 return;
599 }
600
601 /* Puts all the desktop windows into the hash table so that we
602 can have a nice list of them. */
603 static void
604 find_desktop_windows (IndicatorAppmenu * iapp)
605 {
606 GList * windows = bamf_matcher_get_windows(iapp->matcher);
607 GList * lwindow;
608
609 for (lwindow = windows; lwindow != NULL; lwindow = g_list_next(lwindow)) {
610 BamfView * view = BAMF_VIEW(lwindow->data);
611 new_window(iapp->matcher, view, iapp);
612 }
613
614 g_list_free(windows);
615
616 return;
617 }
618
619 /* When new windows are born, we check to see if they're desktop
620 windows. */
621 static void
622 new_window (BamfMatcher * matcher, BamfView * view, gpointer user_data)
623 {
624 if (view == NULL || !BAMF_IS_WINDOW(view)) {
625 return;
626 }
627
628 BamfWindow * window = BAMF_WINDOW(view);
629 IndicatorAppmenu * iapp = INDICATOR_APPMENU(user_data);
630 guint32 xid = bamf_window_get_xid(window);
631
632 /* Make sure we don't destroy it later */
633 g_hash_table_remove(iapp->destruction_timers, GUINT_TO_POINTER(xid));
634
635 if (bamf_window_get_window_type(window) != BAMF_WINDOW_DESKTOP) {
636 return;
637 }
638
639 g_hash_table_insert(iapp->desktop_windows, GUINT_TO_POINTER(xid), GINT_TO_POINTER(TRUE));
640
641 g_debug("New Desktop Window: %X", xid);
642
643 gpointer pwm = g_hash_table_lookup(iapp->apps, GUINT_TO_POINTER(xid));
644 if (pwm != NULL) {
645 WindowMenus * wm = WINDOW_MENUS(pwm);
646 iapp->desktop_menu = wm;
647 g_debug("Setting Desktop Menus to: %X", xid);
648 if (iapp->active_window == NULL && iapp->default_app == NULL) {
649 switch_default_app(iapp, NULL, NULL);
650 }
651 }
652
653 return;
654 }
655
656 typedef struct _destroy_data_t destroy_data_t;
657 struct _destroy_data_t {
658 IndicatorAppmenu * iapp;
659 guint32 xid;
660 };
661
662 /* Timeout to finally cleanup the window. Causes is to ignore glitches that
663 come from BAMF/WNCK. */
664 static gboolean
665 destroy_window_timeout (gpointer user_data)
666 {
667 destroy_data_t * destroy_data = (destroy_data_t *)user_data;
668 g_hash_table_steal(destroy_data->iapp->destruction_timers, GUINT_TO_POINTER(destroy_data->xid));
669 unregister_window(destroy_data->iapp, destroy_data->xid);
670 return FALSE; /* free's data through source deregistration */
671 }
672
673 /* Unregisters the source in the hash table when it gets removed. This ensure
674 we don't leave any timeouts around */
675 static void
676 source_unregister (gpointer user_data)
677 {
678 g_source_remove(GPOINTER_TO_UINT(user_data));
679 return;
680 }
681
682 /* When windows leave us, this function gets called */
683 static void
684 old_window (BamfMatcher * matcher, BamfView * view, gpointer user_data)
685 {
686 if (view == NULL || !BAMF_IS_WINDOW(view)) {
687 return;
688 }
689
690 IndicatorAppmenu * iapp = INDICATOR_APPMENU(user_data);
691 BamfWindow * window = BAMF_WINDOW(view);
692 guint32 xid = bamf_window_get_xid(window);
693
694 destroy_data_t * destroy_data = g_new0(destroy_data_t, 1);
695 destroy_data->iapp = iapp;
696 destroy_data->xid = xid;
697
698 guint source_id = g_timeout_add_seconds_full(G_PRIORITY_LOW, 5, destroy_window_timeout, destroy_data, g_free);
699 g_hash_table_replace(iapp->destruction_timers, GUINT_TO_POINTER(xid), GUINT_TO_POINTER(source_id));
700
701 return;
702 }
703
704 /* List of desktop files that shouldn't have menu stubs. */
705 const static gchar * stubs_blacklist[] = {
706 /* Firefox */
707 "/usr/share/applications/firefox.desktop",
708 /* Thunderbird */
709 "/usr/share/applications/thunderbird.desktop",
710 /* Open Office */
711 "/usr/share/applications/openoffice.org-base.desktop",
712 "/usr/share/applications/openoffice.org-impress.desktop",
713 "/usr/share/applications/openoffice.org-calc.desktop",
714 "/usr/share/applications/openoffice.org-math.desktop",
715 "/usr/share/applications/openoffice.org-draw.desktop",
716 "/usr/share/applications/openoffice.org-writer.desktop",
717 /* Blender */
718 "/usr/share/applications/blender-fullscreen.desktop",
719 "/usr/share/applications/blender-windowed.desktop",
720 /* Eclipse */
721 "/usr/share/applications/eclipse.desktop",
722
723 NULL
724 };
725
726 /* Check with BAMF, and then check the blacklist of desktop files
727 to see if any are there. Otherwise, show the stubs. */
728 gboolean
729 show_menu_stubs (BamfApplication * app)
730 {
731 if (bamf_application_get_show_menu_stubs(app) == FALSE) {
732 return FALSE;
733 }
734
735 const gchar * desktop_file = bamf_application_get_desktop_file(app);
736 if (desktop_file == NULL || desktop_file[0] == '\0') {
737 return TRUE;
738 }
739
740 int i;
741 for (i = 0; stubs_blacklist[i] != NULL; i++) {
742 if (g_strcmp0(stubs_blacklist[i], desktop_file) == 0) {
743 return FALSE;
744 }
745 }
746
747 return TRUE;
748 }
749
750 /* Get the current set of entries */
751 static GList *
752 get_entries (IndicatorObject * io)
753 {
754 g_return_val_if_fail(IS_INDICATOR_APPMENU(io), NULL);
755 IndicatorAppmenu * iapp = INDICATOR_APPMENU(io);
756
757 /* If we have a focused app with menus, use it's windows */
758 if (iapp->default_app != NULL) {
759 return window_menus_get_entries(iapp->default_app);
760 }
761
762 /* Else, let's go with desktop windows if there isn't a focused window */
763 if (iapp->active_window == NULL) {
764 if (iapp->desktop_menu == NULL) {
765 return NULL;
766 } else {
767 return window_menus_get_entries(iapp->desktop_menu);
768 }
769 }
770
771 /* Oh, now we're looking at stubs. */
772
773 if (iapp->active_stubs == STUBS_UNKNOWN) {
774 iapp->active_stubs = STUBS_SHOW;
775
776 BamfApplication * app = bamf_matcher_get_application_for_window(iapp->matcher, iapp->active_window);
777 if (app != NULL) {
778 /* First check to see if we can find an app, then if we can
779 check to see if it has an opinion on whether we should
780 show the stubs or not. */
781 if (show_menu_stubs(app) == FALSE) {
782 /* If it blocks them, fall out. */
783 iapp->active_stubs = STUBS_HIDE;
784 }
785 }
786 }
787
788 if (iapp->active_stubs == STUBS_HIDE) {
789 return NULL;
790 }
791
792 if (indicator_object_check_environment(INDICATOR_OBJECT(iapp), "unity")) {
793 return NULL;
794 }
795
796 GList * output = NULL;
797 int i;
798
799 /* There is only one item in window_menus now, but there
800 was more, and there is likely to be more in the future
801 so we're leaving this here to avoid a possible bug. */
802 for (i = 0; i < iapp->window_menus->len; i++) {
803 output = g_list_append(output, &g_array_index(iapp->window_menus, IndicatorObjectEntry, i));
804 }
805
806 return output;
807 }
808
809 /* Grabs the location of the entry */
810 static guint
811 get_location (IndicatorObject * io, IndicatorObjectEntry * entry)
812 {
813 guint count = 0;
814 IndicatorAppmenu * iapp = INDICATOR_APPMENU(io);
815 if (iapp->default_app != NULL) {
816 /* Find the location in the app */
817 count = window_menus_get_location(iapp->default_app, entry);
818 } else if (iapp->active_window != NULL) {
819 /* Find the location in the window menus */
820 for (count = 0; count < iapp->window_menus->len; count++) {
821 if (entry == &g_array_index(iapp->window_menus, IndicatorObjectEntry, count)) {
822 break;
823 }
824 }
825 if (count == iapp->window_menus->len) {
826 g_warning("Unable to find entry in default window menus");
827 count = 0;
828 }
829 } else {
830 /* Find the location in the desktop menu */
831 if (iapp->desktop_menu != NULL) {
832 count = window_menus_get_location(iapp->desktop_menu, entry);
833 }
834 }
835 return count;
836 }
837
838 /* Responds to a menuitem being activated on the panel. */
839 static void
840 entry_activate (IndicatorObject * io, IndicatorObjectEntry * entry, guint timestamp)
841 {
842 return entry_activate_window(io, entry, 0, timestamp);
843 }
844
845 /* Responds to a menuitem being activated on the panel. */
846 static void
847 entry_activate_window (IndicatorObject * io, IndicatorObjectEntry * entry, guint windowid, guint timestamp)
848 {
849 IndicatorAppmenu * iapp = INDICATOR_APPMENU(io);
850
851 /* We need to force a focus change in this case as we probably
852 just haven't gotten the signal from BAMF yet */
853 if (windowid != 0) {
854 GList * windows = bamf_matcher_get_windows(iapp->matcher);
855 GList * window;
856 BamfView * newwindow = NULL;
857
858 for (window = windows; window != NULL; window = g_list_next(window)) {
859 if (!BAMF_IS_WINDOW(window->data)) {
860 continue;
861 }
862
863 BamfWindow * testwindow = BAMF_WINDOW(window->data);
864
865 if (windowid == bamf_window_get_xid(testwindow)) {
866 newwindow = BAMF_VIEW(testwindow);
867 break;
868 }
869 }
870 g_list_free(windows);
871
872 if (newwindow != NULL) {
873 active_window_changed(iapp->matcher, BAMF_VIEW(iapp->active_window), newwindow, iapp);
874 }
875 }
876
877 if (iapp->default_app != NULL) {
878 window_menus_entry_activate(iapp->default_app, entry, timestamp);
879 return;
880 }
881
882 if (iapp->active_window == NULL) {
883 if (iapp->desktop_menu != NULL) {
884 window_menus_entry_activate(iapp->desktop_menu, entry, timestamp);
885 }
886 return;
887 }
888
889 /* Else we've got stubs, and the stubs don't care. */
890
891 return;
892 }
893
894 /* Checks to see we cared about a window that's going
895 away, so that we can deal with that */
896 static void
897 window_finalized_is_active (gpointer user_data, GObject * old_window)
898 {
899 g_return_if_fail(IS_INDICATOR_APPMENU(user_data));
900 IndicatorAppmenu * iapp = INDICATOR_APPMENU(user_data);
901
902 /* Pointer comparison as we can't really trust any of the
903 pointers to do any dereferencing */
904 if ((gpointer)iapp->active_window != (gpointer)old_window) {
905 /* Ah, no issue, we weren't caring about this one
906 anyway. */
907 return;
908 }
909
910 /* We're going to a state where we don't know what the active
911 window is, hopefully BAMF will save us */
912 active_window_changed (iapp->matcher, NULL, NULL, iapp);
913
914 return;
915 }
916
917 /* A helper for switch_default_app that takes care of the
918 switching of the active window variable */
919 static void
920 switch_active_window (IndicatorAppmenu * iapp, BamfWindow * active_window)
921 {
922 if (iapp->active_window == active_window) {
923 return;
924 }
925
926 if (iapp->active_window != NULL) {
927 g_object_weak_unref(G_OBJECT(iapp->active_window), window_finalized_is_active, iapp);
928 }
929
930 iapp->active_window = active_window;
931 iapp->active_stubs = STUBS_UNKNOWN;
932
933 /* Close any existing open menu by showing a null entry */
934 window_show_menu(iapp->default_app, NULL, gtk_get_current_event_time(), iapp);
935
936 if (active_window != NULL) {
937 g_object_weak_ref(G_OBJECT(active_window), window_finalized_is_active, iapp);
938 }
939
940 if (iapp->close_item == NULL) {
941 g_warning("No close item!?!?!");
942 return;
943 }
944
945 gtk_widget_set_sensitive(GTK_WIDGET(iapp->close_item), FALSE);
946
947 if (iapp->active_window == NULL) {
948 return;
949 }
950
951 guint32 xid = bamf_window_get_xid(iapp->active_window);
952 if (xid == 0 || bamf_view_is_closed (BAMF_VIEW (iapp->active_window))) {
953 return;
954 }
955
956 GdkWMFunction functions;
957 if (!egg_xid_get_functions(xid, &functions)) {
958 g_debug("Unable to get MWM functions for: %d", xid);
959 functions = GDK_FUNC_ALL;
960 }
961
962 if (functions & GDK_FUNC_ALL || functions & GDK_FUNC_CLOSE) {
963 gtk_widget_set_sensitive(GTK_WIDGET(iapp->close_item), TRUE);
964 }
965
966 return;
967 }
968
969 /* Switch applications, remove all the entires for the previous
970 one and add them for the new application */
971 static void
972 switch_default_app (IndicatorAppmenu * iapp, WindowMenus * newdef, BamfWindow * active_window)
973 {
974 if (iapp->default_app == newdef && iapp->default_app != NULL) {
975 /* We've got an app with menus and it hasn't changed. */
976
977 /* Keep active window up-to-date, though we're probably not
978 using it much. */
979 switch_active_window(iapp, active_window);
980 return;
981 }
982
983 if (iapp->default_app == NULL && iapp->active_window == active_window && newdef == NULL) {
984 /* There's no application menus, but the active window hasn't
985 changed. So there's no change. */
986 return;
987 }
988
989 /* hide the entries that we're swapping out */
990 indicator_object_set_visible (INDICATOR_OBJECT(iapp), FALSE);
991
992 /* Disconnect signals */
993 if (iapp->sig_entry_added != 0) {
994 g_signal_handler_disconnect(G_OBJECT(iapp->default_app), iapp->sig_entry_added);
995 iapp->sig_entry_added = 0;
996 }
997 if (iapp->sig_entry_removed != 0) {
998 g_signal_handler_disconnect(G_OBJECT(iapp->default_app), iapp->sig_entry_removed);
999 iapp->sig_entry_removed = 0;
1000 }
1001 if (iapp->sig_status_changed != 0) {
1002 g_signal_handler_disconnect(G_OBJECT(iapp->default_app), iapp->sig_status_changed);
1003 iapp->sig_status_changed = 0;
1004 }
1005 if (iapp->sig_show_menu != 0) {
1006 g_signal_handler_disconnect(G_OBJECT(iapp->default_app), iapp->sig_show_menu);
1007 iapp->sig_show_menu = 0;
1008 }
1009 if (iapp->sig_a11y_update != 0) {
1010 g_signal_handler_disconnect(G_OBJECT(iapp->default_app), iapp->sig_a11y_update);
1011 iapp->sig_a11y_update = 0;
1012 }
1013
1014 /* Default App is NULL, let's see if it needs replacement */
1015 iapp->default_app = NULL;
1016
1017 /* Update the active window pointer -- may be NULL */
1018 switch_active_window(iapp, active_window);
1019
1020 /* If we're putting up a new window, let's do that now. */
1021 if (newdef != NULL) {
1022 /* Switch */
1023 iapp->default_app = newdef;
1024
1025 /* Connect signals */
1026 iapp->sig_entry_added = g_signal_connect(G_OBJECT(iapp->default_app),
1027 WINDOW_MENUS_SIGNAL_ENTRY_ADDED,
1028 G_CALLBACK(window_entry_added),
1029 iapp);
1030 iapp->sig_entry_removed = g_signal_connect(G_OBJECT(iapp->default_app),
1031 WINDOW_MENUS_SIGNAL_ENTRY_REMOVED,
1032 G_CALLBACK(window_entry_removed),
1033 iapp);
1034 iapp->sig_status_changed = g_signal_connect(G_OBJECT(iapp->default_app),
1035 WINDOW_MENUS_SIGNAL_STATUS_CHANGED,
1036 G_CALLBACK(window_status_changed),
1037 iapp);
1038 iapp->sig_show_menu = g_signal_connect(G_OBJECT(iapp->default_app),
1039 WINDOW_MENUS_SIGNAL_SHOW_MENU,
1040 G_CALLBACK(window_show_menu),
1041 iapp);
1042 iapp->sig_a11y_update = g_signal_connect(G_OBJECT(iapp->default_app),
1043 WINDOW_MENUS_SIGNAL_A11Y_UPDATE,
1044 G_CALLBACK(window_a11y_update),
1045 iapp);
1046 }
1047
1048 /* show the entries that we're swapping in */
1049 indicator_object_set_visible (INDICATOR_OBJECT(iapp), TRUE);
1050
1051 /* Set up initial state for new entries if needed */
1052 if (iapp->default_app != NULL &&
1053 window_menus_get_status (iapp->default_app) != DBUSMENU_STATUS_NORMAL) {
1054 window_status_changed (iapp->default_app,
1055 window_menus_get_status (iapp->default_app),
1056 iapp);
1057 }
1058
1059 return;
1060 }
1061
1062 /* Recieve the signal that the window being shown
1063 has now changed. */
1064 static void
1065 active_window_changed (BamfMatcher * matcher, BamfView * oldview, BamfView * newview, gpointer user_data)
1066 {
1067 BamfWindow * window = NULL;
1068
1069 if (newview != NULL) {
1070 window = BAMF_WINDOW(newview);
1071 if (window == NULL) {
1072 g_warning("Active window changed to View thats not a window.");
1073 }
1074 } else {
1075 g_debug("Active window is: NULL");
1076 }
1077
1078 IndicatorAppmenu * appmenu = INDICATOR_APPMENU(user_data);
1079
1080 if (window != NULL && bamf_window_get_window_type(window) == BAMF_WINDOW_DESKTOP) {
1081 g_debug("Switching to menus from desktop");
1082 switch_default_app(appmenu, NULL, NULL);
1083 return;
1084 }
1085
1086 WindowMenus * menus = NULL;
1087 guint32 xid = 0;
1088
1089 while (window != NULL && menus == NULL) {
1090 xid = bamf_window_get_xid(window);
1091
1092 menus = g_hash_table_lookup(appmenu->apps, GUINT_TO_POINTER(xid));
1093
1094 if (menus == NULL) {
1095 g_debug("Looking for parent window on XID %d", xid);
1096 window = bamf_window_get_transient(window);
1097 }
1098 }
1099
1100 /* Note: We're not using window here, but re-casting the
1101 newwindow variable. Which means we stay where we were
1102 but get the menus from parents. */
1103 g_debug("Switching to menus from XID %d", xid);
1104 if (newview != NULL) {
1105 switch_default_app(appmenu, menus, BAMF_WINDOW(newview));
1106 } else {
1107 switch_default_app(appmenu, menus, NULL);
1108 }
1109
1110 return;
1111 }
1112
1113 /* Respond to the menus being destroyed. We need to deregister
1114 and make sure we weren't being shown. */
1115 static void
1116 menus_destroyed (GObject * menus, gpointer user_data)
1117 {
1118 gboolean reload_menus = FALSE;
1119 WindowMenus * wm = WINDOW_MENUS(menus);
1120 IndicatorAppmenu * iapp = INDICATOR_APPMENU(user_data);
1121
1122 guint32 xid = window_menus_get_xid(wm);
1123 g_return_if_fail(xid != 0);
1124
1125 g_hash_table_steal(iapp->apps, GUINT_TO_POINTER(xid));
1126
1127 g_debug("Removing menus for %d", xid);
1128
1129 if (iapp->desktop_menu == wm) {
1130 iapp->desktop_menu = NULL;
1131 determine_new_desktop(iapp);
1132 if (iapp->default_app == NULL && iapp->active_window == NULL) {
1133 reload_menus = TRUE;
1134 }
1135 }
1136
1137 /* If we're it, let's remove ourselves and BAMF will probably
1138 give us a new entry in a bit. */
1139 if (iapp->default_app == wm) {
1140 reload_menus = TRUE;
1141 }
1142
1143 if (reload_menus) {
1144 switch_default_app(iapp, NULL, NULL);
1145 }
1146
1147 return;
1148 }
1149
1150 /* A new window wishes to register it's windows with us */
1151 static GVariant *
1152 register_window (IndicatorAppmenu * iapp, guint windowid, const gchar * objectpath,
1153 const gchar * sender)
1154 {
1155 g_debug("Registering window ID %d with path %s from %s", windowid, objectpath, sender);
1156
1157 /* Shouldn't do anything, but let's be sure */
1158 g_hash_table_remove(iapp->destruction_timers, GUINT_TO_POINTER(windowid));
1159
1160 if (g_hash_table_lookup(iapp->apps, GUINT_TO_POINTER(windowid)) == NULL && windowid != 0) {
1161 WindowMenus * wm = window_menus_new(windowid, sender, objectpath);
1162 g_return_val_if_fail(wm != NULL, FALSE);
1163
1164 g_hash_table_insert(iapp->apps, GUINT_TO_POINTER(windowid), wm);
1165
1166 emit_signal(iapp, "WindowRegistered",
1167 g_variant_new("(uso)", windowid, sender, objectpath));
1168
1169 gpointer pdesktop = g_hash_table_lookup(iapp->desktop_windows, GUINT_TO_POINTER(windowid));
1170 if (pdesktop != NULL) {
1171 determine_new_desktop(iapp);
1172 }
1173
1174 /* Note: Does not cause ref */
1175 BamfWindow * win = bamf_matcher_get_active_window(iapp->matcher);
1176
1177 active_window_changed(iapp->matcher, NULL, BAMF_VIEW(win), iapp);
1178 } else {
1179 if (windowid == 0) {
1180 g_warning("Can't build windows for a NULL window ID %d with path %s from %s", windowid, objectpath, sender);
1181 } else {
1182 g_warning("Already have a menu for window ID %d with path %s from %s, unregistering that one", windowid, objectpath, sender);
1183 unregister_window(iapp, windowid);
1184
1185 /* NOTE: So we're doing a lookup here. That seems pretty useless
1186 now doesn't it. It's for a good reason. We're going recursive
1187 with a pretty complex set of functions we want to ensure that
1188 we're not going to end up infinitely recursive otherwise things
1189 could go really bad. */
1190 if (g_hash_table_lookup(iapp->apps, GUINT_TO_POINTER(windowid)) == NULL) {
1191 return register_window(iapp, windowid, objectpath, sender);
1192 }
1193
1194 g_warning("Unable to unregister window!");
1195 }
1196 }
1197
1198 return g_variant_new("()");
1199 }
1200
1201 /* Kindly remove an entry from our DB */
1202 static GVariant *
1203 unregister_window (IndicatorAppmenu * iapp, guint windowid)
1204 {
1205 g_debug("Unregistering: %d", windowid);
1206 g_return_val_if_fail(IS_INDICATOR_APPMENU(iapp), NULL);
1207 g_return_val_if_fail(iapp->matcher != NULL, NULL);
1208
1209 /* Make sure we don't destroy it later */
1210 g_hash_table_remove(iapp->destruction_timers, GUINT_TO_POINTER(windowid));
1211
1212 /* If it's a desktop window remove it from that table as well */
1213 g_hash_table_remove(iapp->desktop_windows, GUINT_TO_POINTER(windowid));
1214
1215 /* Now let's see if we've got a WM object for it then
1216 we need to mark it as destroyed and unreference to
1217 actually destroy it. */
1218 gpointer wm = g_hash_table_lookup(iapp->apps, GUINT_TO_POINTER(windowid));
1219 if (wm != NULL) {
1220 GObject * wmo = G_OBJECT(wm);
1221
1222 /* Using destroyed so that if the menus are shown
1223 they'll be switch and the current window gets
1224 updated as well. */
1225 menus_destroyed(wmo, iapp);
1226 g_object_unref(wmo);
1227 }
1228
1229 return NULL;
1230 }
1231
1232 /* Grab the menu information for a specific window */
1233 static GVariant *
1234 get_menu_for_window (IndicatorAppmenu * iapp, guint windowid, GError ** error)
1235 {
1236 WindowMenus * wm = NULL;
1237
1238 if (windowid == 0) {
1239 wm = iapp->default_app;
1240 } else {
1241 wm = WINDOW_MENUS(g_hash_table_lookup(iapp->apps, GUINT_TO_POINTER(windowid)));
1242 }
1243
1244 if (wm == NULL) {
1245 g_set_error_literal(error, error_quark(), ERROR_WINDOW_NOT_FOUND, "Window not found");
1246 return NULL;
1247 }
1248
1249 return g_variant_new("(so)", window_menus_get_address(wm),
1250 window_menus_get_path(wm));
1251 }
1252
1253 /* Get all the menus we have */
1254 static GVariant *
1255 get_menus (IndicatorAppmenu * iapp, GError ** error)
1256 {
1257 if (iapp->apps == NULL) {
1258 g_set_error_literal(error, error_quark(), ERROR_NO_APPLICATIONS, "No applications are registered");
1259 return NULL;
1260 }
1261
1262 GVariantBuilder builder;
1263 GHashTableIter hash_iter;
1264 gpointer value;
1265
1266 g_variant_builder_init (&builder, G_VARIANT_TYPE("a(uso)"));
1267 g_hash_table_iter_init (&hash_iter, iapp->apps);
1268 while (g_hash_table_iter_next (&hash_iter, NULL, &value)) {
1269 if (value != NULL) {
1270 WindowMenus * wm = WINDOW_MENUS(value);
1271 g_variant_builder_add (&builder, "(uso)",
1272 window_menus_get_xid(wm),
1273 window_menus_get_address(wm),
1274 window_menus_get_path(wm));
1275 }
1276 }
1277
1278 return g_variant_new ("(a(uso))", &builder);
1279 }
1280
1281 /* A method has been called from our dbus inteface. Figure out what it
1282 is and dispatch it. */
1283 static void
1284 bus_method_call (GDBusConnection * connection, const gchar * sender,
1285 const gchar * path, const gchar * interface,
1286 const gchar * method, GVariant * params,
1287 GDBusMethodInvocation * invocation, gpointer user_data)
1288 {
1289 IndicatorAppmenu * iapp = INDICATOR_APPMENU(user_data);
1290 GVariant * retval = NULL;
1291 GError * error = NULL;
1292
1293 if (g_strcmp0(method, "RegisterWindow") == 0) {
1294 guint32 xid;
CID 10732 - PW.PARAMETER_HIDDEN
declaration hides parameter "path" (declared at line 1285)
1295 const gchar * path;
1296 g_variant_get(params, "(u&o)", &xid, &path);
1297 retval = register_window(iapp, xid, path, sender);
1298 } else if (g_strcmp0(method, "UnregisterWindow") == 0) {
1299 guint32 xid;
1300 g_variant_get(params, "(u)", &xid);
1301 retval = unregister_window(iapp, xid);
1302 } else if (g_strcmp0(method, "GetMenuForWindow") == 0) {
1303 guint32 xid;
1304 g_variant_get(params, "(u)", &xid);
1305 retval = get_menu_for_window(iapp, xid, &error);
1306 } else if (g_strcmp0(method, "GetMenus") == 0) {
1307 retval = get_menus(iapp, &error);
1308 } else {
1309 g_warning("Calling method '%s' on the indicator service and it's unknown", method);
1310 }
1311
1312 if (error != NULL) {
1313 g_dbus_method_invocation_return_dbus_error(invocation,
1314 "com.canonical.AppMenu.Error",
1315 error->message);
1316 g_error_free(error);
1317 } else {
1318 g_dbus_method_invocation_return_value(invocation, retval);
1319 }
1320 return;
1321 }
1322
1323 /* Pass up the entry added event */
1324 static void
1325 window_entry_added (WindowMenus * mw, IndicatorObjectEntry * entry, gpointer user_data)
1326 {
1327 g_signal_emit_by_name(G_OBJECT(user_data), INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED, entry);
1328 return;
1329 }
1330
1331 /* Pass up the entry removed event */
1332 static void
1333 window_entry_removed (WindowMenus * mw, IndicatorObjectEntry * entry, gpointer user_data)
1334 {
1335 g_signal_emit_by_name(G_OBJECT(user_data), INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED, entry);
1336 return;
1337 }
1338
1339 /* Pass up the status changed event */
1340 static void
1341 window_status_changed (WindowMenus * mw, DbusmenuStatus status, IndicatorAppmenu * iapp)
1342 {
1343 gboolean show_now = (status == DBUSMENU_STATUS_NOTICE);
1344 GList * entry_head, * entries;
1345
1346 entry_head = indicator_object_get_entries(INDICATOR_OBJECT(iapp));
1347
1348 for (entries = entry_head; entries != NULL; entries = g_list_next(entries)) {
1349 IndicatorObjectEntry * entry = (IndicatorObjectEntry *)entries->data;
1350 g_signal_emit(G_OBJECT(iapp), INDICATOR_OBJECT_SIGNAL_SHOW_NOW_CHANGED_ID, 0, entry, show_now);
1351 }
1352
1353 return;
1354 }
1355
1356 /* Pass up the show menu event */
1357 static void
1358 window_show_menu (WindowMenus * mw, IndicatorObjectEntry * entry, guint timestamp, gpointer user_data)
1359 {
1360 g_signal_emit_by_name(G_OBJECT(user_data), INDICATOR_OBJECT_SIGNAL_MENU_SHOW, entry, timestamp);
1361 return;
1362 }
1363
1364 /* Pass up the accessible string update */
1365 static void
1366 window_a11y_update (WindowMenus * mw, IndicatorObjectEntry * entry, gpointer user_data)
1367 {
1368 g_signal_emit_by_name(G_OBJECT(user_data), INDICATOR_OBJECT_SIGNAL_ACCESSIBLE_DESC_UPDATE, entry);
1369 return;
1370 }
1371
1372 /**********************
1373 DEBUG INTERFACE
1374 **********************/
1375
1376 /* Builds the error quark if we need it, otherwise just
1377 returns the same value */
1378 static GQuark
1379 error_quark (void)
1380 {
1381 static GQuark error_quark = 0;
1382
1383 if (error_quark == 0) {
1384 error_quark = g_quark_from_static_string("indicator-appmenu");
1385 }
1386
1387 return error_quark;
1388 }