diff --git a/app/ui/app_menu.ui b/app/ui/app_menu.ui index 5de4ff69..ee08a5e0 100644 --- a/app/ui/app_menu.ui +++ b/app/ui/app_menu.ui @@ -176,6 +176,8 @@ app.on_logs_show +
+
FTP client @@ -390,6 +392,8 @@ app.on_logs_show +
+
FTP client diff --git a/app/ui/extensions/__init__.py b/app/ui/extensions/__init__.py new file mode 100644 index 00000000..d9609036 --- /dev/null +++ b/app/ui/extensions/__init__.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# +# The MIT License (MIT) +# +# Copyright (c) 2023 Dmitriy Yefremov +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# Author: Dmitriy Yefremov +# + + +class BaseExtension: + """ Base extension (plugin) class. """ + # The label that will be displayed in the "Tools" menu. + LABEL = "Base extension" + + def __init__(self, app): + # Current application instance. + # It can be used all public methods, properties or signals. + self._app = app + + def exec(self): + """ Triggers an action for the given extension. + + E.g. shows a dialog or runs an external script. + """ + self._app.show_info_message(f"Hello from {self.__class__.__name__} class!") + + +if __name__ == "__main__": + pass diff --git a/app/ui/main.py b/app/ui/main.py index bf51ae18..48b4fee0 100644 --- a/app/ui/main.py +++ b/app/ui/main.py @@ -590,40 +590,7 @@ class Application(Gtk.Application): def do_startup(self): Gtk.Application.do_startup(self) - # App menu. - builder = get_builder(UI_RESOURCES_PATH + "app_menu.ui", tag="attribute") - if not IS_GNOME_SESSION: - if IS_DARWIN: - self.set_app_menu(builder.get_object("mac_app_menu")) - self.set_menubar(builder.get_object("mac_menu_bar")) - else: - self.set_menubar(builder.get_object("menu_bar")) - else: - tools_menu = builder.get_object("tools_menu") - tools_button = Gtk.MenuButton(visible=True, menu_model=tools_menu, direction=Gtk.ArrowType.NONE) - tools_button.set_tooltip_text(get_message("Tools")) - tools_button.set_image(Gtk.Image.new_from_icon_name("applications-utilities-symbolic", Gtk.IconSize.BUTTON)) - - view_menu = builder.get_object("view_menu") - view_button = Gtk.MenuButton(visible=True, menu_model=view_menu, direction=Gtk.ArrowType.NONE) - view_button.set_tooltip_text(get_message("View")) - - box = Gtk.ButtonBox(visible=True, layout_style="expand") - box.add(tools_button) - box.add(view_button) - self._main_window.get_titlebar().pack_end(box) - # IPTV menu. - self._iptv_menu_button.set_menu_model(builder.get_object("iptv_menu")) - iptv_elem = self._tool_elements.get("fav_iptv_popup_item") - for h in (self.on_iptv, self.on_import_yt_list, self.on_import_m3u, self.on_export_iptv_to_m3u, - self.on_epg_list_configuration, self.on_iptv_list_configuration, self.on_remove_all_unavailable): - iptv_elem.bind_property("sensitive", self.set_action(h.__name__, h, False), "enabled") - - def do_activate(self): - self._main_window.set_application(self) - self._main_window.set_wmclass("DemonEditor", "DemonEditor") - self._main_window.present() - + self.init_app_menu() self.init_actions() self.set_accels() @@ -631,6 +598,11 @@ class Application(Gtk.Application): self.init_appearance() self.filter_set_default() + def do_activate(self): + self._main_window.set_application(self) + self._main_window.set_wmclass("DemonEditor", "DemonEditor") + self._main_window.present() + self.init_profiles() gen = self.init_http_api() GLib.idle_add(lambda: next(gen, False), priority=GLib.PRIORITY_LOW) @@ -674,6 +646,64 @@ class Application(Gtk.Application): self.activate() return 0 + def init_app_menu(self): + builder = get_builder(UI_RESOURCES_PATH + "app_menu.ui", tag="attribute") + if not IS_GNOME_SESSION: + if IS_DARWIN: + self.set_app_menu(builder.get_object("mac_app_menu")) + self.set_menubar(builder.get_object("mac_menu_bar")) + else: + self.set_menubar(builder.get_object("menu_bar")) + else: + tools_menu = builder.get_object("tools_menu") + tools_button = Gtk.MenuButton(visible=True, menu_model=tools_menu, direction=Gtk.ArrowType.NONE) + tools_button.set_tooltip_text(get_message("Tools")) + tools_button.set_image(Gtk.Image.new_from_icon_name("applications-utilities-symbolic", Gtk.IconSize.BUTTON)) + + view_menu = builder.get_object("view_menu") + view_button = Gtk.MenuButton(visible=True, menu_model=view_menu, direction=Gtk.ArrowType.NONE) + view_button.set_tooltip_text(get_message("View")) + + box = Gtk.ButtonBox(visible=True, layout_style="expand") + box.add(tools_button) + box.add(view_button) + self._main_window.get_titlebar().pack_end(box) + # IPTV menu. + self._iptv_menu_button.set_menu_model(builder.get_object("iptv_menu")) + iptv_elem = self._tool_elements.get("fav_iptv_popup_item") + for h in (self.on_iptv, self.on_import_yt_list, self.on_import_m3u, self.on_export_iptv_to_m3u, + self.on_epg_list_configuration, self.on_iptv_list_configuration, self.on_remove_all_unavailable): + iptv_elem.bind_property("sensitive", self.set_action(h.__name__, h, False), "enabled") + + if self._settings.is_enable_experimental: + self.init_extensions(builder) + + def init_extensions(self, builder): + import pkgutil + # Extensions (Plugins) section. + ext_section = builder.get_object(f"{'mac_' if IS_DARWIN else ''}extension_section") + ext_path = f"{self._settings.default_data_path}tools{os.sep}extensions" + ext_paths = [f"{os.path.dirname(__file__)}{os.sep}extensions", ext_path] + extensions = {} + + for importer, name, is_package in pkgutil.iter_modules(ext_paths): + if is_package: + m = importer.find_module(name).load_module() + cls_name = name.capitalize() + if hasattr(m, cls_name): + cls = getattr(m, cls_name) + action_name = f"on_{name}_extension" + item = Gio.MenuItem.new(cls.LABEL, f"app.{action_name}") + ext_section.append_item(item) + extensions[action_name] = cls + + def ac(a, v): + c = extensions[a.get_name()] + e = c(self) + e.exec() + + self.set_action(action_name, ac) + def init_actions(self): self.set_action("on_import_bouquet", self.on_import_bouquet) self.set_action("on_import_bouquets", self.on_import_bouquets) @@ -4335,7 +4365,7 @@ class Application(Gtk.Application): self.show_info_message(message, Gtk.MessageType.ERROR) @run_idle - def show_info_message(self, text, message_type): + def show_info_message(self, text, message_type=Gtk.MessageType.INFO): self._info_bar.set_visible(False) self._info_label.set_text(get_message(text)) self._info_bar.set_message_type(message_type)