diff --git a/README.md b/README.md
index 2eb1620..7577026 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 ...
@@ -59,7 +60,6 @@ rename-tags|Yes|LMW|Change a tag name
select_untagged|Yes|LMW|Enable selection of untagged images
slideshowMusic|No|L|Play music during a slideshow
video_ffmpeg|No|LMW|Export video from darktable
-video_mencoder|No|L|Export video from darktable
### Example Scripts
@@ -68,10 +68,13 @@ These scripts provide examples of how to use specific portions of the API. They
Name|Standalone|OS |Purpose
----|:--------:|:---:|-------
api_version|Yes|LMW|Print the current API version
+darkroom_demo|Yes|LMW|Demonstrate changing images in darkoom
gettextExample|Yes|LM|How to use translation
hello_world|Yes|LMW|Prints hello world when darktable starts
+lighttable_demo|Yes|LMW|Demonstrate controlling lighttable mode, zoom, sorting and filtering
moduleExample|Yes|LMW|How to create a lighttable module
multi_os|No|LMW|How to create a cross platform script that calls an external executable
+panels_demo|Yes|LMW|Demonstrate hiding and showing darktable panels
preferenceExamples|Yes|LMW|How to use preferences in a script
printExamples|Yes|LMW|How to use various print functions from a script
running_os|Yes|LMW|Print out the running operating system
diff --git a/contrib/video_mencoder.lua b/contrib/video_mencoder.lua
deleted file mode 100644
index 71c8a4d..0000000
--- a/contrib/video_mencoder.lua
+++ /dev/null
@@ -1,124 +0,0 @@
---[[
- This file is part of darktable,
- Copyright 2014 by Tobias Jakobs.
-
- 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 .
-]]
---[[
-darktable video export script
-
-ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
-* mencoder (MEncoder is from the MPlayer Team)
-* xdg-open
-* xdg-user-dir
-
-WARNING
-This script is only tested with Linux
-
-USAGE
-* require this script from your main lua file
-]]
-
-local dt = require "darktable"
-local du = require "lib/dtutils"
-local df = require "lib/dtutils.file"
-require "official/yield"
-local gettext = dt.gettext
-
-du.check_min_api_version("2.0.1", "video_mencoder")
-
--- Tell gettext where to find the .mo file translating messages for a particular domain
-gettext.bindtextdomain("video_mencoder",dt.configuration.config_dir.."/lua/locale/")
-
-local function _(msgid)
- return gettext.dgettext("video_mencoder", msgid)
-end
-
-local function show_status(storage, image, format, filename, number, total, high_quality, extra_data)
- dt.print("Export Image "..tostring(number).."/"..tostring(total))
-end
-
-local function create_video_mencoder(storage, image_table, extra_data)
- dt.print("video_mencoder is deprecated. Please use video_ffmpeg instead. Please see the log for more details.")
- dt.print_log("The mencoder executable is no longer distributed as part of most distributions,")
- dt.print_log("therefore video_mencoder is deprecated. video_ffmpeg should be used in it's place. video_mencoder")
- dt.print_log("will be removed when darktable 2.8 is released.")
- if not df.check_if_bin_exists("mencoder") then
- dt.print_error(_("mencoder not found"))
- return
- end
- if not df.check_if_bin_exists("xdg-open") then
- dt.print_error(_("xdg-open not found"))
- return
- end
- if not df.check_if_bin_exists("xdg-user-dir") then
- dt.print_error(_("xdg-user-dir not found"))
- return
- end
-
- exportDirectory = dt.preferences.read("video_mencoder","ExportDirectory","string")
- exportFilename = "output.avi"
- framsePerSecond = dt.preferences.read("video_mencoder","FramesPerSecond","integer")
-
- dt.print_error("Will try to create video now")
- -- Set the codec
- local codec = ""
- local codecPreferences = ""
-
- codecPreferences = dt.preferences.read("video_mencoder","Codec","string")
- if (codecPreferences == "H.264 encoding") then
- codec = 'x264'
- end
- if (codecPreferences == "XviD encoding") then
- codec = 'xvid'
- end
- if not codecPreferences then
- codec = 'x264'
- end
-
- -- Create the command
- local command = "mencoder -idx -nosound -noskip -ovc "..codec.." -lavcopts vcodec=mjpeg -o "..exportDirectory.."/"..exportFilename.." -mf fps="..framsePerSecond .." mf://"
-
- for _,v in pairs(image_table) do
- command = command..v..","
- end
-
- dt.print_error("this is the command: "..command)
- dt.control.execute( command)
-
- dt.print("Video created in "..exportDirectory)
-
- if ( dt.preferences.read("video_mencoder","OpenVideo","bool") == true ) then
- local playVideoCommand = "xdg-open "..exportDirectory.."/"..exportFilename
- dt.control.execute( playVideoCommand)
- end
-end
-
--- Preferences
-dt.preferences.register("video_mencoder", "FramesPerSecond", "float", "Video exort (MEncoder): Frames per second", "Frames per Second in the Video export", 15, 1, 99, 0.1 )
-dt.preferences.register("video_mencoder", "OpenVideo", "bool", "Video exort (MEncoder): Open video after export", "Opens the Video after the export with the standard video player", false )
-
-local handle = io.popen("xdg-user-dir VIDEOS")
-local result = handle:read()
-handle:close()
-if (result == nil) then
- result = ""
-end
-dt.preferences.register("video_mencoder", "ExportDirectory", "directory", "Video exort (MEncoder): Video export directory","A directory that will be used to export a Video",result)
-
--- Get the MEncoder codec list with: mencoder -ovc help
-dt.preferences.register("video_mencoder", "Codec", "enum", "Video exort (MEncoder): Codec","Video codec","H.264 encoding","H.264 encoding","XviD encoding")
-
--- Register
-dt.register_storage("video_mencoder", "Video Export (MEncoder)", show_status, create_video)
diff --git a/examples/darkroom_demo.lua b/examples/darkroom_demo.lua
new file mode 100644
index 0000000..e0e4052
--- /dev/null
+++ b/examples/darkroom_demo.lua
@@ -0,0 +1,98 @@
+--[[
+ This file is part of darktable,
+ copyright (c) 2019 Bill Ferguson
+
+ darktable is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ darktable 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 darktable. If not, see .
+]]
+--[[
+ darkroom_demo - an example script demonstrating how to control image display in darkroom mode
+
+ darkroom_demo is an example script showing how to control the currently displayed image in
+ darkroom mode using lua.
+
+ ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
+ * none
+
+ USAGE
+ * require this script from your main lua file
+
+ BUGS, COMMENTS, SUGGESTIONS
+ * Send to Bill Ferguson, wpferguson@gmail.com
+
+ CHANGES
+]]
+
+local dt = require "darktable"
+local du = require "lib/dtutils"
+
+-- - - - - - - - - - - - - - - - - - - - - - - -
+-- V E R S I O N C H E C K
+-- - - - - - - - - - - - - - - - - - - - - - - -
+
+du.check_min_api_version("5.0.2", "darkroom_mode") -- darktable 3.0
+
+-- - - - - - - - - - - - - - - - - - - - - - - -
+-- C O N S T A N T S
+-- - - - - - - - - - - - - - - - - - - - - - - -
+
+local MODULE_NAME = "darkroom"
+local PS = dt.configuration.running_os == "windows" and "\\" or "/"
+
+-- - - - - - - - - - - - - - - - - - - - - - - -
+-- T R A N S L A T I O N S
+-- - - - - - - - - - - - - - - - - - - - - - - -
+local gettext = dt.gettext
+gettext.bindtextdomain(MODULE_NAME, dt.configuration.config_dir..PS.."lua"..PS.."locale"..PS)
+
+local function _(msgid)
+ return gettext.dgettext(MODULE_NAME, msgid)
+end
+
+-- - - - - - - - - - - - - - - - - - - - - - - -
+-- M A I N
+-- - - - - - - - - - - - - - - - - - - - - - - -
+
+-- alias dt.control.sleep to sleep
+local sleep = dt.control.sleep
+
+-- save the configuration
+
+local current_view = dt.gui.current_view()
+
+-- enter darkroom view
+
+dt.gui.current_view(dt.gui.views.darkroom)
+
+local max_images = 10
+
+dt.print(_("Showing images, with a pause in between each"))
+sleep(1500)
+
+-- display first 10 images of collection pausing for a second between each
+
+for i, img in ipairs(dt.collection) do
+ dt.print(_("Displaying image " .. i))
+ dt.gui.views.darkroom.display_image(img)
+ sleep(1500)
+ if i == max_images then
+ break
+ end
+end
+
+-- return to lighttable view
+
+dt.print(_("Restoring view"))
+sleep(1500)
+dt.gui.current_view(current_view)
+
diff --git a/examples/lighttable_demo.lua b/examples/lighttable_demo.lua
new file mode 100644
index 0000000..c3629ba
--- /dev/null
+++ b/examples/lighttable_demo.lua
@@ -0,0 +1,198 @@
+--[[
+ This file is part of darktable,
+ copyright (c) 2019 Bill Ferguson
+
+ darktable is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ darktable 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 darktable. If not, see .
+]]
+--[[
+ lighttable_demo - an example script demonstrating how to control lighttable display modes
+
+ lighttable_demo is an example script showing how to control lighttable layout, sorting, and
+ filtering from a lua script. If the selected directory has different ratings, color labels, etc,
+ then the sorting and filtering display is a little clearer.
+
+ ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
+ * none
+
+ USAGE
+ * require this script from your main lua file
+
+ BUGS, COMMENTS, SUGGESTIONS
+ * Send to Bill Ferguson, wpferguson@gmail.com
+
+ CHANGES
+]]
+
+local dt = require "darktable"
+local du = require "lib/dtutils"
+
+-- - - - - - - - - - - - - - - - - - - - - - - -
+-- V E R S I O N C H E C K
+-- - - - - - - - - - - - - - - - - - - - - - - -
+
+du.check_min_api_version("5.0.2", "lighttable_demo") -- darktable 3.0
+
+-- - - - - - - - - - - - - - - - - - - - - - - -
+-- C O N S T A N T S
+-- - - - - - - - - - - - - - - - - - - - - - - -
+
+local MODULE_NAME = "lighttable"
+local PS = dt.configuration.running_os == "windows" and "\\" or "/"
+
+-- - - - - - - - - - - - - - - - - - - - - - - -
+-- T R A N S L A T I O N S
+-- - - - - - - - - - - - - - - - - - - - - - - -
+local gettext = dt.gettext
+gettext.bindtextdomain(MODULE_NAME, dt.configuration.config_dir..PS.."lua"..PS.."locale"..PS)
+
+local function _(msgid)
+ return gettext.dgettext(MODULE_NAME, msgid)
+end
+
+-- - - - - - - - - - - - - - - - - - - - - - - -
+-- F U N C T I O N S
+-- - - - - - - - - - - - - - - - - - - - - - - -
+
+-- - - - - - - - - - - - - - - - - - - - - - - -
+-- M A I N
+-- - - - - - - - - - - - - - - - - - - - - - - -
+
+-- alias dt.control.sleep to sleep
+local sleep = dt.control.sleep
+
+local layouts = {
+ "DT_LIGHTTABLE_LAYOUT_ZOOMABLE",
+ "DT_LIGHTTABLE_LAYOUT_FILEMANAGER",
+ "DT_LIGHTTABLE_LAYOUT_CULLING",
+}
+
+local sorts = {
+ "DT_COLLECTION_SORT_NONE",
+ "DT_COLLECTION_SORT_FILENAME",
+ "DT_COLLECTION_SORT_DATETIME",
+ "DT_COLLECTION_SORT_RATING",
+ "DT_COLLECTION_SORT_ID",
+ "DT_COLLECTION_SORT_COLOR",
+ "DT_COLLECTION_SORT_GROUP",
+ "DT_COLLECTION_SORT_PATH",
+ "DT_COLLECTION_SORT_CUSTOM_ORDER",
+ "DT_COLLECTION_SORT_TITLE",
+ "DT_COLLECTION_SORT_DESCRIPTION",
+ "DT_COLLECTION_SORT_ASPECT_RATIO",
+ "DT_COLLECTION_SORT_SHUFFLE"
+}
+
+local sort_orders = {
+ "DT_COLLECTION_SORT_ORDER_ASCENDING",
+ "DT_COLLECTION_SORT_ORDER_DESCENDING"
+}
+
+local ratings = {
+ "DT_COLLECTION_FILTER_ALL",
+ "DT_COLLECTION_FILTER_STAR_NO",
+ "DT_COLLECTION_FILTER_STAR_1",
+ "DT_COLLECTION_FILTER_STAR_2",
+ "DT_COLLECTION_FILTER_STAR_3",
+ "DT_COLLECTION_FILTER_STAR_4",
+ "DT_COLLECTION_FILTER_STAR_5",
+ "DT_COLLECTION_FILTER_REJECT",
+ "DT_COLLECTION_FILTER_NOT_REJECT"
+}
+
+local rating_comparators = {
+ "DT_COLLECTION_RATING_COMP_LT",
+ "DT_COLLECTION_RATING_COMP_LEQ",
+ "DT_COLLECTION_RATING_COMP_EQ",
+ "DT_COLLECTION_RATING_COMP_GEQ",
+ "DT_COLLECTION_RATING_COMP_GT",
+ "DT_COLLECTION_RATING_COMP_NE"
+}
+
+local zoom_levels = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
+
+
+-- save filter, view, and collection parameters
+
+local current_layout = dt.gui.libs.lighttable_mode.layout()
+local current_zoom_level = dt.gui.libs.lighttable_mode.zoom_level()
+local current_rating = dt.gui.libs.filter.rating()
+local current_rating_comparator = dt.gui.libs.filter.rating_comparator()
+local current_sort = dt.gui.libs.filter.sort()
+local current_sort_order = dt.gui.libs.filter.sort_order()
+
+-- cycle through layouts and zooms
+
+dt.print(_("lighttable layout and zoom level demonstration"))
+sleep(2000)
+
+for n, layout in ipairs(layouts) do
+ dt.gui.libs.lighttable_mode.layout(layout)
+ dt.print(_("set lighttable layout to " .. layout))
+ sleep(1500)
+ for i = 1, 10 do
+ dt.gui.libs.lighttable_mode.zoom_level(i)
+ dt.print(_("Set zoom level to " .. i))
+ sleep(1500)
+ end
+ for i = 9, 1, -1 do
+ dt.gui.libs.lighttable_mode.zoom_level(i)
+ dt.print(_("Set zoom level to " .. i))
+ sleep(1500)
+ end
+end
+
+-- cycle through sorts
+
+dt.print(_("lighttable sorting demonstration"))
+dt.gui.libs.lighttable_mode.layout("DT_LIGHTTABLE_LAYOUT_FILEMANAGER")
+dt.gui.libs.lighttable_mode.zoom_level(5)
+
+for n, sort in ipairs(sorts) do
+ dt.gui.libs.filter.sort(sort)
+ dt.print(_("set lighttable sort to " .. sort))
+ sleep(1500)
+
+ for m, sort_order in ipairs(sort_orders) do
+ dt.gui.libs.filter.sort_order(sort_order)
+ dt.print(_("sort order set to " .. sort_order))
+ sleep(1500)
+ end
+end
+
+-- cycle through filters
+
+dt.print(_("lighttable filtering demonstration"))
+
+for n, rating in ipairs(ratings) do
+ dt.gui.libs.filter.rating(rating)
+ dt.print(_("set filter to " .. rating))
+ sleep(1500)
+
+ for m, rating_comparator in ipairs(rating_comparators) do
+ dt.gui.libs.filter.rating_comparator(rating_comparator)
+ dt.print(_("set rating comparator to " .. rating_comparator))
+ sleep(1500)
+ end
+end
+
+-- restore settings
+
+dt.print(_("restoring settings"))
+
+current_layout = dt.gui.libs.lighttable_mode.layout(current_layout)
+current_zoom_level = dt.gui.libs.lighttable_mode.zoom_level(current_zoom_level)
+current_rating = dt.gui.libs.filter.rating(current_rating)
+current_rating_comparator = dt.gui.libs.filter.rating_comparator(current_rating_comparator)
+current_sort =dt.gui.libs.filter.sort(current_sort)
+current_sort_order = dt.gui.libs.filter.sort_order(current_sort_order)
diff --git a/examples/panels_demo.lua b/examples/panels_demo.lua
new file mode 100644
index 0000000..edab6ae
--- /dev/null
+++ b/examples/panels_demo.lua
@@ -0,0 +1,136 @@
+--[[
+ This file is part of darktable,
+ copyright (c) 2019 Bill Ferguson
+
+ darktable is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ darktable 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 darktable. If not, see .
+]]
+--[[
+ panels_demo - an example script demonstrating how to contol panel visibility
+
+ panels_demo is an example script showing how to control panel visibility. It cycles
+ through the panels hiding them one by one, then showing them one by one, then
+ hiding all, then showing all. Finally, the original panel visibility is restored.
+
+ ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
+ * none
+
+ USAGE
+ * require this script from your main lua file
+
+ BUGS, COMMENTS, SUGGESTIONS
+ * Send to Bill Ferguson, wpferguson@gmail.com
+
+ CHANGES
+]]
+
+local dt = require "darktable"
+local du = require "lib/dtutils"
+
+-- - - - - - - - - - - - - - - - - - - - - - - -
+-- V E R S I O N C H E C K
+-- - - - - - - - - - - - - - - - - - - - - - - -
+
+du.check_min_api_version("5.0.2", "panels_demo") -- darktable 3.0
+
+-- - - - - - - - - - - - - - - - - - - - - - - -
+-- C O N S T A N T S
+-- - - - - - - - - - - - - - - - - - - - - - - -
+
+local MODULE_NAME = "panels"
+local PS = dt.configuration.running_os == "windows" and "\\" or "/"
+
+-- - - - - - - - - - - - - - - - - - - - - - - -
+-- T R A N S L A T I O N S
+-- - - - - - - - - - - - - - - - - - - - - - - -
+local gettext = dt.gettext
+gettext.bindtextdomain(MODULE_NAME, dt.configuration.config_dir..PS.."lua"..PS.."locale"..PS)
+
+local function _(msgid)
+ return gettext.dgettext(MODULE_NAME, msgid)
+end
+
+-- - - - - - - - - - - - - - - - - - - - - - - -
+-- M A I N
+-- - - - - - - - - - - - - - - - - - - - - - - -
+
+-- alias dt.control.sleep to sleep
+local sleep = dt.control.sleep
+
+local panels = {"DT_UI_PANEL_CENTER_TOP", -- center top panel
+ "DT_UI_PANEL_CENTER_BOTTOM", -- center bottom panel
+ "DT_UI_PANEL_TOP", -- complete top panel
+ "DT_UI_PANEL_LEFT", -- left panel
+ "DT_UI_PANEL_RIGHT", -- right panel
+ "DT_UI_PANEL_BOTTOM"} -- complete bottom panel
+
+local panel_status = {}
+
+-- save panel visibility
+
+for i = 1,#panels do
+ panel_status[i] = dt.gui.panel_visible(panels[i])
+end
+
+-- show all just in case
+
+dt.gui.panel_show_all()
+
+-- hide center_top, center_bottom, left, top, right, bottom in order
+
+dt.print(_("Hiding all panels, one at a tme"))
+sleep(1500)
+
+for i = 1, #panels do
+ dt.print(_("Hiding " .. panels[i]))
+ dt.gui.panel_hide(panels[i])
+ sleep(1500)
+end
+
+-- display left, then top, then right, then bottom
+
+ dt.print(_("Make panels visible, one at a time"))
+ sleep(1500)
+
+ for i = #panels, 1, -1 do
+ dt.print(_("Showing " .. panels[i]))
+ dt.gui.panel_show(panels[i])
+ sleep(1500)
+ end
+
+-- hide all
+
+dt.print(_("Hiding all panels"))
+sleep(1500)
+
+dt.gui.panel_hide_all()
+sleep(1500)
+
+-- show all
+
+dt.print(_("Showing all panels"))
+sleep(1500)
+
+dt.gui.panel_show_all()
+sleep(1500)
+
+-- restore
+
+dt.print(_("Restoring panels to starting configuration"))
+for i = 1, #panels do
+ if panel_status[i] then
+ dt.gui.panel_show(panels[i])
+ else
+ dt.gui.panel_hide(panels[i])
+ end
+end
diff --git a/locale/de_DE/LC_MESSAGES/select_untagged.mo b/locale/de_DE/LC_MESSAGES/select_untagged.mo
deleted file mode 100644
index 340ba90..0000000
Binary files a/locale/de_DE/LC_MESSAGES/select_untagged.mo and /dev/null differ
diff --git a/locale/fr_FR/LC_MESSAGES/select_untagged.mo b/locale/fr_FR/LC_MESSAGES/select_untagged.mo
deleted file mode 100644
index 1bb384a..0000000
Binary files a/locale/fr_FR/LC_MESSAGES/select_untagged.mo and /dev/null differ