Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
src/gui/actions/styles.c
Go to the documentation of this file.
1/*
2 This file is part of the Ansel project.
3 Copyright (C) 2026 Aurélien PIERRE.
4
5 Ansel 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 Ansel 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 Ansel. If not, see <http://www.gnu.org/licenses/>.
17*/
18
20#include "gui/actions/menu.h"
21#ifdef __APPLE__
22#include "osx/osx.h" // dt_osx_disallow_fullscreen(), used under GDK_WINDOWING_QUARTZ below
23#endif
25#include "gui/application.h"
26#include "gui/styles.h"
27#include "common/act_on.h"
30#include "common/styles.h"
32#include "develop/dev_history.h"
34#include "common/conf.h"
35#include "control/signal.h"
36#include "libs/lib.h"
37
38#include <glib.h>
39#include "widgets/label.h"
40
41static GtkWidget **_styles_menus = NULL;
42static GList **_styles_lists = NULL;
46
47static gboolean _styles_menu_disabled(GtkWidget *widget)
48{
49 return FALSE;
50}
51
52static gboolean _styles_apply_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval,
53 GdkModifierType mods, gpointer user_data)
54{
55 const char *style_name = get_custom_data(GTK_WIDGET(user_data));
56 if(IS_NULL_PTR(style_name) || !*style_name) return FALSE;
57
58 if(dt_conf_get_bool("history/style/ask"))
59 {
60 gchar *title = g_strdup_printf(_("Apply style \"%s\" — merge settings"), style_name);
61 const gboolean ok = dt_gui_merge_options_dialog(title,
62 "history/style/mode",
63 "history/style/copy_iop_order",
64 "history/style/ask",
65 dt_styles_has_module_order(style_name));
66 dt_free(title);
67 if(!ok) return FALSE;
68 }
69
70 GList *imgs = dt_act_on_get_images();
71 const gboolean duplicate = dt_conf_get_bool("ui_last/styles_create_duplicate");
72 gboolean is_darkroom_image_in_list = dt_dev_history_is_image_in_dev(imgs);
73
74 if(is_darkroom_image_in_list)
75 {
76 dt_develop_t *const dev = dt_dev_get_global();
77 imgs = g_list_remove(imgs, GINT_TO_POINTER(dev->image_storage.id));
79 const gboolean applied = dt_history_style_on_image(dev->image_storage.id, style_name, duplicate);
81 if(applied)
83 }
84
85 if(imgs) dt_history_style_on_list(imgs, style_name, duplicate);
86
87 g_list_free(imgs);
88 imgs = NULL;
89
90 return TRUE;
91}
92
94{
96}
97
98static gboolean _styles_create_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval,
99 GdkModifierType mods, gpointer user_data)
100{
101 const int32_t imgid = dt_act_on_get_first_image();
102 if(imgid <= 0) return FALSE;
103
105 return TRUE;
106}
107
108static void _close_styles_popup(GtkWidget *dialog, gint response_id, gpointer data)
109{
110 dt_gui_get_global()->styles_popup.module = (GtkWidget *)g_object_ref(dt_gui_get_global()->styles_popup.module);
111 GtkWidget *content = gtk_dialog_get_content_area(GTK_DIALOG(dt_gui_get_global()->styles_popup.window));
112 gtk_container_remove(GTK_CONTAINER(content), dt_gui_get_global()->styles_popup.module);
114}
115
116static gboolean _styles_open_popup_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval,
117 GdkModifierType mods, gpointer user_data)
118{
119 if(dt_gui_get_global()->styles_popup.window)
120 {
121 gtk_window_present_with_time(GTK_WINDOW(dt_gui_get_global()->styles_popup.window), GDK_CURRENT_TIME);
122 return TRUE;
123 }
124
125 dt_lib_module_t *module = dt_lib_get_module("styles");
126 if(IS_NULL_PTR(module)) return TRUE;
127
130 : dt_lib_gui_get_expander(module);
131 if(IS_NULL_PTR(w)) return TRUE;
132
133 dt_gui_get_global()->styles_popup.module = w;
134
135 GtkWidget *dialog = gtk_dialog_new();
136#ifdef GDK_WINDOWING_QUARTZ
138 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT);
139#endif
140 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
141 gtk_window_set_modal(GTK_WINDOW(dialog), FALSE);
142 gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(dt_gui_main_window()));
143 gtk_window_set_title(GTK_WINDOW(dialog), _("Ansel - Styles"));
144 g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(_close_styles_popup), NULL);
145
147 dt_gui_add_help_link(w, dt_get_help_url(module->plugin_name));
148 gtk_widget_set_size_request(w, DT_PIXEL_APPLY_DPI(450), -1);
149
150 GtkWidget *content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
151 gtk_box_pack_start(GTK_BOX(content), w, TRUE, TRUE, 0);
152 gtk_widget_set_visible(w, TRUE);
153 gtk_widget_show_all(dialog);
154
156 return TRUE;
157}
158
159static gchar *_styles_build_tooltip(const dt_style_t *style)
160{
161 char *items_string = dt_styles_get_item_list_as_string(style->name);
162 gchar *tooltip = NULL;
163
164 if(items_string && *items_string)
165 {
166 if(style->description && *style->description)
167 {
168 gchar *desc = g_markup_escape_text(style->description, -1);
169 tooltip = g_strconcat("<b>", desc, "</b>\n", items_string, NULL);
170 dt_free(desc);
171 }
172 else
173 {
174 tooltip = g_strdup(items_string);
175 }
176 }
177 else if(style->description && *style->description)
178 {
179 tooltip = g_markup_escape_text(style->description, -1);
180 }
181
182 dt_free(items_string);
183 return tooltip;
184}
185
186static GtkWidget *_styles_get_submenu(GtkWidget **menus, GList **lists, GHashTable *submenus,
187 GtkWidget *parent, const gchar *path, const gchar *label,
188 const dt_menus_t index)
189{
190 if(IS_NULL_PTR(parent) || IS_NULL_PTR(path) || !*path || IS_NULL_PTR(label) || !*label)
191 return parent;
192
193 GtkWidget *submenu = g_hash_table_lookup(submenus, path);
194 if(submenu) return submenu;
195
196 gchar *menu_label = g_markup_escape_text(label, -1);
197 if(IS_NULL_PTR(menu_label)) menu_label = g_strdup("");
198
199 submenu = gtk_menu_new();
200 gtk_menu_set_accel_group(GTK_MENU(submenu), dt_gui_get_accels()->global_accels);
201
202 gchar *clean_label = strip_markup(menu_label);
203 if(g_strrstr(clean_label, "/") != NULL)
204 g_strdelimit(clean_label, "/", '-');
205 gchar *accel_path = dt_accels_build_path(gtk_menu_get_accel_path(GTK_MENU(parent)), clean_label);
206 gtk_menu_set_accel_path(GTK_MENU(submenu), accel_path);
207 dt_free(accel_path);
208 dt_free(clean_label);
209
210 dt_menu_entry_t *entry = set_menu_entry(menus, lists, menu_label, index, GTK_MENU(parent), NULL,
211 NULL, NULL, NULL, NULL, 0, 0,
212 dt_gui_get_accels()->global_accels);
213 gtk_menu_item_set_submenu(GTK_MENU_ITEM(entry->widget), submenu);
214 gtk_menu_shell_append(GTK_MENU_SHELL(parent), entry->widget);
215
216 g_hash_table_insert(submenus, g_strdup(path), submenu);
217 dt_free(menu_label);
218 return submenu;
219}
220
221static void _styles_add_menu_entry(GtkWidget **menus, GList **lists, GtkWidget *parent, const dt_menus_t index,
222 const gchar *label, const gchar *tooltip, const gchar *style_name)
223{
224 dt_menu_entry_t *entry = set_menu_entry(menus, lists, label, index, GTK_MENU(parent), NULL,
226 0, 0, dt_gui_get_accels()->global_accels);
227
228 gtk_menu_shell_append(GTK_MENU_SHELL(parent), entry->widget);
229 gtk_menu_item_set_reserve_indicator(GTK_MENU_ITEM(entry->widget), TRUE);
230 g_object_set_data_full(G_OBJECT(entry->widget), "custom-data", g_strdup(style_name), g_free);
231
232 if(tooltip)
233 gtk_widget_set_tooltip_markup(entry->widget, tooltip);
234}
235
236static gboolean _styles_history_prepend_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval,
237 GdkModifierType mods, gpointer user_data)
238{
239 dt_conf_set_int("history/style/mode", DT_HISTORY_MERGE_PREPEND);
240 return TRUE;
241}
242
244{
245 return dt_conf_get_int("history/style/mode") == DT_HISTORY_MERGE_PREPEND;
246}
247
248static gboolean _styles_history_append_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval,
249 GdkModifierType mods, gpointer user_data)
250{
251 dt_conf_set_int("history/style/mode", DT_HISTORY_MERGE_APPEND);
252 return TRUE;
253}
254
256{
257 return dt_conf_get_int("history/style/mode") == DT_HISTORY_MERGE_APPEND;
258}
259
260static gboolean _styles_history_replace_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval,
261 GdkModifierType mods, gpointer user_data)
262{
263 dt_conf_set_int("history/style/mode", DT_HISTORY_MERGE_REPLACE);
264 return TRUE;
265}
266
268{
269 return dt_conf_get_int("history/style/mode") == DT_HISTORY_MERGE_REPLACE;
270}
271
272static gboolean _styles_copy_iop_order_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval,
273 GdkModifierType mods, gpointer user_data)
274{
275 dt_conf_set_bool("history/style/copy_iop_order", !dt_conf_get_bool("history/style/copy_iop_order"));
276 return TRUE;
277}
278
280{
281 return dt_conf_get_bool("history/style/copy_iop_order");
282}
283
284static gboolean _styles_ask_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval,
285 GdkModifierType mods, gpointer user_data)
286{
287 dt_conf_set_bool("history/style/ask", !dt_conf_get_bool("history/style/ask"));
288 return TRUE;
289}
290
292{
293 return dt_conf_get_bool("history/style/ask");
294}
295
296static void _styles_menu_clear(void)
297{
299
301 GList *children = gtk_container_get_children(GTK_CONTAINER(menu));
302 for(GList *child = children; child; child = g_list_next(child))
303 gtk_widget_destroy(GTK_WIDGET(child->data));
304 g_list_free(children);
305 children = NULL;
306
307 if(*_styles_lists)
308 {
309 // Menu entries are owned by their GtkMenuItem: set_menu_entry() frees the
310 // dt_menu_entry_t from the widget "destroy" signal. Only the temporary list
311 // nodes need to be released here after destroying the menu children above.
312 g_list_free(*_styles_lists);
313 *_styles_lists = NULL;
314 }
315}
316
317static gboolean _styles_menu_rebuild_idle(gpointer user_data)
318{
320 {
322 return G_SOURCE_REMOVE;
323 }
324
327 gtk_widget_show_all(_styles_menus[_styles_index]);
328 gtk_widget_queue_resize(_styles_menus[_styles_index]);
329
331 return G_SOURCE_REMOVE;
332}
333
334static void _styles_menu_rebuild_callback(gpointer instance, gpointer user_data)
335{
338}
339
340
341void append_styles(GtkWidget **menus, GList **lists, const dt_menus_t index)
342{
343 _styles_menus = menus;
344 _styles_lists = lists;
345 _styles_index = index;
347 {
349 G_CALLBACK(_styles_menu_rebuild_callback), NULL);
351 }
352
353 GList *styles = dt_styles_get_list("");
354
355 if(IS_NULL_PTR(styles))
356 {
357 add_sub_menu_entry(menus, lists, _("No styles available"), index, NULL, NULL, NULL, NULL,
359 }
360 else
361 {
362 GHashTable *submenus = g_hash_table_new_full(g_str_hash, g_str_equal, dt_free_gpointer, NULL);
363
364 for(GList *iter = styles; iter; iter = g_list_next(iter))
365 {
366 dt_style_t *style = (dt_style_t *)iter->data;
367 if(IS_NULL_PTR(style) || IS_NULL_PTR(style->name)) continue;
368
369 gchar **split = g_strsplit(style->name, "|", -1);
370 gchar *tooltip = _styles_build_tooltip(style);
371
372 int leaf = -1;
373 for(int i = 0; split[i]; i++)
374 if(split[i][0] != '\0')
375 leaf = i;
376
377 GtkWidget *parent_menu = menus[index];
378 GString *submenu_path = g_string_new(NULL);
379 for(int i = 0; i < leaf; i++)
380 {
381 if(split[i][0] == '\0') continue;
382
383 if(submenu_path->len > 0)
384 g_string_append_c(submenu_path, '|');
385 g_string_append(submenu_path, split[i]);
386 parent_menu = _styles_get_submenu(menus, lists, submenus, parent_menu,
387 submenu_path->str, split[i], index);
388 }
389
390 gchar *label = g_markup_escape_text(leaf >= 0 ? split[leaf] : style->name, -1);
391 if(IS_NULL_PTR(label)) label = g_strdup("");
392 _styles_add_menu_entry(menus, lists, parent_menu, index, label, tooltip, style->name);
393
394 g_string_free(submenu_path, TRUE);
395 dt_free(label);
397 g_strfreev(split);
398 }
399
400 g_hash_table_destroy(submenus);
401 g_list_free_full(styles, dt_style_free);
402 styles = NULL;
403 }
404
405 add_menu_separator(menus[index]);
406
407 add_top_submenu_entry(menus, lists, _("History pasting mode"), index);
408 GtkWidget *parent = get_last_widget(lists);
409
410 add_sub_sub_menu_entry(menus, parent, lists, _("Prepend"), index, NULL,
412 gtk_widget_set_tooltip_text(get_last_widget(lists),
413 _("Apply style BEFORE the current history.\n"
414 "CURRENT EDITS are applied afterwards and win conflicts."));
415
416 add_sub_sub_menu_entry(menus, parent, lists, _("Append"), index, NULL,
418 gtk_widget_set_tooltip_text(get_last_widget(lists),
419 _("Apply style AFTER the current history.\n"
420 "STYLE EDITS are applied afterwards and win conflicts."));
421
422 add_sub_sub_menu_entry(menus, parent, lists, _("Replace"), index, NULL,
424 gtk_widget_set_tooltip_text(get_last_widget(lists),
425 _("Discard the current history and replace it entirely with the style."));
426
427 add_top_submenu_entry(menus, lists, _("Nodes pasting mode"), index);
428 parent = get_last_widget(lists);
429
430 add_sub_sub_menu_entry(menus, parent, lists, _("Copy module order"), index, NULL,
432
433 add_sub_menu_entry(menus, lists, _("Ask merge settings before apply"), index, NULL,
435
436 add_menu_separator(menus[index]);
437 add_sub_menu_entry(menus, lists, _("Create new style..."), index, NULL,
439
440 add_sub_menu_entry(menus, lists, _("Manage styles..."), index, NULL,
441 _styles_open_popup_callback, NULL, NULL, NULL, 0, 0);
442}
443
444// clang-format off
445// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
446// vim: shiftwidth=2 expandtab tabstop=2 cindent
447// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
448// clang-format on
gchar * dt_accels_build_path(const gchar *scope, const gchar *feature)
Handle default and user-set shortcuts (accelerators)
int dt_act_on_get_images_nb(const gboolean only_visible, const gboolean force)
Definition act_on.c:53
GList * dt_act_on_get_images()
Definition act_on.c:38
int32_t dt_act_on_get_first_image()
Definition act_on.c:67
GtkWidget * dt_gui_main_window(void)
struct dt_accels_t * dt_gui_get_accels(void)
void dt_gui_add_help_link(GtkWidget *widget, char *link)
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
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)
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
Integer for name, clamped to the bounds declared in the XML.
struct dt_develop_t * dt_dev_get_global(void)
Definition darktable.c:528
struct dt_gui_gtk_t * dt_gui_get_global(void)
Definition darktable.c:523
void dt_apply_dev_history_update(dt_develop_t *dev)
Reload the current darkroom history and refresh every dependent GUI.
gboolean dt_dev_history_is_image_in_dev(GList *imgs)
Check whether any image of the list is the one currently loaded in the darkroom.
void dt_dev_undo_start_record(dt_develop_t *dev)
Definition develop.c:1854
void dt_dev_undo_end_record(dt_develop_t *dev)
Definition develop.c:1865
void dt_gui_styles_dialog_new(int32_t imgid)
gboolean dt_history_style_on_image(const int32_t imgid, const char *name, const gboolean duplicate)
gboolean dt_history_style_on_list(const GList *list, const char *name, const gboolean duplicate)
@ DT_HISTORY_MERGE_REPLACE
@ DT_HISTORY_MERGE_PREPEND
@ DT_HISTORY_MERGE_APPEND
gboolean dt_gui_merge_options_dialog(const char *title, const char *mode_key, const char *iop_order_key, const char *ask_key, const gboolean iop_order_available)
Show a modal dialog to pick merge mode and pipeline order before a paste or style apply.
const char * tooltip
Definition image.h:310
gchar * strip_markup(const char *s)
Remove Pango/Gtk markup and accel mnemonics from a text label. If markup parsing fails,...
Definition label.c:87
void dt_lib_gui_set_expanded(dt_lib_module_t *module, gboolean expanded)
Definition lib.c:854
GtkWidget * dt_lib_gui_get_expander(dt_lib_module_t *module)
Definition lib.c:1039
#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:96
static void dt_free_gpointer(gpointer ptr)
g_free() one pointer, with the signature GDestroyNotify wants.
Definition mem_alloc.h:184
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
void add_sub_sub_menu_entry(GtkWidget **menus, GtkWidget *parent, GList **lists, const gchar *label, const dt_menus_t index, void *data, gboolean(*action_callback)(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data), gboolean(*checked_callback)(GtkWidget *widget), gboolean(*active_callback)(GtkWidget *widget), gboolean(*sensitive_callback)(GtkWidget *widget), guint key_val, GdkModifierType mods)
Definition menu.c:607
dt_menu_entry_t * set_menu_entry(GtkWidget **menus, GList **items_list, const gchar *label, dt_menus_t menu_index, GtkMenu *parent, void *data, gboolean(*action_callback)(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data), gboolean(*checked_callback)(GtkWidget *widget), gboolean(*active_callback)(GtkWidget *widget), gboolean(*sensitive_callback)(GtkWidget *widget), guint key_val, GdkModifierType mods, GtkAccelGroup *accel_group)
Definition menu.c:327
GtkWidget * get_last_widget(GList **list)
Definition menu.c:641
void add_sub_menu_entry(GtkWidget **menus, GList **lists, const gchar *label, const dt_menus_t index, void *data, gboolean(*action_callback)(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data), gboolean(*checked_callback)(GtkWidget *widget), gboolean(*active_callback)(GtkWidget *widget), gboolean(*sensitive_callback)(GtkWidget *widget), guint key_val, GdkModifierType mods)
Definition menu.c:565
void * get_custom_data(GtkWidget *widget)
Definition menu.c:635
void add_top_submenu_entry(GtkWidget **menus, GList **lists, const gchar *label, const dt_menus_t index)
Definition menu.c:538
void add_menu_separator(GtkWidget *menu)
Definition menu.c:621
gboolean has_active_images()
Definition menu.c:659
dt_menus_t
Definition menu.h:42
@ DT_MENU_STYLES
Definition menu.h:47
void dt_osx_disallow_fullscreen(GtkWidget *widget)
Definition osx.mm:105
struct dt_control_signal_t * dt_control_signal_get_global(void)
Definition darktable.c:616
@ DT_SIGNAL_STYLE_CHANGED
This signal is raised when a style is added/deleted/changed
Definition signal.h:150
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:396
char * dt_styles_get_item_list_as_string(const char *name)
void dt_style_free(gpointer data)
gboolean dt_styles_has_module_order(const char *name)
GList * dt_styles_get_list(const char *filter)
static GList ** _styles_lists
static dt_menus_t _styles_index
static void _styles_menu_rebuild_callback(gpointer instance, gpointer user_data)
static GtkWidget ** _styles_menus
static gboolean _styles_ask_checked_callback(GtkWidget *widget)
static gboolean _styles_history_prepend_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
static GtkWidget * _styles_get_submenu(GtkWidget **menus, GList **lists, GHashTable *submenus, GtkWidget *parent, const gchar *path, const gchar *label, const dt_menus_t index)
static gboolean _styles_apply_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
static gboolean _styles_ask_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
static gboolean _styles_history_prepend_checked_callback(GtkWidget *widget)
static gboolean _styles_copy_iop_order_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
static void _close_styles_popup(GtkWidget *dialog, gint response_id, gpointer data)
static gboolean _styles_open_popup_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
static gboolean _styles_signal_connected
static gboolean _styles_menu_disabled(GtkWidget *widget)
static gboolean _styles_history_append_checked_callback(GtkWidget *widget)
void append_styles(GtkWidget **menus, GList **lists, const dt_menus_t index)
static void _styles_add_menu_entry(GtkWidget **menus, GList **lists, GtkWidget *parent, const dt_menus_t index, const gchar *label, const gchar *tooltip, const gchar *style_name)
static guint _styles_menu_rebuild_source
static gboolean _styles_history_append_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
static gboolean _styles_copy_iop_order_checked_callback(GtkWidget *widget)
static gchar * _styles_build_tooltip(const dt_style_t *style)
static gboolean _styles_create_sensitive_callback(GtkWidget *widget)
static gboolean _styles_create_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
static gboolean _styles_history_replace_checked_callback(GtkWidget *widget)
static void _styles_menu_clear(void)
static gboolean _styles_menu_rebuild_idle(gpointer user_data)
static gboolean _styles_history_replace_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data)
dt_image_t image_storage
Definition develop.h:225
struct dt_gui_gtk_t::@35 styles_popup
GtkWidget * window
int32_t id
Definition image.h:401
Definition menu.h:65
GtkWidget * widget
Definition menu.h:66
gchar * description
gchar * name
char * dt_get_help_url(char *name)
#define DT_PIXEL_APPLY_DPI(value)