Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
imageop_gui.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2020-2022 Diederik Ter Rahe.
4 Copyright (C) 2020-2022 Pascal Obry.
5 Copyright (C) 2022 Aldric Renaudin.
6 Copyright (C) 2022 Martin Bařinka.
7 Copyright (C) 2022 Miloš Komarčević.
8 Copyright (C) 2025 Aurélien PIERRE.
9
10 darktable is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 darktable is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with darktable. If not, see <http://www.gnu.org/licenses/>.
22*/
23
24#include "develop/imageop_gui.h"
25#include "develop/imageop.h"
26#include "bauhaus/bauhaus.h"
27#include "dtgtk/button.h"
28#include "common/darktable.h"
29#include "gui/gtk.h"
30
31
32#ifdef GDK_WINDOWING_QUARTZ
33#include "osx/osx.h"
34#endif
35
36#include <assert.h>
37#include <gmodule.h>
38#include <math.h>
39#include <stdlib.h>
40#include <string.h>
41#include <strings.h>
42#include <time.h>
43
44typedef struct dt_module_param_t
45{
46 dt_iop_module_t *module;
47 void *param;
49
50static void _iop_toggle_callback(GtkWidget *togglebutton, dt_module_param_t *data)
51{
52 if(darktable.gui->reset) return;
53
54 dt_iop_module_t *self = data->module;
55 gboolean *field = (gboolean*)(data->param);
56
57 gboolean previous = *field;
58 *field = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(togglebutton));
59
60 if(*field != previous)
61 {
62 dt_iop_gui_changed(self, togglebutton, &previous);
63 }
64}
65
66// Add to the module internal list of widgets for incremental browsing
67// Note: Bauhaus widgets do it internally upon setting label
69{
70 if(!IS_NULL_PTR(self) && !IS_NULL_PTR(widget))
71 {
72 dt_gui_module_t *mod = (dt_gui_module_t *)self;
73 mod->widget_list = g_list_append(mod->widget_list, widget);
74 }
75}
76
78{
81
82 size_t param_index = 0;
83 gboolean skip_label = FALSE;
84
85 const size_t param_length = strlen(param) + 1;
86 char *param_name = g_malloc(param_length);
87 char *base_name = g_malloc(param_length);
88 if(sscanf(param, "%[^[][%" G_GSIZE_FORMAT "]", base_name, &param_index) == 2)
89 {
90 sprintf(param_name, "%s[0]", base_name);
91 skip_label = TRUE;
92 }
93 else
94 {
95 memcpy(param_name, param, param_length);
96 }
97 dt_free(base_name);
98
99 const dt_introspection_field_t *f = self->so->get_f(param_name);
100
101 GtkWidget *slider = NULL;
102 size_t offset = 0;
103
104 if(!IS_NULL_PTR(f))
105 {
106 if(f->header.type == DT_INTROSPECTION_TYPE_FLOAT)
107 {
108 const float min = f->Float.Min;
109 const float max = f->Float.Max;
110 offset = f->header.offset + param_index * sizeof(float);
111 const float defval = *(float*)((uint8_t *)d + offset);
112
113 const float top = fminf(max-min, fmaxf(fabsf(min), fabsf(max)));
114 const int digits = MAX(2, -floorf(log10f(top/100)+.1));
115
117 }
118 else if(f->header.type == DT_INTROSPECTION_TYPE_INT)
119 {
120 const int min = f->Int.Min;
121 const int max = f->Int.Max;
122 offset = f->header.offset + param_index * sizeof(int);
123 const int defval = *(int*)((uint8_t *)d + offset);
124
126 }
127 else if(f->header.type == DT_INTROSPECTION_TYPE_USHORT)
128 {
129 const unsigned short min = f->UShort.Min;
130 const unsigned short max = f->UShort.Max;
131 offset = f->header.offset + param_index * sizeof(unsigned short);
132 const unsigned short defval = *(unsigned short*)((uint8_t *)d + offset);
133
135 }
136 else f = NULL;
137 }
138
139 if(!IS_NULL_PTR(f))
140 {
141 dt_bauhaus_widget_set_field(slider, (uint8_t *)p + offset, f->header.type);
142
143 if(!skip_label)
144 {
145 if (*f->header.description)
146 {
147 // we do not want to support a context as it break all translations see #5498
148 // dt_bauhaus_widget_set_label(slider, g_dpgettext2(NULL, "introspection description", f->header.description));
149 dt_bauhaus_widget_set_label(slider, f->header.description);
150 }
151 else
152 {
153 gchar *str = dt_util_str_replace(f->header.field_name, "_", " ");
154
155 dt_bauhaus_widget_set_label(slider, str);
156
157 dt_free(str);
158 }
159 }
160 }
161 else
162 {
163 gchar *str = g_strdup_printf("'%s' is not a float/int/unsigned short/slider parameter", param_name);
164
166 dt_bauhaus_widget_set_label(slider, str);
167
168 dt_free(str);
169 }
170
171 if(!self->widget) self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
172 gtk_box_pack_start(GTK_BOX(self->widget), slider, FALSE, FALSE, 0);
173
176
177 dt_free(param_name);
178
179 return slider;
180}
181
183{
185 dt_introspection_field_t *f = self->so->get_f(param);
186
188 gchar *str = NULL;
189
190 if (!IS_NULL_PTR(f) && (f->header.type == DT_INTROSPECTION_TYPE_ENUM ||
191 f->header.type == DT_INTROSPECTION_TYPE_INT ||
192 f->header.type == DT_INTROSPECTION_TYPE_UINT ||
193 f->header.type == DT_INTROSPECTION_TYPE_BOOL ))
194 {
195 dt_bauhaus_widget_set_field(combobox, (uint8_t *)p + f->header.offset, f->header.type);
196
197 if (*f->header.description)
198 {
199 // we do not want to support a context as it break all translations see #5498
200 // dt_bauhaus_widget_set_label(combobox, g_dpgettext2(NULL, "introspection description", f->header.description));
201 dt_bauhaus_widget_set_label(combobox, f->header.description);
202 }
203 else
204 {
205 str = dt_util_str_replace(f->header.field_name, "_", " ");
206
207 dt_bauhaus_widget_set_label(combobox, str);
208
209 dt_free(str);
210 }
211
212 if(f->header.type == DT_INTROSPECTION_TYPE_BOOL)
213 {
214 dt_bauhaus_combobox_add(combobox, _("no"));
215 dt_bauhaus_combobox_add(combobox, _("yes"));
216 }
217 else if(f->header.type == DT_INTROSPECTION_TYPE_ENUM)
218 {
219 for(dt_introspection_type_enum_tuple_t *iter = f->Enum.values; iter && iter->name; iter++)
220 {
221 // we do not want to support a context as it break all translations see #5498
222 // dt_bauhaus_combobox_add_full(combobox, g_dpgettext2(NULL, "introspection description", iter->description), DT_BAUHAUS_COMBOBOX_ALIGN_RIGHT, GINT_TO_POINTER(iter->value), NULL, TRUE);
223 if(*iter->description)
224 dt_bauhaus_combobox_add_full(combobox, gettext(iter->description), DT_BAUHAUS_COMBOBOX_ALIGN_RIGHT, GINT_TO_POINTER(iter->value), NULL, TRUE);
225 }
226 }
227 }
228 else
229 {
230 str = g_strdup_printf("'%s' is not an enum/int/bool/combobox parameter", param);
231
232 dt_bauhaus_widget_set_label(combobox, str);
233
234 dt_free(str);
235 }
236
237 if(!self->widget) self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
238 gtk_box_pack_start(GTK_BOX(self->widget), combobox, FALSE, FALSE, 0);
239
242
243 return combobox;
244}
245
247{
249 dt_introspection_field_t *f = self->so->get_f(param);
250
251 GtkWidget *button = NULL;
252 gchar *str = NULL;
253
254 if(!IS_NULL_PTR(f) && f->header.type == DT_INTROSPECTION_TYPE_BOOL)
255 {
256 // we do not want to support a context as it break all translations see #5498
257 // button = gtk_check_button_new_with_label(g_dpgettext2(NULL, "introspection description", f->header.description));
258 str = *f->header.description
259 ? g_strdup(f->header.description)
260 : dt_util_str_replace(f->header.field_name, "_", " ");
261
262 GtkWidget *label = gtk_label_new(_(str));
263 gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END);
264 button = gtk_check_button_new();
265 gtk_container_add(GTK_CONTAINER(button), label);
266 dt_module_param_t *module_param = (dt_module_param_t *)g_malloc(sizeof(dt_module_param_t));
267 module_param->module = self;
268 module_param->param = (uint8_t *)p + f->header.offset;
269 g_signal_connect_data(G_OBJECT(button), "toggled", G_CALLBACK(_iop_toggle_callback), module_param, (GClosureNotify)g_free, 0);
270 }
271 else
272 {
273 str = g_strdup_printf("'%s' is not a bool/togglebutton parameter", param);
274
275 button = gtk_check_button_new_with_label(str);
276 }
277
278 _add_widget_to_module_list(self, button);
279
280 dt_free(str);
281 if(!self->widget) self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
282 gtk_box_pack_start(GTK_BOX(self->widget), button, FALSE, FALSE, 0);
283
284 return button;
285}
286
287GtkWidget *dt_iop_togglebutton_new(dt_iop_module_t *self, const char *section, const gchar *label, const gchar *ctrl_label,
288 GCallback callback, gboolean local, guint accel_key, GdkModifierType mods,
290{
291 GtkWidget *w = dtgtk_togglebutton_new(paint, 0, NULL);
292 g_signal_connect(G_OBJECT(w), "button-press-event", callback, self);
293
294 if(IS_NULL_PTR(ctrl_label))
295 gtk_widget_set_tooltip_text(w, _(label));
296 else
297 {
298 gchar *tooltip = g_strdup_printf(_("%s\nctrl+click to %s"), _(label), _(ctrl_label));
299 gtk_widget_set_tooltip_text(w, tooltip);
301 }
302
304
305 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), FALSE);
306 if(GTK_IS_BOX(box)) gtk_box_pack_end(GTK_BOX(box), w, FALSE, FALSE, 0);
307
308 return w;
309}
310
311GtkWidget *dt_iop_togglebutton_new_no_register(dt_iop_module_t *self, const char *section, const gchar *label,
312 const gchar *ctrl_label, GCallback callback, gboolean local,
313 guint accel_key, GdkModifierType mods,
315{
316 GtkWidget *w = dtgtk_togglebutton_new(paint, 0, NULL);
317 g_signal_connect(G_OBJECT(w), "button-press-event", callback, self);
318
319 if(IS_NULL_PTR(ctrl_label))
320 gtk_widget_set_tooltip_text(w, _(label));
321 else
322 {
323 gchar *tooltip = g_strdup_printf(_("%s\nctrl+click to %s"), _(label), _(ctrl_label));
324 gtk_widget_set_tooltip_text(w, tooltip);
326 }
327
328 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), FALSE);
329 if(GTK_IS_BOX(box)) gtk_box_pack_end(GTK_BOX(box), w, FALSE, FALSE, 0);
330
331 return w;
332}
333
334GtkWidget *dt_iop_button_new(dt_iop_module_t *self, const gchar *label,
335 GCallback callback, gboolean local, guint accel_key, GdkModifierType mods,
336 DTGTKCairoPaintIconFunc paint, gint paintflags, GtkWidget *box)
337{
338 GtkWidget *button = NULL;
339
340 if(paint)
341 {
342 button = dtgtk_button_new(paint, paintflags, NULL);
343 gtk_widget_set_tooltip_text(button, _(label));
344 }
345 else
346 {
347 button = gtk_button_new_with_label(_(label));
348 gtk_label_set_ellipsize(GTK_LABEL(gtk_bin_get_child(GTK_BIN(button))), PANGO_ELLIPSIZE_END);
349 }
350 _add_widget_to_module_list(self, button);
351
352 g_signal_connect(G_OBJECT(button), "clicked", callback, (gpointer)self);
353
354 if(GTK_IS_BOX(box)) gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
355
356 return button;
357}
358
360{
361 const gboolean mask_down = dt_conf_get_bool("masks_scroll_down_increases");
362 return up ? !mask_down : mask_down;
363}
364
365// clang-format off
366// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
367// vim: shiftwidth=2 expandtab tabstop=2 cindent
368// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
369// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
GtkWidget * dt_bauhaus_slider_new(dt_bauhaus_t *bh, dt_gui_module_t *self)
Definition bauhaus.c:1775
GtkWidget * dt_bauhaus_slider_new_with_range_and_feedback(dt_bauhaus_t *bh, dt_gui_module_t *self, float min, float max, float step, float defval, int digits, int feedback)
Definition bauhaus.c:1786
void dt_bauhaus_widget_set_field(GtkWidget *widget, gpointer field, dt_introspection_type_t field_type)
Definition bauhaus.c:1710
void dt_bauhaus_combobox_add_full(GtkWidget *widget, const char *text, dt_bauhaus_combobox_alignment_t align, gpointer data, void(free_func)(void *data), gboolean sensitive)
Definition bauhaus.c:2038
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1653
GtkWidget * dt_bauhaus_combobox_new(dt_bauhaus_t *bh, dt_gui_module_t *self)
Definition bauhaus.c:1842
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
Definition bauhaus.c:2016
@ DT_BAUHAUS_COMBOBOX_ALIGN_RIGHT
Definition bauhaus.h:125
#define DT_BAUHAUS_WIDGET(obj)
Definition bauhaus.h:60
GtkWidget * dtgtk_button_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
Definition button.c:134
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
const dt_aligned_pixel_t f
static const float const float const float min
const float max
const float top
int dt_conf_get_bool(const char *name)
darktable_t darktable
Definition darktable.c:181
#define dt_free(ptr)
Definition darktable.h:456
#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 darktable.h:281
void dt_iop_params_t
Definition dev_history.h:41
void(* DTGTKCairoPaintIconFunc)(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
Definition dtgtk/paint.h:75
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
#define DT_GUI_MODULE(x)
const char * tooltip
Definition image.h:251
void dt_iop_gui_changed(dt_iop_module_t *action, GtkWidget *widget, gpointer data)
Definition imageop.c:3155
GtkWidget * dt_iop_togglebutton_new_no_register(dt_iop_module_t *self, const char *section, const gchar *label, const gchar *ctrl_label, GCallback callback, gboolean local, guint accel_key, GdkModifierType mods, DTGTKCairoPaintIconFunc paint, GtkWidget *box)
GtkWidget * dt_iop_button_new(dt_iop_module_t *self, const gchar *label, GCallback callback, gboolean local, guint accel_key, GdkModifierType mods, DTGTKCairoPaintIconFunc paint, gint paintflags, GtkWidget *box)
static void _iop_toggle_callback(GtkWidget *togglebutton, dt_module_param_t *data)
Definition imageop_gui.c:50
GtkWidget * dt_iop_togglebutton_new(dt_iop_module_t *self, const char *section, const gchar *label, const gchar *ctrl_label, GCallback callback, gboolean local, guint accel_key, GdkModifierType mods, DTGTKCairoPaintIconFunc paint, GtkWidget *box)
GtkWidget * dt_bauhaus_toggle_from_params(dt_iop_module_t *self, const char *param)
GtkWidget * dt_bauhaus_slider_from_params(dt_iop_module_t *self, const char *param)
Definition imageop_gui.c:77
static void _add_widget_to_module_list(dt_iop_module_t *self, GtkWidget *widget)
Definition imageop_gui.c:68
gboolean dt_mask_scroll_increases(int up)
GtkWidget * dt_bauhaus_combobox_from_params(dt_iop_module_t *self, const char *param)
@ DT_INTROSPECTION_TYPE_BOOL
@ DT_INTROSPECTION_TYPE_ENUM
@ DT_INTROSPECTION_TYPE_FLOAT
@ DT_INTROSPECTION_TYPE_UINT
@ DT_INTROSPECTION_TYPE_USHORT
@ DT_INTROSPECTION_TYPE_INT
struct _GtkWidget GtkWidget
Definition splash.h:29
const float const float param
struct dt_gui_gtk_t * gui
Definition darktable.h:775
struct dt_bauhaus_t * bauhaus
Definition darktable.h:778
gboolean use_default_callback
Definition bauhaus.h:215
int32_t reset
Definition gtk.h:172
The dt_gui_module_t type is the intersection between a dt_lib_module_t and a dt_iop_module_t structur...
dt_iop_params_t * default_params
Definition imageop.h:307
GtkWidget * widget
Definition imageop.h:337
dt_iop_module_so_t * so
Definition imageop.h:359
GtkWidget * header
Definition imageop.h:341
dt_iop_params_t * params
Definition imageop.h:307
dt_iop_module_t *void * param
Definition imageop_gui.c:47
#define MAX(a, b)
Definition thinplate.c:29
GtkWidget * dtgtk_togglebutton_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
gchar * dt_util_str_replace(const gchar *string, const gchar *pattern, const gchar *substitute)
Definition utility.c:136