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