Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
studio_import.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 "common/darktable.h"
27#include "bauhaus/bauhaus.h"
28#include "common/datetime.h"
29#include "common/debug.h"
31#include "control/conf.h"
32#include "control/control.h"
34#include "gui/gtk.h"
35#include "gui/gtkentry.h"
36#include "libs/lib.h"
37#include "libs/lib_api.h"
38
39DT_MODULE(1)
40
42{
43 // Source tab
45
46 // Destination tab
48 // Container of every copy-only setting, hidden when "Add to library".
58
59 // Session controls: interval/status at the top, toggle as the notebook's action widget
65
66const char *name(dt_lib_module_t *self)
67{
68 return _("Auto import");
69}
70
71const char **views(dt_lib_module_t *self)
72{
73 static const char *v[] = { "studio_capture", NULL };
74 return v;
75}
76
81
83{
84 return 990;
85}
86
91{
92 const gboolean active = dt_folder_survey_is_active();
93
94 // The engine compares scans against a baseline keyed on the source folder:
95 // both stay locked while monitoring runs.
96 gtk_widget_set_sensitive(d->source_folder, !active);
97 gtk_widget_set_sensitive(d->interval, !active);
98
99 g_signal_handlers_block_matched(d->toggle, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, d);
100 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->toggle), active);
101 g_signal_handlers_unblock_matched(d->toggle, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, d);
102 gtk_button_set_label(GTK_BUTTON(d->toggle), active ? _("Stop the session") : _("Start the session"));
103
104 if(active)
105 {
106 gtk_widget_set_sensitive(d->toggle, TRUE);
107 gtk_widget_hide(d->status_icon);
108 char *folder = dt_conf_get_string("studio_capture/folder");
109 gchar *status = g_strdup_printf(_("Status: Monitoring `%s`"), folder ? folder : "");
110 gtk_label_set_text(GTK_LABEL(d->status), status);
111 dt_free(status);
112 dt_free(folder);
113 return;
114 }
115
116 // Not running: gray out Start until the configuration has the minimum it
117 // needs to succeed, and say why (in the theme's warning color) in the status label.
118 const char *message = NULL;
119 const gboolean ready = dt_folder_survey_can_start(&message);
120 gtk_widget_set_sensitive(d->toggle, ready);
121 gtk_widget_show(d->status_icon);
122 if(ready)
123 {
124 dt_gui_set_symbolic_icon(d->status_icon, "emblem-ok-symbolic", GTK_ICON_SIZE_BUTTON, NULL);
125 gtk_label_set_text(GTK_LABEL(d->status), _("Ready to start the session."));
126 }
127 else
128 {
129 const GdkRGBA *warning = &darktable.gui->colors[DT_GUI_COLOR_WARNING];
130 dt_gui_set_symbolic_icon(d->status_icon, "emblem-important-symbolic", GTK_ICON_SIZE_BUTTON, warning);
131 gchar *color = g_strdup_printf("#%02x%02x%02x", (int)(warning->red * 255), (int)(warning->green * 255),
132 (int)(warning->blue * 255));
133 gchar *markup = g_markup_printf_escaped("<span foreground='%s'>%s</span>", color, message);
134 gtk_label_set_markup(GTK_LABEL(d->status), markup);
135 dt_free(color);
136 dt_free(markup);
137 }
138}
139
141{
142 const gboolean copy = dt_conf_get_bool("studio_capture/copy");
143
144 // The copy-only settings are meaningless when images are added in place.
145 // copy_options carries no-show-all so the framework's blanket show_all on
146 // the module widget cannot re-show it behind our back.
147 if(copy)
148 {
149 gtk_widget_set_no_show_all(d->copy_options, FALSE);
150 gtk_widget_show_all(d->copy_options);
151 gtk_widget_set_no_show_all(d->copy_options, TRUE);
152 }
153 else
154 gtk_widget_hide(d->copy_options);
155
156 if(!copy)
157 {
158 gtk_label_set_text(GTK_LABEL(d->preview), _("No copy. Images will be added from the surveyed folder."));
159 return;
160 }
161
162 char *preview = dt_folder_survey_destination_preview();
163 gtk_label_set_text(GTK_LABEL(d->preview),
164 preview ? preview : _("Can't build a valid destination path. Check the settings above."));
165 dt_free(preview);
166}
167
168static void _studio_import_source_folder_callback(GtkWidget *widget, gpointer user_data)
169{
170 char *folder = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
171 if(!IS_NULL_PTR(folder))
172 {
173 dt_conf_set_string("studio_capture/folder", folder);
174 dt_free(folder);
175 }
177}
178
179static void _studio_import_interval_callback(GtkWidget *widget, gpointer user_data)
180{
181 dt_conf_set_int("studio_capture/interval", gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)));
182}
183
184static void _studio_import_toggle_callback(GtkWidget *widget, gpointer user_data)
185{
187
188 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
190 else
192
193 // On start failure the engine stays inactive: reflect the real state.
195}
196
197static void _studio_import_survey_changed_callback(gpointer instance, gpointer user_data)
198{
200}
201
202static void _studio_import_copy_callback(GtkWidget *widget, gpointer user_data)
203{
204 dt_conf_set_bool("studio_capture/copy", gtk_combo_box_get_active(GTK_COMBO_BOX(widget)) == 1);
207}
208
209static void _studio_import_delete_callback(GtkWidget *widget, gpointer user_data)
210{
211 dt_conf_set_bool("studio_capture/delete_source",
212 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
213}
214
215static void _studio_import_conflict_callback(GtkWidget *widget, gpointer user_data)
216{
217 dt_conf_set_int("studio_capture/on_conflict", gtk_combo_box_get_active(GTK_COMBO_BOX(widget)));
218}
219
220static void _studio_import_datetime_callback(GtkWidget *widget, gpointer user_data)
221{
222 dt_conf_set_string("studio_capture/datetime", gtk_entry_get_text(GTK_ENTRY(widget)));
225}
226
227static void _studio_import_update_date(GtkCalendar *calendar, GtkWidget *entry)
228{
229 guint year, month, day;
230 gtk_calendar_get_date(calendar, &year, &month, &day);
231 GTimeZone *tz = g_time_zone_new_local();
232
233 // GDateTime counts months from 1 but GtkCalendar from 0.
234 GDateTime *datetime = g_date_time_new(tz, year, month + 1, day, 0, 0, 0.);
235 g_time_zone_unref(tz);
236 gchar *date = g_date_time_format(datetime, "%F");
237 gtk_entry_set_text(GTK_ENTRY(entry), date);
238 dt_free(date);
239 g_date_time_unref(datetime);
240}
241
242static void _studio_import_jobcode_callback(GtkWidget *widget, gpointer user_data)
243{
244 dt_conf_set_string("studio_capture/jobcode", gtk_entry_get_text(GTK_ENTRY(widget)));
247}
248
249static void _studio_import_base_folder_callback(GtkWidget *widget, gpointer user_data)
250{
251 char *folder = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
252 if(!IS_NULL_PTR(folder))
253 {
254 dt_conf_set_string("studio_capture/base_directory_pattern", folder);
255 dt_free(folder);
256 }
259}
260
261static void _studio_import_subfolder_callback(GtkWidget *widget, gpointer user_data)
262{
263 dt_conf_set_string("studio_capture/sub_directory_pattern", gtk_entry_get_text(GTK_ENTRY(widget)));
266}
267
268static void _studio_import_file_pattern_callback(GtkWidget *widget, gpointer user_data)
269{
270 dt_conf_set_string("studio_capture/filename_pattern", gtk_entry_get_text(GTK_ENTRY(widget)));
273}
274
278static void _studio_import_pack_row(GtkBox *parent, const char *label_text, GtkWidget *widget)
279{
280 GtkWidget *row = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
281 GtkWidget *label = gtk_label_new(label_text);
282 gtk_widget_set_halign(label, GTK_ALIGN_START);
283 gtk_box_pack_start(GTK_BOX(row), label, FALSE, FALSE, 0);
284 gtk_box_pack_start(GTK_BOX(row), widget, FALSE, FALSE, 0);
285 gtk_box_pack_start(parent, row, FALSE, FALSE, 0);
286}
287
289{
291 self->data = (void *)d;
292
293 self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
294
295 /* Session controls: scan frequency + status, at the top of the module */
296
297 GtkWidget *control_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
298
299 GtkWidget *datetime_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
300 {
301 GtkWidget *datetime_label = gtk_label_new(_("Project date"));
302 gtk_widget_set_halign(datetime_label, GTK_ALIGN_START);
303 gtk_box_pack_start(GTK_BOX(datetime_box), datetime_label, FALSE, FALSE, 0);
304
305 d->datetime = gtk_entry_new();
306 gtk_entry_set_text(GTK_ENTRY(d->datetime), dt_conf_get_string_const("studio_capture/datetime"));
307 gtk_entry_set_placeholder_text(GTK_ENTRY(d->datetime), _("Current date at import time"));
308 gtk_widget_set_tooltip_text(d->datetime,
309 _("Format: YYYY-MM-DD, optionally followed by HH:MM:SS.mmm\n"
310 "Partial values are completed with defaults, e.g. \"2026\" becomes "
311 "2026-01-01.\n"
312 "Leave empty to use the current date at import time."));
313 g_signal_connect(G_OBJECT(d->datetime), "changed", G_CALLBACK(_studio_import_datetime_callback), d);
314
315 // Same calendar-popover date picker as the regular Import dialog (common/import.c).
316 GtkWidget *calendar = gtk_calendar_new();
317 GDateTime *now = g_date_time_new_now_local();
318 // GtkCalendar uses months in [0:11]. Glib GDateTime returns months in [1:12].
319 gtk_calendar_select_month(GTK_CALENDAR(calendar), g_date_time_get_month(now) - 1, g_date_time_get_year(now));
320 const guint today = g_date_time_get_day_of_month(now);
321 gtk_calendar_select_day(GTK_CALENDAR(calendar), today);
322 gtk_calendar_mark_day(GTK_CALENDAR(calendar), today);
323 g_date_time_unref(now);
324 GtkBox *box_datetime = attach_popover(d->datetime, "appointment-new-symbolic", calendar);
325 g_signal_connect(G_OBJECT(calendar), "day-selected", G_CALLBACK(_studio_import_update_date), d->datetime);
326 gtk_widget_set_valign(GTK_WIDGET(box_datetime), GTK_ALIGN_START);
327
328 gtk_box_pack_start(GTK_BOX(datetime_box), GTK_WIDGET(box_datetime), FALSE, FALSE, 0);
329
330 }
331 gtk_box_pack_start(GTK_BOX(control_box), datetime_box, FALSE, FALSE, 0);
332
333 GtkWidget *jobcode_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
334 {
335 GtkWidget *jobcode_label = gtk_label_new(_("Jobcode"));
336 gtk_widget_set_halign(jobcode_label, GTK_ALIGN_START);
337 gtk_box_pack_start(GTK_BOX(jobcode_box), jobcode_label, FALSE, FALSE, 0);
338
339 d->jobcode = gtk_entry_new();
340 gtk_entry_set_text(GTK_ENTRY(d->jobcode), dt_conf_get_string_const("studio_capture/jobcode"));
341 g_signal_connect(G_OBJECT(d->jobcode), "changed", G_CALLBACK(_studio_import_jobcode_callback), d);
342 gtk_box_pack_start(GTK_BOX(jobcode_box), d->jobcode, TRUE, TRUE, 0);
343 }
344 gtk_box_pack_start(GTK_BOX(control_box), jobcode_box, TRUE, TRUE, 0);
345
346 GtkWidget *interval_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
347 {
348 GtkWidget *interval_label = gtk_label_new(_("Scan frequency (seconds)"));
349 gtk_widget_set_halign(interval_label, GTK_ALIGN_START);
350 gtk_box_pack_start(GTK_BOX(interval_box), interval_label, FALSE, FALSE, 0);
351
352 d->interval = gtk_spin_button_new_with_range(2, 3600, 1);
353 gtk_spin_button_set_value(GTK_SPIN_BUTTON(d->interval),
354 CLAMP(dt_conf_get_int("studio_capture/interval"), 2, 60));
355 gtk_widget_set_tooltip_text(d->interval, _("Applied the next time the session is started"));
356 g_signal_connect(G_OBJECT(d->interval), "value-changed", G_CALLBACK(_studio_import_interval_callback), d);
357 gtk_box_pack_start(GTK_BOX(interval_box), d->interval, TRUE, TRUE, 0);
358 }
359 gtk_box_pack_start(GTK_BOX(control_box), interval_box, TRUE, TRUE, 0);
360
361 d->status = gtk_label_new("");
362 gtk_widget_set_halign(d->status, GTK_ALIGN_START);
363 gtk_label_set_line_wrap(GTK_LABEL(d->status), TRUE);
364 gtk_label_set_ellipsize(GTK_LABEL(d->status), PANGO_ELLIPSIZE_MIDDLE);
365 gtk_label_set_lines(GTK_LABEL(d->status), 2);
366 PangoLayout *status_layout = gtk_widget_create_pango_layout(d->status, "Xg\nXg");
367 int status_width, status_height;
368 pango_layout_get_pixel_size(status_layout, &status_width, &status_height);
369 g_object_unref(status_layout);
370 gtk_widget_set_size_request(d->status, -1, status_height);
371
372 d->status_icon = gtk_image_new_from_icon_name("emblem-ok-symbolic", GTK_ICON_SIZE_BUTTON);
373 gtk_widget_set_valign(d->status_icon, GTK_ALIGN_CENTER);
374 gtk_widget_set_no_show_all(d->status_icon, TRUE);
375
376 GtkWidget *status_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
377 gtk_box_pack_start(GTK_BOX(status_box), d->status_icon, FALSE, FALSE, 0);
378 gtk_box_pack_start(GTK_BOX(status_box), d->status, TRUE, TRUE, 0);
379 gtk_box_pack_start(GTK_BOX(control_box), status_box, FALSE, FALSE, 0);
380 gtk_widget_set_margin_bottom(control_box, DT_GUI_BOX_SPACING);
381
382 gtk_box_pack_start(GTK_BOX(self->widget), control_box, FALSE, FALSE, 0);
383
384
385
386
387
388 GtkNotebook *notebook = dt_ui_notebook_new();
389 GtkWidget *source_page
390 = dt_ui_notebook_page(notebook, _("Source"), _("Folder monitored during the session"));
391 GtkWidget *destination_page
392 = dt_ui_notebook_page(notebook, _("Destination"), _("Where and how the captured images are imported"));
393 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(notebook), FALSE, FALSE, 0);
394
395 /* Start/Stop sits on the tab row itself, to the right of the tab labels. */
396 d->toggle = gtk_toggle_button_new_with_label(_("Start the session"));
397 gtk_widget_set_tooltip_text(d->toggle,
398 _("Monitor the source folder and import new images as they appear.\n"
399 "The first scan records the existing images as a baseline: only images "
400 "arriving afterwards are imported."));
401 g_signal_connect(G_OBJECT(d->toggle), "toggled", G_CALLBACK(_studio_import_toggle_callback), d);
402 gtk_notebook_set_action_widget(notebook, d->toggle, GTK_PACK_END);
403 gtk_widget_show(d->toggle);
404
405 /* Source tab */
406
407 GtkWidget *source_folder_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
408 {
409 GtkWidget *source_folder_label = gtk_label_new(_("Source"));
410 gtk_widget_set_halign(source_folder_label, GTK_ALIGN_START);
411 gtk_box_pack_start(GTK_BOX(source_folder_box), source_folder_label, FALSE, FALSE, 0);
412
413 d->source_folder = gtk_file_chooser_button_new(_("Select a folder to survey"),
414 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
415 const char *folder = dt_conf_get_string_const("studio_capture/folder");
416 if(!IS_NULL_PTR(folder) && folder[0])
417 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(d->source_folder), folder);
418 gtk_widget_set_tooltip_text(d->source_folder, _("Folder receiving the captured images to monitor"));
419 g_signal_connect(G_OBJECT(d->source_folder), "file-set",
421 gtk_box_pack_start(GTK_BOX(source_folder_box), d->source_folder, TRUE, TRUE, 0);
422 }
423 gtk_box_pack_start(GTK_BOX(source_page), source_folder_box, FALSE, FALSE, 0);
424
425 d->delete_source = gtk_check_button_new_with_label(_("Delete original file"));
426 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->delete_source),
427 dt_conf_get_bool("studio_capture/delete_source"));
428 gtk_widget_set_tooltip_text(d->delete_source,
429 _("Delete the original image file after verifying the complete copy."));
430 g_signal_connect(G_OBJECT(d->delete_source), "toggled", G_CALLBACK(_studio_import_delete_callback), d);
431 gtk_box_pack_start(GTK_BOX(source_page), d->delete_source, FALSE, FALSE, 0);
432
433 /* Destination tab */
436 GINT_TO_POINTER(0), NULL, TRUE);
438 GINT_TO_POINTER(1), NULL, TRUE);
439 dt_bauhaus_combobox_set(d->copy, dt_conf_get_bool("studio_capture/copy"));
440 g_signal_connect(G_OBJECT(d->copy), "value-changed", G_CALLBACK(_studio_import_copy_callback), d);
441 gtk_box_pack_start(GTK_BOX(destination_page), d->copy, FALSE, FALSE, 0);
442
443
444 d->copy_options = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
445 gtk_widget_set_no_show_all(d->copy_options, TRUE);
446 gtk_box_pack_start(GTK_BOX(destination_page), d->copy_options, FALSE, FALSE, 0);
447
449 {
450 dt_bauhaus_widget_set_label(d->on_conflict, N_("On conflict"));
452 GINT_TO_POINTER(0), NULL, TRUE);
454 GINT_TO_POINTER(1), NULL, TRUE);
455 dt_bauhaus_combobox_add_full(d->on_conflict, _("Create unique filename"), DT_BAUHAUS_COMBOBOX_ALIGN_RIGHT,
456 GINT_TO_POINTER(2), NULL, TRUE);
457 dt_bauhaus_combobox_set(d->on_conflict, CLAMP(dt_conf_get_int("studio_capture/on_conflict"), DT_IMPORT_ONCONFLICT_SKIP,
459 dt_bauhaus_disable_accels(d->on_conflict);
460 gtk_widget_set_tooltip_text(d->on_conflict, _("Expected behaviour when the naming pattern produces a destination file "
461 "that already exists"));
462 g_signal_connect(G_OBJECT(d->on_conflict), "value-changed", G_CALLBACK(_studio_import_conflict_callback), d);
463 }
464 gtk_box_pack_start(GTK_BOX(d->copy_options), d->on_conflict, TRUE, TRUE, 0);
465
466 GtkWidget *base_folder_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
467 {
468 GtkWidget *base_folder_label = gtk_label_new(_("Base directory"));
469 gtk_widget_set_halign(base_folder_label, GTK_ALIGN_START);
470 gtk_box_pack_start(GTK_BOX(base_folder_box), base_folder_label, TRUE, TRUE, 0);
471
472 d->base_folder = gtk_file_chooser_button_new(_("Select a base directory"), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
473 const char *base_folder = dt_conf_get_string_const("studio_capture/base_directory_pattern");
474 if(!IS_NULL_PTR(base_folder) && base_folder[0])
475 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(d->base_folder), base_folder);
476 g_signal_connect(G_OBJECT(d->base_folder), "file-set", G_CALLBACK(_studio_import_base_folder_callback), d);
477 gtk_box_pack_end(GTK_BOX(base_folder_box), d->base_folder, TRUE, TRUE, 0);
478 }
479 gtk_box_pack_start(GTK_BOX(d->copy_options), base_folder_box, TRUE, TRUE, 0);
480
481 d->subfolder_pattern = gtk_entry_new();
482 gtk_entry_set_text(GTK_ENTRY(d->subfolder_pattern),
483 dt_conf_get_string_const("studio_capture/sub_directory_pattern"));
485 "$(");
486 gtk_widget_set_tooltip_text(d->subfolder_pattern,
487 _("Start typing `$(` to see available variables through auto-completion"));
488 g_signal_connect(G_OBJECT(d->subfolder_pattern), "changed",
490 _studio_import_pack_row(GTK_BOX(d->copy_options), _("Project directory pattern"), d->subfolder_pattern);
491
492 d->file_pattern = gtk_entry_new();
493 gtk_entry_set_text(GTK_ENTRY(d->file_pattern), dt_conf_get_string_const("studio_capture/filename_pattern"));
495 gtk_widget_set_tooltip_text(d->file_pattern,
496 _("Start typing `$(` to see available variables through auto-completion"));
497 g_signal_connect(G_OBJECT(d->file_pattern), "changed",
499 _studio_import_pack_row(GTK_BOX(d->copy_options), _("File naming pattern"), d->file_pattern);
500
501 d->preview = gtk_label_new("");
502 gtk_widget_set_halign(d->preview, GTK_ALIGN_START);
503 gtk_label_set_ellipsize(GTK_LABEL(d->preview), PANGO_ELLIPSIZE_MIDDLE);
504 gtk_label_set_line_wrap(GTK_LABEL(d->preview), TRUE);
505 gtk_box_pack_start(GTK_BOX(destination_page), d->preview, FALSE, FALSE, 0);
506
509
512}
513
514void view_enter(struct dt_lib_module_t *self, struct dt_view_t *old_view, struct dt_view_t *new_view)
515{
516 // The session may have been resumed or halted outside this module.
518}
519
521{
523 self->data);
524 g_free(self->data);
525 self->data = NULL;
526}
527
528// clang-format off
529// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
530// vim: shiftwidth=2 expandtab tabstop=2 cindent
531// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
532// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
void dt_bauhaus_combobox_add_full(GtkWidget *widget, const char *text, dt_bauhaus_combobox_alignment_t align, gpointer data, void(free_func)(void *data), gboolean sensitive)
Definition bauhaus.c:2041
void dt_bauhaus_disable_accels(GtkWidget *widget)
Definition bauhaus.c:3957
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2304
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1656
GtkWidget * dt_bauhaus_combobox_new(dt_bauhaus_t *bh, dt_gui_module_t *self)
Definition bauhaus.c:1845
@ DT_BAUHAUS_COMBOBOX_ALIGN_RIGHT
Definition bauhaus.h:125
static const int row
char * name
void dt_conf_set_bool(const char *name, int val)
int dt_conf_get_bool(const char *name)
gchar * dt_conf_get_string(const char *name)
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
void dt_conf_set_string(const char *name, const char *val)
const char * dt_conf_get_string_const(const char *name)
darktable_t darktable
Definition darktable.c:183
#define DT_MODULE(MODVER)
Definition darktable.h:140
#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 dt_folder_survey_start()
Validate the persisted configuration and start monitoring.
char * dt_folder_survey_destination_preview()
Build the expanded destination path of a sample file from the current configuration,...
gboolean dt_folder_survey_is_active()
TRUE while the periodic folder scan is running.
gboolean dt_folder_survey_can_start(const char **message)
Check, without any side effect, whether the persisted configuration has everything dt_folder_survey_s...
void dt_folder_survey_halt()
User-requested stop: end monitoring and clear the persisted session marker so the next application st...
GtkWidget * dt_ui_notebook_page(GtkNotebook *notebook, const char *text, const char *tooltip)
Definition gtk.c:2470
GtkNotebook * dt_ui_notebook_new()
Definition gtk.c:2465
void dt_gui_set_symbolic_icon(GtkWidget *image, const char *icon_name, GtkIconSize size, const GdkRGBA *color)
Set a symbolic icon on an image widget, optionally forcing a specific color.
Definition gtk.c:153
GtkBox * attach_popover(GtkWidget *widget, const char *icon, GtkWidget *content)
Definition gtk.c:3411
@ DT_GUI_COLOR_WARNING
Definition gtk.h:158
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
void dt_gtkentry_setup_completion(GtkEntry *entry, const dt_gtkentry_completion_spec *compl_list, const char *trigger_char)
Definition gtkentry.c:173
const dt_gtkentry_completion_spec * dt_gtkentry_get_default_path_compl_list()
Definition gtkentry.c:197
#define DT_GUI_MODULE(x)
@ DT_IMPORT_ONCONFLICT_UNIQUE
Definition import_jobs.h:39
@ DT_IMPORT_ONCONFLICT_SKIP
Definition import_jobs.h:37
const float v
void copy(double *dest, double *source, size_t num_el)
Copy a flat buffer.
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
Definition signal.h:387
@ DT_SIGNAL_FOLDER_SURVEY_CHANGED
Raised when the folder survey starts or stops monitoring. no param, no returned value.
Definition signal.h:330
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:376
struct _GtkWidget GtkWidget
Definition splash.h:29
static const char *const day[7]
Definition strptime.c:97
struct dt_gui_gtk_t * gui
Definition darktable.h:793
struct dt_control_signal_t * signals
Definition darktable.h:792
struct dt_bauhaus_t * bauhaus
Definition darktable.h:796
GdkRGBA colors[DT_GUI_COLOR_LAST]
Definition gtk.h:178
GModule *void * data
Definition lib.h:80
GtkWidget * widget
Definition lib.h:84
GtkWidget * subfolder_pattern
static void _studio_import_file_pattern_callback(GtkWidget *widget, gpointer user_data)
static void _studio_import_source_folder_callback(GtkWidget *widget, gpointer user_data)
static void _studio_import_toggle_callback(GtkWidget *widget, gpointer user_data)
static void _studio_import_interval_callback(GtkWidget *widget, gpointer user_data)
static void _studio_import_update_date(GtkCalendar *calendar, GtkWidget *entry)
void gui_cleanup(dt_lib_module_t *self)
static void _studio_import_pack_row(GtkBox *parent, const char *label_text, GtkWidget *widget)
Pack one field as a start-aligned label above its input widget.
static void _studio_import_jobcode_callback(GtkWidget *widget, gpointer user_data)
static void _studio_import_delete_callback(GtkWidget *widget, gpointer user_data)
uint32_t container(dt_lib_module_t *self)
static void _studio_import_copy_callback(GtkWidget *widget, gpointer user_data)
static void _studio_import_update_preview(dt_lib_studio_import_t *d)
static void _studio_import_update_state(dt_lib_studio_import_t *d)
Sync the session controls and locks with the engine state.
static void _studio_import_datetime_callback(GtkWidget *widget, gpointer user_data)
static void _studio_import_conflict_callback(GtkWidget *widget, gpointer user_data)
void gui_init(dt_lib_module_t *self)
void view_enter(struct dt_lib_module_t *self, struct dt_view_t *old_view, struct dt_view_t *new_view)
int position()
const char ** views(dt_lib_module_t *self)
static void _studio_import_survey_changed_callback(gpointer instance, gpointer user_data)
static void _studio_import_subfolder_callback(GtkWidget *widget, gpointer user_data)
static void _studio_import_base_folder_callback(GtkWidget *widget, gpointer user_data)
GtkWidget * notebook
@ DT_UI_CONTAINER_PANEL_LEFT_CENTER