From 9fd2ce79dd1649d51df5826cb351d2e81f113a5a Mon Sep 17 00:00:00 2001 From: Sean Davis Date: Fri, 17 Sep 2021 08:15:39 -0400 Subject: [PATCH] Handle unprefixed applications.menu, show diagnostics on menu load failure (#67) --- menulibre/MenuEditor.py | 29 ++++++- menulibre/MenulibreApplication.py | 35 ++++++++ menulibre/MenulibreTreeview.py | 11 ++- menulibre/util.py | 28 +++++++ po/menulibre.pot | 131 +++++++++++++++++------------- 5 files changed, 173 insertions(+), 61 deletions(-) diff --git a/menulibre/MenuEditor.py b/menulibre/MenuEditor.py index 77b34e02..973b58ad 100644 --- a/menulibre/MenuEditor.py +++ b/menulibre/MenuEditor.py @@ -46,7 +46,20 @@ def get_default_menu(): """Return the filename of the default application menu.""" - return '%s%s' % (util.getDefaultMenuPrefix(), 'applications.menu') + prefixes = [ + util.getDefaultMenuPrefix(), + '' + ] + user_basedir = util.getUserMenuPath() + for prefix in prefixes: + filename = '%s%s' % (prefix, 'applications.menu') + user_dir = os.path.join(user_basedir, filename) + if os.path.exists(user_dir): + return filename + system_dir = util.getSystemMenuPath(filename) + if system_dir: + return filename + return None def load_fallback_icon(icon_size): @@ -119,8 +132,10 @@ def get_treestore(): # Filename, exp, show treestore = Gtk.TreeStore(str, str, str, int, Gio.Icon, str, str, bool, bool) - menu = get_menus()[0] - return menu_to_treestore(treestore, None, menu) + menus = get_menus() + if not menus: + return None + return menu_to_treestore(treestore, None, menus[0]) def get_submenus(menu, tree_dir): @@ -189,6 +204,8 @@ def get_submenus(menu, tree_dir): def get_menus(): """Get the menus from the MenuEditor""" menu = MenuEditor() + if not menu.loaded: + return None structure = [] toplevels = [] global menu_name @@ -230,12 +247,16 @@ def getUserMenuXml(tree): class MenuEditor(object): """MenuEditor class, adapted and minimized from Alacarte Menu Editor.""" + loaded = False + def __init__(self, basename=None): """init""" # Remember to keep menulibre-menu-validate's GMenu object creation # in-sync with this code basename = basename or get_default_menu() + if basename is None: + return self.tree = GMenu.Tree.new(basename, GMenu.TreeFlags.SHOW_EMPTY | @@ -250,6 +271,8 @@ def __init__(self, basename=None): logger.debug("Using menu: %s" % self.path) self.loadDOM() + self.loaded = True + def loadDOM(self): """loadDOM""" try: diff --git a/menulibre/MenulibreApplication.py b/menulibre/MenulibreApplication.py index 3ed6f586..7c192f9b 100644 --- a/menulibre/MenulibreApplication.py +++ b/menulibre/MenulibreApplication.py @@ -281,6 +281,39 @@ def root_lockout(self): dialog.run() sys.exit(1) + def menu_load_failure(self): + primary = _("MenuLibre failed to load.") + + docs_url = "https://github.com/bluesabre/menulibre/wiki/Frequently-Asked-Questions" + + # Translators: This link goes to the online documentation with more + # information. + secondary = _("The default menu could not be found. Please see the " + "online documentation " + "for more information.") % docs_url + + secondary += "\n\n%s" % _("Diagnostics") + + diagnostics = util.getMenuDiagnostics() + for k, v in diagnostics.items(): + secondary += "\n%s: %s" % (k, v) + + dialog = Gtk.MessageDialog(None, 0, Gtk.MessageType.ERROR, + Gtk.ButtonsType.CLOSE, primary) + dialog.format_secondary_markup(secondary) + + try: + box = dialog.get_children()[0] + box = box.get_children()[0] + box = box.get_children()[1] + label = box.get_children()[1] + label.set_selectable(True) + except AttributeError: + pass + + dialog.run() + sys.exit(1) + def configure_application_window(self, builder, app): """Glade is currently unable to create a GtkApplicationWindow. This function takes the GtkWindow from the UI file and reparents the @@ -592,6 +625,8 @@ def configure_application_toolbar(self, builder): def configure_application_treeview(self, builder): """Configure the menu-browsing GtkTreeView.""" self.treeview = MenulibreTreeview.Treeview(self, builder) + if not self.treeview.loaded: + self.menu_load_failure() treeview = self.treeview.get_treeview() treeview.set_search_entry(self.search_box) self.search_box.connect('changed', self.on_app_search_changed, diff --git a/menulibre/MenulibreTreeview.py b/menulibre/MenulibreTreeview.py index 0aa82e60..aa7f3602 100644 --- a/menulibre/MenulibreTreeview.py +++ b/menulibre/MenulibreTreeview.py @@ -40,12 +40,17 @@ class Treeview(GObject.GObject): (GObject.TYPE_BOOLEAN,)), } + loaded = False + def __init__(self, parent, builder): GObject.GObject.__init__(self) self.parent = parent # Configure Widgets - self._configure_treeview(builder) + if self._configure_treeview(builder): + self.loaded = True + else: + return self._configure_toolbar(builder) # Defaults @@ -57,6 +62,8 @@ def _configure_treeview(self, builder): """Configure the TreeView widget.""" # Get the menu treestore. treestore = MenuEditor.get_treestore() + if not treestore: + return False self._treeview = builder.get_object('classic_view_treeview') @@ -105,6 +112,8 @@ def _configure_treeview(self, builder): self.menu_timeout_id = 0 + return True + def _configure_toolbar(self, builder): """Configure the toolbar widget.""" self._toolbar = builder.get_object('browser_toolbar') diff --git a/menulibre/util.py b/menulibre/util.py index 04522581..c537db5e 100644 --- a/menulibre/util.py +++ b/menulibre/util.py @@ -207,6 +207,34 @@ def getDefaultMenuPrefix(): # noqa return prefix +def getMenuDiagnostics(): + diagnostics = {} + keys = [ + "XDG_CURRENT_DESKTOP", + "XDG_MENU_PREFIX", + "DESKTOP_SESSION", + "KDE_SESSION_VERSION" + ] + for k in keys: + diagnostics[k] = os.environ.get(k, "None") + + menu_dirs = [ + getUserMenuPath() + ] + for path in GLib.get_system_config_dirs(): + menu_dirs.append(os.path.join(path, 'menus')) + menus = [] + for menu_dir in menu_dirs: + for filename in os.listdir(menu_dir): + if filename.endswith(".menu"): + menus.append(os.path.join(menu_dir, filename)) + menus.sort() + + diagnostics["MENUS"] = ", ".join(menus) + + return diagnostics + + def getItemPath(file_id): """Return the path to the system-installed .desktop file.""" for path in GLib.get_system_data_dirs(): diff --git a/po/menulibre.pot b/po/menulibre.pot index f58ffa01..9636af13 100644 --- a/po/menulibre.pot +++ b/po/menulibre.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-17 04:21-0400\n" +"POT-Creation-Date: 2021-09-17 08:14-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -57,19 +57,19 @@ msgstr "" #. Translators: Toolbar button to undo last change to currently selected item. #. Translators: Undo action tooltip -#: ../data/ui/MenulibreWindow.ui.h:14 ../menulibre/MenulibreApplication.py:433 +#: ../data/ui/MenulibreWindow.ui.h:14 ../menulibre/MenulibreApplication.py:466 msgid "Undo" msgstr "" #. Translators: Toolbar button to redo the last undone change to currently selected item. #. Translators: Redo action tooltip -#: ../data/ui/MenulibreWindow.ui.h:16 ../menulibre/MenulibreApplication.py:442 +#: ../data/ui/MenulibreWindow.ui.h:16 ../menulibre/MenulibreApplication.py:475 msgid "Redo" msgstr "" #. Translators: Toolbar button to revery the currently selected item to it #. Translators: Revert action tooltip -#: ../data/ui/MenulibreWindow.ui.h:18 ../menulibre/MenulibreApplication.py:451 +#: ../data/ui/MenulibreWindow.ui.h:18 ../menulibre/MenulibreApplication.py:484 msgid "Revert" msgstr "" @@ -80,7 +80,7 @@ msgstr "" #. Translators: Toolbar button to delete the currently selected item. #. Translators: Delete action tooltip -#: ../data/ui/MenulibreWindow.ui.h:22 ../menulibre/MenulibreApplication.py:469 +#: ../data/ui/MenulibreWindow.ui.h:22 ../menulibre/MenulibreApplication.py:502 msgid "Delete" msgstr "" @@ -88,7 +88,7 @@ msgstr "" #. Translators: Save On Leave Dialog, do save, then leave. #. Translators: Save Launcher action tooltip #: ../data/ui/MenulibreWindow.ui.h:23 ../menulibre/Dialogs.py:103 -#: ../menulibre/Dialogs.py:132 ../menulibre/MenulibreApplication.py:424 +#: ../menulibre/Dialogs.py:132 ../menulibre/MenulibreApplication.py:457 msgid "Save" msgstr "" @@ -124,7 +124,7 @@ msgstr "" #. Translators: Placeholder text/hint for the application comment entry. #. Translators: "Description" tree column header -#: ../data/ui/MenulibreWindow.ui.h:37 ../menulibre/MenulibreApplication.py:851 +#: ../data/ui/MenulibreWindow.ui.h:37 ../menulibre/MenulibreApplication.py:886 msgid "Description" msgstr "" @@ -555,8 +555,8 @@ msgid "Click on the main application window for '%s'." msgstr "" #. Translators: Separator menu item -#: ../menulibre/MenuEditor.py:91 ../menulibre/MenulibreApplication.py:1233 -#: ../menulibre/MenulibreApplication.py:1731 +#: ../menulibre/MenuEditor.py:104 ../menulibre/MenulibreApplication.py:1268 +#: ../menulibre/MenulibreApplication.py:1766 msgid "Separator" msgstr "" @@ -705,198 +705,215 @@ msgid "" "Please see the online documentation for more information." msgstr "" +#: ../menulibre/MenulibreApplication.py:285 +msgid "MenuLibre failed to load." +msgstr "" + +#. Translators: This link goes to the online documentation with more +#. information. +#: ../menulibre/MenulibreApplication.py:291 +#, python-format +msgid "" +"The default menu could not be found. Please see the online " +"documentation for more information." +msgstr "" + +#: ../menulibre/MenulibreApplication.py:295 +msgid "Diagnostics" +msgstr "" + #. Translators: Add Launcher action label -#: ../menulibre/MenulibreApplication.py:395 +#: ../menulibre/MenulibreApplication.py:428 msgid "Add _Launcher…" msgstr "" #. Translators: Add Launcher action tooltip -#: ../menulibre/MenulibreApplication.py:397 +#: ../menulibre/MenulibreApplication.py:430 msgid "Add Launcher…" msgstr "" #. Translators: Add Directory action label -#: ../menulibre/MenulibreApplication.py:404 +#: ../menulibre/MenulibreApplication.py:437 msgid "Add _Directory…" msgstr "" #. Translators: Add Directory action tooltip -#: ../menulibre/MenulibreApplication.py:406 +#: ../menulibre/MenulibreApplication.py:439 msgid "Add Directory…" msgstr "" #. Translators: Add Separator action label -#: ../menulibre/MenulibreApplication.py:413 +#: ../menulibre/MenulibreApplication.py:446 msgid "_Add Separator…" msgstr "" #. Translators: Add Separator action tooltip -#: ../menulibre/MenulibreApplication.py:415 +#: ../menulibre/MenulibreApplication.py:448 msgid "Add Separator…" msgstr "" #. Translators: Save Launcher action label -#: ../menulibre/MenulibreApplication.py:422 +#: ../menulibre/MenulibreApplication.py:455 msgid "_Save" msgstr "" #. Translators: Undo action label -#: ../menulibre/MenulibreApplication.py:431 +#: ../menulibre/MenulibreApplication.py:464 msgid "_Undo" msgstr "" #. Translators: Redo action label -#: ../menulibre/MenulibreApplication.py:440 +#: ../menulibre/MenulibreApplication.py:473 msgid "_Redo" msgstr "" #. Translators: Revert action label -#: ../menulibre/MenulibreApplication.py:449 +#: ../menulibre/MenulibreApplication.py:482 msgid "_Revert" msgstr "" #. Translators: Execute action label -#: ../menulibre/MenulibreApplication.py:458 +#: ../menulibre/MenulibreApplication.py:491 msgid "_Execute" msgstr "" #. Translators: Execute action tooltip -#: ../menulibre/MenulibreApplication.py:460 +#: ../menulibre/MenulibreApplication.py:493 msgid "Execute Launcher" msgstr "" #. Translators: Delete action label -#: ../menulibre/MenulibreApplication.py:467 +#: ../menulibre/MenulibreApplication.py:500 msgid "_Delete" msgstr "" #. Translators: Quit action label -#: ../menulibre/MenulibreApplication.py:476 +#: ../menulibre/MenulibreApplication.py:509 msgid "_Quit" msgstr "" #. Translators: Quit action tooltip -#: ../menulibre/MenulibreApplication.py:478 -#: ../menulibre/MenulibreApplication.py:2261 +#: ../menulibre/MenulibreApplication.py:511 +#: ../menulibre/MenulibreApplication.py:2296 msgid "Quit" msgstr "" #. Translators: Help action label -#: ../menulibre/MenulibreApplication.py:485 +#: ../menulibre/MenulibreApplication.py:518 msgid "_Contents" msgstr "" #. Translators: Help action tooltip -#: ../menulibre/MenulibreApplication.py:487 -#: ../menulibre/MenulibreApplication.py:2259 +#: ../menulibre/MenulibreApplication.py:520 +#: ../menulibre/MenulibreApplication.py:2294 msgid "Help" msgstr "" #. Translators: About action label -#: ../menulibre/MenulibreApplication.py:494 +#: ../menulibre/MenulibreApplication.py:527 msgid "_About" msgstr "" #. Translators: About action tooltip -#: ../menulibre/MenulibreApplication.py:496 -#: ../menulibre/MenulibreApplication.py:2260 +#: ../menulibre/MenulibreApplication.py:529 +#: ../menulibre/MenulibreApplication.py:2295 msgid "About" msgstr "" #. Translators: "Categories" launcher section -#: ../menulibre/MenulibreApplication.py:639 +#: ../menulibre/MenulibreApplication.py:674 msgid "Categories" msgstr "" #. Translators: "Actions" launcher section -#: ../menulibre/MenulibreApplication.py:642 +#: ../menulibre/MenulibreApplication.py:677 msgid "Actions" msgstr "" #. Translators: "Advanced" launcher section -#: ../menulibre/MenulibreApplication.py:645 +#: ../menulibre/MenulibreApplication.py:680 msgid "Advanced" msgstr "" #. Translators: Launcher-specific categories, camelcase "This Entry" -#: ../menulibre/MenulibreApplication.py:819 +#: ../menulibre/MenulibreApplication.py:854 msgid "ThisEntry" msgstr "" #. Translators: Placeholder text for the launcher-specific category #. selection. -#: ../menulibre/MenulibreApplication.py:840 +#: ../menulibre/MenulibreApplication.py:875 msgid "Select a category" msgstr "" #. Translators: "Category Name" tree column header -#: ../menulibre/MenulibreApplication.py:844 +#: ../menulibre/MenulibreApplication.py:879 msgid "Category Name" msgstr "" #. Translators: "This Entry" launcher-specific category group -#: ../menulibre/MenulibreApplication.py:947 +#: ../menulibre/MenulibreApplication.py:982 msgid "This Entry" msgstr "" #. Translators: Placeholder text for a newly created action -#: ../menulibre/MenulibreApplication.py:1008 +#: ../menulibre/MenulibreApplication.py:1043 msgid "New Shortcut" msgstr "" #. Translators: File Chooser Dialog, window title. -#: ../menulibre/MenulibreApplication.py:1158 +#: ../menulibre/MenulibreApplication.py:1193 msgid "Select a working directory…" msgstr "" #. Translators: File Chooser Dialog, window title. -#: ../menulibre/MenulibreApplication.py:1162 +#: ../menulibre/MenulibreApplication.py:1197 msgid "Select an executable…" msgstr "" #. Translators: This error is displayed when the user does not #. have sufficient file system permissions to delete the #. selected file. -#: ../menulibre/MenulibreApplication.py:1409 +#: ../menulibre/MenulibreApplication.py:1444 msgid "You do not have permission to delete this file." msgstr "" #. Translators: Placeholder text for a newly created launcher. -#: ../menulibre/MenulibreApplication.py:1657 +#: ../menulibre/MenulibreApplication.py:1692 msgid "New Launcher" msgstr "" #. Translators: Placeholder text for a newly created launcher's #. description. -#: ../menulibre/MenulibreApplication.py:1660 ../menulibre/MenulibreXdg.py:49 +#: ../menulibre/MenulibreApplication.py:1695 ../menulibre/MenulibreXdg.py:49 msgid "A small descriptive blurb about this application." msgstr "" #. Translators: Placeholder text for a newly created directory. -#: ../menulibre/MenulibreApplication.py:1710 +#: ../menulibre/MenulibreApplication.py:1745 msgid "New Directory" msgstr "" #. Translators: Placeholder text for a newly created directory's #. description. -#: ../menulibre/MenulibreApplication.py:1713 +#: ../menulibre/MenulibreApplication.py:1748 msgid "A small descriptive blurb about this directory." msgstr "" #. Translators: Confirmation dialog to delete the selected #. separator. -#: ../menulibre/MenulibreApplication.py:2138 +#: ../menulibre/MenulibreApplication.py:2173 msgid "Are you sure you want to delete this separator?" msgstr "" #. Translators: Confirmation dialog to delete the selected launcher. -#: ../menulibre/MenulibreApplication.py:2142 +#: ../menulibre/MenulibreApplication.py:2177 #, python-format msgid "Are you sure you want to delete \"%s\"?" msgstr "" #. Translators: Menu item to open the Parsing Errors dialog. -#: ../menulibre/MenulibreApplication.py:2254 +#: ../menulibre/MenulibreApplication.py:2289 msgid "Parsing Error Log" msgstr "" @@ -911,7 +928,7 @@ msgid "Images" msgstr "" #. Translators: "Search Results" treeview column header -#: ../menulibre/MenulibreTreeview.py:64 +#: ../menulibre/MenulibreTreeview.py:71 msgid "Search Results" msgstr "" @@ -923,7 +940,7 @@ msgstr "" #. Translators: This error is displayed when a desktop file cannot #. be correctly read by MenuLibre. A (possibly untranslated) error #. code is displayed. -#: ../menulibre/util.py:621 +#: ../menulibre/util.py:649 #, python-format msgid "Unable to load desktop file due to the following error: %s" msgstr "" @@ -931,31 +948,31 @@ msgstr "" #. Translators: This error is displayed when the first group in a #. failing desktop file is incorrect. "Start group" can be safely #. translated. -#: ../menulibre/util.py:636 +#: ../menulibre/util.py:664 #, python-format msgid "Start group is invalid - currently '%s', should be '%s'" msgstr "" #. Translators: This error is displayed when a required key is #. missing in a failing desktop file. -#: ../menulibre/util.py:646 +#: ../menulibre/util.py:674 #, python-format msgid "%s key not found" msgstr "" #. Translators: This error is displayed when a failing desktop file #. has an invalid value for the provided key. -#: ../menulibre/util.py:651 +#: ../menulibre/util.py:679 #, python-format msgid "%s value is invalid - currently '%s', should be '%s'" msgstr "" -#: ../menulibre/util.py:680 +#: ../menulibre/util.py:708 #, python-format msgid "%s program '%s' has not been found in the PATH" msgstr "" -#: ../menulibre/util.py:683 +#: ../menulibre/util.py:711 #, python-format msgid "" "%s program '%s' is not a valid shell command according to GLib." @@ -964,6 +981,6 @@ msgstr "" #. Translators: This error is displayed for a failing desktop file where #. errors were detected but the file seems otherwise valid. -#: ../menulibre/util.py:689 +#: ../menulibre/util.py:717 msgid "Unknown error. Desktop file appears to be valid." msgstr ""