From e998c73c0b8283aa97a0db67166e9de756f2d1c6 Mon Sep 17 00:00:00 2001 From: Yuri Konotopov Date: Wed, 11 Oct 2023 22:21:30 +0400 Subject: [PATCH] Add Ayatana AppIndicator backend to trayicon plugin Signed-off-by: Yuri Konotopov --- plugins/trayicon.plugin | 4 +-- plugins/trayicon.py | 78 ++++++++++++++++++++++++++++++++--------- src/ui/icons.c | 2 +- src/ui/icons.h | 10 ++++++ 4 files changed, 75 insertions(+), 19 deletions(-) diff --git a/plugins/trayicon.plugin b/plugins/trayicon.plugin index 468a5f868..9d6e5d5d7 100644 --- a/plugins/trayicon.plugin +++ b/plugins/trayicon.plugin @@ -3,7 +3,7 @@ Module=trayicon Loader=python3 Icon=preferences-desktop-accessibility IAge=2 -Name=Tray Icon (GNOME Classic) +Name=Tray Icon (Application Indicator and GNOME Classic) Name[de]=Tray Icon (GNOME Klassik) Name[it]=Icona di vassoio (GNOME Classic) Name[he]=סמל מגש (GNOME קלאסי) @@ -12,7 +12,7 @@ Description[de]=Zeigt ein kleines Tray Icon an mit dem Liferea schnell angezeigt Description[it]=Mostra una piccola icona nel vassoio di sistema per visualizzare o nascondere l'applicazione. Description[he]=הצג סמל מגש מערכת קטן כדי להסתיר או להציג את Liferea Description[pl]=Wyświetl małą ikonę na pasku powiadamiania, aby ukryć/odkryć Liferea. -Authors=Lars Windolf +Authors=Lars Windolf , Yuri Konotopov Copyright=Copyright © 2013 Lars Windolf Website=https://lzone.de/liferea/ Help=https://lzone.de/liferea/ diff --git a/plugins/trayicon.py b/plugins/trayicon.py index ebe76d391..b26995f6b 100644 --- a/plugins/trayicon.py +++ b/plugins/trayicon.py @@ -26,6 +26,18 @@ from gi.repository import GObject, Gtk, Liferea from gi.repository import Gdk, GdkPixbuf +try: + gi.require_version('AyatanaAppIndicator3', '0.1') + from gi.repository import AyatanaAppIndicator3 as AppIndicator + APPINDICATOR_AVAILABLE = True +except (ImportError, ValueError): + try: + gi.require_version('AppIndicator3', '0.1') + from gi.repository import AppIndicator3 as AppIndicator + APPINDICATOR_AVAILABLE = True + except (ImportError, ValueError): + APPINDICATOR_AVAILABLE = False + _ = lambda x: x try: t = gettext.translation("liferea") @@ -117,16 +129,31 @@ class TrayiconPlugin (GObject.Object, Liferea.ShellActivatable): feedlist = None def do_activate(self): - self.read_pix = Liferea.icon_create_from_file("emblem-web.svg") - # FIXME: Support a scalable image! - self.unread_pix = Liferea.icon_create_from_file("unread.png") + if APPINDICATOR_AVAILABLE: + self.indicator = AppIndicator.Indicator.new( + "Liferea", + Liferea.icon_find_pixmap_file("emblem-web.svg"), + AppIndicator.IndicatorCategory.APPLICATION_STATUS + ) + + self.indicator.set_attention_icon_full( + Liferea.icon_find_pixmap_file("unread.png"), + _("Liferea unread icon") + ) + self.indicator.set_status(AppIndicator.IndicatorStatus.ACTIVE) + self.indicator.set_title("Liferea") + else: + self.read_pix = Liferea.icon_create_from_file("emblem-web.svg") + # FIXME: Support a scalable image! + self.unread_pix = Liferea.icon_create_from_file("unread.png") - self.staticon = Gtk.StatusIcon () - self.staticon.connect("activate", self.trayicon_click) - self.staticon.connect("popup_menu", self.trayicon_popup) - self.staticon.connect("size-changed", self.trayicon_size_changed) - self.staticon.set_visible(True) - self.trayicon_set_pixbuf(self.read_pix) + self.staticon = Gtk.StatusIcon () + self.staticon.connect("activate", self.trayicon_click) + self.staticon.connect("popup_menu", self.trayicon_popup) + self.staticon.connect("size-changed", self.trayicon_size_changed) + self.staticon.set_visible(True) + + self.trayicon_set_pixbuf(self.read_pix) self.menu = Gtk.Menu() menuitem_toggle = Gtk.MenuItem(_("Show / Hide")) @@ -149,6 +176,9 @@ def do_activate(self): self.menu.append(menuitem_quit) self.menu.show_all() + if APPINDICATOR_AVAILABLE: + self.indicator.set_menu(self.menu) + self.window = self.shell.get_window() self.delete_signal_id = GObject.signal_lookup("delete_event", Gtk.Window) GObject.signal_handlers_block_matched (self.window, @@ -251,20 +281,33 @@ def feedlist_new_items_cb(self, feedlist=None, new_count=-1): if feedlist is None: feedlist = self.shell.props.feed_list new_count = feedlist.get_new_item_count() - if new_count > 0: - double_figure = min(99, new_count) # show max 2 digit - pix = self.show_new_count(double_figure) + + new_count = min(99, new_count) # show max 2 digit + if APPINDICATOR_AVAILABLE: + if new_count > 0: + self.indicator.set_label(str(new_count), "99") + self.indicator.set_status(AppIndicator.IndicatorStatus.ATTENTION) + else: + self.indicator.set_label("", "99") + self.indicator.set_status(AppIndicator.IndicatorStatus.ACTIVE) else: - pix = self.read_pix + if new_count > 0: + pix = self.show_new_count(new_count) + else: + pix = self.read_pix - self.trayicon_set_pixbuf(pix) + self.trayicon_set_pixbuf(pix) def trayicon_size_changed(self, widget, size): self.feedlist_new_items_cb() return True def do_deactivate(self): - self.staticon.set_visible(False) + if APPINDICATOR_AVAILABLE: + self.indicator.set_status(AppIndicator.IndicatorStatus.PASSIVE) + else: + self.staticon.set_visible(False) + self.window.disconnect_by_func(self.trayicon_close_action) GObject.signal_handlers_unblock_matched (self.window, GObject.SignalMatchType.ID | GObject.SignalMatchType.DATA, @@ -277,6 +320,9 @@ def do_deactivate(self): self.window.deiconify() self.window.show() - del self.staticon + if APPINDICATOR_AVAILABLE: + del self.indicator + else: + del self.staticon del self.window del self.menu diff --git a/src/ui/icons.c b/src/ui/icons.c index a1df620a2..031330a9b 100644 --- a/src/ui/icons.c +++ b/src/ui/icons.c @@ -28,7 +28,7 @@ static GIcon *icons[MAX_ICONS]; /*<< list of icon assignments */ -static gchar * +gchar * icon_find_pixmap_file (const gchar *filename) { gchar *pathname = g_build_filename (PACKAGE_DATA_DIR G_DIR_SEPARATOR_S PACKAGE G_DIR_SEPARATOR_S "pixmaps", filename, NULL); diff --git a/src/ui/icons.h b/src/ui/icons.h index 3461aa1f2..53d393522 100644 --- a/src/ui/icons.h +++ b/src/ui/icons.h @@ -62,6 +62,16 @@ void icons_load (void); */ const GIcon * icon_get (lifereaIcon icon); +/** + * icon_find_pixmap_file: + * @filename: the name of the file + * + * Takes a file name relative to "pixmaps" directory and returns it's path. + * + * Returns: (transfer full): file path or NULL + */ +gchar * icon_find_pixmap_file (const gchar *filename); + /** * icon_create_from_file: * @filename: the name of the file