Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
157 changes: 90 additions & 67 deletions qt/icon.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()}'
)
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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')