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
25#include "common/darktable.h"
26#include "common/debug.h"
29#include "common/image.h"
30#include "common/styles.h"
31#include "control/conf.h"
32#include "control/control.h"
33#include "gui/gtk.h"
34#include "libs/lib.h"
35#include "libs/lib_api.h"
36#include "views/view.h"
37
38DT_MODULE(1)
39
45
47{
49 STYLES_COL_FULLNAME, // set only on leaves (actual styles, not category nodes)
52
54{
55 GtkWidget *pool_treeview; // flat, ordered list of applied style names
56 GtkTreeViewColumn *pool_up_col;
57 GtkTreeViewColumn *pool_down_col;
58 GtkTreeViewColumn *pool_remove_col;
59
60 GtkWidget *styles_treeview; // categories (split on "|") with styles as leaves
61 GtkTreeViewColumn *styles_add_col;
62 // A treeview cell rendered by GtkCellRenderer is not its own CSS node, so
63 // the theme's `*:disabled { opacity }` rule never reaches it: "sensitive"
64 // bindings render identically whether true or false. Graying a leaf
65 // already in the pool therefore uses two direct mechanisms computed live
66 // in cell_data_func callbacks instead: an explicit "foreground" color for
67 // the name, and swapping in a pre-dimmed pixbuf for the add icon.
68 GdkPixbuf *add_icon_enabled;
70
71 GList *pool; // ordered list of g_strdup'd style names: the source of truth
73
74const char *name(dt_lib_module_t *self)
75{
76 return _("Auto style");
77}
78
79const char **views(dt_lib_module_t *self)
80{
81 static const char *v[] = { "studio_capture", NULL };
82 return v;
83}
84
89
91{
92 return 980;
93}
94
96{
97 for(GList *l = d->pool; l; l = g_list_next(l))
98 if(!g_strcmp0((const char *)l->data, name)) return TRUE;
99 return FALSE;
100}
101
106{
107 GString *conf = g_string_new(NULL);
108 for(GList *l = d->pool; l; l = g_list_next(l))
109 {
110 if(conf->len) g_string_append(conf, DT_FOLDER_SURVEY_STYLES_SEPARATOR);
111 g_string_append(conf, (const char *)l->data);
112 }
113
115 g_string_free(conf, TRUE);
116}
117
122{
123 GtkListStore *store = gtk_list_store_new(POOL_NUM_COLS, G_TYPE_STRING);
124 GtkTreeIter iter;
125 for(GList *l = d->pool; l; l = g_list_next(l))
126 {
127 gtk_list_store_append(store, &iter);
128 gtk_list_store_set(store, &iter, POOL_COL_NAME, (const char *)l->data, -1);
129 }
130 gtk_tree_view_set_model(GTK_TREE_VIEW(d->pool_treeview), GTK_TREE_MODEL(store));
131 g_object_unref(store);
132}
133
137static void _studio_style_prune_pool(dt_lib_studio_style_t *d, GList *all_styles)
138{
139 GList *l = d->pool;
140 while(l)
141 {
142 GList *next = g_list_next(l);
143 gboolean found = FALSE;
144 for(GList *s = all_styles; s && !found; s = g_list_next(s))
145 found = !g_strcmp0(((dt_style_t *)s->data)->name, (const char *)l->data);
146 if(!found)
147 {
148 dt_free(l->data);
149 d->pool = g_list_delete_link(d->pool, l);
150 }
151 l = next;
152 }
153}
154
160static gboolean _studio_style_get_node_for_name(GtkTreeModel *model, const gboolean root, GtkTreeIter *iter,
161 const gchar *segment_name)
162{
163 GtkTreeIter parent = *iter;
164
165 if(root)
166 {
167 if(!gtk_tree_model_get_iter_first(model, iter))
168 {
169 gtk_tree_store_append(GTK_TREE_STORE(model), iter, NULL);
170 return FALSE;
171 }
172 }
173 else
174 {
175 if(!gtk_tree_model_iter_children(model, iter, &parent))
176 {
177 gtk_tree_store_append(GTK_TREE_STORE(model), iter, &parent);
178 return FALSE;
179 }
180 }
181
182 do
183 {
184 gchar *node_name = NULL;
185 gtk_tree_model_get(model, iter, STYLES_COL_NAME, &node_name, -1);
186 const gboolean match = !g_strcmp0(node_name, segment_name);
187 dt_free(node_name);
188 if(match) return TRUE;
189 }
190 while(gtk_tree_model_iter_next(model, iter));
191
192 gtk_tree_store_append(GTK_TREE_STORE(model), iter, root ? NULL : &parent);
193 return FALSE;
194}
195
202{
203 GList *all_styles = dt_styles_get_list("");
204 _studio_style_prune_pool(d, all_styles);
205
206 GtkTreeStore *store = gtk_tree_store_new(STYLES_NUM_COLS, G_TYPE_STRING, G_TYPE_STRING);
207 GtkTreeIter iter;
208
209 for(GList *s = all_styles; s; s = g_list_next(s))
210 {
211 const dt_style_t *style = (const dt_style_t *)s->data;
212 gchar **segments = g_strsplit(style->name, "|", 0);
213
214 for(int k = 0; segments[k]; k++)
215 {
216 const gboolean found = _studio_style_get_node_for_name(GTK_TREE_MODEL(store), k == 0, &iter, segments[k]);
217 if(!found)
218 {
219 if(segments[k + 1])
220 gtk_tree_store_set(store, &iter, STYLES_COL_NAME, segments[k], -1);
221 else
222 gtk_tree_store_set(store, &iter, STYLES_COL_NAME, segments[k], STYLES_COL_FULLNAME, style->name, -1);
223 }
224 }
225 g_strfreev(segments);
226 }
227
228 gtk_tree_view_set_model(GTK_TREE_VIEW(d->styles_treeview), GTK_TREE_MODEL(store));
229 g_object_unref(store);
230
231 g_list_free_full(all_styles, dt_style_free);
232}
233
240static void _studio_style_name_cell_data_func(GtkTreeViewColumn *column, GtkCellRenderer *renderer,
241 GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
242{
244 gchar *fullname = NULL;
245 gtk_tree_model_get(model, iter, STYLES_COL_FULLNAME, &fullname, -1);
246
247 if(!IS_NULL_PTR(fullname) && _studio_style_pool_contains(d, fullname))
248 g_object_set(renderer, "foreground-set", TRUE, "foreground", "#888", NULL);
249 else
250 g_object_set(renderer, "foreground-set", FALSE, NULL);
251
252 dt_free(fullname);
253}
254
260static void _studio_style_add_cell_data_func(GtkTreeViewColumn *column, GtkCellRenderer *renderer,
261 GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
262{
264 gchar *fullname = NULL;
265 gtk_tree_model_get(model, iter, STYLES_COL_FULLNAME, &fullname, -1);
266
267 if(IS_NULL_PTR(fullname))
268 {
269 g_object_set(renderer, "visible", FALSE, NULL);
270 }
271 else
272 {
273 const gboolean in_pool = _studio_style_pool_contains(d, fullname);
274 g_object_set(renderer, "visible", TRUE, "pixbuf", in_pool ? d->add_icon_disabled : d->add_icon_enabled,
275 NULL);
276 }
277
278 dt_free(fullname);
279}
280
285static void _studio_style_pool_action_click(dt_lib_studio_style_t *d, GtkTreePath *path, GtkTreeViewColumn *column)
286{
287 const int index = gtk_tree_path_get_indices(path)[0];
288
289 if(column == d->pool_up_col)
290 {
291 if(index <= 0) return;
292 GList *previous = g_list_nth(d->pool, index - 1);
293 GList *current = g_list_next(previous);
294 gpointer tmp = previous->data;
295 previous->data = current->data;
296 current->data = tmp;
297 }
298 else if(column == d->pool_down_col)
299 {
300 GList *current = g_list_nth(d->pool, index);
301 GList *next = current ? g_list_next(current) : NULL;
302 if(IS_NULL_PTR(current) || IS_NULL_PTR(next)) return;
303 gpointer tmp = current->data;
304 current->data = next->data;
305 next->data = tmp;
306 }
307 else if(column == d->pool_remove_col)
308 {
309 GList *link = g_list_nth(d->pool, index);
310 if(IS_NULL_PTR(link)) return;
311 char *removed_name = (char *)link->data;
312 d->pool = g_list_delete_link(d->pool, link);
313 gtk_widget_queue_draw(d->styles_treeview); // un-gray the corresponding leaf, if visible
314 dt_free(removed_name);
315 }
316 else
317 return;
318
321}
322
329static gboolean _studio_style_pool_button_pressed(GtkWidget *treeview, GdkEventButton *event, gpointer user_data)
330{
332 if(IS_NULL_PTR(event) || event->type != GDK_BUTTON_PRESS || event->button != GDK_BUTTON_PRIMARY) return FALSE;
333
334 GtkTreePath *path = NULL;
335 GtkTreeViewColumn *column = NULL;
336 if(!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), (gint)event->x, (gint)event->y, &path, &column,
337 NULL, NULL))
338 return FALSE;
339
340 const gboolean consumed = column == d->pool_up_col || column == d->pool_down_col
341 || column == d->pool_remove_col;
342 if(consumed) _studio_style_pool_action_click(d, path, column);
343 gtk_tree_path_free(path);
344 return consumed;
345}
346
347static gboolean _studio_style_pool_query_tooltip(GtkWidget *treeview, gint x, gint y, gboolean keyboard_tip,
348 GtkTooltip *tooltip, gpointer user_data)
349{
351 if(keyboard_tip) return FALSE;
352
353 GtkTreePath *path = NULL;
354 GtkTreeViewColumn *column = NULL;
355 if(!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), x, y, &path, &column, NULL, NULL)) return FALSE;
356 gtk_tree_path_free(path);
357
358 const char *text = NULL;
359 if(column == d->pool_up_col) text = _("Apply this style earlier");
360 else if(column == d->pool_down_col) text = _("Apply this style later");
361 else if(column == d->pool_remove_col) text = _("Remove this style from the pool");
362 if(IS_NULL_PTR(text)) return FALSE;
363
364 gtk_tooltip_set_text(tooltip, text);
365 return TRUE;
366}
367
371static gboolean _studio_style_add_click(dt_lib_studio_style_t *d, GtkWidget *treeview, GtkTreePath *path)
372{
373 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
374 GtkTreeIter iter;
375 if(!gtk_tree_model_get_iter(model, &iter, path)) return FALSE;
376
377 gchar *fullname = NULL;
378 gtk_tree_model_get(model, &iter, STYLES_COL_FULLNAME, &fullname, -1);
379 if(IS_NULL_PTR(fullname)) return FALSE; // a category node, not a style
380
381 if(!_studio_style_pool_contains(d, fullname))
382 {
383 d->pool = g_list_append(d->pool, g_strdup(fullname));
385 gtk_widget_queue_draw(d->styles_treeview); // gray out the just-added leaf
387 }
388 dt_free(fullname);
389 return TRUE;
390}
391
392static gboolean _studio_style_styles_button_pressed(GtkWidget *treeview, GdkEventButton *event, gpointer user_data)
393{
395 if(IS_NULL_PTR(event) || event->type != GDK_BUTTON_PRESS || event->button != GDK_BUTTON_PRIMARY) return FALSE;
396
397 GtkTreePath *path = NULL;
398 GtkTreeViewColumn *column = NULL;
399 if(!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), (gint)event->x, (gint)event->y, &path, &column,
400 NULL, NULL))
401 return FALSE;
402
403 gboolean consumed = FALSE;
404 if(column == d->styles_add_col) consumed = _studio_style_add_click(d, treeview, path);
405 gtk_tree_path_free(path);
406 return consumed;
407}
408
409static gboolean _studio_style_styles_query_tooltip(GtkWidget *treeview, gint x, gint y, gboolean keyboard_tip,
410 GtkTooltip *tooltip, gpointer user_data)
411{
413 if(keyboard_tip) return FALSE;
414
415 GtkTreePath *path = NULL;
416 GtkTreeViewColumn *column = NULL;
417 if(!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview), x, y, &path, &column, NULL, NULL)) return FALSE;
418 gtk_tree_path_free(path);
419
420 if(column != d->styles_add_col) return FALSE;
421 gtk_tooltip_set_text(tooltip, _("Add this style to the pool"));
422 return TRUE;
423}
424
428static void _studio_style_apply_callback(GtkWidget *widget, gpointer user_data)
429{
431
432 const int32_t imgid = dt_view_active_images_get_first();
433 if(imgid <= UNKNOWN_IMAGE)
434 {
435 dt_control_log(_("No image is displayed."));
436 return;
437 }
438 if(IS_NULL_PTR(d->pool))
439 {
440 dt_control_log(_("The pool is empty."));
441 return;
442 }
443
444 dt_hm_batch_state_t batch = { 0 };
445 int applied = 0;
446
447 for(GList *l = d->pool; l; l = g_list_next(l))
448 {
449 const char *pool_name = (const char *)l->data;
450 const int32_t style_id = dt_styles_get_id_by_name(pool_name);
451 if(style_id > 0
452 && !dt_styles_apply_to_image_merge(pool_name, style_id, imgid, DT_HISTORY_MERGE_APPEND, &batch))
453 {
454 applied++;
455 }
456 }
458
459 if(applied == 0)
460 {
461 dt_control_log(_("No style could be applied."));
462 return;
463 }
464
465 // History went straight to DB: refresh cached metadata, mipmap and thumbnails,
466 // then let listeners (studio center view) re-fetch their surfaces.
469 dt_control_log(ngettext("Applied %d style.", "Applied %d styles.", applied), applied);
470}
471
472static void _studio_style_changed_callback(gpointer instance, gpointer user_data)
473{
478}
479
484{
486 gchar **names = g_strsplit(conf && conf[0] ? conf : "", DT_FOLDER_SURVEY_STYLES_SEPARATOR, -1);
487 dt_free(conf);
488
489 for(gchar **pool_name = names; *pool_name; pool_name++)
490 if((*pool_name)[0] != '\0') d->pool = g_list_append(d->pool, g_strdup(*pool_name));
491
492 g_strfreev(names);
493}
494
499static GtkTreeViewColumn *_studio_style_add_name_column(GtkWidget *treeview, const int name_model_col,
500 GtkCellRenderer **out_renderer)
501{
502 GtkTreeViewColumn *col = gtk_tree_view_column_new();
503 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
504 g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, (gchar *)0);
505 gtk_tree_view_column_pack_start(col, renderer, TRUE);
506 gtk_tree_view_column_add_attribute(col, renderer, "text", name_model_col);
507 gtk_tree_view_column_set_expand(col, TRUE);
508 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), col);
509 if(out_renderer) *out_renderer = renderer;
510 return col;
511}
512
517static GtkTreeViewColumn *_studio_style_add_icon_column(GtkWidget *treeview, const char *icon_name,
518 GtkCellRenderer **out_renderer)
519{
520 GtkTreeViewColumn *col = gtk_tree_view_column_new();
521 GtkCellRenderer *renderer = gtk_cell_renderer_pixbuf_new();
522 g_object_set(renderer, "icon-name", icon_name, "stock-size", GTK_ICON_SIZE_MENU, NULL);
523 gtk_tree_view_column_pack_start(col, renderer, FALSE);
524 gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED);
525 gtk_tree_view_column_set_fixed_width(col, DT_PIXEL_APPLY_DPI(24));
526 gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), col);
527 if(out_renderer) *out_renderer = renderer;
528 return col;
529}
530
537static GdkPixbuf *_studio_style_load_icon(const char *icon_name, const double alpha)
538{
539 gint width = 16;
540 gint height = 16;
541 gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height);
542
543 GdkPixbuf *base = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), icon_name, width,
544 GTK_ICON_LOOKUP_FORCE_SIZE, NULL);
545 if(IS_NULL_PTR(base) || alpha >= 1.0) return base;
546
547 cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
548 cairo_t *cr = cairo_create(surface);
549 gdk_cairo_set_source_pixbuf(cr, base, 0, 0);
550 cairo_paint_with_alpha(cr, alpha);
551 cairo_destroy(cr);
552
553 GdkPixbuf *dimmed = gdk_pixbuf_get_from_surface(surface, 0, 0, width, height);
554 cairo_surface_destroy(surface);
555 g_object_unref(base);
556 return dimmed;
557}
558
560{
562 self->data = (void *)d;
563
564 self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
565
566 /* Pool: the ordered, applied styles, with inline reorder/remove icons. */
567 GtkWidget *pool_label = gtk_label_new(_("Pool"));
568 gtk_widget_set_halign(pool_label, GTK_ALIGN_START);
569 gtk_box_pack_start(GTK_BOX(self->widget), pool_label, FALSE, FALSE, 0);
570
571 d->pool_treeview = gtk_tree_view_new();
572 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(d->pool_treeview), FALSE);
573 _studio_style_add_name_column(d->pool_treeview, POOL_COL_NAME, NULL);
574 d->pool_up_col = _studio_style_add_icon_column(d->pool_treeview, "go-up-symbolic", NULL);
575 d->pool_down_col = _studio_style_add_icon_column(d->pool_treeview, "go-down-symbolic", NULL);
576 d->pool_remove_col = _studio_style_add_icon_column(d->pool_treeview, "list-remove-symbolic", NULL);
577 g_signal_connect(d->pool_treeview, "button-press-event", G_CALLBACK(_studio_style_pool_button_pressed), d);
578 gtk_widget_set_has_tooltip(d->pool_treeview, TRUE);
579 g_signal_connect(d->pool_treeview, "query-tooltip", G_CALLBACK(_studio_style_pool_query_tooltip), d);
580
581 gtk_box_pack_start(GTK_BOX(self->widget),
582 dt_ui_scroll_wrap(d->pool_treeview, 100, "plugins/darkroom/studio_style/poolwindowheight",
584 TRUE, TRUE, 0);
585
586 GtkWidget *apply = gtk_button_new_with_label(_("Apply to the displayed image"));
587 gtk_widget_set_tooltip_text(apply, _("Replace the history of the displayed image with the pool of styles"));
588 g_signal_connect(G_OBJECT(apply), "clicked", G_CALLBACK(_studio_style_apply_callback), d);
589 gtk_box_pack_start(GTK_BOX(self->widget), apply, FALSE, FALSE, 0);
590
591 /* Styles: every available style, grouped into categories, with an inline add icon on leaves. */
592 GtkWidget *styles_label = gtk_label_new(_("Styles"));
593 gtk_widget_set_halign(styles_label, GTK_ALIGN_START);
594 gtk_box_pack_start(GTK_BOX(self->widget), styles_label, FALSE, FALSE, 0);
595
596 d->add_icon_enabled = _studio_style_load_icon("list-add-symbolic", 1.0);
597 d->add_icon_disabled = _studio_style_load_icon("list-add-symbolic", 0.35);
598
599 d->styles_treeview = gtk_tree_view_new();
600 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(d->styles_treeview), FALSE);
601 GtkCellRenderer *name_renderer = NULL;
602 GtkTreeViewColumn *name_col
603 = _studio_style_add_name_column(d->styles_treeview, STYLES_COL_NAME, &name_renderer);
604 gtk_tree_view_column_set_cell_data_func(name_col, name_renderer, _studio_style_name_cell_data_func, d, NULL);
605
606 GtkCellRenderer *add_renderer = NULL;
607 d->styles_add_col = _studio_style_add_icon_column(d->styles_treeview, "list-add-symbolic", &add_renderer);
608 /* The cell_data_func drives "pixbuf" on every row; clear the static "icon-name" set by
609 * _studio_style_add_icon_column() so the two properties never compete. */
610 g_object_set(add_renderer, "icon-name", NULL, NULL);
611 gtk_tree_view_column_set_cell_data_func(d->styles_add_col, add_renderer, _studio_style_add_cell_data_func, d,
612 NULL);
613 g_signal_connect(d->styles_treeview, "button-press-event", G_CALLBACK(_studio_style_styles_button_pressed), d);
614 gtk_widget_set_has_tooltip(d->styles_treeview, TRUE);
615 g_signal_connect(d->styles_treeview, "query-tooltip", G_CALLBACK(_studio_style_styles_query_tooltip), d);
616
617 gtk_box_pack_start(GTK_BOX(self->widget),
618 dt_ui_scroll_wrap(d->styles_treeview, 100,
619 "plugins/darkroom/studio_style/styleswindowheight", DT_UI_RESIZE_DYNAMIC),
620 TRUE, TRUE, 0);
621
625
627 G_CALLBACK(_studio_style_changed_callback), d);
628}
629
631{
634 if(!IS_NULL_PTR(d))
635 {
636 g_list_free_full(d->pool, dt_free_gpointer);
637 if(!IS_NULL_PTR(d->add_icon_enabled)) g_object_unref(d->add_icon_enabled);
638 if(!IS_NULL_PTR(d->add_icon_disabled)) g_object_unref(d->add_icon_disabled);
639 }
640 g_free(self->data);
641 self->data = NULL;
642}
643
644// clang-format off
645// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
646// vim: shiftwidth=2 expandtab tabstop=2 cindent
647// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
648// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
int width
Definition bilateral.h:1
int height
Definition bilateral.h:1
void dt_image_history_changed(const int32_t imgid, const gboolean refresh_filmstrip)
char * name
gchar * dt_conf_get_string(const char *name)
void dt_conf_set_string(const char *name, const char *val)
void dt_control_log(const char *msg,...)
Definition control.c:777
darktable_t darktable
Definition darktable.c:183
#define UNKNOWN_IMAGE
Definition darktable.h:194
#define DT_MODULE(MODVER)
Definition darktable.h:140
static void dt_free_gpointer(gpointer ptr)
Definition darktable.h:475
#define dt_free(ptr)
Definition darktable.h:468
#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
int store(dt_imageio_module_storage_t *self, dt_imageio_module_data_t *sdata, const int32_t imgid, dt_imageio_module_format_t *format, dt_imageio_module_data_t *fdata, const int num, const int total, const gboolean high_quality, const gboolean export_masks, dt_colorspaces_color_profile_type_t icc_type, const gchar *icc_filename, dt_iop_color_intent_t icc_intent, dt_export_metadata_t *metadata)
Definition disk.c:252
#define DT_FOLDER_SURVEY_STYLES_SEPARATOR
#define DT_FOLDER_SURVEY_STYLES_CONF_KEY
GtkWidget * dt_ui_scroll_wrap(GtkWidget *w, gint min_size, char *config_str, dt_ui_resize_mode_t mode)
Wrap a scrollable content widget in a recessed, vertically resizable scrolled window.
Definition gtk.c:2945
@ DT_UI_RESIZE_DYNAMIC
Definition gtk.h:266
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
#define DT_PIXEL_APPLY_DPI(value)
Definition gtk.h:90
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
const char * tooltip
Definition image.h:251
const char * model
static const float x
const float v
float *const restrict const size_t k
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
Definition signal.h:387
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
Definition signal.h:366
@ DT_SIGNAL_DEVELOP_HISTORY_CHANGE
This signal is raised when develop history is changed no param, no returned value.
Definition signal.h:204
@ 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
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)
struct dt_control_signal_t * signals
Definition darktable.h:792
GModule *void * data
Definition lib.h:80
GtkWidget * widget
Definition lib.h:84
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...
int32_t dt_view_active_images_get_first()
Definition view.c:1337
@ DT_UI_CONTAINER_PANEL_LEFT_CENTER