1 /*
2 This file is part of darktable,
3 Copyright (C) 2009-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 #pragma once
20
21 #include "common/darktable.h"
22 #include "common/dtpthread.h"
23
24 #include <gtk/gtk.h>
25 #include <stdint.h>
26
27 #define DT_GUI_IOP_MODULE_CONTROL_SPACING 0
28
29 #define DT_GUI_THUMBSIZE_REDUCE 0.7f
30
31 /* helper macro that applies the DPI transformation to fixed pixel values. input should be defaulting to 96
32 * DPI */
33 #define DT_PIXEL_APPLY_DPI(value) ((value) * darktable.gui->dpi_factor)
34
35 typedef struct dt_gui_widgets_t
36 {
37
38 // Borders
39 GtkWidget *left_border;
40 GtkWidget *right_border;
41 GtkWidget *bottom_border;
42 GtkWidget *top_border;
43
44 /* left panel */
45 GtkGrid *panel_left; // panel grid 3 rows, top,center,bottom and file on center
46 GtkGrid *panel_right;
47
48 /* resize of left/right panels */
49 gboolean panel_handle_dragging;
50 int panel_handle_x, panel_handle_y;
51 } dt_gui_widgets_t;
52
53 typedef struct dt_gui_scrollbars_t
54 {
55 GtkWidget *vscrollbar;
56 GtkWidget *hscrollbar;
57
58 gboolean visible;
59 gboolean dragging;
60 } dt_gui_scrollbars_t;
61
62 typedef enum dt_gui_color_t
63 {
64 DT_GUI_COLOR_BG = 0,
65 DT_GUI_COLOR_DARKROOM_BG,
66 DT_GUI_COLOR_DARKROOM_PREVIEW_BG,
67 DT_GUI_COLOR_LIGHTTABLE_BG,
68 DT_GUI_COLOR_LIGHTTABLE_PREVIEW_BG,
69 DT_GUI_COLOR_LIGHTTABLE_FONT,
70 DT_GUI_COLOR_PRINT_BG,
71 DT_GUI_COLOR_BRUSH_CURSOR,
72 DT_GUI_COLOR_BRUSH_TRACE,
73 DT_GUI_COLOR_THUMBNAIL_BG,
74 DT_GUI_COLOR_THUMBNAIL_SELECTED_BG,
75 DT_GUI_COLOR_THUMBNAIL_HOVER_BG,
76 DT_GUI_COLOR_THUMBNAIL_OUTLINE,
77 DT_GUI_COLOR_THUMBNAIL_SELECTED_OUTLINE,
78 DT_GUI_COLOR_THUMBNAIL_HOVER_OUTLINE,
79 DT_GUI_COLOR_THUMBNAIL_FONT,
80 DT_GUI_COLOR_THUMBNAIL_SELECTED_FONT,
81 DT_GUI_COLOR_THUMBNAIL_HOVER_FONT,
82 DT_GUI_COLOR_THUMBNAIL_BORDER,
83 DT_GUI_COLOR_THUMBNAIL_SELECTED_BORDER,
84 DT_GUI_COLOR_FILMSTRIP_BG,
85 DT_GUI_COLOR_CULLING_SELECTED_BORDER,
86 DT_GUI_COLOR_CULLING_FILMSTRIP_SELECTED_BORDER,
87 DT_GUI_COLOR_PREVIEW_HOVER_BORDER,
88 DT_GUI_COLOR_LOG_BG,
89 DT_GUI_COLOR_LOG_FG,
90 DT_GUI_COLOR_MAP_COUNT_SAME_LOC,
91 DT_GUI_COLOR_MAP_COUNT_DIFF_LOC,
92 DT_GUI_COLOR_MAP_COUNT_BG,
93 DT_GUI_COLOR_MAP_LOC_SHAPE_HIGH,
94 DT_GUI_COLOR_MAP_LOC_SHAPE_LOW,
95 DT_GUI_COLOR_MAP_LOC_SHAPE_DEF,
96 DT_GUI_COLOR_LAST
97 } dt_gui_color_t;
98
99 typedef struct dt_gui_gtk_t
100 {
101
102 struct dt_ui_t *ui;
103
104 dt_gui_widgets_t widgets;
105
106 dt_gui_scrollbars_t scrollbars;
107
108 cairo_surface_t *surface;
109 GtkMenu *presets_popup_menu;
110 char *last_preset;
111
112 int32_t reset;
113 GdkRGBA colors[DT_GUI_COLOR_LAST];
114
115 int32_t center_tooltip; // 0 = no tooltip, 1 = new tooltip, 2 = old tooltip
116
117 gboolean grouping;
118 int32_t expanded_group_id;
119
120 gboolean show_overlays;
121 gboolean show_focus_peaking;
122 GtkWidget *focus_peaking_button;
123
124 double dpi, dpi_factor, ppd, ppd_thb;
125
126 int icon_size; // size of top panel icons
127
128 // store which gtkrc we loaded:
129 char gtkrc[PATH_MAX];
130
131 GtkWidget *scroll_to[2]; // one for left, one for right
132
133 gint scroll_mask;
134 guint sidebar_scroll_mask;
135
136 cairo_filter_t filter_image; // filtering used for all modules expect darkroom
137 cairo_filter_t dr_filter_image; // filtering used in the darkroom
138
139 dt_pthread_mutex_t mutex;
140 } dt_gui_gtk_t;
141
dt_cairo_image_surface_create(cairo_format_t format,int width,int height)142 static inline cairo_surface_t *dt_cairo_image_surface_create(cairo_format_t format, int width, int height) {
143 cairo_surface_t *cst = cairo_image_surface_create(format, width * darktable.gui->ppd, height * darktable.gui->ppd);
144 cairo_surface_set_device_scale(cst, darktable.gui->ppd, darktable.gui->ppd);
145 return cst;
146 }
147
dt_cairo_image_surface_create_for_data(unsigned char * data,cairo_format_t format,int width,int height,int stride)148 static inline cairo_surface_t *dt_cairo_image_surface_create_for_data(unsigned char *data, cairo_format_t format, int width, int height, int stride) {
149 cairo_surface_t *cst = cairo_image_surface_create_for_data(data, format, width, height, stride);
150 cairo_surface_set_device_scale(cst, darktable.gui->ppd, darktable.gui->ppd);
151 return cst;
152 }
153
dt_cairo_image_surface_create_from_png(const char * filename)154 static inline cairo_surface_t *dt_cairo_image_surface_create_from_png(const char *filename) {
155 cairo_surface_t *cst = cairo_image_surface_create_from_png(filename);
156 cairo_surface_set_device_scale(cst, darktable.gui->ppd, darktable.gui->ppd);
157 return cst;
158 }
159
dt_cairo_image_surface_get_width(cairo_surface_t * surface)160 static inline int dt_cairo_image_surface_get_width(cairo_surface_t *surface) {
161 return cairo_image_surface_get_width(surface) / darktable.gui->ppd;
162 }
163
dt_cairo_image_surface_get_height(cairo_surface_t * surface)164 static inline int dt_cairo_image_surface_get_height(cairo_surface_t *surface) {
165 return cairo_image_surface_get_height(surface) / darktable.gui->ppd;
166 }
167
dt_gdk_cairo_surface_create_from_pixbuf(const GdkPixbuf * pixbuf,int scale,GdkWindow * for_window)168 static inline cairo_surface_t *dt_gdk_cairo_surface_create_from_pixbuf(const GdkPixbuf *pixbuf, int scale, GdkWindow *for_window) {
169 cairo_surface_t *cst = gdk_cairo_surface_create_from_pixbuf(pixbuf, scale, for_window);
170 cairo_surface_set_device_scale(cst, darktable.gui->ppd, darktable.gui->ppd);
171 return cst;
172 }
173
dt_gdk_pixbuf_new_from_file_at_size(const char * filename,int width,int height,GError ** error)174 static inline GdkPixbuf *dt_gdk_pixbuf_new_from_file_at_size(const char *filename, int width, int height, GError **error) {
175 return gdk_pixbuf_new_from_file_at_size(filename, width * darktable.gui->ppd, height * darktable.gui->ppd, error);
176 }
177
178 int dt_gui_gtk_init(dt_gui_gtk_t *gui);
179 void dt_gui_gtk_run(dt_gui_gtk_t *gui);
180 void dt_gui_gtk_cleanup(dt_gui_gtk_t *gui);
181 void dt_gui_gtk_quit();
182 void dt_gui_store_last_preset(const char *name);
183 int dt_gui_gtk_load_config();
184 int dt_gui_gtk_write_config();
185 void dt_gui_gtk_set_source_rgb(cairo_t *cr, dt_gui_color_t);
186 void dt_gui_gtk_set_source_rgba(cairo_t *cr, dt_gui_color_t, float opacity_coef);
187 double dt_get_system_gui_ppd(GtkWidget *widget);
188
189 /* Check sidebar_scroll_default and modifier keys to determine if scroll event
190 * should be processed by control or by panel. If default is panel scroll but
191 * modifiers are pressed to indicate the control should be scrolled, then remove
192 * the modifiers from the event before returning false */
193 gboolean dt_gui_ignore_scroll(GdkEventScroll *event);
194 /* Return requested scroll delta(s) from event. If delta_x or delta_y
195 * is NULL, do not return that delta. Return TRUE if requested deltas
196 * can be retrieved. Handles both GDK_SCROLL_UP/DOWN/LEFT/RIGHT and
197 * GDK_SCROLL_SMOOTH style scroll events. */
198 gboolean dt_gui_get_scroll_deltas(const GdkEventScroll *event, gdouble *delta_x, gdouble *delta_y);
199 /* Same as above, except accumulate smooth scrolls deltas of < 1 and
200 * only set deltas and return TRUE once scrolls accumulate to >= 1.
201 * Effectively makes smooth scroll events act like old-style unit
202 * scroll events. */
203 gboolean dt_gui_get_scroll_unit_deltas(const GdkEventScroll *event, int *delta_x, int *delta_y);
204
205 /* Note that on macOS Shift+vertical scroll can be reported as Shift+horizontal scroll.
206 * So if Shift changes scrolling effect, both scrolls should be handled the same.
207 * For this case (or if it's otherwise useful) use the following 2 functions. */
208
209 /* Return sum of scroll deltas from event. Return TRUE if any deltas
210 * can be retrieved. Handles both GDK_SCROLL_UP/DOWN/LEFT/RIGHT and
211 * GDK_SCROLL_SMOOTH style scroll events. */
212 gboolean dt_gui_get_scroll_delta(const GdkEventScroll *event, gdouble *delta);
213 /* Same as above, except accumulate smooth scrolls deltas of < 1 and
214 * only set delta and return TRUE once scrolls accumulate to >= 1.
215 * Effectively makes smooth scroll events act like old-style unit
216 * scroll events. */
217 gboolean dt_gui_get_scroll_unit_delta(const GdkEventScroll *event, int *delta);
218
219 /** block any keyaccelerators when widget have focus, block is released when widget lose focus. */
220 void dt_gui_key_accel_block_on_focus_connect(GtkWidget *w);
221 /** clean up connected signal handlers before destroying your widget: */
222 void dt_gui_key_accel_block_on_focus_disconnect(GtkWidget *w);
223
224 /*
225 * new ui api
226 */
227
228
229 typedef enum dt_ui_container_t
230 {
231 /* the top container of left panel, the top container
232 disables the module expander and does not scroll with other modules
233 */
234 DT_UI_CONTAINER_PANEL_LEFT_TOP = 0,
235
236 /* the center container of left panel, the center container
237 contains the scrollable area that all plugins are placed within and last
238 widget is the end marker.
239 This container will always expand|fill empty vertical space
240 */
241 DT_UI_CONTAINER_PANEL_LEFT_CENTER = 1,
242
243 /* the bottom container of left panel, this container works just like
244 the top container but will be attached to bottom in the panel, such as
245 plugins like background jobs module in lighttable and the plugin selection
246 module in darkroom,
247 */
248 DT_UI_CONTAINER_PANEL_LEFT_BOTTOM = 2,
249
250 DT_UI_CONTAINER_PANEL_RIGHT_TOP = 3,
251 DT_UI_CONTAINER_PANEL_RIGHT_CENTER = 4,
252 DT_UI_CONTAINER_PANEL_RIGHT_BOTTOM = 5,
253
254
255 /* the top header bar, left slot where darktable name is placed */
256 DT_UI_CONTAINER_PANEL_TOP_LEFT = 6,
257 /* center which is expanded as wide it can */
258 DT_UI_CONTAINER_PANEL_TOP_CENTER = 7,
259 /* right side were the different views are accessed */
260 DT_UI_CONTAINER_PANEL_TOP_RIGHT = 8,
261
262 DT_UI_CONTAINER_PANEL_CENTER_TOP_LEFT = 9,
263 DT_UI_CONTAINER_PANEL_CENTER_TOP_CENTER = 10,
264 DT_UI_CONTAINER_PANEL_CENTER_TOP_RIGHT = 11,
265
266 DT_UI_CONTAINER_PANEL_CENTER_BOTTOM_LEFT = 12,
267 DT_UI_CONTAINER_PANEL_CENTER_BOTTOM_CENTER = 13,
268 DT_UI_CONTAINER_PANEL_CENTER_BOTTOM_RIGHT = 14,
269
270 /* this panel is placed at bottom of ui
271 only used by the filmstrip if shown */
272 DT_UI_CONTAINER_PANEL_BOTTOM = 15,
273
274 /* Count of containers */
275 DT_UI_CONTAINER_SIZE
276 } dt_ui_container_t;
277
278 typedef enum dt_ui_panel_t
279 {
280 /* the header panel */
281 DT_UI_PANEL_TOP,
282 /* center top toolbar panel */
283 DT_UI_PANEL_CENTER_TOP,
284 /* center bottom toolbar panel */
285 DT_UI_PANEL_CENTER_BOTTOM,
286 /* left panel */
287 DT_UI_PANEL_LEFT,
288 /* right panel */
289 DT_UI_PANEL_RIGHT,
290 /* bottom panel */
291 DT_UI_PANEL_BOTTOM,
292
293 DT_UI_PANEL_SIZE
294 } dt_ui_panel_t;
295
296 typedef enum dt_ui_border_t
297 {
298 DT_UI_BORDER_TOP,
299 DT_UI_BORDER_BOTTOM,
300 DT_UI_BORDER_LEFT,
301 DT_UI_BORDER_RIGHT,
302
303 DT_UI_BORDER_SIZE
304 } dt_ui_border_t;
305
306 /** \brief add's a widget to a defined container */
307 void dt_ui_container_add_widget(struct dt_ui_t *ui, const dt_ui_container_t c, GtkWidget *w);
308 /** \brief gives a widget focus in the container */
309 void dt_ui_container_focus_widget(struct dt_ui_t *ui, const dt_ui_container_t c, GtkWidget *w);
310 /** \brief calls a callback on all children widgets from container */
311 void dt_ui_container_foreach(struct dt_ui_t *ui, const dt_ui_container_t c, GtkCallback callback);
312 /** \brief destroy all child widgets from container */
313 void dt_ui_container_destroy_children(struct dt_ui_t *ui, const dt_ui_container_t c);
314 /** \brief shows/hide a panel */
315 void dt_ui_panel_show(struct dt_ui_t *ui, const dt_ui_panel_t, gboolean show, gboolean write);
316 /** \brief restore saved state of panel visibility for current view */
317 void dt_ui_restore_panels(struct dt_ui_t *ui);
318 /** \brief update scrollbars for current view */
319 void dt_ui_update_scrollbars(struct dt_ui_t *ui);
320 /** show or hide scrollbars */
321 void dt_ui_scrollbars_show(struct dt_ui_t *ui, gboolean show);
322 /** \brief toggle view of panels eg. collaps/expands to previous view state */
323 void dt_ui_toggle_panels_visibility(struct dt_ui_t *ui);
324 /** \brief draw user's attention */
325 void dt_ui_notify_user();
326 /** \brief get visible state of panel */
327 gboolean dt_ui_panel_visible(struct dt_ui_t *ui, const dt_ui_panel_t);
328 /** \brief get width of right, left, or bottom panel */
329 int dt_ui_panel_get_size(struct dt_ui_t *ui, const dt_ui_panel_t p);
330 /** \brief set width of right, left, or bottom panel */
331 void dt_ui_panel_set_size(struct dt_ui_t *ui, const dt_ui_panel_t p, int s);
332 /** \brief get the center drawable widget */
333 GtkWidget *dt_ui_center(struct dt_ui_t *ui);
334 GtkWidget *dt_ui_center_base(struct dt_ui_t *ui);
335 /** \brief get the main window widget */
336 GtkWidget *dt_ui_main_window(struct dt_ui_t *ui);
337 /** \brief get the thumb table */
338 struct dt_thumbtable_t *dt_ui_thumbtable(struct dt_ui_t *ui);
339 /** \brief get the log message widget */
340 GtkWidget *dt_ui_log_msg(struct dt_ui_t *ui);
341 /** \brief get the toast message widget */
342 GtkWidget *dt_ui_toast_msg(struct dt_ui_t *ui);
343
344 GtkBox *dt_ui_get_container(struct dt_ui_t *ui, const dt_ui_container_t c);
345
346 /* activate ellipsization of the combox entries */
347 void dt_ellipsize_combo(GtkComboBox *cbox);
348
dt_ui_section_label_set(GtkWidget * label)349 static inline void dt_ui_section_label_set(GtkWidget *label)
350 {
351 gtk_widget_set_halign(label, GTK_ALIGN_FILL); // make it span the whole available width
352 gtk_label_set_xalign (GTK_LABEL(label), 0.5f);
353 gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END); // ellipsize labels
354 gtk_widget_set_name(label, "section_label"); // make sure that we can style these easily
355 }
356
dt_ui_section_label_new(const gchar * str)357 static inline GtkWidget *dt_ui_section_label_new(const gchar *str)
358 {
359 GtkWidget *label = gtk_label_new(str);
360 dt_ui_section_label_set(label);
361 return label;
362 };
363
dt_ui_label_new(const gchar * str)364 static inline GtkWidget *dt_ui_label_new(const gchar *str)
365 {
366 GtkWidget *label = gtk_label_new(str);
367 gtk_widget_set_halign(label, GTK_ALIGN_START);
368 gtk_label_set_xalign (GTK_LABEL(label), 0.0f);
369 gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END);
370 return label;
371 };
372
373 // clears all the pages of the notebook
374 void dt_ui_notebook_clear(GtkNotebook *notebook);
375
376 GtkWidget *dt_ui_notebook_page(GtkNotebook *notebook, const char *text, const char *tooltip);
377
378 // show a dialog box with 2 buttons in case some user interaction is required BEFORE dt's gui is initialised.
379 // this expects gtk_init() to be called already which should be the case during most of dt's init phase.
380 gboolean dt_gui_show_standalone_yes_no_dialog(const char *title, const char *markup, const char *no_text,
381 const char *yes_text);
382
383 // similar to the one above. this one asks the user for some string. the hint is shown in the empty entry box
384 char *dt_gui_show_standalone_string_dialog(const char *title, const char *markup, const char *placeholder,
385 const char *no_text, const char *yes_text);
386
387 void *dt_gui_show_splashscreen();
388 void dt_gui_close_splashscreen(void *splashscreen);
389
390 void dt_gui_add_help_link(GtkWidget *widget, const char *link);
391
392 // load a CSS theme
393 void dt_gui_load_theme(const char *theme);
394
395 // reload GUI scalings
396 void dt_configure_ppd_dpi(dt_gui_gtk_t *gui);
397
398 // translate key press events to remove any modifiers used to produce the keyval
399 // for example when the shift key is used to create the asterisk character
400 guint dt_gui_translated_key_state(GdkEventKey *event);
401
402 // return modifier keys currently pressed, independent of any key event
403 GdkModifierType dt_key_modifier_state();
404
405 // create an ellipsized button with label, tooltip and help link
dt_ui_button_new(const gchar * label,const gchar * tooltip,const gchar * help)406 static inline GtkWidget *dt_ui_button_new(const gchar *label, const gchar *tooltip, const gchar *help)
407 {
408 GtkWidget *button = gtk_button_new_with_label(label);
409 gtk_label_set_ellipsize(GTK_LABEL(gtk_bin_get_child(GTK_BIN(button))), PANGO_ELLIPSIZE_END);
410 if(tooltip) gtk_widget_set_tooltip_text(button, tooltip);
411 if(help) dt_gui_add_help_link(button, help);
412 return button;
413 };
414
415 GtkWidget *dt_ui_scroll_wrap(GtkWidget *w, gint min_size, char *config_str);
416
417 // check whether the given container has any user-added children
418 gboolean dt_gui_container_has_children(GtkContainer *container);
419 // return a count of the user-added children in the given container
420 int dt_gui_container_num_children(GtkContainer *container);
421 // return the first child of the given container
422 GtkWidget *dt_gui_container_first_child(GtkContainer *container);
423 // return the requested child of the given container, or NULL if it has fewer children
424 GtkWidget *dt_gui_container_nth_child(GtkContainer *container, int which);
425
426 // remove all of the children we've added to the container. Any which no longer have any references will
427 // be destroyed.
428 void dt_gui_container_remove_children(GtkContainer *container);
429
430 // delete all of the children we've added to the container. Use this function only if you are SURE
431 // there are no other references to any of the children (if in doubt, use dt_gui_container_remove_children
432 // instead; it's a bit slower but safer).
433 void dt_gui_container_destroy_children(GtkContainer *container);
434
435 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
436 // vim: shiftwidth=2 expandtab tabstop=2 cindent
437 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
438