From 3d3ad89fba13c0fa10e83e288375dd88cddc6a83 Mon Sep 17 00:00:00 2001 From: "U-DESKTOP-HQME86J\\marco" Date: Fri, 20 Dec 2019 23:00:57 +0100 Subject: [PATCH 1/6] Add ext_editor.lua to contrib --- contrib/ext_editor.lua | 255 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 contrib/ext_editor.lua diff --git a/contrib/ext_editor.lua b/contrib/ext_editor.lua new file mode 100644 index 0000000..c7b464b --- /dev/null +++ b/contrib/ext_editor.lua @@ -0,0 +1,255 @@ +--[[ + + DESCRIPTION + ext_editor.lua - edit images with external editors - for Windows only at the moment + + This script provides helpers to edit image files with programs extenal to darktable. + It adds: + - a new target storage "collection". Image exported will be reimported to collection for + further edit with external programs + - a new lighttable module "external editors", to select a program from a list of up to + - 9 external editors and run it on a selected image + - a set of lua preferences in order to configure name and path of up to 9 external editors + - a set of lua shortcuts in order to quick launch the external editors + + USAGE + * require this script from main lua file + + * in "preferences / lua options" configure name and path of external programs + * in "preferences / shortcuts / lua" configure shortcuts for external programs (optional) + + * in the export dialog choose "collection" and select the format and bit depth for the + exported image + * press "export" + * the exported image will be imported into collection and grouped with the original image + + * select an image for editing with en external program + * in lighttable / external editors, select program and press "edit with external program" + * edit the image with the external editor, overwite the file, quit the external program + * lighttable will be updated + + CAVEATS + * currently developed and tested only for Windows + + BUGS, COMMENTS, SUGGESTIONS + * send to Marco Carrarini, marco.carrarini@gmail.com + + CHANGES + * 20191220 - initial version + +]] + +local dt = require "darktable" +local df = require "lib/dtutils.file" +local dtsys = require "lib/dtutils.system" + +dt.preferences.register("ext_editor","program_path_9", "string","path to external editor 9", "path to external editor","") +dt.preferences.register("ext_editor","program_name_9", "string","name of external editor 9", "name of external editor","") + +dt.preferences.register("ext_editor","program_path_8", "string","path to external editor 8", "path to external editor","") +dt.preferences.register("ext_editor","program_name_8", "string","name of external editor 8", "name of external editor","") + +dt.preferences.register("ext_editor","program_path_7", "string","path to external editor 7", "path to external editor","") +dt.preferences.register("ext_editor","program_name_7", "string","name of external editor 7", "name of external editor","") + +dt.preferences.register("ext_editor","program_path_6", "string","path to external editor 6", "path to external editor","") +dt.preferences.register("ext_editor","program_name_6", "string","name of external editor 6", "name of external editor","") + +dt.preferences.register("ext_editor","program_path_5", "string","path to external editor 5", "path to external editor","") +dt.preferences.register("ext_editor","program_name_5", "string","name of external editor 5", "name of external editor","") + +dt.preferences.register("ext_editor","program_path_4", "string","path to external editor 4", "path to external editor","") +dt.preferences.register("ext_editor","program_name_4", "string","name of external editor 4", "name of external editor","") + +dt.preferences.register("ext_editor","program_path_3", "string","path to external editor 3", "path to external editor","") +dt.preferences.register("ext_editor","program_name_3", "string","name of external editor 3", "name of external editor","") + +dt.preferences.register("ext_editor","program_path_2", "string","path to external editor 2", "path to external editor","") +dt.preferences.register("ext_editor","program_name_2", "string","name of external editor 2", "name of external editor","") + +dt.preferences.register("ext_editor","program_path_1", "string","path to external editor 1", "path to external editor","") +dt.preferences.register("ext_editor","program_name_1", "string","name of external editor 1", "name of external editor","") + +local programs = { + [1] = dt.preferences.read("ext_editor","program_path_1", "string"), + [2] = dt.preferences.read("ext_editor","program_path_2", "string"), + [3] = dt.preferences.read("ext_editor","program_path_3", "string"), + [4] = dt.preferences.read("ext_editor","program_path_4", "string"), + [5] = dt.preferences.read("ext_editor","program_path_5", "string"), + [6] = dt.preferences.read("ext_editor","program_path_6", "string"), + [7] = dt.preferences.read("ext_editor","program_path_7", "string"), + [8] = dt.preferences.read("ext_editor","program_path_8", "string"), + [9] = dt.preferences.read("ext_editor","program_path_9", "string") + } + +local names = { + [1] = dt.preferences.read("ext_editor","program_name_1", "string"), + [2] = dt.preferences.read("ext_editor","program_name_2", "string"), + [3] = dt.preferences.read("ext_editor","program_name_3", "string"), + [4] = dt.preferences.read("ext_editor","program_name_4", "string"), + [5] = dt.preferences.read("ext_editor","program_name_5", "string"), + [6] = dt.preferences.read("ext_editor","program_name_6", "string"), + [7] = dt.preferences.read("ext_editor","program_name_7", "string"), + [8] = dt.preferences.read("ext_editor","program_name_8", "string"), + [9] = dt.preferences.read("ext_editor","program_name_9", "string") + } + +if not (dt.preferences.read("ext_editor","initialized", "bool")) then + dt.preferences.write("ext_editor","lastchoice", "integer", 1) + dt.preferences.write("ext_editor","initialized", "bool", true) + end + +local lastchoice = dt.preferences.read("ext_editor","lastchoice", "integer") + +local combobox = dt.new_widget("combobox") + { + label = "choose program", + value = lastchoice, -- remember status, + names[1], + names[2], + names[3], + names[4], + names[5], + names[6], + names[7], + names[8], + names[9] + } + + +local function OpenWith(choice) + + local images = dt.gui.selection() + local curr_image = "" + + if #images == 1 then + for _,image in pairs(images) do + curr_image = image.path.."\\"..image.filename + local run_cmd = '"'..programs[choice]..'" "'..curr_image..'"' --additional quotes for spaces in filename + dt.print("Launching "..names[choice].."...") + dtsys.external_command(run_cmd) + --image:drop_cache() *** it doesn't work! why ? Delete and Import instead *** + image:delete() + local myimage = dt.database.import(curr_image) + dt.preferences.write("ext_editor","lastchoice", "integer", choice) + end + else + dt.print('please select one image') + end + + end + +local button1 = dt.new_widget("button") + { + label = "edit with external program", + clicked_callback = function (_) + local choice = combobox.selected + OpenWith(choice) + end + } + + +local function group_if_not_member(img, new_img) + local image_table = img:get_group_members() + local is_member = false + for _,image in ipairs(image_table) do + if image.filename == new_img.filename then + is_member = true + end + end + if not is_member then + new_img:group_with(img.group_leader) + end + end + + +local function show_status(storage, image, format, filename, + number, total, high_quality, extra_data) + dt.print(string.format(_("Export Image %i/%i"), number, total)) + end + + +local function export2collection(storage, image_table, extra_data) + + -- list of exported images + local img_list + + -- reset and create image list + img_list = "" + + for _,exp_img in pairs(image_table) do + exp_img = df.sanitize_filename(exp_img) + img_list = img_list ..exp_img.. " " + end + +--[[ + for each of the image, exported image pairs + move the exported image into the directory with the original + then import the image into the database which will group it with the original + and then copy over any tags other than darktable tags +]] + for image,exported_image in pairs(image_table) do + + local myimage_name = image.path .. "/" .. df.get_filename(exported_image) + + while df.check_if_file_exists(myimage_name) do + myimage_name = df.filename_increment(myimage_name) + -- limit to 99 more exports of the original export + if string.match(df.get_basename(myimage_name), "_(d-)$") == "99" then + break + end + end + + local result = df.file_move(exported_image, myimage_name) + + if result then + local myimage = dt.database.import(myimage_name) + + group_if_not_member(image, myimage) + + for _,tag in pairs(dt.tags.get_tags(image)) do + if not (string.sub(tag.name,1,9) == "darktable") then + dt.tags.attach(tag,myimage) + end + end + end + end + end + + +local function program_shortcut(event, shortcut) + local choice = tonumber(string.sub(shortcut, -1)) + OpenWith(choice) +end + + +dt.register_storage("exp2coll", "collection", show_status, export2collection) + +dt.register_lib( + "ext_editor", -- module name + "external editors", -- name + true, -- expandable + false, -- resetable + {[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers + dt.new_widget("box") -- widget + { + orientation = "vertical", + combobox, + button1 + }, + nil,-- view_enter + nil -- view_leave + ) + +dt.register_event("shortcut", program_shortcut, "edit with program 1") +dt.register_event("shortcut", program_shortcut, "edit with program 2") +dt.register_event("shortcut", program_shortcut, "edit with program 3") +dt.register_event("shortcut", program_shortcut, "edit with program 4") +dt.register_event("shortcut", program_shortcut, "edit with program 5") +dt.register_event("shortcut", program_shortcut, "edit with program 6") +dt.register_event("shortcut", program_shortcut, "edit with program 7") +dt.register_event("shortcut", program_shortcut, "edit with program 8") +dt.register_event("shortcut", program_shortcut, "edit with program 9") + +-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua +-- kate: hl Lua; From 54ff56b02136f52e8e642cf215dac58d050f652b Mon Sep 17 00:00:00 2001 From: "U-DESKTOP-HQME86J\\marco" Date: Sat, 21 Dec 2019 21:12:46 +0100 Subject: [PATCH 2/6] added copy and edit button + code cleaning --- contrib/ext_editor.lua | 436 +++++++++++++++++++++++++---------------- 1 file changed, 265 insertions(+), 171 deletions(-) diff --git a/contrib/ext_editor.lua b/contrib/ext_editor.lua index c7b464b..5d934a3 100644 --- a/contrib/ext_editor.lua +++ b/contrib/ext_editor.lua @@ -30,19 +30,282 @@ CAVEATS * currently developed and tested only for Windows + * button are not equal in size and centered BUGS, COMMENTS, SUGGESTIONS * send to Marco Carrarini, marco.carrarini@gmail.com CHANGES - * 20191220 - initial version + * 20191220 - initial version, PR done + * 20191221 - reworked, cleaned, added "copy and edit" ]] + local dt = require "darktable" local df = require "lib/dtutils.file" local dtsys = require "lib/dtutils.system" + +-- executables of the external editors +local program_paths = { + [1] = dt.preferences.read("ext_editor","program_path_1", "string"), + [2] = dt.preferences.read("ext_editor","program_path_2", "string"), + [3] = dt.preferences.read("ext_editor","program_path_3", "string"), + [4] = dt.preferences.read("ext_editor","program_path_4", "string"), + [5] = dt.preferences.read("ext_editor","program_path_5", "string"), + [6] = dt.preferences.read("ext_editor","program_path_6", "string"), + [7] = dt.preferences.read("ext_editor","program_path_7", "string"), + [8] = dt.preferences.read("ext_editor","program_path_8", "string"), + [9] = dt.preferences.read("ext_editor","program_path_9", "string") + } + + +-- friendly name of the external editors +local program_names = { + [1] = dt.preferences.read("ext_editor","program_name_1", "string"), + [2] = dt.preferences.read("ext_editor","program_name_2", "string"), + [3] = dt.preferences.read("ext_editor","program_name_3", "string"), + [4] = dt.preferences.read("ext_editor","program_name_4", "string"), + [5] = dt.preferences.read("ext_editor","program_name_5", "string"), + [6] = dt.preferences.read("ext_editor","program_name_6", "string"), + [7] = dt.preferences.read("ext_editor","program_name_7", "string"), + [8] = dt.preferences.read("ext_editor","program_name_8", "string"), + [9] = dt.preferences.read("ext_editor","program_name_9", "string") + } + + +-- last used editor +if not (dt.preferences.read("ext_editor","initialized", "bool")) then + dt.preferences.write("ext_editor","lastchoice", "integer", 1) + dt.preferences.write("ext_editor","initialized", "bool", true) + end +local lastchoice = dt.preferences.read("ext_editor","lastchoice", "integer") + + +-- combobox +local combobox = dt.new_widget("combobox") + { + label = "choose program", + tooltip = "select the external editor from the list", + value = lastchoice, -- remember status, + program_names[1], + program_names[2], + program_names[3], + program_names[4], + program_names[5], + program_names[6], + program_names[7], + program_names[8], + program_names[9] + } + + +-- group image with leader image +local function group_if_not_member(img, new_img) + local image_table = img:get_group_members() + local is_member = false + for _,image in ipairs(image_table) do + if image.filename == new_img.filename then + is_member = true + end + end + if not is_member then + new_img:group_with(img.group_leader) + end + end + + +-- shows export progress +local function show_status(storage, image, format, filename, + number, total, high_quality, extra_data) + dt.print(string.format(_("Export Image %i/%i"), number, total)) + end + + +-- callback for button "edit" +local function OpenWith(choice) + + local images = dt.gui.selection() + + if #images == 1 then + for _, image in pairs(images) do + + -- image to be edited + name = image.path.."\\"..image.filename + + -- launch the external editor + local run_cmd = '"'..program_paths[choice]..'" "'..name..'"' + dt.print("Launching "..program_names[choice].."...") + dtsys.external_command(run_cmd) + + -- refresh image cache + image:drop_cache() + + -- remember the last editor used + dt.preferences.write("ext_editor","lastchoice", "integer", choice) + end + else + dt.print('please select one image') + end + + end + + +-- callback for button "copy and edit" +local function CopyAndOpenWith(choice) + + local images = dt.gui.selection() + + if #images == 1 then + for _,image in pairs(images) do + + -- image to be copied and edited + local name = image.path.."\\"..image.filename + local new_name = name + + -- create unique filename + while df.check_if_file_exists(new_name) do + new_name = df.filename_increment(new_name) + end + + -- limit to 99 more exports of the original export + if string.match(df.get_basename(new_name), "_(d-)$") == "99" then + break + end + + -- physical copy + local result = df.file_copy(name, new_name) + + if result then + -- launch the external editor + local run_cmd = '"'..program_paths[choice]..'" "'..new_name..'"' + dt.print("Launching "..program_names[choice].."...") + dtsys.external_command(run_cmd) + + -- import in database and group + local new_image = dt.database.import(new_name) + group_if_not_member(image, new_image) + + -- copy image tags + for _,tag in pairs(dt.tags.get_tags(image)) do + if not (string.sub(tag.name,1,9) == "darktable") then + dt.tags.attach(tag, new_image) + end + end + + -- remember the last editor used + dt.preferences.write("ext_editor","lastchoice", "integer", choice) + end + end + else + dt.print('please select one image') + end + + + end + + +-- button edit +local button1 = dt.new_widget("button") + { + label = "edit", + tooltip = "open the selected image in external editor", + dt_lua_align_t = center, + clicked_callback = function (_) + local choice = combobox.selected + OpenWith(choice) + end + } + + +-- button copy and edit +local button2 = dt.new_widget("button") + { + label = "edit a copy", + tooltip = "create a copy of the selected image and open it in external editor", + clicked_callback = function (_) + local choice = combobox.selected + CopyAndOpenWith(choice) + end + } + + +-- box for the two buttons +local box1 = dt.new_widget ("box") { + orientation = "horizontal", + button1, + button2 +} + + +-- export images and reimport in collection +local function export2collection(storage, image_table, extra_data) + + for image, temp_name in pairs(image_table) do + + -- images are first exported in temp folder + -- create unique filename + local new_name = image.path .."\\"..df.get_filename(temp_name) + while df.check_if_file_exists(new_name) do + new_name = df.filename_increment(new_name) + + -- limit to 99 more exports of the original export + if string.match(df.get_basename(new_name), "_(d-)$") == "99" then + break + end + end + + -- image moved to collection folder + local result = df.file_move(temp_name, new_name) + + if result then + + -- import in database and group + local new_image = dt.database.import(new_name) + group_if_not_member(image, new_image) + + -- copy image tags + for _,tag in pairs(dt.tags.get_tags(image)) do + if not (string.sub(tag.name,1,9) == "darktable") then + dt.tags.attach(tag, new_image) + end + end + end + end + end + + +-- callback function for shortcuts +local function program_shortcut(event, shortcut) + local choice = tonumber(string.sub(shortcut, -1)) + OpenWith(choice) +end + + +-- register new storage +dt.register_storage("exp2coll", "collection", show_status, export2collection) + + +-- register new lighttable module +dt.register_lib( + "ext_editor", -- module name + "external editors", -- name + true, -- expandable + false, -- resetable + {[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, + dt.new_widget("box") -- widget + { + orientation = "vertical", + combobox, + box1 + }, + nil,-- view_enter + nil -- view_leave + ) + + +-- register new preferences dt.preferences.register("ext_editor","program_path_9", "string","path to external editor 9", "path to external editor","") dt.preferences.register("ext_editor","program_name_9", "string","name of external editor 9", "name of external editor","") @@ -70,177 +333,8 @@ dt.preferences.register("ext_editor","program_name_2", "string","name of externa dt.preferences.register("ext_editor","program_path_1", "string","path to external editor 1", "path to external editor","") dt.preferences.register("ext_editor","program_name_1", "string","name of external editor 1", "name of external editor","") -local programs = { - [1] = dt.preferences.read("ext_editor","program_path_1", "string"), - [2] = dt.preferences.read("ext_editor","program_path_2", "string"), - [3] = dt.preferences.read("ext_editor","program_path_3", "string"), - [4] = dt.preferences.read("ext_editor","program_path_4", "string"), - [5] = dt.preferences.read("ext_editor","program_path_5", "string"), - [6] = dt.preferences.read("ext_editor","program_path_6", "string"), - [7] = dt.preferences.read("ext_editor","program_path_7", "string"), - [8] = dt.preferences.read("ext_editor","program_path_8", "string"), - [9] = dt.preferences.read("ext_editor","program_path_9", "string") - } -local names = { - [1] = dt.preferences.read("ext_editor","program_name_1", "string"), - [2] = dt.preferences.read("ext_editor","program_name_2", "string"), - [3] = dt.preferences.read("ext_editor","program_name_3", "string"), - [4] = dt.preferences.read("ext_editor","program_name_4", "string"), - [5] = dt.preferences.read("ext_editor","program_name_5", "string"), - [6] = dt.preferences.read("ext_editor","program_name_6", "string"), - [7] = dt.preferences.read("ext_editor","program_name_7", "string"), - [8] = dt.preferences.read("ext_editor","program_name_8", "string"), - [9] = dt.preferences.read("ext_editor","program_name_9", "string") - } - -if not (dt.preferences.read("ext_editor","initialized", "bool")) then - dt.preferences.write("ext_editor","lastchoice", "integer", 1) - dt.preferences.write("ext_editor","initialized", "bool", true) - end - -local lastchoice = dt.preferences.read("ext_editor","lastchoice", "integer") - -local combobox = dt.new_widget("combobox") - { - label = "choose program", - value = lastchoice, -- remember status, - names[1], - names[2], - names[3], - names[4], - names[5], - names[6], - names[7], - names[8], - names[9] - } - - -local function OpenWith(choice) - - local images = dt.gui.selection() - local curr_image = "" - - if #images == 1 then - for _,image in pairs(images) do - curr_image = image.path.."\\"..image.filename - local run_cmd = '"'..programs[choice]..'" "'..curr_image..'"' --additional quotes for spaces in filename - dt.print("Launching "..names[choice].."...") - dtsys.external_command(run_cmd) - --image:drop_cache() *** it doesn't work! why ? Delete and Import instead *** - image:delete() - local myimage = dt.database.import(curr_image) - dt.preferences.write("ext_editor","lastchoice", "integer", choice) - end - else - dt.print('please select one image') - end - - end - -local button1 = dt.new_widget("button") - { - label = "edit with external program", - clicked_callback = function (_) - local choice = combobox.selected - OpenWith(choice) - end - } - - -local function group_if_not_member(img, new_img) - local image_table = img:get_group_members() - local is_member = false - for _,image in ipairs(image_table) do - if image.filename == new_img.filename then - is_member = true - end - end - if not is_member then - new_img:group_with(img.group_leader) - end - end - - -local function show_status(storage, image, format, filename, - number, total, high_quality, extra_data) - dt.print(string.format(_("Export Image %i/%i"), number, total)) - end - - -local function export2collection(storage, image_table, extra_data) - - -- list of exported images - local img_list - - -- reset and create image list - img_list = "" - - for _,exp_img in pairs(image_table) do - exp_img = df.sanitize_filename(exp_img) - img_list = img_list ..exp_img.. " " - end - ---[[ - for each of the image, exported image pairs - move the exported image into the directory with the original - then import the image into the database which will group it with the original - and then copy over any tags other than darktable tags -]] - for image,exported_image in pairs(image_table) do - - local myimage_name = image.path .. "/" .. df.get_filename(exported_image) - - while df.check_if_file_exists(myimage_name) do - myimage_name = df.filename_increment(myimage_name) - -- limit to 99 more exports of the original export - if string.match(df.get_basename(myimage_name), "_(d-)$") == "99" then - break - end - end - - local result = df.file_move(exported_image, myimage_name) - - if result then - local myimage = dt.database.import(myimage_name) - - group_if_not_member(image, myimage) - - for _,tag in pairs(dt.tags.get_tags(image)) do - if not (string.sub(tag.name,1,9) == "darktable") then - dt.tags.attach(tag,myimage) - end - end - end - end - end - - -local function program_shortcut(event, shortcut) - local choice = tonumber(string.sub(shortcut, -1)) - OpenWith(choice) -end - - -dt.register_storage("exp2coll", "collection", show_status, export2collection) - -dt.register_lib( - "ext_editor", -- module name - "external editors", -- name - true, -- expandable - false, -- resetable - {[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, -- containers - dt.new_widget("box") -- widget - { - orientation = "vertical", - combobox, - button1 - }, - nil,-- view_enter - nil -- view_leave - ) - +-- register new shortcuts dt.register_event("shortcut", program_shortcut, "edit with program 1") dt.register_event("shortcut", program_shortcut, "edit with program 2") dt.register_event("shortcut", program_shortcut, "edit with program 3") From 64c8f8fc3e78b03dd1d764d1eb16ea7a551e99ac Mon Sep 17 00:00:00 2001 From: "U-DESKTOP-HQME86J\\marco" Date: Sun, 22 Dec 2019 21:08:32 +0100 Subject: [PATCH 3/6] added darkroom mode --- contrib/ext_editor.lua | 192 +++++++++++++++++++++++------------------ 1 file changed, 110 insertions(+), 82 deletions(-) diff --git a/contrib/ext_editor.lua b/contrib/ext_editor.lua index 5d934a3..9ca7d4c 100644 --- a/contrib/ext_editor.lua +++ b/contrib/ext_editor.lua @@ -3,7 +3,7 @@ DESCRIPTION ext_editor.lua - edit images with external editors - for Windows only at the moment - This script provides helpers to edit image files with programs extenal to darktable. + This script provides helpers to edit image files with programs external to darktable. It adds: - a new target storage "collection". Image exported will be reimported to collection for further edit with external programs @@ -24,20 +24,40 @@ * the exported image will be imported into collection and grouped with the original image * select an image for editing with en external program - * in lighttable / external editors, select program and press "edit with external program" - * edit the image with the external editor, overwite the file, quit the external program - * lighttable will be updated + * in lighttable / external editors, select program and press "edit" + * edit the image with the external editor, overwite the file, quit the external program + * the selected image will be updated + or + * in lighttable / external editors, select program and press "edit a copy" + * edit the image with the external editor, overwite the file, quit the external program + * a copy of the selected image will be created and updated + or + * in lighttable select target storage "collection" + * enter in darkroom + * to create an export or a copy press CRTL+E + * use the shortcut to edit the current image with the corresponding external editor + * overwite the file, quit the external program + * the darkroom view will be updated + + * warning: mouseover on lighttable / filmstrip will prevail on current image + * this is the default DT behavior, not a bug of this script CAVEATS * currently developed and tested only for Windows - * button are not equal in size and centered - + + TODO + * localization + * check min API version + * support other OS + * buttons are not equal in size and centered + BUGS, COMMENTS, SUGGESTIONS * send to Marco Carrarini, marco.carrarini@gmail.com CHANGES * 20191220 - initial version, PR done * 20191221 - reworked, cleaned, added "copy and edit" + * 20191222 - added darkroom mode ]] @@ -83,24 +103,6 @@ if not (dt.preferences.read("ext_editor","initialized", "bool")) then local lastchoice = dt.preferences.read("ext_editor","lastchoice", "integer") --- combobox -local combobox = dt.new_widget("combobox") - { - label = "choose program", - tooltip = "select the external editor from the list", - value = lastchoice, -- remember status, - program_names[1], - program_names[2], - program_names[3], - program_names[4], - program_names[5], - program_names[6], - program_names[7], - program_names[8], - program_names[9] - } - - -- group image with leader image local function group_if_not_member(img, new_img) local image_table = img:get_group_members() @@ -119,15 +121,13 @@ local function group_if_not_member(img, new_img) -- shows export progress local function show_status(storage, image, format, filename, number, total, high_quality, extra_data) - dt.print(string.format(_("Export Image %i/%i"), number, total)) + dt.print(string.format("Exporting Image %i/%i ...", number, total)) end -- callback for button "edit" -local function OpenWith(choice) - - local images = dt.gui.selection() - +local function OpenWith(images, choice) + if #images == 1 then for _, image in pairs(images) do @@ -139,11 +139,29 @@ local function OpenWith(choice) dt.print("Launching "..program_names[choice].."...") dtsys.external_command(run_cmd) - -- refresh image cache - image:drop_cache() + -- refresh the image view + -- note that only image:drop_cache() is not enough to refresh view in darkroom mode + -- therefore image must be deleted and reimported to force refresh + + local image_leader = image.group_leader + image:delete () + local new_image = dt.database.import(name) + new_image:group_with(image_leader) + + -- copy tags from image leader + for _,tag in pairs(dt.tags.get_tags(image_leader)) do + if not (string.sub(tag.name,1,9) == "darktable") then + dt.tags.attach(tag, new_image) + end + end -- remember the last editor used dt.preferences.write("ext_editor","lastchoice", "integer", choice) + + -- refresh darkroom view + if dt.gui.current_view () == dt.gui.views.darkroom then + dt.gui.views.darkroom.display_image(new_image) + end end else dt.print('please select one image') @@ -153,9 +171,7 @@ local function OpenWith(choice) -- callback for button "copy and edit" -local function CopyAndOpenWith(choice) - - local images = dt.gui.selection() +local function CopyAndOpenWith(images, choice) if #images == 1 then for _,image in pairs(images) do @@ -206,37 +222,11 @@ local function CopyAndOpenWith(choice) end --- button edit -local button1 = dt.new_widget("button") - { - label = "edit", - tooltip = "open the selected image in external editor", - dt_lua_align_t = center, - clicked_callback = function (_) - local choice = combobox.selected - OpenWith(choice) - end - } - - --- button copy and edit -local button2 = dt.new_widget("button") - { - label = "edit a copy", - tooltip = "create a copy of the selected image and open it in external editor", - clicked_callback = function (_) - local choice = combobox.selected - CopyAndOpenWith(choice) - end - } - - --- box for the two buttons -local box1 = dt.new_widget ("box") { - orientation = "horizontal", - button1, - button2 -} +-- callback function for shortcuts +local function program_shortcut(event, shortcut) + local choice = tonumber(string.sub(shortcut, -1)) + OpenWith(dt.gui.action_images, choice) + end -- export images and reimport in collection @@ -260,31 +250,63 @@ local function export2collection(storage, image_table, extra_data) local result = df.file_move(temp_name, new_name) if result then - -- import in database and group local new_image = dt.database.import(new_name) group_if_not_member(image, new_image) - - -- copy image tags - for _,tag in pairs(dt.tags.get_tags(image)) do - if not (string.sub(tag.name,1,9) == "darktable") then - dt.tags.attach(tag, new_image) - end - end end end end --- callback function for shortcuts -local function program_shortcut(event, shortcut) - local choice = tonumber(string.sub(shortcut, -1)) - OpenWith(choice) -end +-- combobox +local combobox = dt.new_widget("combobox") + { + label = "choose program", + tooltip = "select the external editor from the list", + value = lastchoice, -- remember status, + program_names[1], + program_names[2], + program_names[3], + program_names[4], + program_names[5], + program_names[6], + program_names[7], + program_names[8], + program_names[9] + } --- register new storage -dt.register_storage("exp2coll", "collection", show_status, export2collection) +-- button edit +local button1 = dt.new_widget("button") + { + label = " edit ", + tooltip = "open the selected image in external editor", + dt_lua_align_t = center, + clicked_callback = function (_) + local choice = combobox.selected + OpenWith(dt.gui.action_images, choice) + end + } + + +-- button edit a copy +local button2 = dt.new_widget("button") + { + label = " edit a copy ", + tooltip = "create a copy of the selected image and open it in external editor", + clicked_callback = function (_) + local choice = combobox.selected + CopyAndOpenWith(dt.gui.action_images, choice) + end + } + + +-- box for the two buttons +local box1 = dt.new_widget ("box") { + orientation = "horizontal", + button1, + button2 +} -- register new lighttable module @@ -305,7 +327,11 @@ dt.register_lib( ) --- register new preferences +-- register new storage +dt.register_storage("exp2coll", "collection", show_status, export2collection) + + +-- register the new preferences dt.preferences.register("ext_editor","program_path_9", "string","path to external editor 9", "path to external editor","") dt.preferences.register("ext_editor","program_name_9", "string","name of external editor 9", "name of external editor","") @@ -334,7 +360,7 @@ dt.preferences.register("ext_editor","program_path_1", "string","path to externa dt.preferences.register("ext_editor","program_name_1", "string","name of external editor 1", "name of external editor","") --- register new shortcuts +-- register the new shortcuts dt.register_event("shortcut", program_shortcut, "edit with program 1") dt.register_event("shortcut", program_shortcut, "edit with program 2") dt.register_event("shortcut", program_shortcut, "edit with program 3") @@ -345,5 +371,7 @@ dt.register_event("shortcut", program_shortcut, "edit with program 7") dt.register_event("shortcut", program_shortcut, "edit with program 8") dt.register_event("shortcut", program_shortcut, "edit with program 9") +-- end of script + -- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua -- kate: hl Lua; From 6a89357b547aea10c8d595762c79d889dac091dd Mon Sep 17 00:00:00 2001 From: "U-DESKTOP-HQME86J\\marco" Date: Mon, 23 Dec 2019 11:34:01 +0100 Subject: [PATCH 4/6] compatibility with Linux --- contrib/ext_editor.lua | 44 ++++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/contrib/ext_editor.lua b/contrib/ext_editor.lua index 9ca7d4c..63f7b93 100644 --- a/contrib/ext_editor.lua +++ b/contrib/ext_editor.lua @@ -1,7 +1,7 @@ --[[ DESCRIPTION - ext_editor.lua - edit images with external editors - for Windows only at the moment + ext_editor.lua - edit images with external editors This script provides helpers to edit image files with programs external to darktable. It adds: @@ -15,7 +15,7 @@ USAGE * require this script from main lua file - * in "preferences / lua options" configure name and path of external programs + * in "preferences / lua options" configure name and command/path of external programs * in "preferences / shortcuts / lua" configure shortcuts for external programs (optional) * in the export dialog choose "collection" and select the format and bit depth for the @@ -43,12 +43,11 @@ * this is the default DT behavior, not a bug of this script CAVEATS - * currently developed and tested only for Windows + * tested with DT 3.0 in Windows and Ubuntu + * MAC compatibility not tested TODO * localization - * check min API version - * support other OS * buttons are not equal in size and centered BUGS, COMMENTS, SUGGESTIONS @@ -58,15 +57,25 @@ * 20191220 - initial version, PR done * 20191221 - reworked, cleaned, added "copy and edit" * 20191222 - added darkroom mode + * 20191223 - check API version, OS compatibility ]] local dt = require "darktable" +local du = require "lib/dtutils" local df = require "lib/dtutils.file" local dtsys = require "lib/dtutils.system" +-- check API version +du.check_min_api_version("5.0.2", "ext_editor") -- darktable 3.0 + + +-- OS compatibility +local PS = dt.configuration.running_os == "windows" and "\\" or "/" + + -- executables of the external editors local program_paths = { [1] = dt.preferences.read("ext_editor","program_path_1", "string"), @@ -103,21 +112,6 @@ if not (dt.preferences.read("ext_editor","initialized", "bool")) then local lastchoice = dt.preferences.read("ext_editor","lastchoice", "integer") --- group image with leader image -local function group_if_not_member(img, new_img) - local image_table = img:get_group_members() - local is_member = false - for _,image in ipairs(image_table) do - if image.filename == new_img.filename then - is_member = true - end - end - if not is_member then - new_img:group_with(img.group_leader) - end - end - - -- shows export progress local function show_status(storage, image, format, filename, number, total, high_quality, extra_data) @@ -132,7 +126,7 @@ local function OpenWith(images, choice) for _, image in pairs(images) do -- image to be edited - name = image.path.."\\"..image.filename + name = image.path..PS..image.filename -- launch the external editor local run_cmd = '"'..program_paths[choice]..'" "'..name..'"' @@ -177,7 +171,7 @@ local function CopyAndOpenWith(images, choice) for _,image in pairs(images) do -- image to be copied and edited - local name = image.path.."\\"..image.filename + local name = image.path..PS..image.filename local new_name = name -- create unique filename @@ -201,7 +195,7 @@ local function CopyAndOpenWith(images, choice) -- import in database and group local new_image = dt.database.import(new_name) - group_if_not_member(image, new_image) + new_image:group_with(image.group_leader) -- copy image tags for _,tag in pairs(dt.tags.get_tags(image)) do @@ -236,7 +230,7 @@ local function export2collection(storage, image_table, extra_data) -- images are first exported in temp folder -- create unique filename - local new_name = image.path .."\\"..df.get_filename(temp_name) + local new_name = image.path ..PS..df.get_filename(temp_name) while df.check_if_file_exists(new_name) do new_name = df.filename_increment(new_name) @@ -252,7 +246,7 @@ local function export2collection(storage, image_table, extra_data) if result then -- import in database and group local new_image = dt.database.import(new_name) - group_if_not_member(image, new_image) + new_image:group_with(image.group_leader) end end end From 5d7a60206ed4ff3591316e711ba023d59c3830e9 Mon Sep 17 00:00:00 2001 From: "U-DESKTOP-HQME86J\\marco" Date: Wed, 25 Dec 2019 16:02:53 +0100 Subject: [PATCH 5/6] requested updates --- README.md | 1 + contrib/ext_editor.lua | 371 ----------------------------------------- 2 files changed, 1 insertion(+), 371 deletions(-) delete mode 100644 contrib/ext_editor.lua diff --git a/README.md b/README.md index 2eb1620..2c49299 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ CollectHelper|Yes|LMW|Add buttons to selected images module to manipulate the co copy_attach_detach_tags|Yes|LMW|Copy and paste tags from/to images cr2hdr|Yes|L|Process image created with Magic Lantern Dual ISO enfuseAdvanced|No|LMW|Merge multiple images into Dynamic Range Increase (DRI) or Depth From Focus (DFF) images +ext_editor|No|LW|Export pictures to collection and edit them with up to nine user-defined external editors face_recognition|No|LM|Identify and tag images using facial recognition fujifilm_ratings|No|LM|Support importing Fujifilm ratings geoJSON_export|No|L|Create a geo JSON script with thumbnails for use in ... diff --git a/contrib/ext_editor.lua b/contrib/ext_editor.lua deleted file mode 100644 index 63f7b93..0000000 --- a/contrib/ext_editor.lua +++ /dev/null @@ -1,371 +0,0 @@ ---[[ - - DESCRIPTION - ext_editor.lua - edit images with external editors - - This script provides helpers to edit image files with programs external to darktable. - It adds: - - a new target storage "collection". Image exported will be reimported to collection for - further edit with external programs - - a new lighttable module "external editors", to select a program from a list of up to - - 9 external editors and run it on a selected image - - a set of lua preferences in order to configure name and path of up to 9 external editors - - a set of lua shortcuts in order to quick launch the external editors - - USAGE - * require this script from main lua file - - * in "preferences / lua options" configure name and command/path of external programs - * in "preferences / shortcuts / lua" configure shortcuts for external programs (optional) - - * in the export dialog choose "collection" and select the format and bit depth for the - exported image - * press "export" - * the exported image will be imported into collection and grouped with the original image - - * select an image for editing with en external program - * in lighttable / external editors, select program and press "edit" - * edit the image with the external editor, overwite the file, quit the external program - * the selected image will be updated - or - * in lighttable / external editors, select program and press "edit a copy" - * edit the image with the external editor, overwite the file, quit the external program - * a copy of the selected image will be created and updated - or - * in lighttable select target storage "collection" - * enter in darkroom - * to create an export or a copy press CRTL+E - * use the shortcut to edit the current image with the corresponding external editor - * overwite the file, quit the external program - * the darkroom view will be updated - - * warning: mouseover on lighttable / filmstrip will prevail on current image - * this is the default DT behavior, not a bug of this script - - CAVEATS - * tested with DT 3.0 in Windows and Ubuntu - * MAC compatibility not tested - - TODO - * localization - * buttons are not equal in size and centered - - BUGS, COMMENTS, SUGGESTIONS - * send to Marco Carrarini, marco.carrarini@gmail.com - - CHANGES - * 20191220 - initial version, PR done - * 20191221 - reworked, cleaned, added "copy and edit" - * 20191222 - added darkroom mode - * 20191223 - check API version, OS compatibility - -]] - - -local dt = require "darktable" -local du = require "lib/dtutils" -local df = require "lib/dtutils.file" -local dtsys = require "lib/dtutils.system" - - --- check API version -du.check_min_api_version("5.0.2", "ext_editor") -- darktable 3.0 - - --- OS compatibility -local PS = dt.configuration.running_os == "windows" and "\\" or "/" - - --- executables of the external editors -local program_paths = { - [1] = dt.preferences.read("ext_editor","program_path_1", "string"), - [2] = dt.preferences.read("ext_editor","program_path_2", "string"), - [3] = dt.preferences.read("ext_editor","program_path_3", "string"), - [4] = dt.preferences.read("ext_editor","program_path_4", "string"), - [5] = dt.preferences.read("ext_editor","program_path_5", "string"), - [6] = dt.preferences.read("ext_editor","program_path_6", "string"), - [7] = dt.preferences.read("ext_editor","program_path_7", "string"), - [8] = dt.preferences.read("ext_editor","program_path_8", "string"), - [9] = dt.preferences.read("ext_editor","program_path_9", "string") - } - - --- friendly name of the external editors -local program_names = { - [1] = dt.preferences.read("ext_editor","program_name_1", "string"), - [2] = dt.preferences.read("ext_editor","program_name_2", "string"), - [3] = dt.preferences.read("ext_editor","program_name_3", "string"), - [4] = dt.preferences.read("ext_editor","program_name_4", "string"), - [5] = dt.preferences.read("ext_editor","program_name_5", "string"), - [6] = dt.preferences.read("ext_editor","program_name_6", "string"), - [7] = dt.preferences.read("ext_editor","program_name_7", "string"), - [8] = dt.preferences.read("ext_editor","program_name_8", "string"), - [9] = dt.preferences.read("ext_editor","program_name_9", "string") - } - - --- last used editor -if not (dt.preferences.read("ext_editor","initialized", "bool")) then - dt.preferences.write("ext_editor","lastchoice", "integer", 1) - dt.preferences.write("ext_editor","initialized", "bool", true) - end -local lastchoice = dt.preferences.read("ext_editor","lastchoice", "integer") - - --- shows export progress -local function show_status(storage, image, format, filename, - number, total, high_quality, extra_data) - dt.print(string.format("Exporting Image %i/%i ...", number, total)) - end - - --- callback for button "edit" -local function OpenWith(images, choice) - - if #images == 1 then - for _, image in pairs(images) do - - -- image to be edited - name = image.path..PS..image.filename - - -- launch the external editor - local run_cmd = '"'..program_paths[choice]..'" "'..name..'"' - dt.print("Launching "..program_names[choice].."...") - dtsys.external_command(run_cmd) - - -- refresh the image view - -- note that only image:drop_cache() is not enough to refresh view in darkroom mode - -- therefore image must be deleted and reimported to force refresh - - local image_leader = image.group_leader - image:delete () - local new_image = dt.database.import(name) - new_image:group_with(image_leader) - - -- copy tags from image leader - for _,tag in pairs(dt.tags.get_tags(image_leader)) do - if not (string.sub(tag.name,1,9) == "darktable") then - dt.tags.attach(tag, new_image) - end - end - - -- remember the last editor used - dt.preferences.write("ext_editor","lastchoice", "integer", choice) - - -- refresh darkroom view - if dt.gui.current_view () == dt.gui.views.darkroom then - dt.gui.views.darkroom.display_image(new_image) - end - end - else - dt.print('please select one image') - end - - end - - --- callback for button "copy and edit" -local function CopyAndOpenWith(images, choice) - - if #images == 1 then - for _,image in pairs(images) do - - -- image to be copied and edited - local name = image.path..PS..image.filename - local new_name = name - - -- create unique filename - while df.check_if_file_exists(new_name) do - new_name = df.filename_increment(new_name) - end - - -- limit to 99 more exports of the original export - if string.match(df.get_basename(new_name), "_(d-)$") == "99" then - break - end - - -- physical copy - local result = df.file_copy(name, new_name) - - if result then - -- launch the external editor - local run_cmd = '"'..program_paths[choice]..'" "'..new_name..'"' - dt.print("Launching "..program_names[choice].."...") - dtsys.external_command(run_cmd) - - -- import in database and group - local new_image = dt.database.import(new_name) - new_image:group_with(image.group_leader) - - -- copy image tags - for _,tag in pairs(dt.tags.get_tags(image)) do - if not (string.sub(tag.name,1,9) == "darktable") then - dt.tags.attach(tag, new_image) - end - end - - -- remember the last editor used - dt.preferences.write("ext_editor","lastchoice", "integer", choice) - end - end - else - dt.print('please select one image') - end - - - end - - --- callback function for shortcuts -local function program_shortcut(event, shortcut) - local choice = tonumber(string.sub(shortcut, -1)) - OpenWith(dt.gui.action_images, choice) - end - - --- export images and reimport in collection -local function export2collection(storage, image_table, extra_data) - - for image, temp_name in pairs(image_table) do - - -- images are first exported in temp folder - -- create unique filename - local new_name = image.path ..PS..df.get_filename(temp_name) - while df.check_if_file_exists(new_name) do - new_name = df.filename_increment(new_name) - - -- limit to 99 more exports of the original export - if string.match(df.get_basename(new_name), "_(d-)$") == "99" then - break - end - end - - -- image moved to collection folder - local result = df.file_move(temp_name, new_name) - - if result then - -- import in database and group - local new_image = dt.database.import(new_name) - new_image:group_with(image.group_leader) - end - end - end - - --- combobox -local combobox = dt.new_widget("combobox") - { - label = "choose program", - tooltip = "select the external editor from the list", - value = lastchoice, -- remember status, - program_names[1], - program_names[2], - program_names[3], - program_names[4], - program_names[5], - program_names[6], - program_names[7], - program_names[8], - program_names[9] - } - - --- button edit -local button1 = dt.new_widget("button") - { - label = " edit ", - tooltip = "open the selected image in external editor", - dt_lua_align_t = center, - clicked_callback = function (_) - local choice = combobox.selected - OpenWith(dt.gui.action_images, choice) - end - } - - --- button edit a copy -local button2 = dt.new_widget("button") - { - label = " edit a copy ", - tooltip = "create a copy of the selected image and open it in external editor", - clicked_callback = function (_) - local choice = combobox.selected - CopyAndOpenWith(dt.gui.action_images, choice) - end - } - - --- box for the two buttons -local box1 = dt.new_widget ("box") { - orientation = "horizontal", - button1, - button2 -} - - --- register new lighttable module -dt.register_lib( - "ext_editor", -- module name - "external editors", -- name - true, -- expandable - false, -- resetable - {[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, - dt.new_widget("box") -- widget - { - orientation = "vertical", - combobox, - box1 - }, - nil,-- view_enter - nil -- view_leave - ) - - --- register new storage -dt.register_storage("exp2coll", "collection", show_status, export2collection) - - --- register the new preferences -dt.preferences.register("ext_editor","program_path_9", "string","path to external editor 9", "path to external editor","") -dt.preferences.register("ext_editor","program_name_9", "string","name of external editor 9", "name of external editor","") - -dt.preferences.register("ext_editor","program_path_8", "string","path to external editor 8", "path to external editor","") -dt.preferences.register("ext_editor","program_name_8", "string","name of external editor 8", "name of external editor","") - -dt.preferences.register("ext_editor","program_path_7", "string","path to external editor 7", "path to external editor","") -dt.preferences.register("ext_editor","program_name_7", "string","name of external editor 7", "name of external editor","") - -dt.preferences.register("ext_editor","program_path_6", "string","path to external editor 6", "path to external editor","") -dt.preferences.register("ext_editor","program_name_6", "string","name of external editor 6", "name of external editor","") - -dt.preferences.register("ext_editor","program_path_5", "string","path to external editor 5", "path to external editor","") -dt.preferences.register("ext_editor","program_name_5", "string","name of external editor 5", "name of external editor","") - -dt.preferences.register("ext_editor","program_path_4", "string","path to external editor 4", "path to external editor","") -dt.preferences.register("ext_editor","program_name_4", "string","name of external editor 4", "name of external editor","") - -dt.preferences.register("ext_editor","program_path_3", "string","path to external editor 3", "path to external editor","") -dt.preferences.register("ext_editor","program_name_3", "string","name of external editor 3", "name of external editor","") - -dt.preferences.register("ext_editor","program_path_2", "string","path to external editor 2", "path to external editor","") -dt.preferences.register("ext_editor","program_name_2", "string","name of external editor 2", "name of external editor","") - -dt.preferences.register("ext_editor","program_path_1", "string","path to external editor 1", "path to external editor","") -dt.preferences.register("ext_editor","program_name_1", "string","name of external editor 1", "name of external editor","") - - --- register the new shortcuts -dt.register_event("shortcut", program_shortcut, "edit with program 1") -dt.register_event("shortcut", program_shortcut, "edit with program 2") -dt.register_event("shortcut", program_shortcut, "edit with program 3") -dt.register_event("shortcut", program_shortcut, "edit with program 4") -dt.register_event("shortcut", program_shortcut, "edit with program 5") -dt.register_event("shortcut", program_shortcut, "edit with program 6") -dt.register_event("shortcut", program_shortcut, "edit with program 7") -dt.register_event("shortcut", program_shortcut, "edit with program 8") -dt.register_event("shortcut", program_shortcut, "edit with program 9") - --- end of script - --- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua --- kate: hl Lua; From cfa71927de1434478e393205e0ed12f2e46261c7 Mon Sep 17 00:00:00 2001 From: "U-DESKTOP-HQME86J\\marco" Date: Wed, 25 Dec 2019 16:16:44 +0100 Subject: [PATCH 6/6] requested updates --- contrib/ext_editor.lua | 371 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 371 insertions(+) create mode 100644 contrib/ext_editor.lua diff --git a/contrib/ext_editor.lua b/contrib/ext_editor.lua new file mode 100644 index 0000000..0b5054b --- /dev/null +++ b/contrib/ext_editor.lua @@ -0,0 +1,371 @@ +--[[ + + DESCRIPTION + ext_editor.lua - edit images with external editors + + This script provides helpers to edit image files with programs external to darktable. + It adds: + - a new target storage "collection". Image exported will be reimported to collection for + further edit with external programs + - a new lighttable module "external editors", to select a program from a list of up to + - 9 external editors and run it on a selected image + - a set of lua preferences in order to configure name and path of up to 9 external editors + - a set of lua shortcuts in order to quick launch the external editors + + USAGE + * require this script from main lua file + + * in "preferences / lua options" configure name and path/command of external programs + * in "preferences / shortcuts / lua" configure shortcuts for external programs (optional) + + * in the export dialog choose "collection" and select the format and bit depth for the + exported image + * press "export" + * the exported image will be imported into collection and grouped with the original image + + * select an image for editing with en external program + * in lighttable / external editors, select program and press "edit" + * edit the image with the external editor, overwite the file, quit the external program + * the selected image will be updated + or + * in lighttable / external editors, select program and press "edit a copy" + * edit the image with the external editor, overwite the file, quit the external program + * a copy of the selected image will be created and updated + or + * in lighttable select target storage "collection" + * enter in darkroom + * to create an export or a copy press CRTL+E + * use the shortcut to edit the current image with the corresponding external editor + * overwite the file, quit the external program + * the darkroom view will be updated + + * warning: mouseover on lighttable / filmstrip will prevail on current image + * this is the default DT behavior, not a bug of this script + + CAVEATS + * tested with DT 3.0 in Windows and Ubuntu + * MAC compatibility not tested + + TODO + * localization + * buttons are not equal in size and centered + + BUGS, COMMENTS, SUGGESTIONS + * send to Marco Carrarini, marco.carrarini@gmail.com + + CHANGES + * 20191220 - initial version, PR done + * 20191221 - reworked, cleaned, added "copy and edit" + * 20191222 - added darkroom mode + * 20191223 - check API version, OS compatibility + +]] + + +local dt = require "darktable" +local du = require "lib/dtutils" +local df = require "lib/dtutils.file" +local dtsys = require "lib/dtutils.system" + + +-- check API version +du.check_min_api_version("5.0.2", "ext_editor") -- darktable 3.0 + + +-- OS compatibility +local PS = dt.configuration.running_os == "windows" and "\\" or "/" + + +-- executables of the external editors +local program_paths = { + [1] = dt.preferences.read("ext_editor","program_path_1", "string"), + [2] = dt.preferences.read("ext_editor","program_path_2", "string"), + [3] = dt.preferences.read("ext_editor","program_path_3", "string"), + [4] = dt.preferences.read("ext_editor","program_path_4", "string"), + [5] = dt.preferences.read("ext_editor","program_path_5", "string"), + [6] = dt.preferences.read("ext_editor","program_path_6", "string"), + [7] = dt.preferences.read("ext_editor","program_path_7", "string"), + [8] = dt.preferences.read("ext_editor","program_path_8", "string"), + [9] = dt.preferences.read("ext_editor","program_path_9", "string") + } + + +-- friendly names of the external editors +local program_names = { + [1] = dt.preferences.read("ext_editor","program_name_1", "string"), + [2] = dt.preferences.read("ext_editor","program_name_2", "string"), + [3] = dt.preferences.read("ext_editor","program_name_3", "string"), + [4] = dt.preferences.read("ext_editor","program_name_4", "string"), + [5] = dt.preferences.read("ext_editor","program_name_5", "string"), + [6] = dt.preferences.read("ext_editor","program_name_6", "string"), + [7] = dt.preferences.read("ext_editor","program_name_7", "string"), + [8] = dt.preferences.read("ext_editor","program_name_8", "string"), + [9] = dt.preferences.read("ext_editor","program_name_9", "string") + } + + +-- last used editor +if not (dt.preferences.read("ext_editor","initialized", "bool")) then + dt.preferences.write("ext_editor","lastchoice", "integer", 1) + dt.preferences.write("ext_editor","initialized", "bool", true) + end +local lastchoice = dt.preferences.read("ext_editor","lastchoice", "integer") + + +-- shows export progress +local function show_status(storage, image, format, filename, + number, total, high_quality, extra_data) + dt.print(string.format("Exporting Image %i/%i ...", number, total)) + end + + +-- callback for button "edit" +local function OpenWith(images, choice) + + if #images == 1 then + for _, image in pairs(images) do + + -- image to be edited + name = image.path..PS..image.filename + + -- launch the external editor + local run_cmd = df.sanitize_filename(program_paths[choice]).." "..df.sanitize_filename(name) + dt.print("Launching "..program_names[choice].."...") + dtsys.external_command(run_cmd) + + -- refresh the image view + -- note that only image:drop_cache() is not enough to refresh view in darkroom mode + -- therefore image must be deleted and reimported to force refresh + + local image_leader = image.group_leader + image:delete () + local new_image = dt.database.import(name) + new_image:group_with(image_leader) + + -- copy tags from image leader + for _,tag in pairs(dt.tags.get_tags(image_leader)) do + if not (string.sub(tag.name,1,9) == "darktable") then + dt.tags.attach(tag, new_image) + end + end + + -- remember the last editor used + dt.preferences.write("ext_editor","lastchoice", "integer", choice) + + -- refresh darkroom view + if dt.gui.current_view () == dt.gui.views.darkroom then + dt.gui.views.darkroom.display_image(new_image) + end + end + else + dt.print('please select one image') + end + + end + + +-- callback for button "copy and edit" +local function CopyAndOpenWith(images, choice) + + if #images == 1 then + for _,image in pairs(images) do + + -- image to be copied and edited + local name = image.path..PS..image.filename + local new_name = name + + -- create unique filename + while df.check_if_file_exists(new_name) do + new_name = df.filename_increment(new_name) + end + + -- limit to 99 more exports of the original export + if string.match(df.get_basename(new_name), "_(d-)$") == "99" then + break + end + + -- physical copy + local result = df.file_copy(name, new_name) + + if result then + -- launch the external editor + local run_cmd = df.sanitize_filename(program_paths[choice]).." "..df.sanitize_filename(new_name) + dt.print("Launching "..program_names[choice].."...") + dtsys.external_command(run_cmd) + + -- import in database and group + local new_image = dt.database.import(new_name) + new_image:group_with(image.group_leader) + + -- copy image tags + for _,tag in pairs(dt.tags.get_tags(image)) do + if not (string.sub(tag.name,1,9) == "darktable") then + dt.tags.attach(tag, new_image) + end + end + + -- remember the last editor used + dt.preferences.write("ext_editor","lastchoice", "integer", choice) + end + end + else + dt.print('please select one image') + end + + + end + + +-- callback function for shortcuts +local function program_shortcut(event, shortcut) + local choice = tonumber(string.sub(shortcut, -1)) + OpenWith(dt.gui.action_images, choice) + end + + +-- export images and reimport in collection +local function export2collection(storage, image_table, extra_data) + + for image, temp_name in pairs(image_table) do + + -- images are first exported in temp folder + -- create unique filename + local new_name = image.path ..PS..df.get_filename(temp_name) + while df.check_if_file_exists(new_name) do + new_name = df.filename_increment(new_name) + + -- limit to 99 more exports of the original export + if string.match(df.get_basename(new_name), "_(d-)$") == "99" then + break + end + end + + -- image moved to collection folder + local result = df.file_move(temp_name, new_name) + + if result then + -- import in database and group + local new_image = dt.database.import(new_name) + new_image:group_with(image.group_leader) + end + end + end + + +-- combobox +local combobox = dt.new_widget("combobox") + { + label = "choose program", + tooltip = "select the external editor from the list", + value = lastchoice, -- remember status, + program_names[1], + program_names[2], + program_names[3], + program_names[4], + program_names[5], + program_names[6], + program_names[7], + program_names[8], + program_names[9] + } + + +-- button edit +local button1 = dt.new_widget("button") + { + label = "edit", + tooltip = "open the selected image in external editor", + dt_lua_align_t = center, + clicked_callback = function (_) + local choice = combobox.selected + OpenWith(dt.gui.action_images, choice) + end + } + + +-- button edit a copy +local button2 = dt.new_widget("button") + { + label = "edit a copy", + tooltip = "create a copy of the selected image and open it in external editor", + clicked_callback = function (_) + local choice = combobox.selected + CopyAndOpenWith(dt.gui.action_images, choice) + end + } + + +-- box for the two buttons +local box1 = dt.new_widget ("box") { + orientation = "horizontal", + button1, + button2 +} + + +-- register new lighttable module +dt.register_lib( + "ext_editor", -- module name + "external editors", -- name + true, -- expandable + false, -- resetable + {[dt.gui.views.lighttable] = {"DT_UI_CONTAINER_PANEL_RIGHT_CENTER", 100}}, + dt.new_widget("box") -- widget + { + orientation = "vertical", + combobox, + box1 + }, + nil,-- view_enter + nil -- view_leave + ) + + +-- register new storage +dt.register_storage("exp2coll", "collection", show_status, export2collection) + + +-- register the new preferences +dt.preferences.register("ext_editor","program_path_9", "string","command for external editor 9", "command for external editor, including full path if needed","") +dt.preferences.register("ext_editor","program_name_9", "string","friendly name of external editor 9", "friendly name of external editor","") + +dt.preferences.register("ext_editor","program_path_8", "string","command for external editor 8", "command for external editor, including full path if needed","") +dt.preferences.register("ext_editor","program_name_8", "string","friendly name of external editor 8", "friendly name of external editor","") + +dt.preferences.register("ext_editor","program_path_7", "string","command for external editor 7", "command for external editor, including full path if needed","") +dt.preferences.register("ext_editor","program_name_7", "string","friendly name of external editor 7", "friendly name of external editor","") + +dt.preferences.register("ext_editor","program_path_6", "string","command for external editor 6", "command for external editor, including full path if needed","") +dt.preferences.register("ext_editor","program_name_6", "string","friendly name of external editor 6", "friendly name of external editor","") + +dt.preferences.register("ext_editor","program_path_5", "string","command for external editor 5", "command for external editor, including full path if needed","") +dt.preferences.register("ext_editor","program_name_5", "string","friendly name of external editor 5", "friendly name of external editor","") + +dt.preferences.register("ext_editor","program_path_4", "string","command for external editor 4", "command for external editor, including full path if needed","") +dt.preferences.register("ext_editor","program_name_4", "string","friendly name of external editor 4", "friendly name of external editor","") + +dt.preferences.register("ext_editor","program_path_3", "string","command for external editor 3", "command for external editor, including full path if needed","") +dt.preferences.register("ext_editor","program_name_3", "string","friendly name of external editor 3", "friendly name of external editor","") + +dt.preferences.register("ext_editor","program_path_2", "string","command for external editor 2", "command for external editor, including full path if needed","") +dt.preferences.register("ext_editor","program_name_2", "string","friendly name of external editor 2", "friendly name of external editor","") + +dt.preferences.register("ext_editor","program_path_1", "string","command for external editor 1", "command for external editor, including full path if needed","") +dt.preferences.register("ext_editor","program_name_1", "string","friendly name of external editor 1", "friendly name of external editor","") + + +-- register the new shortcuts +dt.register_event("shortcut", program_shortcut, "edit with program 1") +dt.register_event("shortcut", program_shortcut, "edit with program 2") +dt.register_event("shortcut", program_shortcut, "edit with program 3") +dt.register_event("shortcut", program_shortcut, "edit with program 4") +dt.register_event("shortcut", program_shortcut, "edit with program 5") +dt.register_event("shortcut", program_shortcut, "edit with program 6") +dt.register_event("shortcut", program_shortcut, "edit with program 7") +dt.register_event("shortcut", program_shortcut, "edit with program 8") +dt.register_event("shortcut", program_shortcut, "edit with program 9") + +-- end of script + +-- vim: shiftwidth=2 expandtab tabstop=2 cindent syntax=lua +-- kate: hl Lua;