From 6d025108e8d331c3166fc0f10e6418a112e52942 Mon Sep 17 00:00:00 2001 From: Yannic Meyer Date: Tue, 8 Sep 2026 09:56:44 +0200 Subject: [PATCH 1/6] Update rename_images.lua - add pattern preview --- contrib/rename_images.lua | 47 +++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/contrib/rename_images.lua b/contrib/rename_images.lua index 8aeb8b82..4cbb8ad9 100644 --- a/contrib/rename_images.lua +++ b/contrib/rename_images.lua @@ -37,7 +37,6 @@ TODO * Add pattern builder - * Add new name preview ]] local dt = require "darktable" @@ -107,6 +106,8 @@ local function install_module() dt.new_widget("box"){ orientation = "vertical", rename.widgets.pattern, + rename.widgets.preview_button, + rename.widgets.preview_label, rename.widgets.button, }, nil, @@ -128,6 +129,15 @@ end -- M A I N -- - - - - - - - - - - - - - - - - - - - - - - - +-- runs variable substitution for one image and returns the result, +-- or -1 if the substitution failed (see ds.substitute_list) +local function substitute_pattern(image, sequence, pattern) + ds.build_substitute_list(image, sequence, pattern, USER, PICTURES, HOME, DESKTOP) + local new_name = ds.substitute_list(pattern) + ds.clear_substitute_list() + return new_name +end + local function do_rename(images) if #images > 0 then local first_image = images[1] @@ -141,14 +151,12 @@ local function do_rename(images) for i, image in ipairs(images) do if job.valid then job.percent = i / #images - ds.build_substitute_list(image, i, pattern, USER, PICTURES, HOME, DESKTOP) - local new_name = ds.substitute_list(pattern) + local new_name = substitute_pattern(image, i, pattern) if new_name == -1 then dt.print(_("unable to do variable substitution, exiting...")) stop_job(job) return end - ds.clear_substitute_list() local args = {} local path = string.sub(df.get_path(new_name), 1, -2) if string.len(path) == 0 then @@ -185,6 +193,25 @@ local function do_rename(images) end end +local function do_preview(images) + if #images > 0 then + local image = images[1] + local pattern = rename.widgets.pattern.text + if string.len(pattern) > 0 then + local new_name = substitute_pattern(image, 1, pattern) + if new_name == -1 then + rename.widgets.preview_label.label = _("unable to do variable substitution") + else + rename.widgets.preview_label.label = df.get_filename(new_name) + end + else -- pattern length + rename.widgets.preview_label.label = _("please enter the new name or pattern") + end + else -- image count + rename.widgets.preview_label.label = _("please select an image first") + end +end + -- - - - - - - - - - - - - - - - - - - - - - - - -- W I D G E T S @@ -205,6 +232,18 @@ if pattern_pref then rename.widgets.pattern.text = pattern_pref end +rename.widgets.preview_button = dt.new_widget("button"){ + label = _("preview filename"), + clicked_callback = function(this) + do_preview(dt.gui.action_images) + end +} + +rename.widgets.preview_label = dt.new_widget("label"){ + label = "", + selectable = true, +} + rename.widgets.button = dt.new_widget("button"){ label = _("rename"), clicked_callback = function(this) From ef586dc3a9fd071d454d46a14a654124d1291d43 Mon Sep 17 00:00:00 2001 From: Yannic Meyer Date: Tue, 8 Sep 2026 11:56:21 +0200 Subject: [PATCH 2/6] add checkbox + textbox for custom extensions --- contrib/rename_images.lua | 61 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/contrib/rename_images.lua b/contrib/rename_images.lua index 4cbb8ad9..41e524e9 100644 --- a/contrib/rename_images.lua +++ b/contrib/rename_images.lua @@ -37,6 +37,7 @@ TODO * Add pattern builder + * Add new name preview ]] local dt = require "darktable" @@ -95,6 +96,18 @@ local function stop_job(job) job.valid = false end +-- make sure filename ends with the given extension (without leading dot); +-- does nothing if extension is empty +local function ensure_extension(filename, extension) + if extension ~= "" then + local suffix = "." .. extension + if string.lower(string.sub(filename, -string.len(suffix))) ~= string.lower(suffix) then + filename = filename .. suffix + end + end + return filename +end + local function install_module() if not rename.module_installed then dt.register_lib( @@ -106,6 +119,8 @@ local function install_module() dt.new_widget("box"){ orientation = "vertical", rename.widgets.pattern, + rename.widgets.use_existing_extension, + rename.widgets.extension_pattern, rename.widgets.preview_button, rename.widgets.preview_label, rename.widgets.button, @@ -138,6 +153,33 @@ local function substitute_pattern(image, sequence, pattern) return new_name end +-- work out which extension to use for a given image, based on the +-- "use existing extension" checkbox and (if unchecked) the extension pattern +local function determine_extension(image, sequence) + if rename.widgets.use_existing_extension.value then + return df.get_filetype(image.filename) + end + + local extension_pattern = rename.widgets.extension_pattern.text + local extension = "" + if string.len(extension_pattern) > 0 then + local substituted = substitute_pattern(image, sequence, extension_pattern) + if substituted ~= -1 then + extension = substituted + end + end + + if extension == "" then + return df.get_filetype(image.filename) + end + + if string.sub(extension, 1, 1) == "." then + extension = string.sub(extension, 2) + end + + return extension +end + local function do_rename(images) if #images > 0 then local first_image = images[1] @@ -162,7 +204,7 @@ local function do_rename(images) if string.len(path) == 0 then path = image.path end - local filename = df.get_filename(new_name) + local filename = ensure_extension(df.get_filename(new_name), determine_extension(image, i)) local filmname = image.path if path ~= image.path then if not df.check_if_file_exists(df.sanitize_filename(path)) then @@ -202,7 +244,7 @@ local function do_preview(images) if new_name == -1 then rename.widgets.preview_label.label = _("unable to do variable substitution") else - rename.widgets.preview_label.label = df.get_filename(new_name) + rename.widgets.preview_label.label = ensure_extension(df.get_filename(new_name), determine_extension(image, 1)) end else -- pattern length rename.widgets.preview_label.label = _("please enter the new name or pattern") @@ -232,6 +274,21 @@ if pattern_pref then rename.widgets.pattern.text = pattern_pref end +rename.widgets.use_existing_extension = dt.new_widget("check_button"){ + label = _("use existing extension"), + value = true, + clicked_callback = function(self) + rename.widgets.extension_pattern.visible = not self.value + end +} + +rename.widgets.extension_pattern = dt.new_widget("entry"){ + tooltip = _("pattern or literal text for the new extension") .. "\n" .. + _("leave empty to keep the existing extension"), + text = "$(FILE.EXTENSION)", + visible = false, +} + rename.widgets.preview_button = dt.new_widget("button"){ label = _("preview filename"), clicked_callback = function(this) From a8eb1913858358ef24930c440f58f3c858f0d2a1 Mon Sep 17 00:00:00 2001 From: Yannic Meyer Date: Tue, 8 Sep 2026 12:53:41 +0200 Subject: [PATCH 3/6] bugfixes --- contrib/rename_images.lua | 54 +++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/contrib/rename_images.lua b/contrib/rename_images.lua index 41e524e9..b4c60bee 100644 --- a/contrib/rename_images.lua +++ b/contrib/rename_images.lua @@ -169,22 +169,29 @@ local function determine_extension(image, sequence) end end - if extension == "" then - return df.get_filetype(image.filename) - end - if string.sub(extension, 1, 1) == "." then extension = string.sub(extension, 2) end + if extension == "" then + return df.get_filetype(image.filename) + end + return extension end +-- save the pattern and extension-pattern fields so they survive a restart; +-- the checkbox persists itself immediately on click (see its clicked_callback) +local function persist_pattern_prefs() + dt.preferences.write(MODULE_NAME, "pattern", "string", rename.widgets.pattern.text) + dt.preferences.write(MODULE_NAME, "extension_pattern", "string", rename.widgets.extension_pattern.text) +end + local function do_rename(images) if #images > 0 then local first_image = images[1] local pattern = rename.widgets.pattern.text - dt.preferences.write(MODULE_NAME, "pattern", "string", pattern) + persist_pattern_prefs() dt.print_log("pattern is " .. pattern) if string.len(pattern) > 0 then local datetime = os.date("*t") @@ -239,6 +246,7 @@ local function do_preview(images) if #images > 0 then local image = images[1] local pattern = rename.widgets.pattern.text + persist_pattern_prefs() if string.len(pattern) > 0 then local new_name = substitute_pattern(image, 1, pattern) if new_name == -1 then @@ -274,20 +282,44 @@ if pattern_pref then rename.widgets.pattern.text = pattern_pref end +rename.widgets.extension_pattern = dt.new_widget("entry"){ + tooltip = _("pattern or literal text for the new extension") .. "\n" .. + _("leave empty to keep the existing extension"), + text = "$(FILE.EXTENSION)", + visible = false, + reset_callback = function(self) + self.text = "$(FILE.EXTENSION)" + self.visible = false + dt.preferences.write(MODULE_NAME, "extension_pattern", "string", self.text) + end +} + +-- defined AFTER extension_pattern: setting "value" below fires +-- clicked_callback immediately during construction, which references +-- rename.widgets.extension_pattern rename.widgets.use_existing_extension = dt.new_widget("check_button"){ label = _("use existing extension"), value = true, clicked_callback = function(self) rename.widgets.extension_pattern.visible = not self.value + dt.preferences.write(MODULE_NAME, "use_existing_extension", "bool", self.value) + dt.preferences.write(MODULE_NAME, "use_existing_extension_saved", "bool", true) end } -rename.widgets.extension_pattern = dt.new_widget("entry"){ - tooltip = _("pattern or literal text for the new extension") .. "\n" .. - _("leave empty to keep the existing extension"), - text = "$(FILE.EXTENSION)", - visible = false, -} +-- only override the widget defaults if the checkbox was explicitly +-- saved before; dt.preferences.read(..., "bool") returns false rather +-- than nil for a preference that was never set, so a sentinel flag is +-- needed to tell "never saved" apart from "saved as false" +if dt.preferences.read(MODULE_NAME, "use_existing_extension_saved", "bool") then + rename.widgets.use_existing_extension.value = dt.preferences.read(MODULE_NAME, "use_existing_extension", "bool") + rename.widgets.extension_pattern.visible = not rename.widgets.use_existing_extension.value +end + +local extension_pattern_pref = dt.preferences.read(MODULE_NAME, "extension_pattern", "string") +if extension_pattern_pref and extension_pattern_pref ~= "" then + rename.widgets.extension_pattern.text = extension_pattern_pref +end rename.widgets.preview_button = dt.new_widget("button"){ label = _("preview filename"), From 595df576455a390c5112c74cb61aaa4f5266662f Mon Sep 17 00:00:00 2001 From: Yannic Meyer Date: Tue, 8 Sep 2026 13:10:04 +0200 Subject: [PATCH 4/6] added option for moving files while renaming --- contrib/rename_images.lua | 73 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 5 deletions(-) diff --git a/contrib/rename_images.lua b/contrib/rename_images.lua index b4c60bee..cc2c431b 100644 --- a/contrib/rename_images.lua +++ b/contrib/rename_images.lua @@ -119,6 +119,7 @@ local function install_module() dt.new_widget("box"){ orientation = "vertical", rename.widgets.pattern, + rename.widgets.allow_moving_files, rename.widgets.use_existing_extension, rename.widgets.extension_pattern, rename.widgets.preview_button, @@ -144,12 +145,36 @@ end -- M A I N -- - - - - - - - - - - - - - - - - - - - - - - - +-- characters that are forbidden (or at least highly problematic) in +-- filenames across common operating systems; always replaced, regardless +-- of the "allow moving files" setting +local function sanitize_forbidden_characters(name) + return (name:gsub('[<>:"|?*%c]', "-")) +end + +-- replace path separators with "-" unless the user explicitly opted in to +-- allow the rename pattern to move files into different folders; the +-- underlying split_filepath() (used by df.get_path/df.get_filename) treats +-- both "/" and "\" as separators regardless of the current OS, so both are +-- always allowed together here too, rather than just the OS's native one +local function sanitize_path_separators(name) + if rename.widgets.allow_moving_files.value then + return name + end + return (name:gsub("[/\\]", "-")) +end + -- runs variable substitution for one image and returns the result, -- or -1 if the substitution failed (see ds.substitute_list) local function substitute_pattern(image, sequence, pattern) ds.build_substitute_list(image, sequence, pattern, USER, PICTURES, HOME, DESKTOP) local new_name = ds.substitute_list(pattern) ds.clear_substitute_list() + if new_name == -1 then + return -1 + end + new_name = sanitize_path_separators(new_name) + new_name = sanitize_forbidden_characters(new_name) return new_name end @@ -180,6 +205,25 @@ local function determine_extension(image, sequence) return extension end +-- returns the pattern's folder component with any leading separators +-- stripped, so a leading "/" or "\" can never point to the filesystem +-- root (or, for an AppImage, the process's working directory) - the +-- folder is always resolved relative to the image's current location +local function resolve_relative_path(new_name) + local path = string.sub(df.get_path(new_name), 1, -2) + return (string.gsub(path, "^[/\\]+", "")) +end + +-- work out the destination folder for a renamed image, always relative +-- to the image's current folder (see resolve_relative_path) +local function resolve_path(new_name, image) + local relative_path = resolve_relative_path(new_name) + if string.len(relative_path) == 0 then + return image.path + end + return image.path .. PS .. relative_path +end + -- save the pattern and extension-pattern fields so they survive a restart; -- the checkbox persists itself immediately on click (see its clicked_callback) local function persist_pattern_prefs() @@ -207,10 +251,7 @@ local function do_rename(images) return end local args = {} - local path = string.sub(df.get_path(new_name), 1, -2) - if string.len(path) == 0 then - path = image.path - end + local path = resolve_path(new_name, image) local filename = ensure_extension(df.get_filename(new_name), determine_extension(image, i)) local filmname = image.path if path ~= image.path then @@ -252,7 +293,13 @@ local function do_preview(images) if new_name == -1 then rename.widgets.preview_label.label = _("unable to do variable substitution") else - rename.widgets.preview_label.label = ensure_extension(df.get_filename(new_name), determine_extension(image, 1)) + local relative_path = resolve_relative_path(new_name) + local filename = ensure_extension(df.get_filename(new_name), determine_extension(image, 1)) + if string.len(relative_path) > 0 then + rename.widgets.preview_label.label = relative_path .. PS .. filename + else + rename.widgets.preview_label.label = filename + end end else -- pattern length rename.widgets.preview_label.label = _("please enter the new name or pattern") @@ -282,6 +329,22 @@ if pattern_pref then rename.widgets.pattern.text = pattern_pref end +rename.widgets.allow_moving_files = dt.new_widget("check_button"){ + label = _("allow moving files"), + value = false, + tooltip = _("allows for moving files during renaming by using slash/backslash to construct a filepath") .. "\n" .. + _("useful for e.g. sorting files by their date") .. "\n" .. + _("caution: use with extra care"), + clicked_callback = function(self) + dt.preferences.write(MODULE_NAME, "allow_moving_files", "bool", self.value) + dt.preferences.write(MODULE_NAME, "allow_moving_files_saved", "bool", true) + end +} + +if dt.preferences.read(MODULE_NAME, "allow_moving_files_saved", "bool") then + rename.widgets.allow_moving_files.value = dt.preferences.read(MODULE_NAME, "allow_moving_files", "bool") +end + rename.widgets.extension_pattern = dt.new_widget("entry"){ tooltip = _("pattern or literal text for the new extension") .. "\n" .. _("leave empty to keep the existing extension"), From 814e2415364c82a8d46ce81ff17abf318c4508fa Mon Sep 17 00:00:00 2001 From: Yannic Meyer Date: Tue, 8 Sep 2026 13:15:28 +0200 Subject: [PATCH 5/6] bugfixes --- contrib/rename_images.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/contrib/rename_images.lua b/contrib/rename_images.lua index cc2c431b..2eca9fe0 100644 --- a/contrib/rename_images.lua +++ b/contrib/rename_images.lua @@ -194,6 +194,11 @@ local function determine_extension(image, sequence) end end + -- collapse runs of consecutive dots into a single dot, so that e.g. + -- "......." is treated the same as a single "." (falls through to the + -- empty-extension check below, which falls back to the original extension) + extension = string.gsub(extension, "%.+", ".") + if string.sub(extension, 1, 1) == "." then extension = string.sub(extension, 2) end From 06d2e73f5961e69b330a2de394f6c42b56e79199 Mon Sep 17 00:00:00 2001 From: Yannic Meyer Date: Tue, 8 Sep 2026 13:38:31 +0200 Subject: [PATCH 6/6] bugfixes, styling --- contrib/rename_images.lua | 124 ++++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 57 deletions(-) diff --git a/contrib/rename_images.lua b/contrib/rename_images.lua index 2eca9fe0..473d9945 100644 --- a/contrib/rename_images.lua +++ b/contrib/rename_images.lua @@ -1,43 +1,39 @@ --[[ - rename.lua - rename image file(s) +rename.lua - rename image file(s) - Copyright (C) 2020, 2021 Bill Ferguson . +Copyright (C) 2020, 2021 Bill Ferguson . - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see . +You should have received a copy of the GNU General Public License +along with this program. If not, see . ]] --[[ - rename - rename an image file or files +rename - rename an image file or files - This shortcut resets the GPS information to that contained within - the image file. If no GPS info is in the image file, the GPS data - is cleared. +USAGE +* enable the script in the script manager +* select an image or images +* enter a renaming pattern +* test the pattern by clicking "preview filename" +* click "rename" to rename the files - USAGE - * require this script from your luarc file or start it from script_manager - * select an image or images - * enter a renaming pattern - * click the button to rename the files +BUGS, COMMENTS, SUGGESTIONS +* Report to https://github.com/darktable-org/lua-scripts/issues - BUGS, COMMENTS, SUGGESTIONS - * Send to Bill Ferguson, wpferguson@gmail.com +CHANGES - CHANGES - - TODO - * Add pattern builder - * Add new name preview +TODO +* Add pattern builder ]] local dt = require "darktable" @@ -45,12 +41,12 @@ local du = require "lib/dtutils" local df = require "lib/dtutils.file" local ds = require "lib/dtutils.string" -du.check_min_api_version("7.0.0", "rename_images") +du.check_min_api_version("7.0.0", "rename_images") local gettext = dt.gettext.gettext local function _(msgid) - return gettext(msgid) +return gettext(msgid) end -- namespace variable @@ -67,7 +63,7 @@ local script_data = {} script_data.metadata = { name = _("rename images"), purpose = _("rename an image file or files"), - author = "Bill Ferguson ", + author = "Bill Ferguson , Yannic Meyer", help = "https://docs.darktable.org/lua/stable/lua.scripts.manual/scripts/contrib/rename_images" } @@ -119,9 +115,9 @@ local function install_module() dt.new_widget("box"){ orientation = "vertical", rename.widgets.pattern, - rename.widgets.allow_moving_files, - rename.widgets.use_existing_extension, + rename.widgets.change_file_extension, rename.widgets.extension_pattern, + rename.widgets.allow_moving_files, rename.widgets.preview_button, rename.widgets.preview_label, rename.widgets.button, @@ -152,36 +148,41 @@ local function sanitize_forbidden_characters(name) return (name:gsub('[<>:"|?*%c]', "-")) end --- replace path separators with "-" unless the user explicitly opted in to --- allow the rename pattern to move files into different folders; the +-- replace path separators with "-" unless allow_separators is true; the -- underlying split_filepath() (used by df.get_path/df.get_filename) treats -- both "/" and "\" as separators regardless of the current OS, so both are -- always allowed together here too, rather than just the OS's native one -local function sanitize_path_separators(name) - if rename.widgets.allow_moving_files.value then +local function sanitize_path_separators(name, allow_separators) + if allow_separators then return name end return (name:gsub("[/\\]", "-")) end --- runs variable substitution for one image and returns the result, --- or -1 if the substitution failed (see ds.substitute_list) -local function substitute_pattern(image, sequence, pattern) +-- runs variable substitution for one image and returns the result, or -1 +-- if the substitution failed (see ds.substitute_list). The result always +-- has forbidden filename characters replaced (sanitize_forbidden_characters), +-- and has path separators replaced too unless allow_separators is true. +-- allow_separators is only ever passed for the main rename pattern, gated +-- behind the "allow moving files" checkbox - never for the extension +-- pattern, where a stray separator could never be useful and would just +-- corrupt the final filename +local function substitute_pattern(image, sequence, pattern, allow_separators) ds.build_substitute_list(image, sequence, pattern, USER, PICTURES, HOME, DESKTOP) local new_name = ds.substitute_list(pattern) ds.clear_substitute_list() if new_name == -1 then return -1 end - new_name = sanitize_path_separators(new_name) + new_name = sanitize_path_separators(new_name, allow_separators) new_name = sanitize_forbidden_characters(new_name) return new_name end -- work out which extension to use for a given image, based on the --- "use existing extension" checkbox and (if unchecked) the extension pattern +-- "change file extension" checkbox and (if checked) the extension pattern local function determine_extension(image, sequence) - if rename.widgets.use_existing_extension.value then + if not rename.widgets.change_file_extension.value then return df.get_filetype(image.filename) end @@ -236,6 +237,9 @@ local function persist_pattern_prefs() dt.preferences.write(MODULE_NAME, "extension_pattern", "string", rename.widgets.extension_pattern.text) end +-- renames/moves every image in `images` according to the current pattern, +-- extension, and "allow moving files" settings; runs as a cancellable job +-- and updates the lighttable selection to the renamed images once done local function do_rename(images) if #images > 0 then local first_image = images[1] @@ -249,7 +253,7 @@ local function do_rename(images) for i, image in ipairs(images) do if job.valid then job.percent = i / #images - local new_name = substitute_pattern(image, i, pattern) + local new_name = substitute_pattern(image, i, pattern, rename.widgets.allow_moving_files.value) if new_name == -1 then dt.print(_("unable to do variable substitution, exiting...")) stop_job(job) @@ -288,13 +292,16 @@ local function do_rename(images) end end +-- mirrors do_rename's computation for the first selected image, but only +-- updates the preview label instead of actually touching any files - keep +-- this in sync with do_rename whenever that function's logic changes local function do_preview(images) if #images > 0 then local image = images[1] local pattern = rename.widgets.pattern.text persist_pattern_prefs() if string.len(pattern) > 0 then - local new_name = substitute_pattern(image, 1, pattern) + local new_name = substitute_pattern(image, 1, pattern, rename.widgets.allow_moving_files.value) if new_name == -1 then rename.widgets.preview_label.label = _("unable to do variable substitution") else @@ -362,16 +369,17 @@ rename.widgets.extension_pattern = dt.new_widget("entry"){ end } --- defined AFTER extension_pattern: setting "value" below fires --- clicked_callback immediately during construction, which references --- rename.widgets.extension_pattern -rename.widgets.use_existing_extension = dt.new_widget("check_button"){ - label = _("use existing extension"), - value = true, +-- defined AFTER extension_pattern: setting "value" to something other than +-- its GTK default fires clicked_callback immediately during construction, +-- which references rename.widgets.extension_pattern; kept for safety even +-- though the default here is now "false" (matching GTK's own default) +rename.widgets.change_file_extension = dt.new_widget("check_button"){ + label = _("change file extension"), + value = false, clicked_callback = function(self) - rename.widgets.extension_pattern.visible = not self.value - dt.preferences.write(MODULE_NAME, "use_existing_extension", "bool", self.value) - dt.preferences.write(MODULE_NAME, "use_existing_extension_saved", "bool", true) + rename.widgets.extension_pattern.visible = self.value + dt.preferences.write(MODULE_NAME, "change_file_extension", "bool", self.value) + dt.preferences.write(MODULE_NAME, "change_file_extension_saved", "bool", true) end } @@ -379,9 +387,9 @@ rename.widgets.use_existing_extension = dt.new_widget("check_button"){ -- saved before; dt.preferences.read(..., "bool") returns false rather -- than nil for a preference that was never set, so a sentinel flag is -- needed to tell "never saved" apart from "saved as false" -if dt.preferences.read(MODULE_NAME, "use_existing_extension_saved", "bool") then - rename.widgets.use_existing_extension.value = dt.preferences.read(MODULE_NAME, "use_existing_extension", "bool") - rename.widgets.extension_pattern.visible = not rename.widgets.use_existing_extension.value +if dt.preferences.read(MODULE_NAME, "change_file_extension_saved", "bool") then + rename.widgets.change_file_extension.value = dt.preferences.read(MODULE_NAME, "change_file_extension", "bool") + rename.widgets.extension_pattern.visible = rename.widgets.change_file_extension.value end local extension_pattern_pref = dt.preferences.read(MODULE_NAME, "extension_pattern", "string") @@ -399,6 +407,8 @@ rename.widgets.preview_button = dt.new_widget("button"){ rename.widgets.preview_label = dt.new_widget("label"){ label = "", selectable = true, + halign = "center", + tooltip = _("shows the filename (and destination subfolder, if any) the rename button would produce"), } rename.widgets.button = dt.new_widget("button"){