From 53f15ba8b8664e3e8077f2713f34af5b54924317 Mon Sep 17 00:00:00 2001 From: Bill Ferguson Date: Fri, 2 Jul 2021 21:51:20 -0400 Subject: [PATCH] [color_profile_manager] created color_profile_manager to manage the darktable input and output color profiles --- contrib/color_profile_manager.lua | 372 ++++++++++++++++++++++++++++++ 1 file changed, 372 insertions(+) create mode 100644 contrib/color_profile_manager.lua diff --git a/contrib/color_profile_manager.lua b/contrib/color_profile_manager.lua new file mode 100644 index 0000000..bd09755 --- /dev/null +++ b/contrib/color_profile_manager.lua @@ -0,0 +1,372 @@ +--[[ + + color_profile_manager.lua - manage external darktable color profiles + + Copyright (C) 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 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 . +]] +--[[ + color_profile_manager - manage external darktable color profiles + + This script provides a tool to manage input and output external color + profiles used by darktable. Color profiles can be added or removed + to/from the correct directories so that darktable can find and use + them. + + ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT + * None + + USAGE + * require this script from your main lua file + * select the input or output set of color profiles + * a list of profiles is displayed. Click the check box beside the + profile name to select it for removal. Click the "remove profile" + button to remove the profile. + * use the file selector to select a color profile to add to the currently + selected set (input or output) + * click the "add profile" button to add the profile to the selected set + + BUGS, COMMENTS, SUGGESTIONS + * Send to Bill Ferguson, wpferguson@gmail.com or raise an issue on + https://github.com/dakrtable-org/lua-scripts +]] +local dt = require "darktable" +local du = require "lib/dtutils" +local df = require "lib/dtutils.file" + +du.check_min_api_version("7.0.0", "color_profile_manager") + +-- - - - - - - - - - - - - - - - - - - - - - - - +-- L O C A L I Z A T I O N +-- - - - - - - - - - - - - - - - - - - - - - - - + +local gettext = dt.gettext + +-- Tell gettext where to find the .mo file translating messages for a particular domain +gettext.bindtextdomain("color_profile_manager", dt.configuration.config_dir .. "/lua/locale/") + +local function _(msgid) + return gettext.dgettext("color_profile_manager", msgid) +end + +-- - - - - - - - - - - - - - - - - - - - - - - - +-- C O N S T A N T S +-- - - - - - - - - - - - - - - - - - - - - - - - + +local MODULE_NAME = "color_profile_manager" +local PS = dt.configuration.running_os == "windows" and "\\" or "/" +local CS = dt.configuration.running_os == "windows" and "&" or ";" +local DIR_CMD = dt.configuration.running_os == "windows" and "dir /b" or "ls" +local COLOR_IN_DIR = dt.configuration.config_dir .. PS .. "color" .. PS .. "in" +local COLOR_OUT_DIR = dt.configuration.config_dir .. PS .. "color" .. PS .. "out" + +-- - - - - - - - - - - - - - - - - - - - - - - - +-- N A M E S P A C E +-- - - - - - - - - - - - - - - - - - - - - - - - + +local cpm = {} +cpm.widgets = {} +cpm.widgets.profile_box = dt.new_widget("box"){ + orientation = "vertical", +} +cpm.module_installed = false +cpm.event_registered = false +-- - - - - - - - - - - - - - - - - - - - - - - - +-- F U N C T I O N S +-- - - - - - - - - - - - - - - - - - - - - - - - + +local function check_for_directories() + if not df.test_file(COLOR_IN_DIR, "d") then + dt.print_log("didn't find " .. COLOR_IN_DIR) + return false + elseif not df.test_file(COLOR_OUT_DIR, "d") then + dt.print_log("didn't find " .. COLOR_OUT_DIR) + return false + else + dt.print_log("both directories exist") + return true + end +end + +local function add_directories() + df.mkdir(COLOR_IN_DIR) + df.mkdir(COLOR_OUT_DIR) +end + +local function list_profiles(dir) + local files = {} + local p = io.popen(DIR_CMD .. " " .. dir) + if p then + for line in p:lines() do + table.insert(files, line) + end + end + p:close() + return files +end + +local function add_profile(file, dir) + df.file_copy(file, dir) + dt.print(_("added color profile " .. file .. " to " .. dir)) + dt.print_log("color profile " .. file .. " added to " .. dir) +end + +local function remove_profile(file, dir) + os.remove(dir .. PS .. file) + dt.print(_("removed color profile " .. file .. " from " .. dir)) + dt.print_log("color profile " .. file .. " removed from " .. dir) +end + +local function clear_profile_box() + for i = #cpm.widgets.profile_box, 1, -1 do + cpm.widgets.profile_box[i] = nil + end +end + +local function add_profile_callback() + local profile_dir = COLOR_IN_DIR + if cpm.widgets.profile_set.selected == 2 then + profile_dir = COLOR_OUT_DIR + end + + local new_profile = cpm.widgets.profile_selector.value + if string.lower(df.get_filetype(new_profile)) ~= "icm" and string.lower(df.get_filetype(new_profile)) ~= "icc" then + dt.print(_("ERROR: color profile must be an icc or icm file")) + dt.print_error(new_profile .. " selected and isn't an icc or icm file") + return + end + + -- set selector value to directory that new profile came from + -- in case there are more + cpm.widgets.profile_selector.value = df.get_path(cpm.widgets.profile_selector.value) + add_profile(new_profile, profile_dir) + local files = list_profiles(profile_dir) + local widgets = {} + + local profile_ptr = 1 + for i, file in ipairs(files) do + if #cpm.widgets.profile_box == 0 or cpm.widgets.profile_box[profile_ptr].label ~= file then + table.insert(widgets, dt.new_widget("check_button"){value = false, label = file}) + else + table.insert(widgets, cpm.widgets.profile_box[profile_ptr]) + profile_ptr = profile_ptr + 1 + if profile_ptr > #cpm.widgets.profile_box then + profile_ptr = #cpm.widgets.profile_box + end + end + end + clear_profile_box() + for _, widget in ipairs(widgets) do + table.insert(cpm.widgets.profile_box, widget) + end + if not cpm.widgets.remove_box.visible then + cpm.widgets.remove_box.visible = true + end +end + +local function remove_profile_callback() + local widgets_to_keep = {} + local profile_dir = COLOR_IN_DIR + if cpm.widgets.profile_set.selected == 2 then + profile_dir = COLOR_OUT_DIR + end + + for _, widget in ipairs(cpm.widgets.profile_box) do + if widget.value == true then + remove_profile(widget.label, profile_dir) + else + table.insert(widgets_to_keep, widget) + end + end + clear_profile_box() + for _, widget in ipairs(widgets_to_keep) do + table.insert(cpm.widgets.profile_box, widget) + end + if #cpm.widgets.profile_box == 0 then + cpm.widgets.remove_box.visible = false + else + cpm.widgets.remove_box.visible = true + end +end + +local function list_profile_callback(choice) + local list_dir = COLOR_IN_DIR + if choice == 2 then + list_dir = COLOR_OUT_DIR + end + + local files = list_profiles(list_dir) + + if #files == 0 then + cpm.widgets.remove_box.visible = false + else + cpm.widgets.remove_box.visible = true + end + + clear_profile_box() + + for i, file in ipairs(files) do + cpm.widgets.profile_box[i] = dt.new_widget("check_button"){value = false, label = file} + end +end + +local function install_module() + if not cpm.module_installed then + dt.register_lib( + MODULE_NAME, -- Module name + _("color profile manager"), -- Visible name + true, -- expandable + false, -- resetable + {[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_LEFT_CENTER", 0}}, -- containers + cpm.widgets.main_widget, + nil,-- view_enter + nil -- view_leave + ) + cpm.module_installed = true + dt.control.sleep(500) + if not cpm.initialized then + cpm.widgets.profile_set.visible = false + cpm.widgets.add_box.visible = false + cpm.widgets.remove_box.visible = false + else + cpm.widgets.profile_set.selected = 1 + end + end +end + +local function destroy() + dt.gui.libs[MODULE_NAME].visible = false + return +end + +local function restart() + dt.gui.libs[MODULE_NAME].visible = true + return +end +-- - - - - - - - - - - - - - - - - - - - - - - - +-- W I D G E T S +-- - - - - - - - - - - - - - - - - - - - - - - - + +cpm.initialized = check_for_directories() +dt.print_log("cpm.initialized is " .. tostring(cpm.initialized)) + +if not cpm.initialized then + dt.print_log("creating init_box") + cpm.widgets.init_box = dt.new_widget("box"){ + orientation = "vertical", + dt.new_widget("button"){ + label = _("initialize color profiles"), + tooltip = _("create the directory structure to contain the color profiles"), + clicked_callback = function(this) + add_directories() + cpm.initialized = true + cpm.widgets.profile_set.visible = true + cpm.widgets.add_box.visible = true + cpm.widgets.remove_box.visible = false + cpm.widgets.init_box.visible = false + cpm.widgets.profile_set.selected = 1 + end + } + } +end + +cpm.widgets.profile_set = dt.new_widget("combobox"){ + label = _("select profile set"), + tooltip = _("select input or output profiles"), + visible = cpm.initialized and false or true, + changed_callback = function(this) + if cpm.initialized then + list_profile_callback(this.selected) + end + end, + _("input"), _("output"), +} + +cpm.widgets.profile_selector = dt.new_widget("file_chooser_button"){ + title = _("select color profile to add"), + tooltip = _("select the .icc or .icm file to add"), + is_directory = false +} + +cpm.widgets.add_box = dt.new_widget("box"){ + orientation = "vertical", + visible = cpm.initialized and true or false, + dt.new_widget("section_label"){label = _("add profile")}, + cpm.widgets.profile_selector, + dt.new_widget("button"){ + label = _("add selected color profile"), + tooltip = _("add selected file to profiles"), + clicked_callback = function(this) + add_profile_callback() + end + } +} + +cpm.widgets.remove_box = dt.new_widget("box"){ + orientation = "vertical", + visible = cpm.initialized and true or false, + dt.new_widget("section_label"){label = _("remove profile")}, + cpm.widgets.profile_box, + dt.new_widget("button"){ + label = _("remove selected profile(s)"), + tooltip = _("remove the checked profile(s)"), + clicked_callback = function(this) + remove_profile_callback() + end + } +} + +local main_widgets = {} + +if not cpm.initialized then + table.insert(main_widgets, cpm.widgets.init_box) +end +table.insert(main_widgets, cpm.widgets.profile_set) +table.insert(main_widgets, cpm.widgets.remove_box) +table.insert(main_widgets, cpm.widgets.add_box) + +cpm.widgets.main_widget = dt.new_widget("box"){ + orientation = "vertical", + table.unpack(main_widgets) +} + +-- - - - - - - - - - - - - - - - - - - - - - - - +-- M A I N +-- - - - - - - - - - - - - - - - - - - - - - - - + +if dt.gui.current_view().id == "lighttable" then + install_module() +else + if not cpm.event_registered then + dt.register_event( + MODULE_NAME, "view-changed", + function(event, old_view, new_view) + if new_view.name == "lighttable" and old_view.name == "darkroom" then + install_module() + end + end + ) + cpm.event_registered = true + end +end + +local script_data = {} + +script_data.destroy = destroy +script_data.restart = restart +script_data.destroy_method = "hide" +script_data.show = restart + +return script_data \ No newline at end of file