mirror of
https://github.com/weechat/weechat.git
synced 2026-06-12 14:14:48 +02:00
core: add /theme rename to rename a user theme file
This commit is contained in:
+1
-1
@@ -16,7 +16,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
### Added
|
||||
|
||||
- core: add `/theme` command with subcommands `list`, `apply`, `reset`, `save`, `delete`, `info`, automatic backup of current themable options before apply, and built-in "light" theme
|
||||
- core: add `/theme` command with subcommands `list`, `apply`, `reset`, `save`, `rename`, `delete`, `info`, automatic backup of current themable options before apply, and built-in "light" theme
|
||||
- core: detect terminal background on first start and automatically apply the built-in "light" theme when a light terminal is detected
|
||||
- core: add `themable` flag on configuration options (auto-set for color options; explicit opt-in for string options containing `${color:...}` references via the `type|themable` syntax)
|
||||
- core: add option weechat.look.theme (informational, set by `/theme apply`)
|
||||
|
||||
@@ -2283,6 +2283,19 @@ Reserved names (built-in theme names like `+light+` and any name
|
||||
starting with `+backup-+`) are refused. Files live at
|
||||
`+${weechat_config_dir}/themes/<name>.theme+`.
|
||||
|
||||
Rename a user theme (typical use: keep a useful automatic backup
|
||||
under a meaningful name):
|
||||
|
||||
----
|
||||
/theme rename backup-20260525-094210-123456 mybackup
|
||||
----
|
||||
|
||||
Built-in themes have no file and cannot be renamed; the target name
|
||||
cannot match a built-in name or start with `+backup-+`, and the
|
||||
target file must not already exist. The `+[info]+` `+name+` field
|
||||
inside the file is rewritten so `/theme info` reports the new name
|
||||
consistently.
|
||||
|
||||
Delete a user theme:
|
||||
|
||||
----
|
||||
|
||||
@@ -2270,6 +2270,19 @@ Reserved names (built-in theme names like `+light+` and any name
|
||||
starting with `+backup-+`) are refused. Files live at
|
||||
`+${weechat_config_dir}/themes/<name>.theme+`.
|
||||
|
||||
Rename a user theme (typical use: keep a useful automatic backup
|
||||
under a meaningful name):
|
||||
|
||||
----
|
||||
/theme rename backup-20260525-094210-123456 mybackup
|
||||
----
|
||||
|
||||
Built-in themes have no file and cannot be renamed; the target name
|
||||
cannot match a built-in name or start with `+backup-+`, and the
|
||||
target file must not already exist. The `+[info]+` `+name+` field
|
||||
inside the file is rewritten so `/theme info` reports the new name
|
||||
consistently.
|
||||
|
||||
Delete a user theme:
|
||||
|
||||
----
|
||||
|
||||
@@ -2319,6 +2319,20 @@ Les noms réservés (noms de thèmes intégrés comme `+light+` et tout nom
|
||||
commençant par `+backup-+`) sont refusés. Les fichiers sont placés
|
||||
dans `+${weechat_config_dir}/themes/<nom>.theme+`.
|
||||
|
||||
Renommer un thème utilisateur (usage typique : conserver une
|
||||
sauvegarde automatique utile sous un nom plus parlant) :
|
||||
|
||||
----
|
||||
/theme rename backup-20260525-094210-123456 maSauvegarde
|
||||
----
|
||||
|
||||
Les thèmes intégrés n'ont pas de fichier et ne peuvent pas être
|
||||
renommés ; le nom cible ne peut pas correspondre à un nom intégré ni
|
||||
commencer par `+backup-+`, et le fichier cible ne doit pas déjà
|
||||
exister. Le champ `+name+` de la section `+[info]+` à l'intérieur du
|
||||
fichier est réécrit afin que `/theme info` affiche le nouveau nom de
|
||||
manière cohérente.
|
||||
|
||||
Supprimer un thème utilisateur :
|
||||
|
||||
----
|
||||
|
||||
@@ -2525,6 +2525,19 @@ Reserved names (built-in theme names like `+light+` and any name
|
||||
starting with `+backup-+`) are refused. Files live at
|
||||
`+${weechat_config_dir}/themes/<name>.theme+`.
|
||||
|
||||
Rename a user theme (typical use: keep a useful automatic backup
|
||||
under a meaningful name):
|
||||
|
||||
----
|
||||
/theme rename backup-20260525-094210-123456 mybackup
|
||||
----
|
||||
|
||||
Built-in themes have no file and cannot be renamed; the target name
|
||||
cannot match a built-in name or start with `+backup-+`, and the
|
||||
target file must not already exist. The `+[info]+` `+name+` field
|
||||
inside the file is rewritten so `/theme info` reports the new name
|
||||
consistently.
|
||||
|
||||
Delete a user theme:
|
||||
|
||||
----
|
||||
|
||||
@@ -2461,6 +2461,19 @@ Reserved names (built-in theme names like `+light+` and any name
|
||||
starting with `+backup-+`) are refused. Files live at
|
||||
`+${weechat_config_dir}/themes/<name>.theme+`.
|
||||
|
||||
Rename a user theme (typical use: keep a useful automatic backup
|
||||
under a meaningful name):
|
||||
|
||||
----
|
||||
/theme rename backup-20260525-094210-123456 mybackup
|
||||
----
|
||||
|
||||
Built-in themes have no file and cannot be renamed; the target name
|
||||
cannot match a built-in name or start with `+backup-+`, and the
|
||||
target file must not already exist. The `+[info]+` `+name+` field
|
||||
inside the file is rewritten so `/theme info` reports the new name
|
||||
consistently.
|
||||
|
||||
Delete a user theme:
|
||||
|
||||
----
|
||||
|
||||
@@ -2277,6 +2277,19 @@ Reserved names (built-in theme names like `+light+` and any name
|
||||
starting with `+backup-+`) are refused. Files live at
|
||||
`+${weechat_config_dir}/themes/<name>.theme+`.
|
||||
|
||||
Rename a user theme (typical use: keep a useful automatic backup
|
||||
under a meaningful name):
|
||||
|
||||
----
|
||||
/theme rename backup-20260525-094210-123456 mybackup
|
||||
----
|
||||
|
||||
Built-in themes have no file and cannot be renamed; the target name
|
||||
cannot match a built-in name or start with `+backup-+`, and the
|
||||
target file must not already exist. The `+[info]+` `+name+` field
|
||||
inside the file is rewritten so `/theme info` reports the new name
|
||||
consistently.
|
||||
|
||||
Delete a user theme:
|
||||
|
||||
----
|
||||
|
||||
@@ -2179,6 +2179,19 @@ Reserved names (built-in theme names like `+light+` and any name
|
||||
starting with `+backup-+`) are refused. Files live at
|
||||
`+${weechat_config_dir}/themes/<name>.theme+`.
|
||||
|
||||
Rename a user theme (typical use: keep a useful automatic backup
|
||||
under a meaningful name):
|
||||
|
||||
----
|
||||
/theme rename backup-20260525-094210-123456 mybackup
|
||||
----
|
||||
|
||||
Built-in themes have no file and cannot be renamed; the target name
|
||||
cannot match a built-in name or start with `+backup-+`, and the
|
||||
target file must not already exist. The `+[info]+` `+name+` field
|
||||
inside the file is rewritten so `/theme info` reports the new name
|
||||
consistently.
|
||||
|
||||
Delete a user theme:
|
||||
|
||||
----
|
||||
|
||||
@@ -7349,6 +7349,13 @@ COMMAND_CALLBACK(theme)
|
||||
? 1 : 0);
|
||||
}
|
||||
|
||||
/* "/theme rename <old> <new>": rename a user theme file */
|
||||
if (string_strcmp (argv[1], "rename") == 0)
|
||||
{
|
||||
COMMAND_MIN_ARGS(4, "rename");
|
||||
return theme_rename (argv[2], argv[3]);
|
||||
}
|
||||
|
||||
/* "/theme delete <name>": remove a user theme file */
|
||||
if (string_strcmp (argv[1], "delete") == 0)
|
||||
{
|
||||
@@ -10081,6 +10088,7 @@ command_init (void)
|
||||
" || apply <name>"
|
||||
" || reset"
|
||||
" || save <name> [-full]"
|
||||
" || rename <old> <new>"
|
||||
" || delete <name>"
|
||||
" || info <name>"),
|
||||
CMD_ARGS_DESC(
|
||||
@@ -10101,6 +10109,11 @@ command_init (void)
|
||||
"written, use \"-full\" to write every themable option; "
|
||||
"the name must not match a built-in theme or start with "
|
||||
"\"backup-\""),
|
||||
N_("raw[rename]: rename a user theme file (typically to "
|
||||
"give an automatic backup a meaningful name); refuses to "
|
||||
"rename built-in themes, refuses target names matching a "
|
||||
"built-in or starting with \"backup-\", and refuses if "
|
||||
"the target file already exists"),
|
||||
N_("raw[delete]: delete a user theme file (refuses to delete "
|
||||
"built-in themes, which have no file)"),
|
||||
N_("raw[info]: display details on a theme (name, description, "
|
||||
@@ -10123,6 +10136,7 @@ command_init (void)
|
||||
" || apply %(theme_themes_all)"
|
||||
" || reset"
|
||||
" || save %(theme_themes_user) -full"
|
||||
" || rename %(theme_themes_files)"
|
||||
" || delete %(theme_themes_user)"
|
||||
" || info %(theme_themes_all)",
|
||||
&command_theme, NULL, NULL);
|
||||
|
||||
@@ -2086,6 +2086,40 @@ completion_list_add_theme_themes_user_cb (const void *pointer, void *data,
|
||||
return WEECHAT_RC_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add every on-disk theme file (user files + backups, no built-ins)
|
||||
* to the completion list; suitable for /theme rename which can take a
|
||||
* backup as its source.
|
||||
*/
|
||||
|
||||
int
|
||||
completion_list_add_theme_themes_files_cb (const void *pointer, void *data,
|
||||
const char *completion_item,
|
||||
struct t_gui_buffer *buffer,
|
||||
struct t_gui_completion *completion)
|
||||
{
|
||||
struct t_completion_theme_dir ctx;
|
||||
char *dir;
|
||||
|
||||
/* make C compiler happy */
|
||||
(void) pointer;
|
||||
(void) data;
|
||||
(void) completion_item;
|
||||
(void) buffer;
|
||||
|
||||
dir = NULL;
|
||||
string_asprintf (&dir, "%s/themes", weechat_config_dir);
|
||||
if (dir)
|
||||
{
|
||||
ctx.completion = completion;
|
||||
ctx.show_backups = 1;
|
||||
dir_exec_on_files (dir, 0, 0, &completion_theme_add_file_cb, &ctx);
|
||||
free (dir);
|
||||
}
|
||||
|
||||
return WEECHAT_RC_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a secured data to completion list.
|
||||
*/
|
||||
@@ -2483,6 +2517,10 @@ completion_init (void)
|
||||
hook_completion (NULL, "theme_themes_user",
|
||||
N_("names of user theme files (excludes built-ins and backups)"),
|
||||
&completion_list_add_theme_themes_user_cb, NULL, NULL);
|
||||
hook_completion (NULL, "theme_themes_files",
|
||||
N_("names of theme files on disk (user files + backups, "
|
||||
"no built-ins)"),
|
||||
&completion_list_add_theme_themes_files_cb, NULL, NULL);
|
||||
hook_completion (NULL, "secured_data",
|
||||
N_("names of secured data (file sec.conf, section data)"),
|
||||
&completion_list_add_secured_data_cb, NULL, NULL);
|
||||
|
||||
@@ -1175,6 +1175,160 @@ theme_save (const char *name, int full)
|
||||
return WEECHAT_RC_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Rename a user theme file.
|
||||
*
|
||||
* Refuse to rename a built-in (no file) or to a name reserved for
|
||||
* built-ins or automatic backups. The target name must not already
|
||||
* exist on disk. The file content is copied with the [info] name
|
||||
* field rewritten so the parsed theme name stays consistent with the
|
||||
* new filename. If "weechat.look.theme" was pointing at the old name,
|
||||
* it is updated to the new name.
|
||||
*
|
||||
* Return WEECHAT_RC_OK on success, WEECHAT_RC_ERROR on validation or
|
||||
* I/O failure (in which case no file is created or removed).
|
||||
*/
|
||||
|
||||
int
|
||||
theme_rename (const char *old_name, const char *new_name)
|
||||
{
|
||||
char *old_path, *new_path, line[2048];
|
||||
FILE *fin, *fout;
|
||||
const char *trimmed;
|
||||
int in_info, name_done;
|
||||
|
||||
if (!old_name || !old_name[0] || !new_name || !new_name[0])
|
||||
return WEECHAT_RC_ERROR;
|
||||
|
||||
if (theme_search (old_name))
|
||||
{
|
||||
gui_chat_printf (NULL,
|
||||
_("%sCannot rename built-in theme \"%s\""),
|
||||
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
|
||||
old_name);
|
||||
return WEECHAT_RC_ERROR;
|
||||
}
|
||||
|
||||
if (strcmp (old_name, new_name) == 0)
|
||||
{
|
||||
gui_chat_printf (NULL,
|
||||
_("%sNew name is the same as old name"),
|
||||
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR]);
|
||||
return WEECHAT_RC_ERROR;
|
||||
}
|
||||
|
||||
if (strncmp (new_name, "backup-", 7) == 0)
|
||||
{
|
||||
gui_chat_printf (NULL,
|
||||
_("%sName \"%s\" is reserved for automatic backups"),
|
||||
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
|
||||
new_name);
|
||||
return WEECHAT_RC_ERROR;
|
||||
}
|
||||
|
||||
if (theme_search (new_name))
|
||||
{
|
||||
gui_chat_printf (NULL,
|
||||
_("%sName \"%s\" is reserved for a built-in theme"),
|
||||
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
|
||||
new_name);
|
||||
return WEECHAT_RC_ERROR;
|
||||
}
|
||||
|
||||
old_path = theme_user_file_path (old_name);
|
||||
if (!old_path)
|
||||
return WEECHAT_RC_ERROR;
|
||||
if (access (old_path, R_OK) != 0)
|
||||
{
|
||||
gui_chat_printf (NULL,
|
||||
_("%sTheme \"%s\" not found"),
|
||||
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
|
||||
old_name);
|
||||
free (old_path);
|
||||
return WEECHAT_RC_ERROR;
|
||||
}
|
||||
|
||||
new_path = theme_user_file_path (new_name);
|
||||
if (!new_path)
|
||||
{
|
||||
free (old_path);
|
||||
return WEECHAT_RC_ERROR;
|
||||
}
|
||||
if (access (new_path, F_OK) == 0)
|
||||
{
|
||||
gui_chat_printf (NULL,
|
||||
_("%sTheme \"%s\" already exists"),
|
||||
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
|
||||
new_name);
|
||||
free (old_path);
|
||||
free (new_path);
|
||||
return WEECHAT_RC_ERROR;
|
||||
}
|
||||
|
||||
fin = fopen (old_path, "r");
|
||||
fout = (fin) ? fopen (new_path, "w") : NULL;
|
||||
if (!fin || !fout)
|
||||
{
|
||||
if (fin)
|
||||
fclose (fin);
|
||||
gui_chat_printf (NULL,
|
||||
_("%sFailed to rename theme \"%s\" to \"%s\""),
|
||||
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
|
||||
old_name, new_name);
|
||||
free (old_path);
|
||||
free (new_path);
|
||||
return WEECHAT_RC_ERROR;
|
||||
}
|
||||
|
||||
in_info = 0;
|
||||
name_done = 0;
|
||||
while (fgets (line, sizeof (line), fin))
|
||||
{
|
||||
trimmed = line;
|
||||
while (*trimmed == ' ' || *trimmed == '\t')
|
||||
trimmed++;
|
||||
if (*trimmed == '[')
|
||||
{
|
||||
in_info = (strncmp (trimmed, "[info]", 6) == 0);
|
||||
fputs (line, fout);
|
||||
continue;
|
||||
}
|
||||
if (in_info && !name_done
|
||||
&& trimmed[0] == 'n' && trimmed[1] == 'a'
|
||||
&& trimmed[2] == 'm' && trimmed[3] == 'e'
|
||||
&& (trimmed[4] == ' ' || trimmed[4] == '\t' || trimmed[4] == '='))
|
||||
{
|
||||
fprintf (fout, "name = \"%s\"\n", new_name);
|
||||
name_done = 1;
|
||||
continue;
|
||||
}
|
||||
fputs (line, fout);
|
||||
}
|
||||
fclose (fin);
|
||||
if (fclose (fout) != 0 || unlink (old_path) != 0)
|
||||
{
|
||||
unlink (new_path);
|
||||
gui_chat_printf (NULL,
|
||||
_("%sFailed to rename theme \"%s\" to \"%s\""),
|
||||
gui_chat_prefix[GUI_CHAT_PREFIX_ERROR],
|
||||
old_name, new_name);
|
||||
free (old_path);
|
||||
free (new_path);
|
||||
return WEECHAT_RC_ERROR;
|
||||
}
|
||||
|
||||
if (strcmp (CONFIG_STRING(config_look_theme), old_name) == 0)
|
||||
config_file_option_set (config_look_theme, new_name, 1);
|
||||
|
||||
gui_chat_printf (NULL,
|
||||
_("Theme \"%s\" renamed to \"%s\""),
|
||||
old_name, new_name);
|
||||
|
||||
free (old_path);
|
||||
free (new_path);
|
||||
return WEECHAT_RC_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete a user theme file.
|
||||
*
|
||||
|
||||
@@ -71,6 +71,7 @@ extern struct t_arraylist *theme_list (void);
|
||||
extern int theme_apply (const char *name);
|
||||
extern int theme_reset (void);
|
||||
extern int theme_save (const char *name, int full);
|
||||
extern int theme_rename (const char *old_name, const char *new_name);
|
||||
extern int theme_delete (const char *name);
|
||||
extern char *theme_make_backup (void);
|
||||
extern char *theme_user_file_path (const char *name);
|
||||
|
||||
@@ -949,6 +949,82 @@ TEST(CoreTheme, Delete)
|
||||
free (path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test functions:
|
||||
* theme_rename
|
||||
*/
|
||||
|
||||
TEST(CoreTheme, Rename)
|
||||
{
|
||||
char *src_path, *dst_path;
|
||||
struct stat st;
|
||||
FILE *file;
|
||||
char buf[2048];
|
||||
size_t len;
|
||||
|
||||
/* NULL / empty arguments => error */
|
||||
LONGS_EQUAL(WEECHAT_RC_ERROR, theme_rename (NULL, "dst"));
|
||||
LONGS_EQUAL(WEECHAT_RC_ERROR, theme_rename ("src", NULL));
|
||||
LONGS_EQUAL(WEECHAT_RC_ERROR, theme_rename ("", "dst"));
|
||||
LONGS_EQUAL(WEECHAT_RC_ERROR, theme_rename ("src", ""));
|
||||
|
||||
/* refuses to rename a built-in (no file to rename) */
|
||||
theme_register (NULL, NULL, "dark", NULL);
|
||||
LONGS_EQUAL(WEECHAT_RC_ERROR, theme_rename ("dark", "renamed"));
|
||||
|
||||
/* refuses target == reserved "backup-" prefix */
|
||||
LONGS_EQUAL(WEECHAT_RC_OK, theme_save ("rn_src", 0));
|
||||
LONGS_EQUAL(WEECHAT_RC_ERROR, theme_rename ("rn_src", "backup-foo"));
|
||||
|
||||
/* refuses target == built-in name */
|
||||
LONGS_EQUAL(WEECHAT_RC_ERROR, theme_rename ("rn_src", "dark"));
|
||||
|
||||
/* refuses same name */
|
||||
LONGS_EQUAL(WEECHAT_RC_ERROR, theme_rename ("rn_src", "rn_src"));
|
||||
|
||||
/* source missing => error */
|
||||
LONGS_EQUAL(WEECHAT_RC_ERROR, theme_rename ("does_not_exist", "rn_dst"));
|
||||
|
||||
/* refuses target that already exists */
|
||||
LONGS_EQUAL(WEECHAT_RC_OK, theme_save ("rn_dst", 0));
|
||||
LONGS_EQUAL(WEECHAT_RC_ERROR, theme_rename ("rn_src", "rn_dst"));
|
||||
LONGS_EQUAL(WEECHAT_RC_OK, theme_delete ("rn_dst"));
|
||||
|
||||
/* happy path: rename moves the file and rewrites the [info] name */
|
||||
src_path = theme_user_file_path ("rn_src");
|
||||
dst_path = theme_user_file_path ("rn_dst");
|
||||
CHECK(src_path != NULL);
|
||||
CHECK(dst_path != NULL);
|
||||
|
||||
LONGS_EQUAL(WEECHAT_RC_OK, theme_rename ("rn_src", "rn_dst"));
|
||||
|
||||
/* old file gone, new file exists */
|
||||
LONGS_EQUAL(-1, stat (src_path, &st));
|
||||
LONGS_EQUAL(0, stat (dst_path, &st));
|
||||
|
||||
/* [info] name field inside the renamed file is updated */
|
||||
file = fopen (dst_path, "r");
|
||||
CHECK(file != NULL);
|
||||
len = fread (buf, 1, sizeof (buf) - 1, file);
|
||||
buf[len] = '\0';
|
||||
fclose (file);
|
||||
CHECK(strstr (buf, "name = \"rn_dst\"") != NULL);
|
||||
CHECK(strstr (buf, "name = \"rn_src\"") == NULL);
|
||||
|
||||
/* if weechat.look.theme pointed at the old name, the label moves too */
|
||||
LONGS_EQUAL(WEECHAT_RC_OK, theme_save ("rn_active", 0));
|
||||
config_file_option_set (config_look_theme, "rn_active", 1);
|
||||
LONGS_EQUAL(WEECHAT_RC_OK, theme_rename ("rn_active", "rn_moved"));
|
||||
STRCMP_EQUAL("rn_moved", CONFIG_STRING(config_look_theme));
|
||||
|
||||
/* cleanup */
|
||||
config_file_option_reset (config_look_theme, 1);
|
||||
theme_delete ("rn_dst");
|
||||
theme_delete ("rn_moved");
|
||||
free (src_path);
|
||||
free (dst_path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test functions:
|
||||
* theme_init
|
||||
|
||||
Reference in New Issue
Block a user