Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
studio_style.c
Go to the documentation of this file.
1/*
2 This file is part of the Ansel project.
3 Copyright (C) 2026 Guillaume STUTIN.
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
26#include "system/macros.h"
27#include "system/mem_alloc.h"
29#include "control/signal.h"
32#include "common/image.h"
33#include "common/styles.h"
34#include "common/conf.h"
36#include "libs/lib.h"
37#include "libs/lib_api.h"
38#include "views/view.h"
39#include "gui/window_manager.h"
40#include "widgets/scroll_wrap.h"
41
42DT_MODULE(1)
43
49
51{
53 STYLES_COL_FULLNAME, // set only on leaves (actual styles, not category nodes)
56
58{
59 GtkWidget *pool_treeview; // flat, ordered list of applied style names
60 GtkTreeViewColumn *pool_up_col;
61 GtkTreeViewColumn *pool_down_col;
62 GtkTreeViewColumn *pool_remove_col;
63
64 GtkWidget *styles_treeview; // categories (split on "|") with styles as leaves
65 GtkTreeViewColumn *styles_add_col;
66 // A treeview cell rendered by GtkCellRenderer is not its own CSS node, so
67 // the theme's `*:disabled { opacity }` rule never reaches it: "sensitive"
68 // bindings render identically whether true or false. Graying a leaf
69 // already in the pool therefore uses two direct mechanisms computed live
70 // in cell_data_func callbacks instead: an explicit "foreground" color for
71 // the name, and swapping in a pre-dimmed pixbuf for the add icon.
72 GdkPixbuf *add_icon_enabled;
74
75 GList *pool; // ordered list of g_strdup'd style names: the source of truth
77
78const char *name(dt_lib_module_t *self)
79{
80 return _("Auto style");
81}
82
83const char **views(dt_lib_module_t *self)
84{
85 static const char *v[] = { "studio_capture", NULL };
86 return v;
87}
88
93
95{
96 return 980;
97}
98
100{
101 for(GList *l = d->pool; l; l = g_list_next(l))
102 if(!g_strcmp0((const char *)l->data, name)) return TRUE;
103 return FALSE;
104}
105
110{
111 GString *conf = g_string_new(NULL);
112 for(GList *l = d->pool; l; l = g_list_next(l))
113 {
114 if(conf->len) g_string_append(conf, DT_FOLDER_SURVEY_STYLES_SEPARATOR);
115 g_string_append(conf, (const char *)l->data);
116 }
117
119 g_string_free(conf, TRUE);
120}
121
126{
127 GtkListStore *store = gtk_list_store_new(POOL_NUM_COLS, G_TYPE_STRING);
128 GtkTreeIter iter;
129 for(GList *l = d->pool; l; l = g_list_next(l))
130 {
131 gtk_list_store_append(store, &iter);
132 gtk_list_store_set(store, &iter, POOL_COL_NAME, (const char *)l->data, -1);
133 }
134 gtk_tree_view_set_model(GTK_TREE_VIEW(d->pool_treeview), GTK_TREE_MODEL(store));
135 g_object_unref(store);
136}
137
141static void _studio_style_prune_pool(dt_lib_studio_style_t *d, GList *all_styles)
142{
143 GList *l = d->pool;
144 while(l)
145 {
146 GList *next = g_list_next(l);
147 gboolean found = FALSE;
148 for(GList *s = all_styles; s && !found; s = g_list_next(s))
149 found = !g_strcmp0(((dt_style_t *)s->data)->name, (const char *)l->data);
150 if(!found)
151 {
152 dt_free(l->data);
153 d->pool = g_list_delete_link(d->pool, l);
154 }
155 l = next;
156 }
157}
158
164static gboolean _studio_style_get_node_for_name(GtkTreeModel *model, const gboolean root, GtkTreeIter *iter,
165 const gchar *segment_name)
166{
167 GtkTreeIter parent = *iter;
168
169 if(root)
170 {
171 if(!gtk_tree_model_get_iter_first(model, iter))
172 {
173 gtk_tree_store_append(GTK_TREE_STORE(model), iter, NULL);
174 return FALSE;
175 }
176 }
177 else
178 {
179 if(!gtk_tree_model_iter_children(model, iter, &parent))
180 {
181 gtk_tree_store_append(GTK_TREE_STORE(model), iter, &parent);
182 return FALSE;
183 }
184 }
185
186 do
187 {
188 gchar *node_name = NULL;
189 gtk_tree_model_get(model, iter, STYLES_COL_NAME, &node_name, -1);
190 const gboolean match = !g_strcmp0(node_name, segment_name);
191 dt_free(node_name);
192 if(match) return TRUE;
193 }
194 while(gtk_tree_model_iter_next(model, iter));
195
196 gtk_tree_store_append(GTK_TREE_STORE(model), iter, root ? NULL : &parent);
197 return FALSE;
198}
199
206{
207 GList *all_styles = dt_styles_get_list("");
208 _studio_style_prune_pool(d, all_styles);
209
210 GtkTreeStore *store = gtk_tree_store_new(STYLES_NUM_COLS, G_TYPE_STRING, G_TYPE_STRING);
211 GtkTreeIter iter;
212
213 for(GList *s = all_styles; s; s = g_list_next(s))
214 {
215 const dt_style_t *style = (const dt_style_t *)s->data;
216 gchar **segments = g_strsplit(style->name, "|", 0);
217
218 for(int k = 0; segments[k]; k++)
219 {
220 const gboolean found = _studio_style_get_node_for_name(GTK_TREE_MODEL(store), k == 0, &iter, segments[k]);
221 if(!found)
222 {
223 if(segments[k + 1])
224 gtk_tree_store_set(store, &iter, STYLES_COL_NAME, segments[k], -1);
225 else
226 gtk_tree_store_set(store, &iter, STYLES_COL_NAME, segments[k], STYLES_COL_FULLNAME, style->name, -1);
227 }
228 }
229 g_strfreev(segments);
230 }
231
232 gtk_tree_view_set_model(GTK_TREE_VIEW(d->styles_treeview), GTK_TREE_MODEL(store));
233 g_object_unref(store);
234
235 g_list_free_full(all_styles, dt_style_free);
236}
237
244static void _studio_style_name_cell_data_func(GtkTreeViewColumn *column, GtkCellRenderer *renderer,
245 GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
246{
248 gchar *fullname = NULL;
249 gtk_tree_model_get(model, iter, STYLES_COL_FULLNAME, &fullname, -1);
250
251 if(!IS_NULL_PTR(fullname) && _studio_style_pool_contains(d, fullname))
252 g_object_set(renderer, "foreground-set", TRUE, "foreground", "#888", NULL);
253 else
254 g_object_set(renderer, "foreground-set", FALSE, NULL);
255
256 dt_free(fullname);
257}
258
264static void _studio_style_add_cell_data_func(GtkTreeViewColumn *column, GtkCellRenderer *renderer,
265 GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
266{
268 gchar *fullname = NULL;
269 gtk_tree_model_get(model, iter, STYLES_COL_FULLNAME, &fullname, -1);
270
271 if(IS_NULL_PTR(fullname))
272 {
273 g_object_set(renderer, "visible", FALSE, NULL);
274 }
275 else
276 {
277 const gboolean in_pool = _studio_style_pool_contains(d, fullname);
278 g_object_set(renderer, "visible", TRUE, "pixbuf", in_pool ? d->add_icon_disabled : d->add_icon_enabled,
279 NULL);
280 }
281
282 dt_free(fullname);
283}
284
289static void _studio_style_pool_action_click(dt_lib_studio_style_t *d, GtkTreePath *path, GtkTreeViewColumn *column)
290{
291 const int index = gtk_tree_path_get_indices(path)[0];
292
293 if(column == d->pool_up_col)
294 {
295 if(index <= 0) return;
296 GList *previous = g_list_nth(d->pool, index - 1);
297 GList *current = g_list_next(previous);
298 gpointer tmp = previous->data;
299 previous->data = current->data;
300 current->data = tmp;
301 }
302 else if(column == d->pool_down_col)
303 {
304 GList *current = g_list_nth(d->pool, index);
305 GList *next = current ? g_list_next(current) : NULL;
306 if(IS_NULL_PTR(current) || IS_NULL_PTR(next)) return;
307 gpointer tmp = current->data;
308 current->data = next->data;
309 next->data = tmp;
310 }
311 else if(column == d->pool_remove_col)
312 {
313 GList *link = g_list_nth(d->pool, index);
314 if(IS_NULL_PTR(link)) return;
315 char *removed_name = (char *)link->data;
316 d->pool = g_list_delete_link(d->pool, link);
317 gtk_widget_queue_draw(d->styles_treeview); // un-gray the corresponding leaf, if visible
318 dt_free(removed_name);
319 }
320 else
321 return;
322
325}
326
333static gboolean _studio_style_pool_button_pressed(GtkWidget *treeview, GdkEventButton *event, gpointer user_data)
334{
336 if(IS_NULL_PTR(event) || event->type != GDK_BUTTON_PRESS || event->button != GDK_BUTTON_PRIMARY) return FALSE;
337
338 GtkTreePath *path = NULL;
339 GtkTreeViewColumn *column = NULL;
340 if(!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), (gint)event->x, (gint)event->y, &path, &column,
341 NULL, NULL))
342 return FALSE;
343
344 const gboolean consumed = column == d->pool_up_col || column == d->pool_down_col
345 || column == d->pool_remove_col;
346 if(consumed) _studio_style_pool_action_click(d, path, column);
347 gtk_tree_path_free(path);
348 return consumed;
349}
350
351static gboolean _studio_style_pool_query_tooltip(GtkWidget *treeview, gint x, gint y, gboolean keyboard_tip,
352 GtkTooltip *tooltip, gpointer user_data)
353{
355 if(keyboard_tip) return FALSE;
356
357 GtkTreePath *path = NULL;
358 GtkTreeViewColumn *column = NULL;
359 if(!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), x, y, &path, &column, NULL, NULL)) return FALSE;
360 gtk_tree_path_free(path);
361
362 const char *text = NULL;
363 if(column == d->pool_up_col) text = _("Apply this style earlier");
364 else if(column == d->pool_down_col) text = _("Apply this style later");
365 else if(column == d->pool_remove_col) text = _("Remove this style from the pool");
366 if(IS_NULL_PTR(text)) return FALSE;
367
368 gtk_tooltip_set_text(tooltip, text);
369 return TRUE;
370}
371
375static gboolean _studio_style_add_click(dt_lib_studio_style_t *d, GtkWidget *treeview, GtkTreePath *path)
376{
377 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
378 GtkTreeIter iter;
379 if(!gtk_tree_model_get_iter(model, &iter, path)) return FALSE;
380
381 gchar *fullname = NULL;
382 gtk_tree_model_get(model, &iter, STYLES_COL_FULLNAME, &fullname, -1);
383 if(IS_NULL_PTR(fullname)) return FALSE; // a category node, not a style
384
385 if(!_studio_style_pool_contains(d, fullname))
386 {
387 d->pool = g_list_append(d->pool, g_strdup(fullname));
389 gtk_widget_queue_draw(d->styles_treeview); // gray out the just-added leaf
391 }
392 dt_free(fullname);
393 return TRUE;
394}
395
396static gboolean _studio_style_styles_button_pressed(GtkWidget *treeview, GdkEventButton *event, gpointer user_data)
397{
399 if(IS_NULL_PTR(event) || event->type != GDK_BUTTON_PRESS || event->button != GDK_BUTTON_PRIMARY) return FALSE;
400
401 GtkTreePath *path = NULL;
402 GtkTreeViewColumn *column = NULL;
403 if(!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), (gint)event->x, (gint)event->y, &path, &column,
404 NULL, NULL))
405 return FALSE;
406
407 gboolean consumed = FALSE;
408 if(column == d->styles_add_col) consumed = _studio_style_add_click(d, treeview, path);
409 gtk_tree_path_free(path);
410 return consumed;
411}
412
413static gboolean _studio_style_styles_query_tooltip(GtkWidget *treeview, gint x, gint y, gboolean keyboard_tip,
414 GtkTooltip *tooltip, gpointer user_data)
415{
417 if(keyboard_tip) return FALSE;
418
419 GtkTreePath *path = NULL;
420 GtkTreeViewColumn *column = NULL;
421 if(!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), x, y, &path, &column, NULL, NULL)) return FALSE;
422 gtk_tree_path_free(path);
423
424 if(column != d->styles_add_col) return FALSE;
425 gtk_tooltip_set_text(tooltip, _("Add this style to the pool"));
426 return TRUE;
427}
428
432static void _studio_style_apply_callback(GtkWidget *widget, gpointer user_data)
433{
435
436 const int32_t imgid = dt_view_active_images_get_first();
437 if(imgid <= UNKNOWN_IMAGE)
438 {
439 dt_control_log(_("No image is displayed."));
440 return;
441 }
442 if(IS_NULL_PTR(d->pool))
443 {
444 dt_control_log(_("The pool is empty."));
445 return;
446 }
447
448 dt_hm_batch_state_t batch = { 0 };
449 int applied = 0;
450
451 for(GList *l = d->pool; l; l = g_list_next(l))
452 {
453 const char *pool_name = (const char *)l->data;
454 const int32_t style_id = dt_styles_get_id_by_name(pool_name);
455 if(style_id > 0
456 && !dt_styles_apply_to_image_merge(pool_name, style_id, imgid, DT_HISTORY_MERGE_APPEND, &batch))
457 {
458 applied++;
459 }
460 }
462
463 if(applied == 0)
464 {
465 dt_control_log(_("No style could be applied."));
466 return;
467 }
468
469 // History went straight to DB: refresh cached metadata, mipmap and thumbnails,
470 // then let listeners (studio center view) re-fetch their surfaces.
473 dt_control_log(ngettext("Applied %d style.", "Applied %d styles.", applied), applied);
474}
475
476static void _studio_style_changed_callback(gpointer instance, gpointer user_data)
477{
482}
483
488{
490 gchar **names = g_strsplit(conf && conf[0] ? conf : "", DT_FOLDER_SURVEY_STYLES_SEPARATOR, -1);
491 dt_free(conf);
492
493 for(gchar **pool_name = names; *pool_name; pool_name++)
494 if((*pool_name)[0] != '\0') d->pool = g_list_append(d->pool, g_strdup(*pool_name));
495
496 g_strfreev(names);
497}
498
503static GtkTreeViewColumn *_studio_style_add_name_column(GtkWidget *treeview, const int name_model_col,
504 GtkCellRenderer **out_renderer)
505{
506 GtkTreeViewColumn *col = gtk_tree_view_column_new();
507 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
508 g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, (gchar *)0);
509 gtk_tree_view_column_pack_start(col, renderer, TRUE);
510 gtk_tree_view_column_add_attribute(col, renderer, "text", name_model_col);
511 gtk_tree_view_column_set_expand(col, TRUE);
512 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), col);
513 if(out_renderer) *out_renderer = renderer;
514 return col;
515}
516
521static GtkTreeViewColumn *_studio_style_add_icon_column(GtkWidget *treeview, const char *icon_name,
522 GtkCellRenderer **out_renderer)
523{
524 GtkTreeViewColumn *col = gtk_tree_view_column_new();
525 GtkCellRenderer *renderer = gtk_cell_renderer_pixbuf_new();
526 g_object_set(renderer, "icon-name", icon_name, "stock-size", GTK_ICON_SIZE_MENU, NULL);
527 gtk_tree_view_column_pack_start(col, renderer, FALSE);
528 gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED);
529 gtk_tree_view_column_set_fixed_width(col, DT_PIXEL_APPLY_DPI(24));
530 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), col);
531 if(out_renderer) *out_renderer = renderer;
532 return col;
533}
534
541static GdkPixbuf *_studio_style_load_icon(const char *icon_name, const double alpha)
542{
543 gint width = 16;
544 gint height = 16;
545 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
546
547 GdkPixbuf *base = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), icon_name, width,
548 GTK_ICON_LOOKUP_FORCE_SIZE, NULL);
549 if(IS_NULL_PTR(base) || alpha >= 1.0) return base;
550
551 cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
552 cairo_t *cr = cairo_create(surface);
553 gdk_cairo_set_source_pixbuf(cr, base, 0, 0);
554 cairo_paint_with_alpha(cr, alpha);
555 cairo_destroy(cr);
556
557 GdkPixbuf *dimmed = gdk_pixbuf_get_from_surface(surface, 0, 0, width, height);
558 cairo_surface_destroy(surface);
559 g_object_unref(base);
560 return dimmed;
561}
562
564{
566 self->data = (void *)d;
567
568 self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
569
570 /* Pool: the ordered, applied styles, with inline reorder/remove icons. */
571 GtkWidget *pool_label = gtk_label_new(_("Pool"));
572 gtk_widget_set_halign(pool_label, GTK_ALIGN_START);
573 gtk_box_pack_start(GTK_BOX(self->widget), pool_label, FALSE, FALSE, 0);
574
575 d->pool_treeview = gtk_tree_view_new();
576 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(d->pool_treeview), FALSE);
577 _studio_style_add_name_column(d->pool_treeview, POOL_COL_NAME, NULL);
578 d->pool_up_col = _studio_style_add_icon_column(d->pool_treeview, "go-up-symbolic", NULL);
579 d->pool_down_col = _studio_style_add_icon_column(d->pool_treeview, "go-down-symbolic", NULL);
580 d->pool_remove_col = _studio_style_add_icon_column(d->pool_treeview, "list-remove-symbolic", NULL);
581 g_signal_connect(d->pool_treeview, "button-press-event", G_CALLBACK(_studio_style_pool_button_pressed), d);
582 gtk_widget_set_has_tooltip(d->pool_treeview, TRUE);
583 g_signal_connect(d->pool_treeview, "query-tooltip", G_CALLBACK(_studio_style_pool_query_tooltip), d);
584
585 gtk_box_pack_start(GTK_BOX(self->widget),
586 dt_ui_scroll_wrap(d->pool_treeview, 100, "plugins/darkroom/studio_style/poolwindowheight",
588 TRUE, TRUE, 0);
589
590 GtkWidget *apply = gtk_button_new_with_label(_("Apply to the displayed image"));
591 gtk_widget_set_tooltip_text(apply, _("Replace the history of the displayed image with the pool of styles"));
592 g_signal_connect(G_OBJECT(apply), "clicked", G_CALLBACK(_studio_style_apply_callback), d);
593 gtk_box_pack_start(GTK_BOX(self->widget), apply, FALSE, FALSE, 0);
594
595 /* Styles: every available style, grouped into categories, with an inline add icon on leaves. */
596 GtkWidget *styles_label = gtk_label_new(_("Styles"));
597 gtk_widget_set_halign(styles_label, GTK_ALIGN_START);
598 gtk_box_pack_start(GTK_BOX(self->widget), styles_label, FALSE, FALSE, 0);
599
600 d->add_icon_enabled = _studio_style_load_icon("list-add-symbolic", 1.0);
601 d->add_icon_disabled = _studio_style_load_icon("list-add-symbolic", 0.35);
602
603 d->styles_treeview = gtk_tree_view_new();
604 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(d->styles_treeview), FALSE);
605 GtkCellRenderer *name_renderer = NULL;
606 GtkTreeViewColumn *name_col
607 = _studio_style_add_name_column(d->styles_treeview, STYLES_COL_NAME, &name_renderer);
608 gtk_tree_view_column_set_cell_data_func(name_col, name_renderer, _studio_style_name_cell_data_func, d, NULL);
609
610 GtkCellRenderer *add_renderer = NULL;
611 d->styles_add_col = _studio_style_add_icon_column(d->styles_treeview, "list-add-symbolic", &add_renderer);
612 /* The cell_data_func drives "pixbuf" on every row; clear the static "icon-name" set by
613 * _studio_style_add_icon_column() so the two properties never compete. */
614 g_object_set(add_renderer, "icon-name", NULL, NULL);
615 gtk_tree_view_column_set_cell_data_func(d->styles_add_col, add_renderer, _studio_style_add_cell_data_func, d,
616 NULL);
617 g_signal_connect(d->styles_treeview, "button-press-event", G_CALLBACK(_studio_style_styles_button_pressed), d);
618 gtk_widget_set_has_tooltip(d->styles_treeview, TRUE);
619 g_signal_connect(d->styles_treeview, "query-tooltip", G_CALLBACK(_studio_style_styles_query_tooltip), d);
620
621 gtk_box_pack_start(GTK_BOX(self->widget),
622 dt_ui_scroll_wrap(d->styles_treeview, 100,
623 "plugins/darkroom/studio_style/styleswindowheight", DT_UI_RESIZE_DYNAMIC),
624 TRUE, TRUE, 0);
625
629
631 G_CALLBACK(_studio_style_changed_callback), d);
632}
633
635{
638 if(!IS_NULL_PTR(d))
639 {
640 g_list_free_full(d->pool, dt_free_gpointer);
641 if(!IS_NULL_PTR(d->add_icon_enabled)) g_object_unref(d->add_icon_enabled);
642 if(!IS_NULL_PTR(d->add_icon_disabled)) g_object_unref(d->add_icon_disabled);
643 }
644 g_free(self->data);
645 self->data = NULL;
646}
647
648// clang-format off
649// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
650// vim: shiftwidth=2 expandtab tabstop=2 cindent
651// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
652// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
static const float x
const float v
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
gchar * dt_conf_get_string(const char *name)
Read the stored string for name as a private copy.
void dt_conf_set_string(const char *name, const char *val)
void dt_image_history_changed(const int32_t imgid, const gboolean refresh_filmstrip)
void dt_control_log(const char *msg,...)
Definition control.c:824
GHashTable * names
GtkWidget* -> the name the application knows it by.
GtkTreeStore * store
its model, owned by the view
#define DT_FOLDER_SURVEY_STYLES_SEPARATOR
#define DT_FOLDER_SURVEY_STYLES_CONF_KEY
void dt_hm_batch_state_cleanup(dt_hm_batch_state_t *batch)
Release resources held by a batch state (the cached order). Safe to call on a zeroed state.
@ DT_HISTORY_MERGE_APPEND
#define UNKNOWN_IMAGE
Definition image.h:78
const char * tooltip
Definition image.h:310
const char * model
float *const restrict const size_t k
#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
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
#define DT_MODULE(MODVER)
Instantiate the version handshake every module must export. Use once, at file scope,...
const char * name
Definition pdf.h:90
GtkWidget * dt_ui_scroll_wrap(GtkWidget *w, gint min_size, const char *config_str, dt_ui_resize_mode_t mode)
Wrap a scrollable content widget in a recessed, vertically resizable scrolled window.
@ DT_UI_RESIZE_DYNAMIC
Definition scroll_wrap.h:42
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
Definition signal.h:407
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:386
struct dt_control_signal_t * dt_control_signal_get_global(void)
Definition darktable.c:616
@ DT_SIGNAL_DEVELOP_HISTORY_CHANGE
This signal is raised when develop history is changed no param, no returned value.
Definition signal.h:207
@ 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
int dt_styles_apply_to_image_merge(const char *name, const int style_id, const int32_t newimgid, const dt_history_merge_strategy_t mode, dt_hm_batch_state_t *batch)
void dt_style_free(gpointer data)
int32_t dt_styles_get_id_by_name(const char *name)
GList * dt_styles_get_list(const char *filter)
GModule *void * data
Definition lib.h:79
GtkWidget * widget
Definition lib.h:83
GtkTreeViewColumn * pool_down_col
GtkWidget * pool_treeview
GtkWidget * styles_treeview
GdkPixbuf * add_icon_disabled
GtkTreeViewColumn * pool_up_col
GdkPixbuf * add_icon_enabled
GtkTreeViewColumn * styles_add_col
GtkTreeViewColumn * pool_remove_col
gchar * name
dt_studio_pool_cols_t
@ POOL_COL_NAME
@ POOL_NUM_COLS
static gboolean _studio_style_styles_button_pressed(GtkWidget *treeview, GdkEventButton *event, gpointer user_data)
static GtkTreeViewColumn * _studio_style_add_name_column(GtkWidget *treeview, const int name_model_col, GtkCellRenderer **out_renderer)
Append the text column carrying the row's display name. Packed with expand=TRUE so any later-appended...
static gboolean _studio_style_pool_query_tooltip(GtkWidget *treeview, gint x, gint y, gboolean keyboard_tip, GtkTooltip *tooltip, gpointer user_data)
static void _studio_style_populate_styles_tree(dt_lib_studio_style_t *d)
Rebuild the styles tree from every available style, grouping category segments split on "|" in the st...
static GtkTreeViewColumn * _studio_style_add_icon_column(GtkWidget *treeview, const char *icon_name, GtkCellRenderer **out_renderer)
Append one fixed-width icon column, for per-row action clicks handled by column identity (see the *_b...
static gboolean _studio_style_pool_contains(dt_lib_studio_style_t *d, const char *name)
static gboolean _studio_style_add_click(dt_lib_studio_style_t *d, GtkWidget *treeview, GtkTreePath *path)
Add the style leaf under a click to the end of the pool.
static gboolean _studio_style_get_node_for_name(GtkTreeModel *model, const gboolean root, GtkTreeIter *iter, const gchar *segment_name)
Find, or create as a child of the last found/created sibling, the tree node for one "|"-separated pat...
static void _studio_style_save(dt_lib_studio_style_t *d)
Persist the pool, in order, to conf.
void gui_cleanup(dt_lib_module_t *self)
static void _studio_style_changed_callback(gpointer instance, gpointer user_data)
static gboolean _studio_style_pool_button_pressed(GtkWidget *treeview, GdkEventButton *event, gpointer user_data)
Left click on a pool row's up/down/remove icon column.
static gboolean _studio_style_styles_query_tooltip(GtkWidget *treeview, gint x, gint y, gboolean keyboard_tip, GtkTooltip *tooltip, gpointer user_data)
static void _studio_style_name_cell_data_func(GtkTreeViewColumn *column, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
Gray a leaf's name when its style is already in the pool. Computed live at render time (not stored in...
uint32_t container(dt_lib_module_t *self)
static void _studio_style_pool_action_click(dt_lib_studio_style_t *d, GtkTreePath *path, GtkTreeViewColumn *column)
Apply the up/down/remove action for the pool row under a click, identified by which icon column was h...
static GdkPixbuf * _studio_style_load_icon(const char *icon_name, const double alpha)
Load a themed icon at the menu icon size, optionally pre-blended to a lower alpha....
static void _studio_style_apply_callback(GtkWidget *widget, gpointer user_data)
Re-apply the pool, in order, to the displayed image.
void gui_init(dt_lib_module_t *self)
int position()
const char ** views(dt_lib_module_t *self)
static void _studio_style_load_pool(dt_lib_studio_style_t *d)
Load the persisted, ordered pool from conf, once at startup.
static void _studio_style_rebuild_pool_ui(dt_lib_studio_style_t *d)
Rebuild the pool treeview, in order, from d->pool.
dt_studio_styles_cols_t
@ STYLES_NUM_COLS
@ STYLES_COL_FULLNAME
@ STYLES_COL_NAME
static void _studio_style_prune_pool(dt_lib_studio_style_t *d, GList *all_styles)
Drop pool entries whose style no longer exists.
static void _studio_style_add_cell_data_func(GtkTreeViewColumn *column, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
Show the add icon only on leaves, swapping in a pre-dimmed pixbuf when the leaf's style is already in...
Telling the user something happened.
int32_t dt_view_active_images_get_first()
Definition view.c:1307
#define DT_GUI_BOX_SPACING
#define DT_PIXEL_APPLY_DPI(value)
@ DT_UI_CONTAINER_PANEL_LEFT_CENTER