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 "system/macros.h"
27#include "system/mem_alloc.h"
29#include "control/signal.h"
30#include "widgets/bauhaus.h"
32#include "common/conf.h"
34#include "widgets/gtkentry.h"
35#include "libs/lib.h"
36#include "libs/lib_api.h"
37#include "gui/window_manager.h"
38#include "widgets/label.h"
39#include "widgets/notebook.h"
40#include "widgets/popup.h"
41
42DT_MODULE(1)
43
45{
46 // Source tab
48
49 // Destination tab
51 // Container of every copy-only setting, hidden when "Add to library".
61
62 // Session controls: interval/status at the top, toggle as the notebook's action widget
68
69const char *name(dt_lib_module_t *self)
70{
71 return _("Auto import");
72}
73
74const char **views(dt_lib_module_t *self)
75{
76 static const char *v[] = { "studio_capture", NULL };
77 return v;
78}
79
84
86{
87 return 990;
88}
89
94{
95 const gboolean active = dt_folder_survey_is_active();
96
97 // The engine compares scans against a baseline keyed on the source folder:
98 // both stay locked while monitoring runs.
99 gtk_widget_set_sensitive(d->source_folder, !active);
100 gtk_widget_set_sensitive(d->interval, !active);
101
102 g_signal_handlers_block_matched(d->toggle, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, d);
103 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->toggle), active);
104 g_signal_handlers_unblock_matched(d->toggle, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, d);
105 gtk_button_set_label(GTK_BUTTON(d->toggle), active ? _("Stop the session") : _("Start the session"));
106
107 if(active)
108 {
109 gtk_widget_set_sensitive(d->toggle, TRUE);
110 gtk_widget_hide(d->status_icon);
111 char *folder = dt_conf_get_string("studio_capture/folder");
112 gchar *status = g_strdup_printf(_("Status: Monitoring `%s`"), folder ? folder : "");
113 gtk_label_set_text(GTK_LABEL(d->status), status);
116 return;
117 }
118
119 // Not running: gray out Start until the configuration has the minimum it
120 // needs to succeed, and say why (in the theme's warning color) in the status label.
121 const char *message = NULL;
122 const gboolean ready = dt_folder_survey_can_start(&message);
123 gtk_widget_set_sensitive(d->toggle, ready);
124 gtk_widget_show(d->status_icon);
125 if(ready)
126 {
127 dt_gui_set_symbolic_icon(d->status_icon, "emblem-ok-symbolic", GTK_ICON_SIZE_BUTTON, NULL);
128 gtk_label_set_text(GTK_LABEL(d->status), _("Ready to start the session."));
129 }
130 else
131 {
132 const GdkRGBA *warning = &dt_widget_colors()[DT_GUI_COLOR_WARNING];
133 dt_gui_set_symbolic_icon(d->status_icon, "emblem-important-symbolic", GTK_ICON_SIZE_BUTTON, warning);
134 gchar *color = g_strdup_printf("#%02x%02x%02x", (int)(warning->red * 255), (int)(warning->green * 255),
135 (int)(warning->blue * 255));
136 gchar *markup = g_markup_printf_escaped("<span foreground='%s'>%s</span>", color, message);
137 gtk_label_set_markup(GTK_LABEL(d->status), markup);
138 dt_free(color);
139 dt_free(markup);
140 }
141}
142
144{
145 const gboolean copy = dt_conf_get_bool("studio_capture/copy");
146
147 // The copy-only settings are meaningless when images are added in place.
148 // copy_options carries no-show-all so the framework's blanket show_all on
149 // the module widget cannot re-show it behind our back.
150 if(copy)
151 {
152 gtk_widget_set_no_show_all(d->copy_options, FALSE);
153 gtk_widget_show_all(d->copy_options);
154 gtk_widget_set_no_show_all(d->copy_options, TRUE);
155 }
156 else
157 gtk_widget_hide(d->copy_options);
158
159 if(!copy)
160 {
161 gtk_label_set_text(GTK_LABEL(d->preview), _("No copy. Images will be added from the surveyed folder."));
162 return;
163 }
164
166 gtk_label_set_text(GTK_LABEL(d->preview),
167 preview ? preview : _("Can't build a valid destination path. Check the settings above."));
169}
170
171static void _studio_import_source_folder_callback(GtkWidget *widget, gpointer user_data)
172{
173 char *folder = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
174 if(!IS_NULL_PTR(folder))
175 {
176 dt_conf_set_string("studio_capture/folder", folder);
178 }
180}
181
182static void _studio_import_interval_callback(GtkWidget *widget, gpointer user_data)
183{
184 dt_conf_set_int("studio_capture/interval", gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(widget)));
185}
186
187static void _studio_import_toggle_callback(GtkWidget *widget, gpointer user_data)
188{
190
191 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
193 else
195
196 // On start failure the engine stays inactive: reflect the real state.
198}
199
200static void _studio_import_survey_changed_callback(gpointer instance, gpointer user_data)
201{
203}
204
205static void _studio_import_copy_callback(GtkWidget *widget, gpointer user_data)
206{
207 dt_conf_set_bool("studio_capture/copy", gtk_combo_box_get_active(GTK_COMBO_BOX(widget)) == 1);
210}
211
212static void _studio_import_delete_callback(GtkWidget *widget, gpointer user_data)
213{
214 dt_conf_set_bool("studio_capture/delete_source",
215 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)));
216}
217
218static void _studio_import_conflict_callback(GtkWidget *widget, gpointer user_data)
219{
220 dt_conf_set_int("studio_capture/on_conflict", gtk_combo_box_get_active(GTK_COMBO_BOX(widget)));
221}
222
223static void _studio_import_datetime_callback(GtkWidget *widget, gpointer user_data)
224{
225 dt_conf_set_string("studio_capture/datetime", gtk_entry_get_text(GTK_ENTRY(widget)));
228}
229
230static void _studio_import_update_date(GtkCalendar *calendar, GtkWidget *entry)
231{
232 guint year, month, day;
233 gtk_calendar_get_date(calendar, &year, &month, &day);
234 GTimeZone *tz = g_time_zone_new_local();
235
236 // GDateTime counts months from 1 but GtkCalendar from 0.
237 GDateTime *datetime = g_date_time_new(tz, year, month + 1, day, 0, 0, 0.);
238 g_time_zone_unref(tz);
239 gchar *date = g_date_time_format(datetime, "%F");
240 gtk_entry_set_text(GTK_ENTRY(entry), date);
241 dt_free(date);
242 g_date_time_unref(datetime);
243}
244
245static void _studio_import_jobcode_callback(GtkWidget *widget, gpointer user_data)
246{
247 dt_conf_set_string("studio_capture/jobcode", gtk_entry_get_text(GTK_ENTRY(widget)));
250}
251
252static void _studio_import_base_folder_callback(GtkWidget *widget, gpointer user_data)
253{
254 char *folder = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
255 if(!IS_NULL_PTR(folder))
256 {
257 dt_conf_set_string("studio_capture/base_directory_pattern", folder);
259 }
262}
263
264static void _studio_import_subfolder_callback(GtkWidget *widget, gpointer user_data)
265{
266 dt_conf_set_string("studio_capture/sub_directory_pattern", gtk_entry_get_text(GTK_ENTRY(widget)));
269}
270
271static void _studio_import_file_pattern_callback(GtkWidget *widget, gpointer user_data)
272{
273 dt_conf_set_string("studio_capture/filename_pattern", gtk_entry_get_text(GTK_ENTRY(widget)));
276}
277
281static void _studio_import_pack_row(GtkBox *parent, const char *label_text, GtkWidget *widget)
282{
283 GtkWidget *row = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
284 GtkWidget *label = gtk_label_new(label_text);
285 gtk_widget_set_halign(label, GTK_ALIGN_START);
286 gtk_box_pack_start(GTK_BOX(row), label, FALSE, FALSE, 0);
287 gtk_box_pack_start(GTK_BOX(row), widget, FALSE, FALSE, 0);
288 gtk_box_pack_start(parent, row, FALSE, FALSE, 0);
289}
290
292{
294 self->data = (void *)d;
295
296 self->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
297
298 /* Session controls: scan frequency + status, at the top of the module */
299
300 GtkWidget *control_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
301
302 GtkWidget *datetime_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
303 {
304 GtkWidget *datetime_label = gtk_label_new(_("Project date"));
305 gtk_widget_set_halign(datetime_label, GTK_ALIGN_START);
306 gtk_box_pack_start(GTK_BOX(datetime_box), datetime_label, FALSE, FALSE, 0);
307
308 d->datetime = gtk_entry_new();
309 gtk_entry_set_text(GTK_ENTRY(d->datetime), dt_conf_get_string_const("studio_capture/datetime"));
310 gtk_entry_set_placeholder_text(GTK_ENTRY(d->datetime), _("Current date at import time"));
311 gtk_widget_set_tooltip_text(d->datetime,
312 _("Format: YYYY-MM-DD, optionally followed by HH:MM:SS.mmm\n"
313 "Partial values are completed with defaults, e.g. \"2026\" becomes "
314 "2026-01-01.\n"
315 "Leave empty to use the current date at import time."));
316 g_signal_connect(G_OBJECT(d->datetime), "changed", G_CALLBACK(_studio_import_datetime_callback), d);
317
318 // Same calendar-popover date picker as the regular Import dialog (common/import.c).
319 GtkWidget *calendar = gtk_calendar_new();
320 GDateTime *now = g_date_time_new_now_local();
321 // GtkCalendar uses months in [0:11]. Glib GDateTime returns months in [1:12].
322 gtk_calendar_select_month(GTK_CALENDAR(calendar), g_date_time_get_month(now) - 1, g_date_time_get_year(now));
323 const guint today = g_date_time_get_day_of_month(now);
324 gtk_calendar_select_day(GTK_CALENDAR(calendar), today);
325 gtk_calendar_mark_day(GTK_CALENDAR(calendar), today);
326 g_date_time_unref(now);
327 GtkBox *box_datetime = attach_popover(d->datetime, "appointment-new-symbolic", calendar);
328 g_signal_connect(G_OBJECT(calendar), "day-selected", G_CALLBACK(_studio_import_update_date), d->datetime);
329 gtk_widget_set_valign(GTK_WIDGET(box_datetime), GTK_ALIGN_START);
330
331 gtk_box_pack_start(GTK_BOX(datetime_box), GTK_WIDGET(box_datetime), FALSE, FALSE, 0);
332
333 }
334 gtk_box_pack_start(GTK_BOX(control_box), datetime_box, FALSE, FALSE, 0);
335
336 GtkWidget *jobcode_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
337 {
338 GtkWidget *jobcode_label = gtk_label_new(_("Jobcode"));
339 gtk_widget_set_halign(jobcode_label, GTK_ALIGN_START);
340 gtk_box_pack_start(GTK_BOX(jobcode_box), jobcode_label, FALSE, FALSE, 0);
341
342 d->jobcode = gtk_entry_new();
343 gtk_entry_set_text(GTK_ENTRY(d->jobcode), dt_conf_get_string_const("studio_capture/jobcode"));
344 g_signal_connect(G_OBJECT(d->jobcode), "changed", G_CALLBACK(_studio_import_jobcode_callback), d);
345 gtk_box_pack_start(GTK_BOX(jobcode_box), d->jobcode, TRUE, TRUE, 0);
346 }
347 gtk_box_pack_start(GTK_BOX(control_box), jobcode_box, TRUE, TRUE, 0);
348
349 GtkWidget *interval_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
350 {
351 GtkWidget *interval_label = gtk_label_new(_("Scan frequency (seconds)"));
352 gtk_widget_set_halign(interval_label, GTK_ALIGN_START);
353 gtk_box_pack_start(GTK_BOX(interval_box), interval_label, FALSE, FALSE, 0);
354
355 d->interval = gtk_spin_button_new_with_range(2, 3600, 1);
356 gtk_spin_button_set_value(GTK_SPIN_BUTTON(d->interval),
357 CLAMP(dt_conf_get_int("studio_capture/interval"), 2, 60));
358 gtk_widget_set_tooltip_text(d->interval, _("Applied the next time the session is started"));
359 g_signal_connect(G_OBJECT(d->interval), "value-changed", G_CALLBACK(_studio_import_interval_callback), d);
360 gtk_box_pack_start(GTK_BOX(interval_box), d->interval, TRUE, TRUE, 0);
361 }
362 gtk_box_pack_start(GTK_BOX(control_box), interval_box, TRUE, TRUE, 0);
363
364 d->status = gtk_label_new("");
365 gtk_widget_set_halign(d->status, GTK_ALIGN_START);
366 gtk_label_set_line_wrap(GTK_LABEL(d->status), TRUE);
367 gtk_label_set_ellipsize(GTK_LABEL(d->status), PANGO_ELLIPSIZE_MIDDLE);
368 gtk_label_set_lines(GTK_LABEL(d->status), 2);
369 PangoLayout *status_layout = gtk_widget_create_pango_layout(d->status, "Xg\nXg");
370 int status_width, status_height;
371 pango_layout_get_pixel_size(status_layout, &status_width, &status_height);
372 g_object_unref(status_layout);
373 gtk_widget_set_size_request(d->status, -1, status_height);
374
375 d->status_icon = gtk_image_new_from_icon_name("emblem-ok-symbolic", GTK_ICON_SIZE_BUTTON);
376 gtk_widget_set_valign(d->status_icon, GTK_ALIGN_CENTER);
377 gtk_widget_set_no_show_all(d->status_icon, TRUE);
378
379 GtkWidget *status_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
380 gtk_box_pack_start(GTK_BOX(status_box), d->status_icon, FALSE, FALSE, 0);
381 gtk_box_pack_start(GTK_BOX(status_box), d->status, TRUE, TRUE, 0);
382 gtk_box_pack_start(GTK_BOX(control_box), status_box, FALSE, FALSE, 0);
383 gtk_widget_set_margin_bottom(control_box, DT_GUI_BOX_SPACING);
384
385 gtk_box_pack_start(GTK_BOX(self->widget), control_box, FALSE, FALSE, 0);
386
387
388
389
390
391 GtkNotebook *notebook = dt_ui_notebook_new();
392 GtkWidget *source_page
393 = dt_ui_notebook_page(notebook, _("Source"), _("Folder monitored during the session"));
394 GtkWidget *destination_page
395 = dt_ui_notebook_page(notebook, _("Destination"), _("Where and how the captured images are imported"));
396 gtk_box_pack_start(GTK_BOX(self->widget), GTK_WIDGET(notebook), FALSE, FALSE, 0);
397
398 /* Start/Stop sits on the tab row itself, to the right of the tab labels. */
399 d->toggle = gtk_toggle_button_new_with_label(_("Start the session"));
400 gtk_widget_set_tooltip_text(d->toggle,
401 _("Monitor the source folder and import new images as they appear.\n"
402 "The first scan records the existing images as a baseline: only images "
403 "arriving afterwards are imported."));
404 g_signal_connect(G_OBJECT(d->toggle), "toggled", G_CALLBACK(_studio_import_toggle_callback), d);
405 gtk_notebook_set_action_widget(notebook, d->toggle, GTK_PACK_END);
406 gtk_widget_show(d->toggle);
407
408 /* Source tab */
409
410 GtkWidget *source_folder_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
411 {
412 GtkWidget *source_folder_label = gtk_label_new(_("Source"));
413 gtk_widget_set_halign(source_folder_label, GTK_ALIGN_START);
414 gtk_box_pack_start(GTK_BOX(source_folder_box), source_folder_label, FALSE, FALSE, 0);
415
416 d->source_folder = gtk_file_chooser_button_new(_("Select a folder to survey"),
417 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
418 const char *folder = dt_conf_get_string_const("studio_capture/folder");
419 if(!IS_NULL_PTR(folder) && folder[0])
420 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(d->source_folder), folder);
421 gtk_widget_set_tooltip_text(d->source_folder, _("Folder receiving the captured images to monitor"));
422 g_signal_connect(G_OBJECT(d->source_folder), "file-set",
424 gtk_box_pack_start(GTK_BOX(source_folder_box), d->source_folder, TRUE, TRUE, 0);
425 }
426 gtk_box_pack_start(GTK_BOX(source_page), source_folder_box, FALSE, FALSE, 0);
427
428 d->delete_source = gtk_check_button_new_with_label(_("Delete original file"));
429 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->delete_source),
430 dt_conf_get_bool("studio_capture/delete_source"));
431 gtk_widget_set_tooltip_text(d->delete_source,
432 _("Delete the original image file after verifying the complete copy."));
433 g_signal_connect(G_OBJECT(d->delete_source), "toggled", G_CALLBACK(_studio_import_delete_callback), d);
434 gtk_box_pack_start(GTK_BOX(source_page), d->delete_source, FALSE, FALSE, 0);
435
436 /* Destination tab */
439 GINT_TO_POINTER(0), NULL, TRUE);
441 GINT_TO_POINTER(1), NULL, TRUE);
442 dt_bauhaus_combobox_set(d->copy, dt_conf_get_bool("studio_capture/copy"));
443 g_signal_connect(G_OBJECT(d->copy), "value-changed", G_CALLBACK(_studio_import_copy_callback), d);
444 gtk_box_pack_start(GTK_BOX(destination_page), d->copy, FALSE, FALSE, 0);
445
446
447 d->copy_options = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
448 gtk_widget_set_no_show_all(d->copy_options, TRUE);
449 gtk_box_pack_start(GTK_BOX(destination_page), d->copy_options, FALSE, FALSE, 0);
450
452 {
453 dt_bauhaus_widget_set_label(d->on_conflict, N_("On conflict"));
455 GINT_TO_POINTER(0), NULL, TRUE);
457 GINT_TO_POINTER(1), NULL, TRUE);
458 dt_bauhaus_combobox_add_full(d->on_conflict, _("Create unique filename"), DT_BAUHAUS_COMBOBOX_ALIGN_RIGHT,
459 GINT_TO_POINTER(2), NULL, TRUE);
460 dt_bauhaus_combobox_set(d->on_conflict, CLAMP(dt_conf_get_int("studio_capture/on_conflict"), DT_IMPORT_ONCONFLICT_SKIP,
462 dt_bauhaus_disable_accels(d->on_conflict);
463 gtk_widget_set_tooltip_text(d->on_conflict, _("Expected behaviour when the naming pattern produces a destination file "
464 "that already exists"));
465 g_signal_connect(G_OBJECT(d->on_conflict), "value-changed", G_CALLBACK(_studio_import_conflict_callback), d);
466 }
467 gtk_box_pack_start(GTK_BOX(d->copy_options), d->on_conflict, TRUE, TRUE, 0);
468
469 GtkWidget *base_folder_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
470 {
471 GtkWidget *base_folder_label = gtk_label_new(_("Base directory"));
472 gtk_widget_set_halign(base_folder_label, GTK_ALIGN_START);
473 gtk_box_pack_start(GTK_BOX(base_folder_box), base_folder_label, TRUE, TRUE, 0);
474
475 d->base_folder = gtk_file_chooser_button_new(_("Select a base directory"), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
476 const char *base_folder = dt_conf_get_string_const("studio_capture/base_directory_pattern");
477 if(!IS_NULL_PTR(base_folder) && base_folder[0])
478 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(d->base_folder), base_folder);
479 g_signal_connect(G_OBJECT(d->base_folder), "file-set", G_CALLBACK(_studio_import_base_folder_callback), d);
480 gtk_box_pack_end(GTK_BOX(base_folder_box), d->base_folder, TRUE, TRUE, 0);
481 }
482 gtk_box_pack_start(GTK_BOX(d->copy_options), base_folder_box, TRUE, TRUE, 0);
483
484 d->subfolder_pattern = gtk_entry_new();
485 gtk_entry_set_text(GTK_ENTRY(d->subfolder_pattern),
486 dt_conf_get_string_const("studio_capture/sub_directory_pattern"));
488 "$(");
489 gtk_widget_set_tooltip_text(d->subfolder_pattern,
490 _("Start typing `$(` to see available variables through auto-completion"));
491 g_signal_connect(G_OBJECT(d->subfolder_pattern), "changed",
493 _studio_import_pack_row(GTK_BOX(d->copy_options), _("Project directory pattern"), d->subfolder_pattern);
494
495 d->file_pattern = gtk_entry_new();
496 gtk_entry_set_text(GTK_ENTRY(d->file_pattern), dt_conf_get_string_const("studio_capture/filename_pattern"));
498 gtk_widget_set_tooltip_text(d->file_pattern,
499 _("Start typing `$(` to see available variables through auto-completion"));
500 g_signal_connect(G_OBJECT(d->file_pattern), "changed",
502 _studio_import_pack_row(GTK_BOX(d->copy_options), _("File naming pattern"), d->file_pattern);
503
504 d->preview = gtk_label_new("");
505 gtk_widget_set_halign(d->preview, GTK_ALIGN_START);
506 gtk_label_set_ellipsize(GTK_LABEL(d->preview), PANGO_ELLIPSIZE_MIDDLE);
507 gtk_label_set_line_wrap(GTK_LABEL(d->preview), TRUE);
508 gtk_box_pack_start(GTK_BOX(destination_page), d->preview, FALSE, FALSE, 0);
509
512
515}
516
517void view_enter(struct dt_lib_module_t *self, struct dt_view_t *old_view, struct dt_view_t *new_view)
518{
519 // The session may have been resumed or halted outside this module.
521}
522
524{
526 self->data);
527 g_free(self->data);
528 self->data = NULL;
529}
530
531// clang-format off
532// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
533// vim: shiftwidth=2 expandtab tabstop=2 cindent
534// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
535// 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:1846
void dt_bauhaus_disable_accels(GtkWidget *widget)
Definition bauhaus.c:3734
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
Definition bauhaus.c:2090
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
Definition bauhaus.c:1504
GtkWidget * dt_bauhaus_combobox_new(dt_bauhaus_t *bh, dt_gui_module_t *self)
Definition bauhaus.c:1698
@ DT_BAUHAUS_COMBOBOX_ALIGN_RIGHT
Definition bauhaus.h:127
const float v
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
Definition colorspaces.h:98
static const int row
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)
Read the stored string for name as a private copy.
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
Integer for name, clamped to the bounds declared in the XML.
void dt_conf_set_string(const char *name, const char *val)
const char * dt_conf_get_string_const(const char *name)
Borrow the stored string for name without copying it.
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
Definition darktable.c:646
GtkWidget * preview
what the selected row actually captures
GtkWidget * folder
destination folder chooser
GtkWidget * status
result of the last capture
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...
GdkRGBA color[]
Definition geotagging.c:541
void dt_gtkentry_setup_completion(GtkEntry *entry, const dt_gtkentry_completion_spec *compl_list, const char *trigger_char)
Definition gtkentry.c:176
const dt_gtkentry_completion_spec * dt_gtkentry_get_default_path_compl_list()
Definition gtkentry.c:200
#define DT_GUI_MODULE(x)
@ DT_IMPORT_ONCONFLICT_UNIQUE
Definition import_jobs.h:39
@ DT_IMPORT_ONCONFLICT_SKIP
Definition import_jobs.h:37
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 label.c:56
#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
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
#define DT_MODULE(MODVER)
Instantiate the version handshake every module must export. Use once, at file scope,...
GtkWidget * dt_ui_notebook_page(GtkNotebook *notebook, const char *text, const char *tooltip)
Definition notebook.c:88
GtkNotebook * dt_ui_notebook_new()
Definition notebook.c:83
const char * name
Definition pdf.h:90
GtkBox * attach_popover(GtkWidget *widget, const char *icon, GtkWidget *content)
Definition popup.c:91
void copy(TYPE *dest, TYPE *source, size_t num_el)
Copy a flat buffer used to initialize test matrices.
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
Definition signal.h:407
struct dt_control_signal_t * dt_control_signal_get_global(void)
Definition darktable.c:616
@ DT_SIGNAL_FOLDER_SURVEY_CHANGED
Raised when the folder survey starts or stops monitoring. no param, no returned value.
Definition signal.h:337
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
Definition signal.h:396
static const char *const day[7]
Definition strptime.c:97
GModule *void * data
Definition lib.h:79
GtkWidget * widget
Definition lib.h:83
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
GdkRGBA * dt_widget_colors(void)
@ DT_GUI_COLOR_WARNING
#define DT_GUI_BOX_SPACING
@ DT_UI_CONTAINER_PANEL_LEFT_CENTER