7
7
Ted Gould <ted@canonical.com>
8
Marco Trevisan <marco@canonical.com>
9
10
This program is free software: you can redistribute it and/or modify it
10
11
under the terms of the GNU General Public License version 3, as published
77
typedef enum _AppmenuMode AppmenuMode;
76
84
struct _IndicatorAppmenuClass {
77
85
IndicatorObjectClass parent_class;
79
void (*window_registered) (IndicatorAppmenu * iapp, guint wid, gchar * address, gpointer path, gpointer user_data);
80
void (*window_unregistered) (IndicatorAppmenu * iapp, guint wid, gpointer user_data);
83
88
struct _IndicatorAppmenu {
84
89
IndicatorObject parent;
86
93
WindowMenu * default_app;
90
97
BamfWindow * active_window;
91
98
ActiveStubsState active_stubs;
93
gulong sig_entry_added;
94
gulong sig_entry_removed;
95
gulong sig_status_changed;
97
gulong sig_a11y_update;
99
100
GtkMenuItem * close_item;
101
101
GArray * window_menus;
103
103
GHashTable * desktop_windows;
137
137
/**********************
139
139
**********************/
140
static gboolean indicator_appmenu_delayed_init (IndicatorAppmenu * iapp);
140
141
static void indicator_appmenu_dispose (GObject *object);
141
142
static void indicator_appmenu_finalize (GObject *object);
142
143
static void build_window_menus (IndicatorAppmenu * iapp);
153
154
static void switch_default_app (IndicatorAppmenu * iapp,
154
155
WindowMenu * newdef,
155
156
BamfWindow * active_window);
156
static void find_desktop_windows (IndicatorAppmenu * iapp);
157
static void find_relevant_windows (IndicatorAppmenu * iapp);
157
158
static void new_window (BamfMatcher * matcher,
159
160
gpointer user_data);
195
196
static void on_name_lost (GDBusConnection * connection,
196
197
const gchar * name,
197
198
gpointer user_data);
199
static WindowMenu * ensure_menus (IndicatorAppmenu * iapp,
200
BamfWindow * window);
198
201
static GVariant * unregister_window (IndicatorAppmenu * iapp,
203
static void connect_to_menu_signals (IndicatorAppmenu * iapp,
201
206
/* Unique error codes for debug interface */
261
266
indicator_appmenu_init (IndicatorAppmenu *self)
263
self->default_app = NULL;
264
268
self->apps = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_object_unref);
265
self->matcher = NULL;
266
self->active_window = NULL;
269
self->mode = MODE_STANDARD;
267
270
self->active_stubs = STUBS_UNKNOWN;
268
self->close_item = NULL;
271
self->dbus_registration = 0;
273
/* Setup the entries for the fallbacks */
274
self->window_menus = g_array_sized_new(FALSE, FALSE, sizeof(IndicatorObjectEntry), 2);
276
272
/* Setup the cache of windows with possible desktop entries */
277
273
self->desktop_windows = g_hash_table_new(g_direct_hash, g_direct_equal);
278
self->desktop_menu = NULL; /* Starts NULL until found */
280
build_window_menus(self);
275
g_idle_add((GSourceFunc) indicator_appmenu_delayed_init, self);
278
/* Delayed Init, this is done so it can happen after that the mode has been set */
280
indicator_appmenu_delayed_init (IndicatorAppmenu *self)
282
if (indicator_object_check_environment(INDICATOR_OBJECT(self), "unity-all-menus")) {
283
self->mode = MODE_UNITY_ALL_MENUS;
284
} else if (indicator_object_check_environment(INDICATOR_OBJECT(self), "unity")) {
285
self->mode = MODE_UNITY;
288
if (self->mode != MODE_STANDARD)
289
self->active_stubs = STUBS_HIDE;
291
if (self->active_stubs != STUBS_HIDE)
292
build_window_menus(self);
282
294
/* Get the default BAMF matcher */
283
295
self->matcher = bamf_matcher_get_default();
293
305
g_signal_connect(G_OBJECT(self->matcher), "view-closed", G_CALLBACK(old_window), self);
296
find_desktop_windows(self);
308
find_relevant_windows(self);
298
310
/* Request a name so others can find us */
299
311
self->owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
365
379
iapp->dbus_registration = 0;
368
if (iapp->bus != NULL) {
369
g_object_unref(iapp->bus);
382
g_clear_object(&iapp->bus);
373
384
if (iapp->owner_id != 0) {
374
385
g_bus_unown_name(iapp->owner_id);
378
389
/* bring down the matcher before resetting to no menu so we don't
379
390
get match signals */
380
if (iapp->matcher != NULL) {
381
g_object_unref(iapp->matcher);
382
iapp->matcher = NULL;
391
g_clear_object(&iapp->matcher);
385
393
/* No specific ref */
386
switch_default_app (iapp, NULL, NULL);
388
if (iapp->apps != NULL) {
389
g_hash_table_destroy(iapp->apps);
393
if (iapp->desktop_windows != NULL) {
394
g_hash_table_destroy(iapp->desktop_windows);
395
iapp->desktop_windows = NULL;
394
switch_default_app(iapp, NULL, NULL);
396
g_clear_pointer(&iapp->apps, g_hash_table_destroy);
397
g_clear_pointer(&iapp->desktop_windows, g_hash_table_destroy);
398
399
if (iapp->desktop_menu != NULL) {
399
400
/* Wait, nothing here? Yup. We're not referencing the
413
414
IndicatorAppmenu * iapp = INDICATOR_APPMENU(object);
415
416
if (iapp->window_menus != NULL) {
416
if (iapp->window_menus->len != 0) {
417
g_warning("Window menus weren't free'd in dispose!");
417
g_signal_handlers_disconnect_by_data(iapp->close_item, iapp);
420
for (i = 0; i < iapp->window_menus->len; ++i) {
421
IndicatorObjectEntry *entry = &g_array_index(iapp->window_menus, IndicatorObjectEntry, i);
422
g_clear_object(&(entry->label));
423
g_clear_object(&(entry->menu));
419
425
g_array_free(iapp->window_menus, TRUE);
420
iapp->window_menus = NULL;
428
g_signal_handlers_disconnect_by_data(iapp->matcher, iapp);
423
430
G_OBJECT_CLASS (indicator_appmenu_parent_class)->finalize (object);
496
503
GtkMenuItem * mi = NULL;
497
504
GtkStockItem stockitem;
506
/* Setup the entries for the fallbacks */
507
iapp->window_menus = g_array_sized_new(FALSE, FALSE, sizeof(IndicatorObjectEntry), 2);
509
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
500
511
if (!gtk_stock_lookup(GTK_STOCK_FILE, &stockitem)) {
501
512
g_warning("Unable to find the file menu stock item");
502
513
stockitem.label = "_File";
515
G_GNUC_END_IGNORE_DEPRECATIONS
504
517
entries[0].label = GTK_LABEL(gtk_label_new_with_mnemonic(stockitem.label));
505
518
g_object_ref(G_OBJECT(entries[0].label));
506
519
gtk_widget_show(GTK_WIDGET(entries[0].label));
508
521
entries[0].menu = GTK_MENU(gtk_menu_new());
509
522
g_object_ref(G_OBJECT(entries[0].menu));
524
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
511
525
mi = GTK_MENU_ITEM(gtk_image_menu_item_new_from_stock(GTK_STOCK_CLOSE, agroup));
526
G_GNUC_END_IGNORE_DEPRECATIONS
512
528
gtk_widget_set_sensitive(GTK_WIDGET(mi), FALSE);
513
529
g_signal_connect(G_OBJECT(mi), "activate", G_CALLBACK(close_current), iapp);
514
530
gtk_widget_show(GTK_WIDGET(mi));
520
536
/* Copy the entries on the stack into the array */
521
537
g_array_insert_vals(iapp->window_menus, 0, entries, 1);
526
540
/* Determine which windows should be used as the desktop
548
/* Puts all the desktop windows into the hash table so that we
563
/* Puts all the windows we care about into the hash table so that we
549
564
can have a nice list of them. */
551
find_desktop_windows (IndicatorAppmenu * iapp)
566
find_relevant_windows (IndicatorAppmenu * iapp)
553
568
GList * windows = bamf_matcher_get_windows(iapp->matcher);
576
591
IndicatorAppmenu * iapp = INDICATOR_APPMENU(user_data);
577
592
guint32 xid = bamf_window_get_xid(window);
594
if (iapp->mode == MODE_UNITY_ALL_MENUS) {
595
ensure_menus(iapp, window);
579
599
if (bamf_window_get_window_type(window) != BAMF_WINDOW_DESKTOP) {
593
613
switch_default_app(iapp, NULL, NULL);
600
618
/* When windows leave us, this function gets called */
602
620
old_window (BamfMatcher * matcher, BamfView * view, gpointer user_data)
604
if (view == NULL || !BAMF_IS_WINDOW(view)) {
622
if (!BAMF_IS_WINDOW(view)) {
617
635
/* List of desktop files that shouldn't have menu stubs. */
618
636
const static gchar * stubs_blacklist[] = {
620
"/usr/share/applications/firefox.desktop",
621
639
/* Thunderbird */
622
"/usr/share/applications/thunderbird.desktop",
640
"/thunderbird.desktop",
623
641
/* Open Office */
624
"/usr/share/applications/openoffice.org-base.desktop",
625
"/usr/share/applications/openoffice.org-impress.desktop",
626
"/usr/share/applications/openoffice.org-calc.desktop",
627
"/usr/share/applications/openoffice.org-math.desktop",
628
"/usr/share/applications/openoffice.org-draw.desktop",
629
"/usr/share/applications/openoffice.org-writer.desktop",
642
"/openoffice.org-base.desktop",
643
"/openoffice.org-impress.desktop",
644
"/openoffice.org-calc.desktop",
645
"/openoffice.org-math.desktop",
646
"/openoffice.org-draw.desktop",
647
"/openoffice.org-writer.desktop",
631
"/usr/share/applications/blender-fullscreen.desktop",
632
"/usr/share/applications/blender-windowed.desktop",
649
"/blender-fullscreen.desktop",
650
"/blender-windowed.desktop",
634
"/usr/share/applications/eclipse.desktop",
667
685
g_return_val_if_fail(IS_INDICATOR_APPMENU(io), NULL);
668
686
IndicatorAppmenu * iapp = INDICATOR_APPMENU(io);
689
GList* entries = NULL;
691
if (iapp->mode == MODE_UNITY_ALL_MENUS) {
692
g_hash_table_iter_init(&iter, iapp->apps);
693
while (g_hash_table_iter_next(&iter, NULL, &value)) {
694
GList *app_entries = window_menu_get_entries(WINDOW_MENU (value));
695
entries = g_list_concat(app_entries, entries);
670
701
/* If we have a focused app with menus, use it's windows */
671
702
if (iapp->default_app != NULL) {
705
if (indicator_object_check_environment(INDICATOR_OBJECT(iapp), "unity")) {
709
GList * output = NULL;
712
736
/* There is only one item in window_menus now, but there
713
737
was more, and there is likely to be more in the future
714
738
so we're leaving this here to avoid a possible bug. */
715
740
for (i = 0; i < iapp->window_menus->len; i++) {
716
output = g_list_append(output, &g_array_index(iapp->window_menus, IndicatorObjectEntry, i));
741
entries = g_list_append(entries, &g_array_index(iapp->window_menus, IndicatorObjectEntry, i));
722
747
/* Grabs the location of the entry */
728
753
if (iapp->default_app != NULL) {
729
754
/* Find the location in the app */
730
755
count = window_menu_get_location(iapp->default_app, entry);
731
} else if (iapp->active_window != NULL) {
756
} else if (iapp->active_window != NULL && iapp->window_menus) {
732
757
/* Find the location in the window menus */
733
758
for (count = 0; count < iapp->window_menus->len; count++) {
734
759
if (entry == &g_array_index(iapp->window_menus, IndicatorObjectEntry, count)) {
760
785
static BamfWindow *
761
786
xid_to_bamf_window (IndicatorAppmenu * iapp, guint xid)
763
GList * windows = bamf_matcher_get_windows(iapp->matcher);
788
BamfApplication *application = bamf_matcher_get_application_for_xid(iapp->matcher, xid);
789
GList * children = bamf_view_get_children (BAMF_VIEW (application));
765
791
BamfWindow * newwindow = NULL;
767
for (window = windows; window != NULL; window = g_list_next(window)) {
768
if (!BAMF_IS_WINDOW(window->data)) {
793
for (l = children; l; l = l->next) {
794
if (!BAMF_IS_WINDOW(l->data)) {
772
BamfWindow * testwindow = BAMF_WINDOW(window->data);
798
BamfWindow * testwindow = BAMF_WINDOW(l->data);
774
800
if (xid == bamf_window_get_xid(testwindow)) {
775
801
newwindow = testwindow;
779
g_list_free(windows);
805
g_list_free(children);
781
807
return newwindow;
853
879
iapp->active_window = active_window;
854
iapp->active_stubs = STUBS_UNKNOWN;
881
if (iapp->mode == MODE_STANDARD)
882
iapp->active_stubs = STUBS_UNKNOWN;
856
884
/* Close any existing open menu by showing a null entry */
857
885
window_show_menu(iapp->default_app, NULL, gtk_get_current_event_time(), iapp);
921
connect_to_menu_signals (IndicatorAppmenu * iapp, WindowMenu * menus)
923
g_return_if_fail(G_IS_OBJECT(menus));
925
/* Connect signals */
926
g_signal_connect(menus,
927
WINDOW_MENU_SIGNAL_ENTRY_ADDED,
928
G_CALLBACK(window_entry_added),
930
g_signal_connect(menus,
931
WINDOW_MENU_SIGNAL_ENTRY_REMOVED,
932
G_CALLBACK(window_entry_removed),
934
g_signal_connect(menus,
935
WINDOW_MENU_SIGNAL_STATUS_CHANGED,
936
G_CALLBACK(window_status_changed),
938
g_signal_connect(menus,
939
WINDOW_MENU_SIGNAL_SHOW_MENU,
940
G_CALLBACK(window_show_menu),
942
g_signal_connect(menus,
943
WINDOW_MENU_SIGNAL_A11Y_UPDATE,
944
G_CALLBACK(window_a11y_update),
892
948
/* Switch applications, remove all the entires for the previous
893
949
one and add them for the new application */
895
951
switch_default_app (IndicatorAppmenu * iapp, WindowMenu * newdef, BamfWindow * active_window)
953
if (iapp->mode == MODE_UNITY_ALL_MENUS) {
897
957
if (iapp->default_app == newdef && iapp->default_app != NULL) {
898
958
/* We've got an app with menus and it hasn't changed. */
912
972
/* hide the entries that we're swapping out */
913
973
indicator_object_set_visible (INDICATOR_OBJECT(iapp), FALSE);
915
/* Disconnect signals */
916
if (iapp->sig_entry_added != 0) {
917
g_signal_handler_disconnect(G_OBJECT(iapp->default_app), iapp->sig_entry_added);
918
iapp->sig_entry_added = 0;
920
if (iapp->sig_entry_removed != 0) {
921
g_signal_handler_disconnect(G_OBJECT(iapp->default_app), iapp->sig_entry_removed);
922
iapp->sig_entry_removed = 0;
924
if (iapp->sig_status_changed != 0) {
925
g_signal_handler_disconnect(G_OBJECT(iapp->default_app), iapp->sig_status_changed);
926
iapp->sig_status_changed = 0;
928
if (iapp->sig_show_menu != 0) {
929
g_signal_handler_disconnect(G_OBJECT(iapp->default_app), iapp->sig_show_menu);
930
iapp->sig_show_menu = 0;
932
if (iapp->sig_a11y_update != 0) {
933
g_signal_handler_disconnect(G_OBJECT(iapp->default_app), iapp->sig_a11y_update);
934
iapp->sig_a11y_update = 0;
937
/* Default App is NULL, let's see if it needs replacement */
938
iapp->default_app = NULL;
975
if (iapp->default_app)
977
/* Disconnect signals */
978
g_signal_handlers_disconnect_by_data(iapp->default_app, iapp);
980
/* Default App is NULL, let's see if it needs replacement */
981
iapp->default_app = NULL;
940
984
/* Update the active window pointer -- may be NULL */
941
985
switch_active_window(iapp, active_window);
944
988
if (newdef != NULL) {
946
990
iapp->default_app = newdef;
948
/* Connect signals */
949
iapp->sig_entry_added = g_signal_connect(G_OBJECT(iapp->default_app),
950
WINDOW_MENU_SIGNAL_ENTRY_ADDED,
951
G_CALLBACK(window_entry_added),
953
iapp->sig_entry_removed = g_signal_connect(G_OBJECT(iapp->default_app),
954
WINDOW_MENU_SIGNAL_ENTRY_REMOVED,
955
G_CALLBACK(window_entry_removed),
957
iapp->sig_status_changed = g_signal_connect(G_OBJECT(iapp->default_app),
958
WINDOW_MENU_SIGNAL_STATUS_CHANGED,
959
G_CALLBACK(window_status_changed),
961
iapp->sig_show_menu = g_signal_connect(G_OBJECT(iapp->default_app),
962
WINDOW_MENU_SIGNAL_SHOW_MENU,
963
G_CALLBACK(window_show_menu),
965
iapp->sig_a11y_update = g_signal_connect(G_OBJECT(iapp->default_app),
966
WINDOW_MENU_SIGNAL_A11Y_UPDATE,
967
G_CALLBACK(window_a11y_update),
991
connect_to_menu_signals(iapp, iapp->default_app);
971
994
/* show the entries that we're swapping in */
1009
track_menus (IndicatorAppmenu * iapp, guint xid, WindowMenu * menus)
1011
g_return_if_fail(IS_WINDOW_MENU(menus));
1013
g_hash_table_insert(iapp->apps, GUINT_TO_POINTER(xid), menus);
1015
if (iapp->mode == MODE_UNITY_ALL_MENUS) {
1016
connect_to_menu_signals(iapp, menus);
1021
ensure_menus (IndicatorAppmenu * iapp, BamfWindow * window)
1023
WindowMenu * menus = NULL;
1026
while (window != NULL && menus == NULL) {
1027
xid = bamf_window_get_xid(window);
1029
menus = g_hash_table_lookup(iapp->apps, GUINT_TO_POINTER(xid));
1031
/* First look to see if we can get these from the
1032
GMenuModel access */
1033
if (menus == NULL) {
1034
gchar * uniquename = bamf_window_get_utf8_prop (window, "_GTK_UNIQUE_BUS_NAME");
1036
if (uniquename != NULL) {
1037
BamfApplication * app = bamf_matcher_get_application_for_window(iapp->matcher, window);
1039
menus = WINDOW_MENU(window_menu_model_new(app, window));
1040
track_menus(iapp, xid, menus);
1046
if (menus == NULL) {
1047
g_debug("Looking for parent window on XID %d", xid);
1048
window = bamf_window_get_transient(window);
985
1055
/* Recieve the signal that the window being shown
986
1056
has now changed. */
1001
1071
IndicatorAppmenu * appmenu = INDICATOR_APPMENU(user_data);
1073
if (window != NULL && appmenu->mode == MODE_UNITY_ALL_MENUS) {
1074
ensure_menus(appmenu, window);
1003
1078
if (window != NULL && bamf_window_get_window_type(window) == BAMF_WINDOW_DESKTOP) {
1004
1079
g_debug("Switching to menus from desktop");
1005
1080
switch_default_app(appmenu, NULL, NULL);
1009
WindowMenu * menus = NULL;
1012
while (window != NULL && menus == NULL) {
1013
xid = bamf_window_get_xid(window);
1015
menus = g_hash_table_lookup(appmenu->apps, GUINT_TO_POINTER(xid));
1017
/* First look to see if we can get these from the
1018
GMenuModel access */
1019
if (menus == NULL) {
1020
gchar * uniquename = bamf_window_get_utf8_prop (window, "_GTK_UNIQUE_BUS_NAME");
1022
if (uniquename != NULL) {
1023
BamfApplication * app = bamf_matcher_get_application_for_window(matcher, window);
1025
menus = WINDOW_MENU(window_menu_model_new(app, window));
1027
g_hash_table_insert(appmenu->apps, GUINT_TO_POINTER(xid), menus);
1033
if (menus == NULL) {
1034
g_debug("Looking for parent window on XID %d", xid);
1035
window = bamf_window_get_transient(window);
1084
WindowMenu * menus = ensure_menus (appmenu, window);
1039
1086
/* Note: We're not using window here, but re-casting the
1040
1087
newwindow variable. Which means we stay where we were
1041
1088
but get the menus from parents. */
1042
g_debug("Switching to menus from XID %d", xid);
1089
g_debug("Switching to menus from XID %d", newview ? bamf_window_get_xid(BAMF_WINDOW(newview)) : 0);
1043
1090
if (newview != NULL) {
1044
1091
switch_default_app(appmenu, menus, BAMF_WINDOW(newview));
1046
1093
switch_default_app(appmenu, menus, NULL);
1052
1097
/* Respond to the menus being destroyed. We need to deregister
1057
1102
gboolean reload_menus = FALSE;
1058
1103
WindowMenu * wm = g_hash_table_lookup(iapp->apps, GUINT_TO_POINTER(windowid));
1059
g_return_if_fail (wm != NULL);
1104
g_return_if_fail (IS_WINDOW_MENU(wm));
1061
1106
g_hash_table_steal(iapp->apps, GUINT_TO_POINTER(windowid));
1107
g_signal_handlers_disconnect_by_data(wm, iapp);
1063
1109
g_debug("Removing menus for %d", windowid);
1094
1140
WindowMenu * wm = WINDOW_MENU(window_menu_dbusmenu_new(windowid, sender, objectpath));
1095
1141
g_return_val_if_fail(wm != NULL, FALSE);
1097
g_hash_table_insert(iapp->apps, GUINT_TO_POINTER(windowid), wm);
1143
track_menus(iapp, windowid, wm);
1099
1145
emit_signal(iapp, "WindowRegistered",
1100
1146
g_variant_new("(uso)", windowid, sender, objectpath));
1296
1340
IndicatorObjectEntry * entry = (IndicatorObjectEntry *)entries->data;
1297
1341
g_signal_emit(G_OBJECT(iapp), INDICATOR_OBJECT_SIGNAL_SHOW_NOW_CHANGED_ID, 0, entry, show_now);
1303
1345
/* Pass up the show menu event */
1305
1347
window_show_menu (WindowMenu * mw, IndicatorObjectEntry * entry, guint timestamp, gpointer user_data)
1307
1349
g_signal_emit_by_name(G_OBJECT(user_data), INDICATOR_OBJECT_SIGNAL_MENU_SHOW, entry, timestamp);
1311
1352
/* Pass up the accessible string update */