1 /*
2     This file is part of darktable,
3     Copyright (C) 2011-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 
19 #include <gdk/gdkkeysyms.h>
20 
21 #include "common/collection.h"
22 #include "common/debug.h"
23 #include "common/selection.h"
24 #include "control/conf.h"
25 #include "control/control.h"
26 #include "dtgtk/thumbtable.h"
27 #include "gui/accelerators.h"
28 #include "gui/gtk.h"
29 #include "libs/lib.h"
30 #include "libs/lib_api.h"
31 
32 DT_MODULE(1)
33 
34 typedef struct dt_lib_tool_lighttable_t
35 {
36   GtkWidget *zoom;
37   GtkWidget *zoom_entry;
38   GtkWidget *layout_box;
39   GtkWidget *layout_filemanager;
40   GtkWidget *layout_zoomable;
41   GtkWidget *layout_culling_dynamic;
42   GtkWidget *layout_culling_fix;
43   GtkWidget *layout_preview;
44   dt_lighttable_layout_t layout, base_layout;
45   int current_zoom;
46   gboolean fullpreview;
47   gboolean fullpreview_focus;
48   gboolean combo_evt_reset;
49 } dt_lib_tool_lighttable_t;
50 
51 /* set zoom proxy function */
52 static void _lib_lighttable_set_zoom(dt_lib_module_t *self, gint zoom);
53 static gint _lib_lighttable_get_zoom(dt_lib_module_t *self);
54 
55 /* get/set layout proxy function */
56 static dt_lighttable_layout_t _lib_lighttable_get_layout(dt_lib_module_t *self);
57 
58 /* zoom slider change callback */
59 static void _lib_lighttable_zoom_slider_changed(GtkRange *range, gpointer user_data);
60 /* zoom entry change callback */
61 static gboolean _lib_lighttable_zoom_entry_changed(GtkWidget *entry, GdkEventKey *event,
62                                                    dt_lib_module_t *self);
63 
64 static void _set_zoom(dt_lib_module_t *self, int zoom);
65 
name(dt_lib_module_t * self)66 const char *name(dt_lib_module_t *self)
67 {
68   return _("lighttable");
69 }
70 
views(dt_lib_module_t * self)71 const char **views(dt_lib_module_t *self)
72 {
73   static const char *v[] = {"lighttable", NULL};
74   return v;
75 }
76 
container(dt_lib_module_t * self)77 uint32_t container(dt_lib_module_t *self)
78 {
79   return DT_UI_CONTAINER_PANEL_CENTER_BOTTOM_CENTER;
80 }
81 
expandable(dt_lib_module_t * self)82 int expandable(dt_lib_module_t *self)
83 {
84   return 0;
85 }
86 
position()87 int position()
88 {
89   return 1001;
90 }
91 
_lib_lighttable_update_btn(dt_lib_module_t * self)92 static void _lib_lighttable_update_btn(dt_lib_module_t *self)
93 {
94   dt_lib_tool_lighttable_t *d = (dt_lib_tool_lighttable_t *)self->data;
95 
96   // which btn should be active ?
97   GtkWidget *active = d->layout_filemanager;
98   if(d->fullpreview)
99     active = d->layout_preview;
100   else if(d->layout == DT_LIGHTTABLE_LAYOUT_CULLING_DYNAMIC)
101     active = d->layout_culling_dynamic;
102   else if(d->layout == DT_LIGHTTABLE_LAYOUT_CULLING)
103     active = d->layout_culling_fix;
104   else if(d->layout == DT_LIGHTTABLE_LAYOUT_ZOOMABLE)
105     active = d->layout_zoomable;
106 
107   GList *children = gtk_container_get_children(GTK_CONTAINER(d->layout_box));
108   for(const GList *l = children; l; l = g_list_next(l))
109   {
110     GtkWidget *w = (GtkWidget *)l->data;
111     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), (w == active));
112   }
113   g_list_free(children);
114 
115   // and now we set the tooltips
116   if(d->fullpreview)
117     gtk_widget_set_tooltip_text(d->layout_preview, _("click to exit from full preview layout."));
118   else
119     gtk_widget_set_tooltip_text(d->layout_preview, _("click to enter full preview layout."));
120 
121   if(d->layout != DT_LIGHTTABLE_LAYOUT_CULLING || d->fullpreview)
122     gtk_widget_set_tooltip_text(d->layout_culling_fix, _("click to enter culling layout in fixed mode."));
123   else
124     gtk_widget_set_tooltip_text(d->layout_culling_fix, _("click to exit culling layout."));
125 
126   if(d->layout != DT_LIGHTTABLE_LAYOUT_CULLING_DYNAMIC || d->fullpreview)
127     gtk_widget_set_tooltip_text(d->layout_culling_dynamic, _("click to enter culling layout in dynamic mode."));
128   else
129     gtk_widget_set_tooltip_text(d->layout_culling_dynamic, _("click to exit culling layout."));
130 }
131 
_lib_lighttable_set_layout(dt_lib_module_t * self,dt_lighttable_layout_t layout)132 static void _lib_lighttable_set_layout(dt_lib_module_t *self, dt_lighttable_layout_t layout)
133 {
134   dt_lib_tool_lighttable_t *d = (dt_lib_tool_lighttable_t *)self->data;
135 
136   // we deal with fullpreview first.
137   if(!d->fullpreview && layout == DT_LIGHTTABLE_LAYOUT_PREVIEW)
138   {
139     // special case for preview : we don't change previous values, just show full preview
140     d->fullpreview = TRUE;
141     _lib_lighttable_update_btn(self);
142     dt_view_lighttable_set_preview_state(darktable.view_manager, TRUE, d->fullpreview_focus);
143     return;
144   }
145   else if(d->fullpreview && layout != DT_LIGHTTABLE_LAYOUT_PREVIEW)
146   {
147     d->fullpreview = FALSE;
148     dt_view_lighttable_set_preview_state(darktable.view_manager, FALSE, FALSE);
149     // and we continue to select the right layout...
150   }
151 
152   const int current_layout = dt_conf_get_int("plugins/lighttable/layout");
153   d->layout = layout;
154 
155   if(current_layout != layout)
156   {
157     if(d->layout == DT_LIGHTTABLE_LAYOUT_CULLING_DYNAMIC)
158     {
159       d->current_zoom = MAX(1, MIN(30, dt_collection_get_selected_count(darktable.collection)));
160       if(d->current_zoom == 1) d->current_zoom = dt_conf_get_int("plugins/lighttable/culling_num_images");
161     }
162     else if(d->layout == DT_LIGHTTABLE_LAYOUT_CULLING)
163     {
164       d->current_zoom = dt_conf_get_int("plugins/lighttable/culling_num_images");
165     }
166     else
167     {
168       d->current_zoom = dt_conf_get_int("plugins/lighttable/images_in_row");
169     }
170 
171     gtk_widget_set_sensitive(d->zoom_entry, (d->layout != DT_LIGHTTABLE_LAYOUT_CULLING_DYNAMIC && !d->fullpreview));
172     gtk_widget_set_sensitive(d->zoom, (d->layout != DT_LIGHTTABLE_LAYOUT_CULLING_DYNAMIC && !d->fullpreview));
173     gtk_range_set_value(GTK_RANGE(d->zoom), d->current_zoom);
174 
175     dt_conf_set_int("plugins/lighttable/layout", layout);
176     if(layout == DT_LIGHTTABLE_LAYOUT_FILEMANAGER || layout == DT_LIGHTTABLE_LAYOUT_ZOOMABLE)
177     {
178       d->base_layout = layout;
179       dt_conf_set_int("plugins/lighttable/base_layout", layout);
180     }
181 
182     dt_control_queue_redraw_center();
183   }
184   else
185   {
186     dt_control_queue_redraw_center();
187   }
188 
189   _lib_lighttable_update_btn(self);
190 }
191 
_lib_lighttable_layout_btn_release(GtkWidget * w,GdkEventButton * event,dt_lib_module_t * self)192 static gboolean _lib_lighttable_layout_btn_release(GtkWidget *w, GdkEventButton *event, dt_lib_module_t *self)
193 {
194   dt_lib_tool_lighttable_t *d = (dt_lib_tool_lighttable_t *)self->data;
195   if(d->combo_evt_reset) return FALSE;
196 
197   const gboolean active
198       = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)); // note : this is the state before the change
199   dt_lighttable_layout_t new_layout = DT_LIGHTTABLE_LAYOUT_FILEMANAGER;
200   if(!active)
201   {
202     // that means we want to activate the button
203     if(w == d->layout_preview)
204     {
205       d->fullpreview_focus = dt_modifier_is(event->state, GDK_CONTROL_MASK);
206       new_layout = DT_LIGHTTABLE_LAYOUT_PREVIEW;
207     }
208     else if(w == d->layout_culling_fix)
209       new_layout = DT_LIGHTTABLE_LAYOUT_CULLING;
210     else if(w == d->layout_culling_dynamic)
211       new_layout = DT_LIGHTTABLE_LAYOUT_CULLING_DYNAMIC;
212     else if(w == d->layout_zoomable)
213       new_layout = DT_LIGHTTABLE_LAYOUT_ZOOMABLE;
214   }
215   else
216   {
217     // that means we want to deactivate the button
218     if(w == d->layout_preview)
219       new_layout = d->layout;
220     else if(w == d->layout_culling_dynamic || w == d->layout_culling_fix)
221       new_layout = d->base_layout;
222     else
223     {
224       // we can't exit from filemanager or zoomable
225       return TRUE;
226     }
227   }
228 
229   _lib_lighttable_set_layout(self, new_layout);
230   return TRUE;
231 }
232 
gui_init(dt_lib_module_t * self)233 void gui_init(dt_lib_module_t *self)
234 {
235   /* initialize ui widgets */
236   dt_lib_tool_lighttable_t *d = (dt_lib_tool_lighttable_t *)g_malloc0(sizeof(dt_lib_tool_lighttable_t));
237   self->data = (void *)d;
238 
239   self->widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
240   d->layout = MIN(DT_LIGHTTABLE_LAYOUT_LAST - 1, dt_conf_get_int("plugins/lighttable/layout"));
241   d->base_layout = MIN(DT_LIGHTTABLE_LAYOUT_LAST - 1, dt_conf_get_int("plugins/lighttable/base_layout"));
242 
243   if(d->layout == DT_LIGHTTABLE_LAYOUT_CULLING)
244     d->current_zoom = dt_conf_get_int("plugins/lighttable/culling_num_images");
245   else if(d->layout == DT_LIGHTTABLE_LAYOUT_CULLING_DYNAMIC)
246   {
247     d->current_zoom = MAX(1, MIN(DT_LIGHTTABLE_MAX_ZOOM, dt_collection_get_selected_count(darktable.collection)));
248     if(d->current_zoom == 1) d->current_zoom = dt_conf_get_int("plugins/lighttable/culling_num_images");
249   }
250   else
251     d->current_zoom = dt_conf_get_int("plugins/lighttable/images_in_row");
252 
253   // create the layouts icon list
254   d->layout_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
255   gtk_widget_set_name(d->layout_box, "lighttable_layouts_box");
256   gtk_box_pack_start(GTK_BOX(self->widget), d->layout_box, TRUE, TRUE, 0);
257 
258   d->layout_filemanager = dtgtk_togglebutton_new(dtgtk_cairo_paint_lt_mode_grid, CPF_STYLE_FLAT, NULL);
259   dt_action_define(&darktable.view_manager->proxy.lighttable.view->actions, NULL,
260                    "toggle filemanager layout", d->layout_filemanager, NULL);
261   dt_gui_add_help_link(d->layout_filemanager, dt_get_help_url("layout_filemanager"));
262   gtk_widget_set_tooltip_text(d->layout_filemanager, _("click to enter filemanager layout."));
263   g_signal_connect(G_OBJECT(d->layout_filemanager), "button-release-event",
264                    G_CALLBACK(_lib_lighttable_layout_btn_release), self);
265   gtk_box_pack_start(GTK_BOX(d->layout_box), d->layout_filemanager, TRUE, TRUE, 0);
266 
267   d->layout_zoomable = dtgtk_togglebutton_new(dtgtk_cairo_paint_lt_mode_zoom, CPF_STYLE_FLAT, NULL);
268   dt_action_define(&darktable.view_manager->proxy.lighttable.view->actions, NULL,
269                    "toggle zoomable lighttable layout", d->layout_zoomable, NULL);
270   dt_gui_add_help_link(d->layout_zoomable, dt_get_help_url("layout_zoomable"));
271   gtk_widget_set_tooltip_text(d->layout_zoomable, _("click to enter zoomable lighttable layout."));
272   g_signal_connect(G_OBJECT(d->layout_zoomable), "button-release-event",
273                    G_CALLBACK(_lib_lighttable_layout_btn_release), self);
274   gtk_box_pack_start(GTK_BOX(d->layout_box), d->layout_zoomable, TRUE, TRUE, 0);
275 
276   d->layout_culling_fix = dtgtk_togglebutton_new(dtgtk_cairo_paint_lt_mode_culling_fixed, CPF_STYLE_FLAT, NULL);
277   dt_action_define(&darktable.view_manager->proxy.lighttable.view->actions, NULL,
278                    "toggle culling mode", d->layout_culling_fix, NULL);
279   dt_gui_add_help_link(d->layout_culling_fix, dt_get_help_url("layout_culling"));
280   g_signal_connect(G_OBJECT(d->layout_culling_fix), "button-release-event",
281                    G_CALLBACK(_lib_lighttable_layout_btn_release), self);
282   gtk_box_pack_start(GTK_BOX(d->layout_box), d->layout_culling_fix, TRUE, TRUE, 0);
283 
284   d->layout_culling_dynamic
285       = dtgtk_togglebutton_new(dtgtk_cairo_paint_lt_mode_culling_dynamic, CPF_STYLE_FLAT, NULL);
286   dt_action_define(&darktable.view_manager->proxy.lighttable.view->actions, NULL,
287                    "toggle culling dynamic mode", d->layout_culling_dynamic, NULL);
288   dt_gui_add_help_link(d->layout_culling_dynamic, dt_get_help_url("layout_culling"));
289   g_signal_connect(G_OBJECT(d->layout_culling_dynamic), "button-release-event",
290                    G_CALLBACK(_lib_lighttable_layout_btn_release), self);
291   gtk_box_pack_start(GTK_BOX(d->layout_box), d->layout_culling_dynamic, TRUE, TRUE, 0);
292 
293   d->layout_preview = dtgtk_togglebutton_new(dtgtk_cairo_paint_lt_mode_fullpreview, CPF_STYLE_FLAT, NULL);
294   dt_action_define(&darktable.view_manager->proxy.lighttable.view->actions, NULL,
295                    "toggle sticky preview mode", d->layout_preview, NULL);
296   dt_gui_add_help_link(d->layout_preview, dt_get_help_url("layout_preview"));
297   g_signal_connect(G_OBJECT(d->layout_preview), "button-release-event",
298                    G_CALLBACK(_lib_lighttable_layout_btn_release), self);
299   gtk_box_pack_start(GTK_BOX(d->layout_box), d->layout_preview, TRUE, TRUE, 0);
300 
301   _lib_lighttable_update_btn(self);
302 
303   /* create horizontal zoom slider */
304   d->zoom = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 1, DT_LIGHTTABLE_MAX_ZOOM, 1);
305   gtk_widget_set_size_request(GTK_WIDGET(d->zoom), DT_PIXEL_APPLY_DPI(140), -1);
306   gtk_scale_set_draw_value(GTK_SCALE(d->zoom), FALSE);
307   gtk_range_set_increments(GTK_RANGE(d->zoom), 1, 1);
308   gtk_box_pack_start(GTK_BOX(self->widget), d->zoom, TRUE, TRUE, 0);
309 
310   /* manual entry of the zoom level */
311   d->zoom_entry = gtk_entry_new();
312   gtk_entry_set_alignment(GTK_ENTRY(d->zoom_entry), 1.0);
313   gtk_entry_set_max_length(GTK_ENTRY(d->zoom_entry), 2);
314   gtk_entry_set_width_chars(GTK_ENTRY(d->zoom_entry), 3);
315   gtk_entry_set_max_width_chars(GTK_ENTRY(d->zoom_entry), 3);
316   gtk_box_pack_start(GTK_BOX(self->widget), d->zoom_entry, TRUE, TRUE, 0);
317 
318   g_signal_connect(G_OBJECT(d->zoom), "value-changed", G_CALLBACK(_lib_lighttable_zoom_slider_changed),
319                    (gpointer)self);
320   g_signal_connect(d->zoom_entry, "key-press-event", G_CALLBACK(_lib_lighttable_zoom_entry_changed), self);
321   gtk_range_set_value(GTK_RANGE(d->zoom), d->current_zoom);
322 
323   _lib_lighttable_zoom_slider_changed(GTK_RANGE(d->zoom), self); // the slider defaults to 1 and GTK doesn't
324                                                                  // fire a value-changed signal when setting
325                                                                  // it to 1 => empty text box
326 
327   gtk_widget_set_sensitive(d->zoom_entry, (d->layout != DT_LIGHTTABLE_LAYOUT_CULLING_DYNAMIC && !d->fullpreview));
328   gtk_widget_set_sensitive(d->zoom, (d->layout != DT_LIGHTTABLE_LAYOUT_CULLING_DYNAMIC && !d->fullpreview));
329 
330   darktable.view_manager->proxy.lighttable.module = self;
331   darktable.view_manager->proxy.lighttable.set_zoom = _lib_lighttable_set_zoom;
332   darktable.view_manager->proxy.lighttable.get_zoom = _lib_lighttable_get_zoom;
333   darktable.view_manager->proxy.lighttable.get_layout = _lib_lighttable_get_layout;
334   darktable.view_manager->proxy.lighttable.set_layout = _lib_lighttable_set_layout;
335 }
336 
gui_cleanup(dt_lib_module_t * self)337 void gui_cleanup(dt_lib_module_t *self)
338 {
339   g_free(self->data);
340   self->data = NULL;
341 }
342 
_set_zoom(dt_lib_module_t * self,int zoom)343 static void _set_zoom(dt_lib_module_t *self, int zoom)
344 {
345   dt_lib_tool_lighttable_t *d = (dt_lib_tool_lighttable_t *)self->data;
346   if(d->layout == DT_LIGHTTABLE_LAYOUT_CULLING)
347   {
348     dt_conf_set_int("plugins/lighttable/culling_num_images", zoom);
349     dt_control_queue_redraw_center();
350   }
351   else if(d->layout == DT_LIGHTTABLE_LAYOUT_FILEMANAGER || d->layout == DT_LIGHTTABLE_LAYOUT_ZOOMABLE)
352   {
353     dt_conf_set_int("plugins/lighttable/images_in_row", zoom);
354     dt_thumbtable_zoom_changed(dt_ui_thumbtable(darktable.gui->ui), d->current_zoom, zoom);
355   }
356 }
357 
_lib_lighttable_zoom_slider_changed(GtkRange * range,gpointer user_data)358 static void _lib_lighttable_zoom_slider_changed(GtkRange *range, gpointer user_data)
359 {
360   dt_lib_module_t *self = (dt_lib_module_t *)user_data;
361   dt_lib_tool_lighttable_t *d = (dt_lib_tool_lighttable_t *)self->data;
362 
363   const int i = gtk_range_get_value(range);
364   gchar *i_as_str = g_strdup_printf("%d", i);
365   gtk_entry_set_text(GTK_ENTRY(d->zoom_entry), i_as_str);
366   _set_zoom(self, i);
367   d->current_zoom = i;
368   g_free(i_as_str);
369 }
370 
_lib_lighttable_zoom_entry_changed(GtkWidget * entry,GdkEventKey * event,dt_lib_module_t * self)371 static gboolean _lib_lighttable_zoom_entry_changed(GtkWidget *entry, GdkEventKey *event, dt_lib_module_t *self)
372 {
373   dt_lib_tool_lighttable_t *d = (dt_lib_tool_lighttable_t *)self->data;
374   switch(event->keyval)
375   {
376     case GDK_KEY_Escape:
377     case GDK_KEY_Tab:
378     {
379       // reset
380       int i = 0;
381       if(d->layout == DT_LIGHTTABLE_LAYOUT_CULLING || d->layout == DT_LIGHTTABLE_LAYOUT_CULLING_DYNAMIC)
382         i = dt_conf_get_int("plugins/lighttable/culling_num_images");
383       else
384         i = dt_conf_get_int("plugins/lighttable/images_in_row");
385       gchar *i_as_str = g_strdup_printf("%d", i);
386       gtk_entry_set_text(GTK_ENTRY(d->zoom_entry), i_as_str);
387       g_free(i_as_str);
388       gtk_window_set_focus(GTK_WINDOW(dt_ui_main_window(darktable.gui->ui)), NULL);
389       return FALSE;
390     }
391 
392     case GDK_KEY_Return:
393     case GDK_KEY_KP_Enter:
394     {
395       // apply zoom level
396       const gchar *value = gtk_entry_get_text(GTK_ENTRY(d->zoom_entry));
397       int i = atoi(value);
398       gtk_range_set_value(GTK_RANGE(d->zoom), i);
399       gtk_window_set_focus(GTK_WINDOW(dt_ui_main_window(darktable.gui->ui)), NULL);
400       return FALSE;
401     }
402 
403     // allow 0 .. 9, left/right movement using arrow keys and del/backspace
404     case GDK_KEY_0:
405     case GDK_KEY_KP_0:
406     case GDK_KEY_1:
407     case GDK_KEY_KP_1:
408     case GDK_KEY_2:
409     case GDK_KEY_KP_2:
410     case GDK_KEY_3:
411     case GDK_KEY_KP_3:
412     case GDK_KEY_4:
413     case GDK_KEY_KP_4:
414     case GDK_KEY_5:
415     case GDK_KEY_KP_5:
416     case GDK_KEY_6:
417     case GDK_KEY_KP_6:
418     case GDK_KEY_7:
419     case GDK_KEY_KP_7:
420     case GDK_KEY_8:
421     case GDK_KEY_KP_8:
422     case GDK_KEY_9:
423     case GDK_KEY_KP_9:
424 
425     case GDK_KEY_Left:
426     case GDK_KEY_Right:
427     case GDK_KEY_Delete:
428     case GDK_KEY_BackSpace:
429       return FALSE;
430 
431     default: // block everything else
432       return TRUE;
433   }
434 }
435 
_lib_lighttable_get_layout(dt_lib_module_t * self)436 static dt_lighttable_layout_t _lib_lighttable_get_layout(dt_lib_module_t *self)
437 {
438   dt_lib_tool_lighttable_t *d = (dt_lib_tool_lighttable_t *)self->data;
439   return d->layout;
440 }
441 
_lib_lighttable_set_zoom(dt_lib_module_t * self,gint zoom)442 static void _lib_lighttable_set_zoom(dt_lib_module_t *self, gint zoom)
443 {
444   dt_lib_tool_lighttable_t *d = (dt_lib_tool_lighttable_t *)self->data;
445   gtk_range_set_value(GTK_RANGE(d->zoom), zoom);
446   d->current_zoom = zoom;
447 }
448 
_lib_lighttable_get_zoom(dt_lib_module_t * self)449 static gint _lib_lighttable_get_zoom(dt_lib_module_t *self)
450 {
451   dt_lib_tool_lighttable_t *d = (dt_lib_tool_lighttable_t *)self->data;
452   return d->current_zoom;
453 }
454 
_lib_lighttable_key_accel_toggle_filemanager(GtkAccelGroup * accel_group,GObject * acceleratable,guint keyval,GdkModifierType modifier,gpointer data)455 static gboolean _lib_lighttable_key_accel_toggle_filemanager(GtkAccelGroup *accel_group, GObject *acceleratable,
456                                                              guint keyval, GdkModifierType modifier, gpointer data)
457 {
458   dt_lib_module_t *self = (dt_lib_module_t *)data;
459   _lib_lighttable_set_layout(self, DT_LIGHTTABLE_LAYOUT_FILEMANAGER);
460   return TRUE;
461 }
462 
_lib_lighttable_key_accel_toggle_zoomable(GtkAccelGroup * accel_group,GObject * acceleratable,guint keyval,GdkModifierType modifier,gpointer data)463 static gboolean _lib_lighttable_key_accel_toggle_zoomable(GtkAccelGroup *accel_group, GObject *acceleratable,
464                                                           guint keyval, GdkModifierType modifier, gpointer data)
465 {
466   dt_lib_module_t *self = (dt_lib_module_t *)data;
467   _lib_lighttable_set_layout(self, DT_LIGHTTABLE_LAYOUT_ZOOMABLE);
468   return TRUE;
469 }
470 
_lib_lighttable_key_accel_toggle_preview(GtkAccelGroup * accel_group,GObject * acceleratable,guint keyval,GdkModifierType modifier,gpointer data)471 static gboolean _lib_lighttable_key_accel_toggle_preview(GtkAccelGroup *accel_group, GObject *acceleratable,
472                                                          guint keyval, GdkModifierType modifier, gpointer data)
473 {
474   dt_lib_module_t *self = (dt_lib_module_t *)data;
475   dt_lib_tool_lighttable_t *d = (dt_lib_tool_lighttable_t *)self->data;
476 
477   if(d->fullpreview)
478     _lib_lighttable_set_layout(self, d->layout);
479   else
480   {
481     d->fullpreview_focus = FALSE;
482     _lib_lighttable_set_layout(self, DT_LIGHTTABLE_LAYOUT_PREVIEW);
483   }
484 
485   return TRUE;
486 }
487 
_lib_lighttable_key_accel_toggle_preview_focus(GtkAccelGroup * accel_group,GObject * acceleratable,guint keyval,GdkModifierType modifier,gpointer data)488 static gboolean _lib_lighttable_key_accel_toggle_preview_focus(GtkAccelGroup *accel_group, GObject *acceleratable,
489                                                                guint keyval, GdkModifierType modifier,
490                                                                gpointer data)
491 {
492   dt_lib_module_t *self = (dt_lib_module_t *)data;
493   dt_lib_tool_lighttable_t *d = (dt_lib_tool_lighttable_t *)self->data;
494 
495   if(d->fullpreview)
496     _lib_lighttable_set_layout(self, d->layout);
497   else
498   {
499     d->fullpreview_focus = TRUE;
500     _lib_lighttable_set_layout(self, DT_LIGHTTABLE_LAYOUT_PREVIEW);
501   }
502 
503   return TRUE;
504 }
505 
_lib_lighttable_key_accel_toggle_culling_dynamic_mode(GtkAccelGroup * accel_group,GObject * acceleratable,guint keyval,GdkModifierType modifier,gpointer data)506 static gboolean _lib_lighttable_key_accel_toggle_culling_dynamic_mode(GtkAccelGroup *accel_group,
507                                                                       GObject *acceleratable, guint keyval,
508                                                                       GdkModifierType modifier, gpointer data)
509 {
510   dt_lib_module_t *self = (dt_lib_module_t *)data;
511   dt_lib_tool_lighttable_t *d = (dt_lib_tool_lighttable_t *)self->data;
512 
513   // if we are already in any culling layout, we return to the base layout
514   if(d->layout != DT_LIGHTTABLE_LAYOUT_CULLING && d->layout != DT_LIGHTTABLE_LAYOUT_CULLING_DYNAMIC)
515     _lib_lighttable_set_layout(self, DT_LIGHTTABLE_LAYOUT_CULLING_DYNAMIC);
516   else
517     _lib_lighttable_set_layout(self, d->base_layout);
518 
519   dt_control_queue_redraw_center();
520   return TRUE;
521 }
522 
_lib_lighttable_key_accel_toggle_culling_mode(GtkAccelGroup * accel_group,GObject * acceleratable,guint keyval,GdkModifierType modifier,gpointer data)523 static gboolean _lib_lighttable_key_accel_toggle_culling_mode(GtkAccelGroup *accel_group, GObject *acceleratable,
524                                                               guint keyval, GdkModifierType modifier,
525                                                               gpointer data)
526 {
527   dt_lib_module_t *self = (dt_lib_module_t *)data;
528   dt_lib_tool_lighttable_t *d = (dt_lib_tool_lighttable_t *)self->data;
529 
530   // if we are already in any culling layout, we return to the base layout
531   if(d->layout != DT_LIGHTTABLE_LAYOUT_CULLING && d->layout != DT_LIGHTTABLE_LAYOUT_CULLING_DYNAMIC)
532     _lib_lighttable_set_layout(self, DT_LIGHTTABLE_LAYOUT_CULLING);
533   else
534     _lib_lighttable_set_layout(self, d->base_layout);
535 
536   dt_control_queue_redraw_center();
537   return TRUE;
538 }
539 
_lib_lighttable_key_accel_toggle_culling_zoom_mode(GtkAccelGroup * accel_group,GObject * acceleratable,guint keyval,GdkModifierType modifier,gpointer data)540 static gboolean _lib_lighttable_key_accel_toggle_culling_zoom_mode(GtkAccelGroup *accel_group,
541                                                                    GObject *acceleratable, guint keyval,
542                                                                    GdkModifierType modifier, gpointer data)
543 {
544   dt_lib_module_t *self = (dt_lib_module_t *)data;
545   dt_lib_tool_lighttable_t *d = (dt_lib_tool_lighttable_t *)self->data;
546 
547   if(d->layout == DT_LIGHTTABLE_LAYOUT_CULLING)
548     _lib_lighttable_set_layout(self, DT_LIGHTTABLE_LAYOUT_CULLING_DYNAMIC);
549   else if(d->layout == DT_LIGHTTABLE_LAYOUT_CULLING_DYNAMIC)
550     _lib_lighttable_set_layout(self, DT_LIGHTTABLE_LAYOUT_CULLING);
551 
552   return TRUE;
553 }
554 
_lib_lighttable_key_accel_exit_layout(GtkAccelGroup * accel_group,GObject * acceleratable,guint keyval,GdkModifierType modifier,gpointer data)555 static gboolean _lib_lighttable_key_accel_exit_layout(GtkAccelGroup *accel_group, GObject *acceleratable,
556                                                       guint keyval, GdkModifierType modifier, gpointer data)
557 {
558   dt_lib_module_t *self = (dt_lib_module_t *)data;
559   dt_lib_tool_lighttable_t *d = (dt_lib_tool_lighttable_t *)self->data;
560 
561   if(d->fullpreview)
562     _lib_lighttable_set_layout(self, d->layout);
563   else if(d->layout != d->base_layout)
564     _lib_lighttable_set_layout(self, d->base_layout);
565 
566   return TRUE;
567 }
568 
init_key_accels(dt_lib_module_t * self)569 void init_key_accels(dt_lib_module_t *self)
570 {
571   // view accels
572   dt_accel_register_lib_as_view("lighttable", NC_("accel", "toggle filemanager layout"), 0, 0);
573   dt_accel_register_lib_as_view("lighttable", NC_("accel", "toggle zoomable lighttable layout"), 0, 0);
574   dt_accel_register_lib_as_view("lighttable", NC_("accel", "toggle culling mode"), GDK_KEY_x, 0);
575   dt_accel_register_lib_as_view("lighttable", NC_("accel", "toggle culling dynamic mode"), GDK_KEY_x,
576                                 GDK_CONTROL_MASK);
577   dt_accel_register_lib_as_view("lighttable", NC_("accel", "toggle culling zoom mode"), GDK_KEY_less, 0);
578   dt_accel_register_lib_as_view("lighttable", NC_("accel", "toggle sticky preview mode"), GDK_KEY_f, 0);
579   dt_accel_register_lib_as_view("lighttable", NC_("accel", "toggle sticky preview mode with focus detection"), 0,
580                                 0);
581   dt_accel_register_lib_as_view("lighttable", NC_("accel", "exit current layout"), GDK_KEY_Escape, 0);
582 }
583 
connect_key_accels(dt_lib_module_t * self)584 void connect_key_accels(dt_lib_module_t *self)
585 {
586   /* setup key accelerators */
587 
588   // view accels
589   dt_accel_connect_lib_as_view(
590       self, "lighttable", "toggle filemanager layout",
591       g_cclosure_new(G_CALLBACK(_lib_lighttable_key_accel_toggle_filemanager), self, NULL));
592   dt_accel_connect_lib_as_view(self, "lighttable", "toggle zoomable lighttable layout",
593                                g_cclosure_new(G_CALLBACK(_lib_lighttable_key_accel_toggle_zoomable), self, NULL));
594   dt_accel_connect_lib_as_view(
595       self, "lighttable", "toggle culling dynamic mode",
596       g_cclosure_new(G_CALLBACK(_lib_lighttable_key_accel_toggle_culling_dynamic_mode), self, NULL));
597   dt_accel_connect_lib_as_view(
598       self, "lighttable", "toggle culling mode",
599       g_cclosure_new(G_CALLBACK(_lib_lighttable_key_accel_toggle_culling_mode), self, NULL));
600   dt_accel_connect_lib_as_view(
601       self, "lighttable", "toggle culling zoom mode",
602       g_cclosure_new(G_CALLBACK(_lib_lighttable_key_accel_toggle_culling_zoom_mode), self, NULL));
603   dt_accel_connect_lib_as_view(self, "lighttable", "toggle sticky preview mode",
604                                g_cclosure_new(G_CALLBACK(_lib_lighttable_key_accel_toggle_preview), self, NULL));
605   dt_accel_connect_lib_as_view(
606       self, "lighttable", "toggle sticky preview mode with focus detection",
607       g_cclosure_new(G_CALLBACK(_lib_lighttable_key_accel_toggle_preview_focus), self, NULL));
608 
609   dt_accel_connect_lib_as_view(self, "lighttable", "exit current layout",
610                                g_cclosure_new(G_CALLBACK(_lib_lighttable_key_accel_exit_layout), self, NULL));
611 }
612 
613 #ifdef USE_LUA
layout_cb(lua_State * L)614 static int layout_cb(lua_State *L)
615 {
616   dt_lib_module_t *self = lua_touserdata(L, lua_upvalueindex(1));
617   const dt_lighttable_layout_t tmp = _lib_lighttable_get_layout(self);
618   if(lua_gettop(L) > 0){
619     dt_lighttable_layout_t value;
620     luaA_to(L, dt_lighttable_layout_t, &value, 1);
621     _lib_lighttable_set_layout(self, value);
622   }
623   luaA_push(L, dt_lighttable_layout_t, &tmp);
624   return 1;
625 }
zoom_level_cb(lua_State * L)626 static int zoom_level_cb(lua_State *L)
627 {
628   dt_lib_module_t *self = lua_touserdata(L, lua_upvalueindex(1));
629   const gint tmp = _lib_lighttable_get_zoom(self);
630   if(lua_gettop(L) > 0){
631     int value;
632     luaA_to(L, int, &value, 1);
633     _lib_lighttable_set_zoom(self, value);
634   }
635   luaA_push(L, int, &tmp);
636   return 1;
637 }
638 
init(struct dt_lib_module_t * self)639 void init(struct dt_lib_module_t *self)
640 {
641   lua_State *L = darktable.lua_state.state;
642   int my_type = dt_lua_module_entry_get_type(L, "lib", self->plugin_name);
643   lua_pushlightuserdata(L, self);
644   lua_pushcclosure(L, layout_cb, 1);
645   dt_lua_gtk_wrap(L);
646   lua_pushcclosure(L, dt_lua_type_member_common, 1);
647   dt_lua_type_register_const_type(L, my_type, "layout");
648   lua_pushlightuserdata(L, self);
649   lua_pushcclosure(L, zoom_level_cb, 1);
650   dt_lua_gtk_wrap(L);
651   lua_pushcclosure(L, dt_lua_type_member_common, 1);
652   dt_lua_type_register_const_type(L, my_type, "zoom_level");
653 
654   luaA_enum(L,dt_lighttable_layout_t);
655   luaA_enum_value(L, dt_lighttable_layout_t, DT_LIGHTTABLE_LAYOUT_FIRST);
656   luaA_enum_value(L, dt_lighttable_layout_t, DT_LIGHTTABLE_LAYOUT_ZOOMABLE);
657   luaA_enum_value(L, dt_lighttable_layout_t, DT_LIGHTTABLE_LAYOUT_FILEMANAGER);
658   luaA_enum_value(L, dt_lighttable_layout_t, DT_LIGHTTABLE_LAYOUT_CULLING);
659   luaA_enum_value(L, dt_lighttable_layout_t, DT_LIGHTTABLE_LAYOUT_CULLING_DYNAMIC);
660   luaA_enum_value(L, dt_lighttable_layout_t, DT_LIGHTTABLE_LAYOUT_PREVIEW);
661   luaA_enum_value(L, dt_lighttable_layout_t, DT_LIGHTTABLE_LAYOUT_LAST);
662 }
663 #endif
664 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
665 // vim: shiftwidth=2 expandtab tabstop=2 cindent
666 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
667