diff --git a/contrib/micharambou b/contrib/micharambou new file mode 120000 index 00000000..bbe4f2d2 --- /dev/null +++ b/contrib/micharambou @@ -0,0 +1 @@ +/home/bill/src/dtluadev/gitannex/micharambou/ \ No newline at end of file diff --git a/data/icons/blank20.png b/data/icons/blank20.png new file mode 100644 index 00000000..81ed5157 Binary files /dev/null and b/data/icons/blank20.png differ diff --git a/data/icons/path20.png b/data/icons/path20.png new file mode 100644 index 00000000..1021d864 Binary files /dev/null and b/data/icons/path20.png differ diff --git a/data/icons/power.png b/data/icons/power.png new file mode 100644 index 00000000..f9d1fe57 Binary files /dev/null and b/data/icons/power.png differ diff --git a/data/icons/poweroff.png b/data/icons/poweroff.png new file mode 100644 index 00000000..1fb1e75c Binary files /dev/null and b/data/icons/poweroff.png differ diff --git a/development b/development new file mode 120000 index 00000000..1c69f2fb --- /dev/null +++ b/development @@ -0,0 +1 @@ +/home/bill/src/lua-scripts.local/development/ \ No newline at end of file diff --git a/tools/script_manager.lua b/tools/script_manager.lua index 71609321..8beed26a 100644 --- a/tools/script_manager.lua +++ b/tools/script_manager.lua @@ -1,6 +1,6 @@ --[[ This file is part of darktable, - copyright (c) 2018, 2020, 2023 Bill Ferguson + copyright (c) 2018, 2020, 2023, 2024 Bill Ferguson darktable is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,9 +22,9 @@ manage the lua scripts. On startup script_manager scans the lua scripts directory to see what scripts are present. - Scripts are sorted by 'category' based on what sub-directory they are in. With no - additional script repositories iinstalled, the categories are contrib, examples, official - and tools. When a category is selected the buttons show the script name and whether the + Scripts are sorted by 'folder' based on what sub-directory they are in. With no + additional script repositories iinstalled, the folders are contrib, examples, official + and tools. When a folder is selected the buttons show the script name and whether the script is started or stopped. The button is a toggle, so if the script is stopped click the button to start it and vice versa. @@ -60,7 +60,9 @@ local debug = require "darktable.debug" local gettext = dt.gettext.gettext -dt.gettext.bindtextdomain("script_manager", dt.configuration.config_dir .."/lua/locale/") +-- Tell gettext where to find the .mo file translating messages for a particular domain +dt.gettext.bindtextdomain("script_manager", dt.configuration.config_dir .. "/lua/locale/") + local function _(msgid) return gettext(msgid) @@ -68,30 +70,34 @@ end -- api check -du.check_min_api_version("5.0.0", "script_manager") +du.check_min_api_version("9.3.0", "script_manager") -- - - - - - - - - - - - - - - - - - - - - - - - -- C O N S T A N T S -- - - - - - - - - - - - - - - - - - - - - - - - -- path separator -local PS = dt.configuration.running_os == "windows" and "\\" or "/" +local PS = dt.configuration.running_os == "windows" and "\\" or "/" -- command separator -local CS = dt.configuration.running_os == "windows" and "&" or ";" +local CS = dt.configuration.running_os == "windows" and "&" or ";" + +local MODULE = "script_manager" -local MODULE = "script_manager" +local MIN_BUTTONS_PER_PAGE = 5 +local MAX_BUTTONS_PER_PAGE = 20 +local DEFAULT_BUTTONS_PER_PAGE = 10 -local MIN_BUTTONS_PER_PAGE = 5 -local MAX_BUTTONS_PER_PAGE = 20 -local DEFAULT_BUTTONS_PER_PAGE = 10 +local DEFAULT_LOG_LEVEL = log.error -local DEFAULT_LOG_LEVEL = log.error +local LUA_DIR = dt.configuration.config_dir .. PS .. "lua" +local LUA_SCRIPT_REPO = "https://github.com/darktable-org/lua-scripts.git" -local LUA_DIR = dt.configuration.config_dir .. PS .. "lua" -local LUA_SCRIPT_REPO = "https://github.com/darktable-org/lua-scripts.git" +local LUA_API_VER = "API-" .. dt.configuration.api_version_string -local LUA_API_VER = "API-" .. dt.configuration.api_version_string +-- local POWER_ICON = dt.configuration.config_dir .. "/lua/data/data/icons/power.png" +local POWER_ICON = dt.configuration.config_dir .. "/lua/data/icons/path20.png" +local BLANK_ICON = dt.configuration.config_dir .. "/lua/data/icons/blank20.png" -- - - - - - - - - - - - - - - - - - - - - - - - -- P R E F E R E N C E S @@ -128,7 +134,7 @@ sm.event_registered = false -- set up tables to contain all the widgets and choices sm.widgets = {} -sm.categories = {} +sm.folders = {} -- set log level for functions @@ -137,14 +143,14 @@ sm.log_level = DEFAULT_LOG_LEVEL --[[ sm.scripts is a table of tables for containing the scripts - It is organized as into category (folder) subtables containing + It is organized as into folder (folder) subtables containing each script definition, which is a table sm.scripts- | - - category------------| + - folder------------| | - script - - category----| | + - folder----| | - script| | - script - script| @@ -152,7 +158,7 @@ sm.log_level = DEFAULT_LOG_LEVEL and a script table looks like name the name of the script file without the lua extension - path category (folder), path separator, path, name without the lua extension + path folder (folder), path separator, path, name without the lua extension doc the header comments from the script to be used as a tooltip script_name the folder, path separator, and name without the lua extension running true if running, false if not, hidden if running but the @@ -178,10 +184,7 @@ sm.page_status = {} sm.page_status.num_buttons = DEFAULT_BUTTONS_PER_PAGE sm.page_status.buttons_created = 0 sm.page_status.current_page = 0 -sm.page_status.category = "" - --- use color in the interface? -sm.use_color = false +sm.page_status.folder = "" -- installed script repositories sm.installed_repositories = { @@ -212,17 +215,24 @@ end local function pref_read(name, pref_type) local old_log_level = set_log_level(sm.log_level) + log.msg(log.debug, "name is " .. name .. " and type is " .. pref_type) + local val = dt.preferences.read(MODULE, name, pref_type) + log.msg(log.debug, "read value " .. tostring(val)) + restore_log_level(old_log_level) return val end local function pref_write(name, pref_type, value) local old_log_level = set_log_level(sm.log_level) + log.msg(log.debug, "writing value " .. tostring(value) .. " for name " .. name) + dt.preferences.write(MODULE, name, pref_type, value) + restore_log_level(old_log_level) end @@ -232,12 +242,15 @@ end local function get_repo_status(repo) local old_log_level = set_log_level(sm.log_level) - local p = io.popen("cd " .. repo .. CS .. "git status") + + local p = dtsys.io_popen("cd " .. repo .. CS .. "git status") + if p then local data = p:read("*a") p:close() return data end + log.msg(log.error, "unable to get status of " .. repo) restore_log_level(old_log_level) return nil @@ -245,8 +258,11 @@ end local function get_current_repo_branch(repo) local old_log_level = set_log_level(sm.log_level) + local branch = nil - local p = io.popen("cd " .. repo .. CS .. "git branch --all") + + local p = dtsys.io_popen("cd " .. repo .. CS .. "git branch --all") + if p then local data = p:read("*a") p:close() @@ -254,23 +270,29 @@ local function get_current_repo_branch(repo) for _, b in ipairs(branches) do log.msg(log.debug, "branch for testing is " .. b) branch = string.match(b, "^%* (.-)$") + if branch then log.msg(log.info, "current repo branch is " .. branch) return branch end + end end + if not branch then log.msg(log.error, "no current branch detected in repo_data") end + restore_log_level(old_log_level) return nil end local function get_repo_branches(repo) local old_log_level = set_log_level(sm.log_level) + local branches = {} - local p = io.popen("cd " .. repo .. CS .. "git pull --all" .. CS .. "git branch --all") + local p = dtsys.io_popen("cd " .. repo .. CS .. "git pull --all" .. CS .. "git branch --all") + if p then local data = p:read("*a") p:close() @@ -285,12 +307,14 @@ local function get_repo_branches(repo) end end end + restore_log_level(old_log_level) return branches end local function is_repo_clean(repo_data) local old_log_level = set_log_level(sm.log_level) + if string.match(repo_data, "\n%s-%a.-%a:%s-%a%g-\n") then log.msg(log.info, "repo is dirty") return false @@ -298,13 +322,17 @@ local function is_repo_clean(repo_data) log.msg(log.info, "repo is clean") return true end + restore_log_level(old_log_level) end local function checkout_repo_branch(repo, branch) local old_log_level = set_log_level(sm.log_level) + log.msg(log.info, "checkout out branch " .. branch .. " from repository " .. repo) - os.execute("cd " .. repo .. CS .. "git checkout " .. branch) + + dtsys.os_execute("cd " .. repo .. CS .. "git checkout " .. branch) + restore_log_level(old_log_level) end @@ -314,16 +342,20 @@ end local function update_combobox_choices(combobox, choice_table, selected) local old_log_level = set_log_level(sm.log_level) + local items = #combobox local choices = #choice_table + for i, name in ipairs(choice_table) do combobox[i] = name end + if choices < items then for j = items, choices + 1, -1 do combobox[j] = nil end end + if not selected then selected = 1 end @@ -334,8 +366,10 @@ end local function string_trim(str) local old_log_level = set_log_level(sm.log_level) + local result = string.gsub(str, "^%s+", "") result = string.gsub(result, "%s+$", "") + restore_log_level(old_log_level) return result end @@ -344,18 +378,76 @@ local function string_dequote(str) return string.gsub(str, "['\"]", "") end +local function string_dei18n(str) + return string.match(str, "%_%((.+)%)") +end + +local function string_chop(str) + return str:sub(1, -2) +end + ------------------ -- script handling ------------------ -local function add_script_category(category) +local function add_script_folder(folder) + local old_log_level = set_log_level(sm.log_level) + + if #sm.folders == 0 or not string.match(du.join(sm.folders, " "), ds.sanitize_lua(folder)) then + table.insert(sm.folders, folder) + sm.scripts[folder] = {} + log.msg(log.debug, "created folder " .. folder) + end + + restore_log_level(old_log_level) +end + +local function get_script_metadata(script) local old_log_level = set_log_level(sm.log_level) - if #sm.categories == 0 or not string.match(du.join(sm.categories, " "), ds.sanitize_lua(category)) then - table.insert(sm.categories, category) - sm.scripts[category] = {} - log.msg(log.debug, "created category " .. category) + -- set_log_level(log.debug) + + log.msg(log.debug, "processing metatdata for " .. script) + + local description = nil + local metadata = nil + + f = io.open(LUA_DIR .. PS .. script .. ".lua") + if f then + -- slurp the file + local content = f:read("*all") + f:close() + -- grab the script_data.metadata table + description = string.match(content, "script_data%.metadata = %{\r?\n(.-)\r?\n%}") + else + log.msg(log.error, "cant read from " .. script) + end + + if description then + metadata = "" + -- format it into a string block for display + local lines = du.split(description, "\n") + log.msg(log.debug, "got " .. #lines .. " lines") + local first = 1 + for i = 1, #lines do + log.msg(log.debug, "splitting line " .. lines[i]) + local parts = du.split(lines[i], " = ") + log.msg(log.debug, "got value " .. parts[1] .. " and data " .. parts[2]) + if string.match(parts[2], "%_%(") then + parts[2] = _(string_dequote(string_dei18n(parts[2]))) + else + parts[2] = string_dequote(parts[2]) + end + if string.match(parts[2], ",$") then + parts[2] = string_chop(parts[2]) + end + metadata = metadata .. string.format("%s%-10s\t%s", first and "" or "\n", parts[1], parts[2]) + first = nil + end + log.msg(log.debug, "script data is \n" .. metadata) end + restore_log_level(old_log_level) + return metadata end local function get_script_doc(script) @@ -369,7 +461,9 @@ local function get_script_doc(script) -- assume that the second block comment is the documentation description = string.match(content, "%-%-%[%[.-%]%].-%-%-%[%[(.-)%]%]") else - log.msg(log.error, "Cant read from " .. script) + + log.msg(log.error, "can't read from " .. script) + end if description then restore_log_level(old_log_level) @@ -382,18 +476,28 @@ end local function activate(script) local old_log_level = set_log_level(sm.log_level) + local status = nil -- status of start function local err = nil -- error message returned if module doesn't start + log.msg(log.info, "activating " .. script.name) + if script.running == false then + script_manager_running_script = script.name + status, err = du.prequire(script.path) log.msg(log.debug, "prequire returned " .. tostring(status) .. " and for err " .. tostring(err)) + script_manager_running_script = nil + if status then pref_write(script.script_name, "bool", true) - log.msg(log.screen, string.format(_("loaded %s"), script.script_name)) + + log.msg(log.screen, _(string.format("loaded %s", script.script_name))) + script.running = true + if err ~= true then log.msg(log.debug, "got lib data") script.data = err @@ -403,17 +507,20 @@ local function activate(script) else script.data = nil end + else - log.msg(log.screen, string.format(_("%s failed to load"), script.script_name)) - log.msg(log.error, "Error loading " .. script.script_name) - log.msg(log.error, "Error message: " .. err) - end + + log.msg(log.screen, _(string.format("%s failed to load", script.script_name))) + log.msg(log.error, "error loading " .. script.script_name) + log.msg(log.error, "error message: " .. err) + else -- script is a lib and loaded but hidden and the user wants to reload script.data.restart() script.running = true status = true pref_write(script.script_name, "bool", true) end + restore_log_level(old_log_level) return status end @@ -428,9 +535,13 @@ local function deactivate(script) -- deactivate it.... local old_log_level = set_log_level(sm.log_level) + pref_write(script.script_name, "bool", false) + if script.data then + script.data.destroy() + if script.data.destroy_method then if string.match(script.data.destroy_method, "hide") then script.running = "hidden" @@ -442,44 +553,57 @@ local function deactivate(script) package.loaded[script.script_name] = nil script.running = false end + log.msg(log.info, "turned off " .. script.script_name) - log.msg(log.screen, string.format(_("%s stopped"), script.name)) + + log.msg(log.screen, _(string.format("%s stopped", script.name))) + else script.running = false + log.msg(log.info, "setting " .. script.script_name .. " to not start") - log.msg(log.screen, string.format(_("%s will not start when darktable is restarted"), script.name)) + + log.msg(log.screen, _(string.format("%s will not start when darktable is restarted", script.name))) + end + restore_log_level(old_log_level) end -local function add_script_name(name, path, category) +local function add_script_name(name, path, folder) local old_log_level = set_log_level(sm.log_level) - log.msg(log.debug, "category is " .. category) + + log.msg(log.debug, "folder is " .. folder) log.msg(log.debug, "name is " .. name) + local script = { name = name, - path = category .. "/" .. path .. name, + path = folder .. "/" .. path .. name, running = false, - doc = get_script_doc(category .. "/" .. path .. name), - script_name = category .. "/" .. name, + doc = get_script_doc(folder .. "/" .. path .. name), + metadata = get_script_metadata(folder .. "/" .. path .. name), + script_name = folder .. "/" .. name, data = nil } - table.insert(sm.scripts[category], script) + + table.insert(sm.scripts[folder], script) + if pref_read(script.script_name, "bool") then activate(script) else pref_write(script.script_name, "bool", false) end + restore_log_level(old_log_level) end local function process_script_data(script_file) local old_log_level = set_log_level(sm.log_level) - -- the script file supplied is category/filename.filetype - -- the following pattern splits the string into category, path, name, fileename, and filetype + -- the script file supplied is folder/filename.filetype + -- the following pattern splits the string into folder, path, name, fileename, and filetype -- for example contrib/gimp.lua becomes - -- category - contrib + -- folder - contrib -- path - -- name - gimp.lua -- filename - gimp @@ -496,14 +620,42 @@ local function process_script_data(script_file) log.msg(log.info, "processing " .. script_file) -- add the script data - local category,path,name,filename,filetype = string.match(script_file, pattern) + local folder,path,name,filename,filetype = string.match(script_file, pattern) - if category and name and path then - log.msg(log.debug, "category is " .. category) + if folder and name and path then + log.msg(log.debug, "folder is " .. folder) log.msg(log.debug, "name is " .. name) - add_script_category(category) - add_script_name(name, path, category) + add_script_folder(folder) + add_script_name(name, path, folder) + end + + restore_log_level(old_log_level) +end + +local function ensure_lib_in_search_path(line) + local old_log_level = set_log_level(sm.log_level) + + log.msg(log.debug, "line is " .. line) + + if string.match(line, ds.sanitize_lua(dt.configuration.config_dir .. PS .. "lua/lib")) then + log.msg(log.debug, line .. " is already in search path, returning...") + return + end + + local pattern = dt.configuration.running_os == "windows" and "(.+)\\lib\\.+lua" or "(.+)/lib/.+lua" + local path = string.match(line, pattern) + + log.msg(log.debug, "extracted path is " .. path) + log.msg(log.debug, "package.path is " .. package.path) + + if not string.match(package.path, ds.sanitize_lua(path)) then + + log.msg(log.debug, "path isn't in package.path, adding...") + + package.path = package.path .. ";" .. path .. "/?.lua" + + log.msg(log.debug, "new package.path is " .. package.path) end restore_log_level(old_log_level) @@ -540,15 +692,20 @@ end local function scan_scripts(script_dir) local old_log_level = set_log_level(sm.log_level) + local script_count = 0 local find_cmd = "find -L " .. script_dir .. " -name \\*.lua -print | sort" + if dt.configuration.running_os == "windows" then find_cmd = "dir /b/s \"" .. script_dir .. "\\*.lua\" | sort" end + log.msg(log.debug, "find command is " .. find_cmd) + -- scan the scripts - local output = io.popen(find_cmd) + local output = dtsys.io_popen(find_cmd) for line in output:lines() do + log.msg(log.debug, "line is " .. line) local l = string.gsub(line, ds.sanitize_lua(LUA_DIR) .. PS, "") -- strip the lua dir off local script_file = l:sub(1,-5) -- strip off .lua\n if not string.match(script_file, "script_manager") then -- let's not include ourself @@ -564,6 +721,7 @@ local function scan_scripts(script_dir) end end end + restore_log_level(old_log_level) return script_count end @@ -575,7 +733,9 @@ local function update_scripts() local git = sm.executables.git if not git then - dt.print(_("ERROR: git not found, install or specify the location of the git executable.")) + + log.msg(log.screen, _("ERROR: git not found. Install or specify the location of the git executable.")) + return end @@ -589,7 +749,7 @@ local function update_scripts() end if result == 0 then - dt.print(_("lua scripts successfully updated")) + log.msg(log.screen, _("lua scripts successfully updated")) end restore_log_level(old_log_level) @@ -602,65 +762,85 @@ end local function update_script_update_choices() local old_log_level = set_log_level(sm.log_level) + local installs = {} local pref_string = "" + for i, repo in ipairs(sm.installed_repositories) do table.insert(installs, repo.name) pref_string = pref_string .. i .. "," .. repo.name .. "," .. repo.directory .. "," end + update_combobox_choices(sm.widgets.update_script_choices, installs, 1) + log.msg(log.debug, "repo pref string is " .. pref_string) pref_write("installed_repos", "string", pref_string) + restore_log_level(old_log_level) end local function scan_repositories() local old_log_level = set_log_level(sm.log_level) + local script_count = 0 local find_cmd = "find -L " .. LUA_DIR .. " -name \\*.git -print | sort" + if dt.configuration.running_os == "windows" then find_cmd = "dir /b/s /a:d " .. LUA_DIR .. PS .. "*.git | sort" end + log.msg(log.debug, "find command is " .. find_cmd) - local output = io.popen(find_cmd) + + local output = dtsys.io_popen(find_cmd) + for line in output:lines() do local l = string.gsub(line, ds.sanitize_lua(LUA_DIR) .. PS, "") -- strip the lua dir off - local category = string.match(l, "(.-)" .. PS) -- get everything to teh first / - if category then -- if we have a category (.git doesn't) - log.msg(log.debug, "found category " .. category) - if not string.match(category, "plugins") and not string.match(category, "%.git") then -- skip plugins + local folder = string.match(l, "(.-)" .. PS) -- get everything to the first / + + if folder then -- if we have a folder (.git doesn't) + + log.msg(log.debug, "found folder " .. folder) + + if not string.match(folder, "plugins") and not string.match(folder, "%.git") then -- skip plugins + if #sm.installed_repositories == 1 then - log.msg(log.debug, "only 1 repo, adding " .. category) - table.insert(sm.installed_repositories, {name = category, directory = LUA_DIR .. PS .. category}) + log.msg(log.debug, "only 1 repo, adding " .. folder) + table.insert(sm.installed_repositories, {name = folder, directory = LUA_DIR .. PS .. folder}) else log.msg(log.debug, "more than 1 repo, we have to search the repos to make sure it's not there") local found = nil + for _, repo in ipairs(sm.installed_repositories) do - if string.match(repo.name, ds.sanitize_lua(category)) then + if string.match(repo.name, ds.sanitize_lua(folder)) then log.msg(log.debug, "matched " .. repo.name) found = true break end end + if not found then - table.insert(sm.installed_repositories, {name = category, directory = LUA_DIR .. PS .. category}) + table.insert(sm.installed_repositories, {name = folder, directory = LUA_DIR .. PS .. folder}) end + end end end end + update_script_update_choices() + restore_log_level(old_log_level) end local function install_scripts() local old_log_level = set_log_level(sm.log_level) + local url = sm.widgets.script_url.text - local category = sm.widgets.new_category.text + local folder = sm.widgets.new_folder.text - if string.match(du.join(sm.categories, " "), ds.sanitize_lua(category)) then - log.msg(log.screen, string.format(_("category %s is already in use, please specify a different category name."), category)) - log.msg(log.error, "category " .. category .. " already exists, returning...") + if string.match(du.join(sm.folders, " "), ds.sanitize_lua(folder)) then + log.msg(log.screen, _(string.format("folder %s is already in use. Please specify a different folder name.", folder))) + log.msg(log.error, "folder " .. folder .. " already exists, returning...") restore_log_level(old_log_level) return end @@ -670,12 +850,12 @@ local function install_scripts() local git = sm.executables.git if not git then - dt.print(_("ERROR: git not found, install or specify the location of the git executable.")) + log.msg(log.screen, _("ERROR: git not found. Install or specify the location of the git executable.")) restore_log_level(old_log_level) return end - local git_command = "cd " .. LUA_DIR .. " " .. CS .. " " .. git .. " clone " .. url .. " " .. category + local git_command = "cd " .. LUA_DIR .. " " .. CS .. " " .. git .. " clone " .. url .. " " .. folder log.msg(log.debug, "update git command is " .. git_command) if dt.configuration.running_os == "windows" then @@ -687,30 +867,34 @@ local function install_scripts() log.msg(log.info, "result from import is " .. result) if result == 0 then - local count = scan_scripts(LUA_DIR .. PS .. category) + local count = scan_scripts(LUA_DIR .. PS .. folder) + if count > 0 then - update_combobox_choices(sm.widgets.category_selector, sm.categories, sm.widgets.category_selector.selected) - dt.print(string.format(_("scripts successfully installed into category "), category)) - table.insert(sm.installed_repositories, {name = category, directory = LUA_DIR .. PS .. category}) + update_combobox_choices(sm.widgets.folder_selector, sm.folders, sm.widgets.folder_selector.selected) + dt.print(_("scripts successfully installed into folder ") .. folder) + table.insert(sm.installed_repositories, {name = folder, directory = LUA_DIR .. PS .. folder}) update_script_update_choices() - for i = 1, #sm.widgets.category_selector do - if string.match(sm.widgets.category_selector[i], ds.sanitize_lua(category)) then - log.msg(log.debug, "setting category selector to " .. i) - sm.widgets.category_selector.selected = i + + for i = 1, #sm.widgets.folder_selector do + if string.match(sm.widgets.folder_selector[i], ds.sanitize_lua(folder)) then + log.msg(log.debug, "setting folder selector to " .. i) + sm.widgets.folder_selector.selected = i break end i = i + 1 end + log.msg(log.debug, "clearing text fields") sm.widgets.script_url.text = "" - sm.widgets.new_category.text = "" + sm.widgets.new_folder.text = "" sm.widgets.main_menu.selected = 3 else - dt.print(_("no scripts found to install")) - log.msg(log.error, "scan_scripts returned " .. count .. " scripts found. Not adding to category_selector") + log.msg(log.screen, _("No scripts found to install")) + log.msg(log.error, "scan_scripts returned " .. count .. " scripts found. Not adding to folder_selector") end + else - dt.print(_("failed to download scripts")) + log.msg(log.screen, _("failed to download scripts")) end restore_log_level(old_log_level) @@ -719,102 +903,101 @@ end local function clear_button(number) local old_log_level = set_log_level(sm.log_level) + local button = sm.widgets.buttons[number] - button.label = "" + local label = sm.widgets.labels[number] + + button.image = BLANK_ICON button.tooltip = "" button.sensitive = false - --button.name = "" + label.label = "" + button.name = "" + restore_log_level(old_log_level) end -local function find_script(category, name) +local function find_script(folder, name) local old_log_level = set_log_level(sm.log_level) - log.msg(log.debug, "looking for script " .. name .. " in category " .. category) - for _, script in ipairs(sm.scripts[category]) do + + log.msg(log.debug, "looking for script " .. name .. " in folder " .. folder) + + for _, script in ipairs(sm.scripts[folder]) do if string.match(script.name, "^" .. ds.sanitize_lua(name) .. "$") then return script end end + restore_log_level(old_log_level) return nil end -local function populate_buttons(category, first, last) +local function populate_buttons(folder, first, last) local old_log_level = set_log_level(sm.log_level) - log.msg(log.debug, "category is " .. category .. " and first is " .. first .. " and last is " .. last) + + log.msg(log.debug, "folder is " .. folder .. " and first is " .. first .. " and last is " .. last) + local button_num = 1 + for i = first, last do - script = sm.scripts[category][i] - button = sm.widgets.buttons[button_num] + local script = sm.scripts[folder][i] + local button = sm.widgets.buttons[button_num] + local label = sm.widgets.labels[button_num] + if script.running == true then - if sm.use_color then - button.label = script.name - button.name = "sm_started" - else - button.label = string.format(_("%s started"), script.name) - end + button.name = "pb_on" else - if sm.use_color then - button.label = script.name - button.name = "sm_stopped" - else - button.label = string.format(_("%s stopped"), script.name) - end + button.name = "pb_off" end - button.ellipsize = "middle" + + button.image = POWER_ICON + label.label = script.name + label.name = "pb_label" + button.ellipsize = "end" button.sensitive = true - button.tooltip = script.doc + label.tooltip = script.metadata and script.metadata or script.doc + button.clicked_callback = function (this) - local script_name = nil + local cb_script = script local state = nil - if sm.use_color then - script_name = string.match(this.label, "(.+)") - else - script_name, state = string.match(this.label, "(.-) (.+)") - end - local script = find_script(sm.widgets.category_selector.value, script_name) - if script then - log.msg(log.debug, "found script " .. script.name .. " with path " .. script.path) - if script.running == true then - log.msg(log.debug, "deactivating " .. script.name .. " on " .. script.path .. " for button " .. this.label) - deactivate(script) - if sm.use_color then - this.name = "sm_stopped" - else - this.label = string.format(_("%s stopped"), script.name) - end + if cb_script then + log.msg(log.debug, "found script " .. cb_script.name .. " with path " .. cb_script.path) + if cb_script.running == true then + log.msg(log.debug, "deactivating " .. cb_script.name .. " on " .. cb_script.path) + deactivate(cb_script) + this.name = "pb_off" else - log.msg(log.debug, "activating " .. script.name .. " on " .. script.path .. " for button " .. this.label) - local result = activate(script) + log.msg(log.debug, "activating " .. cb_script.name .. " on " .. script.path) + local result = activate(cb_script) if result then - if sm.use_color then - this.name = "sm_started" - else - this.label = string.format(_("%s started"), script.name) - end + this.name = "pb_on" end end - else - log.msg(log.error, "script " .. script_name .. " not found") end end + button_num = button_num + 1 end + if button_num <= sm.page_status.num_buttons then for i = button_num, sm.page_status.num_buttons do clear_button(i) end end + restore_log_level(old_log_level) end local function paginate(direction) local old_log_level = set_log_level(sm.log_level) - local category = sm.page_status.category - log.msg(log.debug, "category is " .. category) - local num_scripts = #sm.scripts[category] + + local folder = sm.page_status.folder + log.msg(log.debug, "folder is " .. folder) + + local num_scripts = #sm.scripts[folder] log.msg(log.debug, "num_scripts is " .. num_scripts) + local max_pages = math.ceil(num_scripts / sm.page_status.num_buttons) + local cur_page = sm.page_status.current_page log.msg(log.debug, "max pages is " .. max_pages) @@ -836,7 +1019,9 @@ local function paginate(direction) log.msg(log.debug, "took path 2") cur_page = 1 end + log.msg(log.debug, "cur_page is " .. cur_page .. " and max_pages is " .. max_pages) + if cur_page == max_pages and cur_page == 1 then sm.widgets.page_forward.sensitive = false sm.widgets.page_back.sensitive = false @@ -852,115 +1037,167 @@ local function paginate(direction) end sm.page_status.current_page = cur_page + first = (cur_page * sm.page_status.num_buttons) - (sm.page_status.num_buttons - 1) + if first + sm.page_status.num_buttons > num_scripts then last = num_scripts else last = first + sm.page_status.num_buttons - 1 end - sm.widgets.page_status.label = string.format(_("page %d of %d"), cur_page, max_pages) - populate_buttons(category, first, last) + sm.widgets.page_status.label = _(string.format("page %d of %d", cur_page, max_pages)) + + populate_buttons(folder, first, last) + restore_log_level(old_log_level) end -local function change_category(category) +local function change_folder(folder) local old_log_level = set_log_level(sm.log_level) - if not category then - log.msg(log.debug "setting category to selector value " .. sm.widgets.category_selector.value) - sm.page_status.category = sm.widgets.category_selector.value + + if not folder then + log.msg(log.debug "setting folder to selector value " .. sm.widgets.folder_selector.value) + sm.page_status.folder = sm.widgets.folder_selector.value else - log.msg(log.debug, "setting catgory to argument " .. category) - sm.page_status.category = category + log.msg(log.debug, "setting catgory to argument " .. folder) + sm.page_status.folder = folder end paginate(2) + restore_log_level(old_log_level) end local function change_num_buttons() local old_log_level = set_log_level(sm.log_level) + cur_buttons = sm.page_status.num_buttons new_buttons = sm.widgets.num_buttons.value + pref_write("num_buttons", "integer", new_buttons) + if new_buttons < cur_buttons then + log.msg(log.debug, "took new is less than current branch") + for i = 1, cur_buttons - new_buttons do table.remove(sm.widgets.scripts) end + log.msg(log.debug, "finished removing widgets, now there are " .. #sm.widgets.buttons) elseif new_buttons > cur_buttons then + log.msg(log.debug, "took new is greater than current branch") + log.msg(log.debug, "number of scripts is " .. #sm.widgets.scripts) + log.msg(log.debug, "number of buttons is " .. #sm.widgets.buttons) + log.msg(log.debug, "number of labels is " .. #sm.widgets.labels) + log.msg(log.debug, "number of boxes is " .. #sm.widgets.boxes) + if new_buttons > sm.page_status.buttons_created then + for i = sm.page_status.buttons_created + 1, new_buttons do + log.msg(log.debug, "i is " .. i) table.insert(sm.widgets.buttons, dt.new_widget("button"){}) + log.msg(log.debug, "inserted new button") + log.msg(log.debug, "number of buttons is " .. #sm.widgets.buttons) + table.insert(sm.widgets.labels, dt.new_widget("label"){}) + log.msg(log.debug, "inserted new label") + log.msg(log.debug, "number of labels is " .. #sm.widgets.labels) + table.insert(sm.widgets.boxes, dt.new_widget("box"){ orientation = "horizontal", expand = false, fill = false, + sm.widgets.buttons[i], sm.widgets.labels[i]}) + log.msg(log.debug, "inserted new box") sm.page_status.buttons_created = sm.page_status.buttons_created + 1 end + end + log.msg(log.debug, "cur_buttons is " .. cur_buttons .. " and new_buttons is " .. new_buttons) log.msg(log.debug, #sm.widgets.buttons .. " buttons are available") + for i = cur_buttons + 1, new_buttons do log.msg(log.debug, "inserting button " .. i .. " into scripts widget") - table.insert(sm.widgets.scripts, sm.widgets.buttons[i]) + table.insert(sm.widgets.scripts, sm.widgets.boxes[i]) end + log.msg(log.debug, "finished adding widgets, now there are " .. #sm.widgets.buttons) else -- no change log.msg(log.debug, "no change, just returning") return end + sm.page_status.num_buttons = new_buttons log.msg(log.debug, "num_buttons set to " .. sm.page_status.num_buttons) paginate(2) -- force the buttons to repopulate sm.widgets.main_menu.selected = 3 -- jump back to start/stop scripts + restore_log_level(old_log_level) end local function load_preferences() local old_log_level = set_log_level(sm.log_level) + -- load the prefs and update settings -- update_script_choices + local pref_string = pref_read("installed_repos", "string") local entries = du.split(pref_string, ",") + while #entries > 2 do local num = table.remove(entries, 1) local name = table.remove(entries, 1) local directory = table.remove(entries, 1) + if not string.match(sm.installed_repositories[1].name, "^" .. ds.sanitize_lua(name) .. "$") then table.insert(sm.installed_repositories, {name = name, directory = directory}) end + end + update_script_update_choices() log.msg(log.debug, "updated installed scripts") - -- category selector - local val = pref_read("category_selector", "integer") + + -- folder selector + local val = pref_read("folder_selector", "integer") + if val == 0 then val = 1 end - sm.widgets.category_selector.selected = val - sm.page_status.category = sm.widgets.category_selector.value - log.msg(log.debug, "updated category selector and set it to " .. sm.widgets.category_selector.value) + + sm.widgets.folder_selector.selected = val + sm.page_status.folder = sm.widgets.folder_selector.value + log.msg(log.debug, "updated folder selector and set it to " .. sm.widgets.folder_selector.value) + -- num_buttons local val = pref_read("num_buttons", "integer") + if val == 0 then val = DEFAULT_BUTTONS_PER_PAGE end + sm.widgets.num_buttons.value = val log.msg(log.debug, "set page buttons to " .. val) + change_num_buttons() log.msg(log.debug, "paginated") + -- main menu local val = pref_read("main_menu_action", "integer") log.msg(log.debug, "read " .. val .. " for main menu") + if val == 0 then val = 3 end + sm.widgets.main_menu.selected = val log.msg(log.debug, "set main menu to val " .. val .. " which is " .. sm.widgets.main_menu.value) log.msg(log.debug, "set main menu to " .. sm.widgets.main_menu.value) + restore_log_level(old_log_level) end local function install_module() local old_log_level = set_log_level(sm.log_level) + if not sm.module_installed then dt.register_lib( "script_manager", -- Module name @@ -974,22 +1211,13 @@ local function install_module() ) sm.module_installed = true end + sm.run = true sm.use_color = pref_read("use_color", "bool") log.msg(log.debug, "set run to true, loading preferences") load_preferences() scan_repositories() - --[[dt.print_log("\n\nsetting sm visible false\n\n") - dt.gui.libs["script_manager"].visible = false - dt.control.sleep(5000) - dt.print_log("setting sm visible true") - dt.gui.libs["script_manager"].visible = true - --[[dt.control.sleep(5000) - dt.print_log("setting sm expanded false") - dt.gui.libs["script_manager"].expanded = false - dt.control.sleep(5000) - dt.print_log("setting sm expanded true") - dt.gui.libs["script_manager"].expanded = true]] + restore_log_level(old_log_level) end @@ -1008,22 +1236,28 @@ if check_for_updates then local repo = LUA_DIR if current_branch then + if sm.executables.git and clean and + (current_branch == "master" or string.match(current_branch, "^API%-")) then -- only make changes to clean branches local branches = get_repo_branches(LUA_DIR) + if current_branch ~= LUA_API_VER and current_branch ~= "master" then -- probably upgraded from an earlier api version so get back to master -- to use the latest version of script_manager to get the proper API checkout_repo_branch(repo, "master") - log.msg(log.screen, "lua API version reset, please restart darktable") + log.msg(log.screen, _("lua API version reset, please restart darktable")) + elseif LUA_API_VER == current_branch then -- do nothing, we are fine log.msg(log.debug, "took equal branch, doing nothing") + elseif string.match(LUA_API_VER, "dev") then -- we are on a dev API version, so checkout the dev -- api version or checkout/stay on master log.msg(log.debug, "took the dev branch") local match = false + for _, branch in ipairs(branches) do log.msg(log.debug, "checking branch " .. branch .. " against API " .. LUA_API_VER) if LUA_API_VER == branch then @@ -1032,6 +1266,7 @@ if check_for_updates then checkout_repo_branch(repo, branch) end end + if not match then if current_branch == "master" then log.msg(log.info, "staying on master, no dev branch yet") @@ -1040,25 +1275,33 @@ if check_for_updates then checkout_repo_branch(repo, "master") end end + elseif #branches > 0 and LUA_API_VER > branches[#branches] then log.msg(log.info, "no newer branches, staying on master") -- stay on master + else -- checkout the appropriate branch for API version if it exists log.msg(log.info, "checking out the appropriate API branch") + local match = false + for _, branch in ipairs(branches) do log.msg(log.debug, "checking branch " .. branch .. " against API " .. LUA_API_VER) + if LUA_API_VER == branch then match = true log.msg(log.info, "checking out repo branch " .. branch) checkout_repo_branch(repo, branch) log.msg(log.screen, "you must restart darktable to use the correct version of the lua") end + end + if not match then log.msg(log.warn, "no matching branch found for " .. LUA_API_VER) end + end end end @@ -1100,18 +1343,18 @@ sm.widgets.script_url = dt.new_widget("entry"){ tooltip = _("enter the URL of the git repository containing the scripts you wish to add") } -sm.widgets.new_category = dt.new_widget("entry"){ +sm.widgets.new_folder = dt.new_widget("entry"){ text = "", - placeholder = _("name of new category"), - tooltip = _("enter a category name for the additional scripts") + placeholder = _("name of new folder"), + tooltip = _("enter a folder name for the additional scripts") } sm.widgets.add_scripts = dt.new_widget("box"){ orientation = vertical, dt.new_widget("label"){label = _("URL to download additional scripts from")}, sm.widgets.script_url, - dt.new_widget("label"){label = _("new category to place scripts in")}, - sm.widgets.new_category, + dt.new_widget("label"){label = _("new folder to place scripts in")}, + sm.widgets.new_folder, dt.new_widget("button"){ label = _("install additional scripts"), clicked_callback = function(this) @@ -1126,6 +1369,8 @@ sm.widgets.allow_disable = dt.new_widget("check_button"){ clicked_callback = function(this) if this.value == true then sm.widgets.disable_scripts.sensitive = true + else + sm.widgets.disable_scripts.sensitive = false end end, } @@ -1137,41 +1382,62 @@ sm.widgets.disable_scripts = dt.new_widget("button"){ local LUARC = dt.configuration.config_dir .. PS .. "luarc" df.file_move(LUARC, LUARC .. ".disabled") log.msg(log.info, "lua scripts disabled") - dt.print(_("lua scripts will not run the next time darktable is started")) + log.msg(log.screen, _("lua scripts will not run the next time darktable is started")) end } sm.widgets.install_update = dt.new_widget("box"){ orientation = "vertical", - dt.new_widget("section_label"){label = _("update scripts")}, + dt.new_widget("section_label"){label = _(" ")}, + dt.new_widget("label"){label = " "}, + dt.new_widget("label"){label = _("update scripts")}, + dt.new_widget("label"){label = " "}, sm.widgets.update_script_choices, sm.widgets.update, - dt.new_widget("section_label"){label = _("add more scripts")}, + dt.new_widget("section_label"){label = " "}, + dt.new_widget("label"){label = " "}, + dt.new_widget("label"){label = _("add more scripts")}, + dt.new_widget("label"){label = " "}, sm.widgets.add_scripts, - dt.new_widget("section_label"){label = _("disable scripts")}, + dt.new_widget("section_label"){label = " "}, + dt.new_widget("label"){label = " "}, + dt.new_widget("label"){label = _("disable scripts")}, + dt.new_widget("label"){label = " "}, sm.widgets.allow_disable, - sm.widgets.disable_scripts + sm.widgets.disable_scripts, + dt.new_widget("label"){label = " "}, } -- manage the scripts -sm.widgets.category_selector = dt.new_widget("combobox"){ - label = _("category"), - tooltip = _("select the script category"), +sm.widgets.folder_selector = dt.new_widget("combobox"){ + label = _("folder"), + tooltip = _( "select the script folder"), selected = 1, changed_callback = function(self) if sm.run then - pref_write("category_selector", "integer", self.selected) - change_category(self.value) + pref_write("folder_selector", "integer", self.selected) + change_folder(self.value) end end, - table.unpack(sm.categories), + table.unpack(sm.folders), } +-- a script "button" consists of: +-- a button to start and stop the script +-- a label that contains the name of the script +-- a horizontal box that contains the button and the label + sm.widgets.buttons ={} +sm.widgets.labels = {} +sm.widgets.boxes = {} + for i =1, DEFAULT_BUTTONS_PER_PAGE do table.insert(sm.widgets.buttons, dt.new_widget("button"){}) - sm.page_status.buttons_create = sm.page_status.buttons_created + 1 + table.insert(sm.widgets.labels, dt.new_widget("label"){}) + table.insert(sm.widgets.boxes, dt.new_widget("box"){ orientation = "horizontal", expand = false, fill = false, + sm.widgets.buttons[i], sm.widgets.labels[i]}) + sm.page_status.buttons_created = sm.page_status.buttons_created + 1 end local page_back = "<" @@ -1205,10 +1471,13 @@ sm.widgets.page_control = dt.new_widget("box"){ sm.widgets.scripts = dt.new_widget("box"){ orientation = vertical, + + dt.new_widget("section_label"){label = _(" ")}, + dt.new_widget("label"){label = " "}, dt.new_widget("label"){label = _("scripts")}, - sm.widgets.category_selector, + sm.widgets.folder_selector, sm.widgets.page_control, - table.unpack(sm.widgets.buttons) + table.unpack(sm.widgets.boxes), } -- configure options @@ -1234,21 +1503,16 @@ sm.widgets.change_buttons = dt.new_widget("button"){ sm.widgets.configure = dt.new_widget("box"){ orientation = "vertical", + dt.new_widget("section_label"){label = " "}, + dt.new_widget("label"){label = " "}, dt.new_widget("label"){label = _("configuration")}, + dt.new_widget("label"){label = " "}, sm.widgets.num_buttons, + dt.new_widget("label"){label = " "}, sm.widgets.change_buttons, + dt.new_widget("label"){label = " "}, } -sm.widgets.color = dt.new_widget("check_button"){ - label = _("use color interface?"), - value = pref_read("use_color", "bool"), - clicked_callback = function(this) - pref_write("use_color", "bool", this.value) - sm.use_color = this.value - end -} -table.insert(sm.widgets.configure, sm.widgets.color) - -- stack for the options sm.widgets.main_stack = dt.new_widget("stack"){ @@ -1294,7 +1558,7 @@ else function(event, old_view, new_view) if new_view.name == "lighttable" and old_view.name == "darkroom" then install_module() - end + end end ) sm.event_registered = true