diff -rub a/configure.ac b/configure.ac --- a/configure.ac 2017-02-15 22:15:21.000000000 +0800 +++ b/configure.ac 2020-02-13 00:42:12.928497295 +0800 @@ -58,7 +58,8 @@ glib-2.0 >= $GLIB_REQUIRED_VERSION gio-2.0 >= $GIO_REQUIRED_VERSION indicator3-0.4 >= $INDICATOR_REQUIRED_VERSION - dbusmenu-gtk3-0.4 >= $DBUSMENUGTK_REQUIRED_VERSION) + dbusmenu-gtk3-0.4 >= $DBUSMENUGTK_REQUIRED_VERSION + xapp >= 1.5.0) AC_DEFINE(HAVE_GTK3, 1, [whether gtk3 is available]) ], [test "x$with_gtk" = x2], diff -rub a/src/appindicator3-0.1.pc.in b/src/appindicator3-0.1.pc.in --- a/src/appindicator3-0.1.pc.in 2017-02-15 22:15:21.000000000 +0800 +++ b/src/appindicator3-0.1.pc.in 2020-02-12 23:39:49.768066612 +0800 @@ -5,7 +5,7 @@ includedir=@includedir@ Cflags: -I${includedir}/libappindicator3-0.1 -Requires: dbusmenu-glib-0.4 gtk+-3.0 +Requires: dbusmenu-glib-0.4 gtk+-3.0 xapp Libs: -L${libdir} -lappindicator3 Name: appindicator3-0.1 diff -rub a/src/app-indicator.c b/src/app-indicator.c --- a/src/app-indicator.c 2017-02-15 22:15:36.000000000 +0800 +++ b/src/app-indicator.c 2020-02-13 01:07:11.066450662 +0800 @@ -31,6 +31,8 @@ #include "config.h" #endif +#include + #include #include #include @@ -52,6 +54,8 @@ #define PANEL_ICON_SUFFIX "panel" +#define FALLBACK_ICON "image-missing" + /** * AppIndicatorPrivate: * All of the private data in an instance of an application indicator. @@ -90,7 +94,7 @@ gchar * att_accessible_desc; guint label_change_idle; - GtkStatusIcon * status_icon; + XAppStatusIcon * status_icon; gint fallback_timer; /* Fun stuff */ @@ -186,14 +190,11 @@ static void register_service_cb (GObject * obj, GAsyncResult * res, gpointer user_data); static void start_fallback_timer (AppIndicator * self, gboolean disable_timeout); static gboolean fallback_timer_expire (gpointer data); -static GtkStatusIcon * fallback (AppIndicator * self); +static XAppStatusIcon * fallback (AppIndicator * self); static void status_icon_status_wrapper (AppIndicator * self, const gchar * status, gpointer data); -static gboolean scroll_event_wrapper(GtkWidget *status_icon, GdkEventScroll *event, gpointer user_data); -static gboolean middle_click_wrapper(GtkWidget *status_icon, GdkEventButton *event, gpointer user_data); +// static gboolean scroll_event_wrapper(GtkWidget *status_icon, GdkEventScroll *event, gpointer user_data); static void status_icon_changes (AppIndicator * self, gpointer data); -static void status_icon_activate (GtkStatusIcon * icon, gpointer data); -static void status_icon_menu_activate (GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data); -static void unfallback (AppIndicator * self, GtkStatusIcon * status_icon); +static void unfallback (AppIndicator * self, XAppStatusIcon * status_icon); static gchar * append_panel_icon_suffix (const gchar * icon_name); static gchar * get_real_theme_path (AppIndicator * self); static gchar * append_snap_prefix (const gchar * path); @@ -952,6 +953,9 @@ if (g_strcmp0(oldlabel, priv->label) != 0) { signal_label_change(APP_INDICATOR(object)); + if (priv->status_icon != NULL) { + xapp_status_icon_set_label (priv->status_icon, priv->label ? priv->label : ""); + } } if (oldlabel != NULL) { @@ -983,15 +987,16 @@ g_warning("Unable to send signal for NewTitle: %s", error->message); g_error_free(error); } + + if (priv->status_icon != NULL) { + xapp_status_icon_set_tooltip_text (priv->status_icon, priv->title ? priv->title : ""); + } } if (oldtitle != NULL) { g_free(oldtitle); } - if (priv->status_icon != NULL) { - gtk_status_icon_set_title(priv->status_icon, priv->title ? priv->title : ""); - } break; } case PROP_LABEL_GUIDE: { @@ -1521,19 +1526,60 @@ return; } +/* Handles the middle-click secondary action coming from XAppStatusIcon. */ +static void +status_icon_menu_button_activate (XAppStatusIcon *status_icon, + guint button, + guint activate_time, + gpointer user_data) +{ + AppIndicator *self = APP_INDICATOR (user_data); + + if (button == GDK_BUTTON_PRIMARY || button == GDK_BUTTON_MIDDLE) { + GtkWidget *menuitem = self->priv->sec_activate_target; + + if (menuitem != NULL && + gtk_widget_get_visible (menuitem) && + gtk_widget_get_sensitive (menuitem)) + { + gtk_widget_activate (menuitem); + } + } +} + +static void +status_icon_set_has_secondary_activate (XAppStatusIcon *icon, + gboolean has) +{ + if (icon != NULL) + { + g_object_set_data (G_OBJECT (icon), + "app-indicator-has-secondary-activate", GINT_TO_POINTER (has)); + } +} + /* Creates a StatusIcon that can be used when the application indicator area isn't available. */ -static GtkStatusIcon * +static XAppStatusIcon * fallback (AppIndicator * self) { - GtkStatusIcon * icon = gtk_status_icon_new(); + XAppStatusIcon * icon = xapp_status_icon_new(); + + xapp_status_icon_set_name(icon, app_indicator_get_id(self)); - gtk_status_icon_set_name(icon, app_indicator_get_id(self)); const gchar * title = app_indicator_get_title(self); if (title != NULL) { - gtk_status_icon_set_title(icon, title); + xapp_status_icon_set_tooltip_text (icon, title); } + xapp_status_icon_set_label (icon, app_indicator_get_label (self)); + + /* Tell xapp-status-icon where this is coming from. It will change the behavior + * of left clicks accordingly (to open the menu instead of some sort of 'activation') */ + g_object_set_data (G_OBJECT (icon), "app-indicator", GINT_TO_POINTER (1)); + + status_icon_set_has_secondary_activate (icon, self->priv->sec_activate_enabled); + g_signal_connect(G_OBJECT(self), APP_INDICATOR_SIGNAL_NEW_STATUS, G_CALLBACK(status_icon_status_wrapper), icon); g_signal_connect(G_OBJECT(self), APP_INDICATOR_SIGNAL_NEW_ICON, @@ -1541,12 +1587,11 @@ g_signal_connect(G_OBJECT(self), APP_INDICATOR_SIGNAL_NEW_ATTENTION_ICON, G_CALLBACK(status_icon_changes), icon); + xapp_status_icon_set_secondary_menu (icon, app_indicator_get_menu (self)); + status_icon_changes(self, icon); - g_signal_connect(G_OBJECT(icon), "activate", G_CALLBACK(status_icon_activate), self); - g_signal_connect(G_OBJECT(icon), "popup-menu", G_CALLBACK(status_icon_menu_activate), self); - g_signal_connect(G_OBJECT(icon), "scroll-event", G_CALLBACK(scroll_event_wrapper), self); - g_signal_connect(G_OBJECT(icon), "button-release-event", G_CALLBACK(middle_click_wrapper), self); + g_signal_connect(G_OBJECT(icon), "activate", G_CALLBACK(status_icon_menu_button_activate), self); return icon; } @@ -1561,49 +1606,22 @@ /* A wrapper for redirecting the scroll events to the app-indicator from status icon widget. */ -static gboolean -scroll_event_wrapper (GtkWidget *status_icon, GdkEventScroll *event, gpointer data) -{ - g_return_val_if_fail(IS_APP_INDICATOR(data), FALSE); - AppIndicator * app = APP_INDICATOR(data); - g_signal_emit(app, signals[SCROLL_EVENT], 0, 1, event->direction); +// static gboolean +// scroll_event_wrapper (GtkWidget *status_icon, GdkEventScroll *event, gpointer data) +// { +// g_return_val_if_fail(IS_APP_INDICATOR(data), FALSE); +// AppIndicator * app = APP_INDICATOR(data); +// g_signal_emit(app, signals[SCROLL_EVENT], 0, 1, event->direction); - return TRUE; -} - -static gboolean -middle_click_wrapper (GtkWidget *status_icon, GdkEventButton *event, gpointer data) -{ - g_return_val_if_fail(IS_APP_INDICATOR(data), FALSE); - AppIndicator * app = APP_INDICATOR(data); - AppIndicatorPrivate *priv = app->priv; - - if (event->button == 2 && event->type == GDK_BUTTON_RELEASE) { - GtkAllocation alloc; - gint px = event->x; - gint py = event->y; - gtk_widget_get_allocation (status_icon, &alloc); - GtkWidget *menuitem = priv->sec_activate_target; - - if (px >= 0 && px < alloc.width && py >= 0 && py < alloc.height && - priv->sec_activate_enabled && menuitem && - gtk_widget_get_visible (menuitem) && - gtk_widget_get_sensitive (menuitem)) - { - gtk_widget_activate (menuitem); - return TRUE; - } - } - - return FALSE; -} +// return TRUE; +// } /* This tracks changes to either the status or the icons that are associated with the app indicator */ static void status_icon_changes (AppIndicator * self, gpointer data) { - GtkStatusIcon * icon = GTK_STATUS_ICON(data); + XAppStatusIcon * icon = XAPP_STATUS_ICON(data); /* add the icon_theme_path once if needed */ GtkIconTheme *icon_theme = gtk_icon_theme_get_default(); @@ -1632,17 +1650,17 @@ switch (app_indicator_get_status(self)) { case APP_INDICATOR_STATUS_PASSIVE: /* hide first to avoid that the change is visible to the user */ - gtk_status_icon_set_visible(icon, FALSE); + xapp_status_icon_set_visible(icon, FALSE); icon_name = app_indicator_get_icon(self); break; case APP_INDICATOR_STATUS_ACTIVE: icon_name = app_indicator_get_icon(self); - gtk_status_icon_set_visible(icon, TRUE); + xapp_status_icon_set_visible(icon, TRUE); break; case APP_INDICATOR_STATUS_ATTENTION: /* get the _attention_ icon here */ icon_name = app_indicator_get_attention_icon(self); - gtk_status_icon_set_visible(icon, TRUE); + xapp_status_icon_set_visible(icon, TRUE); break; }; @@ -1650,65 +1668,66 @@ gchar *snapped_icon = append_snap_prefix(icon_name); if (g_file_test(icon_name, G_FILE_TEST_EXISTS)) { - gtk_status_icon_set_from_file(icon, icon_name); + xapp_status_icon_set_icon_name(icon, icon_name); } else if (snapped_icon && g_file_test(snapped_icon, G_FILE_TEST_EXISTS)) { - gtk_status_icon_set_from_file(icon, snapped_icon); + xapp_status_icon_set_icon_name(icon, snapped_icon); } else { gchar *longname = append_panel_icon_suffix(icon_name); if (longname != NULL && gtk_icon_theme_has_icon (icon_theme, longname)) { - gtk_status_icon_set_from_icon_name(icon, longname); + xapp_status_icon_set_icon_name(icon, longname); } else { - gtk_status_icon_set_from_icon_name(icon, icon_name); - } + if (gtk_icon_theme_has_icon (icon_theme, icon_name)) { + xapp_status_icon_set_icon_name(icon, icon_name); + } else { + gint i; + gchar *icon_path; - g_free(longname); + const gchar *extensions[] = { + "png", + "svg", + }; + + for (i = 0; i < G_N_ELEMENTS (extensions); i++) { + icon_path = g_strdup_printf ("%s/%s.%s", theme_path, icon_name, extensions[i]); + + if (g_file_test (icon_path, G_FILE_TEST_EXISTS)) { + break; } - g_free(snapped_icon); + g_clear_pointer (&icon_path, g_free); } - return; -} + if (icon_path) { + xapp_status_icon_set_icon_name (icon, icon_path); + g_free (icon_path); + } else { + // Should we do this?? Otherwise there's a blank (but reactive) + // space in the panel + xapp_status_icon_set_icon_name (icon, FALLBACK_ICON); + } + } + } -/* Handles the activate action by the status icon by showing - the menu in a popup. */ -static void -status_icon_activate (GtkStatusIcon * icon, gpointer data) -{ - GtkMenu * menu = app_indicator_get_menu(APP_INDICATOR(data)); - if (menu == NULL) - return; + g_free(longname); + } - gtk_menu_popup(menu, - NULL, /* Parent Menu */ - NULL, /* Parent item */ - gtk_status_icon_position_menu, - icon, - 1, /* Button */ - gtk_get_current_event_time()); + g_free(snapped_icon); + } return; } -/* Handles the right-click action by the status icon by showing - the menu in a popup. */ -static void -status_icon_menu_activate (GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer user_data) -{ - status_icon_activate(status_icon, user_data); -} - /* Removes the status icon as the application indicator area is now up and running again. */ static void -unfallback (AppIndicator * self, GtkStatusIcon * status_icon) +unfallback (AppIndicator * self, XAppStatusIcon * status_icon) { g_signal_handlers_disconnect_by_func(G_OBJECT(self), status_icon_status_wrapper, status_icon); g_signal_handlers_disconnect_by_func(G_OBJECT(self), status_icon_changes, status_icon); - g_signal_handlers_disconnect_by_func(G_OBJECT(self), scroll_event_wrapper, status_icon); - g_signal_handlers_disconnect_by_func(G_OBJECT(self), middle_click_wrapper, status_icon); - gtk_status_icon_set_visible(status_icon, FALSE); + g_signal_handlers_disconnect_by_func(G_OBJECT(self), status_icon_menu_button_activate, status_icon); + + xapp_status_icon_set_visible(status_icon, FALSE); g_object_unref(G_OBJECT(status_icon)); return; } @@ -1760,6 +1779,8 @@ g_return_if_fail(IS_APP_INDICATOR(data)); AppIndicator *self = data; self->priv->sec_activate_enabled = widget_is_menu_child(self, menuitem); + + status_icon_set_has_secondary_activate (self->priv->status_icon, self->priv->sec_activate_enabled); } @@ -2053,6 +2074,7 @@ PROP_LABEL_GUIDE_S, guide == NULL ? "" : guide, NULL); + return; } @@ -2224,9 +2246,14 @@ priv->menu = GTK_WIDGET (menu); g_object_ref_sink (priv->menu); + if (self->priv->status_icon) { + xapp_status_icon_set_secondary_menu (self->priv->status_icon, menu); + } + setup_dbusmenu (self); priv->sec_activate_enabled = widget_is_menu_child (self, priv->sec_activate_target); + status_icon_set_has_secondary_activate (self->priv->status_icon, priv->sec_activate_enabled); check_connect (self); @@ -2280,6 +2307,7 @@ self); g_object_unref(G_OBJECT(priv->sec_activate_target)); priv->sec_activate_target = NULL; + status_icon_set_has_secondary_activate (priv->status_icon, FALSE); } if (menuitem == NULL) { @@ -2288,8 +2316,9 @@ g_return_if_fail (GTK_IS_WIDGET (menuitem)); - priv->sec_activate_target = g_object_ref(G_OBJECT(menuitem)); + priv->sec_activate_target = g_object_ref(menuitem); priv->sec_activate_enabled = widget_is_menu_child(self, menuitem); + status_icon_set_has_secondary_activate (priv->status_icon, priv->sec_activate_enabled); g_signal_connect(menuitem, "parent-set", G_CALLBACK(sec_activate_target_parent_changed), self); } diff -rub a/src/app-indicator.h b/src/app-indicator.h --- a/src/app-indicator.h 2017-02-15 22:15:21.000000000 +0800 +++ b/src/app-indicator.h 2020-02-13 01:04:06.226176973 +0800 @@ -32,6 +32,14 @@ #include +#if GTK_MAJOR_VERSION == 3 +#define USE_XAPP_FALLBACK 1 +#endif + +#ifdef USE_XAPP_FALLBACK +#include +#endif + G_BEGIN_DECLS /** @@ -218,10 +226,18 @@ void (*app_indicator_reserved_ats)(void); /* Overridable Functions */ +#ifdef USE_XAPP_FALLBACK + XAppStatusIcon * (*fallback) (AppIndicator * indicator); + void (*unfallback) (AppIndicator * indicator, + XAppStatusIcon * status_icon); + +#else GtkStatusIcon * (*fallback) (AppIndicator * indicator); void (*unfallback) (AppIndicator * indicator, GtkStatusIcon * status_icon); +#endif + /* Reserved */ void (*app_indicator_reserved_1)(void); void (*app_indicator_reserved_2)(void); diff -rub a/src/Makefile.am b/src/Makefile.am --- a/src/Makefile.am 2017-02-15 22:15:21.000000000 +0800 +++ b/src/Makefile.am 2020-02-13 00:11:49.889693111 +0800 @@ -57,7 +57,6 @@ libappindicator_la_SOURCES = \ $(libappindicator_headers) \ app-indicator-enum-types.c \ - app-indicator.c \ application-service-marshal.c \ dbus-shared.h \ generate-id.h \ @@ -67,6 +66,14 @@ gen-notification-watcher.xml.h \ gen-notification-watcher.xml.c +if USE_GTK3 +libappindicator_la_SOURCES += \ + app-indicator.c +else +libappindicator_la_SOURCES += \ + app-indicator-gtk2.c +endif + libappindicator_la_LDFLAGS = \ $(COVERAGE_LDFLAGS) \ -version-info 1:0:0 \ @@ -149,9 +156,15 @@ if HAVE_INTROSPECTION +if USE_GTK3 introspection_sources = \ $(addprefix $(srcdir)/,app-indicator.c) \ $(addprefix $(srcdir)/,$(libappindicator_headers)) +else +introspection_sources = \ + $(addprefix $(srcdir)/,app-indicator-gtk2.c) \ + $(addprefix $(srcdir)/,$(libappindicator_headers)) +endif AppIndicator$(VER)-0.1.gir: libappindicator$(VER).la diff -rub a/tests/test-libappindicator-fallback-item.c b/tests/test-libappindicator-fallback-item.c --- a/tests/test-libappindicator-fallback-item.c 2017-02-15 22:15:21.000000000 +0800 +++ b/tests/test-libappindicator-fallback-item.c 2020-02-13 01:06:01.518347682 +0800 @@ -31,8 +31,14 @@ static void test_libappindicator_fallback_item_class_init (TestLibappindicatorFallbackItemClass *klass); static void test_libappindicator_fallback_item_init (TestLibappindicatorFallbackItem *self); + +#ifdef USE_XAPP_FALLBACK +static XAppStatusIcon * fallback (AppIndicator * indicator); +static void unfallback (AppIndicator * indicator, XAppStatusIcon * status_icon); +#else static GtkStatusIcon * fallback (AppIndicator * indicator); static void unfallback (AppIndicator * indicator, GtkStatusIcon * status_icon); +#endif G_DEFINE_TYPE (TestLibappindicatorFallbackItem, test_libappindicator_fallback_item, APP_INDICATOR_TYPE); @@ -63,6 +69,39 @@ gint state = STATE_INIT; +#ifdef USE_XAPP_FALLBACK +static XAppStatusIcon * +fallback (AppIndicator * indicator) +{ + g_debug("Fallback"); + if (state == STATE_INIT) { + state = STATE_FALLBACK; + } else if (state == STATE_UNFALLBACK) { + state = STATE_REFALLBACK; + } else { + g_debug("Error, fallback in state: %d", state); + passed = FALSE; + } + return (XAppStatusIcon *)5; +} + +static void +unfallback (AppIndicator * indicator, XAppStatusIcon * status_icon) +{ + g_debug("Unfallback"); + if (state == STATE_FALLBACK) { + state = STATE_UNFALLBACK; + } else if (state == STATE_REFALLBACK) { + state = STATE_REUNFALLBACK; + passed = TRUE; + g_main_loop_quit(mainloop); + } else { + g_debug("Error, unfallback in state: %d", state); + passed = FALSE; + } + return; +} +#else static GtkStatusIcon * fallback (AppIndicator * indicator) { @@ -94,6 +133,7 @@ } return; } +#endif // USE_XAPP_FALLBACK gboolean kill_func (gpointer data)