1
+ --[[
2
+
3
+ color_profile_manager.lua - manage external darktable color profiles
4
+
5
+ Copyright (C) 2021 Bill Ferguson <[email protected] >.
6
+
7
+ This program is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation; either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ This program is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
19
+ ]]
20
+ --[[
21
+ color_profile_manager - manage external darktable color profiles
22
+
23
+ This script provides a tool to manage input and output external color
24
+ profiles used by darktable. Color profiles can be added or removed
25
+ to/from the correct directories so that darktable can find and use
26
+ them.
27
+
28
+ ADDITIONAL SOFTWARE NEEDED FOR THIS SCRIPT
29
+ * None
30
+
31
+ USAGE
32
+ * require this script from your main lua file
33
+ * select the input or output set of color profiles
34
+ * a list of profiles is displayed. Click the check box beside the
35
+ profile name to select it for removal. Click the "remove profile"
36
+ button to remove the profile.
37
+ * use the file selector to select a color profile to add to the currently
38
+ selected set (input or output)
39
+ * click the "add profile" button to add the profile to the selected set
40
+
41
+ BUGS, COMMENTS, SUGGESTIONS
42
+ * Send to Bill Ferguson, [email protected] or raise an issue on
43
+ https://github.com/dakrtable-org/lua-scripts
44
+ ]]
45
+ local dt = require " darktable"
46
+ local du = require " lib/dtutils"
47
+ local df = require " lib/dtutils.file"
48
+
49
+ du .check_min_api_version (" 7.0.0" , " color_profile_manager" )
50
+
51
+ -- - - - - - - - - - - - - - - - - - - - - - - -
52
+ -- L O C A L I Z A T I O N
53
+ -- - - - - - - - - - - - - - - - - - - - - - - -
54
+
55
+ local gettext = dt .gettext
56
+
57
+ -- Tell gettext where to find the .mo file translating messages for a particular domain
58
+ gettext .bindtextdomain (" color_profile_manager" , dt .configuration .config_dir .. " /lua/locale/" )
59
+
60
+ local function _ (msgid )
61
+ return gettext .dgettext (" color_profile_manager" , msgid )
62
+ end
63
+
64
+ -- - - - - - - - - - - - - - - - - - - - - - - -
65
+ -- C O N S T A N T S
66
+ -- - - - - - - - - - - - - - - - - - - - - - - -
67
+
68
+ local MODULE_NAME = " color_profile_manager"
69
+ local PS = dt .configuration .running_os == " windows" and " \\ " or " /"
70
+ local CS = dt .configuration .running_os == " windows" and " &" or " ;"
71
+ local DIR_CMD = dt .configuration .running_os == " windows" and " dir /b" or " ls"
72
+ local COLOR_IN_DIR = dt .configuration .config_dir .. PS .. " color" .. PS .. " in"
73
+ local COLOR_OUT_DIR = dt .configuration .config_dir .. PS .. " color" .. PS .. " out"
74
+
75
+ -- - - - - - - - - - - - - - - - - - - - - - - -
76
+ -- N A M E S P A C E
77
+ -- - - - - - - - - - - - - - - - - - - - - - - -
78
+
79
+ local cpm = {}
80
+ cpm .widgets = {}
81
+ cpm .widgets .profile_box = dt .new_widget (" box" ){
82
+ orientation = " vertical" ,
83
+ }
84
+ cpm .module_installed = false
85
+ cpm .event_registered = false
86
+ -- - - - - - - - - - - - - - - - - - - - - - - -
87
+ -- F U N C T I O N S
88
+ -- - - - - - - - - - - - - - - - - - - - - - - -
89
+
90
+ local function check_for_directories ()
91
+ if not df .test_file (COLOR_IN_DIR , " d" ) then
92
+ dt .print_log (" didn't find " .. COLOR_IN_DIR )
93
+ return false
94
+ elseif not df .test_file (COLOR_OUT_DIR , " d" ) then
95
+ dt .print_log (" didn't find " .. COLOR_OUT_DIR )
96
+ return false
97
+ else
98
+ dt .print_log (" both directories exist" )
99
+ return true
100
+ end
101
+ end
102
+
103
+ local function add_directories ()
104
+ df .mkdir (COLOR_IN_DIR )
105
+ df .mkdir (COLOR_OUT_DIR )
106
+ end
107
+
108
+ local function list_profiles (dir )
109
+ local files = {}
110
+ local p = io.popen (DIR_CMD .. " " .. dir )
111
+ if p then
112
+ for line in p :lines () do
113
+ table.insert (files , line )
114
+ end
115
+ end
116
+ p :close ()
117
+ return files
118
+ end
119
+
120
+ local function add_profile (file , dir )
121
+ df .file_copy (file , dir )
122
+ dt .print (_ (" added color profile " .. file .. " to " .. dir ))
123
+ dt .print_log (" color profile " .. file .. " added to " .. dir )
124
+ end
125
+
126
+ local function remove_profile (file , dir )
127
+ os.remove (dir .. PS .. file )
128
+ dt .print (_ (" removed color profile " .. file .. " from " .. dir ))
129
+ dt .print_log (" color profile " .. file .. " removed from " .. dir )
130
+ end
131
+
132
+ local function clear_profile_box ()
133
+ for i = # cpm .widgets .profile_box , 1 , - 1 do
134
+ cpm .widgets .profile_box [i ] = nil
135
+ end
136
+ end
137
+
138
+ local function add_profile_callback ()
139
+ local profile_dir = COLOR_IN_DIR
140
+ if cpm .widgets .profile_set .selected == 2 then
141
+ profile_dir = COLOR_OUT_DIR
142
+ end
143
+
144
+ local new_profile = cpm .widgets .profile_selector .value
145
+ if string.lower (df .get_filetype (new_profile )) ~= " icm" and string.lower (df .get_filetype (new_profile )) ~= " icc" then
146
+ dt .print (_ (" ERROR: color profile must be an icc or icm file" ))
147
+ dt .print_error (new_profile .. " selected and isn't an icc or icm file" )
148
+ return
149
+ end
150
+
151
+ -- set selector value to directory that new profile came from
152
+ -- in case there are more
153
+ cpm .widgets .profile_selector .value = df .get_path (cpm .widgets .profile_selector .value )
154
+ add_profile (new_profile , profile_dir )
155
+ local files = list_profiles (profile_dir )
156
+ local widgets = {}
157
+
158
+ local profile_ptr = 1
159
+ for i , file in ipairs (files ) do
160
+ if # cpm .widgets .profile_box == 0 or cpm .widgets .profile_box [profile_ptr ].label ~= file then
161
+ table.insert (widgets , dt .new_widget (" check_button" ){value = false , label = file })
162
+ else
163
+ table.insert (widgets , cpm .widgets .profile_box [profile_ptr ])
164
+ profile_ptr = profile_ptr + 1
165
+ if profile_ptr > # cpm .widgets .profile_box then
166
+ profile_ptr = # cpm .widgets .profile_box
167
+ end
168
+ end
169
+ end
170
+ clear_profile_box ()
171
+ for _ , widget in ipairs (widgets ) do
172
+ table.insert (cpm .widgets .profile_box , widget )
173
+ end
174
+ if not cpm .widgets .remove_box .visible then
175
+ cpm .widgets .remove_box .visible = true
176
+ end
177
+ end
178
+
179
+ local function remove_profile_callback ()
180
+ local widgets_to_keep = {}
181
+ local profile_dir = COLOR_IN_DIR
182
+ if cpm .widgets .profile_set .selected == 2 then
183
+ profile_dir = COLOR_OUT_DIR
184
+ end
185
+
186
+ for _ , widget in ipairs (cpm .widgets .profile_box ) do
187
+ if widget .value == true then
188
+ remove_profile (widget .label , profile_dir )
189
+ else
190
+ table.insert (widgets_to_keep , widget )
191
+ end
192
+ end
193
+ clear_profile_box ()
194
+ for _ , widget in ipairs (widgets_to_keep ) do
195
+ table.insert (cpm .widgets .profile_box , widget )
196
+ end
197
+ if # cpm .widgets .profile_box == 0 then
198
+ cpm .widgets .remove_box .visible = false
199
+ else
200
+ cpm .widgets .remove_box .visible = true
201
+ end
202
+ end
203
+
204
+ local function list_profile_callback (choice )
205
+ local list_dir = COLOR_IN_DIR
206
+ if choice == 2 then
207
+ list_dir = COLOR_OUT_DIR
208
+ end
209
+
210
+ local files = list_profiles (list_dir )
211
+
212
+ if # files == 0 then
213
+ cpm .widgets .remove_box .visible = false
214
+ else
215
+ cpm .widgets .remove_box .visible = true
216
+ end
217
+
218
+ clear_profile_box ()
219
+
220
+ for i , file in ipairs (files ) do
221
+ cpm .widgets .profile_box [i ] = dt .new_widget (" check_button" ){value = false , label = file }
222
+ end
223
+ end
224
+
225
+ local function install_module ()
226
+ if not cpm .module_installed then
227
+ dt .register_lib (
228
+ MODULE_NAME , -- Module name
229
+ _ (" color profile manager" ), -- Visible name
230
+ true , -- expandable
231
+ false , -- resetable
232
+ {[dt .gui .views .lighttable ] = {" DT_UI_CONTAINER_PANEL_LEFT_CENTER" , 0 }}, -- containers
233
+ cpm .widgets .main_widget ,
234
+ nil ,-- view_enter
235
+ nil -- view_leave
236
+ )
237
+ cpm .module_installed = true
238
+ dt .control .sleep (500 )
239
+ if not cpm .initialized then
240
+ cpm .widgets .profile_set .visible = false
241
+ cpm .widgets .add_box .visible = false
242
+ cpm .widgets .remove_box .visible = false
243
+ else
244
+ cpm .widgets .profile_set .selected = 1
245
+ end
246
+ end
247
+ end
248
+
249
+ local function destroy ()
250
+ dt .gui .libs [MODULE_NAME ].visible = false
251
+ return
252
+ end
253
+
254
+ local function restart ()
255
+ dt .gui .libs [MODULE_NAME ].visible = true
256
+ return
257
+ end
258
+ -- - - - - - - - - - - - - - - - - - - - - - - -
259
+ -- W I D G E T S
260
+ -- - - - - - - - - - - - - - - - - - - - - - - -
261
+
262
+ cpm .initialized = check_for_directories ()
263
+ dt .print_log (" cpm.initialized is " .. tostring (cpm .initialized ))
264
+
265
+ if not cpm .initialized then
266
+ dt .print_log (" creating init_box" )
267
+ cpm .widgets .init_box = dt .new_widget (" box" ){
268
+ orientation = " vertical" ,
269
+ dt .new_widget (" button" ){
270
+ label = _ (" initialize color profiles" ),
271
+ tooltip = _ (" create the directory structure to contain the color profiles" ),
272
+ clicked_callback = function (this )
273
+ add_directories ()
274
+ cpm .initialized = true
275
+ cpm .widgets .profile_set .visible = true
276
+ cpm .widgets .add_box .visible = true
277
+ cpm .widgets .remove_box .visible = false
278
+ cpm .widgets .init_box .visible = false
279
+ cpm .widgets .profile_set .selected = 1
280
+ end
281
+ }
282
+ }
283
+ end
284
+
285
+ cpm .widgets .profile_set = dt .new_widget (" combobox" ){
286
+ label = _ (" select profile set" ),
287
+ tooltip = _ (" select input or output profiles" ),
288
+ visible = cpm .initialized and false or true ,
289
+ changed_callback = function (this )
290
+ if cpm .initialized then
291
+ list_profile_callback (this .selected )
292
+ end
293
+ end ,
294
+ _ (" input" ), _ (" output" ),
295
+ }
296
+
297
+ cpm .widgets .profile_selector = dt .new_widget (" file_chooser_button" ){
298
+ title = _ (" select color profile to add" ),
299
+ tooltip = _ (" select the .icc or .icm file to add" ),
300
+ is_directory = false
301
+ }
302
+
303
+ cpm .widgets .add_box = dt .new_widget (" box" ){
304
+ orientation = " vertical" ,
305
+ visible = cpm .initialized and true or false ,
306
+ dt .new_widget (" section_label" ){label = _ (" add profile" )},
307
+ cpm .widgets .profile_selector ,
308
+ dt .new_widget (" button" ){
309
+ label = _ (" add selected color profile" ),
310
+ tooltip = _ (" add selected file to profiles" ),
311
+ clicked_callback = function (this )
312
+ add_profile_callback ()
313
+ end
314
+ }
315
+ }
316
+
317
+ cpm .widgets .remove_box = dt .new_widget (" box" ){
318
+ orientation = " vertical" ,
319
+ visible = cpm .initialized and true or false ,
320
+ dt .new_widget (" section_label" ){label = _ (" remove profile" )},
321
+ cpm .widgets .profile_box ,
322
+ dt .new_widget (" button" ){
323
+ label = _ (" remove selected profile(s)" ),
324
+ tooltip = _ (" remove the checked profile(s)" ),
325
+ clicked_callback = function (this )
326
+ remove_profile_callback ()
327
+ end
328
+ }
329
+ }
330
+
331
+ local main_widgets = {}
332
+
333
+ if not cpm .initialized then
334
+ table.insert (main_widgets , cpm .widgets .init_box )
335
+ end
336
+ table.insert (main_widgets , cpm .widgets .profile_set )
337
+ table.insert (main_widgets , cpm .widgets .remove_box )
338
+ table.insert (main_widgets , cpm .widgets .add_box )
339
+
340
+ cpm .widgets .main_widget = dt .new_widget (" box" ){
341
+ orientation = " vertical" ,
342
+ table.unpack (main_widgets )
343
+ }
344
+
345
+ -- - - - - - - - - - - - - - - - - - - - - - - -
346
+ -- M A I N
347
+ -- - - - - - - - - - - - - - - - - - - - - - - -
348
+
349
+ if dt .gui .current_view ().id == " lighttable" then
350
+ install_module ()
351
+ else
352
+ if not cpm .event_registered then
353
+ dt .register_event (
354
+ MODULE_NAME , " view-changed" ,
355
+ function (event , old_view , new_view )
356
+ if new_view .name == " lighttable" and old_view .name == " darkroom" then
357
+ install_module ()
358
+ end
359
+ end
360
+ )
361
+ cpm .event_registered = true
362
+ end
363
+ end
364
+
365
+ local script_data = {}
366
+
367
+ script_data .destroy = destroy
368
+ script_data .restart = restart
369
+ script_data .destroy_method = " hide"
370
+ script_data .show = restart
371
+
372
+ return script_data
0 commit comments