diff --git a/CHANGES b/CHANGES index 1093b5547..6db26617e 100644 --- a/CHANGES +++ b/CHANGES @@ -52,6 +52,7 @@ Version 1.6.0-dev (development of upcoming release) * Removed: UI translation in Bosnian, Thai, and Occidental/Interlingue (#1914) * Removed: LICENSE file in favor of LICENSES directory and LICENSES.md file * Removed: SSH Cipher configuration in Manage profiles dialog (#2143) +# Fixed: Consider symbolic icons as fallbacks (#2345, #2289) * Fixed: Warn users and prevent backups to exFAT volumes due to lack of hardlink support (#2337) * Fixed: Show proper message to users when root mode fails due to inactive or missing polkit agent (#2328, Derek Veit @DerekVeit) * Fixed: Crash in Compare snapshots dialog (aka Snapshots dialog) when comparing/diff two backups (#2327 Michael Neese @madic-creates) diff --git a/qt/icon.py b/qt/icon.py index 07cb0cb47..97744b410 100644 --- a/qt/icon.py +++ b/qt/icon.py @@ -28,6 +28,11 @@ 'oxygen', ) ICON_NAME_TO_CHECK = 'document-save' +# A workaround especially for GNOME systems not offering regular icons +# specified by the Free Desktop Icon Naming Specs. +# See this thread for more discussion: +# https://lists.debian.org/debian-gtk-gnome/2026/01/msg00000.html +ICON_NAME_TO_CHECK_SYMBOLIC = f'{ICON_NAME_TO_CHECK}-symbolic' for theme in themes_to_try: # Check if the current theme does provide the BiT "logo" icon @@ -36,7 +41,9 @@ # Note: "hicolor" does currently (2022) use different icon names # (not fully compliant to the freedesktop.org spec) # and is not recommended as main theme (it is meant as fallback only). - if not QIcon.fromTheme(ICON_NAME_TO_CHECK).isNull(): + if not QIcon.fromTheme(ICON_NAME_TO_CHECK, + QIcon.fromTheme(ICON_NAME_TO_CHECK_SYMBOLIC) + ).isNull(): logger.debug( f'Icon "{ICON_NAME_TO_CHECK}" found in theme: {QIcon.themeName()}' ) @@ -47,11 +54,13 @@ logger.debug(f'Probing theme: "{theme}" ' f'(activated as "{QIcon.themeName()}")') -if QIcon.fromTheme(ICON_NAME_TO_CHECK).isNull(): +if QIcon.fromTheme(ICON_NAME_TO_CHECK, + QIcon.fromTheme(ICON_NAME_TO_CHECK_SYMBOLIC) + ).isNull(): logger.error( f'Icon theme missing or not supported. Icon "{ICON_NAME_TO_CHECK}" ' - 'not found. An icon theme should be installed. ' - 'For example: tango-icon-theme, oxygen-icon-theme' + f' and "{ICON_NAME_TO_CHECK_SYMBOLIC}" not found. An icon theme should' + ' be installed. For example: tango-icon-theme, oxygen-icon-theme' ) # Dev note: Please prefer choosing icons from the freedesktop.org spec @@ -64,6 +73,23 @@ # the second argument of QIcon.fromTheme() to provide a fallback # icon from the freedesktop.org spec. + +def load_icon(name: str) -> QIcon | None: + return QIcon.fromTheme( + name, + QIcon.fromTheme(f'{name}-symbolic') + ) + + +def load_icon_alt(names: list[str]) -> QIcon | None: + for name in names: + ico = load_icon(name) + + if not ico.isNull(): + return ico + + return None + # BackInTime Logo # TODO If we knew for sure that the global var "qapp" exists then # we could use a built-in "standard" Qt icon as fallback if the theme @@ -76,76 +102,73 @@ BIT_LOGO_SYMBOLIC_NAME = 'backintime-symbolic' # Main toolbar -TAKE_SNAPSHOT = QIcon.fromTheme('document-save') -PAUSE = QIcon.fromTheme('media-playback-pause') -RESUME = QIcon.fromTheme('media-playback-start') -STOP = QIcon.fromTheme('media-playback-stop') -REFRESH = QIcon.fromTheme('view-refresh') +TAKE_SNAPSHOT = load_icon('document-save') +PAUSE = load_icon('media-playback-pause') +RESUME = load_icon('media-playback-start') +STOP = load_icon('media-playback-stop') +REFRESH = load_icon('view-refresh') REFRESH_SNAPSHOT = REFRESH -SNAPSHOT_NAME = QIcon.fromTheme('stock_edit', - QIcon.fromTheme('gtk-edit', - QIcon.fromTheme('edit-rename', - QIcon.fromTheme('accessories-text-editor')))) +SNAPSHOT_NAME = load_icon_alt([ + 'stock_edit', + 'gtk-edit', + 'edit-rename', + 'accessories-text-editor' +]) + EDIT_USER_CALLBACK = SNAPSHOT_NAME -REMOVE_SNAPSHOT = QIcon.fromTheme('edit-delete') -VIEW_SNAPSHOT_LOG = QIcon.fromTheme('text-plain', - QIcon.fromTheme('text-x-generic')) -VIEW_LAST_LOG = QIcon.fromTheme('document-open-recent') -SETTINGS = QIcon.fromTheme('gtk-preferences', - QIcon.fromTheme('configure', - # Free Desktop Icon Naming Specification - QIcon.fromTheme('preferences-system'))) -SHUTDOWN = QIcon.fromTheme('system-shutdown') -EXIT = QIcon.fromTheme('gtk-close', - QIcon.fromTheme('application-exit')) +REMOVE_SNAPSHOT = load_icon('edit-delete') +VIEW_SNAPSHOT_LOG = load_icon_alt(['text-plain', 'text-x-generic']) +VIEW_LAST_LOG = load_icon('document-open-recent') +SETTINGS = load_icon_alt([ + 'gtk-preferences', + 'configure', + # Free Desktop Icon Naming Specification + 'preferences-system' +]) +SHUTDOWN = load_icon('system-shutdown') +EXIT = load_icon_alt(['gtk-close', 'application-exit']) # Help menu -HELP = QIcon.fromTheme('help-browser', - QIcon.fromTheme('help-contents')) -WEBSITE = QIcon.fromTheme('go-home') -CHANGELOG = QIcon.fromTheme('format-justify-fill') -FAQ = QIcon.fromTheme('help-faq', - QIcon.fromTheme('help-hint')) -QUESTION = QIcon.fromTheme('stock_dialog-question', - QIcon.fromTheme('help-feedback')) -BUG = QIcon.fromTheme('stock_dialog-error', - QIcon.fromTheme('tools-report-bug')) -ABOUT = QIcon.fromTheme('help-about') +HELP = load_icon_alt(['help-browser', 'help-contents']) +WEBSITE = load_icon('go-home') +CHANGELOG = load_icon('format-justify-fill') +FAQ = load_icon_alt(['help-faq', 'help-hint']) +QUESTION = load_icon_alt(['stock_dialog-question', 'help-feedback']) +BUG = load_icon_alt(['stock_dialog-error', 'tools-report-bug']) +ABOUT = load_icon('help-about') # Files toolbar -UP = QIcon.fromTheme('go-up') - # currently only in Breeze (see #1159) -SHOW_HIDDEN = QIcon.fromTheme('view-hidden', - # icon installed with BIT #507 - QIcon.fromTheme('show-hidden', - QIcon.fromTheme('list-add'))) -RESTORE = QIcon.fromTheme('edit-undo') -RESTORE_TO = QIcon.fromTheme('document-revert') -SNAPSHOTS = QIcon.fromTheme('file-manager', - QIcon.fromTheme('view-list-details', - QIcon.fromTheme('system-file-manager'))) - -# Snapshot dialog -DIFF_OPTIONS = SETTINGS -DELETE_FILE = REMOVE_SNAPSHOT -SELECT_ALL = QIcon.fromTheme('edit-select-all') +UP = load_icon('go-up') +SHOW_HIDDEN = load_icon_alt([ + 'view-hidden', # currently only in Breeze (see #1159) + 'show-hidden', # icon installed with BIT #507 + 'list-add' +]) +RESTORE = load_icon('edit-undo') +RESTORE_TO = load_icon('document-revert') +SNAPSHOTS = load_icon_alt([ + 'file-manager', + 'view-list-details', + 'system-file-manager' +]) + +# Compare backups dialog (aka Snapshot dialog) +DIFF_OPTIONS = SETTINGS +DELETE_FILE = REMOVE_SNAPSHOT +SELECT_ALL = load_icon('edit-select-all') # Restore dialog -RESTORE_DIALOG = VIEW_SNAPSHOT_LOG +RESTORE_DIALOG = VIEW_SNAPSHOT_LOG # Settings dialog -SETTINGS_DIALOG = SETTINGS -PROFILE_EDIT = SNAPSHOT_NAME -ADD = QIcon.fromTheme('list-add') -REMOVE = QIcon.fromTheme('list-remove') -FOLDER = QIcon.fromTheme('folder') -FILE = QIcon.fromTheme('text-plain', - QIcon.fromTheme('text-x-generic')) -EXCLUDE = QIcon.fromTheme('edit-delete') -# "emblem-default" is a green mark and doesn't make sense in this case. -DEFAULT_EXCLUDE = QIcon.fromTheme('emblem-important') -INVALID_EXCLUDE = QIcon.fromTheme('emblem-ohno', - QIcon.fromTheme('face-surprise')) - -ENCRYPT = QIcon.fromTheme('lock', QIcon.fromTheme('security-high')) -LANGUAGE = QIcon.fromTheme('preferences-desktop-locale') +SETTINGS_DIALOG = SETTINGS +PROFILE_EDIT = SNAPSHOT_NAME +ADD = load_icon('list-add') +REMOVE = load_icon('list-remove') +FOLDER = load_icon('folder') +FILE = load_icon_alt(['text-plain', 'text-x-generic']) +EXCLUDE = load_icon('edit-delete') +DEFAULT_EXCLUDE = load_icon('emblem-important') +INVALID_EXCLUDE = load_icon_alt(['emblem-ohno', 'face-surprise']) +ENCRYPT = load_icon_alt(['lock', 'security-high']) +LANGUAGE = load_icon('preferences-desktop-locale')