Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
application.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2009-2014, 2016 johannes hanika.
4 Copyright (C) 2010 Alexandre Prokoudine.
5 Copyright (C) 2010-2014 Henrik Andersson.
6 Copyright (C) 2010, 2013-2014, 2016 Pascal de Bruijn.
7 Copyright (C) 2010 Richard Hughes.
8 Copyright (C) 2010 Stuart Henderson.
9 Copyright (C) 2010-2019 Tobias Ellinghaus.
10 Copyright (C) 2011 Antony Dovgal.
11 Copyright (C) 2011-2012, 2015 Jérémy Rosen.
12 Copyright (C) 2011 Moritz Lipp.
13 Copyright (C) 2011 Olivier Tribout.
14 Copyright (C) 2011 Robert Bieber.
15 Copyright (C) 2011-2013 Simon Spannagel.
16 Copyright (C) 2012, 2014, 2019-2022 Aldric Renaudin.
17 Copyright (C) 2012-2017, 2019-2020 parafin.
18 Copyright (C) 2012 Richard Wonka.
19 Copyright (C) 2013 Jochem Kossen.
20 Copyright (C) 2013, 2015, 2017-2022 Pascal Obry.
21 Copyright (C) 2013-2016, 2019-2020 Roman Lebedev.
22 Copyright (C) 2014 Mikhail Trishchenkov.
23 Copyright (C) 2015 Edouard Gomez.
24 Copyright (C) 2015 Pedro Côrte-Real.
25 Copyright (C) 2015, 2019 Ulrich Pegelow.
26 Copyright (C) 2016-2017 Peter Budai.
27 Copyright (C) 2017-2018, 2021 Dan Torop.
28 Copyright (C) 2017-2018 Matthieu Moy.
29 Copyright (C) 2017-2018 Rikard Öxler.
30 Copyright (C) 2018-2019 Edgardo Hoszowski.
31 Copyright (C) 2018 Mario Lueder.
32 Copyright (C) 2019-2020, 2022-2023, 2025-2026 Aurélien PIERRE.
33 Copyright (C) 2019 emeikei.
34 Copyright (C) 2019 Heiko Bauke.
35 Copyright (C) 2019 jakubfi.
36 Copyright (C) 2019 vacaboja.
37 Copyright (C) 2020 Bill Ferguson.
38 Copyright (C) 2020-2022 Chris Elston.
39 Copyright (C) 2020-2021 David-Tillmann Schaefer.
40 Copyright (C) 2020-2022 Diederik Ter Rahe.
41 Copyright (C) 2020 Hanno Schwalm.
42 Copyright (C) 2020 Harold le Clément de Saint-Marcq.
43 Copyright (C) 2020-2021 Hubert Kowalski.
44 Copyright (C) 2020 Marco.
45 Copyright (C) 2020, 2022 Miloš Komarčević.
46 Copyright (C) 2020-2021 Philippe Weyland.
47 Copyright (C) 2020 quovadit.
48 Copyright (C) 2020-2021 Ralf Brown.
49 Copyright (C) 2021 darkelectron.
50 Copyright (C) 2021 lhietal.
51 Copyright (C) 2021 luzpaz.
52 Copyright (C) 2022 Martin Bařinka.
53 Copyright (C) 2022 Nicolas Auffray.
54 Copyright (C) 2022 Roman Neuhauser.
55 Copyright (C) 2022 Victor Forsiuk.
56 Copyright (C) 2023 Luca Zulberti.
57 Copyright (C) 2023 Maurizio Paglia.
58 Copyright (C) 2025 Alynx Zhou.
59 Copyright (C) 2025 Guillaume Stutin.
60
61 darktable is free software: you can redistribute it and/or modify
62 it under the terms of the GNU General Public License as published by
63 the Free Software Foundation, either version 3 of the License, or
64 (at your option) any later version.
65
66 darktable is distributed in the hope that it will be useful,
67 but WITHOUT ANY WARRANTY; without even the implied warranty of
68 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
69 GNU General Public License for more details.
70
71 You should have received a copy of the GNU General Public License
72 along with darktable. If not, see <http://www.gnu.org/licenses/>.
73*/
74#include "darktable.h"
75#include "metadata/colorlabels.h" // DT_COLORLABELS_*
76#include "gui/screen_metrics.h"
80#include "common/l10n.h"
82#include "common/utility.h"
83#include "gui/guides.h"
84#include "widgets/expander.h"
85
86#include "gui/application.h"
88#include "gui/common/film_gui.h"
94#include "gui/splash.h"
95
96#include "common/conf.h"
97#include "control/control.h"
98#include "control/jobs.h"
99#include "control/signal.h"
100#include "gui/presets.h"
101#include "views/view.h"
102
103#include <gdk/gdkkeysyms.h>
104#ifdef GDK_WINDOWING_WAYLAND
105#include <gdk/gdkwayland.h>
106#endif
107#ifdef _WIN32
108#include <gdk/gdkwin32.h>
109#endif
110#include <gtk/gtk.h>
111#include <math.h>
112#include <stdlib.h>
113#include <stdio.h>
114#include <string.h>
115#include "widgets/container.h"
116#include "widgets/widget_style.h"
117#ifdef MAC_INTEGRATION
118#include <gtkosxapplication.h>
119#endif
120#ifdef GDK_WINDOWING_QUARTZ
121#include "osx/osx.h"
122#endif
123
124/*
125 * NEW UI API
126 */
127
128/* generic callback for redraw widget signals */
129
130/* widgets/ carries its own colour-label indices so it needs no application header. This is
131 * the only place both enums are visible, so this is where they are pinned together. */
132_Static_assert((int)DT_WIDGET_COLORLABEL_RED == (int)DT_COLORLABELS_RED
135 "widget colour-label indices drifted from dt_colorlabels_enum");
136
137static void _update_display_profile(void);
138static void _ui_widget_redraw_callback(gpointer instance, GtkWidget *widget);
139/* callback for redraw log signals */
140static void _ui_log_redraw_callback(gpointer instance, GtkWidget *widget);
141static void _ui_toast_redraw_callback(gpointer instance, GtkWidget *widget);
142
143// set class function to add CSS classes with just a simple line call
144
145
146
147/* ------------------------------------------------------------------------------------------
148 * Widget-callback suppression depth (see darktable.h for the rationale and API).
149 * ------------------------------------------------------------------------------------------ */
150/* Sub-handle accessors for the GUI singleton (declared in gui/application.h). The orchestrator
151 * binds darktable.gui via dt_gui_get_global(); these narrow it to the parts callers
152 * actually want, so they stop walking the application struct. */
154{
155 return dt_gui_get_global()->ui;
156}
157
159{
160 return dt_gui_get_global()->accels;
161}
162
167
172
173
174
175
176
177/*
178 * OLD UI API
179 */
180static void _init_widgets(dt_gui_gtk_t *gui);
181static gboolean _configure(GtkWidget *da, GdkEventConfigure *event, gpointer user_data);
182
183
184
185
186static gboolean _draw(GtkWidget *da, cairo_t *cr, gpointer user_data)
187{
190 {
192 cairo_paint(cr);
193 }
194 return TRUE;
195}
196
197
198static gboolean _scrolled(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
199{
201
202 int delta_y;
204 {
205 return dt_view_manager_scrolled(darktable.view_manager, event->x, event->y,
206 delta_y < 0,
207 event->state, delta_y);
208 }
209
210 return FALSE;
211}
212
213
215{
220 int width, height;
222 dt_conf_set_int("ui_last/window_width", width);
223 dt_conf_set_int("ui_last/window_height", height);
224
225 gboolean save_window_position = TRUE;
226#ifdef GDK_WINDOWING_WAYLAND
227 GdkDisplay *display = gtk_widget_get_display(widget);
228 if(GDK_IS_WAYLAND_DISPLAY(display))
230#endif
231
233 {
237 {
239 if(!IS_NULL_PTR(monitor))
240 {
242 int monitor_index = -1;
243 for(int i = 0; i < n_monitors; i++)
244 {
246 {
248 break;
249 }
250 }
251 if(monitor_index >= 0)
252 dt_conf_set_int("ui_last/window_monitor", monitor_index);
253 }
254 }
255
257 {
258 int x, y;
259 gtk_window_get_position(GTK_WINDOW(widget), &x, &y);
260 dt_conf_set_int("ui_last/window_x", x);
261 dt_conf_set_int("ui_last/window_y", y);
262 }
263 }
264
266
267 return 0;
268}
269
270
272{
274 dt_gui_add_class(win, "dt_gui_quit");
275 gtk_window_set_title(GTK_WINDOW(win), _("closing Ansel..."));
276
278
279 // Write out windows dimension
281
282 // hide main window
284}
285
286gboolean dt_gui_quit_callback(GtkWidget *widget, GdkEvent *event, gpointer user_data)
287{
289 return TRUE;
290}
291
297
298#ifdef MAC_INTEGRATION
299#ifdef GTK_TYPE_OSX_APPLICATION
300static gboolean _osx_quit_callback(GtkOSXApplication *OSXapp, gpointer user_data)
301#else
302static gboolean _osx_quit_callback(GtkosxApplication *OSXapp, gpointer user_data)
303#endif
304{
309 break;
312 windows = NULL;
313 return TRUE;
314}
315
316#ifdef GTK_TYPE_OSX_APPLICATION
317static gboolean _osx_openfile_callback(GtkOSXApplication *OSXapp, gchar *path, gpointer user_data)
318#else
319static gboolean _osx_openfile_callback(GtkosxApplication *OSXapp, gchar *path, gpointer user_data)
320#endif
321{
322 return dt_load_from_string(path, TRUE, NULL) > 0;
323}
324#endif
325
326static gboolean _configure(GtkWidget *da, GdkEventConfigure *event, gpointer user_data)
327{
328 static int oldw = 0;
329 static int oldh = 0;
330 // make our selves a properly sized pixmap if our window has been resized
331 if(oldw != event->width || oldh != event->height)
332 {
333 // create our new pixmap with the correct size.
335 = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, event->width, event->height);
336 // copy the contents of the old pixmap to the new pixmap. This keeps ugly uninitialized
337 // pixmaps from being painted upon resize
338 // int minw = oldw, minh = oldh;
339 // if(event->width < minw) minw = event->width;
340 // if(event->height < minh) minh = event->height;
341
344 cairo_paint(cr);
345 cairo_destroy(cr);
346
347 // we're done with our old pixmap, so we can get rid of it and replace it with our properly-sized one.
350 // maybe we are on another screen now with > 50% of the area
352 }
353 oldw = event->width;
354 oldh = event->height;
355
356#ifndef GDK_WINDOWING_QUARTZ
357 dt_configure_ppd_dpi((dt_gui_gtk_t *) user_data);
358#endif
359
360 return dt_control_configure(da, event, user_data);
361}
362
363static gboolean _window_configure(GtkWidget *da, GdkEvent *event, gpointer user_data)
364{
365 static int oldx = 0;
366 static int oldy = 0;
367 if(oldx != event->configure.x || oldy != event->configure.y)
368 {
369 // maybe we are on another screen now with > 50% of the area
371 oldx = event->configure.x;
372 oldy = event->configure.y;
373 }
374 return FALSE;
375}
376
378{
379 gboolean valid;
380 double x;
381 double y;
382 guint32 time_ms;
385 double pressure;
386 gboolean have_tilt;
387 double tilt_x;
388 double tilt_y;
390
392
393static inline double _clamp01d(const double value)
394{
395 return MIN(1.0, MAX(0.0, value));
396}
397
398static const gdouble *_event_axes(const GdkEvent *event)
399{
400 if(IS_NULL_PTR(event)) return NULL;
401 switch(event->type)
402 {
404 return ((const GdkEventMotion *)event)->axes;
405 case GDK_BUTTON_PRESS:
409 return ((const GdkEventButton *)event)->axes;
410 default:
411 return NULL;
412 }
413}
414
416 const GdkAxisUse axis, double *value, gboolean *from_source_map)
417{
419 if(IS_NULL_PTR(value)) return FALSE;
420
421 const gdouble *axes = _event_axes(event);
422 if(source_device && axes)
423 {
424 double source_value = 0.0;
426 {
429 return TRUE;
430 }
431 }
432
433 return gdk_event_get_axis((GdkEvent *)event, axis, value);
434}
435
437 const GdkAxisUse axis, double *value)
438{
442 {
444 if(!display || !gdk_display_device_is_grabbed(display, device)) return FALSE;
445 }
446
448 if(n_axes <= 0) return FALSE;
449
450 double *axes = g_newa(double, n_axes);
451 memset(axes, 0, sizeof(double) * n_axes);
452 GdkModifierType modifiers = 0;
453 gdk_device_get_state(device, window, axes, &modifiers);
455}
456
457static gboolean _sample_tablet_state_from_devices(const GdkEvent *event,
458 double *pressure, gboolean *have_pressure,
459 double *tilt_x, double *tilt_y, gboolean *have_tilt,
460 const char **picked_device_name)
461{
462 if(pressure) *pressure = 0.0;
463 if(have_pressure) *have_pressure = FALSE;
464 if(tilt_x) *tilt_x = 0.0;
465 if(tilt_y) *tilt_y = 0.0;
466 if(have_tilt) *have_tilt = FALSE;
468
469 if(IS_NULL_PTR(darktable.gui)) return FALSE;
472 if(IS_NULL_PTR(window)) return FALSE;
473
474 int best_score = -1;
475 double best_p = 0.0;
476 gboolean best_have_p = FALSE;
477 double best_tx = 0.0;
478 double best_ty = 0.0;
479 gboolean best_have_t = FALSE;
480 const char *best_name = NULL;
481
484
485 for(GList *l = runtime_devices; l; l = g_list_next(l))
486 {
487 GdkDevice *device = (GdkDevice *)l->data;
488 if(IS_NULL_PTR(device)) continue;
490 if(source == GDK_SOURCE_KEYBOARD) continue;
492 {
494 /* gdk_device_get_state() requires slave devices to be grabbed.
495 * Skip non-grabbed slaves to avoid GTK/GDK criticals. */
496 if(!display || !gdk_display_device_is_grabbed(display, device)) continue;
497 }
498
500 const gboolean supports_pressure = (axis_flags & GDK_AXIS_FLAG_PRESSURE) != 0;
501 const gboolean supports_x_tilt = (axis_flags & GDK_AXIS_FLAG_XTILT) != 0;
502 const gboolean supports_y_tilt = (axis_flags & GDK_AXIS_FLAG_YTILT) != 0;
503 const gboolean pen_like = (source == GDK_SOURCE_PEN || source == GDK_SOURCE_ERASER || source == GDK_SOURCE_CURSOR);
505
507 if(n_axes <= 0) continue;
508 double *axes = g_newa(double, n_axes);
509 memset(axes, 0, sizeof(double) * n_axes);
510 GdkModifierType modifiers = 0;
511 gdk_device_get_state(device, window, axes, &modifiers);
512
513 double p = 0.0;
514 gboolean have_p = FALSE;
515 double tx = 0.0;
516 double ty = 0.0;
517 gboolean have_tx = FALSE;
518 gboolean have_ty = FALSE;
519
520 for(int i = 0; i < n_axes; i++)
521 {
524 {
525 p = axes[i];
526 have_p = TRUE;
527 }
528 else if(use == GDK_AXIS_XTILT)
529 {
530 tx = axes[i];
531 have_tx = TRUE;
532 }
533 else if(use == GDK_AXIS_YTILT)
534 {
535 ty = axes[i];
536 have_ty = TRUE;
537 }
538 }
539
540 const gboolean have_t = have_tx || have_ty;
541 const int score = (have_p ? 2 : 0) + (have_t ? 2 : 0) + (p > 1e-4 ? 4 : 0)
542 + ((hypot(tx, ty) > 1e-4) ? 3 : 0) + (pen_like ? 1 : 0);
543 if(score <= best_score) continue;
544
546 best_p = p;
548 best_tx = tx;
549 best_ty = ty;
552 }
554 {
557 }
558
559 if(best_score < 0) return FALSE;
560 if(pressure) *pressure = best_p;
561 if(have_pressure) *have_pressure = best_have_p;
562 if(tilt_x) *tilt_x = best_tx;
563 if(tilt_y) *tilt_y = best_ty;
564 if(have_tilt) *have_tilt = best_have_t;
566 return TRUE;
567}
568
569static dt_control_pointer_input_t _extract_pointer_input(const GdkEvent *event, const double x, const double y,
570 const guint32 time_ms, const gboolean reset_kinematics,
571 const char *tag)
572{
573 dt_control_pointer_input_t input = { 0 };
574 input.x = x;
575 input.y = y;
576 input.time_ms = time_ms;
582 const gboolean supports_pressure = (axis_flags & GDK_AXIS_FLAG_PRESSURE) != 0;
583 const gboolean supports_x_tilt = (axis_flags & GDK_AXIS_FLAG_XTILT) != 0;
584 const gboolean supports_y_tilt = (axis_flags & GDK_AXIS_FLAG_YTILT) != 0;
585 gboolean read_pressure = FALSE;
586 gboolean read_x_tilt = FALSE;
587 gboolean read_y_tilt = FALSE;
588 gboolean map_pressure_source = FALSE;
589 gboolean map_xtilt_source = FALSE;
590 gboolean map_ytilt_source = FALSE;
591 gboolean state_pressure_source = FALSE;
592 gboolean state_pressure_event = FALSE;
593 gboolean state_xtilt_source = FALSE;
594 gboolean state_ytilt_source = FALSE;
595 gboolean fallback_pressure = FALSE;
596 gboolean fallback_tilt = FALSE;
597 const char *fallback_device_name = NULL;
599 const int tool_type = tool ? (int)gdk_device_tool_get_tool_type(tool) : -1;
600 const gboolean tool_is_stylus
607
608 {
609 double pressure = 0.0;
611 {
613 input.pressure = _clamp01d(pressure);
614 input.has_pressure = TRUE;
617 }
619 {
621 input.has_pressure = TRUE;
622 }
623 else if(!is_tablet_like)
624 {
626 }
627 }
628
629 if(input.has_pressure && input.pressure <= 0.0 && window)
630 {
631 double p_state = 0.0;
633 {
634 input.pressure = _clamp01d(p_state);
636 }
637 else if(event_device
639 {
640 input.pressure = _clamp01d(p_state);
642 }
643 }
644
645 {
646 double x_tilt = 0.0;
647 double y_tilt = 0.0;
648 const gboolean has_x_tilt
650 const gboolean has_y_tilt
655 {
656 input.tilt_x = CLAMP(x_tilt, -1.0, 1.0);
657 input.tilt_y = CLAMP(y_tilt, -1.0, 1.0);
658 input.has_tilt = TRUE;
662 }
664 {
667 input.has_tilt = TRUE;
668 }
669 else if(!is_tablet_like)
670 {
672 }
673 }
674
675 if(!input.has_tilt && window)
676 {
677 double tx_state = 0.0, ty_state = 0.0;
680 if(has_tx || has_ty)
681 {
682 input.tilt_x = CLAMP(tx_state, -1.0, 1.0);
683 input.tilt_y = CLAMP(ty_state, -1.0, 1.0);
684 input.has_tilt = TRUE;
690 }
691 }
692
693 if(input.has_tilt)
694 input.tilt = _clamp01d(hypot(input.tilt_x, input.tilt_y));
695 else
696 input.tilt = 0.0;
697
698 if(!input.has_pressure || !input.has_tilt)
699 {
700 double fb_pressure = 0.0;
701 gboolean fb_have_pressure = FALSE;
702 double fb_tilt_x = 0.0;
703 double fb_tilt_y = 0.0;
704 gboolean fb_have_tilt = FALSE;
708 {
709 if(!input.has_pressure && fb_have_pressure)
710 {
712 input.has_pressure = TRUE;
714 }
715 if(!input.has_tilt && fb_have_tilt)
716 {
717 input.tilt_x = CLAMP(fb_tilt_x, -1.0, 1.0);
718 input.tilt_y = CLAMP(fb_tilt_y, -1.0, 1.0);
719 input.tilt = _clamp01d(hypot(input.tilt_x, input.tilt_y));
720 input.has_tilt = TRUE;
722 }
723 }
724 }
725
726 if(input.has_pressure || input.has_tilt) is_tablet_like = TRUE;
727
729 {
735 input.acceleration = 0.0;
736 return input;
737 }
738
740 {
741 const double dt_s = MAX((double)(time_ms - _tablet_motion_state.time_ms), 1.0) * 1e-3;
742 const double dx = x - _tablet_motion_state.x;
743 const double dy = y - _tablet_motion_state.y;
744 const double speed_px_s = hypot(dx, dy) / dt_s;
745 const double accel_px_s2 = fabs(speed_px_s - _tablet_motion_state.speed_px_s) / dt_s;
746 /* Normalize acceleration for stylus mapping. 25000 px/s² keeps a useful
747 * dynamic range while clipping extreme event jitter. */
748 input.acceleration = _clamp01d(accel_px_s2 / 25000.0);
749 _tablet_motion_state.speed_px_s = speed_px_s;
750 }
751 else
752 {
753 input.acceleration = 0.0;
755 }
756
761
763 "[tablet] %s dev='%s' src_dev='%s' evt_dev='%s' src=%d tablet=%d tool=%d supports[p=%d xt=%d yt=%d] read[p=%d xt=%d yt=%d] map_src[p=%d xt=%d yt=%d] state[p_src=%d p_evt=%d xt_src=%d yt_src=%d] fallback[p=%d t=%d dev='%s'] values[p=%.4f tx=%.4f ty=%.4f t=%.4f a=%.4f] xy=(%.1f, %.1f) t_ms=%u reset=%d\n",
764 tag ? tag : "event",
765 device ? gdk_device_get_name(device) : "<none>",
768 (int)source,
769 is_tablet_like ? 1 : 0,
770 tool_type,
771 supports_pressure ? 1 : 0,
772 supports_x_tilt ? 1 : 0,
773 supports_y_tilt ? 1 : 0,
774 read_pressure ? 1 : 0,
775 read_x_tilt ? 1 : 0,
776 read_y_tilt ? 1 : 0,
777 map_pressure_source ? 1 : 0,
778 map_xtilt_source ? 1 : 0,
779 map_ytilt_source ? 1 : 0,
780 state_pressure_source ? 1 : 0,
781 state_pressure_event ? 1 : 0,
782 state_xtilt_source ? 1 : 0,
783 state_ytilt_source ? 1 : 0,
784 fallback_pressure ? 1 : 0,
785 fallback_tilt ? 1 : 0,
787 input.has_pressure ? input.pressure : -1.0,
788 input.has_tilt ? input.tilt_x : 0.0,
789 input.has_tilt ? input.tilt_y : 0.0,
790 input.has_tilt ? input.tilt : 0.0,
791 input.acceleration,
792 x, y,
793 time_ms,
794 reset_kinematics ? 1 : 0);
795
796 return input;
797}
798
799static gboolean _button_pressed(GtkWidget *w, GdkEventButton *event, gpointer user_data)
800{
802
803 /* Reset Gtk focus */
806
807 const dt_control_pointer_input_t input = _extract_pointer_input((const GdkEvent *)event, event->x, event->y,
808 event->time, TRUE, "button-press");
810 const double pressure = input.has_pressure ? input.pressure : 1.0;
811 dt_control_button_pressed(event->x, event->y, pressure, event->button, event->type, event->state & 0xf);
812 return FALSE;
813}
814
815static gboolean _button_released(GtkWidget *w, GdkEventButton *event, gpointer user_data)
816{
818 const dt_control_pointer_input_t input = _extract_pointer_input((const GdkEvent *)event, event->x, event->y,
819 event->time, FALSE, "button-release");
821 dt_control_button_released(event->x, event->y, event->button, event->state & 0xf);
822
823 return TRUE;
824}
825
826static gboolean _mouse_moved(GtkWidget *w, GdkEventMotion *event, gpointer user_data)
827{
829
830 const dt_control_pointer_input_t input = _extract_pointer_input((const GdkEvent *)event, event->x, event->y,
831 event->time, FALSE, "motion");
833 dt_control_mouse_moved(event->x, event->y, input.has_pressure ? input.pressure : 1.0, event->state & 0xf);
834 return FALSE;
835}
836
837#ifdef _WIN32
838/* Arbitrary stable subclass identifier encoded as ASCII "ASNN".
839 * It only needs to stay unique within this process for SetWindowSubclass(). */
840#define DT_WIN32_CURSOR_SUBCLASS_CENTER ((UINT_PTR)0x41534e4e)
841
844{
845 /* On Win32, DefSubclassProc() answers WM_SETCURSOR for the drawing area in center view by
846 * restoring the window-class default arrow on every mouse move. The center
847 * view already selected the proper cursor through GDK, so swallow the
848 * client-area reset and keep the current cursor unchanged until the view
849 * requests another explicit cursor change. */
851 return TRUE;
852
853 return DefSubclassProc(hwnd, message, w_param, l_param);
854}
855
863
871#endif
872
873static gboolean _key_pressed(GtkWidget *w, GdkEventKey *event)
874{
877 return TRUE;
878}
879
880static gboolean _center_leave(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
881{
883 return TRUE;
884}
885
886static gboolean _center_enter(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
887{
889 return TRUE;
890}
891
892static const char* _get_source_name(int pos)
893{
894 static const gchar *SOURCE_NAMES[]
895 = { "GDK_SOURCE_MOUSE", "GDK_SOURCE_PEN", "GDK_SOURCE_ERASER", "GDK_SOURCE_CURSOR",
896 "GDK_SOURCE_KEYBOARD", "GDK_SOURCE_TOUCHSCREEN", "GDK_SOURCE_TOUCHPAD", "GDK_SOURCE_TRACKPOINT",
897 "GDK_SOURCE_TABLET_PAD" };
898 if(pos >= G_N_ELEMENTS(SOURCE_NAMES)) return "<UNKNOWN>";
899 return SOURCE_NAMES[pos];
900}
901
902static const char* _get_mode_name(int pos)
903{
904 static const gchar *MODE_NAMES[] = { "GDK_MODE_DISABLED", "GDK_MODE_SCREEN", "GDK_MODE_WINDOW" };
905 if(pos >= G_N_ELEMENTS(MODE_NAMES)) return "<UNKNOWN>";
906 return MODE_NAMES[pos];
907}
908
909static const char* _get_axis_name(int pos)
910{
911 static const gchar *AXIS_NAMES[]
912 = { "GDK_AXIS_IGNORE", "GDK_AXIS_X", "GDK_AXIS_Y", "GDK_AXIS_PRESSURE",
913 "GDK_AXIS_XTILT", "GDK_AXIS_YTILT", "GDK_AXIS_WHEEL", "GDK_AXIS_DISTANCE",
914 "GDK_AXIS_ROTATION", "GDK_AXIS_SLIDER", "GDK_AXIS_LAST" };
915 if(pos >= G_N_ELEMENTS(AXIS_NAMES)) return "<UNKNOWN>";
916 return AXIS_NAMES[pos];
917}
918
919
920/* ---- Host hooks for the shortcut system (widgets/accelerators.c) --------------------- */
921#define DT_ACCEL_SEARCH_RECENT_KEY "plugins/accel_search/recent_entries"
922
924{
925 return dt_gui_main_window();
926}
927
937
938/* dt_control_change_cursor() and dt_toast_log() are macros, so the widget hooks need real
939 * functions to point at. */
940// The host owns the preferences; widgets only know the key they were given.
941static gboolean _widget_stored_int(const char *key, int *value)
942{
943 if(!dt_conf_key_exists(key)) return FALSE;
945 return TRUE;
946}
947
948static void _widget_store_int(const char *key, int value)
949{
951}
952
953static gboolean _widget_stored_bool(const char *key)
954{
955 return dt_conf_get_bool(key);
956}
957
958static void _widget_store_bool(const char *key, gboolean value)
959{
961}
962
963// The display profile can only be probed once the control loop is up: before that there is no
964// realised window to read an X atom or a colord property from. common/colorspaces.c does not
965// know what a control loop is, so the check lives here, with the three call sites it guards.
971
972// Relay a display-profile change onto the signal bus. colorspaces/ raises nothing itself.
977
982
983static void _widget_cursor(GdkCursorType cursor)
984{
986}
987
988static void _widget_message(const char *message)
989{
990 dt_toast_log("%s", message);
991}
992
993static gint _accels_top_offset(void)
994{
997 if(IS_NULL_PTR(top_panel) || !gtk_widget_get_visible(top_panel)) return 0;
998 return gtk_widget_get_allocated_height(top_panel);
999}
1000
1001static gchar *_accels_recent_get(int index)
1002{
1003 gchar *key = g_strdup_printf("%s/%d", DT_ACCEL_SEARCH_RECENT_KEY, index);
1005 dt_free(key);
1006 return value;
1007}
1008
1009static void _accels_recent_set(int index, const char *value)
1010{
1011 gchar *key = g_strdup_printf("%s/%d", DT_ACCEL_SEARCH_RECENT_KEY, index);
1013 dt_free(key);
1014}
1015
1016/* common/ reports startup progress; opening the splash on the first message is display
1017 * state, so it lives here rather than in whatever subsystem happens to be slow. */
1018static void _gui_startup_progress(const char *message)
1019{
1020 // dt_gui_splash_init() already no-ops once the splash exists, so no "have I opened it?"
1021 // flag is needed here -- which is the same flag common/opencl.c used to carry.
1023 dt_gui_splash_updatef("%s", message);
1024}
1025
1026/* common/ announces that an image's thumbnail is stale; this turns that into the two
1027 * widget refreshes. Registered at the end of dt_gui_gtk_init(), so headless runs never
1028 * have a handler and the notification is a no-op there. */
1029static void _gui_refresh_thumbnail(int32_t imgid, gboolean refresh_filmstrip)
1030{
1031 struct dt_ui_t *ui = dt_gui_get_ui();
1032 if(IS_NULL_PTR(ui)) return;
1033
1035
1036 // Best-effort: refreshing the filmstrip spawns an export thread that competes with the
1037 // realtime darkroom main preview, so darkroom write paths ask for it to be skipped.
1040}
1041
1043{
1044 /* lets zero mem */
1045 memset(gui, 0, sizeof(dt_gui_gtk_t));
1046
1048
1049 // force gtk3 to use normal scroll bars instead of the popup thing. they get in the way of controls
1050 // the alternative would be to gtk_scrolled_window_set_overlay_scrolling(..., FALSE); every single widget
1051 // that might have scroll bars
1052 g_setenv("GTK_OVERLAY_SCROLLING", "0", 0);
1053
1054 // same for ubuntus overlay-scrollbar-gtk3
1055 g_setenv("LIBOVERLAY_SCROLLBAR", "0", 0);
1056
1057 // unset gtk rc from kde:
1058 char path[PATH_MAX] = { 0 }, datadir[PATH_MAX] = { 0 }, sharedir[PATH_MAX] = { 0 }, configdir[PATH_MAX] = { 0 };
1059 dt_loc_get_datadir(datadir, sizeof(datadir));
1060 dt_loc_get_sharedir(sharedir, sizeof(sharedir));
1061 dt_loc_get_user_config_dir(configdir, sizeof(configdir));
1062
1063 const char *css_theme = dt_conf_get_string_const("ui_last/theme");
1064 if(css_theme)
1065 g_strlcpy(gui->gtkrc, css_theme, sizeof(gui->gtkrc));
1066 else
1067 g_snprintf(gui->gtkrc, sizeof(gui->gtkrc), "ansel");
1068
1069#ifdef MAC_INTEGRATION
1070#ifdef GTK_TYPE_OSX_APPLICATION
1073 OSXApp, GTK_MENU_SHELL(gtk_menu_bar_new())); // needed for default entries to show up
1074#else
1077 OSXApp, GTK_MENU_SHELL(gtk_menu_bar_new())); // needed for default entries to show up
1078#endif
1079 g_signal_connect(G_OBJECT(OSXApp), "NSApplicationBlockTermination", G_CALLBACK(_osx_quit_callback), NULL);
1081#endif
1082
1083 GtkWidget *widget;
1084 gui->ui = g_malloc0(sizeof(dt_ui_t));
1085 gui->surface = NULL;
1086 gui->center_tooltip = 0;
1087 gui->culling_mode = FALSE;
1088 gui->presets_popup_menu = NULL;
1089 gui->last_preset = NULL;
1090 gui->export_popup.window = NULL;
1091 gui->export_popup.module = NULL;
1092 gui->styles_popup.window = NULL;
1093 gui->styles_popup.module = NULL;
1094
1095 // smooth scrolling must be enabled to handle trackpad/touch events
1097
1098 // Emulates the same feature as Gtk focus but for scrolling events
1099 // The GtkWidget capturing scrolling events will write its address in this pointer
1101
1102 // Init global accels. We localize the config because accels pathes use translated GUI labels.
1103 // User switching between languages may loose their custom shortcuts if we didn't localize them.
1104 // NOTE: needs to be inited before widgets, more specifically before the global menu
1106 gchar *keyboardrc_path = g_build_filename(configdir, keyboardrc, NULL);
1107
1108 GtkAccelFlags flags = 0;
1109 if(dt_conf_get_bool("accels/mask")) flags |= GTK_ACCEL_MASK;
1113
1114 // Initializing widgets
1115 _init_widgets(gui);
1116
1117 //init overlay colors
1119
1120 dt_concat_path_file(path, datadir, "icons");
1122 dt_concat_path_file(path, sharedir, "icons");
1124
1126 widget = center;
1127
1130 gtk_widget_grab_focus(widget);
1132 g_signal_connect(G_OBJECT(widget), "configure-event", G_CALLBACK(_configure), gui);
1133 g_signal_connect(G_OBJECT(widget), "draw", G_CALLBACK(_draw), NULL);
1134 g_signal_connect(G_OBJECT(widget), "motion-notify-event", G_CALLBACK(_mouse_moved), NULL);
1135 g_signal_connect(G_OBJECT(widget), "key-press-event", G_CALLBACK(_key_pressed), NULL);
1136 g_signal_connect(G_OBJECT(widget), "leave-notify-event", G_CALLBACK(_center_leave), NULL);
1137 g_signal_connect(G_OBJECT(widget), "enter-notify-event", G_CALLBACK(_center_enter), NULL);
1138 g_signal_connect(G_OBJECT(widget), "button-press-event", G_CALLBACK(_button_pressed), NULL);
1139 g_signal_connect(G_OBJECT(widget), "button-release-event", G_CALLBACK(_button_released), NULL);
1140 g_signal_connect(G_OBJECT(widget), "scroll-event", G_CALLBACK(_scrolled), NULL);
1141#ifdef _WIN32
1144 if(gtk_widget_get_realized(widget))
1145 _center_realize(widget, NULL);
1146#endif
1147
1149
1151 // update the profile when the window is moved. resize is already handled in configure()
1152 widget = dt_ui_main_window(darktable.gui->ui);
1153 g_signal_connect(G_OBJECT(widget), "configure-event", G_CALLBACK(_window_configure), NULL);
1154
1156
1157 // load theme
1159
1160 // let's try to support pressure sensitive input devices like tablets for mask drawing
1161 dt_print(DT_DEBUG_INPUT, "[input device] Input devices found:\n\n");
1162
1163 GList *input_devices
1165 const int manager_slave_count = 0;
1166 const int manager_floating_count = 0;
1169 dt_print(DT_DEBUG_INPUT, "[input device] seat capabilities bitmask: %u\n",
1171 dt_print(DT_DEBUG_INPUT, "[input device] stylus-capable devices reported by seat: %d\n", g_list_length(stylus_devices));
1172 dt_print(DT_DEBUG_INPUT, "[input device] manager fallback devices: slave=%d floating=%d merged_total=%d\n",
1174 for(GList *l = stylus_devices; !IS_NULL_PTR(l); l = g_list_next(l))
1175 {
1176 GdkDevice *device = (GdkDevice *)l->data;
1177 if(IS_NULL_PTR(device)) continue;
1178 dt_print(DT_DEBUG_INPUT, " [tablet seat] %s source=%s axes_flags=%u n_axes=%d\n",
1181 }
1182 if(stylus_devices)
1183 {
1186 }
1187 for(GList *l = input_devices; !IS_NULL_PTR(l); l = g_list_next(l))
1188 {
1189 GdkDevice *device = (GdkDevice *)l->data;
1190 if(IS_NULL_PTR(device)) continue;
1192 const gint n_axes = (source == GDK_SOURCE_KEYBOARD ? 0 : gdk_device_get_n_axes(device));
1193
1194 // force-enable everything we find in screen mode.
1195 // TODO: make that an user param ?
1197
1198 dt_print(DT_DEBUG_INPUT, "%s (%s), source: %s, mode: %s, %d axes, %d keys\n", gdk_device_get_name(device),
1199 (source != GDK_SOURCE_KEYBOARD) && gdk_device_get_has_cursor(device) ? "with cursor" : "no cursor",
1200 _get_source_name(source),
1203
1204 for(int i = 0; i < n_axes; i++)
1205 {
1207 }
1208 dt_print(DT_DEBUG_INPUT, "\n");
1209 }
1210 if(input_devices)
1211 {
1212 g_list_free(input_devices);
1213 input_devices = NULL;
1214 }
1215
1216 // Gtk seems to capture some reserved shortcuts (Tab). We need to bypass it entirely
1217 // by hacking all events.
1220
1221 // finally set the cursor to be the default.
1222 // for some reason this is needed on some systems to pick up the correctly themed cursor
1225
1226 // Tell common/ how to repaint a stale thumbnail. The backend announces staleness and
1227 // knows nothing about thumbtables; this is the only place the two are connected.
1228 // Tell the widget layer which thread owns widget state, and how the user wants scrolling.
1230 dt_widget_set_scroll_reversed(dt_conf_get_bool("scroll/reverse_x"), dt_conf_get_bool("scroll/reverse_y"));
1231
1246
1253
1254 return 0;
1255}
1256
1258{
1260 GtkAllocation allocation;
1261 gtk_widget_get_allocation(widget, &allocation);
1262
1263 if(darktable.gui->surface)
1264 {
1267 }
1268
1270 = dt_cairo_image_surface_create(CAIRO_FORMAT_ARGB32, allocation.width, allocation.height);
1271 // need to pre-configure views to avoid crash caused by draw coming before configure-event
1273 const int tb = dt_control_get_global()->tabborder;
1274 dt_view_manager_configure(darktable.view_manager, allocation.width - 2 * tb, allocation.height - 2 * tb);
1275#ifdef MAC_INTEGRATION
1276#ifdef GTK_TYPE_OSX_APPLICATION
1278#else
1280#endif
1281#endif
1282#ifdef GDK_WINDOWING_QUARTZ
1284#endif
1285 /* start the event loop */
1286 gtk_main();
1287
1288 if (darktable.gui->surface)
1289 {
1292 }
1293 dt_cleanup();
1294}
1295
1296
1298{
1299 GtkWidget *widget = gui->ui->main_window;
1300
1301 gui->ppd = dt_get_system_gui_ppd(widget);
1302 dt_widget_set_ppd(gui->ppd);
1305
1306 // get the screen resolution
1307 const float screen_dpi_overwrite = dt_conf_get_float("screen_dpi_overwrite");
1308 if(screen_dpi_overwrite > 0.0)
1309 {
1312 dt_print(DT_DEBUG_CONTROL, "[screen resolution] setting the screen resolution to %f dpi as specified in "
1313 "the configuration file\n",
1315 }
1316 else
1317 {
1318#ifdef GDK_WINDOWING_QUARTZ
1319 dt_osx_autoset_dpi(widget);
1320#endif
1322 if(gui->dpi < 0.0)
1323 {
1324 gui->dpi = 96.0;
1326 dt_print(DT_DEBUG_CONTROL, "[screen resolution] setting the screen resolution to the default 96 dpi\n");
1327 }
1328 else
1329 dt_print(DT_DEBUG_CONTROL, "[screen resolution] setting the screen resolution to %f dpi\n", gui->dpi);
1330 }
1331 gui->dpi_factor
1332 = gui->dpi / 96;
1333 dt_screen_set_dpi(gui->dpi); // the raw resolution, for whoever reports it rather than scales by it
1334 dt_widget_set_dpi_factor(gui->dpi_factor); // according to man xrandr and the docs of gdk_screen_set_resolution 96 is the default
1335
1336 // em depends on the screen DPI (point -> px), so refresh it here too.
1338}
1339
1340// Last DT_GUI_BOX_SPACING value actually applied to containers. Used to retarget exactly
1341// the containers that carry the standard spacing when em changes, leaving deliberate 0-spacing
1342// and custom-spacing containers untouched. Seeded with the pre-em reference (10px).
1343static gint _last_box_spacing = 10;
1344
1345typedef struct _spacing_ctx_t { gint old_s, new_s; } _spacing_ctx_t;
1346
1347// Recursively retarget GtkBox/GtkGrid/GtkFlowBox children whose spacing still equals the
1348// previously-applied standard spacing. Setting spacing inside gtk_container_foreach() doesn't
1349// mutate the child list, so the walk is safe.
1350static void _refresh_container_spacing(GtkWidget *w, gpointer user_data)
1351{
1352 const _spacing_ctx_t *c = (const _spacing_ctx_t *)user_data;
1353
1354 if(GTK_IS_BOX(w))
1355 {
1356 if(gtk_box_get_spacing(GTK_BOX(w)) == c->old_s) gtk_box_set_spacing(GTK_BOX(w), c->new_s);
1357 }
1358 else if(GTK_IS_FLOW_BOX(w))
1359 {
1360 if((gint)gtk_flow_box_get_row_spacing(GTK_FLOW_BOX(w)) == c->old_s)
1362 if((gint)gtk_flow_box_get_column_spacing(GTK_FLOW_BOX(w)) == c->old_s)
1364 }
1365 else if(GTK_IS_GRID(w))
1366 {
1367 if((gint)gtk_grid_get_row_spacing(GTK_GRID(w)) == c->old_s)
1368 gtk_grid_set_row_spacing(GTK_GRID(w), c->new_s);
1369 if((gint)gtk_grid_get_column_spacing(GTK_GRID(w)) == c->old_s)
1371 }
1372
1373 if(GTK_IS_CONTAINER(w))
1375}
1376
1377// Propagate a new DT_GUI_BOX_SPACING to already-built containers across every toplevel, so a
1378// runtime font/DPI change updates the inner gutters live (gtk_*_set_spacing bakes the value into
1379// the widget at creation time, so reloading the CSS alone is not enough).
1381{
1382 const gint new_s = DT_GUI_BOX_SPACING;
1383 if(new_s == _last_box_spacing) return;
1384
1385 _spacing_ctx_t c = { _last_box_spacing, new_s };
1386 GList *toplevels = gtk_window_list_toplevels(); // list owned by us, elements not reffed
1387 for(GList *l = toplevels; l; l = l->next)
1390
1391 _last_box_spacing = new_s;
1392}
1393
1395{
1396 dt_gui_gtk_t *gui = darktable.gui;
1397 if(!gui || !gui->ui || !gui->ui->main_window) return;
1398
1400 PangoFontDescription *desc = NULL;
1402 if(!desc) return;
1403
1404 const gint size = pango_font_description_get_size(desc);
1405 if(size > 0)
1406 {
1408 // already device-independent px
1409 gui->em = (double)size / PANGO_SCALE;
1410 else
1411 // points -> px at the screen DPI, matching how GTK renders point-sized fonts
1412 gui->em = (double)size / PANGO_SCALE * gui->dpi / 72.0;
1413
1415 }
1417
1418 // The new em may change DT_GUI_BOX_SPACING; push it to existing containers so the change is live.
1420}
1421
1423{
1424 if(IS_NULL_PTR(layout) || !darktable.gui) return;
1425 // Cairo-drawn text is laid out in points; the screen DPI converts those to device-independent px,
1426 // matching how GTK renders the rest of the UI. Centralized here so call sites never hand-write the DPI.
1428}
1429
1430
1431static gboolean _focus_in_out_event(GtkWidget *widget, GdkEvent *event, gpointer user_data)
1432{
1434 return FALSE;
1435}
1436
1437static gboolean _ui_log_button_press_event(GtkWidget *widget, GdkEvent *event, gpointer user_data)
1438{
1439 gtk_widget_hide(GTK_WIDGET(user_data));
1440 return TRUE;
1441}
1442
1443static gboolean _ui_toast_button_press_event(GtkWidget *widget, GdkEvent *event, gpointer user_data)
1444{
1445 gtk_widget_hide(GTK_WIDGET(user_data));
1446 return TRUE;
1447}
1448
1450{
1452 GtkWidget *widget;
1453
1454 // Creating the main window
1456 gtk_widget_set_name(gui->ui->main_window , "main_window");
1457 gtk_window_set_role(GTK_WINDOW(gui->ui->main_window ), "main-app");
1460
1461 // Init the titlebar ASAP because we replace the desktop titlebar & decoration with ours
1462 dt_ui_init_titlebar(gui->ui);
1463
1465
1467
1468 // NOTE: allowing full-screen on startup shits the bed with MacOS
1469 if(dt_conf_get_bool("ui_last/maximized"))
1470 {
1471 gboolean restore_window_position = TRUE;
1472#ifdef GDK_WINDOWING_WAYLAND
1474 if(GDK_IS_WAYLAND_DISPLAY(display))
1476#endif
1477
1479 {
1482
1484 {
1485 if(dt_conf_key_exists("ui_last/window_monitor"))
1486 {
1487 const int monitor_index = dt_conf_get_int("ui_last/window_monitor");
1490 }
1491
1493 && dt_conf_key_exists("ui_last/window_x")
1494 && dt_conf_key_exists("ui_last/window_y"))
1495 {
1496 const int x = dt_conf_get_int("ui_last/window_x");
1497 const int y = dt_conf_get_int("ui_last/window_y");
1499 }
1500
1501 if(IS_NULL_PTR(monitor))
1505 }
1506
1507 if(!IS_NULL_PTR(monitor))
1508 {
1509 GdkRectangle workarea = { 0 };
1512 }
1513 }
1514
1516 }
1517 else
1518 {
1519 int width = dt_conf_get_int("ui_last/window_width");
1520 int height = dt_conf_get_int("ui_last/window_height");
1522
1523 gboolean restore_window_position = TRUE;
1524#ifdef GDK_WINDOWING_WAYLAND
1526 if(GDK_IS_WAYLAND_DISPLAY(display))
1528#endif
1529
1531 && dt_conf_key_exists("ui_last/window_x")
1532 && dt_conf_key_exists("ui_last/window_y"))
1533 {
1534 const int x = dt_conf_get_int("ui_last/window_x");
1535 const int y = dt_conf_get_int("ui_last/window_y");
1536
1537 int clamped_x = x;
1538 int clamped_y = y;
1541
1543 {
1544 if(dt_conf_key_exists("ui_last/window_monitor"))
1545 {
1546 const int monitor_index = dt_conf_get_int("ui_last/window_monitor");
1549 }
1550
1551 if(IS_NULL_PTR(monitor))
1553 if(IS_NULL_PTR(monitor))
1557 }
1558
1559 if(!IS_NULL_PTR(monitor))
1560 {
1561 GdkRectangle workarea = { 0 };
1563
1564 const int max_x = workarea.x + MAX(0, workarea.width - width);
1565 const int max_y = workarea.y + MAX(0, workarea.height - height);
1566 clamped_x = CLAMP(x, workarea.x, max_x);
1567 clamped_y = CLAMP(y, workarea.y, max_y);
1568 }
1569
1571 }
1572 }
1573
1575
1580
1581 container = gui->ui->main_window;
1582
1583 // Adding the outermost vbox
1586 gtk_widget_show(widget);
1587
1588 /* connect to signal redraw all */
1591
1592 container = widget;
1593
1594 // Initializing the main table
1596
1597 /* the log message */
1603 dt_gui_add_class(darktable.gui->ui->log_msg, "dt_messages");
1608 //gtk_overlay_reorder_overlay(GTK_OVERLAY(darktable.gui->ui->center_base), eb, -1);
1609
1610 /* the toast message */
1616 g_signal_connect(G_OBJECT(eb), "scroll-event", G_CALLBACK(_scrolled), NULL);
1618
1624
1625 dt_gui_add_class(darktable.gui->ui->toast_msg, "dt_messages");
1630 //gtk_overlay_reorder_overlay(GTK_OVERLAY(darktable.gui->ui->center_base), eb, -1);
1631
1632 /* update log message label */
1635
1636 /* update toast message label */
1639
1640
1641 // Showing everything
1643
1646}
1647
1657
1659{
1661 gtk_container_foreach(GTK_CONTAINER(ui->containers[c]), callback, (gpointer)ui->containers[c]);
1662}
1663
1668
1683
1684/* this is called as a signal handler, the signal raising logic asserts the gdk lock. */
1685static void _ui_widget_redraw_callback(gpointer instance, GtkWidget *widget)
1686{
1687 gtk_widget_queue_draw(widget);
1688}
1689
1690static void _ui_log_redraw_callback(gpointer instance, GtkWidget *widget)
1691{
1692 // draw log message, if any
1693 dt_control_t *const control = dt_control_get_global();
1695 if(!GTK_IS_LABEL(widget))
1696 {
1698 return;
1699 }
1700 if(dt_control_get_global()->log_ack != dt_control_get_global()->log_pos)
1701 {
1702 if(strcmp(dt_control_get_global()->log_message[dt_control_get_global()->log_ack], gtk_label_get_text(GTK_LABEL(widget))))
1704 gtk_widget_show(widget);
1705 }
1706 else
1707 {
1708 gtk_widget_hide(widget);
1709 }
1711}
1712
1713static void _ui_toast_redraw_callback(gpointer instance, GtkWidget *widget)
1714{
1715 // draw toast message, if any
1716 dt_control_t *const control = dt_control_get_global();
1718 if(!GTK_IS_LABEL(widget))
1719 {
1721 return;
1722 }
1723 if(dt_control_get_global()->toast_ack != dt_control_get_global()->toast_pos)
1724 {
1725 if(strcmp(dt_control_get_global()->toast_message[dt_control_get_global()->toast_ack], gtk_label_get_text(GTK_LABEL(widget))))
1726 gtk_label_set_markup(GTK_LABEL(widget), dt_control_get_global()->toast_message[dt_control_get_global()->toast_ack]);
1727 if(!gtk_widget_get_visible(widget))
1728 {
1731 gtk_widget_show(widget);
1732 }
1733 }
1734 else
1735 {
1736 if(gtk_widget_get_visible(widget)) gtk_widget_hide(widget);
1737 }
1739}
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751// TODO: should that go to another place than gtk.c?
1752void dt_gui_add_help_link(GtkWidget *widget, char *link)
1753{
1754 g_object_set_data_full(G_OBJECT(widget), "dt-help-url", link, g_free);
1756}
1757
1758// load a CSS theme
1759void dt_gui_load_theme(const char *theme)
1760{
1761 char theme_css[PATH_MAX] = { 0 };
1762 g_snprintf(theme_css, sizeof(theme_css), "%s.css", theme);
1763
1764 if(!dt_conf_key_exists("use_system_font"))
1765 dt_conf_set_bool("use_system_font", TRUE);
1766
1767 //set font size
1768 if(dt_conf_get_bool("use_system_font"))
1770 else
1771 {
1772 //font name can only use period as decimal separator
1773 //but printf format strings use comma for some locales, so replace comma with period
1774 gchar *font_size = g_strdup_printf(_("%.1f"), dt_conf_get_float("font_size"));
1776 gchar *font_name = g_strdup_printf(_("Sans %s"), font_size_updated);
1781 }
1782
1783 gchar *path, *usercsspath;
1784 char datadir[PATH_MAX] = { 0 }, configdir[PATH_MAX] = { 0 };
1785 dt_loc_get_datadir(datadir, sizeof(datadir));
1786 dt_loc_get_user_config_dir(configdir, sizeof(configdir));
1787
1788 // user dir theme
1789 path = g_build_filename(configdir, "themes", theme_css, NULL);
1791 {
1792 // dt dir theme
1793 dt_free(path);
1794 path = g_build_filename(datadir, "themes", theme_css, NULL);
1796 {
1797 // fallback to default theme
1798 dt_free(path);
1799 path = g_build_filename(datadir, "themes", "ansel.css", NULL);
1800 dt_conf_set_string("ui_last/theme", "ansel");
1801 }
1802 else
1803 dt_conf_set_string("ui_last/theme", theme);
1804 }
1805 else
1806 dt_conf_set_string("ui_last/theme", theme);
1807
1808 GError *error = NULL;
1809
1813
1814 usercsspath = g_build_filename(configdir, "user.css", NULL);
1815
1816 gchar *path_uri = g_filename_to_uri(path, NULL, &error);
1818 fprintf(stderr, "%s: could not convert path %s to URI. Error: %s\n", G_STRFUNC, path, error->message);
1819
1822 fprintf(stderr, "%s: could not convert path %s to URI. Error: %s\n", G_STRFUNC, usercsspath, error->message);
1823
1824 gchar *themecss = NULL;
1826 {
1827 themecss = g_strjoin(NULL, "@import url('", path_uri,
1828 "'); @import url('", usercsspath_uri, "');", NULL);
1829 }
1830 else
1831 {
1832 themecss = g_strjoin(NULL, "@import url('", path_uri, "');", NULL);
1833 }
1834
1837 dt_free(path);
1839
1840 if(dt_conf_get_bool("ui/hide_tooltips"))
1841 {
1842 gchar *newcss = g_strjoin(NULL, themecss, " tooltip {opacity: 0; background: transparent;}", NULL);
1844 themecss = newcss;
1845 }
1846
1848 {
1849 fprintf(stderr, "%s: error parsing combined CSS %s: %s\n", G_STRFUNC, themecss, error->message);
1851 }
1852
1854
1856
1857 // setup the colors
1858
1859 GdkRGBA *c = dt_widget_colors();
1860 GtkWidget *main_window = dt_ui_main_window(darktable.gui->ui);
1862
1863 c[DT_GUI_COLOR_BG] = (GdkRGBA){ 0.1333, 0.1333, 0.1333, 1.0 };
1864
1865 struct color_init
1866 {
1867 const char *name;
1868 GdkRGBA default_col;
1869 } init[DT_GUI_COLOR_LAST] = {
1870 [DT_GUI_COLOR_DARKROOM_BG] = { "darkroom_bg_color", { .2, .2, .2, 1.0 } },
1871 [DT_GUI_COLOR_DARKROOM_PREVIEW_BG] = { "darkroom_preview_bg_color", { .1, .1, .1, 1.0 } },
1872 [DT_GUI_COLOR_LIGHTTABLE_BG] = { "lighttable_bg_color", { .2, .2, .2, 1.0 } },
1873 [DT_GUI_COLOR_LIGHTTABLE_PREVIEW_BG] = { "lighttable_preview_bg_color", { .1, .1, .1, 1.0 } },
1874 [DT_GUI_COLOR_LIGHTTABLE_FONT] = { "lighttable_bg_font_color", { .7, .7, .7, 1.0 } },
1875 [DT_GUI_COLOR_PRINT_BG] = { "print_bg_color", { .2, .2, .2, 1.0 } },
1876 [DT_GUI_COLOR_BRUSH_CURSOR] = { "brush_cursor", { 1., 1., 1., 0.9 } },
1877 [DT_GUI_COLOR_BRUSH_TRACE] = { "brush_trace", { 0., 0., 0., 0.8 } },
1878 [DT_GUI_COLOR_BUTTON_FG] = { "button_fg", { 0.7, 0.7, 0.7, 0.55 } },
1879 [DT_GUI_COLOR_THUMBNAIL_BG] = { "thumbnail_bg_color", { 0.4, 0.4, 0.4, 1.0 } },
1880 [DT_GUI_COLOR_THUMBNAIL_SELECTED_BG] = { "thumbnail_selected_bg_color", { 0.8, 0.8, 0.8, 1.0 } },
1881 [DT_GUI_COLOR_THUMBNAIL_HOVER_BG] = { "thumbnail_hover_bg_color", { 0.65, 0.65, 0.65, 1.0 } },
1882 [DT_GUI_COLOR_THUMBNAIL_OUTLINE] = { "thumbnail_outline_color", { 0.2, 0.2, 0.2, 1.0 } },
1883 [DT_GUI_COLOR_THUMBNAIL_SELECTED_OUTLINE] = { "thumbnail_selected_outline_color", { 0.4, 0.4, 0.4, 1.0 } },
1884 [DT_GUI_COLOR_THUMBNAIL_HOVER_OUTLINE] = { "thumbnail_hover_outline_color", { 0.6, 0.6, 0.6, 1.0 } },
1885 [DT_GUI_COLOR_THUMBNAIL_FONT] = { "thumbnail_font_color", { 0.425, 0.425, 0.425, 1.0 } },
1886 [DT_GUI_COLOR_THUMBNAIL_SELECTED_FONT] = { "thumbnail_selected_font_color", { 0.5, 0.5, 0.5, 1.0 } },
1887 [DT_GUI_COLOR_THUMBNAIL_HOVER_FONT] = { "thumbnail_hover_font_color", { 0.7, 0.7, 0.7, 1.0 } },
1888 [DT_GUI_COLOR_THUMBNAIL_BORDER] = { "thumbnail_border_color", { 0.1, 0.1, 0.1, 1.0 } },
1889 [DT_GUI_COLOR_THUMBNAIL_SELECTED_BORDER] = { "thumbnail_selected_border_color", { 0.9, 0.9, 0.9, 1.0 } },
1890 [DT_GUI_COLOR_FILMSTRIP_BG] = { "filmstrip_bg_color", { 0.2, 0.2, 0.2, 1.0 } },
1891 [DT_GUI_COLOR_PREVIEW_HOVER_BORDER] = { "preview_hover_border_color", { 0.9, 0.9, 0.9, 1.0 } },
1892 [DT_GUI_COLOR_LOG_BG] = { "log_bg_color", { 0.1, 0.1, 0.1, 1.0 } },
1893 [DT_GUI_COLOR_LOG_FG] = { "log_fg_color", { 0.6, 0.6, 0.6, 1.0 } },
1894 [DT_GUI_COLOR_MAP_COUNT_SAME_LOC] = { "map_count_same_loc_color", { 1.0, 1.0, 1.0, 1.0 } },
1895 [DT_GUI_COLOR_MAP_COUNT_DIFF_LOC] = { "map_count_diff_loc_color", { 1.0, 0.85, 0.0, 1.0 } },
1896 [DT_GUI_COLOR_MAP_COUNT_BG] = { "map_count_bg_color", { 0.0, 0.0, 0.0, 1.0 } },
1897 [DT_GUI_COLOR_MAP_LOC_SHAPE_HIGH] = { "map_count_circle_color_h", { 1.0, 1.0, 0.8, 1.0 } },
1898 [DT_GUI_COLOR_MAP_LOC_SHAPE_LOW] = { "map_count_circle_color_l", { 0.0, 0.0, 0.0, 1.0 } },
1899 [DT_GUI_COLOR_MAP_LOC_SHAPE_DEF] = { "map_count_circle_color_d", { 1.0, 0.0, 0.0, 1.0 } },
1900 [DT_GUI_COLOR_WARNING] = { "warning_color", { 1.0, 0.647, 0.0, 1.0 } },
1901 };
1902
1903 // starting from 1 as DT_GUI_COLOR_BG is not part of this table
1904 for(int i = 1; i < DT_GUI_COLOR_LAST; i++)
1905 {
1907 {
1908 c[i] = init[i].default_col;
1909 }
1910 }
1911
1913
1914 // The active theme/font may change the root font size, so refresh the cached em
1915 // that drives DT_GUI_BOX_SPACING.
1917}
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971// draw rounded rectangle
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1991{
1992 // Refocus window, useful if we just closed a popup/modal/transient
1994
1995 // Desperate measure to refocus the window
1999
2000 // dt_view_manager_name() returns the translated display name (e.g. "Table lumineuse"), not the
2001 // internal id: compare against module_name, which is stable and untranslated.
2003 if(!IS_NULL_PTR(current_view) && !strcmp(current_view->module_name, "lighttable"))
2004 {
2006 }
2007 else
2008 {
2010 }
2011
2012 // Be sure to re-enable accelerators
2014}
2015
2016// clang-format off
2017// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
2018// vim: shiftwidth=2 expandtab tabstop=2 cindent
2019// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
2020// clang-format on
void dt_accels_set_top_offset_handler(dt_accels_top_offset_handler_t handler)
gboolean dt_accels_dispatch(GtkWidget *w, GdkEvent *event, gpointer user_data)
Force our listener for all key strokes to bypass reserved Gtk keys.
dt_accels_t * dt_accels_init(char *config_file, GtkAccelFlags flags)
void dt_accels_set_refocus_handler(dt_accels_refocus_handler_t handler)
void dt_accels_set_recent_handlers(dt_accels_recent_get_handler_t get, dt_accels_recent_set_handler_t set)
void dt_accels_set_global(dt_accels_t *accels)
static void dt_accels_disable(dt_accels_t *accels, gboolean state)
static const char * _get_source_name(int pos)
static gboolean _widget_stored_int(const char *key, int *value)
static double _clamp01d(const double value)
static void _accels_recent_set(int index, const char *value)
static gboolean _sample_tablet_state_from_devices(const GdkEvent *event, double *pressure, gboolean *have_pressure, double *tilt_x, double *tilt_y, gboolean *have_tilt, const char **picked_device_name)
static void _ui_widget_redraw_callback(gpointer instance, GtkWidget *widget)
static gint _accels_top_offset(void)
GtkWidget * dt_gui_main_window(void)
static gboolean _button_released(GtkWidget *w, GdkEventButton *event, gpointer user_data)
static dt_control_pointer_input_t _extract_pointer_input(const GdkEvent *event, const double x, const double y, const guint32 time_ms, const gboolean reset_kinematics, const char *tag)
void dt_ui_container_foreach(dt_ui_t *ui, const dt_ui_container_t c, GtkCallback callback)
calls a callback on all children widgets from container
gboolean dt_gui_quit_callback(GtkWidget *widget, GdkEvent *event, gpointer user_data)
static gboolean _center_leave(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
static const gdouble * _event_axes(const GdkEvent *event)
static gint _widget_natural_width(GtkWidget *widget)
static const char * _get_mode_name(int pos)
static void _center_unrealize(GtkWidget *widget, gpointer user_data)
static void _widget_store_bool(const char *key, gboolean value)
static void _widget_message(const char *message)
static gboolean _key_pressed(GtkWidget *w, GdkEventKey *event)
static gboolean _center_enter(GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
struct dt_accels_t * dt_gui_get_accels(void)
static gboolean _ui_toast_button_press_event(GtkWidget *widget, GdkEvent *event, gpointer user_data)
static gboolean _button_pressed(GtkWidget *w, GdkEventButton *event, gpointer user_data)
static dt_tablet_motion_state_t _tablet_motion_state
static gchar * _accels_recent_get(int index)
static gboolean _draw(GtkWidget *da, cairo_t *cr, gpointer user_data)
static gboolean _ui_log_button_press_event(GtkWidget *widget, GdkEvent *event, gpointer user_data)
static void _notify_profile_changed(void)
void dt_ui_container_destroy_children(dt_ui_t *ui, const dt_ui_container_t c)
destroy all child widgets from container
struct dt_ui_t * dt_gui_get_ui(void)
static gboolean _widget_stored_bool(const char *key)
static LRESULT CALLBACK _center_win32_cursor_proc(HWND hwnd, UINT message, WPARAM w_param, LPARAM l_param, UINT_PTR subclass_id, DWORD_PTR ref_data)
void dt_gui_load_theme(const char *theme)
static void _widget_cursor(GdkCursorType cursor)
int dt_gui_gtk_write_config()
void dt_gui_store_last_preset(const char *name)
void dt_configure_ppd_dpi(dt_gui_gtk_t *gui)
GtkWidget * dt_gui_center_widget(void)
static gboolean _get_axis_value_for_source(const GdkEvent *event, GdkDevice *source_device, const GdkAxisUse axis, double *value, gboolean *from_source_map)
static const char * _get_axis_name(int pos)
void dt_gui_gtk_quit()
static void _init_widgets(dt_gui_gtk_t *gui)
static void _update_display_profile(void)
static gboolean _mouse_moved(GtkWidget *w, GdkEventMotion *event, gpointer user_data)
#define DT_ACCEL_SEARCH_RECENT_KEY
void dt_gui_add_help_link(GtkWidget *widget, char *link)
#define DT_WIN32_CURSOR_SUBCLASS_CENTER
void dt_ui_notify_user()
draw user's attention
static void _notebook_page_changed(gpointer owner)
static gboolean _focus_in_out_event(GtkWidget *widget, GdkEvent *event, gpointer user_data)
static void _ui_log_redraw_callback(gpointer instance, GtkWidget *widget)
int dt_gui_gtk_init(dt_gui_gtk_t *gui)
static void _widget_store_int(const char *key, int value)
void dt_gui_refocus_center()
static void _gui_refresh_thumbnail(int32_t imgid, gboolean refresh_filmstrip)
static void _refresh_all_container_spacing(void)
static gint _last_box_spacing
void dt_gui_set_pango_resolution(PangoLayout *layout)
static gboolean _configure(GtkWidget *da, GdkEventConfigure *event, gpointer user_data)
static gboolean _scrolled(GtkWidget *widget, GdkEventScroll *event, gpointer user_data)
static void _ui_toast_redraw_callback(gpointer instance, GtkWidget *widget)
void dt_gui_gtk_run(dt_gui_gtk_t *gui)
static GtkWidget * _widget_root_window(void)
static void _refresh_container_spacing(GtkWidget *w, gpointer user_data)
static gboolean _window_configure(GtkWidget *da, GdkEvent *event, gpointer user_data)
static void _center_realize(GtkWidget *widget, gpointer user_data)
static void _gui_startup_progress(const char *message)
void dt_ui_container_focus_widget(dt_ui_t *ui, const dt_ui_container_t c, GtkWidget *w)
gives a widget focus in the container
static gboolean _sample_axis_from_device_state(GdkWindow *window, GdkDevice *device, const GdkAxisUse axis, double *value)
void dt_gui_update_em(void)
static void error(char *msg)
Definition ashift_lsd.c:202
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void init(dt_imageio_module_format_t *self)
Definition avif.c:157
uint32_t container(dt_lib_module_t *self)
void dt_collection_gui_register_handlers(void)
@ DT_COLORLABELS_PURPLE
Definition colorlabels.h:46
@ DT_COLORLABELS_LAST
Definition colorlabels.h:47
@ DT_COLORLABELS_RED
Definition colorlabels.h:42
static const float x
void dt_colorspaces_set_profile_changed_handler(dt_colorspaces_profile_changed_handler_t handler)
Register the one callback fired when the display profile changes.
void dt_colorspaces_set_display_profile(const dt_colorspaces_color_profile_type_t profile_type, GtkWidget *widget)
Refresh the cached display profile from the monitor showing widget (X atom, colord,...
The colour-profile module's API: which profiles exist, and how to apply one.
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
void dt_conf_set_bool(const char *name, int val)
int dt_conf_get_bool(const char *name)
int dt_conf_key_exists(const char *key)
float dt_conf_get_float(const char *name)
gchar * dt_conf_get_string(const char *name)
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
void dt_conf_set_string(const char *name, const char *val)
const char * dt_conf_get_string_const(const char *name)
void dt_gui_container_destroy_children(GtkContainer *container)
Definition container.c:80
void dt_control_key_pressed(GdkEventKey *event)
Definition control.c:632
void dt_control_button_pressed(double x, double y, double pressure, int which, int type, uint32_t state)
Definition control.c:719
void * dt_control_expose(void *voidptr)
Definition control.c:547
void dt_toast_log(const char *msg,...)
Definition control.c:821
void dt_control_quit()
Definition control.c:436
void dt_control_mouse_leave()
Definition control.c:617
gboolean dt_control_configure(GtkWidget *da, GdkEventConfigure *event, gpointer user_data)
Definition control.c:499
void dt_control_button_released(double x, double y, int which, uint32_t state)
Definition control.c:637
void dt_control_set_pointer_input(const dt_control_pointer_input_t *input)
Definition control.c:76
int dt_control_running()
Definition control.c:426
void dt_control_mouse_moved(double x, double y, double pressure, int which)
Definition control.c:627
void dt_control_mouse_enter()
Definition control.c:622
#define dt_control_change_cursor(cursor)
Definition control.h:119
struct dt_control_t * dt_control_get_global(void)
Definition darktable.c:693
int dt_load_from_string(const gchar *input, gboolean open_image_in_dr, gboolean *single_image)
Definition darktable.c:390
void dt_cleanup()
Definition darktable.c:1840
darktable_t darktable
Definition darktable.c:215
struct dt_gui_gtk_t * dt_gui_get_global(void)
Definition darktable.c:523
static int dt_pthread_mutex_unlock(dt_pthread_mutex_t *mutex) RELEASE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:385
static int dt_pthread_mutex_init(dt_pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Definition dtpthread.h:370
static int dt_pthread_mutex_lock(dt_pthread_mutex_t *mutex) ACQUIRE(mutex) NO_THREAD_SAFETY_ANALYSIS
Definition dtpthread.h:375
void dt_loc_get_sharedir(char *sharedir, size_t bufsize)
void dt_loc_get_datadir(char *datadir, size_t bufsize)
void dt_loc_get_user_config_dir(char *configdir, size_t bufsize)
void dt_film_gui_register_handlers(void)
Definition film_gui.c:107
void dt_folder_survey_gui_register_handlers()
void dt_gui_presets_init()
void dt_guides_set_overlay_colors()
Definition guides.c:668
void dt_history_merge_gui_register_handlers(void)
const char * dt_l10n_get_current_lang(dt_l10n_t *l10n)
Definition l10n.c:456
struct dt_l10n_t * dt_l10n_get_global(void)
Definition darktable.c:668
@ DT_DEBUG_INPUT
Definition logging.h:50
@ DT_DEBUG_CONTROL
Definition logging.h:37
@ DT_DEBUG_MASKS
Definition logging.h:48
int32_t dt_get_debug_flags(void)
Definition darktable.c:2075
void dt_print(dt_debug_thread_t thread, const char *msg,...) __attribute__((format(printf
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition macros.h:65
#define dt_free(ptr)
Definition mem_alloc.h:97
char * key
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
size_t size
Definition mipmap_cache.c:3
dt_mipmap_buffer_dsc_flags flags
Definition mipmap_cache.c:4
void dt_osx_focus_window()
Definition osx.mm:302
void dt_osx_autoset_dpi(GtkWidget *widget)
Definition osx.mm:55
void dt_concat_path_file(char destination[4096], const char path[4096], const char *const file)
Append a constant filename to a variable, stack-based, fixed-sized, directory, and add a / in-between...
#define PATH_MAX
Definition paths.h:45
const char * name
Definition pdf.h:90
@ DT_COLORSPACE_DISPLAY
The monitor profile – the one list entry that mutates after init.
void dt_screen_set_dpi(double dpi)
static cairo_surface_t * dt_cairo_image_surface_create(cairo_format_t format, int width, int height)
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:382
struct dt_control_signal_t * dt_control_signal_get_global(void)
Definition darktable.c:648
@ DT_SIGNAL_CONTROL_REDRAW_ALL
This signal is raised when dt_control_queue_redraw() is called. no param, no returned value.
Definition signal.h:72
@ DT_SIGNAL_CONTROL_TOAST_REDRAW
This signal is raised when dt_control_toast_redraw() is called. no param, no returned value.
Definition signal.h:291
@ DT_SIGNAL_CONTROL_NOTEBOOK_TAB_CHANGED
This signal is raised when a GtkNotebook created/registered via dt_ui_notebook_set_picker_owner() swi...
Definition signal.h:305
@ DT_SIGNAL_CONTROL_PROFILE_CHANGED
This signal is raised when the screen profile has changed no param, no returned value.
Definition signal.h:239
@ DT_SIGNAL_CONTROL_LOG_REDRAW
This signal is raised when dt_control_log_redraw() is called. no param, no returned value.
Definition signal.h:286
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:392
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
void dt_gui_splash_init(void)
Definition splash.c:512
void dt_gui_splash_updatef(const char *format,...)
Definition splash.c:687
void dt_gui_splash_set_transient_for(GtkWidget *parent)
Definition splash.c:82
void dt_startup_progress_set_handler(dt_startup_progress_handler_t handler)
struct dt_gui_gtk_t * gui
Definition darktable.h:186
struct dt_view_manager_t * view_manager
Definition darktable.h:183
double tabborder
Definition control.h:220
dt_pthread_mutex_t log_mutex
Definition control.h:247
dt_pthread_mutex_t toast_mutex
Definition control.h:254
struct dt_gui_gtk_t::@33 export_popup
int32_t center_tooltip
cairo_surface_t * surface
dt_accels_t * accels
GtkMenu * presets_popup_menu
double dpi_factor
dt_ui_t * ui
dt_pthread_mutex_t mutex
gboolean culling_mode
char gtkrc[PATH_MAX]
GtkWidget * window
cairo_filter_t filter_image
char * last_preset
struct dt_gui_gtk_t::@34 styles_popup
GtkWidget * grid
Definition thumbtable.h:110
dt_thumbtable_t * thumbtable_lighttable
GtkWidget * containers[DT_UI_CONTAINER_SIZE]
GtkWidget * main_window
GtkWidget * center_base
GtkWidget * log_msg
dt_thumbtable_t * thumbtable_filmstrip
GtkWidget * center
GtkWidget * panels[DT_UI_PANEL_SIZE]
GtkWidget * toast_msg
char module_name[64]
Definition view.h:154
GtkWidget * window
typedef double((*spd)(unsigned long int wavelength, double TempK))
#define MIN(a, b)
Definition thinplate.c:32
#define MAX(a, b)
Definition thinplate.c:29
void dt_thumbnail_notify_set_handler(dt_thumbnail_refresh_handler_t handler)
A widget to manage and display image thumbnails in Ansel's lighttable and filmstrip views.
#define dt_thumbtable_refresh_thumbnail(table, imgid, reinit)
Definition thumbtable.h:293
gchar * dt_util_str_replace(const gchar *string, const gchar *pattern, const gchar *substitute)
Definition utility.c:139
void dt_view_manager_configure(dt_view_manager_t *vm, int width, int height)
Definition view.c:627
int dt_view_manager_scrolled(dt_view_manager_t *vm, double x, double y, int up, int state, int delta_y)
Definition view.c:639
const dt_view_t * dt_view_manager_get_current_view(dt_view_manager_t *vm)
Definition view.c:139
void dt_widget_set_ppd(double ppd)
void dt_widget_set_cursor_handler(dt_widget_cursor_handler_t handler)
gboolean dt_gui_get_scroll_unit_delta(const GdkEventScroll *event, int *delta)
void dt_widget_set_debug_overlays(gboolean enabled)
void dt_widget_set_dpi_factor(double factor)
void dt_widget_set_image_filter(cairo_filter_t filter)
void dt_widget_set_scroll_reversed(gboolean reverse_x, gboolean reverse_y)
GdkRGBA * dt_widget_colors(void)
void dt_gui_freeze_reset(void)
void dt_widget_set_theme_loaded(gboolean loaded)
void dt_widget_set_natural_width_handler(dt_widget_natural_width_handler_t handler)
void dt_widget_set_root_window_handler(dt_widget_root_window_handler_t handler)
void dt_widget_set_em_size(double em)
void dt_widget_set_gui_thread(pthread_t thread)
void dt_widget_set_notebook_page_handler(dt_widget_notebook_page_handler_t handler)
void dt_widget_set_refocus_handler(dt_widget_refocus_handler_t handler)
GdkEventMask dt_widget_scroll_mask(void)
void dt_widget_set_scroll_focus(GtkWidget *widget)
double dt_get_system_gui_ppd(GtkWidget *widget)
void dt_widget_set_storage_handlers(dt_widget_stored_int_getter_t get_int, dt_widget_stored_int_setter_t set_int, dt_widget_stored_bool_getter_t get_bool, dt_widget_stored_bool_setter_t set_bool)
void dt_widget_set_message_handler(dt_widget_message_handler_t handler)
void dt_widget_set_mouse_radius(float radius, float clamped)
void dt_widget_set_scroll_mask(GdkEventMask mask)
@ DT_GUI_COLOR_BG
@ DT_GUI_COLOR_DARKROOM_PREVIEW_BG
@ DT_GUI_COLOR_LIGHTTABLE_FONT
@ DT_GUI_COLOR_MAP_LOC_SHAPE_HIGH
@ DT_GUI_COLOR_LOG_FG
@ DT_GUI_COLOR_BRUSH_CURSOR
@ DT_GUI_COLOR_LAST
@ DT_GUI_COLOR_THUMBNAIL_FONT
@ DT_GUI_COLOR_THUMBNAIL_SELECTED_BG
@ DT_GUI_COLOR_MAP_COUNT_BG
@ DT_GUI_COLOR_LOG_BG
@ DT_GUI_COLOR_THUMBNAIL_SELECTED_BORDER
@ DT_GUI_COLOR_THUMBNAIL_OUTLINE
@ DT_GUI_COLOR_MAP_LOC_SHAPE_LOW
@ DT_GUI_COLOR_LIGHTTABLE_BG
@ DT_GUI_COLOR_BUTTON_FG
@ DT_GUI_COLOR_DARKROOM_BG
@ DT_GUI_COLOR_THUMBNAIL_BORDER
@ DT_GUI_COLOR_LIGHTTABLE_PREVIEW_BG
@ DT_GUI_COLOR_WARNING
@ DT_GUI_COLOR_MAP_COUNT_SAME_LOC
@ DT_GUI_COLOR_MAP_COUNT_DIFF_LOC
@ DT_GUI_COLOR_THUMBNAIL_BG
@ DT_GUI_COLOR_PREVIEW_HOVER_BORDER
@ DT_GUI_COLOR_FILMSTRIP_BG
@ DT_GUI_COLOR_BRUSH_TRACE
@ DT_GUI_COLOR_THUMBNAIL_SELECTED_FONT
@ DT_GUI_COLOR_THUMBNAIL_SELECTED_OUTLINE
@ DT_GUI_COLOR_THUMBNAIL_HOVER_BG
@ DT_GUI_COLOR_PRINT_BG
@ DT_GUI_COLOR_MAP_LOC_SHAPE_DEF
@ DT_GUI_COLOR_THUMBNAIL_HOVER_FONT
@ DT_GUI_COLOR_THUMBNAIL_HOVER_OUTLINE
#define DT_UI_SCALE_DEVICE(value)
@ DT_WIDGET_COLORLABEL_RED
@ DT_WIDGET_COLORLABEL_PURPLE
@ DT_WIDGET_COLORLABEL_COUNT
#define DT_GUI_BOX_SPACING
#define DT_PIXEL_APPLY_DPI(value)
void dt_gui_add_class(GtkWidget *widget, const gchar *class_name)
GtkWidget * dt_ui_toast_msg(dt_ui_t *ui)
GtkWidget * dt_ui_center(dt_ui_t *ui)
void dt_ui_cleanup_titlebar(dt_ui_t *ui)
void dt_ui_init_titlebar(dt_ui_t *ui)
int dt_ui_panel_get_size(dt_ui_t *ui, const dt_ui_panel_t p)
gboolean dt_ui_panel_ancestor(dt_ui_t *ui, const dt_ui_panel_t p, GtkWidget *w)
GtkWidget * dt_ui_log_msg(dt_ui_t *ui)
GtkWidget * dt_ui_main_window(dt_ui_t *ui)
void dt_ui_init_main_table(GtkWidget *parent, dt_ui_t *ui)
GtkWidget * dt_ui_center_base(dt_ui_t *ui)
dt_ui_container_t
@ DT_UI_PANEL_TOP
@ DT_UI_PANEL_LEFT
@ DT_UI_PANEL_RIGHT