By using this site, you agree to have cookies stored on your device, strictly for functional purposes, such as storing your session and preferences.

Dismiss

add a currently basic notifier

roundabout,
created on Sunday, 17 August 2025, 20:31:04 (1755462664), received on Sunday, 17 August 2025, 20:31:34 (1755462694)
Author identity: Vlad <vlad.muntoiu@gmail.com>

999274bbebf9fe9571eb5dfff72859e7df0f4ebb

applets/notifier/__init__.py

@@ -0,0 +1,144 @@

                                
                                
                                
                            
                                
                                    
                                        
                                        """
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        D-Bus notifier applet for the Panorama panel.
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        Copyright 2025, roundabout-host.com <vlad@roundabout-host.com>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        This program is free software: you can redistribute it and/or modify
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        it under the terms of the GNU General Public Licence as published by
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        the Free Software Foundation, either version 3 of the Licence, or
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        (at your option) any later version.
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        This program is distributed in the hope that it will be useful,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        but WITHOUT ANY WARRANTY; without even the implied warranty of
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        GNU General Public Licence for more details.
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        You should have received a copy of the GNU General Public Licence
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        along with this program. If not, see <https://www.gnu.org/licenses/>.
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        """
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        from __future__ import annotations
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        import panorama_panel
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        import locale
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        from pathlib import Path
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        from pydbus import SessionBus
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        import gi
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        gi.require_version("Gtk", "4.0")
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        from gi.repository import Gtk, Gdk, GLib
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        module_directory = Path(__file__).resolve().parent
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        locale.bindtextdomain("panorama-app-menu", module_directory / "locale")
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        _ = lambda x: locale.dgettext("panorama-app-menu", x)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        custom_css = """
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        .notification-button {
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            padding: 0;
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        }
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        """
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        css_provider = Gtk.CssProvider()
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        css_provider.load_from_data(custom_css)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        Gtk.StyleContext.add_provider_for_display(
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            Gdk.Display.get_default(),
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            css_provider,
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        )
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        class NotificationsService:
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            """
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            <node>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                              <interface name="org.freedesktop.Notifications">
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                <method name="GetCapabilities">
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                  <arg type="as" name="caps" direction="out"/>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                </method>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                <method name="GetServerInformation">
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                  <arg type="s" name="name" direction="out"/>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                  <arg type="s" name="vendor" direction="out"/>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                  <arg type="s" name="version" direction="out"/>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                  <arg type="s" name="spec_version" direction="out"/>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                </method>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                <method name="Notify">
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                  <arg type="s" name="app_name" direction="in"/>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                  <arg type="u" name="replaces_id" direction="in"/>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                  <arg type="s" name="app_icon" direction="in"/>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                  <arg type="s" name="summary" direction="in"/>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                  <arg type="s" name="body" direction="in"/>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                  <arg type="as" name="actions" direction="in"/>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                  <arg type="a{sv}" name="hints" direction="in"/>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                  <arg type="i" name="expire_timeout" direction="in"/>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                  <arg type="u" name="id" direction="out"/>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                </method>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                              </interface>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            </node>
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            """
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            def __init__(self, applet: NotifierApplet):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self._next_id = 1
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.applet = applet
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            def GetCapabilities(self):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                return ["body"]
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            def GetServerInformation(self):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                # name, vendor, version, spec_version
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                return "panorama-panel", "panorama", "0.1", "1.2"
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            def Notify(self, *args):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                notification_id = self._next_id
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self._next_id += 1
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.applet.push_notification(*args)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                return notification_id
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        class NotifierApplet(panorama_panel.Applet):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            name = _("Notification centre")
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            description = _("Get desktop notifications")
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            def __init__(self, orientation=Gtk.Orientation.HORIZONTAL, config=None):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                super().__init__(orientation=orientation, config=config)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                if config is None:
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    config = {}
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.button = Gtk.MenuButton()
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.button.set_has_frame(False)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.button.add_css_class("notification-button")
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.stack = Gtk.Stack()
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.button.set_child(self.stack)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.notification_indicator = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.notification_indicator.append(Gtk.Image.new_from_icon_name(""))
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.stack.add_child(self.notification_indicator)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_UP_DOWN)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.stack.set_transition_duration(500)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                # TODO: support the other parameters; add a popover with a history
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.append(self.button)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.service = NotificationsService(self)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                bus = SessionBus()
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.publishing = bus.publish("org.freedesktop.Notifications", self.service)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            def push_notification(self, app_name, replaces_id, app_icon, summary, body, actions, hints, expire_timeout):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                new_child = Gtk.Label.new(_(summary))
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.stack.add_child(new_child)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.stack.set_visible_child(new_child)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                def remove_notification():
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    self.stack.set_visible_child(self.stack.get_visible_child().get_prev_sibling())
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    def remove_child():
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                        self.stack.remove(new_child)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    GLib.timeout_add(self.stack.get_transition_duration(), remove_child)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    return False
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                if expire_timeout <= 0:
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                    expire_timeout = 2500
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                GLib.timeout_add(expire_timeout, remove_notification)
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                # TODO: notify that it was closed
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            def get_config(self):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                return {}
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                        
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                            def shutdown(self):
                                        
                                        
                                        
                                    
                                
                                
                                
                            
                                
                                    
                                        
                                                self.publishing.unpublish()