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 "common/collection.h"
20 #include "common/darktable.h"
21 #include "common/l10n.h"
22 #include "control/conf.h"
23 #include "control/control.h"
24 #include "dtgtk/button.h"
25 #include "dtgtk/culling.h"
26 #include "dtgtk/thumbtable.h"
27 #include "dtgtk/togglebutton.h"
28 #include "gui/accelerators.h"
29 #include "gui/preferences.h"
30 #include "libs/lib.h"
31 #include "libs/lib_api.h"
32 #ifdef GDK_WINDOWING_QUARTZ
33 #include "osx/osx.h"
34 #endif
35
36 DT_MODULE(1)
37
38 typedef struct dt_lib_tool_preferences_t
39 {
40 GtkWidget *preferences_button, *grouping_button, *overlays_button, *help_button, *keymap_button;
41 GtkWidget *over_popup, *thumbnails_box, *culling_box;
42 GtkWidget *over_label, *over_r0, *over_r1, *over_r2, *over_r3, *over_r4, *over_r5, *over_r6, *over_timeout,
43 *over_tt;
44 GtkWidget *over_culling_label, *over_culling_r0, *over_culling_r3, *over_culling_r4, *over_culling_r6,
45 *over_culling_timeout, *over_culling_tt;
46 gboolean disable_over_events;
47 } dt_lib_tool_preferences_t;
48
49 /* callback for grouping button */
50 static void _lib_filter_grouping_button_clicked(GtkWidget *widget, gpointer user_data);
51 /* callback for preference button */
52 static void _lib_preferences_button_clicked(GtkWidget *widget, gpointer user_data);
53 /* callback for help button */
54 static void _lib_help_button_clicked(GtkWidget *widget, gpointer user_data);
55 /* callbacks for key mapping button */
56 static void _lib_keymap_button_clicked(GtkWidget *widget, gpointer user_data);
57 static gboolean _lib_keymap_button_press_release(GtkWidget *button, GdkEventButton *event, gpointer user_data);
58
name(dt_lib_module_t * self)59 const char *name(dt_lib_module_t *self)
60 {
61 return _("preferences");
62 }
63
views(dt_lib_module_t * self)64 const char **views(dt_lib_module_t *self)
65 {
66 static const char *v[] = {"*", NULL};
67 return v;
68 }
69
container(dt_lib_module_t * self)70 uint32_t container(dt_lib_module_t *self)
71 {
72 return DT_UI_CONTAINER_PANEL_CENTER_TOP_RIGHT;
73 }
74
expandable(dt_lib_module_t * self)75 int expandable(dt_lib_module_t *self)
76 {
77 return 0;
78 }
79
position()80 int position()
81 {
82 return 1001;
83 }
84
_overlays_accels_callback(GtkAccelGroup * accel_group,GObject * acceleratable,guint keyval,GdkModifierType modifier,gpointer data)85 static void _overlays_accels_callback(GtkAccelGroup *accel_group, GObject *acceleratable, guint keyval,
86 GdkModifierType modifier, gpointer data)
87 {
88 dt_thumbnail_overlay_t over = (dt_thumbnail_overlay_t)GPOINTER_TO_INT(data);
89 dt_thumbtable_set_overlays_mode(dt_ui_thumbtable(darktable.gui->ui), over);
90 }
91
_overlays_toggle_button(GtkWidget * w,gpointer user_data)92 static void _overlays_toggle_button(GtkWidget *w, gpointer user_data)
93 {
94 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
95 dt_lib_tool_preferences_t *d = (dt_lib_tool_preferences_t *)self->data;
96
97 if(d->disable_over_events) return;
98
99 dt_thumbnail_overlay_t over = DT_THUMBNAIL_OVERLAYS_HOVER_NORMAL;
100 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->over_r0)))
101 over = DT_THUMBNAIL_OVERLAYS_NONE;
102 else if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->over_r2)))
103 over = DT_THUMBNAIL_OVERLAYS_HOVER_EXTENDED;
104 else if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->over_r3)))
105 over = DT_THUMBNAIL_OVERLAYS_ALWAYS_NORMAL;
106 else if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->over_r4)))
107 over = DT_THUMBNAIL_OVERLAYS_ALWAYS_EXTENDED;
108 else if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->over_r5)))
109 over = DT_THUMBNAIL_OVERLAYS_MIXED;
110 else if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->over_r6)))
111 over = DT_THUMBNAIL_OVERLAYS_HOVER_BLOCK;
112
113 dt_ui_thumbtable(darktable.gui->ui)->show_tooltips = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->over_tt));
114 dt_thumbtable_set_overlays_mode(dt_ui_thumbtable(darktable.gui->ui), over);
115
116 gtk_widget_set_sensitive(d->over_timeout, (over == DT_THUMBNAIL_OVERLAYS_HOVER_BLOCK));
117
118 // we don't hide the popup in case of block overlay, as the user may want to tweak the duration
119 if(over != DT_THUMBNAIL_OVERLAYS_HOVER_BLOCK) gtk_widget_hide(d->over_popup);
120
121 #ifdef USE_LUA
122 gboolean show = (over == DT_THUMBNAIL_OVERLAYS_ALWAYS_NORMAL || over == DT_THUMBNAIL_OVERLAYS_ALWAYS_EXTENDED);
123 dt_lua_async_call_alien(dt_lua_event_trigger_wrapper, 0, NULL, NULL, LUA_ASYNC_TYPENAME, "const char*",
124 "global_toolbox-overlay_toggle", LUA_ASYNC_TYPENAME, "bool", show, LUA_ASYNC_DONE);
125 #endif // USE_LUA
126 }
127
_overlays_toggle_culling_button(GtkWidget * w,gpointer user_data)128 static void _overlays_toggle_culling_button(GtkWidget *w, gpointer user_data)
129 {
130 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
131 dt_lib_tool_preferences_t *d = (dt_lib_tool_preferences_t *)self->data;
132
133 if(d->disable_over_events) return;
134
135 dt_thumbnail_overlay_t over = DT_THUMBNAIL_OVERLAYS_HOVER_BLOCK;
136 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->over_culling_r0)))
137 over = DT_THUMBNAIL_OVERLAYS_NONE;
138 else if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->over_culling_r3)))
139 over = DT_THUMBNAIL_OVERLAYS_ALWAYS_NORMAL;
140 else if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->over_culling_r4)))
141 over = DT_THUMBNAIL_OVERLAYS_ALWAYS_EXTENDED;
142
143 dt_culling_mode_t cmode = DT_CULLING_MODE_CULLING;
144 if(dt_view_lighttable_preview_state(darktable.view_manager)) cmode = DT_CULLING_MODE_PREVIEW;
145 gchar *txt = g_strdup_printf("plugins/lighttable/overlays/culling/%d", cmode);
146 dt_conf_set_int(txt, over);
147 g_free(txt);
148 txt = g_strdup_printf("plugins/lighttable/tooltips/culling/%d", cmode);
149 dt_conf_set_bool(txt, gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->over_culling_tt)));
150 g_free(txt);
151 dt_view_lighttable_culling_preview_reload_overlays(darktable.view_manager);
152
153 gtk_widget_set_sensitive(d->over_culling_timeout, (over == DT_THUMBNAIL_OVERLAYS_HOVER_BLOCK));
154
155 // we don't hide the popup in case of block overlay, as the user may want to tweak the duration
156 if(over != DT_THUMBNAIL_OVERLAYS_HOVER_BLOCK) gtk_widget_hide(d->over_popup);
157
158 #ifdef USE_LUA
159 gboolean show = (over == DT_THUMBNAIL_OVERLAYS_ALWAYS_NORMAL || over == DT_THUMBNAIL_OVERLAYS_ALWAYS_EXTENDED);
160 dt_lua_async_call_alien(dt_lua_event_trigger_wrapper, 0, NULL, NULL, LUA_ASYNC_TYPENAME, "const char*",
161 "global_toolbox-overlay_toggle", LUA_ASYNC_TYPENAME, "bool", show, LUA_ASYNC_DONE);
162 #endif // USE_LUA
163 }
164
_overlays_timeout_changed(GtkWidget * w,gpointer user_data)165 static void _overlays_timeout_changed(GtkWidget *w, gpointer user_data)
166 {
167 dt_lib_module_t *self = (dt_lib_module_t *)user_data;
168 dt_lib_tool_preferences_t *d = (dt_lib_tool_preferences_t *)self->data;
169
170 const int val = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(w));
171
172 if(w == d->over_timeout)
173 {
174 dt_thumbtable_set_overlays_block_timeout(dt_ui_thumbtable(darktable.gui->ui), val);
175 }
176 else if(w == d->over_culling_timeout)
177 {
178 dt_culling_mode_t cmode = DT_CULLING_MODE_CULLING;
179 if(dt_view_lighttable_preview_state(darktable.view_manager)) cmode = DT_CULLING_MODE_PREVIEW;
180 gchar *txt = g_strdup_printf("plugins/lighttable/overlays/culling_block_timeout/%d", cmode);
181 dt_conf_set_int(txt, val);
182 g_free(txt);
183
184 dt_view_lighttable_culling_preview_reload_overlays(darktable.view_manager);
185 }
186 }
187
_overlays_show_popup(GtkWidget * button,dt_lib_module_t * self)188 static void _overlays_show_popup(GtkWidget *button, dt_lib_module_t *self)
189 {
190 dt_lib_tool_preferences_t *d = (dt_lib_tool_preferences_t *)self->data;
191
192 d->disable_over_events = TRUE;
193
194 gboolean show = FALSE;
195
196 // thumbnails part
197 const dt_view_t *cv = dt_view_manager_get_current_view(darktable.view_manager);
198 gboolean thumbs_state;
199 if(g_strcmp0(cv->module_name, "slideshow") == 0)
200 {
201 thumbs_state = FALSE;
202 }
203 else if(g_strcmp0(cv->module_name, "lighttable") == 0)
204 {
205 if(dt_view_lighttable_preview_state(darktable.view_manager)
206 || dt_view_lighttable_get_layout(darktable.view_manager) == DT_LIGHTTABLE_LAYOUT_CULLING)
207 {
208 thumbs_state = dt_ui_panel_visible(darktable.gui->ui, DT_UI_PANEL_BOTTOM);
209 }
210 else
211 {
212 thumbs_state = TRUE;
213 }
214 }
215 else
216 {
217 thumbs_state = dt_ui_panel_visible(darktable.gui->ui, DT_UI_PANEL_BOTTOM);
218 }
219
220
221 if(thumbs_state)
222 {
223 // we write the label with the size category
224 gchar *txt = g_strdup_printf("%s %d (%d %s)", _("thumbnails overlays for size"),
225 dt_ui_thumbtable(darktable.gui->ui)->prefs_size,
226 dt_ui_thumbtable(darktable.gui->ui)->thumb_size, _("px"));
227 gtk_label_set_text(GTK_LABEL(d->over_label), txt);
228 g_free(txt);
229
230 // we get and set the current value
231 dt_thumbnail_overlay_t mode = dt_ui_thumbtable(darktable.gui->ui)->overlays;
232
233 gtk_spin_button_set_value(GTK_SPIN_BUTTON(d->over_timeout),
234 dt_ui_thumbtable(darktable.gui->ui)->overlays_block_timeout);
235 gtk_widget_set_sensitive(d->over_timeout, FALSE);
236
237 if(mode == DT_THUMBNAIL_OVERLAYS_NONE)
238 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->over_r0), TRUE);
239 else if(mode == DT_THUMBNAIL_OVERLAYS_HOVER_EXTENDED)
240 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->over_r2), TRUE);
241 else if(mode == DT_THUMBNAIL_OVERLAYS_ALWAYS_NORMAL)
242 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->over_r3), TRUE);
243 else if(mode == DT_THUMBNAIL_OVERLAYS_ALWAYS_EXTENDED)
244 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->over_r4), TRUE);
245 else if(mode == DT_THUMBNAIL_OVERLAYS_MIXED)
246 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->over_r5), TRUE);
247 else if(mode == DT_THUMBNAIL_OVERLAYS_HOVER_BLOCK)
248 {
249 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->over_r6), TRUE);
250 gtk_widget_set_sensitive(d->over_timeout, TRUE);
251 }
252 else
253 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->over_r1), TRUE);
254
255 if(mode == DT_THUMBNAIL_OVERLAYS_HOVER_BLOCK)
256 {
257 gtk_widget_set_tooltip_text(d->over_timeout,
258 _("duration before the block overlay is hidden after each mouse movement on the "
259 "image\nset -1 to never hide the overlay"));
260 }
261 else
262 {
263 gtk_widget_set_tooltip_text(d->over_timeout, _("timeout only available for block overlay"));
264 }
265
266 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->over_tt), dt_ui_thumbtable(darktable.gui->ui)->show_tooltips);
267
268 gtk_widget_show_all(d->thumbnails_box);
269 show = TRUE;
270 }
271 else
272 {
273 gtk_widget_hide(d->thumbnails_box);
274 }
275
276 // and we do the same for culling/preview if needed
277 if(g_strcmp0(cv->module_name, "lighttable") == 0
278 && (dt_view_lighttable_preview_state(darktable.view_manager)
279 || dt_view_lighttable_get_layout(darktable.view_manager) == DT_LIGHTTABLE_LAYOUT_CULLING))
280 {
281 dt_culling_mode_t cmode = DT_CULLING_MODE_CULLING;
282 if(dt_view_lighttable_preview_state(darktable.view_manager)) cmode = DT_CULLING_MODE_PREVIEW;
283
284 // we write the label text
285 if(cmode == DT_CULLING_MODE_CULLING)
286 gtk_label_set_text(GTK_LABEL(d->over_culling_label), _("culling overlays"));
287 else
288 gtk_label_set_text(GTK_LABEL(d->over_culling_label), _("preview overlays"));
289
290 // we get and set the current value
291 gchar *otxt = g_strdup_printf("plugins/lighttable/overlays/culling/%d", cmode);
292 dt_thumbnail_overlay_t mode = dt_conf_get_int(otxt);
293 g_free(otxt);
294
295 otxt = g_strdup_printf("plugins/lighttable/overlays/culling_block_timeout/%d", cmode);
296 int timeout = 2;
297 if(!dt_conf_key_exists(otxt))
298 timeout = dt_conf_get_int("plugins/lighttable/overlay_timeout");
299 else
300 timeout = dt_conf_get_int(otxt);
301 g_free(otxt);
302
303 gtk_spin_button_set_value(GTK_SPIN_BUTTON(d->over_culling_timeout), timeout);
304 gtk_widget_set_sensitive(d->over_culling_timeout, FALSE);
305
306 if(mode == DT_THUMBNAIL_OVERLAYS_NONE)
307 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->over_culling_r0), TRUE);
308 else if(mode == DT_THUMBNAIL_OVERLAYS_ALWAYS_NORMAL)
309 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->over_culling_r3), TRUE);
310 else if(mode == DT_THUMBNAIL_OVERLAYS_ALWAYS_EXTENDED)
311 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->over_culling_r4), TRUE);
312 else
313 {
314 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->over_culling_r6), TRUE);
315 gtk_widget_set_sensitive(d->over_culling_timeout, TRUE);
316 }
317
318 if(mode == DT_THUMBNAIL_OVERLAYS_HOVER_BLOCK)
319 {
320 gtk_widget_set_tooltip_text(d->over_culling_timeout,
321 _("duration before the block overlay is hidden after each mouse movement on the "
322 "image\nset -1 to never hide the overlay"));
323 }
324 else
325 {
326 gtk_widget_set_tooltip_text(d->over_culling_timeout, _("timeout only available for block overlay"));
327 }
328
329 otxt = g_strdup_printf("plugins/lighttable/tooltips/culling/%d", cmode);
330 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->over_culling_tt), dt_conf_get_bool(otxt));
331 g_free(otxt);
332
333 gtk_widget_show_all(d->culling_box);
334 show = TRUE;
335 }
336 else
337 {
338 gtk_widget_hide(d->culling_box);
339 }
340
341 if(show)
342 {
343 GdkDevice *pointer = gdk_seat_get_pointer(gdk_display_get_default_seat(gdk_display_get_default()));
344
345 int x, y;
346 GdkWindow *pointer_window = gdk_device_get_window_at_position(pointer, &x, &y);
347 gpointer pointer_widget = NULL;
348 if(pointer_window)
349 gdk_window_get_user_data(pointer_window, &pointer_widget);
350
351 GdkRectangle rect = { gtk_widget_get_allocated_width(button) / 2,
352 gtk_widget_get_allocated_height(button), 1, 1 };
353
354 if(pointer_widget && button != pointer_widget)
355 gtk_widget_translate_coordinates(pointer_widget, button, x, y, &rect.x, &rect.y);
356
357 gtk_popover_set_pointing_to(GTK_POPOVER(d->over_popup), &rect);
358
359 gtk_widget_show(d->over_popup);
360 }
361 else
362 dt_control_log(_("overlays not available here..."));
363
364 d->disable_over_events = FALSE;
365 }
366
_main_icons_register_size(GtkWidget * widget,GdkRectangle * allocation,gpointer user_data)367 static void _main_icons_register_size(GtkWidget *widget, GdkRectangle *allocation, gpointer user_data)
368 {
369
370 GtkStateFlags state = gtk_widget_get_state_flags(widget);
371 GtkStyleContext *context = gtk_widget_get_style_context(widget);
372
373 /* get the css geometry properties */
374 GtkBorder margin, border, padding;
375 gtk_style_context_get_margin(context, state, &margin);
376 gtk_style_context_get_border(context, state, &border);
377 gtk_style_context_get_padding(context, state, &padding);
378
379 /* we first remove css margin border and padding from allocation */
380 int width = allocation->width - margin.left - margin.right - border.left - border.right - padding.left - padding.right;
381
382 GtkStyleContext *ccontext = gtk_widget_get_style_context(DTGTK_BUTTON(widget)->canvas);
383 GtkBorder cmargin;
384 gtk_style_context_get_margin(ccontext, state, &cmargin);
385
386 /* we remove the extra room for optical alignment */
387 width = round((float)width * (1.0 - (cmargin.left + cmargin.right) / 100.0f));
388
389 // we store the icon size in order to keep in sync thumbtable overlays
390 darktable.gui->icon_size = width;
391 }
392
gui_init(dt_lib_module_t * self)393 void gui_init(dt_lib_module_t *self)
394 {
395 /* initialize ui widgets */
396 dt_lib_tool_preferences_t *d = (dt_lib_tool_preferences_t *)g_malloc0(sizeof(dt_lib_tool_preferences_t));
397 self->data = (void *)d;
398
399 self->widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
400
401 /* create the grouping button */
402 d->grouping_button = dtgtk_togglebutton_new(dtgtk_cairo_paint_grouping, CPF_STYLE_FLAT, NULL);
403 dt_action_define(&darktable.control->actions_global, NULL, "grouping", d->grouping_button, &dt_action_def_toggle);
404 gtk_box_pack_start(GTK_BOX(self->widget), d->grouping_button, FALSE, FALSE, 0);
405 if(darktable.gui->grouping)
406 gtk_widget_set_tooltip_text(d->grouping_button, _("expand grouped images"));
407 else
408 gtk_widget_set_tooltip_text(d->grouping_button, _("collapse grouped images"));
409 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->grouping_button), darktable.gui->grouping);
410 g_signal_connect(G_OBJECT(d->grouping_button), "clicked", G_CALLBACK(_lib_filter_grouping_button_clicked),
411 NULL);
412
413 /* create the "show/hide overlays" button */
414 d->overlays_button = dtgtk_button_new(dtgtk_cairo_paint_overlays, CPF_STYLE_FLAT, NULL);
415 gtk_widget_set_tooltip_text(d->overlays_button, _("click to change the type of overlays shown on thumbnails"));
416 gtk_box_pack_start(GTK_BOX(self->widget), d->overlays_button, FALSE, FALSE, 0);
417 d->over_popup = gtk_popover_new(d->overlays_button);
418 gtk_widget_set_size_request(d->over_popup, 350, -1);
419 g_object_set(G_OBJECT(d->over_popup), "transitions-enabled", FALSE, NULL);
420 g_signal_connect(G_OBJECT(d->overlays_button), "clicked", G_CALLBACK(_overlays_show_popup), self);
421 // we register size of overlay icon to keep in sync thumbtable overlays
422 g_signal_connect(G_OBJECT(d->overlays_button), "size-allocate", G_CALLBACK(_main_icons_register_size), NULL);
423
424 GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
425
426 gtk_container_add(GTK_CONTAINER(d->over_popup), vbox);
427
428 // thumbnails overlays
429 d->thumbnails_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
430
431 d->over_label = gtk_label_new(_("overlay mode for size"));
432 gtk_widget_set_name(d->over_label, "overlays_label");
433 gtk_box_pack_start(GTK_BOX(d->thumbnails_box), d->over_label, TRUE, TRUE, 0);
434 d->over_r0 = gtk_radio_button_new_with_label(NULL, _("no overlays"));
435 g_signal_connect(G_OBJECT(d->over_r0), "toggled", G_CALLBACK(_overlays_toggle_button), self);
436 gtk_box_pack_start(GTK_BOX(d->thumbnails_box), d->over_r0, TRUE, TRUE, 0);
437 d->over_r1
438 = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(d->over_r0), _("overlays on mouse hover"));
439 g_signal_connect(G_OBJECT(d->over_r1), "toggled", G_CALLBACK(_overlays_toggle_button), self);
440 gtk_box_pack_start(GTK_BOX(d->thumbnails_box), d->over_r1, TRUE, TRUE, 0);
441 d->over_r2 = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(d->over_r0),
442 _("extended overlays on mouse hover"));
443 g_signal_connect(G_OBJECT(d->over_r2), "toggled", G_CALLBACK(_overlays_toggle_button), self);
444 gtk_box_pack_start(GTK_BOX(d->thumbnails_box), d->over_r2, TRUE, TRUE, 0);
445 d->over_r3 = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(d->over_r0), _("permanent overlays"));
446 g_signal_connect(G_OBJECT(d->over_r3), "toggled", G_CALLBACK(_overlays_toggle_button), self);
447 gtk_box_pack_start(GTK_BOX(d->thumbnails_box), d->over_r3, TRUE, TRUE, 0);
448 d->over_r4 = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(d->over_r0),
449 _("permanent extended overlays"));
450 g_signal_connect(G_OBJECT(d->over_r4), "toggled", G_CALLBACK(_overlays_toggle_button), self);
451 gtk_box_pack_start(GTK_BOX(d->thumbnails_box), d->over_r4, TRUE, TRUE, 0);
452 d->over_r5 = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(d->over_r0),
453 _("permanent overlays extended on mouse hover"));
454 g_signal_connect(G_OBJECT(d->over_r5), "toggled", G_CALLBACK(_overlays_toggle_button), self);
455 gtk_box_pack_start(GTK_BOX(d->thumbnails_box), d->over_r5, TRUE, TRUE, 0);
456 GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
457 d->over_r6 = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(d->over_r0),
458 _("overlays block on mouse hover during (s)"));
459 g_signal_connect(G_OBJECT(d->over_r6), "toggled", G_CALLBACK(_overlays_toggle_button), self);
460 gtk_box_pack_start(GTK_BOX(hbox), d->over_r6, TRUE, TRUE, 0);
461 d->over_timeout = gtk_spin_button_new_with_range(-1, 99, 1);
462 g_signal_connect(G_OBJECT(d->over_timeout), "value-changed", G_CALLBACK(_overlays_timeout_changed), self);
463 gtk_box_pack_start(GTK_BOX(hbox), d->over_timeout, TRUE, TRUE, 0);
464 gtk_box_pack_start(GTK_BOX(d->thumbnails_box), hbox, TRUE, TRUE, 0);
465 d->over_tt = gtk_check_button_new_with_label(_("show tooltip"));
466 g_signal_connect(G_OBJECT(d->over_tt), "toggled", G_CALLBACK(_overlays_toggle_button), self);
467 gtk_widget_set_name(d->over_tt, "show-tooltip");
468 gtk_box_pack_start(GTK_BOX(d->thumbnails_box), d->over_tt, TRUE, TRUE, 0);
469
470 gtk_box_pack_start(GTK_BOX(vbox), d->thumbnails_box, TRUE, TRUE, 0);
471
472 // culling/preview overlays
473 d->culling_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
474
475 d->over_culling_label = gtk_label_new(_("overlay mode for size"));
476 gtk_widget_set_name(d->over_culling_label, "overlays_label");
477 gtk_box_pack_start(GTK_BOX(d->culling_box), d->over_culling_label, TRUE, TRUE, 0);
478 d->over_culling_r0 = gtk_radio_button_new_with_label(NULL, _("no overlays"));
479 g_signal_connect(G_OBJECT(d->over_culling_r0), "toggled", G_CALLBACK(_overlays_toggle_culling_button), self);
480 gtk_box_pack_start(GTK_BOX(d->culling_box), d->over_culling_r0, TRUE, TRUE, 0);
481 d->over_culling_r3
482 = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(d->over_culling_r0), _("permanent overlays"));
483 g_signal_connect(G_OBJECT(d->over_culling_r3), "toggled", G_CALLBACK(_overlays_toggle_culling_button), self);
484 gtk_box_pack_start(GTK_BOX(d->culling_box), d->over_culling_r3, TRUE, TRUE, 0);
485 d->over_culling_r4 = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(d->over_culling_r0),
486 _("permanent extended overlays"));
487 g_signal_connect(G_OBJECT(d->over_culling_r4), "toggled", G_CALLBACK(_overlays_toggle_culling_button), self);
488 gtk_box_pack_start(GTK_BOX(d->culling_box), d->over_culling_r4, TRUE, TRUE, 0);
489 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
490 d->over_culling_r6 = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(d->over_culling_r0),
491 _("overlays block on mouse hover during (s)"));
492 g_signal_connect(G_OBJECT(d->over_culling_r6), "toggled", G_CALLBACK(_overlays_toggle_culling_button), self);
493 gtk_box_pack_start(GTK_BOX(hbox), d->over_culling_r6, TRUE, TRUE, 0);
494 d->over_culling_timeout = gtk_spin_button_new_with_range(-1, 99, 1);
495 g_signal_connect(G_OBJECT(d->over_culling_timeout), "value-changed", G_CALLBACK(_overlays_timeout_changed), self);
496 gtk_box_pack_start(GTK_BOX(hbox), d->over_culling_timeout, TRUE, TRUE, 0);
497 gtk_box_pack_start(GTK_BOX(d->culling_box), hbox, TRUE, TRUE, 0);
498 d->over_culling_tt = gtk_check_button_new_with_label(_("show tooltip"));
499 g_signal_connect(G_OBJECT(d->over_culling_tt), "toggled", G_CALLBACK(_overlays_toggle_culling_button), self);
500 gtk_widget_set_name(d->over_culling_tt, "show-tooltip");
501 gtk_box_pack_start(GTK_BOX(d->culling_box), d->over_culling_tt, TRUE, TRUE, 0);
502
503 gtk_box_pack_start(GTK_BOX(vbox), d->culling_box, TRUE, TRUE, 0);
504 gtk_widget_show(vbox);
505
506 /* create the widget help button */
507 d->help_button = dtgtk_togglebutton_new(dtgtk_cairo_paint_help, CPF_STYLE_FLAT, NULL);
508 gtk_box_pack_start(GTK_BOX(self->widget), d->help_button, FALSE, FALSE, 0);
509 gtk_widget_set_tooltip_text(d->help_button, _("enable this, then click on a control element to see its online help"));
510 g_signal_connect(G_OBJECT(d->help_button), "clicked", G_CALLBACK(_lib_help_button_clicked), d);
511 dt_gui_add_help_link(d->help_button, dt_get_help_url("global_toolbox_help"));
512
513 /* create the shortcuts button */
514 d->keymap_button = dtgtk_togglebutton_new(dtgtk_cairo_paint_shortcut, CPF_STYLE_FLAT, NULL);
515 dt_action_define(&darktable.control->actions_global, NULL, "shortcuts", d->keymap_button, &dt_action_def_toggle);
516 gtk_box_pack_start(GTK_BOX(self->widget), d->keymap_button, FALSE, FALSE, 0);
517 gtk_widget_set_tooltip_text(d->keymap_button, _("define shortcuts\n"
518 "hover over a widget and press keys with mouse click and scroll or move combinations\n"
519 "repeat same combination again to delete mapping\n"
520 "click on a widget, module or screen area to open the dialog for further configuration"));
521 g_signal_connect(G_OBJECT(d->keymap_button), "clicked", G_CALLBACK(_lib_keymap_button_clicked), d);
522 g_signal_connect(G_OBJECT(d->keymap_button), "button-press-event", G_CALLBACK(_lib_keymap_button_press_release), d);
523 g_signal_connect(G_OBJECT(d->keymap_button), "button-release-event", G_CALLBACK(_lib_keymap_button_press_release), d);
524 dt_gui_add_help_link(d->keymap_button, dt_get_help_url("global_toolbox_keymap"));
525
526 // the rest of these is added in reverse order as they are always put at the end of the container.
527 // that's done so that buttons added via Lua will come first.
528
529 /* create the preference button */
530 d->preferences_button = dtgtk_button_new(dtgtk_cairo_paint_preferences, CPF_STYLE_FLAT, NULL);
531 gtk_box_pack_end(GTK_BOX(self->widget), d->preferences_button, FALSE, FALSE, 0);
532 gtk_widget_set_tooltip_text(d->preferences_button, _("show global preferences"));
533 g_signal_connect(G_OBJECT(d->preferences_button), "clicked", G_CALLBACK(_lib_preferences_button_clicked),
534 NULL);
535 dt_gui_add_help_link(d->preferences_button, dt_get_help_url("global_toolbox_preferences"));
536
537 }
538
gui_cleanup(dt_lib_module_t * self)539 void gui_cleanup(dt_lib_module_t *self)
540 {
541 g_free(self->data);
542 self->data = NULL;
543 }
544
_lib_preferences_button_clicked(GtkWidget * widget,gpointer user_data)545 void _lib_preferences_button_clicked(GtkWidget *widget, gpointer user_data)
546 {
547 dt_gui_preferences_show();
548 }
549
_lib_filter_grouping_button_clicked(GtkWidget * widget,gpointer user_data)550 static void _lib_filter_grouping_button_clicked(GtkWidget *widget, gpointer user_data)
551 {
552
553 darktable.gui->grouping = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
554 if(darktable.gui->grouping)
555 gtk_widget_set_tooltip_text(widget, _("expand grouped images"));
556 else
557 gtk_widget_set_tooltip_text(widget, _("collapse grouped images"));
558 dt_conf_set_bool("ui_last/grouping", darktable.gui->grouping);
559 darktable.gui->expanded_group_id = -1;
560 dt_collection_update_query(darktable.collection, DT_COLLECTION_CHANGE_RELOAD, DT_COLLECTION_PROP_GROUPING, NULL);
561
562 #ifdef USE_LUA
563 dt_lua_async_call_alien(dt_lua_event_trigger_wrapper,
564 0,NULL,NULL,
565 LUA_ASYNC_TYPENAME,"const char*","global_toolbox-grouping_toggle",
566 LUA_ASYNC_TYPENAME,"bool",darktable.gui->grouping,
567 LUA_ASYNC_DONE);
568 #endif // USE_LUA
569 }
570
571 // TODO: this doesn't work for all widgets. the reason being that the GtkEventBox we put libs/iops into catches events.
get_help_url(GtkWidget * widget)572 static char *get_help_url(GtkWidget *widget)
573 {
574 while(widget)
575 {
576 // if the widget doesn't have a help url set go up the widget hierarchy to find a parent that has an url
577 gchar *help_url = g_object_get_data(G_OBJECT(widget), "dt-help-url");
578
579 if(help_url)
580 return help_url;
581
582 // TODO: shall we cross from libs/iops to the core gui? if not, here is the place to break out of the loop
583
584 widget = gtk_widget_get_parent(widget);
585 }
586
587 return NULL;
588 }
589
_get_base_url()590 static char *_get_base_url()
591 {
592 const gboolean use_default_url =
593 dt_conf_get_bool("context_help/use_default_url");
594 const char *c_base_url = dt_confgen_get("context_help/url", DT_DEFAULT);
595 char *base_url = dt_conf_get_string("context_help/url");
596
597 if(use_default_url)
598 {
599 // want to use default URL, reset darktablerc
600 dt_conf_set_string("context_help/url", c_base_url);
601 return g_strdup(c_base_url);
602 }
603 else
604 return base_url;
605 }
606
_main_do_event_help(GdkEvent * event,gpointer data)607 static void _main_do_event_help(GdkEvent *event, gpointer data)
608 {
609 dt_lib_tool_preferences_t *d = (dt_lib_tool_preferences_t *)data;
610
611 gboolean handled = FALSE;
612
613 switch(event->type)
614 {
615 case GDK_BUTTON_PRESS:
616 {
617 GtkWidget *event_widget = gtk_get_event_widget(event);
618 if(event_widget)
619 {
620 // TODO: When the widget doesn't have a help url set we should probably look at the parent(s)
621 gchar *help_url = get_help_url(event_widget);
622 if(help_url && *help_url)
623 {
624 GtkWidget *win = dt_ui_main_window(darktable.gui->ui);
625 dt_print(DT_DEBUG_CONTROL, "[context help] opening `%s'\n", help_url);
626 char *base_url = _get_base_url();
627
628 // The base_url is: docs.darktable.org/usermanual
629 // The full format for the documentation pages is:
630 // <base-url>/<ver>/<lang>[/path/to/page]
631 // Where:
632 // <ver> = development | 3.6 | 3.8 ...
633 // <lang> = en / fr ... (default = en)
634
635 // in case of a standard release, append the dt version to the url
636 if(dt_is_dev_version())
637 {
638 base_url = dt_util_dstrcat(base_url, "development/");
639 }
640 else
641 {
642 char *ver = dt_version_major_minor();
643 base_url = dt_util_dstrcat(base_url, "%s/", ver);
644 g_free(ver);
645 }
646
647 char *last_base_url = dt_conf_get_string("context_help/last_url");
648
649 // if url is https://www.darktable.org/usermanual/,
650 // it is the old deprecated url and we need to update it
651 if(!last_base_url
652 || !*last_base_url
653 || (strcmp(base_url, last_base_url) != 0))
654 {
655 g_free(last_base_url);
656 last_base_url = base_url;
657
658 // ask the user if darktable.org may be accessed
659 GtkWidget *dialog = gtk_message_dialog_new
660 (GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT,
661 GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
662 _("do you want to access `%s'?"), last_base_url);
663 #ifdef GDK_WINDOWING_QUARTZ
664 dt_osx_disallow_fullscreen(dialog);
665 #endif
666
667 gtk_window_set_title(GTK_WINDOW(dialog), _("access the online usermanual?"));
668 const gint res = gtk_dialog_run(GTK_DIALOG(dialog));
669 gtk_widget_destroy(dialog);
670 if(res == GTK_RESPONSE_YES)
671 {
672 dt_conf_set_string("context_help/last_url", last_base_url);
673 }
674 else
675 {
676 g_free(base_url);
677 base_url = NULL;
678 }
679 }
680 if(base_url)
681 {
682 char *lang = "en";
683 GError *error = NULL;
684
685 // array of languages the usermanual supports.
686 // NULL MUST remain the last element of the array
687 const char *supported_languages[] =
688 { "en", "fr", "de", "eo", "es", "gl", "it", "pl", "pt-br", "uk", NULL };
689 int lang_index = 0;
690 gboolean is_language_supported = FALSE;
691
692 if(darktable.l10n != NULL)
693 {
694 dt_l10n_language_t *language = NULL;
695 if(darktable.l10n->selected != -1)
696 language = (dt_l10n_language_t *)g_list_nth(darktable.l10n->languages, darktable.l10n->selected)->data;
697 if (language != NULL)
698 lang = language->code;
699 while(supported_languages[lang_index])
700 {
701 gchar *nlang = g_strdup(lang);
702
703 // try lang as-is
704 if(!g_ascii_strcasecmp(nlang, supported_languages[lang_index]))
705 {
706 is_language_supported = TRUE;
707 }
708
709 if(!is_language_supported)
710 {
711 // keep only first part up to _
712 for(gchar *p = nlang; *p; p++)
713 if(*p == '_') *p = '\0';
714
715 if(!g_ascii_strcasecmp(nlang, supported_languages[lang_index]))
716 {
717 is_language_supported = TRUE;
718 }
719 }
720
721 g_free(nlang);
722 if(is_language_supported) break;
723
724 lang_index++;
725 }
726 }
727
728 // language not found, default to EN
729 if(!is_language_supported) lang_index = 0;
730
731 char *url = g_build_path("/", base_url, supported_languages[lang_index], help_url, NULL);
732
733 // TODO: call the web browser directly so that file:// style base for local installs works
734 const gboolean uri_success = gtk_show_uri_on_window(GTK_WINDOW(win), url, gtk_get_current_event_time(), &error);
735 g_free(base_url);
736 g_free(url);
737 if(uri_success)
738 {
739 dt_control_log(_("help url opened in web browser"));
740 }
741 else
742 {
743 dt_control_log(_("error while opening help url in web browser"));
744 if (error != NULL) // uri_success being FALSE should guarantee that
745 {
746 fprintf (stderr, "unable to read file: %s\n", error->message);
747 g_error_free (error);
748 }
749 }
750 }
751 }
752 else
753 {
754 dt_control_log(_("there is no help available for this element"));
755 }
756 }
757 handled = TRUE;
758 break;
759 }
760
761 case GDK_BUTTON_RELEASE:
762 {
763 // reset GTK to normal behaviour
764
765 g_signal_handlers_block_by_func(d->help_button, _lib_help_button_clicked, d);
766 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->help_button), FALSE);
767 g_signal_handlers_unblock_by_func(d->help_button, _lib_help_button_clicked, d);
768
769 dt_control_allow_change_cursor();
770 dt_control_change_cursor(GDK_LEFT_PTR);
771 gdk_event_handler_set((GdkEventFunc)gtk_main_do_event, NULL, NULL);
772
773 handled = TRUE;
774 }
775 break;
776
777 case GDK_ENTER_NOTIFY:
778 case GDK_LEAVE_NOTIFY:
779 {
780 GtkWidget *event_widget = gtk_get_event_widget(event);
781 if(event_widget)
782 {
783 gchar *help_url = get_help_url(event_widget);
784 if(help_url)
785 {
786 // TODO: find a better way to tell the user that the hovered widget has a help link
787 dt_cursor_t cursor = event->type == GDK_ENTER_NOTIFY ? GDK_QUESTION_ARROW : GDK_X_CURSOR;
788 dt_control_allow_change_cursor();
789 dt_control_change_cursor(cursor);
790 dt_control_forbid_change_cursor();
791 }
792 }
793 break;
794 }
795 default:
796 break;
797 }
798
799 if(!handled) gtk_main_do_event(event);
800 }
801
802 // Don't save across sessions (window managers role)
803 static struct { gint x, y, w, h; } _shortcuts_dialog_posize = {};
804
_resize_shortcuts_dialog(GtkWidget * widget,GdkEvent * event,gpointer user_data)805 static gboolean _resize_shortcuts_dialog(GtkWidget *widget, GdkEvent *event, gpointer user_data)
806 {
807 gtk_window_get_position(GTK_WINDOW(widget), &_shortcuts_dialog_posize.x, &_shortcuts_dialog_posize.y);
808 gtk_window_get_size(GTK_WINDOW(widget), &_shortcuts_dialog_posize.w, &_shortcuts_dialog_posize.h);
809
810 dt_conf_set_int("ui_last/shortcuts_dialog_width", _shortcuts_dialog_posize.w);
811 dt_conf_set_int("ui_last/shortcuts_dialog_height", _shortcuts_dialog_posize.h);
812
813 return FALSE;
814 }
815
_set_mapping_mode_cursor(GtkWidget * widget)816 static void _set_mapping_mode_cursor(GtkWidget *widget)
817 {
818 GdkCursorType cursor = GDK_DIAMOND_CROSS;
819
820 if(GTK_IS_EVENT_BOX(widget)) widget = gtk_bin_get_child(GTK_BIN(widget));
821
822 if(widget && !strcmp(gtk_widget_get_name(widget), "module-header"))
823 cursor = GDK_BASED_ARROW_DOWN;
824 else if(g_hash_table_lookup(darktable.control->widgets, darktable.control->mapping_widget)
825 && darktable.develop)
826 {
827 switch(dt_dev_modulegroups_basics_module_toggle(darktable.develop, widget, FALSE))
828 {
829 case 1: cursor = GDK_SB_UP_ARROW; break;
830 case -1: cursor = GDK_SB_DOWN_ARROW; break;
831 default: cursor = GDK_BOX_SPIRAL;
832 }
833 }
834
835 dt_control_allow_change_cursor();
836 dt_control_change_cursor(cursor);
837 dt_control_forbid_change_cursor();
838 }
839
_show_shortcuts_prefs(GtkWidget * w)840 static void _show_shortcuts_prefs(GtkWidget *w)
841 {
842 GtkWidget *shortcuts_dialog = gtk_dialog_new_with_buttons(_("shortcuts"), GTK_WINDOW(dt_ui_main_window(darktable.gui->ui)),
843 GTK_DIALOG_DESTROY_WITH_PARENT, NULL, NULL);
844 if(!_shortcuts_dialog_posize.w)
845 gtk_window_set_default_size(GTK_WINDOW(shortcuts_dialog),
846 DT_PIXEL_APPLY_DPI(dt_conf_get_int("ui_last/shortcuts_dialog_width")),
847 DT_PIXEL_APPLY_DPI(dt_conf_get_int("ui_last/shortcuts_dialog_height")));
848 else
849 {
850 gtk_window_move(GTK_WINDOW(shortcuts_dialog), _shortcuts_dialog_posize.x, _shortcuts_dialog_posize.y);
851 gtk_window_resize(GTK_WINDOW(shortcuts_dialog), _shortcuts_dialog_posize.w, _shortcuts_dialog_posize.h);
852 }
853 g_signal_connect(G_OBJECT(shortcuts_dialog), "configure-event", G_CALLBACK(_resize_shortcuts_dialog), NULL);
854
855 //grab the content area of the dialog
856 GtkWidget *content = gtk_dialog_get_content_area(GTK_DIALOG(shortcuts_dialog));
857 gtk_box_pack_start(GTK_BOX(content), dt_shortcuts_prefs(w), TRUE, TRUE, 0);
858
859 gtk_widget_show_all(shortcuts_dialog);
860 gtk_dialog_run(GTK_DIALOG(shortcuts_dialog));
861 gtk_widget_destroy(shortcuts_dialog);
862 }
863
_main_do_event_keymap(GdkEvent * event,gpointer data)864 static void _main_do_event_keymap(GdkEvent *event, gpointer data)
865 {
866 GtkWidget *event_widget = gtk_get_event_widget(event);
867
868 switch(event->type)
869 {
870 case GDK_LEAVE_NOTIFY:
871 case GDK_ENTER_NOTIFY:
872 if(darktable.control->mapping_widget
873 && event->crossing.mode == GDK_CROSSING_UNGRAB)
874 break;
875 case GDK_GRAB_BROKEN:
876 case GDK_FOCUS_CHANGE:
877 darktable.control->mapping_widget = event_widget;
878 _set_mapping_mode_cursor(event_widget);
879 break;
880 case GDK_BUTTON_PRESS:
881 if(gdk_display_device_is_grabbed(gdk_window_get_display(event->button.window), event->button.device))
882 break;
883
884 GtkWidget *main_window = dt_ui_main_window(darktable.gui->ui);
885 if(gtk_widget_get_toplevel(event_widget) != main_window)
886 break;
887
888 if(!gtk_window_is_active(GTK_WINDOW(main_window)))
889 break;
890
891 dt_lib_tool_preferences_t *d = (dt_lib_tool_preferences_t *)data;
892 if(event_widget == d->keymap_button)
893 break;
894
895 if(GTK_IS_ENTRY(event_widget))
896 break;
897
898 if(event->button.button != GDK_BUTTON_PRIMARY)
899 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->keymap_button), FALSE);
900 else if(dt_modifier_is(event->button.state, GDK_CONTROL_MASK))
901 {
902 if(darktable.develop)
903 {
904 dt_dev_modulegroups_basics_module_toggle(darktable.develop, event_widget, TRUE);
905 _set_mapping_mode_cursor(event_widget);
906 }
907 }
908 else
909 {
910 // allow opening modules to map widgets inside
911 if(GTK_IS_EVENT_BOX(event_widget)) event_widget = gtk_bin_get_child(GTK_BIN(event_widget));
912 if(event_widget && !strcmp(gtk_widget_get_name(event_widget), "module-header"))
913 break;
914
915 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->keymap_button), FALSE);
916 _show_shortcuts_prefs(event_widget);
917 }
918
919 return;
920 default:
921 break;
922 }
923
924 gtk_main_do_event(event);
925 }
926
_lib_help_button_clicked(GtkWidget * widget,gpointer user_data)927 static void _lib_help_button_clicked(GtkWidget *widget, gpointer user_data)
928 {
929 dt_control_change_cursor(GDK_X_CURSOR);
930 dt_control_forbid_change_cursor();
931 gdk_event_handler_set(_main_do_event_help, user_data, NULL);
932 }
933
_lib_keymap_button_clicked(GtkWidget * widget,gpointer user_data)934 static void _lib_keymap_button_clicked(GtkWidget *widget, gpointer user_data)
935 {
936 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
937 {
938 gdk_event_handler_set(_main_do_event_keymap, user_data, NULL);
939 }
940 else
941 {
942 darktable.control->mapping_widget = NULL;
943 dt_control_allow_change_cursor();
944 dt_control_change_cursor(GDK_LEFT_PTR);
945 gdk_event_handler_set((GdkEventFunc)gtk_main_do_event, NULL, NULL);
946 }
947 }
948
_lib_keymap_button_press_release(GtkWidget * button,GdkEventButton * event,gpointer user_data)949 static gboolean _lib_keymap_button_press_release(GtkWidget *button, GdkEventButton *event, gpointer user_data)
950 {
951 static guint start_time = 0;
952
953 int delay = 0;
954 g_object_get(gtk_settings_get_default(), "gtk-long-press-time", &delay, NULL);
955
956 if((event->type == GDK_BUTTON_PRESS && event->button == 3) ||
957 (event->type == GDK_BUTTON_RELEASE && event->time - start_time > delay))
958 {
959 _show_shortcuts_prefs(NULL);
960 return TRUE;
961 }
962 else
963 {
964 start_time = event->time;
965 return FALSE;
966 }
967 }
968
init_key_accels(dt_lib_module_t * self)969 void init_key_accels(dt_lib_module_t *self)
970 {
971 dt_accel_register_global(NC_("accel", "grouping"), 0, 0);
972 dt_accel_register_global(NC_("accel", "thumbnail overlays options"), 0, 0);
973 dt_accel_register_global(NC_("accel", "preferences"), 0, 0);
974 dt_accel_register_global(NC_("accel", "shortcuts"), 0, 0);
975
976 dt_accel_register_global(NC_("accel", "thumbnail overlays/no overlays"), 0, 0);
977 dt_accel_register_global(NC_("accel", "thumbnail overlays/overlays on mouse hover"), 0, 0);
978 dt_accel_register_global(NC_("accel", "thumbnail overlays/extended overlays on mouse hover"), 0, 0);
979 dt_accel_register_global(NC_("accel", "thumbnail overlays/permanent overlays"), 0, 0);
980 dt_accel_register_global(NC_("accel", "thumbnail overlays/permanent extended overlays"), 0, 0);
981 dt_accel_register_global(NC_("accel", "thumbnail overlays/permanent overlays extended on mouse hover"), 0, 0);
982 dt_accel_register_global(NC_("accel", "thumbnail overlays/overlays block on mouse hover"), 0, 0);
983 }
984
connect_key_accels(dt_lib_module_t * self)985 void connect_key_accels(dt_lib_module_t *self)
986 {
987 dt_lib_tool_preferences_t *d = (dt_lib_tool_preferences_t *)self->data;
988
989 dt_accel_connect_button_lib_as_global(self, "thumbnail overlays options", d->overlays_button);
990 dt_accel_connect_button_lib_as_global(self, "preferences", d->preferences_button);
991
992 dt_accel_connect_lib_as_global( self, "thumbnail overlays/no overlays",
993 g_cclosure_new(G_CALLBACK(_overlays_accels_callback), GINT_TO_POINTER(DT_THUMBNAIL_OVERLAYS_NONE), NULL));
994 dt_accel_connect_lib_as_global(self, "thumbnail overlays/overlays on mouse hover",
995 g_cclosure_new(G_CALLBACK(_overlays_accels_callback),
996 GINT_TO_POINTER(DT_THUMBNAIL_OVERLAYS_HOVER_NORMAL), NULL));
997 dt_accel_connect_lib_as_global(self, "thumbnail overlays/extended overlays on mouse hover",
998 g_cclosure_new(G_CALLBACK(_overlays_accels_callback),
999 GINT_TO_POINTER(DT_THUMBNAIL_OVERLAYS_HOVER_EXTENDED), NULL));
1000 dt_accel_connect_lib_as_global(self, "thumbnail overlays/permanent overlays",
1001 g_cclosure_new(G_CALLBACK(_overlays_accels_callback),
1002 GINT_TO_POINTER(DT_THUMBNAIL_OVERLAYS_ALWAYS_NORMAL), NULL));
1003 dt_accel_connect_lib_as_global(self, "thumbnail overlays/permanent extended overlays",
1004 g_cclosure_new(G_CALLBACK(_overlays_accels_callback),
1005 GINT_TO_POINTER(DT_THUMBNAIL_OVERLAYS_ALWAYS_EXTENDED), NULL));
1006 dt_accel_connect_lib_as_global(
1007 self, "thumbnail overlays/permanent overlays extended on mouse hover",
1008 g_cclosure_new(G_CALLBACK(_overlays_accels_callback), GINT_TO_POINTER(DT_THUMBNAIL_OVERLAYS_MIXED), NULL));
1009 dt_accel_connect_lib_as_global(self, "thumbnail overlays/overlays block on mouse hover",
1010 g_cclosure_new(G_CALLBACK(_overlays_accels_callback),
1011 GINT_TO_POINTER(DT_THUMBNAIL_OVERLAYS_HOVER_BLOCK), NULL));
1012 }
1013
1014 #ifdef USE_LUA
1015
grouping_member(lua_State * L)1016 static int grouping_member(lua_State *L)
1017 {
1018 dt_lib_module_t *self = *(dt_lib_module_t **)lua_touserdata(L, 1);
1019 dt_lib_tool_preferences_t *d = (dt_lib_tool_preferences_t *)self->data;
1020 if(lua_gettop(L) != 3)
1021 {
1022 lua_pushboolean(L, darktable.gui->grouping);
1023 return 1;
1024 }
1025 else
1026 {
1027 gboolean value = lua_toboolean(L, 3);
1028 if(darktable.gui->grouping != value)
1029 {
1030 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->grouping_button), value);
1031 }
1032 }
1033 return 0;
1034 }
1035
show_overlays_member(lua_State * L)1036 static int show_overlays_member(lua_State *L)
1037 {
1038 dt_lib_module_t *self = *(dt_lib_module_t **)lua_touserdata(L, 1);
1039 dt_lib_tool_preferences_t *d = (dt_lib_tool_preferences_t *)self->data;
1040 if(lua_gettop(L) != 3)
1041 {
1042 lua_pushboolean(L, darktable.gui->show_overlays);
1043 return 1;
1044 }
1045 else
1046 {
1047 gboolean value = lua_toboolean(L, 3);
1048 if(darktable.gui->show_overlays != value)
1049 {
1050 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->overlays_button), value);
1051 }
1052 }
1053 return 0;
1054 }
1055
init(struct dt_lib_module_t * self)1056 void init(struct dt_lib_module_t *self)
1057 {
1058 lua_State *L = darktable.lua_state.state;
1059 int my_type = dt_lua_module_entry_get_type(L, "lib", self->plugin_name);
1060
1061 lua_pushcfunction(L, grouping_member);
1062 dt_lua_gtk_wrap(L);
1063 dt_lua_type_register_type(L, my_type, "grouping");
1064 lua_pushcfunction(L, show_overlays_member);
1065 dt_lua_gtk_wrap(L);
1066 dt_lua_type_register_type(L, my_type, "show_overlays");
1067
1068 lua_pushcfunction(L, dt_lua_event_multiinstance_register);
1069 lua_pushcfunction(L, dt_lua_event_multiinstance_destroy);
1070 lua_pushcfunction(L, dt_lua_event_multiinstance_trigger);
1071 dt_lua_event_add(L, "global_toolbox-grouping_toggle");
1072
1073 lua_pushcfunction(L, dt_lua_event_multiinstance_register);
1074 lua_pushcfunction(L, dt_lua_event_multiinstance_destroy);
1075 lua_pushcfunction(L, dt_lua_event_multiinstance_trigger);
1076 dt_lua_event_add(L, "global_toolbox-overlay_toggle");
1077 }
1078
1079 #endif // USE_LUA
1080
1081 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
1082 // vim: shiftwidth=2 expandtab tabstop=2 cindent
1083 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1084