1 /*
2 This file is part of darktable,
3 Copyright (C) 2013-2021 darktable developers.
4
5 darktable is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 darktable is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include "lua/preferences.h"
19 #include "control/conf.h"
20 #include "gui/gtk.h"
21 #include "lua/call.h"
22 #include "lua/widget/widget.h"
23 #include <glib.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 typedef enum
28 {
29 pref_enum,
30 pref_dir,
31 pref_file,
32 pref_string,
33 pref_bool,
34 pref_int,
35 pref_float,
36 pref_lua,
37 } lua_pref_type;
38
39
40 typedef struct enum_data_t
41 {
42 char *default_value;
43 luaA_Type enum_type;
44 } enum_data_t;
45 typedef struct dir_data_t
46 {
47 char *default_value;
48 } dir_data_t;
49 typedef struct file_data_t
50 {
51 char *default_value;
52 } file_data_t;
53 typedef struct string_data_t
54 {
55 char *default_value;
56 } string_data_t;
57 typedef struct bool_data_t
58 {
59 gboolean default_value;
60 } bool_data_t;
61 typedef struct int_data_t
62 {
63 int default_value;
64 } int_data_t;
65
66 typedef struct float_data_t
67 {
68 float default_value;
69 } float_data_t;
70 typedef struct lua_data_t
71 {
72 char *default_value;
73 } lua_data_t;
74
75 typedef union all_data_t
76 {
77 enum_data_t enum_data;
78 dir_data_t dir_data;
79 file_data_t file_data;
80 string_data_t string_data;
81 bool_data_t bool_data;
82 int_data_t int_data;
83 float_data_t float_data;
84 lua_data_t lua_data;
85 } all_data_t;
86
87 struct pref_element;
88 typedef void (update_widget_function)(struct pref_element* , GtkWidget* , GtkWidget* );
89 static void update_widget_enum(struct pref_element* cur_elt, GtkWidget* dialog, GtkWidget* labelev);
90 static void update_widget_dir(struct pref_element* cur_elt, GtkWidget* dialog, GtkWidget* labelev);
91 static void update_widget_file(struct pref_element* cur_elt, GtkWidget* dialog, GtkWidget* labelev);
92 static void update_widget_string(struct pref_element* cur_elt, GtkWidget* dialog, GtkWidget* labelev);
93 static void update_widget_bool(struct pref_element* cur_elt, GtkWidget* dialog, GtkWidget* labelev);
94 static void update_widget_int(struct pref_element* cur_elt, GtkWidget* dialog, GtkWidget* labelev);
95 static void update_widget_float(struct pref_element* cur_elt, GtkWidget* dialog, GtkWidget* labelev);
96 static void update_widget_lua(struct pref_element* cur_elt, GtkWidget* dialog, GtkWidget* labelev);
97
98 typedef struct pref_element
99 {
100 char *script;
101 char *name;
102 char *label;
103 char *tooltip;
104 char *tooltip_reset;
105 lua_pref_type type;
106 struct pref_element *next;
107 all_data_t type_data;
108 // The widget used for this preference
109 GtkWidget *widget;
110 update_widget_function* update_widget;
111
112
113 } pref_element;
114
destroy_pref_element(pref_element * elt)115 static void destroy_pref_element(pref_element *elt)
116 {
117 free(elt->script);
118 free(elt->name);
119 free(elt->label);
120 free(elt->tooltip);
121 free(elt->tooltip_reset);
122 if(elt->widget) g_object_unref(elt->widget);
123 switch(elt->type)
124 {
125 case pref_enum:
126 free(elt->type_data.enum_data.default_value);
127 break;
128 case pref_dir:
129 free(elt->type_data.dir_data.default_value);
130 break;
131 case pref_file:
132 free(elt->type_data.file_data.default_value);
133 break;
134 case pref_string:
135 free(elt->type_data.string_data.default_value);
136 break;
137 case pref_lua:
138 free(elt->type_data.lua_data.default_value);
139 break;
140 case pref_bool:
141 case pref_int:
142 case pref_float:
143 default:
144 break;
145 }
146 free(elt);
147 }
148
149 static pref_element *pref_list = NULL;
150
151 // get all the darktablerc keys
get_keys(lua_State * L)152 static int get_keys(lua_State *L)
153 {
154 dt_pthread_mutex_lock(&darktable.conf->mutex);
155 GList* keys = g_hash_table_get_keys(darktable.conf->table);
156 dt_pthread_mutex_unlock(&darktable.conf->mutex);
157
158 keys = g_list_sort(keys, (GCompareFunc) strcmp);
159 lua_newtable(L);
160 int table_index = 1;
161 for(const GList* key = keys; key; key = g_list_next(key))
162 {
163 lua_pushstring(L, key->data);
164 lua_seti(L, -2, table_index);
165 table_index++;
166 }
167 g_list_free(keys);
168 return 1;
169 }
170
get_pref_name(char * tgt,size_t size,const char * script,const char * name)171 static void get_pref_name(char *tgt, size_t size, const char *script, const char *name)
172 {
173 snprintf(tgt, size, "lua/%s/%s", script, name);
174 }
175
176 // read lua and darktable prefs
read_pref(lua_State * L)177 static int read_pref(lua_State *L)
178 {
179 const char *script = luaL_checkstring(L, 1);
180 const char *name = luaL_checkstring(L, 2);
181 lua_pref_type i;
182 luaA_to(L, lua_pref_type, &i, 3);
183
184 char pref_name[1024];
185 if(strcmp(script, "darktable") != 0)
186 get_pref_name(pref_name, sizeof(pref_name), script, name);
187 else
188 snprintf(pref_name, sizeof(pref_name), "%s", name);
189 switch(i)
190 {
191 case pref_enum:
192 {
193 const char *str = dt_conf_get_string_const(pref_name);
194 lua_pushstring(L, str);
195 break;
196 }
197 case pref_dir:
198 {
199 const char *str = dt_conf_get_string_const(pref_name);
200 lua_pushstring(L, str);
201 break;
202 }
203 case pref_file:
204 {
205 const char *str = dt_conf_get_string_const(pref_name);
206 lua_pushstring(L, str);
207 break;
208 }
209 case pref_string:
210 {
211 const char *str = dt_conf_get_string_const(pref_name);
212 lua_pushstring(L, str);
213 break;
214 }
215 case pref_bool:
216 lua_pushboolean(L, dt_conf_get_bool(pref_name));
217 break;
218 case pref_int:
219 lua_pushinteger(L, dt_conf_get_int(pref_name));
220 break;
221 case pref_float:
222 lua_pushnumber(L, dt_conf_get_float(pref_name));
223 break;
224 case pref_lua:
225 {
226 const char *str = dt_conf_get_string_const(pref_name);
227 lua_pushstring(L, str);
228 break;
229 }
230 }
231 return 1;
232 }
233
234 // write lua prefs
write_pref(lua_State * L)235 static int write_pref(lua_State *L)
236 {
237 const char *script = luaL_checkstring(L, 1);
238 const char *name = luaL_checkstring(L, 2);
239 int i, tmp;
240 luaA_to(L, lua_pref_type, &i, 3);
241
242 char pref_name[1024];
243 get_pref_name(pref_name, sizeof(pref_name), script, name);
244 switch(i)
245 {
246 case pref_enum:
247 luaA_to_type(L, luaA_type_find(L,pref_name), &tmp, 4);
248 dt_conf_set_string(pref_name, lua_tostring(L, 4));
249 break;
250 case pref_dir:
251 dt_conf_set_string(pref_name, luaL_checkstring(L, 4));
252 break;
253 case pref_file:
254 dt_conf_set_string(pref_name, luaL_checkstring(L, 4));
255 break;
256 case pref_string:
257 dt_conf_set_string(pref_name, luaL_checkstring(L, 4));
258 break;
259 case pref_bool:
260 luaL_checktype(L, 4, LUA_TBOOLEAN);
261 dt_conf_set_bool(pref_name, lua_toboolean(L, 4));
262 break;
263 case pref_int:
264 dt_conf_set_int(pref_name, luaL_checkinteger(L, 4));
265 break;
266 case pref_float:
267 dt_conf_set_float(pref_name, luaL_checknumber(L, 4));
268 break;
269 case pref_lua:
270 dt_conf_set_string(pref_name, luaL_checkstring(L, 4));
271 break;
272 }
273 return 0;
274 }
275
276 // destroy lua prefs
destroy_pref(lua_State * L)277 static int destroy_pref(lua_State *L)
278 {
279 gboolean result;
280 const char *script = luaL_checkstring(L, 1);
281 const char *name = luaL_checkstring(L, 2);
282
283 char pref_name[1024];
284 get_pref_name(pref_name, sizeof(pref_name), script, name);
285 dt_pthread_mutex_lock(&darktable.conf->mutex);
286 result = g_hash_table_remove(darktable.conf->table, pref_name);
287 dt_pthread_mutex_unlock(&darktable.conf->mutex);
288 lua_pushboolean(L, result);
289 return 1;
290 }
291
response_callback_enum(GtkDialog * dialog,gint response_id,pref_element * cur_elt)292 static void response_callback_enum(GtkDialog *dialog, gint response_id, pref_element *cur_elt)
293 {
294 if(response_id == GTK_RESPONSE_DELETE_EVENT)
295 {
296 char pref_name[1024];
297 get_pref_name(pref_name, sizeof(pref_name), cur_elt->script, cur_elt->name);
298 char *text = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(cur_elt->widget));
299 dt_conf_set_string(pref_name, text);
300 g_free(text);
301 }
302 }
303
304
response_callback_dir(GtkDialog * dialog,gint response_id,pref_element * cur_elt)305 static void response_callback_dir(GtkDialog *dialog, gint response_id, pref_element *cur_elt)
306 {
307 if(response_id == GTK_RESPONSE_DELETE_EVENT)
308 {
309 char pref_name[1024];
310 get_pref_name(pref_name, sizeof(pref_name), cur_elt->script, cur_elt->name);
311 gchar *folder = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(cur_elt->widget));
312 dt_conf_set_string(pref_name, folder);
313 g_free(folder);
314 }
315 }
316
317
response_callback_file(GtkDialog * dialog,gint response_id,pref_element * cur_elt)318 static void response_callback_file(GtkDialog *dialog, gint response_id, pref_element *cur_elt)
319 {
320 if(response_id == GTK_RESPONSE_DELETE_EVENT)
321 {
322 char pref_name[1024];
323 get_pref_name(pref_name, sizeof(pref_name), cur_elt->script, cur_elt->name);
324 gchar *file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(cur_elt->widget));
325 dt_conf_set_string(pref_name, file);
326 g_free(file);
327 }
328 }
329
330
response_callback_string(GtkDialog * dialog,gint response_id,pref_element * cur_elt)331 static void response_callback_string(GtkDialog *dialog, gint response_id, pref_element *cur_elt)
332 {
333 if(response_id == GTK_RESPONSE_DELETE_EVENT)
334 {
335 char pref_name[1024];
336 get_pref_name(pref_name, sizeof(pref_name), cur_elt->script, cur_elt->name);
337 dt_conf_set_string(pref_name, gtk_entry_get_text(GTK_ENTRY(cur_elt->widget)));
338 }
339 }
340
341
response_callback_bool(GtkDialog * dialog,gint response_id,pref_element * cur_elt)342 static void response_callback_bool(GtkDialog *dialog, gint response_id, pref_element *cur_elt)
343 {
344 if(response_id == GTK_RESPONSE_DELETE_EVENT)
345 {
346 char pref_name[1024];
347 get_pref_name(pref_name, sizeof(pref_name), cur_elt->script, cur_elt->name);
348 dt_conf_set_bool(pref_name, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cur_elt->widget)));
349 }
350 }
351
352
response_callback_int(GtkDialog * dialog,gint response_id,pref_element * cur_elt)353 static void response_callback_int(GtkDialog *dialog, gint response_id, pref_element *cur_elt)
354 {
355 if(response_id == GTK_RESPONSE_DELETE_EVENT)
356 {
357 char pref_name[1024];
358 get_pref_name(pref_name, sizeof(pref_name), cur_elt->script, cur_elt->name);
359 dt_conf_set_int(pref_name, gtk_spin_button_get_value(GTK_SPIN_BUTTON(cur_elt->widget)));
360 }
361 }
362
363
response_callback_float(GtkDialog * dialog,gint response_id,pref_element * cur_elt)364 static void response_callback_float(GtkDialog *dialog, gint response_id, pref_element *cur_elt)
365 {
366 if(response_id == GTK_RESPONSE_DELETE_EVENT)
367 {
368 char pref_name[1024];
369 get_pref_name(pref_name, sizeof(pref_name), cur_elt->script, cur_elt->name);
370 dt_conf_set_float(pref_name, gtk_spin_button_get_value(GTK_SPIN_BUTTON(cur_elt->widget)));
371 }
372 }
373
374
response_callback_lua(GtkDialog * dialog,gint response_id,pref_element * cur_elt)375 static void response_callback_lua(GtkDialog *dialog, gint response_id, pref_element *cur_elt)
376 {
377 if(response_id == GTK_RESPONSE_DELETE_EVENT)
378 {
379 dt_lua_lock_silent();
380 lua_State * L = darktable.lua_state.state;
381 lua_pushcfunction(L, dt_lua_widget_trigger_callback);
382 luaA_push(L, lua_widget, &cur_elt->widget);
383 lua_pushstring(L, "set_pref");
384 lua_call(L, 2, 0);
385 dt_lua_unlock();
386 }
387 }
388
389
reset_widget_enum(GtkWidget * label,GdkEventButton * event,pref_element * cur_elt)390 static gboolean reset_widget_enum(GtkWidget *label, GdkEventButton *event, pref_element *cur_elt)
391 {
392 if(event->type == GDK_2BUTTON_PRESS)
393 {
394 gtk_combo_box_set_active(GTK_COMBO_BOX(cur_elt->widget), 0);
395 return TRUE;
396 }
397 return FALSE;
398 }
399
400
reset_widget_dir(GtkWidget * label,GdkEventButton * event,pref_element * cur_elt)401 static gboolean reset_widget_dir(GtkWidget *label, GdkEventButton *event, pref_element *cur_elt)
402 {
403 if(event->type == GDK_2BUTTON_PRESS)
404 {
405 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(cur_elt->widget),
406 cur_elt->type_data.dir_data.default_value);
407 return TRUE;
408 }
409 return FALSE;
410 }
411
412
reset_widget_file(GtkWidget * label,GdkEventButton * event,pref_element * cur_elt)413 static gboolean reset_widget_file(GtkWidget *label, GdkEventButton *event, pref_element *cur_elt)
414 {
415 if(event->type == GDK_2BUTTON_PRESS)
416 {
417 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(cur_elt->widget),
418 cur_elt->type_data.file_data.default_value);
419 return TRUE;
420 }
421 return FALSE;
422 }
423
424
reset_widget_string(GtkWidget * label,GdkEventButton * event,pref_element * cur_elt)425 static gboolean reset_widget_string(GtkWidget *label, GdkEventButton *event, pref_element *cur_elt)
426 {
427 if(event->type == GDK_2BUTTON_PRESS)
428 {
429 gtk_entry_set_text(GTK_ENTRY(cur_elt->widget), cur_elt->type_data.string_data.default_value);
430 return TRUE;
431 }
432 return FALSE;
433 }
434
435
reset_widget_bool(GtkWidget * label,GdkEventButton * event,pref_element * cur_elt)436 static gboolean reset_widget_bool(GtkWidget *label, GdkEventButton *event, pref_element *cur_elt)
437 {
438 if(event->type == GDK_2BUTTON_PRESS)
439 {
440 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cur_elt->widget),
441 cur_elt->type_data.bool_data.default_value);
442 return TRUE;
443 }
444 return FALSE;
445 }
446
447
reset_widget_int(GtkWidget * label,GdkEventButton * event,pref_element * cur_elt)448 static gboolean reset_widget_int(GtkWidget *label, GdkEventButton *event, pref_element *cur_elt)
449 {
450 if(event->type == GDK_2BUTTON_PRESS)
451 {
452 gtk_spin_button_set_value(GTK_SPIN_BUTTON(cur_elt->widget), cur_elt->type_data.int_data.default_value);
453 return TRUE;
454 }
455 return FALSE;
456 }
457
458
reset_widget_float(GtkWidget * label,GdkEventButton * event,pref_element * cur_elt)459 static gboolean reset_widget_float(GtkWidget *label, GdkEventButton *event, pref_element *cur_elt)
460 {
461 if(event->type == GDK_2BUTTON_PRESS)
462 {
463 gtk_spin_button_set_value(GTK_SPIN_BUTTON(cur_elt->widget), cur_elt->type_data.float_data.default_value);
464 return TRUE;
465 }
466 return FALSE;
467 }
468
469
reset_widget_lua(GtkWidget * label,GdkEventButton * event,pref_element * cur_elt)470 static gboolean reset_widget_lua(GtkWidget *label, GdkEventButton *event, pref_element *cur_elt)
471 {
472 if(event->type == GDK_2BUTTON_PRESS)
473 {
474 char pref_name[1024];
475 get_pref_name(pref_name, sizeof(pref_name), cur_elt->script, cur_elt->name);
476 gchar *old_str = dt_conf_get_string(pref_name);
477 dt_conf_set_string(pref_name, cur_elt->type_data.lua_data.default_value);
478 dt_lua_lock_silent();
479 lua_State * L = darktable.lua_state.state;
480 lua_pushcfunction(L, dt_lua_widget_trigger_callback);
481 luaA_push(L, lua_widget, &cur_elt->widget);
482 luaA_push(L, lua_widget, &cur_elt->widget);
483 lua_pushstring(L, "set_pref");
484 lua_call(L, 3, 0);
485 dt_lua_unlock();
486 dt_conf_set_string(pref_name, old_str);
487 g_free(old_str);
488 return TRUE;
489 }
490 return FALSE;
491 }
492
493
update_widget_enum(pref_element * cur_elt,GtkWidget * dialog,GtkWidget * labelev)494 static void update_widget_enum(pref_element* cur_elt, GtkWidget* dialog, GtkWidget* labelev)
495 {
496 char pref_name[1024];
497 get_pref_name(pref_name, sizeof(pref_name), cur_elt->script, cur_elt->name);
498 g_signal_connect(G_OBJECT(labelev), "button-press-event", G_CALLBACK(reset_widget_enum), cur_elt);
499 g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(response_callback_enum), cur_elt);
500 gtk_combo_box_set_active(GTK_COMBO_BOX(cur_elt->widget), 0);
501 const char *value = dt_conf_get_string_const(pref_name);
502 do {
503 char * active_entry = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(cur_elt->widget));
504 if(!active_entry)
505 {
506 gtk_combo_box_set_active(GTK_COMBO_BOX(cur_elt->widget), -1);
507 g_free(active_entry);
508 break;
509 }
510 else if(!strcmp(active_entry, value))
511 {
512 g_free(active_entry);
513 break;
514 }
515 else
516 {
517 gtk_combo_box_set_active(GTK_COMBO_BOX(cur_elt->widget), gtk_combo_box_get_active(GTK_COMBO_BOX(cur_elt->widget)) + 1);
518 g_free(active_entry);
519 }
520 } while(true);
521 }
522
523
update_widget_dir(pref_element * cur_elt,GtkWidget * dialog,GtkWidget * labelev)524 static void update_widget_dir(pref_element* cur_elt, GtkWidget* dialog, GtkWidget* labelev)
525 {
526 char pref_name[1024];
527 get_pref_name(pref_name, sizeof(pref_name), cur_elt->script, cur_elt->name);
528 const char *str = dt_conf_get_string_const(pref_name);
529 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(cur_elt->widget), str);
530 g_signal_connect(G_OBJECT(labelev), "button-press-event", G_CALLBACK(reset_widget_dir), cur_elt);
531 g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(response_callback_dir), cur_elt);
532 }
533
534
update_widget_file(pref_element * cur_elt,GtkWidget * dialog,GtkWidget * labelev)535 static void update_widget_file(pref_element* cur_elt, GtkWidget* dialog, GtkWidget* labelev)
536 {
537 char pref_name[1024];
538 get_pref_name(pref_name, sizeof(pref_name), cur_elt->script, cur_elt->name);
539 const char *str = dt_conf_get_string_const(pref_name);
540 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(cur_elt->widget), str);
541 g_signal_connect(G_OBJECT(labelev), "button-press-event", G_CALLBACK(reset_widget_file), cur_elt);
542 g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(response_callback_file), cur_elt);
543 }
544
545
update_widget_string(pref_element * cur_elt,GtkWidget * dialog,GtkWidget * labelev)546 static void update_widget_string(pref_element* cur_elt, GtkWidget* dialog, GtkWidget* labelev)
547 {
548 char pref_name[1024];
549 get_pref_name(pref_name, sizeof(pref_name), cur_elt->script, cur_elt->name);
550 g_signal_connect(G_OBJECT(labelev), "button-press-event", G_CALLBACK(reset_widget_string), cur_elt);
551 g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(response_callback_string), cur_elt);
552 const char *str = dt_conf_get_string_const(pref_name);
553 gtk_entry_set_text(GTK_ENTRY(cur_elt->widget), str);
554 }
555
556
update_widget_bool(pref_element * cur_elt,GtkWidget * dialog,GtkWidget * labelev)557 static void update_widget_bool(pref_element* cur_elt, GtkWidget* dialog, GtkWidget* labelev)
558 {
559 char pref_name[1024];
560 get_pref_name(pref_name, sizeof(pref_name), cur_elt->script, cur_elt->name);
561 g_signal_connect(G_OBJECT(labelev), "button-press-event", G_CALLBACK(reset_widget_bool), cur_elt);
562 g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(response_callback_bool), cur_elt);
563 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cur_elt->widget), dt_conf_get_bool(pref_name));
564 }
565
566
update_widget_int(pref_element * cur_elt,GtkWidget * dialog,GtkWidget * labelev)567 static void update_widget_int(pref_element* cur_elt, GtkWidget* dialog, GtkWidget* labelev)
568 {
569 char pref_name[1024];
570 get_pref_name(pref_name, sizeof(pref_name), cur_elt->script, cur_elt->name);
571 gtk_spin_button_set_value(GTK_SPIN_BUTTON(cur_elt->widget), dt_conf_get_int(pref_name));
572 g_signal_connect(G_OBJECT(labelev), "button-press-event", G_CALLBACK(reset_widget_int), cur_elt);
573 g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(response_callback_int), cur_elt);
574 }
575
576
update_widget_float(pref_element * cur_elt,GtkWidget * dialog,GtkWidget * labelev)577 static void update_widget_float(pref_element* cur_elt, GtkWidget* dialog, GtkWidget* labelev)
578 {
579 char pref_name[1024];
580 get_pref_name(pref_name, sizeof(pref_name), cur_elt->script, cur_elt->name);
581 gtk_spin_button_set_value(GTK_SPIN_BUTTON(cur_elt->widget), dt_conf_get_float(pref_name));
582 g_signal_connect(G_OBJECT(labelev), "button-press-event", G_CALLBACK(reset_widget_float), cur_elt);
583 g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(response_callback_float), cur_elt);
584 }
585
586
update_widget_lua(pref_element * cur_elt,GtkWidget * dialog,GtkWidget * labelev)587 static void update_widget_lua(pref_element* cur_elt, GtkWidget* dialog, GtkWidget* labelev)
588 {
589 dt_lua_lock_silent();
590 lua_State * L = darktable.lua_state.state;
591 lua_pushcfunction(L, dt_lua_widget_trigger_callback);
592 luaA_push(L, lua_widget, &cur_elt->widget);
593 lua_pushstring(L, "reset");
594 lua_call(L, 2, 0);
595 dt_lua_unlock();
596 g_signal_connect(G_OBJECT(labelev), "button-press-event", G_CALLBACK(reset_widget_lua), cur_elt);
597 g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(response_callback_lua), cur_elt);
598 }
599
600
register_pref_sub(lua_State * L)601 static int register_pref_sub(lua_State *L)
602 {
603 // to avoid leaks, we need to first get all params (which could raise errors) then alloc and fill the struct
604 // this is complicated, but needed
605 pref_element **tmp = lua_touserdata(L, -1);
606 lua_pop(L, 1);
607 *tmp = calloc(1, sizeof(pref_element));
608 pref_element *built_elt = *tmp;
609 int cur_param = 1;
610
611 built_elt->script = strdup(luaL_checkstring(L, cur_param));
612 cur_param++;
613
614 built_elt->name = strdup(luaL_checkstring(L, cur_param));
615 cur_param++;
616
617 luaA_to(L, lua_pref_type, &built_elt->type, cur_param);
618 cur_param++;
619
620 built_elt->label = strdup(luaL_checkstring(L, cur_param));
621 cur_param++;
622
623 built_elt->tooltip = strdup(luaL_checkstring(L, cur_param));
624 cur_param++;
625
626 char pref_name[1024];
627 get_pref_name(pref_name, sizeof(pref_name), built_elt->script, built_elt->name);
628 switch(built_elt->type)
629 {
630 case pref_enum:
631 {
632 luaA_Type enum_type = luaA_type_add(L, pref_name, sizeof(int));
633 luaA_enum_type(L, enum_type, sizeof(int));
634 built_elt->type_data.enum_data.enum_type = enum_type;
635 built_elt->widget = gtk_combo_box_text_new();
636
637 int value = 0;
638 built_elt->type_data.enum_data.default_value = strdup(luaL_checkstring(L, cur_param));
639 while(!lua_isnoneornil(L, cur_param))
640 {
641 luaA_enum_value_type(L, enum_type, &value, luaL_checkstring(L, cur_param));
642 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(built_elt->widget), luaL_checkstring(L, cur_param));
643 cur_param++;
644 value++;
645 }
646
647 if(!dt_conf_key_exists(pref_name)) {
648 dt_conf_set_string(pref_name, built_elt->type_data.enum_data.default_value);
649 }
650
651
652 g_object_ref_sink(G_OBJECT(built_elt->widget));
653 built_elt->tooltip_reset = g_strdup_printf( _("double click to reset to `%s'"),
654 built_elt->type_data.enum_data.default_value);
655 built_elt->update_widget = update_widget_enum;
656 break;
657 }
658 case pref_dir:
659 built_elt->type_data.dir_data.default_value = strdup(luaL_checkstring(L, cur_param));
660 cur_param++;
661
662 if(!dt_conf_key_exists(pref_name)) {
663 dt_conf_set_string(pref_name, built_elt->type_data.dir_data.default_value);
664 }
665 built_elt->widget = gtk_file_chooser_button_new(_("select directory"), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
666 gtk_file_chooser_button_set_width_chars(GTK_FILE_CHOOSER_BUTTON(built_elt->widget), 20);
667 g_object_ref_sink(G_OBJECT(built_elt->widget));
668 built_elt->tooltip_reset = g_strdup_printf( _("double click to reset to `%s'"), built_elt->type_data.dir_data.default_value);
669 built_elt->update_widget = update_widget_dir;
670 break;
671 case pref_file:
672 built_elt->type_data.file_data.default_value = strdup(luaL_checkstring(L, cur_param));
673 cur_param++;
674
675 if(!dt_conf_key_exists(pref_name))
676 dt_conf_set_string(pref_name, built_elt->type_data.file_data.default_value);
677
678 built_elt->widget = gtk_file_chooser_button_new(_("select file"), GTK_FILE_CHOOSER_ACTION_OPEN);
679 gtk_file_chooser_button_set_width_chars(GTK_FILE_CHOOSER_BUTTON(built_elt->widget), 20);
680 built_elt->tooltip_reset= g_strdup_printf( _("double click to reset to `%s'"), built_elt->type_data.file_data.default_value);
681 g_object_ref_sink(G_OBJECT(built_elt->widget));
682 built_elt->update_widget = update_widget_file;
683 break;
684 case pref_string:
685 built_elt->type_data.string_data.default_value = strdup(luaL_checkstring(L, cur_param));
686 cur_param++;
687
688 if(!dt_conf_key_exists(pref_name))
689 dt_conf_set_string(pref_name, built_elt->type_data.string_data.default_value);
690
691 built_elt->widget = gtk_entry_new();
692 built_elt->tooltip_reset= g_strdup_printf( _("double click to reset to `%s'"),
693 built_elt->type_data.string_data.default_value);
694 g_object_ref_sink(G_OBJECT(built_elt->widget));
695 built_elt->update_widget = update_widget_string;
696 break;
697 case pref_bool:
698 luaL_checktype(L, cur_param, LUA_TBOOLEAN);
699 built_elt->type_data.bool_data.default_value = lua_toboolean(L, cur_param);
700 cur_param++;
701
702 if(!dt_conf_key_exists(pref_name))
703 dt_conf_set_bool(pref_name, built_elt->type_data.bool_data.default_value);
704
705 built_elt->widget = gtk_check_button_new();
706 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(built_elt->widget), dt_conf_get_bool(pref_name));
707 g_object_ref_sink(G_OBJECT(built_elt->widget));
708 built_elt->tooltip_reset = g_strdup_printf( _("double click to reset to `%s'"),
709 built_elt->type_data.bool_data.default_value ? "true" : "false");
710 built_elt->update_widget = update_widget_bool;
711 break;
712 case pref_int:
713 {
714 luaL_checktype(L, cur_param, LUA_TNUMBER);
715 built_elt->type_data.int_data.default_value = lua_tointeger(L, cur_param);
716 cur_param++;
717
718 luaL_checktype(L, cur_param, LUA_TNUMBER);
719 int min = lua_tointeger(L, cur_param);
720 cur_param++;
721
722 luaL_checktype(L, cur_param, LUA_TNUMBER);
723 int max = lua_tointeger(L, cur_param);
724 cur_param++;
725
726 if(!dt_conf_key_exists(pref_name))
727 dt_conf_set_int(pref_name, built_elt->type_data.int_data.default_value);
728 built_elt->widget = gtk_spin_button_new_with_range(min, max, 1);
729 gtk_spin_button_set_digits(GTK_SPIN_BUTTON(built_elt->widget), 0);
730 g_object_ref_sink(G_OBJECT(built_elt->widget));
731 built_elt->tooltip_reset = g_strdup_printf( _("double click to reset to `%d'"),
732 built_elt->type_data.int_data.default_value);
733 built_elt->update_widget = update_widget_int;
734 break;
735 }
736 case pref_float:
737 {
738 luaL_checktype(L, cur_param, LUA_TNUMBER);
739 built_elt->type_data.float_data.default_value = lua_tonumber(L, cur_param);
740 cur_param++;
741
742 luaL_checktype(L, cur_param, LUA_TNUMBER);
743 float min = lua_tonumber(L, cur_param);
744 cur_param++;
745
746 luaL_checktype(L, cur_param, LUA_TNUMBER);
747 float max = lua_tonumber(L, cur_param);
748 cur_param++;
749
750 luaL_checktype(L, cur_param, LUA_TNUMBER);
751 float step = lua_tonumber(L, cur_param);
752 cur_param++;
753
754 if(!dt_conf_key_exists(pref_name))
755 dt_conf_set_float(pref_name, built_elt->type_data.float_data.default_value);
756
757 built_elt->widget = gtk_spin_button_new_with_range(min, max, step);
758 built_elt->tooltip_reset = g_strdup_printf( _("double click to reset to `%f'"),
759 built_elt->type_data.float_data.default_value);
760 g_object_ref_sink(G_OBJECT(built_elt->widget));
761 built_elt->update_widget = update_widget_float;
762 break;
763 }
764 case pref_lua:
765 {
766 built_elt->type_data.lua_data.default_value = strdup(luaL_checkstring(L, cur_param));
767 cur_param++;
768
769 if(!dt_conf_key_exists(pref_name))
770 dt_conf_set_string(pref_name, built_elt->type_data.lua_data.default_value);
771
772 built_elt->tooltip_reset= g_strdup_printf( _("double click to reset to `%s'"),
773 built_elt->type_data.lua_data.default_value);
774
775 lua_widget widget;
776 luaA_to(L, lua_widget, &widget, cur_param);
777 cur_param++;
778 dt_lua_widget_bind(L, widget);
779 built_elt->widget = widget->widget;
780 built_elt->update_widget = update_widget_lua;
781
782 luaL_checktype(L, cur_param, LUA_TFUNCTION);
783 luaA_push(L, lua_widget, widget);
784 lua_pushvalue(L, cur_param);
785 dt_lua_widget_set_callback(L, -2, "set_pref");
786 lua_pop(L,1);
787
788 break;
789 }
790 }
791 return 0;
792 }
793
794
register_pref(lua_State * L)795 static int register_pref(lua_State *L)
796 {
797 // wrapper to catch lua errors in a clean way
798 pref_element *built_elt = NULL;
799 lua_pushcfunction(L, register_pref_sub);
800 dt_lua_gtk_wrap(L);
801 lua_insert(L, 1);
802 lua_pushlightuserdata(L, &built_elt);
803 int result = dt_lua_treated_pcall(L, lua_gettop(L) - 1, 0);
804 if(result == LUA_OK)
805 {
806 built_elt->next = pref_list;
807 pref_list = built_elt;
808 return 0;
809 }
810 else
811 {
812 destroy_pref_element(built_elt);
813 return lua_error(L);
814 }
815 }
816
817
init_tab_lua(GtkWidget * dialog,GtkWidget * stack)818 GtkGrid* init_tab_lua(GtkWidget *dialog, GtkWidget *stack)
819 {
820 if(!pref_list) return NULL; // no option registered => don't create the tab
821 GtkWidget *label, *labelev, *viewport;
822 GtkWidget *grid = gtk_grid_new();
823 int line = 0;
824 gtk_grid_set_row_spacing(GTK_GRID(grid), DT_PIXEL_APPLY_DPI(5));
825 gtk_grid_set_column_spacing(GTK_GRID(grid), DT_PIXEL_APPLY_DPI(5));
826 gtk_grid_set_row_homogeneous(GTK_GRID(grid), TRUE);
827 gtk_widget_set_valign(grid, GTK_ALIGN_START);
828 GtkWidget *scroll = gtk_scrolled_window_new(NULL, NULL);
829 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
830 viewport = gtk_viewport_new(NULL, NULL);
831 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE); // doesn't seem to work from gtkrc
832 gtk_container_add(GTK_CONTAINER(scroll), viewport);
833 gtk_container_add(GTK_CONTAINER(viewport), grid);
834 gtk_stack_add_titled(GTK_STACK(stack), scroll, _("lua options"), _("lua options"));
835
836 for(pref_element *cur_elt = pref_list; cur_elt; cur_elt = cur_elt->next)
837 {
838 char pref_name[1024];
839 get_pref_name(pref_name, sizeof(pref_name), cur_elt->script, cur_elt->name);
840 label = gtk_label_new(cur_elt->label);
841 gtk_widget_set_halign(GTK_WIDGET(label), GTK_ALIGN_START);
842 labelev = gtk_event_box_new();
843 gtk_widget_add_events(labelev, GDK_BUTTON_PRESS_MASK);
844 gtk_container_add(GTK_CONTAINER(labelev), label);
845 cur_elt->update_widget(cur_elt,dialog,labelev);
846 gtk_widget_set_tooltip_text(labelev, cur_elt->tooltip_reset);
847 gtk_event_box_set_visible_window(GTK_EVENT_BOX(labelev), FALSE);
848 gtk_widget_set_tooltip_text(cur_elt->widget, cur_elt->tooltip);
849 gtk_grid_attach(GTK_GRID(grid), labelev, 0, line, 1, 1);
850 gtk_grid_attach(GTK_GRID(grid), cur_elt->widget, 1, line, 1, 1);
851 line++;
852 }
853 return GTK_GRID(grid);
854 }
855
856
destroy_tab_lua(GtkGrid * grid)857 void destroy_tab_lua(GtkGrid *grid)
858 {
859 if(!grid) return;
860 gtk_grid_remove_column(grid, 1); // detach all special widgets to avoid having them destroyed
861 }
862
863
dt_lua_init_preferences(lua_State * L)864 int dt_lua_init_preferences(lua_State *L)
865 {
866 luaA_enum(L, lua_pref_type);
867 luaA_enum_value_name(L, lua_pref_type, pref_string, "string");
868 luaA_enum_value_name(L, lua_pref_type, pref_bool, "bool");
869 luaA_enum_value_name(L, lua_pref_type, pref_int, "integer");
870 luaA_enum_value_name(L, lua_pref_type, pref_float, "float");
871 luaA_enum_value_name(L, lua_pref_type, pref_file, "file");
872 luaA_enum_value_name(L, lua_pref_type, pref_dir, "directory");
873 luaA_enum_value_name(L, lua_pref_type, pref_enum, "enum");
874 luaA_enum_value_name(L, lua_pref_type, pref_lua, "lua");
875
876 dt_lua_push_darktable_lib(L);
877 dt_lua_goto_subtable(L, "preferences");
878
879 lua_pushcfunction(L, register_pref);
880 lua_setfield(L, -2, "register");
881
882 lua_pushcfunction(L, read_pref);
883 lua_setfield(L, -2, "read");
884
885 lua_pushcfunction(L, write_pref);
886 lua_setfield(L, -2, "write");
887
888 lua_pushcfunction(L, destroy_pref);
889 lua_setfield(L, -2, "destroy");
890
891 lua_pushcfunction(L, get_keys);
892 lua_setfield(L, -2, "get_keys");
893
894 lua_pop(L, 1);
895 return 0;
896 }
897
898 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
899 // vim: shiftwidth=2 expandtab tabstop=2 cindent
900 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
901