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