From 9a86f1327bee229be0b01a6bf031d0fd79e02ba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Helleu?= Date: Sat, 4 Jul 2026 14:51:58 +0200 Subject: [PATCH] core: implement /theme apply with themable enforcement and auto-backup Implement /theme apply for themes currently in the in-memory registry. The file-shadowing branch (read a .theme file from ${weechat_config_dir}/themes/ when no built-in matches) is added in the next commit together with the parser. Apply algorithm (theme_apply in core-theme.c): - Look up the theme in the registry; abort with an error if unknown. - If weechat.look.theme_backup is on and the target name does not begin with "backup-", write a full snapshot of every themable option to ${weechat_config_dir}/themes/backup-.theme via theme_make_backup; abort the apply if the backup cannot be written, so the user can always undo. - Iterate the theme's overrides with theme_applying=1 so the per-option config_change_color skips its gui refresh; for each entry look up the option, refuse it if missing or non-themable (warning to core buffer), otherwise call config_file_option_set. - Perform a single gui_color_init_weechat + gui_window_ask_refresh at the end. - Persist the active label in weechat.look.theme and send signal "theme_applied" with the name as data. Add the new option weechat.look.theme_backup (boolean, default on) which controls the backup-or-abort behavior described above. Wire the new /theme apply subcommand into core-command.c with the existing /theme registration; update help text accordingly. --- CHANGELOG.md | 2 +- po/cs.po | 48 ++++- po/de.po | 48 ++++- po/es.po | 48 ++++- po/fr.po | 66 ++++++- po/hu.po | 46 ++++- po/it.po | 48 ++++- po/ja.po | 48 ++++- po/pl.po | 48 ++++- po/pt.po | 48 ++++- po/pt_BR.po | 47 ++++- po/ru.po | 46 ++++- po/sr.po | 48 ++++- po/tr.po | 48 ++++- po/weechat.pot | 46 ++++- src/core/core-command.c | 19 +- src/core/core-config.c | 13 ++ src/core/core-config.h | 1 + src/core/core-theme.c | 292 ++++++++++++++++++++++++++++ src/core/core-theme.h | 2 + tests/unit/core/test-core-theme.cpp | 225 +++++++++++++++++++++ 21 files changed, 1205 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ab3699bf..846470a39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ SPDX-License-Identifier: GPL-3.0-or-later - core: add command `/theme` - core: add `themable` flag on configuration options -- core: add option weechat.look.theme +- core: add options weechat.look.theme and weechat.look.theme_backup - fset: add filter `t:themable` - relay/api: add resource `GET /api/scripts` - relay: add option relay.network.unix_socket_permissions ([#2317](https://github.com/weechat/weechat/issues/2317)) diff --git a/po/cs.po b/po/cs.po index 8aa7f4de1..79cd92b20 100644 --- a/po/cs.po +++ b/po/cs.po @@ -23,7 +23,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 14:09+0200\n" +"POT-Creation-Date: 2026-07-04 15:44+0200\n" "PO-Revision-Date: 2026-05-30 14:01+0200\n" "Last-Translator: Ondřej Súkup \n" "Language-Team: Czech \n" @@ -3699,7 +3699,7 @@ msgid "manage color themes" msgstr "seznam položek polí" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list] || info " +msgid "[list] || apply || info " msgstr "" #, fuzzy @@ -3709,6 +3709,11 @@ msgid "" "theme is marked with \"->\"" msgstr "seznam filtrů" +msgid "" +"raw[apply]: apply a theme (set every themable option to the value from the " +"theme)" +msgstr "" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -3724,6 +3729,14 @@ msgid "" "directory \"themes\" inside the WeeChat configuration directory." msgstr "" +msgid "" +"By default, `/theme apply` command creates a backup of current themable " +"values in directory \"themes\" before applying (file name: \"backup-" +".theme\"); the previous state can be restored with: `/theme apply " +"backup-`. This is controlled by the option " +"weechat.look.theme_backup." +msgstr "" + #, fuzzy #| msgid "values for a configuration option" msgid "toggle value of a config option" @@ -5288,6 +5301,14 @@ msgid "" "startup" msgstr "" +msgid "" +"create a backup theme file with the current themable options before applying " +"a theme with command /theme; if the backup file cannot be written, the apply " +"is aborted (no option is changed); the backup file is written to directory " +"\"themes\" inside the WeeChat configuration directory and can be restored " +"with the command: `/theme apply backup-`" +msgstr "" + #, fuzzy msgid "" "time format for dates converted to strings and displayed in messages (see " @@ -6259,6 +6280,29 @@ msgstr "%s: chyba: slovník \"%s\" není ve vašem systému dostupný" msgid "Resource usage (see \"man getrusage\" for help):" msgstr "Využití paměti (viz \"man mallinfo\" pro nápovědu):" +msgid "Automatic backup written before /theme apply" +msgstr "" + +#, fuzzy, c-format +#| msgid "%sKey \"%s\" not found" +msgid "%sTheme: option \"%s\" not found, skipped" +msgstr "%sKlávesa \"%s\" nenalezena" + +#, fuzzy, c-format +#| msgid "%s: script \"%s\" is not installed" +msgid "%sTheme: option \"%s\" is not themable, skipped" +msgstr "%s: skript \"%s\" není nainstalován" + +#, c-format +msgid "" +"%sUnable to create theme backup; aborting apply (disable option " +"weechat.look.theme_backup to force)" +msgstr "" + +#, c-format +msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" +msgstr "" + #, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "%sChyba aktualizace WeeChat se souborem \"%s\":" diff --git a/po/de.po b/po/de.po index 042dc77ef..f05225ed6 100644 --- a/po/de.po +++ b/po/de.po @@ -26,7 +26,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 14:09+0200\n" +"POT-Creation-Date: 2026-07-04 15:44+0200\n" "PO-Revision-Date: 2026-06-28 08:28+0200\n" "Last-Translator: Nils Görs \n" "Language-Team: German \n" @@ -4091,7 +4091,7 @@ msgid "manage color themes" msgstr "Verwalten von benutzerdefinierten Bar-Items" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list] || info " +msgid "[list] || apply || info " msgstr "" #, fuzzy @@ -4105,6 +4105,11 @@ msgstr "" "raw[list]: listet relay-Server auf (ohne Angabe von Argumente wird diese " "Liste standardmäßig ausgegeben)" +msgid "" +"raw[apply]: apply a theme (set every themable option to the value from the " +"theme)" +msgstr "" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -4121,6 +4126,14 @@ msgid "" "directory \"themes\" inside the WeeChat configuration directory." msgstr "" +msgid "" +"By default, `/theme apply` command creates a backup of current themable " +"values in directory \"themes\" before applying (file name: \"backup-" +".theme\"); the previous state can be restored with: `/theme apply " +"backup-`. This is controlled by the option " +"weechat.look.theme_backup." +msgstr "" + msgid "toggle value of a config option" msgstr "den Wert einer Konfigurationsoption umschalten" @@ -5996,6 +6009,14 @@ msgid "" "startup" msgstr "" +msgid "" +"create a backup theme file with the current themable options before applying " +"a theme with command /theme; if the backup file cannot be written, the apply " +"is aborted (no option is changed); the backup file is written to directory " +"\"themes\" inside the WeeChat configuration directory and can be restored " +"with the command: `/theme apply backup-`" +msgstr "" + msgid "" "time format for dates converted to strings and displayed in messages (see " "man strftime for date/time specifiers)" @@ -7094,6 +7115,29 @@ msgstr "Die Systemfunktion „%s“ ist nicht verfügbar" msgid "Resource usage (see \"man getrusage\" for help):" msgstr "Ressourcennutzung (siehe „man getrusage“ für Hilfe):" +msgid "Automatic backup written before /theme apply" +msgstr "" + +#, fuzzy, c-format +#| msgid "%sKey \"%s\" not found" +msgid "%sTheme: option \"%s\" not found, skipped" +msgstr "%sTastenbelegung \"%s\" nicht gefunden" + +#, fuzzy, c-format +#| msgid "%s: script \"%s\" is not installed" +msgid "%sTheme: option \"%s\" is not themable, skipped" +msgstr "%s: Skript \"%s\" ist nicht installiert" + +#, c-format +msgid "" +"%sUnable to create theme backup; aborting apply (disable option " +"weechat.look.theme_backup to force)" +msgstr "" + +#, c-format +msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" +msgstr "" + #, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "" diff --git a/po/es.po b/po/es.po index 4e24bafec..2a371aa3f 100644 --- a/po/es.po +++ b/po/es.po @@ -24,7 +24,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 14:09+0200\n" +"POT-Creation-Date: 2026-07-04 15:44+0200\n" "PO-Revision-Date: 2026-06-28 08:46+0200\n" "Last-Translator: Santiago Forero \n" "Language-Team: Spanish \n" @@ -3792,7 +3792,7 @@ msgid "manage color themes" msgstr "lista de elementos de barra" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list] || info " +msgid "[list] || apply || info " msgstr "" #, fuzzy @@ -3802,6 +3802,11 @@ msgid "" "theme is marked with \"->\"" msgstr "lista de filtros" +msgid "" +"raw[apply]: apply a theme (set every themable option to the value from the " +"theme)" +msgstr "" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -3817,6 +3822,14 @@ msgid "" "directory \"themes\" inside the WeeChat configuration directory." msgstr "" +msgid "" +"By default, `/theme apply` command creates a backup of current themable " +"values in directory \"themes\" before applying (file name: \"backup-" +".theme\"); the previous state can be restored with: `/theme apply " +"backup-`. This is controlled by the option " +"weechat.look.theme_backup." +msgstr "" + #, fuzzy #| msgid "values for a configuration option" msgid "toggle value of a config option" @@ -5434,6 +5447,14 @@ msgid "" "startup" msgstr "" +msgid "" +"create a backup theme file with the current themable options before applying " +"a theme with command /theme; if the backup file cannot be written, the apply " +"is aborted (no option is changed); the backup file is written to directory " +"\"themes\" inside the WeeChat configuration directory and can be restored " +"with the command: `/theme apply backup-`" +msgstr "" + #, fuzzy msgid "" "time format for dates converted to strings and displayed in messages (see " @@ -6415,6 +6436,29 @@ msgstr "%s: error: diccionario \"%s\" no está disponible en tu sistema" msgid "Resource usage (see \"man getrusage\" for help):" msgstr "Uso de memoria (ver en \"man mallinfo\" por ayuda):" +msgid "Automatic backup written before /theme apply" +msgstr "" + +#, fuzzy, c-format +#| msgid "%sKey \"%s\" not found" +msgid "%sTheme: option \"%s\" not found, skipped" +msgstr "%sTecla \"%s\" no encontrada" + +#, fuzzy, c-format +#| msgid "%s: script \"%s\" is not installed" +msgid "%sTheme: option \"%s\" is not themable, skipped" +msgstr "%s: el script \"%s\" no esta instalado" + +#, c-format +msgid "" +"%sUnable to create theme backup; aborting apply (disable option " +"weechat.look.theme_backup to force)" +msgstr "" + +#, c-format +msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" +msgstr "" + #, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "%sError al actualizar WeeChat con el archivo \"%s\":" diff --git a/po/fr.po b/po/fr.po index 0d42c59c6..33871a9da 100644 --- a/po/fr.po +++ b/po/fr.po @@ -23,8 +23,8 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 14:09+0200\n" -"PO-Revision-Date: 2026-07-04 14:11+0200\n" +"POT-Creation-Date: 2026-07-04 15:44+0200\n" +"PO-Revision-Date: 2026-07-04 15:46+0200\n" "Last-Translator: Sébastien Helleu \n" "Language-Team: French \n" "Language: fr\n" @@ -4019,8 +4019,8 @@ msgid "manage color themes" msgstr "gestion des thèmes de couleurs" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list] || info " -msgstr "[list] || info " +msgid "[list] || apply || info " +msgstr "[list] || apply || info " msgid "" "raw[list]: list registered themes (default action with no argument); active " @@ -4029,6 +4029,13 @@ msgstr "" "raw[list] : afficher les thèmes enregistrés (sans paramètre, cette liste est " "affichée), le thème actif est marqué avec \"->\"" +msgid "" +"raw[apply]: apply a theme (set every themable option to the value from the " +"theme)" +msgstr "" +"raw[apply] : appliquer un thème (définir chaque option personnalisable par " +"thème à la valeur du thème)" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -4049,6 +4056,19 @@ msgstr "" "scripts ; les thèmes utilisateur sont lus à partir de fichiers situés dans " "le répertoire \"themes\" du répertoire de configuration de WeeChat." +msgid "" +"By default, `/theme apply` command creates a backup of current themable " +"values in directory \"themes\" before applying (file name: \"backup-" +".theme\"); the previous state can be restored with: `/theme apply " +"backup-`. This is controlled by the option " +"weechat.look.theme_backup." +msgstr "" +"Par défaut, la commande `/theme apply` crée une sauvegarde des valeurs du " +"thème actuel dans le répertoire \"themes\" avant de les appliquer (nom de " +"fichier : \"backup-.theme\") ; l'état précédent peut être " +"restauré à l'aide de la commande `/theme apply backup-`. Ce " +"comportement est régi par l'option weechat.look.theme_backup." + msgid "toggle value of a config option" msgstr "basculer la valeur d'une option de configuration" @@ -5873,6 +5893,20 @@ msgstr "" "automatiquement, ne pas changer manuellement) ; à titre d'information " "seulement, le thème n'est pas réappliqué au démarrage" +msgid "" +"create a backup theme file with the current themable options before applying " +"a theme with command /theme; if the backup file cannot be written, the apply " +"is aborted (no option is changed); the backup file is written to directory " +"\"themes\" inside the WeeChat configuration directory and can be restored " +"with the command: `/theme apply backup-`" +msgstr "" +"créer un fichier de sauvegarde des options de thème actuelles avant " +"d'appliquer un thème avec la commande `/theme` ; si le fichier de sauvegarde " +"ne peut pas être écrit, l'application est annulée (aucune option n'est " +"modifiée) ; le fichier de sauvegarde est enregistré dans le répertoire " +"\"themes\" situé dans le répertoire de configuration de WeeChat et peut être " +"restauré à l'aide de la commande : `/theme apply backup-`" + msgid "" "time format for dates converted to strings and displayed in messages (see " "man strftime for date/time specifiers)" @@ -6967,6 +7001,30 @@ msgstr "La fonction système \"%s\" n'est pas disponible" msgid "Resource usage (see \"man getrusage\" for help):" msgstr "Utilisation des ressources (voir \"man getrusage\" pour de l'aide) :" +msgid "Automatic backup written before /theme apply" +msgstr "Sauvegarde automatique écrite avant /theme apply" + +#, c-format +msgid "%sTheme: option \"%s\" not found, skipped" +msgstr "%sThème : option \"%s\" non trouvée, ignorée" + +#, c-format +msgid "%sTheme: option \"%s\" is not themable, skipped" +msgstr "%sThème : l'option \"%s\" n'est pas personnalisable par thème, ignorée" + +#, c-format +msgid "" +"%sUnable to create theme backup; aborting apply (disable option " +"weechat.look.theme_backup to force)" +msgstr "" +"%sImpossible de créer une sauvegarde du thème ; abandon de l'application " +"(désactivez l'option weechat.look.theme_backup pour forcer)" + +#, c-format +msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" +msgstr "" +"État précédent sauvé sous le thème \"%s\" ; pour restaurer : /theme apply %s" + #, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "%sErreur de mise à jour de WeeChat avec le fichier \"%s\" :" diff --git a/po/hu.po b/po/hu.po index c13788e06..db1541e9c 100644 --- a/po/hu.po +++ b/po/hu.po @@ -22,7 +22,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 14:09+0200\n" +"POT-Creation-Date: 2026-07-04 15:44+0200\n" "PO-Revision-Date: 2026-03-08 08:59+0100\n" "Last-Translator: Andras Voroskoi \n" "Language-Team: Hungarian \n" @@ -3546,7 +3546,7 @@ msgid "manage color themes" msgstr "Aliaszok listája:\n" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list] || info " +msgid "[list] || apply || info " msgstr "" #, fuzzy @@ -3555,6 +3555,11 @@ msgid "" "theme is marked with \"->\"" msgstr "Aliaszok listája:\n" +msgid "" +"raw[apply]: apply a theme (set every themable option to the value from the " +"theme)" +msgstr "" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -3570,6 +3575,14 @@ msgid "" "directory \"themes\" inside the WeeChat configuration directory." msgstr "" +msgid "" +"By default, `/theme apply` command creates a backup of current themable " +"values in directory \"themes\" before applying (file name: \"backup-" +".theme\"); the previous state can be restored with: `/theme apply " +"backup-`. This is controlled by the option " +"weechat.look.theme_backup." +msgstr "" + #, fuzzy msgid "toggle value of a config option" msgstr "Nem található az opció\n" @@ -4971,6 +4984,14 @@ msgid "" "startup" msgstr "" +msgid "" +"create a backup theme file with the current themable options before applying " +"a theme with command /theme; if the backup file cannot be written, the apply " +"is aborted (no option is changed); the backup file is written to directory " +"\"themes\" inside the WeeChat configuration directory and can be restored " +"with the command: `/theme apply backup-`" +msgstr "" + #, fuzzy msgid "" "time format for dates converted to strings and displayed in messages (see " @@ -5931,6 +5952,27 @@ msgstr "%s a \"%s\" modul nem található\n" msgid "Resource usage (see \"man getrusage\" for help):" msgstr "" +msgid "Automatic backup written before /theme apply" +msgstr "" + +#, fuzzy, c-format +msgid "%sTheme: option \"%s\" not found, skipped" +msgstr "%s a \"%s\" szerver nem található\n" + +#, fuzzy, c-format +msgid "%sTheme: option \"%s\" is not themable, skipped" +msgstr "%s a \"%s\" szerver nem található\n" + +#, c-format +msgid "" +"%sUnable to create theme backup; aborting apply (disable option " +"weechat.look.theme_backup to force)" +msgstr "" + +#, c-format +msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" +msgstr "" + #, fuzzy, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "WeeChat frissítése...\n" diff --git a/po/it.po b/po/it.po index c3e1ca406..e68da77fc 100644 --- a/po/it.po +++ b/po/it.po @@ -22,7 +22,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 14:09+0200\n" +"POT-Creation-Date: 2026-07-04 15:44+0200\n" "PO-Revision-Date: 2026-05-30 14:02+0200\n" "Last-Translator: Esteban I. Ruiz Moreno \n" "Language-Team: Italian \n" @@ -3723,7 +3723,7 @@ msgid "manage color themes" msgstr "elenco degli elementi barra" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list] || info " +msgid "[list] || apply || info " msgstr "" #, fuzzy @@ -3733,6 +3733,11 @@ msgid "" "theme is marked with \"->\"" msgstr "elenco dei filtri" +msgid "" +"raw[apply]: apply a theme (set every themable option to the value from the " +"theme)" +msgstr "" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -3748,6 +3753,14 @@ msgid "" "directory \"themes\" inside the WeeChat configuration directory." msgstr "" +msgid "" +"By default, `/theme apply` command creates a backup of current themable " +"values in directory \"themes\" before applying (file name: \"backup-" +".theme\"); the previous state can be restored with: `/theme apply " +"backup-`. This is controlled by the option " +"weechat.look.theme_backup." +msgstr "" + #, fuzzy #| msgid "values for a configuration option" msgid "toggle value of a config option" @@ -5391,6 +5404,14 @@ msgid "" "startup" msgstr "" +msgid "" +"create a backup theme file with the current themable options before applying " +"a theme with command /theme; if the backup file cannot be written, the apply " +"is aborted (no option is changed); the backup file is written to directory " +"\"themes\" inside the WeeChat configuration directory and can be restored " +"with the command: `/theme apply backup-`" +msgstr "" + msgid "" "time format for dates converted to strings and displayed in messages (see " "man strftime for date/time specifiers)" @@ -6403,6 +6424,29 @@ msgstr "%s: errore: il dizionario \"%s\" non è disponibile su questo sistema" msgid "Resource usage (see \"man getrusage\" for help):" msgstr "Uso della memoria (consultare \"man mallinfo\" per aiuto):" +msgid "Automatic backup written before /theme apply" +msgstr "" + +#, fuzzy, c-format +#| msgid "%sKey \"%s\" not found" +msgid "%sTheme: option \"%s\" not found, skipped" +msgstr "%sTasto \"%s\" non trovato" + +#, fuzzy, c-format +#| msgid "%s: script \"%s\" is not installed" +msgid "%sTheme: option \"%s\" is not themable, skipped" +msgstr "%s: script \"%s\" non installato" + +#, c-format +msgid "" +"%sUnable to create theme backup; aborting apply (disable option " +"weechat.look.theme_backup to force)" +msgstr "" + +#, c-format +msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" +msgstr "" + #, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "%sErrore durante l'aggiornamento di WeeChat con il file \"%s\":" diff --git a/po/ja.po b/po/ja.po index 07a3da743..cbe3a18ac 100644 --- a/po/ja.po +++ b/po/ja.po @@ -22,7 +22,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 14:09+0200\n" +"POT-Creation-Date: 2026-07-04 15:44+0200\n" "PO-Revision-Date: 2026-05-30 14:02+0200\n" "Last-Translator: AYANOKOUZI, Ryuunosuke \n" "Language-Team: Japanese \n" @@ -3807,7 +3807,7 @@ msgid "manage color themes" msgstr "バー要素のリスト" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list] || info " +msgid "[list] || apply || info " msgstr "" #, fuzzy @@ -3817,6 +3817,11 @@ msgid "" "theme is marked with \"->\"" msgstr "フィルタのリスト" +msgid "" +"raw[apply]: apply a theme (set every themable option to the value from the " +"theme)" +msgstr "" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -3833,6 +3838,14 @@ msgid "" "directory \"themes\" inside the WeeChat configuration directory." msgstr "" +msgid "" +"By default, `/theme apply` command creates a backup of current themable " +"values in directory \"themes\" before applying (file name: \"backup-" +".theme\"); the previous state can be restored with: `/theme apply " +"backup-`. This is controlled by the option " +"weechat.look.theme_backup." +msgstr "" + #, fuzzy #| msgid "values for a configuration option" msgid "toggle value of a config option" @@ -5552,6 +5565,14 @@ msgid "" "startup" msgstr "" +msgid "" +"create a backup theme file with the current themable options before applying " +"a theme with command /theme; if the backup file cannot be written, the apply " +"is aborted (no option is changed); the backup file is written to directory " +"\"themes\" inside the WeeChat configuration directory and can be restored " +"with the command: `/theme apply backup-`" +msgstr "" + msgid "" "time format for dates converted to strings and displayed in messages (see " "man strftime for date/time specifiers)" @@ -6580,6 +6601,29 @@ msgstr "%s: エラー: 辞書 \"%s\" がシステム上に見つかりません" msgid "Resource usage (see \"man getrusage\" for help):" msgstr "メモリ使用量 (ヘルプを見るには \"man mallinfo\" を参照してください):" +msgid "Automatic backup written before /theme apply" +msgstr "" + +#, fuzzy, c-format +#| msgid "%sKey \"%s\" not found" +msgid "%sTheme: option \"%s\" not found, skipped" +msgstr "%sキー \"%s\" が見つかりません" + +#, fuzzy, c-format +#| msgid "%s: script \"%s\" is not installed" +msgid "%sTheme: option \"%s\" is not themable, skipped" +msgstr "%s: スクリプト \"%s\" はインストールされていません" + +#, c-format +msgid "" +"%sUnable to create theme backup; aborting apply (disable option " +"weechat.look.theme_backup to force)" +msgstr "" + +#, c-format +msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" +msgstr "" + #, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "%sファイル \"%s\" で WeeChat のアップグレード中にエラー:" diff --git a/po/pl.po b/po/pl.po index 9f8f6ebe6..cc9269af5 100644 --- a/po/pl.po +++ b/po/pl.po @@ -23,7 +23,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 14:09+0200\n" +"POT-Creation-Date: 2026-07-04 15:44+0200\n" "PO-Revision-Date: 2026-06-28 08:55+0200\n" "Last-Translator: Krzysztof Korościk \n" "Language-Team: Polish \n" @@ -3917,7 +3917,7 @@ msgid "manage color themes" msgstr "zarządza niestandardowymi elementami pasków" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list] || info " +msgid "[list] || apply || info " msgstr "" #, fuzzy @@ -3931,6 +3931,11 @@ msgstr "" "raw[list]: wyświetl pośredników (bez podania argumentu wyświetlana jest ta " "lista)" +msgid "" +"raw[apply]: apply a theme (set every themable option to the value from the " +"theme)" +msgstr "" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -3947,6 +3952,14 @@ msgid "" "directory \"themes\" inside the WeeChat configuration directory." msgstr "" +msgid "" +"By default, `/theme apply` command creates a backup of current themable " +"values in directory \"themes\" before applying (file name: \"backup-" +".theme\"); the previous state can be restored with: `/theme apply " +"backup-`. This is controlled by the option " +"weechat.look.theme_backup." +msgstr "" + msgid "toggle value of a config option" msgstr "przełącza wartość opcji konfiguracyjnej" @@ -5708,6 +5721,14 @@ msgid "" "startup" msgstr "" +msgid "" +"create a backup theme file with the current themable options before applying " +"a theme with command /theme; if the backup file cannot be written, the apply " +"is aborted (no option is changed); the backup file is written to directory " +"\"themes\" inside the WeeChat configuration directory and can be restored " +"with the command: `/theme apply backup-`" +msgstr "" + msgid "" "time format for dates converted to strings and displayed in messages (see " "man strftime for date/time specifiers)" @@ -6756,6 +6777,29 @@ msgstr "Funkcja systemowa \"%s\" nie jest dostępna" msgid "Resource usage (see \"man getrusage\" for help):" msgstr "Zużycie zasobów (zobacz \"man getrusage\"):" +msgid "Automatic backup written before /theme apply" +msgstr "" + +#, fuzzy, c-format +#| msgid "%sKey \"%s\" not found" +msgid "%sTheme: option \"%s\" not found, skipped" +msgstr "%sKlawisz \"%s\" nie znaleziony" + +#, fuzzy, c-format +#| msgid "%s: script \"%s\" is not installed" +msgid "%sTheme: option \"%s\" is not themable, skipped" +msgstr "%s: skrypt \"%s\" nie zainstalowany" + +#, c-format +msgid "" +"%sUnable to create theme backup; aborting apply (disable option " +"weechat.look.theme_backup to force)" +msgstr "" + +#, c-format +msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" +msgstr "" + #, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "%sBłąd przy uaktualnianiu WeeChat z użyciem pliku \"%s\":" diff --git a/po/pt.po b/po/pt.po index 9f7a28e76..6a979787d 100644 --- a/po/pt.po +++ b/po/pt.po @@ -22,7 +22,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 14:09+0200\n" +"POT-Creation-Date: 2026-07-04 15:44+0200\n" "PO-Revision-Date: 2026-06-28 08:48+0200\n" "Last-Translator: Vasco Almeida \n" "Language-Team: Portuguese \n" @@ -3816,7 +3816,7 @@ msgid "manage color themes" msgstr "lista de itens da barra" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list] || info " +msgid "[list] || apply || info " msgstr "" #, fuzzy @@ -3826,6 +3826,11 @@ msgid "" "theme is marked with \"->\"" msgstr "lista de filtros" +msgid "" +"raw[apply]: apply a theme (set every themable option to the value from the " +"theme)" +msgstr "" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -3842,6 +3847,14 @@ msgid "" "directory \"themes\" inside the WeeChat configuration directory." msgstr "" +msgid "" +"By default, `/theme apply` command creates a backup of current themable " +"values in directory \"themes\" before applying (file name: \"backup-" +".theme\"); the previous state can be restored with: `/theme apply " +"backup-`. This is controlled by the option " +"weechat.look.theme_backup." +msgstr "" + #, fuzzy #| msgid "values for a configuration option" msgid "toggle value of a config option" @@ -5581,6 +5594,14 @@ msgid "" "startup" msgstr "" +msgid "" +"create a backup theme file with the current themable options before applying " +"a theme with command /theme; if the backup file cannot be written, the apply " +"is aborted (no option is changed); the backup file is written to directory " +"\"themes\" inside the WeeChat configuration directory and can be restored " +"with the command: `/theme apply backup-`" +msgstr "" + msgid "" "time format for dates converted to strings and displayed in messages (see " "man strftime for date/time specifiers)" @@ -6636,6 +6657,29 @@ msgstr "%s: erro: o dicionário \"%s\" não está disponível no sistema" msgid "Resource usage (see \"man getrusage\" for help):" msgstr "Utilização de memória (ver \"man mallingo\" para obter ajuda):" +msgid "Automatic backup written before /theme apply" +msgstr "" + +#, fuzzy, c-format +#| msgid "%sKey \"%s\" not found" +msgid "%sTheme: option \"%s\" not found, skipped" +msgstr "%sTecla \"%s\" não encontrada" + +#, fuzzy, c-format +#| msgid "%s: script \"%s\" is not installed" +msgid "%sTheme: option \"%s\" is not themable, skipped" +msgstr "%s: o script \"%s\" não está instalado" + +#, c-format +msgid "" +"%sUnable to create theme backup; aborting apply (disable option " +"weechat.look.theme_backup to force)" +msgstr "" + +#, c-format +msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" +msgstr "" + #, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "%sErro ao atualizar o WeeChat com o ficheiro \"%s\":" diff --git a/po/pt_BR.po b/po/pt_BR.po index e5797facb..e905f68dc 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -46,7 +46,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 14:09+0200\n" +"POT-Creation-Date: 2026-07-04 15:44+0200\n" "PO-Revision-Date: 2026-06-28 08:49+0200\n" "Last-Translator: Érico Nogueira \n" "Language-Team: Portuguese (Brazil) \n" @@ -3737,7 +3737,7 @@ msgid "manage color themes" msgstr "lista de itens da barra" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list] || info " +msgid "[list] || apply || info " msgstr "" #, fuzzy @@ -3747,6 +3747,11 @@ msgid "" "theme is marked with \"->\"" msgstr "lista de filtros" +msgid "" +"raw[apply]: apply a theme (set every themable option to the value from the " +"theme)" +msgstr "" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -3762,6 +3767,14 @@ msgid "" "directory \"themes\" inside the WeeChat configuration directory." msgstr "" +msgid "" +"By default, `/theme apply` command creates a backup of current themable " +"values in directory \"themes\" before applying (file name: \"backup-" +".theme\"); the previous state can be restored with: `/theme apply " +"backup-`. This is controlled by the option " +"weechat.look.theme_backup." +msgstr "" + #, fuzzy #| msgid "values for a configuration option" msgid "toggle value of a config option" @@ -5339,6 +5352,14 @@ msgid "" "startup" msgstr "" +msgid "" +"create a backup theme file with the current themable options before applying " +"a theme with command /theme; if the backup file cannot be written, the apply " +"is aborted (no option is changed); the backup file is written to directory " +"\"themes\" inside the WeeChat configuration directory and can be restored " +"with the command: `/theme apply backup-`" +msgstr "" + msgid "" "time format for dates converted to strings and displayed in messages (see " "man strftime for date/time specifiers)" @@ -6305,6 +6326,28 @@ msgstr "%s: erro: dicionário \"%s\" não está disponível em seu sistema" msgid "Resource usage (see \"man getrusage\" for help):" msgstr "Uso de memória (veja \"man mallinfo\" para ajuda):" +msgid "Automatic backup written before /theme apply" +msgstr "" + +#, fuzzy, c-format +#| msgid "%sKey \"%s\" not found" +msgid "%sTheme: option \"%s\" not found, skipped" +msgstr "%sTecla \"%s\" não encontrada" + +#, fuzzy, c-format +msgid "%sTheme: option \"%s\" is not themable, skipped" +msgstr "%s%s: script \"%s\" não carregado" + +#, c-format +msgid "" +"%sUnable to create theme backup; aborting apply (disable option " +"weechat.look.theme_backup to force)" +msgstr "" + +#, c-format +msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" +msgstr "" + #, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "" diff --git a/po/ru.po b/po/ru.po index 98c50112a..554c31de9 100644 --- a/po/ru.po +++ b/po/ru.po @@ -23,7 +23,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 14:09+0200\n" +"POT-Creation-Date: 2026-07-04 15:44+0200\n" "PO-Revision-Date: 2026-03-08 08:59+0100\n" "Last-Translator: Aleksey V Zapparov AKA ixti \n" "Language-Team: Russian \n" @@ -3570,7 +3570,7 @@ msgid "manage color themes" msgstr "Список сокращений:\n" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list] || info " +msgid "[list] || apply || info " msgstr "" #, fuzzy @@ -3579,6 +3579,11 @@ msgid "" "theme is marked with \"->\"" msgstr "Список сокращений:\n" +msgid "" +"raw[apply]: apply a theme (set every themable option to the value from the " +"theme)" +msgstr "" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -3594,6 +3599,14 @@ msgid "" "directory \"themes\" inside the WeeChat configuration directory." msgstr "" +msgid "" +"By default, `/theme apply` command creates a backup of current themable " +"values in directory \"themes\" before applying (file name: \"backup-" +".theme\"); the previous state can be restored with: `/theme apply " +"backup-`. This is controlled by the option " +"weechat.look.theme_backup." +msgstr "" + #, fuzzy msgid "toggle value of a config option" msgstr "Не найден параметр\n" @@ -5005,6 +5018,14 @@ msgid "" "startup" msgstr "" +msgid "" +"create a backup theme file with the current themable options before applying " +"a theme with command /theme; if the backup file cannot be written, the apply " +"is aborted (no option is changed); the backup file is written to directory " +"\"themes\" inside the WeeChat configuration directory and can be restored " +"with the command: `/theme apply backup-`" +msgstr "" + #, fuzzy msgid "" "time format for dates converted to strings and displayed in messages (see " @@ -5968,6 +5989,27 @@ msgstr "%s plugin \"%s\" не найден\n" msgid "Resource usage (see \"man getrusage\" for help):" msgstr "" +msgid "Automatic backup written before /theme apply" +msgstr "" + +#, fuzzy, c-format +msgid "%sTheme: option \"%s\" not found, skipped" +msgstr "%s сервер \"%s\" не найден\n" + +#, fuzzy, c-format +msgid "%sTheme: option \"%s\" is not themable, skipped" +msgstr "%s сервер \"%s\" не найден\n" + +#, c-format +msgid "" +"%sUnable to create theme backup; aborting apply (disable option " +"weechat.look.theme_backup to force)" +msgstr "" + +#, c-format +msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" +msgstr "" + #, fuzzy, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "Обновляю WeeChat...\n" diff --git a/po/sr.po b/po/sr.po index 664c3be43..4fa3ed949 100644 --- a/po/sr.po +++ b/po/sr.po @@ -22,7 +22,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 14:09+0200\n" +"POT-Creation-Date: 2026-07-04 15:44+0200\n" "PO-Revision-Date: 2026-06-28 08:52+0200\n" "Last-Translator: Ivan Pešić \n" "Language-Team: Serbian \n" @@ -3894,7 +3894,7 @@ msgid "manage color themes" msgstr "управљање прилагођеним ставкама траке" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list] || info " +msgid "[list] || apply || info " msgstr "" #, fuzzy @@ -3908,6 +3908,11 @@ msgstr "" "raw[list]: листа сервера релеја удаљених (без аргумента се приказује ова " "листа)" +msgid "" +"raw[apply]: apply a theme (set every themable option to the value from the " +"theme)" +msgstr "" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -3924,6 +3929,14 @@ msgid "" "directory \"themes\" inside the WeeChat configuration directory." msgstr "" +msgid "" +"By default, `/theme apply` command creates a backup of current themable " +"values in directory \"themes\" before applying (file name: \"backup-" +".theme\"); the previous state can be restored with: `/theme apply " +"backup-`. This is controlled by the option " +"weechat.look.theme_backup." +msgstr "" + msgid "toggle value of a config option" msgstr "пребацује вредност опције конфигурације" @@ -5684,6 +5697,14 @@ msgid "" "startup" msgstr "" +msgid "" +"create a backup theme file with the current themable options before applying " +"a theme with command /theme; if the backup file cannot be written, the apply " +"is aborted (no option is changed); the backup file is written to directory " +"\"themes\" inside the WeeChat configuration directory and can be restored " +"with the command: `/theme apply backup-`" +msgstr "" + msgid "" "time format for dates converted to strings and displayed in messages (see " "man strftime for date/time specifiers)" @@ -6738,6 +6759,29 @@ msgstr "Системска функција „%s” није доступна" msgid "Resource usage (see \"man getrusage\" for help):" msgstr "Употреба ресурса (за помоћ погледајте „man getrusage”):" +msgid "Automatic backup written before /theme apply" +msgstr "" + +#, fuzzy, c-format +#| msgid "%sKey \"%s\" not found" +msgid "%sTheme: option \"%s\" not found, skipped" +msgstr "%sНије пронађен тастер „%s”" + +#, fuzzy, c-format +#| msgid "%s: script \"%s\" is not installed" +msgid "%sTheme: option \"%s\" is not themable, skipped" +msgstr "%s: скрипта „%s” није инсталирана" + +#, c-format +msgid "" +"%sUnable to create theme backup; aborting apply (disable option " +"weechat.look.theme_backup to force)" +msgstr "" + +#, c-format +msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" +msgstr "" + #, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "%sГрешка приликом ажурирања програма WeeChat фајлом „%s”:" diff --git a/po/tr.po b/po/tr.po index defad9925..8f3e3f2a0 100644 --- a/po/tr.po +++ b/po/tr.po @@ -23,7 +23,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 14:09+0200\n" +"POT-Creation-Date: 2026-07-04 15:44+0200\n" "PO-Revision-Date: 2026-06-28 08:53+0200\n" "Last-Translator: Emir SARI \n" "Language-Team: Turkish \n" @@ -3753,7 +3753,7 @@ msgid "manage color themes" msgstr "özel çubuk ögelerini yönet" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list] || info " +msgid "[list] || apply || info " msgstr "" #, fuzzy @@ -3763,6 +3763,11 @@ msgid "" "theme is marked with \"->\"" msgstr "süzgeçlerin listesi" +msgid "" +"raw[apply]: apply a theme (set every themable option to the value from the " +"theme)" +msgstr "" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -3779,6 +3784,14 @@ msgid "" "directory \"themes\" inside the WeeChat configuration directory." msgstr "" +msgid "" +"By default, `/theme apply` command creates a backup of current themable " +"values in directory \"themes\" before applying (file name: \"backup-" +".theme\"); the previous state can be restored with: `/theme apply " +"backup-`. This is controlled by the option " +"weechat.look.theme_backup." +msgstr "" + msgid "toggle value of a config option" msgstr "bir yapılandırma seçeneğinin değerini aç/kapat" @@ -5504,6 +5517,14 @@ msgid "" "startup" msgstr "" +msgid "" +"create a backup theme file with the current themable options before applying " +"a theme with command /theme; if the backup file cannot be written, the apply " +"is aborted (no option is changed); the backup file is written to directory " +"\"themes\" inside the WeeChat configuration directory and can be restored " +"with the command: `/theme apply backup-`" +msgstr "" + msgid "" "time format for dates converted to strings and displayed in messages (see " "man strftime for date/time specifiers)" @@ -6538,6 +6559,29 @@ msgstr "Sistem işlevi \"%s\" kullanılabilir değil" msgid "Resource usage (see \"man getrusage\" for help):" msgstr "Özkaynak kullanımı (yardım için bkz. \"man getrusage\"):" +msgid "Automatic backup written before /theme apply" +msgstr "" + +#, fuzzy, c-format +#| msgid "%sKey \"%s\" not found" +msgid "%sTheme: option \"%s\" not found, skipped" +msgstr "%s\"%s\" düğmesi bulunamadı" + +#, fuzzy, c-format +#| msgid "%s: script \"%s\" is not installed" +msgid "%sTheme: option \"%s\" is not themable, skipped" +msgstr "%s: \"%s\" betiği kurulu değil" + +#, c-format +msgid "" +"%sUnable to create theme backup; aborting apply (disable option " +"weechat.look.theme_backup to force)" +msgstr "" + +#, c-format +msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" +msgstr "" + #, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "%sWeeChat'i \"%s\" dosyası ile yükseltirken hata:" diff --git a/po/weechat.pot b/po/weechat.pot index 14d57f77d..236688686 100644 --- a/po/weechat.pot +++ b/po/weechat.pot @@ -23,7 +23,7 @@ msgid "" msgstr "" "Project-Id-Version: WeeChat\n" "Report-Msgid-Bugs-To: flashcode@flashtux.org\n" -"POT-Creation-Date: 2026-07-04 14:09+0200\n" +"POT-Creation-Date: 2026-07-04 15:44+0200\n" "PO-Revision-Date: 2014-08-16 10:27+0200\n" "Last-Translator: Sébastien Helleu \n" "Language-Team: weechat-dev \n" @@ -3339,7 +3339,7 @@ msgid "manage color themes" msgstr "" #. TRANSLATORS: only text between angle brackets (eg: "") may be translated -msgid "[list] || info " +msgid "[list] || apply || info " msgstr "" msgid "" @@ -3347,6 +3347,11 @@ msgid "" "theme is marked with \"->\"" msgstr "" +msgid "" +"raw[apply]: apply a theme (set every themable option to the value from the " +"theme)" +msgstr "" + msgid "" "raw[info]: display details on a theme (name, description, creation date, " "WeeChat version, number of option overrides)" @@ -3361,6 +3366,14 @@ msgid "" "directory \"themes\" inside the WeeChat configuration directory." msgstr "" +msgid "" +"By default, `/theme apply` command creates a backup of current themable " +"values in directory \"themes\" before applying (file name: \"backup-" +".theme\"); the previous state can be restored with: `/theme apply " +"backup-`. This is controlled by the option " +"weechat.look.theme_backup." +msgstr "" + msgid "toggle value of a config option" msgstr "" @@ -4678,6 +4691,14 @@ msgid "" "startup" msgstr "" +msgid "" +"create a backup theme file with the current themable options before applying " +"a theme with command /theme; if the backup file cannot be written, the apply " +"is aborted (no option is changed); the backup file is written to directory " +"\"themes\" inside the WeeChat configuration directory and can be restored " +"with the command: `/theme apply backup-`" +msgstr "" + msgid "" "time format for dates converted to strings and displayed in messages (see " "man strftime for date/time specifiers)" @@ -5529,6 +5550,27 @@ msgstr "" msgid "Resource usage (see \"man getrusage\" for help):" msgstr "" +msgid "Automatic backup written before /theme apply" +msgstr "" + +#, c-format +msgid "%sTheme: option \"%s\" not found, skipped" +msgstr "" + +#, c-format +msgid "%sTheme: option \"%s\" is not themable, skipped" +msgstr "" + +#, c-format +msgid "" +"%sUnable to create theme backup; aborting apply (disable option " +"weechat.look.theme_backup to force)" +msgstr "" + +#, c-format +msgid "Previous state saved as theme \"%s\"; to restore: /theme apply %s" +msgstr "" + #, c-format msgid "%sError upgrading WeeChat with file \"%s\":" msgstr "" diff --git a/src/core/core-command.c b/src/core/core-command.c index 13278bc34..55865af09 100644 --- a/src/core/core-command.c +++ b/src/core/core-command.c @@ -7231,6 +7231,13 @@ COMMAND_CALLBACK(theme) return WEECHAT_RC_OK; } + /* "/theme apply ": apply a theme */ + if (string_strcmp (argv[1], "apply") == 0) + { + COMMAND_MIN_ARGS(3, "apply"); + return theme_apply (argv[2]); + } + /* "/theme info ": show details about a theme */ if (string_strcmp (argv[1], "info") == 0) { @@ -9927,10 +9934,13 @@ command_init (void) N_("manage color themes"), /* TRANSLATORS: only text between angle brackets (eg: "") may be translated */ N_("[list]" + " || apply " " || info "), CMD_ARGS_DESC( N_("raw[list]: list registered themes (default action with no " "argument); active theme is marked with \"->\""), + N_("raw[apply]: apply a theme (set every themable option to the " + "value from the theme)"), N_("raw[info]: display details on a theme (name, description, " "creation date, WeeChat version, number of option overrides)"), N_("name: name of a theme"), @@ -9938,8 +9948,15 @@ command_init (void) N_("Themes are named bundles of option overrides. Built-in themes " "are registered in memory by core/plugins/scripts; user themes " "are read from files in directory \"themes\" inside the WeeChat " - "configuration directory.")), + "configuration directory."), + "", + N_("By default, `/theme apply` command creates a backup of current " + "themable values in directory \"themes\" before applying " + "(file name: \"backup-.theme\"); the previous " + "state can be restored with: `/theme apply backup-`. " + "This is controlled by the option weechat.look.theme_backup.")), "list" + " || apply" " || info", &command_theme, NULL, NULL); hook_command ( diff --git a/src/core/core-config.c b/src/core/core-config.c index dfebaf43e..5815304c3 100644 --- a/src/core/core-config.c +++ b/src/core/core-config.c @@ -224,6 +224,7 @@ struct t_config_option *config_look_separator_vertical = NULL; struct t_config_option *config_look_tab_whitespace_char = NULL; struct t_config_option *config_look_tab_width = NULL; struct t_config_option *config_look_theme = NULL; +struct t_config_option *config_look_theme_backup = NULL; struct t_config_option *config_look_time_format = NULL; struct t_config_option *config_look_whitespace_char = NULL; struct t_config_option *config_look_window_auto_zoom = NULL; @@ -4437,6 +4438,18 @@ config_weechat_init_options (void) "only, the theme is not re-applied at startup"), NULL, 0, 0, "", NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + config_look_theme_backup = config_file_new_option ( + weechat_config_file, weechat_config_section_look, + "theme_backup", "boolean", + N_("create a backup theme file with the current themable " + "options before applying a theme with command /theme; if " + "the backup file cannot be written, the apply is aborted " + "(no option is changed); the backup file is written to " + "directory \"themes\" inside the WeeChat configuration " + "directory and can be restored with the command: `/theme apply " + "backup-`"), + NULL, 0, 0, "on", NULL, 0, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); config_look_time_format = config_file_new_option ( weechat_config_file, weechat_config_section_look, "time_format", "string", diff --git a/src/core/core-config.h b/src/core/core-config.h index 68396a31f..fe5839084 100644 --- a/src/core/core-config.h +++ b/src/core/core-config.h @@ -278,6 +278,7 @@ extern struct t_config_option *config_look_separator_vertical; extern struct t_config_option *config_look_tab_whitespace_char; extern struct t_config_option *config_look_tab_width; extern struct t_config_option *config_look_theme; +extern struct t_config_option *config_look_theme_backup; extern struct t_config_option *config_look_time_format; extern struct t_config_option *config_look_whitespace_char; extern struct t_config_option *config_look_window_auto_zoom; diff --git a/src/core/core-theme.c b/src/core/core-theme.c index 957e09d24..4873e81ae 100644 --- a/src/core/core-theme.c +++ b/src/core/core-theme.c @@ -25,16 +25,25 @@ #include "config.h" #endif +#include #include #include +#include #include #include "weechat.h" #include "core-arraylist.h" +#include "core-config.h" +#include "core-config-file.h" +#include "core-dir.h" #include "core-hashtable.h" +#include "core-hook.h" #include "core-string.h" #include "core-theme.h" #include "core-version.h" +#include "../gui/gui-chat.h" +#include "../gui/gui-color.h" +#include "../gui/gui-window.h" #include "../plugins/weechat-plugin.h" @@ -248,6 +257,289 @@ theme_list (void) return list; } +/* + * Build the on-disk path for a user theme: + * "/themes/.theme". + * + * Return NULL on error. + * + * Note: result must be freed after use. + */ + +char * +theme_user_file_path (const char *name) +{ + char *path = NULL; + + if (!name || !name[0]) + return NULL; + string_asprintf (&path, "%s/themes/%s.theme", + weechat_config_dir, name); + return path; +} + +/* + * Build a unique backup theme name "backup-YYYYMMDD-HHMMSS-uuuuuu". + * + * Return NULL on error. + * + * Note: result must be freed after use. + */ + +char * +theme_make_backup_name (void) +{ + struct timeval tv; + struct tm *local_time; + char buf[128]; + + if (gettimeofday (&tv, NULL) != 0) + return NULL; + local_time = localtime (&tv.tv_sec); + if (!local_time) + return NULL; + snprintf (buf, sizeof (buf), + "backup-%04d%02d%02d-%02d%02d%02d-%06ld", + local_time->tm_year + 1900, + local_time->tm_mon + 1, + local_time->tm_mday, + local_time->tm_hour, + local_time->tm_min, + local_time->tm_sec, + (long)tv.tv_usec); + return strdup (buf); +} + +/* + * Write a full snapshot of every themable option to a .theme file at + * "/themes/.theme". + * + * The themes directory is created if missing. The file contains an + * [info] section (name, description, date, weechat version) followed by + * an [options] section listing every themable option's current value. + * + * Return: + * 1: success + * 0: error + */ + +int +theme_write_file_full (const char *name, const char *description) +{ + char *path, *dir, *value, *now; + FILE *file; + struct t_config_file *ptr_config; + struct t_config_section *ptr_section; + struct t_config_option *ptr_option; + + if (!name || !name[0]) + return 0; + + path = NULL; + dir = NULL; + string_asprintf (&dir, "%s/themes", weechat_config_dir); + if (!dir) + return 0; + dir_mkdir (dir, 0755); + free (dir); + + path = theme_user_file_path (name); + if (!path) + return 0; + + file = fopen (path, "w"); + free (path); + if (!file) + return 0; + + now = theme_format_now (); + fprintf (file, "[info]\n"); + fprintf (file, "name = \"%s\"\n", name); + fprintf (file, "description = \"%s\"\n", + (description) ? description : ""); + fprintf (file, "date = \"%s\"\n", (now) ? now : ""); + fprintf (file, "weechat = \"%s\"\n", version_get_version ()); + fprintf (file, "\n[options]\n"); + free (now); + + for (ptr_config = config_files; ptr_config; + ptr_config = ptr_config->next_config) + { + for (ptr_section = ptr_config->sections; ptr_section; + ptr_section = ptr_section->next_section) + { + for (ptr_option = ptr_section->options; ptr_option; + ptr_option = ptr_option->next_option) + { + if (!ptr_option->themable) + continue; + value = config_file_option_value_to_string ( + ptr_option, 0, 0, 1); + fprintf (file, "%s.%s.%s = %s\n", + ptr_config->name, ptr_section->name, + ptr_option->name, + (value) ? value : "\"\""); + free (value); + } + } + } + + fclose (file); + return 1; +} + +/* + * Create a timestamped backup theme file with the current themable state. + * + * Return the backup name, NULL on failure. + * + * Note: result must be freed after use. + */ + +char * +theme_make_backup (void) +{ + char *name; + + name = theme_make_backup_name (); + if (!name) + return NULL; + if (!theme_write_file_full ( + name, + _("Automatic backup written before /theme apply"))) + { + free (name); + return NULL; + } + return name; +} + +/* + * Apply one override entry (callback for hashtable_map during apply). + * + * Refuse entries pointing to options that do not exist or that are not + * themable, logging a warning to the core buffer; the apply itself still + * proceeds with the remaining entries. + */ + +void +theme_apply_set_option_cb (void *data, + struct t_hashtable *hashtable, + const void *key, + const void *value) +{ + struct t_config_option *option = NULL; + + /* make C compiler happy */ + (void) data; + (void) hashtable; + + config_file_search_with_string ((const char *)key, + NULL, NULL, &option, NULL); + if (!option) + { + gui_chat_printf (NULL, + _("%sTheme: option \"%s\" not found, skipped"), + gui_chat_prefix[GUI_CHAT_PREFIX_ERROR], + (const char *)key); + return; + } + if (!option->themable) + { + gui_chat_printf ( + NULL, + _("%sTheme: option \"%s\" is not themable, skipped"), + gui_chat_prefix[GUI_CHAT_PREFIX_ERROR], + (const char *)key); + return; + } + config_file_option_set (option, (const char *)value, 1); +} + +/* + * Apply a theme registered in memory. + * + * If weechat.look.theme_backup is on (and the target name does not begin + * with "backup-"), a backup file is written first; on backup failure the + * apply is aborted before any option is changed. + * + * Iterate the theme's overrides with theme_applying=1 so the per-option + * change callbacks skip their gui refresh; a single refresh is performed + * at the end. + * + * Return: + * WEECHAT_RC_OK: success + * WEECHAT_RC_ERROR: theme name is unknown or the backup could not be created + */ + +int +theme_apply (const char *name) +{ + struct t_theme *theme; + char *backup_name = NULL; + + if (!name || !name[0]) + return WEECHAT_RC_ERROR; + + theme = theme_search (name); + if (!theme) + { + gui_chat_printf (NULL, + _("%sTheme \"%s\" not found"), + gui_chat_prefix[GUI_CHAT_PREFIX_ERROR], + name); + return WEECHAT_RC_ERROR; + } + + /* create a backup of current themable state, if enabled */ + if (CONFIG_BOOLEAN(config_look_theme_backup) + && (strncmp (name, "backup-", 7) != 0)) + { + backup_name = theme_make_backup (); + if (!backup_name) + { + gui_chat_printf ( + NULL, + _("%sUnable to create theme backup; aborting apply " + "(disable option weechat.look.theme_backup to force)"), + gui_chat_prefix[GUI_CHAT_PREFIX_ERROR]); + return WEECHAT_RC_ERROR; + } + } + + /* apply each override; per-option refreshes are suppressed via the + theme_applying flag (see config_change_color) */ + theme_applying = 1; + hashtable_map (theme->overrides, &theme_apply_set_option_cb, NULL); + theme_applying = 0; + + /* single refresh at the end */ + if (gui_init_ok) + { + gui_color_init_weechat (); + gui_window_ask_refresh (1); + } + + /* persist the active theme label */ + config_file_option_set (config_look_theme, name, 1); + + /* tell the user about the backup */ + if (backup_name) + { + gui_chat_printf ( + NULL, + _("Previous state saved as theme \"%s\"; to restore: " + "/theme apply %s"), + backup_name, backup_name); + free (backup_name); + } + + hook_signal_send ("theme_applied", + WEECHAT_HOOK_SIGNAL_STRING, (char *)name); + + return WEECHAT_RC_OK; +} + /* * Initialize the theme subsystem. * diff --git a/src/core/core-theme.h b/src/core/core-theme.h index 52c4b295b..35af5dd8e 100644 --- a/src/core/core-theme.h +++ b/src/core/core-theme.h @@ -44,6 +44,8 @@ extern struct t_theme *theme_search (const char *name); extern struct t_theme *theme_register (const char *name, struct t_hashtable *overrides); extern struct t_arraylist *theme_list (void); +extern int theme_apply (const char *name); +extern char *theme_make_backup (void); extern void theme_init (void); extern void theme_end (void); diff --git a/tests/unit/core/test-core-theme.cpp b/tests/unit/core/test-core-theme.cpp index e493cb75a..254b355c3 100644 --- a/tests/unit/core/test-core-theme.cpp +++ b/tests/unit/core/test-core-theme.cpp @@ -29,15 +29,25 @@ extern "C" { #include +#include #include +#include +#include #include "src/core/core-arraylist.h" +#include "src/core/core-config.h" +#include "src/core/core-config-file.h" #include "src/core/core-hashtable.h" +#include "src/core/core-string.h" #include "src/core/core-theme.h" +#include "src/core/weechat.h" #include "src/plugins/plugin.h" extern char *theme_format_now (void); extern struct t_theme *theme_alloc (const char *name); extern void theme_free (struct t_theme *theme); +extern char *theme_user_file_path (const char *name); +extern char *theme_make_backup_name (void); +extern int theme_write_file_full (const char *name, const char *description); } TEST_GROUP(CoreTheme) @@ -261,6 +271,221 @@ TEST(CoreTheme, List) arraylist_free (list); } +/* + * Test functions: + * theme_user_file_path + */ + +TEST(CoreTheme, UserFilePath) +{ + char *path, *expected; + + /* NULL / empty => NULL */ + POINTERS_EQUAL(NULL, theme_user_file_path (NULL)); + POINTERS_EQUAL(NULL, theme_user_file_path ("")); + + /* "name" => "/themes/name.theme" */ + expected = NULL; + string_asprintf (&expected, "%s/themes/dark.theme", weechat_config_dir); + path = theme_user_file_path ("dark"); + CHECK(path != NULL); + STRCMP_EQUAL(expected, path); + free (path); + free (expected); +} + +/* + * Test functions: + * theme_make_backup_name + */ + +TEST(CoreTheme, MakeBackupName) +{ + char *name; + int i; + + name = theme_make_backup_name (); + CHECK(name != NULL); + + /* format: "backup-YYYYMMDD-HHMMSS-uuuuuu" (29 chars) */ + LONGS_EQUAL(29, (long)strlen (name)); + STRNCMP_EQUAL("backup-", name, 7); + + /* 8 digits for date */ + for (i = 7; i < 15; i++) + CHECK(isdigit ((unsigned char)name[i])); + CHECK(name[15] == '-'); + /* 6 digits for time */ + for (i = 16; i < 22; i++) + CHECK(isdigit ((unsigned char)name[i])); + CHECK(name[22] == '-'); + /* 6 digits for microseconds */ + for (i = 23; i < 29; i++) + CHECK(isdigit ((unsigned char)name[i])); + + free (name); +} + +/* + * Test functions: + * theme_write_file_full + */ + +TEST(CoreTheme, WriteFileFull) +{ + char *path, line[8192]; + FILE *file; + int saw_info, saw_name, saw_description, saw_date, saw_weechat; + int saw_options_section, saw_an_option; + int saw_color_code, saw_string_option, string_option_quoted; + + /* refuse empty/NULL */ + LONGS_EQUAL(0, theme_write_file_full (NULL, NULL)); + LONGS_EQUAL(0, theme_write_file_full ("", NULL)); + + /* write a valid file */ + LONGS_EQUAL(1, theme_write_file_full ("test_wrt", "a description")); + + path = theme_user_file_path ("test_wrt"); + CHECK(path != NULL); + + file = fopen (path, "r"); + CHECK(file != NULL); + + saw_info = saw_name = saw_description = saw_date = saw_weechat = 0; + saw_options_section = saw_an_option = 0; + saw_color_code = saw_string_option = string_option_quoted = 0; + while (fgets (line, sizeof (line) - 1, file)) + { + if (strncmp (line, "[info]", 6) == 0) + saw_info = 1; + else if (strncmp (line, "[options]", 9) == 0) + saw_options_section = 1; + else if (strncmp (line, "name = \"test_wrt\"", 17) == 0) + saw_name = 1; + else if (strncmp (line, "description = \"a description\"", 29) == 0) + saw_description = 1; + else if (strncmp (line, "date = \"", 8) == 0) + saw_date = 1; + else if (strncmp (line, "weechat = \"", 11) == 0) + saw_weechat = 1; + else if (saw_options_section + && (strchr (line, '=') != NULL) + && (strchr (line, '.') != NULL)) + { + saw_an_option = 1; + /* + * values must be stored verbatim: no WeeChat color code + * (byte 0x19) may leak into the file + */ + if (strchr (line, 0x19) != NULL) + saw_color_code = 1; + /* a string option value must be wrapped in double quotes */ + if (strncmp (line, "weechat.look.prefix_error = ", 28) == 0) + { + saw_string_option = 1; + string_option_quoted = (line[28] == '"') ? 1 : 0; + } + } + } + fclose (file); + + LONGS_EQUAL(1, saw_info); + LONGS_EQUAL(1, saw_name); + LONGS_EQUAL(1, saw_description); + LONGS_EQUAL(1, saw_date); + LONGS_EQUAL(1, saw_weechat); + LONGS_EQUAL(1, saw_options_section); + LONGS_EQUAL(1, saw_an_option); + /* no color codes leaked into option values */ + LONGS_EQUAL(0, saw_color_code); + /* the string option was found and its value is quoted */ + LONGS_EQUAL(1, saw_string_option); + LONGS_EQUAL(1, string_option_quoted); + + unlink (path); + free (path); +} + +/* + * Test functions: + * theme_make_backup + */ + +TEST(CoreTheme, MakeBackup) +{ + char *name, *path; + struct stat st; + + name = theme_make_backup (); + CHECK(name != NULL); + STRNCMP_EQUAL("backup-", name, 7); + LONGS_EQUAL(29, (long)strlen (name)); + + /* the backup file must exist on disk */ + path = theme_user_file_path (name); + CHECK(path != NULL); + LONGS_EQUAL(0, stat (path, &st)); + CHECK(st.st_size > 0); + + unlink (path); + free (path); + free (name); +} + +/* + * Test functions: + * theme_apply_set_option_cb + * theme_apply + */ + +TEST(CoreTheme, Apply) +{ + struct t_hashtable *overrides; + struct t_config_option *opt_prefix_error; + char *saved_prefix_error, *saved_theme_label; + int saved_backup; + + /* NULL / empty / missing name => error */ + LONGS_EQUAL(WEECHAT_RC_ERROR, theme_apply (NULL)); + LONGS_EQUAL(WEECHAT_RC_ERROR, theme_apply ("")); + LONGS_EQUAL(WEECHAT_RC_ERROR, theme_apply ("does_not_exist")); + + /* snapshot the option we will mutate + supporting state */ + opt_prefix_error = NULL; + config_file_search_with_string ("weechat.look.prefix_error", + NULL, NULL, &opt_prefix_error, NULL); + CHECK(opt_prefix_error != NULL); + saved_prefix_error = strdup (CONFIG_STRING(opt_prefix_error)); + saved_theme_label = strdup (CONFIG_STRING(config_look_theme)); + saved_backup = CONFIG_BOOLEAN(config_look_theme_backup); + + /* disable backup so the test does not touch the filesystem */ + config_file_option_set (config_look_theme_backup, "off", 1); + + /* register a theme that flips one themable option, then apply */ + overrides = make_overrides ("weechat.look.prefix_error", "TEST!", + NULL, NULL); + theme_register ("apply_test", overrides); + hashtable_free (overrides); + + LONGS_EQUAL(WEECHAT_RC_OK, theme_apply ("apply_test")); + + /* override took effect */ + STRCMP_EQUAL("TEST!", CONFIG_STRING(opt_prefix_error)); + /* active label persisted */ + STRCMP_EQUAL("apply_test", CONFIG_STRING(config_look_theme)); + + /* restore previous state */ + config_file_option_set (opt_prefix_error, saved_prefix_error, 1); + config_file_option_set (config_look_theme, saved_theme_label, 1); + config_file_option_set (config_look_theme_backup, + (saved_backup) ? "on" : "off", 1); + + free (saved_prefix_error); + free (saved_theme_label); +} + /* * Test functions: * theme_init