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/gui.h"
19 #include "common/collection.h"
20 #include "common/darktable.h"
21 #include "common/selection.h"
22 #include "control/control.h"
23 #include "control/settings.h"
24 #include "gui/accelerators.h"
25 #include "lua/call.h"
26 #include "lua/image.h"
27 #include "lua/types.h"
28 #include <glib.h>
29
30 /***********************************************************************
31 Creating the images global variable
32 **********************************************************************/
33
_selection_cb(lua_State * L)34 static int _selection_cb(lua_State *L)
35 {
36 GList *image = dt_collection_get_selected(darktable.collection, -1);
37 if(lua_gettop(L) > 0)
38 {
39 GList *new_selection = NULL;
40 luaL_checktype(L, -1, LUA_TTABLE);
41 lua_pushnil(L);
42 while(lua_next(L, -2) != 0)
43 {
44 /* uses 'key' (at index -2) and 'value' (at index -1) */
45 int imgid;
46 luaA_to(L, dt_lua_image_t, &imgid, -1);
47 new_selection = g_list_prepend(new_selection, GINT_TO_POINTER(imgid));
48 lua_pop(L, 1);
49 }
50 new_selection = g_list_reverse(new_selection);
51 dt_selection_clear(darktable.selection);
52 dt_selection_select_list(darktable.selection, new_selection);
53 g_list_free(new_selection);
54 }
55 lua_newtable(L);
56 int table_index = 1;
57 while(image)
58 {
59 luaA_push(L, dt_lua_image_t, &image->data);
60 lua_seti(L, -2, table_index);
61 table_index++;
62 image = g_list_delete_link(image, image);
63 }
64 return 1;
65 }
66
_hovered_cb(lua_State * L)67 static int _hovered_cb(lua_State *L)
68 {
69 int32_t mouse_over_id = dt_control_get_mouse_over_id();
70 if(mouse_over_id == -1)
71 {
72 lua_pushnil(L);
73 }
74 else
75 {
76 luaA_push(L, dt_lua_image_t, &mouse_over_id);
77 }
78 return 1;
79 }
80
_act_on_cb(lua_State * L)81 static int _act_on_cb(lua_State *L)
82 {
83 lua_newtable(L);
84 int table_index = 1;
85 for(const GList *image = dt_view_get_images_to_act_on(FALSE, TRUE, TRUE); image; image = g_list_next(image))
86 {
87 luaA_push(L, dt_lua_image_t, &image->data);
88 lua_seti(L, -2, table_index);
89 table_index++;
90 }
91 return 1;
92 }
93
_current_view_cb(lua_State * L)94 static int _current_view_cb(lua_State *L)
95 {
96 if(lua_gettop(L) > 0)
97 {
98 dt_view_t *view;
99 luaA_to(L, dt_lua_view_t, &view, 1);
100 dt_ctl_switch_mode_to_by_view(view);
101 }
102 const dt_view_t *current_view = dt_view_manager_get_current_view(darktable.view_manager);
103 dt_lua_module_entry_push(L, "view", current_view->module_name);
104 return 1;
105 }
106
_action_cb(lua_State * L)107 static int _action_cb(lua_State *L)
108 {
109 const gchar *action = luaL_checkstring(L, 1);
110 int instance = luaL_checkinteger(L, 2);
111 const gchar *element = lua_type(L, 3) == LUA_TSTRING ? luaL_checkstring(L, 3) : NULL;
112 const gchar *effect = lua_type(L, 4) == LUA_TSTRING ? luaL_checkstring(L, 4) : NULL;
113
114 float move_size = NAN;
115
116 if(lua_type(L, 5) == LUA_TNUMBER ||
117 (lua_type(L, 5) == LUA_TSTRING && strlen(luaL_checkstring(L, 5)) > 0))
118 {
119 move_size = luaL_checknumber(L, 5);
120 }
121
122 float ret_val = dt_action_process(action, instance, element, effect, move_size);
123
124 lua_pushnumber(L, ret_val);
125
126 return 1;
127 }
128
_panel_visible_cb(lua_State * L)129 static int _panel_visible_cb(lua_State *L)
130 {
131 dt_ui_panel_t p;
132
133 if(lua_gettop(L) > 0)
134 {
135 gboolean result;
136 luaA_to(L, dt_ui_panel_t, &p, 1);
137 result = dt_ui_panel_visible(darktable.gui->ui, p);
138 lua_pushboolean(L, result);
139 return 1;
140 }
141 else
142 {
143 return luaL_error(L, "no panel specified");
144 }
145 }
146
_panel_hide_cb(lua_State * L)147 static int _panel_hide_cb(lua_State *L)
148 {
149 dt_ui_panel_t p;
150 if(lua_gettop(L) > 0)
151 {
152 luaA_to(L, dt_ui_panel_t, &p, 1);
153 dt_ui_panel_show(darktable.gui->ui, p, FALSE, TRUE);
154 return 0;
155 }
156 else
157 {
158 return luaL_error(L, "no panel specified");
159 }
160 }
161
_panel_show_cb(lua_State * L)162 static int _panel_show_cb(lua_State *L)
163 {
164 dt_ui_panel_t p;
165 if(lua_gettop(L) > 0)
166 {
167 luaA_to(L, dt_ui_panel_t, &p, 1);
168 dt_ui_panel_show(darktable.gui->ui, p, TRUE, TRUE);
169 return 0;
170 }
171 else
172 {
173 return luaL_error(L, "no panel specified");
174 }
175 }
176
_panel_hide_all_cb(lua_State * L)177 static int _panel_hide_all_cb(lua_State *L)
178 {
179 for(int k = 0; k < DT_UI_PANEL_SIZE; k++) dt_ui_panel_show(darktable.gui->ui, k, FALSE, TRUE);
180 // code goes here
181 return 0;
182 }
183
_panel_show_all_cb(lua_State * L)184 static int _panel_show_all_cb(lua_State *L)
185 {
186 for(int k = 0; k < DT_UI_PANEL_SIZE; k++) dt_ui_panel_show(darktable.gui->ui, k, TRUE, TRUE);
187 return 0;
188 }
189
_panel_get_size_cb(lua_State * L)190 static int _panel_get_size_cb(lua_State *L)
191 {
192 dt_ui_panel_t p;
193 int size;
194
195 if(lua_gettop(L) > 0)
196 {
197 luaA_to(L, dt_ui_panel_t, &p, 1);
198 if(p == DT_UI_PANEL_LEFT || p == DT_UI_PANEL_RIGHT || p == DT_UI_PANEL_BOTTOM)
199 {
200 size = dt_ui_panel_get_size(darktable.gui->ui, p);
201 lua_pushnumber(L, size);
202 return 1;
203 }
204 else
205 {
206 return luaL_error(L, "size not supported for specified panel");
207 }
208 }
209 else
210 {
211 return luaL_error(L, "no panel specified");
212 }
213 }
214
_panel_set_size_cb(lua_State * L)215 static int _panel_set_size_cb(lua_State *L)
216 {
217 dt_ui_panel_t p;
218 int size;
219
220 if(lua_gettop(L) > 1)
221 {
222 luaA_to(L, dt_ui_panel_t, &p, 1);
223 luaA_to(L, int, &size, 2);
224 if(p == DT_UI_PANEL_LEFT || p == DT_UI_PANEL_RIGHT || p == DT_UI_PANEL_BOTTOM)
225 {
226 dt_ui_panel_set_size(darktable.gui->ui, p, size);
227 return 0;
228 }
229 else
230 {
231 return luaL_error(L, "changing size not supported for specified panel");
232 }
233 }
234 else
235 {
236 return luaL_error(L, "no panel specified");
237 }
238 }
239
240 typedef dt_progress_t *dt_lua_backgroundjob_t;
241
_job_canceled(lua_State * L)242 static int _job_canceled(lua_State *L)
243 {
244 lua_getiuservalue(L, 1, 1);
245 lua_getfield(L, -1, "cancel_callback");
246 lua_pushvalue(L, -3);
247 lua_call(L, 1, 0);
248 lua_pop(L, 2);
249 return 0;
250 }
251
_lua_job_cancelled(dt_progress_t * progress,gpointer user_data)252 static void _lua_job_cancelled(dt_progress_t *progress, gpointer user_data)
253 {
254 dt_lua_async_call_alien(_job_canceled,
255 0, NULL, NULL,
256 LUA_ASYNC_TYPENAME, "dt_lua_backgroundjob_t", progress,
257 LUA_ASYNC_DONE);
258 }
259
_lua_create_job(lua_State * L)260 static int _lua_create_job(lua_State *L)
261 {
262 const char *message = luaL_checkstring(L, 1);
263 gboolean has_progress_bar = lua_toboolean(L, 2);
264 int cancellable = FALSE;
265 if(!lua_isnoneornil(L, 3))
266 {
267 luaL_checktype(L, 3, LUA_TFUNCTION);
268 cancellable = TRUE;
269 }
270 dt_progress_t *progress = dt_control_progress_create(darktable.control, has_progress_bar, message);
271 if(cancellable)
272 {
273 dt_control_progress_make_cancellable(darktable.control, progress, _lua_job_cancelled, progress);
274 }
275 luaA_push(L, dt_lua_backgroundjob_t, &progress);
276 if(cancellable)
277 {
278 lua_getiuservalue(L, -1, 1);
279 lua_pushvalue(L, 3);
280 lua_setfield(L, -2, "cancel_callback");
281 lua_pop(L, 1);
282 }
283 return 1;
284 }
285
_lua_job_progress(lua_State * L)286 static int _lua_job_progress(lua_State *L)
287 {
288 dt_progress_t *progress;
289 luaA_to(L, dt_lua_backgroundjob_t, &progress, 1);
290 dt_pthread_mutex_lock(&darktable.control->progress_system.mutex);
291 GList *iter = g_list_find(darktable.control->progress_system.list, progress);
292 dt_pthread_mutex_unlock(&darktable.control->progress_system.mutex);
293 if(!iter) luaL_error(L, "Accessing an invalid job");
294 if(lua_isnone(L, 3))
295 {
296 double result = dt_control_progress_get_progress(progress);
297 if(!dt_control_progress_has_progress_bar(progress))
298 lua_pushnil(L);
299 else
300 lua_pushnumber(L, result);
301 return 1;
302 }
303 else
304 {
305 double value;
306 luaA_to(L, progress_double, &value, 3);
307 dt_control_progress_set_progress(darktable.control, progress, value);
308 return 0;
309 }
310 }
311
_lua_job_valid(lua_State * L)312 static int _lua_job_valid(lua_State *L)
313 {
314 dt_progress_t *progress;
315 luaA_to(L, dt_lua_backgroundjob_t, &progress, 1);
316 if(lua_isnone(L, 3))
317 {
318 dt_pthread_mutex_lock(&darktable.control->progress_system.mutex);
319 GList *iter = g_list_find(darktable.control->progress_system.list, progress);
320 dt_pthread_mutex_unlock(&darktable.control->progress_system.mutex);
321
322 if(iter)
323 lua_pushboolean(L, true);
324 else
325 lua_pushboolean(L, false);
326
327 return 1;
328 }
329 else
330 {
331 int validity = lua_toboolean(L, 3);
332 if(validity) return luaL_argerror(L, 3, "a job can not be made valid");
333 dt_control_progress_destroy(darktable.control, progress);
334 return 0;
335 }
336 }
337
_on_mouse_over_image_changed(gpointer instance,gpointer user_data)338 static void _on_mouse_over_image_changed(gpointer instance, gpointer user_data)
339 {
340 int imgid = dt_control_get_mouse_over_id();
341 if(imgid != -1)
342 {
343 dt_lua_async_call_alien(dt_lua_event_trigger_wrapper,
344 0, NULL, NULL,
345 LUA_ASYNC_TYPENAME, "char*", "mouse-over-image-changed",
346 LUA_ASYNC_TYPENAME, "dt_lua_image_t", imgid,
347 LUA_ASYNC_DONE);
348 }
349 else
350 {
351 dt_lua_async_call_alien(dt_lua_event_trigger_wrapper,
352 0, NULL, NULL,
353 LUA_ASYNC_TYPENAME, "char*", "mouse-over-image-changed",
354 LUA_ASYNC_DONE);
355 }
356 }
357
358
dt_lua_init_gui(lua_State * L)359 int dt_lua_init_gui(lua_State *L)
360 {
361
362 if(darktable.gui != NULL)
363 {
364 /* images */
365 dt_lua_push_darktable_lib(L);
366 luaA_Type type_id = dt_lua_init_singleton(L, "gui_lib", NULL);
367 lua_setfield(L, -2, "gui");
368 lua_pop(L, 1);
369
370 lua_pushcfunction(L, _selection_cb);
371 dt_lua_gtk_wrap(L);
372 lua_pushcclosure(L, dt_lua_type_member_common, 1);
373 dt_lua_type_register_const_type(L, type_id, "selection");
374 lua_pushcfunction(L, _hovered_cb);
375 dt_lua_type_register_const_type(L, type_id, "hovered");
376 lua_pushcfunction(L, _act_on_cb);
377 dt_lua_type_register_const_type(L, type_id, "action_images");
378 lua_pushcfunction(L, _current_view_cb);
379 lua_pushcclosure(L, dt_lua_type_member_common, 1);
380 dt_lua_type_register_const_type(L, type_id, "current_view");
381 lua_pushcfunction(L, _action_cb);
382 lua_pushcclosure(L, dt_lua_type_member_common, 1);
383 dt_lua_type_register_const_type(L, type_id, "action");
384 lua_pushcfunction(L, _panel_visible_cb);
385 lua_pushcclosure(L, dt_lua_type_member_common, 1);
386 dt_lua_type_register_const_type(L, type_id, "panel_visible");
387 lua_pushcfunction(L, _panel_hide_cb);
388 lua_pushcclosure(L, dt_lua_type_member_common, 1);
389 dt_lua_type_register_const_type(L, type_id, "panel_hide");
390 lua_pushcfunction(L, _panel_show_cb);
391 lua_pushcclosure(L, dt_lua_type_member_common, 1);
392 dt_lua_type_register_const_type(L, type_id, "panel_show");
393 lua_pushcfunction(L, _panel_hide_all_cb);
394 lua_pushcclosure(L, dt_lua_type_member_common, 1);
395 dt_lua_type_register_const_type(L, type_id, "panel_hide_all");
396 lua_pushcfunction(L, _panel_show_all_cb);
397 lua_pushcclosure(L, dt_lua_type_member_common, 1);
398 dt_lua_type_register_const_type(L, type_id, "panel_show_all");
399 lua_pushcfunction(L, _panel_get_size_cb);
400 lua_pushcclosure(L, dt_lua_type_member_common, 1);
401 dt_lua_type_register_const_type(L, type_id, "panel_get_size");
402 lua_pushcfunction(L, _panel_set_size_cb);
403 lua_pushcclosure(L, dt_lua_type_member_common, 1);
404 dt_lua_type_register_const_type(L, type_id, "panel_set_size");
405 lua_pushcfunction(L, _lua_create_job);
406 lua_pushcclosure(L, dt_lua_type_member_common, 1);
407 dt_lua_type_register_const_type(L, type_id, "create_job");
408 dt_lua_module_push(L, "lib");
409 lua_pushcclosure(L, dt_lua_type_member_common, 1);
410 dt_lua_type_register_const_type(L, type_id, "libs");
411 dt_lua_module_push(L, "view");
412 lua_pushcclosure(L, dt_lua_type_member_common, 1);
413 dt_lua_type_register_const_type(L, type_id, "views");
414
415 luaA_enum(L, dt_ui_panel_t);
416 luaA_enum_value(L, dt_ui_panel_t, DT_UI_PANEL_TOP);
417 luaA_enum_value(L, dt_ui_panel_t, DT_UI_PANEL_CENTER_TOP);
418 luaA_enum_value(L, dt_ui_panel_t, DT_UI_PANEL_CENTER_BOTTOM);
419 luaA_enum_value(L, dt_ui_panel_t, DT_UI_PANEL_LEFT);
420 luaA_enum_value(L, dt_ui_panel_t, DT_UI_PANEL_RIGHT);
421 luaA_enum_value(L, dt_ui_panel_t, DT_UI_PANEL_BOTTOM);
422 luaA_enum_value(L, dt_ui_panel_t, DT_UI_PANEL_SIZE);
423
424
425
426 // create a type describing a job object
427 int job_type = dt_lua_init_gpointer_type(L, dt_lua_backgroundjob_t);
428 lua_pushcfunction(L, _lua_job_progress);
429 dt_lua_type_register_type(L, job_type, "percent");
430 lua_pushcfunction(L, _lua_job_valid);
431 dt_lua_type_register_type(L, job_type, "valid");
432
433 // allow to react to highlighting an image
434 lua_pushcfunction(L, dt_lua_event_multiinstance_register);
435 lua_pushcfunction(L, dt_lua_event_multiinstance_destroy);
436 lua_pushcfunction(L, dt_lua_event_multiinstance_trigger);
437 dt_lua_event_add(L, "mouse-over-image-changed");
438 DT_DEBUG_CONTROL_SIGNAL_CONNECT(darktable.signals, DT_SIGNAL_MOUSE_OVER_IMAGE_CHANGE, G_CALLBACK(_on_mouse_over_image_changed), NULL);
439 }
440 return 0;
441 }
442
443 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
444 // vim: shiftwidth=2 expandtab tabstop=2 cindent
445 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
446