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
19#include "common/darktable.h"
20#include "gui/actions/menu.h"
21#include "gui/gtk.h"
22#include "gui/styles.h"
23#include "common/act_on.h"
24#include "common/history.h"
27#include "common/styles.h"
28#include "common/undo.h"
29#include "gui/accelerators.h"
30#include "control/conf.h"
31#include "control/control.h"
32#include "control/signal.h"
33#include "libs/lib.h"
34
35#include <glib.h>
36
37static GtkWidget **_styles_menus = NULL;
38static GList **_styles_lists = NULL;
42
43static gboolean _styles_menu_disabled(GtkWidget *widget)
44{
45 return FALSE;
46}
47
48static gboolean _styles_apply_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval,
49 GdkModifierType mods, gpointer user_data)
50{
51 const char *style_name = get_custom_data(GTK_WIDGET(user_data));
52 if(IS_NULL_PTR(style_name) || !*style_name) return FALSE;
53
54 if(dt_conf_get_bool("history/style/ask"))
55 {
56 gchar *title = g_strdup_printf(_("Apply style \"%s\" — merge settings"), style_name);
57 const gboolean ok = dt_gui_merge_options_dialog(title,
58 "history/style/mode",
59 "history/style/copy_iop_order",
60 "history/style/ask",
61 dt_styles_has_module_order(style_name));
62 dt_free(title);
63 if(!ok) return FALSE;
64 }
65
66 GList *imgs = dt_act_on_get_images();
67 const gboolean duplicate = dt_conf_get_bool("ui_last/styles_create_duplicate");
68 gboolean is_darkroom_image_in_list = dt_menu_is_image_in_dev(imgs);
69
70 if(is_darkroom_image_in_list)
71 {
72 imgs = g_list_remove(imgs, GINT_TO_POINTER(darktable.develop->image_storage.id));
74 const gboolean applied = dt_history_style_on_image(darktable.develop->image_storage.id, style_name, duplicate);
76 if(applied)
78 }
79
80 if(imgs) dt_history_style_on_list(imgs, style_name, duplicate);
81
82 g_list_free(imgs);
83 imgs = NULL;
84
85 return TRUE;
86}
87
89{
91}
92
93static gboolean _styles_create_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval,
94 GdkModifierType mods, gpointer user_data)
95{
96 const int32_t imgid = dt_act_on_get_first_image();
97 if(imgid <= 0) return FALSE;
98
100 return TRUE;
101}
102
103static void _close_styles_popup(GtkWidget *dialog, gint response_id, gpointer data)
104{
105 darktable.gui->styles_popup.module = (GtkWidget *)g_object_ref(darktable.gui->styles_popup.module);
106 GtkWidget *content = gtk_dialog_get_content_area(GTK_DIALOG(darktable.gui->styles_popup.window));
107 gtk_container_remove(GTK_CONTAINER(content), darktable.gui->styles_popup.module);
109}
110
111static gboolean _styles_open_popup_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval,
112 GdkModifierType mods, gpointer user_data)
113{
115 {
116 gtk_window_present_with_time(GTK_WINDOW(darktable.gui->styles_popup.window), GDK_CURRENT_TIME);
117 return TRUE;
118 }
119
120 dt_lib_module_t *module = dt_lib_get_module("styles");
121 if(IS_NULL_PTR(module)) return TRUE;
122
124 ? darktable.gui->styles_popup.module
125 : dt_lib_gui_get_expander(module);
126 if(IS_NULL_PTR(w)) return TRUE;
127
128 darktable.gui->styles_popup.module = w;
129
130 GtkWidget *dialog = gtk_dialog_new();
131#ifdef GDK_WINDOWING_QUARTZ
133 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT);
134#endif
135 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
136 gtk_window_set_modal(GTK_WINDOW(dialog), FALSE);
137 gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(dt_ui_main_window(darktable.gui->ui)));
138 gtk_window_set_title(GTK_WINDOW(dialog), _("Ansel - Styles"));
139 g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(_close_styles_popup), NULL);
140
142 dt_gui_add_help_link(w, dt_get_help_url(module->plugin_name));
143 gtk_widget_set_size_request(w, DT_PIXEL_APPLY_DPI(450), -1);
144
145 GtkWidget *content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
146 gtk_box_pack_start(GTK_BOX(content), w, TRUE, TRUE, 0);
147 gtk_widget_set_visible(w, TRUE);
148 gtk_widget_show_all(dialog);
149
151 return TRUE;
152}
153
154static gchar *_styles_build_tooltip(const dt_style_t *style)
155{
156 char *items_string = dt_styles_get_item_list_as_string(style->name);
157 gchar *tooltip = NULL;
158
159 if(items_string && *items_string)
160 {
161 if(style->description && *style->description)
162 {
163 gchar *desc = g_markup_escape_text(style->description, -1);
164 tooltip = g_strconcat("<b>", desc, "</b>\n", items_string, NULL);
165 dt_free(desc);
166 }
167 else
168 {
169 tooltip = g_strdup(items_string);
170 }
171 }
172 else if(style->description && *style->description)
173 {
174 tooltip = g_markup_escape_text(style->description, -1);
175 }
176
177 dt_free(items_string);
178 return tooltip;
179}
180
181static GtkWidget *_styles_get_submenu(GtkWidget **menus, GList **lists, GHashTable *submenus,
182 GtkWidget *parent, const gchar *path, const gchar *label,
183 const dt_menus_t index)
184{
185 if(IS_NULL_PTR(parent) || IS_NULL_PTR(path) || !*path || IS_NULL_PTR(label) || !*label)
186 return parent;
187
188 GtkWidget *submenu = g_hash_table_lookup(submenus, path);
189 if(submenu) return submenu;
190
191 gchar *menu_label = g_markup_escape_text(label, -1);
192 if(IS_NULL_PTR(menu_label)) menu_label = g_strdup("");
193
194 submenu = gtk_menu_new();
195 gtk_menu_set_accel_group(GTK_MENU(submenu), darktable.gui->accels->global_accels);
196
197 gchar *clean_label = strip_markup(menu_label);
198 if(g_strrstr(clean_label, "/") != NULL)
199 g_strdelimit(clean_label, "/", '-');
200 gchar *accel_path = dt_accels_build_path(gtk_menu_get_accel_path(GTK_MENU(parent)), clean_label);
201 gtk_menu_set_accel_path(GTK_MENU(submenu), accel_path);
202 dt_free(accel_path);
203 dt_free(clean_label);
204
205 dt_menu_entry_t *entry = set_menu_entry(menus, lists, menu_label, index, GTK_MENU(parent), NULL,
206 NULL, NULL, NULL, NULL, 0, 0,
208 gtk_menu_item_set_submenu(GTK_MENU_ITEM(entry->widget), submenu);
209 gtk_menu_shell_append(GTK_MENU_SHELL(parent), entry->widget);
210
211 g_hash_table_insert(submenus, g_strdup(path), submenu);
212 dt_free(menu_label);
213 return submenu;
214}
215
216static void _styles_add_menu_entry(GtkWidget **menus, GList **lists, GtkWidget *parent, const dt_menus_t index,
217 const gchar *label, const gchar *tooltip, const gchar *style_name)
218{
219 dt_menu_entry_t *entry = set_menu_entry(menus, lists, label, index, GTK_MENU(parent), NULL,
222
223 gtk_menu_shell_append(GTK_MENU_SHELL(parent), entry->widget);
224 gtk_menu_item_set_reserve_indicator(GTK_MENU_ITEM(entry->widget), TRUE);
225 g_object_set_data_full(G_OBJECT(entry->widget), "custom-data", g_strdup(style_name), g_free);
226
227 if(tooltip)
228 gtk_widget_set_tooltip_markup(entry->widget, tooltip);
229}
230
231static gboolean _styles_history_prepend_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval,
232 GdkModifierType mods, gpointer user_data)
233{
234 dt_conf_set_int("history/style/mode", DT_HISTORY_MERGE_PREPEND);
235 return TRUE;
236}
237
239{
240 return dt_conf_get_int("history/style/mode") == DT_HISTORY_MERGE_PREPEND;
241}
242
243static gboolean _styles_history_append_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval,
244 GdkModifierType mods, gpointer user_data)
245{
246 dt_conf_set_int("history/style/mode", DT_HISTORY_MERGE_APPEND);
247 return TRUE;
248}
249
251{
252 return dt_conf_get_int("history/style/mode") == DT_HISTORY_MERGE_APPEND;
253}
254
255static gboolean _styles_history_replace_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval,
256 GdkModifierType mods, gpointer user_data)
257{
258 dt_conf_set_int("history/style/mode", DT_HISTORY_MERGE_REPLACE);
259 return TRUE;
260}
261
263{
264 return dt_conf_get_int("history/style/mode") == DT_HISTORY_MERGE_REPLACE;
265}
266
267static gboolean _styles_copy_iop_order_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval,
268 GdkModifierType mods, gpointer user_data)
269{
270 dt_conf_set_bool("history/style/copy_iop_order", !dt_conf_get_bool("history/style/copy_iop_order"));
271 return TRUE;
272}
273
275{
276 return dt_conf_get_bool("history/style/copy_iop_order");
277}
278
279static gboolean _styles_ask_callback(GtkAccelGroup *group, GObject *acceleratable, guint keyval,
280 GdkModifierType mods, gpointer user_data)
281{
282 dt_conf_set_bool("history/style/ask", !dt_conf_get_bool("history/style/ask"));
283 return TRUE;
284}
285
287{
288 return dt_conf_get_bool("history/style/ask");
289}
290
291static void _styles_menu_clear(void)
292{
294
296 GList *children = gtk_container_get_children(GTK_CONTAINER(menu));
297 for(GList *child = children; child; child = g_list_next(child))
298 gtk_widget_destroy(GTK_WIDGET(child->data));
299 g_list_free(children);
300 children = NULL;
301
302 if(*_styles_lists)
303 {
304 // Menu entries are owned by their GtkMenuItem: set_menu_entry() frees the
305 // dt_menu_entry_t from the widget "destroy" signal. Only the temporary list
306 // nodes need to be released here after destroying the menu children above.
307 g_list_free(*_styles_lists);
308 *_styles_lists = NULL;
309 }
310}
311
312static gboolean _styles_menu_rebuild_idle(gpointer user_data)
313{
315 {
317 return G_SOURCE_REMOVE;
318 }
319
322 gtk_widget_show_all(_styles_menus[_styles_index]);
323 gtk_widget_queue_resize(_styles_menus[_styles_index]);
324
326 return G_SOURCE_REMOVE;
327}
328
329static void _styles_menu_rebuild_callback(gpointer instance, gpointer user_data)
330{
333}
334
335
336void append_styles(GtkWidget **menus, GList **lists, const dt_menus_t index)
337{
338 _styles_menus = menus;
339 _styles_lists = lists;
340 _styles_index = index;
342 {
344 G_CALLBACK(_styles_menu_rebuild_callback), NULL);
346 }
347
348 GList *styles = dt_styles_get_list("");
349
350 if(IS_NULL_PTR(styles))
351 {
352 add_sub_menu_entry(menus, lists, _("No styles available"), index, NULL, NULL, NULL, NULL,
354 }
355 else
356 {
357 GHashTable *submenus = g_hash_table_new_full(g_str_hash, g_str_equal, dt_free_gpointer, NULL);
358
359 for(GList *iter = styles; iter; iter = g_list_next(iter))
360 {
361 dt_style_t *style = (dt_style_t *)iter->data;
362 if(IS_NULL_PTR(style) || IS_NULL_PTR(style->name)) continue;
363
364 gchar **split = g_strsplit(style->name, "|", -1);
365 gchar *tooltip = _styles_build_tooltip(style);
366
367 int leaf = -1;
368 for(int i = 0; split[i]; i++)
369 if(split[i][0] != '\0')
370 leaf = i;
371
372 GtkWidget *parent_menu = menus[index];
373 GString *submenu_path = g_string_new(NULL);
374 for(int i = 0; i < leaf; i++)
375 {
376 if(split[i][0] == '\0') continue;
377
378 if(submenu_path->len > 0)
379 g_string_append_c(submenu_path, '|');
380 g_string_append(submenu_path, split[i]);
381 parent_menu = _styles_get_submenu(menus, lists, submenus, parent_menu,
382 submenu_path->str, split[i], index);
383 }
384
385 gchar *label = g_markup_escape_text(leaf >= 0 ? split[leaf] : style->name, -1);
386 if(IS_NULL_PTR(label)) label = g_strdup("");
387 _styles_add_menu_entry(menus, lists, parent_menu, index, label, tooltip, style->name);
388
389 g_string_free(submenu_path, TRUE);
390 dt_free(label);
392 g_strfreev(split);
393 }
394
395 g_hash_table_destroy(submenus);
396 g_list_free_full(styles, dt_style_free);
397 styles = NULL;
398 }
399
400 add_menu_separator(menus[index]);
401
402 add_top_submenu_entry(menus, lists, _("History pasting mode"), index);
403 GtkWidget *parent = get_last_widget(lists);
404
405 add_sub_sub_menu_entry(menus, parent, lists, _("Prepend"), index, NULL,
407 gtk_widget_set_tooltip_text(get_last_widget(lists),
408 _("Apply style BEFORE the current history.\n"
409 "CURRENT EDITS are applied afterwards and win conflicts."));
410
411 add_sub_sub_menu_entry(menus, parent, lists, _("Append"), index, NULL,
413 gtk_widget_set_tooltip_text(get_last_widget(lists),
414 _("Apply style AFTER the current history.\n"
415 "STYLE EDITS are applied afterwards and win conflicts."));
416
417 add_sub_sub_menu_entry(menus, parent, lists, _("Replace"), index, NULL,
419 gtk_widget_set_tooltip_text(get_last_widget(lists),
420 _("Discard the current history and replace it entirely with the style."));
421
422 add_top_submenu_entry(menus, lists, _("Nodes pasting mode"), index);
423 parent = get_last_widget(lists);
424
425 add_sub_sub_menu_entry(menus, parent, lists, _("Copy module order"), index, NULL,
427
428 add_sub_menu_entry(menus, lists, _("Ask merge settings before apply"), index, NULL,
430
431 add_menu_separator(menus[index]);
432 add_sub_menu_entry(menus, lists, _("Create new style..."), index, NULL,
434
435 add_sub_menu_entry(menus, lists, _("Manage styles..."), index, NULL,
436 _styles_open_popup_callback, NULL, NULL, NULL, 0, 0);
437}
438
439// clang-format off
440// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
441// vim: shiftwidth=2 expandtab tabstop=2 cindent
442// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
443// 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:54
GList * dt_act_on_get_images()
Definition act_on.c:39
int32_t dt_act_on_get_first_image()
Definition act_on.c:68
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
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)
darktable_t darktable
Definition darktable.c:181
static gchar * strip_markup(const char *s)
Remove Pango/Gtk markup and accels mnemonics from text labels. If the markup parsing fails,...
Definition darktable.h:1095
static void dt_free_gpointer(gpointer ptr)
Definition darktable.h:463
#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_dev_undo_start_record(dt_develop_t *dev)
Definition develop.c:1614
void dt_dev_undo_end_record(dt_develop_t *dev)
Definition develop.c:1625
void dt_gui_add_help_link(GtkWidget *widget, char *link)
Definition gtk.c:2022
GtkWidget * dt_ui_main_window(dt_ui_t *ui)
get the main window widget
#define DT_PIXEL_APPLY_DPI(value)
Definition gtk.h:90
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:251
void dt_lib_gui_set_expanded(dt_lib_module_t *module, gboolean expanded)
Definition lib.c:1064
GtkWidget * dt_lib_gui_get_expander(dt_lib_module_t *module)
Definition lib.c:1240
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:584
gboolean dt_menu_is_image_in_dev(GList *imgs)
Definition menu.c:660
void dt_menu_apply_dev_history_update(dt_develop_t *dev)
Reload the current darkroom history and refresh every dependent GUI.
Definition menu.c:666
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:330
GtkWidget * get_last_widget(GList **list)
Definition menu.c:618
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:542
void * get_custom_data(GtkWidget *widget)
Definition menu.c:612
void add_top_submenu_entry(GtkWidget **menus, GList **lists, const gchar *label, const dt_menus_t index)
Definition menu.c:515
void add_menu_separator(GtkWidget *menu)
Definition menu.c:598
gboolean has_active_images()
Definition menu.c:636
dt_menus_t
Definition menu.h:42
@ DT_MENU_STYLES
Definition menu.h:47
void dt_osx_disallow_fullscreen(GtkWidget *widget)
Definition osx.mm:104
@ DT_SIGNAL_STYLE_CHANGED
This signal is raised when a style is added/deleted/changed
Definition signal.h:147
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:357
struct _GtkWidget GtkWidget
Definition splash.h:29
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)
struct dt_gui_gtk_t * gui
Definition darktable.h:775
struct dt_control_signal_t * signals
Definition darktable.h:774
struct dt_develop_t * develop
Definition darktable.h:770
GtkAccelGroup * global_accels
dt_image_t image_storage
Definition develop.h:259
dt_accels_t * accels
Definition gtk.h:194
dt_ui_t * ui
Definition gtk.h:164
struct dt_gui_gtk_t::@49 styles_popup
GtkWidget * window
Definition gtk.h:235
int32_t id
Definition image.h:319
Definition menu.h:65
GtkWidget * widget
Definition menu.h:66
gchar * description
gchar * name
char * dt_get_help_url(char *name)