Merge pull request #112 from wpferguson/os_path_fix
Windows and macOS external executable compatibility
This commit is contained in:
179
contrib/gimp.lua
179
contrib/gimp.lua
@@ -67,109 +67,38 @@ local dt = require "darktable"
|
||||
local df = require "lib/dtutils.file"
|
||||
require "official/yield"
|
||||
local gettext = dt.gettext
|
||||
local gimp_widget = nil
|
||||
|
||||
dt.configuration.check_version(...,{3,0,0},{4,0,0},{5,0,0})
|
||||
dt.configuration.check_version(...,{5,0,0})
|
||||
|
||||
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||
gettext.bindtextdomain("gimp",dt.configuration.config_dir.."/lua/locale/")
|
||||
|
||||
local function split_filepath(str)
|
||||
local result = {}
|
||||
-- Thank you Tobias Jakobs for the awesome regular expression, which I tweaked a little
|
||||
result["path"], result["filename"], result["basename"], result["filetype"] = string.match(str, "(.-)(([^\\/]-)%.?([^%.\\/]*))$")
|
||||
return result
|
||||
end
|
||||
|
||||
local function get_path(str)
|
||||
local parts = split_filepath(str)
|
||||
return parts["path"]
|
||||
end
|
||||
|
||||
local function get_filename(str)
|
||||
local parts = split_filepath(str)
|
||||
return parts["filename"]
|
||||
end
|
||||
|
||||
local function get_basename(str)
|
||||
local parts = split_filepath(str)
|
||||
return parts["basename"]
|
||||
end
|
||||
|
||||
local function get_filetype(str)
|
||||
local parts = split_filepath(str)
|
||||
return parts["filetype"]
|
||||
end
|
||||
|
||||
local function _(msgid)
|
||||
return gettext.dgettext("gimp", msgid)
|
||||
end
|
||||
|
||||
-- Thanks Tobias Jakobs for the idea and the correction
|
||||
function checkIfFileExists(filepath)
|
||||
local file = io.open(filepath,"r")
|
||||
local ret
|
||||
if file ~= nil then
|
||||
io.close(file)
|
||||
dt.print_error("true checkIfFileExists: "..filepath)
|
||||
ret = true
|
||||
else
|
||||
dt.print_error(filepath.." not found")
|
||||
ret = false
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
local function filename_increment(filepath)
|
||||
|
||||
-- break up the filepath into parts
|
||||
local path = get_path(filepath)
|
||||
local basename = get_basename(filepath)
|
||||
local filetype = get_filetype(filepath)
|
||||
|
||||
-- check to see if we've incremented before
|
||||
local increment = string.match(basename, "_(%d-)$")
|
||||
|
||||
if increment then
|
||||
-- we do 2 digit increments so make sure we didn't grab part of the filename
|
||||
if string.len(increment) > 2 then
|
||||
-- we got the filename so set the increment to 01
|
||||
increment = "01"
|
||||
else
|
||||
increment = string.format("%02d", tonumber(increment) + 1)
|
||||
basename = string.gsub(basename, "_(%d-)$", "")
|
||||
end
|
||||
else
|
||||
increment = "01"
|
||||
end
|
||||
local incremented_filepath = path .. basename .. "_" .. increment .. "." .. filetype
|
||||
|
||||
dt.print_error("original file was " .. filepath)
|
||||
dt.print_error("incremented file is " .. incremented_filepath)
|
||||
|
||||
return incremented_filepath
|
||||
end
|
||||
|
||||
local function groupIfNotMember(img, new_img)
|
||||
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
|
||||
dt.print_error(image.filename .. " is a member")
|
||||
dt.print_log(image.filename .. " is a member")
|
||||
if image.filename == new_img.filename then
|
||||
is_member = true
|
||||
dt.print_error("Already in group")
|
||||
dt.print_log("Already in group")
|
||||
end
|
||||
end
|
||||
if not is_member then
|
||||
dt.print_error("group leader is "..img.group_leader.filename)
|
||||
dt.print_log("group leader is "..img.group_leader.filename)
|
||||
new_img:group_with(img.group_leader)
|
||||
dt.print_error("Added to group")
|
||||
dt.print_log("Added to group")
|
||||
end
|
||||
end
|
||||
|
||||
local function sanitize_filename(filepath)
|
||||
local path = get_path(filepath)
|
||||
local basename = get_basename(filepath)
|
||||
local filetype = get_filetype(filepath)
|
||||
local path = df.get_path(filepath)
|
||||
local basename = df.get_basename(filepath)
|
||||
local filetype = df.get_filetype(filepath)
|
||||
|
||||
local sanitized = string.gsub(basename, " ", "\\ ")
|
||||
|
||||
@@ -181,60 +110,11 @@ local function show_status(storage, image, format, filename,
|
||||
dt.print(string.format(_("Export Image %i/%i"), number, total))
|
||||
end
|
||||
|
||||
local function fileCopy(fromFile, toFile)
|
||||
local result = nil
|
||||
-- if cp exists, use it
|
||||
if df.check_if_bin_exists("cp") then
|
||||
result = os.execute("cp '" .. fromFile .. "' '" .. toFile .. "'")
|
||||
end
|
||||
-- if cp was not present, or if cp failed, then a pure lua solution
|
||||
if not result then
|
||||
local fileIn, err = io.open(fromFile, 'rb')
|
||||
if fileIn then
|
||||
local fileOut, errr = io.open(toFile, 'w')
|
||||
if fileOut then
|
||||
local content = fileIn:read(4096)
|
||||
while content do
|
||||
fileOut:write(content)
|
||||
content = fileIn:read(4096)
|
||||
end
|
||||
result = true
|
||||
fileIn:close()
|
||||
fileOut:close()
|
||||
else
|
||||
dt.print_error("fileCopy Error: " .. errr)
|
||||
end
|
||||
else
|
||||
dt.print_error("fileCopy Error: " .. err)
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
local function fileMove(fromFile, toFile)
|
||||
local success = os.rename(fromFile, toFile)
|
||||
if not success then
|
||||
-- an error occurred, so let's try using the operating system function
|
||||
if df.check_if_bin_exists("mv") then
|
||||
success = os.execute("mv '" .. fromFile .. "' '" .. toFile .. "'")
|
||||
end
|
||||
-- if the mv didn't exist or succeed, then...
|
||||
if not success then
|
||||
-- pure lua solution
|
||||
success = fileCopy(fromFile, toFile)
|
||||
if success then
|
||||
os.remove(fromFile)
|
||||
else
|
||||
dt.print_error("fileMove Error: Unable to move " .. fromFile .. " to " .. toFile .. ". Leaving " .. fromFile .. " in place.")
|
||||
dt.print(string.format(_("Unable to move edited file into collection. Leaving it as %s"), fromFile))
|
||||
end
|
||||
end
|
||||
end
|
||||
return success -- nil on error, some value if success
|
||||
end
|
||||
|
||||
local function gimp_edit(storage, image_table, extra_data) --finalize
|
||||
if not df.check_if_bin_exists("gimp") then
|
||||
|
||||
local gimp_executable = df.check_if_bin_exists("gimp")
|
||||
|
||||
if not gimp_executable then
|
||||
dt.print_error(_("GIMP not found"))
|
||||
return
|
||||
end
|
||||
@@ -253,9 +133,9 @@ local function gimp_edit(storage, image_table, extra_data) --finalize
|
||||
dt.print(_("Launching GIMP..."))
|
||||
|
||||
local gimpStartCommand
|
||||
gimpStartCommand = "gimp "..img_list
|
||||
gimpStartCommand = gimp_executable .. " " .. img_list
|
||||
|
||||
dt.print_error(gimpStartCommand)
|
||||
dt.print_log(gimpStartCommand)
|
||||
|
||||
dt.control.execute( gimpStartCommand)
|
||||
|
||||
@@ -266,28 +146,28 @@ local function gimp_edit(storage, image_table, extra_data) --finalize
|
||||
|
||||
for image,exported_image in pairs(image_table) do
|
||||
|
||||
local myimage_name = image.path .. "/" .. get_filename(exported_image)
|
||||
local myimage_name = image.path .. "/" .. df.get_filename(exported_image)
|
||||
|
||||
while checkIfFileExists(myimage_name) do
|
||||
myimage_name = filename_increment(myimage_name)
|
||||
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(get_basename(myimage_name), "_(d-)$") == "99" then
|
||||
if string.match(df.get_basename(myimage_name), "_(d-)$") == "99" then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
dt.print_error("moving " .. exported_image .. " to " .. myimage_name)
|
||||
local result = fileMove(exported_image, myimage_name)
|
||||
dt.print_log("moving " .. exported_image .. " to " .. myimage_name)
|
||||
local result = df.file_move(exported_image, myimage_name)
|
||||
|
||||
if result then
|
||||
dt.print_error("importing file")
|
||||
dt.print_log("importing file")
|
||||
local myimage = dt.database.import(myimage_name)
|
||||
|
||||
groupIfNotMember(image, myimage)
|
||||
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.print_error("attaching tag")
|
||||
dt.print_log("attaching tag")
|
||||
dt.tags.attach(tag,myimage)
|
||||
end
|
||||
end
|
||||
@@ -297,6 +177,13 @@ local function gimp_edit(storage, image_table, extra_data) --finalize
|
||||
end
|
||||
|
||||
-- Register
|
||||
dt.register_storage("module_gimp", _("Edit with GIMP"), show_status, gimp_edit)
|
||||
|
||||
local executables = {"gimp"}
|
||||
|
||||
if dt.configuration.running_os ~= "linux" then
|
||||
gimp_widget = df.executable_path_widget(executables)
|
||||
end
|
||||
|
||||
dt.register_storage("module_gimp", _("Edit with GIMP"), show_status, gimp_edit, nil, nil, gimp_widget)
|
||||
|
||||
--
|
||||
|
||||
@@ -23,7 +23,7 @@ dtutils_file.libdoc = {
|
||||
|
||||
local gettext = dt.gettext
|
||||
|
||||
dt.configuration.check_version(...,{3,0,0},{4,0,0},{5,0,0})
|
||||
dt.configuration.check_version(...,{5,0,0})
|
||||
|
||||
-- Tell gettext where to find the .mo file translating messages for a particular domain
|
||||
gettext.bindtextdomain("dtutils.file",dt.configuration.config_dir.."/lua/locale/")
|
||||
@@ -34,14 +34,18 @@ end
|
||||
|
||||
dtutils_file.libdoc.functions["check_if_bin_exists"] = {
|
||||
Name = [[check_if_bin_exists]],
|
||||
Synopsis = [[check if an executable is in the path]],
|
||||
Synopsis = [[check if an executable exists]],
|
||||
Usage = [[local df = require "lib/dtutils.file"
|
||||
|
||||
local result = df.check_if_bin_exists(bin)
|
||||
bin - string - the binary to check for]],
|
||||
Description = [[check_if_bin_exists checks to see if the specified binary executable is
|
||||
in the path.]],
|
||||
Return_Value = [[result - boolean - true if the executable was found, false if not]],
|
||||
Description = [[check_if_bin_exists checks to see if the specified binary exists.
|
||||
check_if_bin_exists first checks to see if a preference for the binary has been
|
||||
registered and uses that if found. The presence of the file is verified, then
|
||||
quoted and returned. If no preference is specified and the operating system is
|
||||
linux then the which command is used to check for a binary in the path. If found
|
||||
that path is returned. If no binary is found, false is returned.]],
|
||||
Return_Value = [[result - string - the path of the binary, false if not found]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
@@ -51,17 +55,27 @@ dtutils_file.libdoc.functions["check_if_bin_exists"] = {
|
||||
}
|
||||
|
||||
function dtutils_file.check_if_bin_exists(bin)
|
||||
local result
|
||||
if (dt.configuration.running_os == 'linux') then
|
||||
result = os.execute("which "..bin)
|
||||
local result = false
|
||||
local path = nil
|
||||
|
||||
if string.match(bin, "/") or string.match(bin, "\\") then
|
||||
path = bin
|
||||
else
|
||||
result = dtutils_file.check_if_file_exists(bin)
|
||||
path = dtutils_file.get_executable_path_preference(bin)
|
||||
end
|
||||
|
||||
if not result then
|
||||
result = false
|
||||
if string.len(path) > 0 then
|
||||
if dtutils_file.check_if_file_exists(path) then
|
||||
result = "\"" .. path .. "\""
|
||||
end
|
||||
elseif dt.configuration.running_os == "linux" then
|
||||
local p = io.popen("which " .. bin)
|
||||
local output = p:read("*a")
|
||||
p:close()
|
||||
if string.len(output) > 0 then
|
||||
result = output:sub(1,-2)
|
||||
end
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
@@ -416,4 +430,99 @@ function dtutils_file.create_unique_filename(filepath)
|
||||
return filepath
|
||||
end
|
||||
|
||||
|
||||
dtutils_file.libdoc.functions["set_executable_path_preference"] = {
|
||||
Name = [[set_executable_path_preference]],
|
||||
Synopsis = [[set a preference for the path to an executable]],
|
||||
Usage = [[local df = require "lib/dtutils.file"
|
||||
|
||||
df.set_executable_path_preference(executable, path)
|
||||
executable - string - the name of the executable to set the path for
|
||||
path - string - the path to the binary]],
|
||||
Description = [[set_executable_path_preference takes an executable name and path to the
|
||||
executable and registers the preference for later use.]],
|
||||
Return_Value = [[]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils_file.set_executable_path_preference(executable, path)
|
||||
dt.preferences.write("executable_paths", executable, "string", path)
|
||||
end
|
||||
|
||||
|
||||
dtutils_file.libdoc.functions["get_executable_path_preference"] = {
|
||||
Name = [[get_executable_path_preference]],
|
||||
Synopsis = [[return the path to an executable from a preference]],
|
||||
Usage = [[local df = require "lib/dtutils.file"
|
||||
|
||||
local result = df.get_executable_path_preference(executable)
|
||||
executable - string - the name of the executable to get the path for]],
|
||||
Description = [[get_executable_path_preference returns the path preference to
|
||||
the requested executable.]],
|
||||
Return_Value = [[result - string - path to the executable]],
|
||||
Limitations = [[executable should be the basename of the executable without extensions]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils_file.get_executable_path_preference(executable)
|
||||
return dt.preferences.read("executable_paths", executable, "string")
|
||||
end
|
||||
|
||||
|
||||
dtutils_file.libdoc.functions["executable_path_widget"] = {
|
||||
Name = [[executable_path_widget]],
|
||||
Synopsis = [[create a widget to get executable path preferences]],
|
||||
Usage = [[local df = require "lib/dtutils.file"
|
||||
|
||||
local widget = df.executable_path_widget(executables)
|
||||
executables - table - a table of strings that are executable names]],
|
||||
Description = [[executable_path_widget takes a table of executable names
|
||||
and builds a set of file selector widgets to get the path to the executable.
|
||||
The resulting widgets are wrapped in a box widget and returned.]],
|
||||
Return_Value = [[widget - widget - a widget containing a file selector widget for
|
||||
each executable.]],
|
||||
Limitations = [[]],
|
||||
Example = [[]],
|
||||
See_Also = [[]],
|
||||
Reference = [[]],
|
||||
License = [[]],
|
||||
Copyright = [[]],
|
||||
}
|
||||
|
||||
function dtutils_file.executable_path_widget(executables)
|
||||
local box_widgets = {}
|
||||
table.insert(box_widgets, dt.new_widget("section_label"){label = "select executable(s)"})
|
||||
for _, executable in pairs(executables) do
|
||||
table.insert(box_widgets, dt.new_widget("label"){label = "select " .. executable .. " executable"})
|
||||
local path = dtutils_file.get_executable_path_preference(executable)
|
||||
if not path then
|
||||
path = ""
|
||||
end
|
||||
table.insert(box_widgets, dt.new_widget("file_chooser_button"){
|
||||
title = "select " .. executable .. " executable",
|
||||
value = path,
|
||||
is_directory = false,
|
||||
changed_callback = function(self)
|
||||
if dtutils_file.check_if_bin_exists(self.value) then
|
||||
dtutils_file.set_executable_path_preference(executable, self.value)
|
||||
end
|
||||
end}
|
||||
)
|
||||
end
|
||||
local box = dt.new_widget("box"){
|
||||
orientation = "vertical",
|
||||
table.unpack(box_widgets)
|
||||
}
|
||||
return box
|
||||
end
|
||||
|
||||
return dtutils_file
|
||||
Reference in New Issue
Block a user