Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
collection.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2010-2012 Henrik Andersson.
4 Copyright (C) 2010-2013 johannes hanika.
5 Copyright (C) 2010-2017 Tobias Ellinghaus.
6 Copyright (C) 2011-2012 José Carlos García Sogo.
7 Copyright (C) 2011 Robert Bieber.
8 Copyright (C) 2012, 2018-2022 Pascal Obry.
9 Copyright (C) 2012 Petr Styblo.
10 Copyright (C) 2012 Richard Wonka.
11 Copyright (C) 2012-2013 Simon Spannagel.
12 Copyright (C) 2013 Gaspard Jankowiak.
13 Copyright (C) 2013 hal.
14 Copyright (C) 2013 Ulrich Pegelow.
15 Copyright (C) 2014-2016 Roman Lebedev.
16 Copyright (C) 2015-2016 Jérémy Rosen.
17 Copyright (C) 2015 Pedro Côrte-Real.
18 Copyright (C) 2016, 2020-2022 Aldric Renaudin.
19 Copyright (C) 2016 itinerarium.
20 Copyright (C) 2016-2017 Peter Budai.
21 Copyright (C) 2016 Petr Synek.
22 Copyright (C) 2017 Dominik Markiewicz.
23 Copyright (C) 2017, 2019 Liran Vaknin.
24 Copyright (C) 2017, 2019 luzpaz.
25 Copyright (C) 2018 August Schwerdfeger.
26 Copyright (C) 2018 Mario Lueder.
27 Copyright (C) 2018 Rick Yorgason.
28 Copyright (C) 2018 Rikard Öxler.
29 Copyright (C) 2018, 2020 Sam Smith.
30 Copyright (C) 2018 Simon Legner.
31 Copyright (C) 2019 Bill Ferguson.
32 Copyright (C) 2019-2020 Heiko Bauke.
33 Copyright (C) 2019 Mark Feit.
34 Copyright (C) 2019 rrd1.
35 Copyright (C) 2020 codingdave@gmail.com.
36 Copyright (C) 2020 David-Tillmann Schaefer.
37 Copyright (C) 2020 Hanno Schwalm.
38 Copyright (C) 2020 Hubert Kowalski.
39 Copyright (C) 2020 JP Verrue.
40 Copyright (C) 2020 jpverrue.
41 Copyright (C) 2020-2022 Philippe Weyland.
42 Copyright (C) 2020 Tino Mettler.
43 Copyright (C) 2020 U-DESKTOP-HQME86J\marco.
44 Copyright (C) 2021 Arnaud TANGUY.
45 Copyright (C) 2021 Chris Elston.
46 Copyright (C) 2021 Daniel Vogelbacher.
47 Copyright (C) 2021 HansBull.
48 Copyright (C) 2021 Harald.
49 Copyright (C) 2021 quovadit.
50 Copyright (C) 2021 Ralf Brown.
51 Copyright (C) 2021 Stefan Boxleitner.
52 Copyright (C) 2022-2026 Aurélien PIERRE.
53 Copyright (C) 2022 Martin Bařinka.
54 Copyright (C) 2022 Miloš Komarčević.
55 Copyright (C) 2023 André Doherty.
56 Copyright (C) 2024-2026 Guillaume Stutin.
57
58 darktable is free software: you can redistribute it and/or modify
59 it under the terms of the GNU General Public License as published by
60 the Free Software Foundation, either version 3 of the License, or
61 (at your option) any later version.
62
63 darktable is distributed in the hope that it will be useful,
64 but WITHOUT ANY WARRANTY; without even the implied warranty of
65 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
66 GNU General Public License for more details.
67
68 You should have received a copy of the GNU General Public License
69 along with darktable. If not, see <http://www.gnu.org/licenses/>.
70*/
71
72#include "common/collection.h"
73#include "common/paths.h" // DT_PATH_MAX
74#include "control/settings.h"
77#include "common/image.h"
79#include "develop/iop_order.h"
80#include "metadata/metadata.h"
81#include "common/utility.h"
83#include "common/datetime.h"
84#include "common/selection.h"
85#include "common/conf.h"
86#include "control/control.h"
87#include "views/view.h"
88
89#include <assert.h>
90#include <glib.h>
91#include <memory.h>
92#include <stdio.h>
93#include <stdlib.h>
94#include <unistd.h>
95#include "gui/application.h"
96
97
98#ifdef _WIN32
99//MSVCRT does not have strptime implemented
100#endif
101
102
103#define SELECT_QUERY "SELECT DISTINCT * FROM %s"
104#define LIMIT_QUERY "LIMIT ?1, ?2"
105
106
107/* Stores the collection query, returns 1 if changed.. */
108/* Counts the number of images in the current collection */
109
110/* determine image offset of specified imgid for the given collection */
111static int dt_collection_image_offset_with_collection(const dt_collection_t *collection, int32_t imgid);
112
113static int _resolve_iop_order_name(const char *text)
114{
115 for(int i = 0; i < DT_IOP_ORDER_LAST; i++)
116 if(strcmp(text, _(dt_iop_order_string(i))) == 0) return i;
117 return -1;
118}
119
122static const char *_order_names[DT_IOP_ORDER_LAST];
123
124/* The application-wide collection.
125 *
126 * Owned HERE, not by darktable_t, and created by the GUI bootstrap (dt_gui_gtk_init())
127 * rather than by dt_init(): the collection is the lighttable's query state, and ansel-cli
128 * has no lighttable. Instantiating it in dt_init() made every CLI run pay for the user's
129 * collection query against their whole library before exporting a single pixel.
130 *
131 * In a GUI-less process the accessor therefore returns NULL, and every public entry point
132 * of this module treats a NULL collection as "the module is not up": a query update is a
133 * no-op, a count is 0. The guard lives at this module boundary ONCE, so callers shared
134 * between GUI and CLI (film import, image removal) stay free of if(darktable.gui) tests.
135 */
137
142
147
153
155{
156 dt_collection_t *collection = g_malloc0(sizeof(dt_collection_t));
157
158 // The database module composes the collection query but cannot translate, so give it the two
159 // things it needs from this layer: how to read a module-order name, and what they are called.
160 for(int i = 0; i < DT_IOP_ORDER_LAST; i++) _order_names[i] = _(dt_iop_order_string(i));
163
164 dt_collection_reset(collection);
165 return collection;
166}
167
168void dt_collection_free(const dt_collection_t *collection)
169{
170 if(IS_NULL_PTR(collection)) return;
171 dt_free(collection->params.text_filter);
172 for(int i = 0; i < collection->n_rules; i++)
173 {
174 gchar *t = (gchar *)collection->rules[i].text;
175 dt_free(t);
176 }
177 dt_free(collection->rules);
178
179 // The composed query, its cached statements and the copy of the rules belong to the database
180 // module now.
182 dt_free(collection);
183}
184
186{
187 if(IS_NULL_PTR(collection)) return NULL;
188 return &collection->params;
189}
190
191
192// Return a pointer to a static string for an "AND" operator if the
193// number of terms processed so far requires it. The variable used
194// for term should be an int initialized to and_operator_initial()
195// before use.
196
197
199{
200 if(IS_NULL_PTR(collection)) return 0;
201 /* store flags to conf */
202 if(collection == dt_collection_get_global())
203 {
204 dt_conf_set_int("plugins/collection/query_flags", collection->params.query_flags);
205 dt_conf_set_int("plugins/collection/filter_flags", collection->params.filter_flags);
206 dt_conf_set_string("plugins/collection/text_filter", collection->params.text_filter ? collection->params.text_filter : "");
207 dt_conf_set_int("plugins/collection/sort", collection->params.sort);
208 dt_conf_set_bool("plugins/collection/descending", collection->params.descending);
209 }
210
211 // Hand the rules over; composing the SQL from them is the database module's.
212 return dt_collection_query_set_rules(&collection->params, collection->rules, collection->n_rules,
213 collection->tagid);
214}
215
217{
218 // Handle culling mode across re-queryings : re-restrict collection to selection
219 if(dt_gui_get_global() && dt_gui_get_global()->culling_mode)
221
223
224 // Handle culling mode across re-queryings : re-restrict collection to selection
225 if(dt_gui_get_global() && dt_gui_get_global()->culling_mode)
227
229}
230
231GList *dt_collection_get(const dt_collection_t *collection, const uint32_t limit)
232{
233 return dt_collection_query_get_images(limit);
234}
235
236int32_t dt_collection_get_nth(const dt_collection_t *collection, const int nth)
237{
238 return dt_collection_query_get_nth(nth);
239}
240
241static int dt_collection_image_offset_with_collection(const dt_collection_t *collection, int32_t imgid)
242{
244}
245
250
255
257{
258 // Culling mode restricts the collection to the selection
259
260 // Remove non-selected from collected images, aka culling mode
263
264 // Backup and reset current selection
267}
268
269uint32_t dt_collection_get_count(const dt_collection_t *collection)
270{
272}
273
274
276{
277 if(IS_NULL_PTR(collection)) return;
278 dt_collection_params_t *params = (dt_collection_params_t *)&collection->params;
279
280 /* setup defaults */
281 params->query_flags = COLLECTION_QUERY_FULL;
282
283 // enable all filters, aka filter in everything
284 params->filter_flags = COLLECTION_FILTER_ALL;
285
286 /* apply stored query parameters from previous darktable session */
287 int flags = dt_conf_get_int("plugins/collection/filter_flags");
288 params->filter_flags = (flags < 0) ? COLLECTION_FILTER_ALL : flags;
289
290 dt_free(params->text_filter);
291 params->text_filter = dt_conf_get_string("plugins/collection/text_filter");
292 params->sort = dt_conf_get_int("plugins/collection/sort");
293 params->descending = dt_conf_get_bool("plugins/collection/descending");
295}
296
298{
299 if(IS_NULL_PTR(collection)) return COLLECTION_FILTER_ALL;
300 return collection->params.filter_flags;
301}
302
304{
305 if(IS_NULL_PTR(collection)) return;
306 dt_collection_params_t *params = (dt_collection_params_t *)&collection->params;
307 params->filter_flags = flags;
308}
309
311{
312 if(IS_NULL_PTR(collection)) return NULL;
313 return collection->params.text_filter;
314}
315
316void dt_collection_set_text_filter(const dt_collection_t *collection, char *text_filter)
317{
318 if(IS_NULL_PTR(collection)) { dt_free(text_filter); return; } // takes ownership even when down
319 dt_collection_params_t *params = (dt_collection_params_t *)&collection->params;
320 dt_free(params->text_filter);
321 params->text_filter = text_filter;
322}
323
325{
326 if(IS_NULL_PTR(collection)) return COLLECTION_QUERY_FULL;
327 return collection->params.query_flags;
328}
329
331{
332 if(IS_NULL_PTR(collection)) return;
333 dt_collection_params_t *params = (dt_collection_params_t *)&collection->params;
334 params->query_flags = flags;
335}
336
338 const int n_rules)
339{
340 if(IS_NULL_PTR(collection)) return;
341 dt_collection_t *c = (dt_collection_t *)collection;
342
343 for(int i = 0; i < c->n_rules; i++)
344 {
345 gchar *t = (gchar *)c->rules[i].text;
346 dt_free(t);
347 }
348 dt_free(c->rules);
349
350 c->rules = g_malloc0_n(MAX(n_rules, 1), sizeof(dt_collection_rule_t));
351 c->n_rules = n_rules;
352 for(int i = 0; i < n_rules; i++)
353 {
354 c->rules[i] = rules[i];
355 c->rules[i].text = rules[i].text ? g_strdup(rules[i].text) : NULL;
356 }
357
358 dt_collection_update(collection);
359}
360
361void dt_collection_set_tag_id(dt_collection_t *collection, const uint32_t tagid)
362{
363 if(IS_NULL_PTR(collection)) return;
364 collection->tagid = tagid;
365}
366
367void dt_collection_set_sort(const dt_collection_t *collection, dt_collection_sort_t sort, gboolean reverse)
368{
369 if(IS_NULL_PTR(collection)) return;
370 dt_collection_params_t *params = (dt_collection_params_t *)&collection->params;
371
372 if(sort != DT_COLLECTION_SORT_NONE)
373 params->sort = sort;
374
375 if(reverse != -1) params->descending = reverse;
376}
377
379{
380 if(IS_NULL_PTR(collection)) return DT_COLLECTION_SORT_NONE;
381 return collection->params.sort;
382}
383
385{
386 if(IS_NULL_PTR(collection)) return FALSE;
387 return collection->params.descending;
388}
389
391{
392 char *col_name = NULL;
393 switch(prop)
394 {
395 case DT_COLLECTION_PROP_FILMROLL: return _("film roll");
396 case DT_COLLECTION_PROP_FOLDERS: return _("folder");
397 case DT_COLLECTION_PROP_CAMERA: return _("camera");
398 case DT_COLLECTION_PROP_TAG: return _("tag");
399 case DT_COLLECTION_PROP_DAY: return _("date taken");
400 case DT_COLLECTION_PROP_TIME: return _("date-time taken");
401 case DT_COLLECTION_PROP_IMPORT_TIMESTAMP: return _("import timestamp");
402 case DT_COLLECTION_PROP_CHANGE_TIMESTAMP: return _("change timestamp");
403 case DT_COLLECTION_PROP_EXPORT_TIMESTAMP: return _("export timestamp");
404 case DT_COLLECTION_PROP_PRINT_TIMESTAMP: return _("print timestamp");
405 case DT_COLLECTION_PROP_HISTORY: return _("history");
406 case DT_COLLECTION_PROP_COLORLABEL: return _("color label");
407 case DT_COLLECTION_PROP_LENS: return _("lens");
408 case DT_COLLECTION_PROP_FOCAL_LENGTH: return _("focal length");
409 case DT_COLLECTION_PROP_ISO: return _("ISO");
410 case DT_COLLECTION_PROP_APERTURE: return _("aperture");
411 case DT_COLLECTION_PROP_EXPOSURE: return _("exposure");
412 case DT_COLLECTION_PROP_FILENAME: return _("filename");
413 case DT_COLLECTION_PROP_GEOTAGGING: return _("geotagging");
414 case DT_COLLECTION_PROP_GROUPING: return _("grouping");
415 case DT_COLLECTION_PROP_LOCAL_COPY: return _("local copy");
416 case DT_COLLECTION_PROP_MODULE: return _("module");
417 case DT_COLLECTION_PROP_ORDER: return _("module order");
418 case DT_COLLECTION_PROP_RATING: return _("rating");
419 case DT_COLLECTION_PROP_QUERY: return _("custom query");
420 case DT_COLLECTION_PROP_LAST: return NULL;
421 default:
422 {
425 {
426 const int i = prop - DT_COLLECTION_PROP_METADATA;
429 {
430 const char *name = (gchar *)dt_metadata_get_name_by_display_order(i);
431 char *setting = g_strdup_printf("plugins/lighttable/metadata/%s_flag", name);
432 const gboolean hidden = dt_conf_get_int(setting) & DT_METADATA_FLAG_HIDDEN;
433 dt_free(setting);
434 if(!hidden) col_name = _(name);
435 }
436 }
437 }
438 }
439 return col_name;
440}
441
442GList *dt_collection_get_all(const dt_collection_t *collection, int limit)
443{
444 return dt_collection_get(collection, limit);
445}
446
447/* splits an input string into a number part and an optional operator part.
448 number can be a decimal integer or rational numerical item.
449 operator can be any of "=", "<", ">", "<=", ">=" and "<>".
450 range notation [x;y] can also be used
451
452 number and operator are returned as pointers to null terminated strings in g_mallocated
453 memory (to be g_free'd after use) - or NULL if no match is found.
454*/
455void dt_collection_split_operator_number(const gchar *input, char **number1, char **number2, char **operator)
456{
457 GRegex *regex;
458 GMatchInfo *match_info;
459
460 *number1 = *number2 = *operator= NULL;
461
462 // we test the range expression first
463 regex = g_regex_new("^\\s*\\[\\s*([-+]?[0-9]+\\.?[0-9]*)\\s*;\\s*([-+]?[0-9]+\\.?[0-9]*)\\s*\\]\\s*$", 0, 0, NULL);
464 g_regex_match_full(regex, input, -1, 0, 0, &match_info, NULL);
465 int match_count = g_match_info_get_match_count(match_info);
466
467 if(match_count == 3)
468 {
469 *number1 = g_match_info_fetch(match_info, 1);
470 *number2 = g_match_info_fetch(match_info, 2);
471 *operator= g_strdup("[]");
472 g_match_info_free(match_info);
473 g_regex_unref(regex);
474 return;
475 }
476
477 g_match_info_free(match_info);
478 g_regex_unref(regex);
479
480 // and we test the classic comparison operators
481 regex = g_regex_new("^\\s*(=|<|>|<=|>=|<>)?\\s*([-+]?[0-9]+\\.?[0-9]*)\\s*$", 0, 0, NULL);
482 g_regex_match_full(regex, input, -1, 0, 0, &match_info, NULL);
483 match_count = g_match_info_get_match_count(match_info);
484
485 if(match_count == 3)
486 {
487 *operator= g_match_info_fetch(match_info, 1);
488 *number1 = g_match_info_fetch(match_info, 2);
489
490 if(*operator && strcmp(*operator, "") == 0)
491 {
492 dt_free(*operator);
493 }
494 }
495
496 g_match_info_free(match_info);
497 g_regex_unref(regex);
498}
499
500static char *_dt_collection_compute_datetime(const char *operator, const char *input)
501{
502 if(strlen(input) < 4) return NULL;
503
504 char bound[DT_DATETIME_LENGTH];
505 gboolean res;
506 if(strcmp(operator, ">") == 0 || strcmp(operator, "<=") == 0)
507 res = dt_datetime_entry_to_exif_upper_bound(bound, sizeof(bound), input);
508 else
509 res = dt_datetime_entry_to_exif(bound, sizeof(bound), input);
510 if(res)
511 return g_strdup(bound);
512 else return NULL;
513}
514/* splits an input string into a date-time part and an optional operator part.
515 operator can be any of "=", "<", ">", "<=", ">=" and "<>".
516 range notation [x;y] can also be used
517 datetime values should follow the pattern YYYY:MM:DD hh:mm:ss.sss
518 but only year part is mandatory
519
520 datetime and operator are returned as pointers to null terminated strings in g_mallocated
521 memory (to be g_free'd after use) - or NULL if no match is found.
522*/
523void dt_collection_split_operator_datetime(const gchar *input, char **number1, char **number2, char **operator)
524{
525 GRegex *regex;
526 GMatchInfo *match_info;
527
528 *number1 = *number2 = *operator= NULL;
529
530 // we test the range expression first
531 // 2 elements : date-time1 and date-time2
532 regex = g_regex_new("^\\s*\\[\\s*(\\d{4}[:.\\d\\s]*)\\s*;\\s*(\\d{4}[:.\\d\\s]*)\\s*\\]\\s*$", 0, 0, NULL);
533 g_regex_match_full(regex, input, -1, 0, 0, &match_info, NULL);
534 int match_count = g_match_info_get_match_count(match_info);
535
536 if(match_count == 3)
537 {
538 gchar *txt = g_match_info_fetch(match_info, 1);
539 gchar *txt2 = g_match_info_fetch(match_info, 2);
540
541 *number1 = _dt_collection_compute_datetime(">=", txt);
542 *number2 = _dt_collection_compute_datetime("<=", txt2);
543 *operator= g_strdup("[]");
544
545 dt_free(txt);
546 dt_free(txt2);
547 g_match_info_free(match_info);
548 g_regex_unref(regex);
549 return;
550 }
551
552 g_match_info_free(match_info);
553 g_regex_unref(regex);
554
555 // and we test the classic comparison operators
556 // 2 elements : operator and date-time
557 regex = g_regex_new("^\\s*(=|<|>|<=|>=|<>)?\\s*(\\d{4}[:.\\d\\s]*)?\\s*%?\\s*$", 0, 0, NULL);
558 g_regex_match_full(regex, input, -1, 0, 0, &match_info, NULL);
559 match_count = g_match_info_get_match_count(match_info);
560
561 if(match_count == 3)
562 {
563 *operator= g_match_info_fetch(match_info, 1);
564 gchar *txt = g_match_info_fetch(match_info, 2);
565
566 if(strcmp(*operator, "") == 0 || strcmp(*operator, "=") == 0 || strcmp(*operator, "<>") == 0)
567 {
568 *number1 = dt_util_dstrcat(*number1, "%s%%", txt);
569 *number2 = _dt_collection_compute_datetime(">", txt);
570 }
571 else
572 *number1 = _dt_collection_compute_datetime(*operator, txt);
573
574 dt_free(txt);
575 }
576
577 // ensure operator is not null
578 if(IS_NULL_PTR(*operator)) *operator= g_strdup("");
579
580 g_match_info_free(match_info);
581 g_regex_unref(regex);
582}
583
584void dt_collection_split_operator_exposure(const gchar *input, char **number1, char **number2, char **operator)
585{
586 GRegex *regex;
587 GMatchInfo *match_info;
588
589 *number1 = *number2 = *operator= NULL;
590
591 // we test the range expression first
592 regex = g_regex_new("^\\s*\\[\\s*(1/)?([0-9]+\\.?[0-9]*)(\")?\\s*;\\s*(1/)?([0-9]+\\.?[0-9]*)(\")?\\s*\\]\\s*$", 0, 0, NULL);
593 g_regex_match_full(regex, input, -1, 0, 0, &match_info, NULL);
594 int match_count = g_match_info_get_match_count(match_info);
595
596 if(match_count == 6 || match_count == 7)
597 {
598 gchar *n1 = g_match_info_fetch(match_info, 2);
599
600 if(strstr(g_match_info_fetch(match_info, 1), "1/") != NULL)
601 *number1 = g_strdup_printf("1.0/%s", n1);
602 else
603 *number1 = n1;
604
605 gchar *n2 = g_match_info_fetch(match_info, 5);
606
607 if(strstr(g_match_info_fetch(match_info, 4), "1/") != NULL)
608 *number2 = g_strdup_printf("1.0/%s", n2);
609 else
610 *number2 = n2;
611
612 *operator= g_strdup("[]");
613 g_match_info_free(match_info);
614 g_regex_unref(regex);
615 return;
616 }
617
618 g_match_info_free(match_info);
619 g_regex_unref(regex);
620
621 // and we test the classic comparison operators
622 regex = g_regex_new("^\\s*(=|<|>|<=|>=|<>)?\\s*(1/)?([0-9]+\\.?[0-9]*)(\")?\\s*$", 0, 0, NULL);
623 g_regex_match_full(regex, input, -1, 0, 0, &match_info, NULL);
624 match_count = g_match_info_get_match_count(match_info);
625 if(match_count == 4 || match_count == 5)
626 {
627 *operator= g_match_info_fetch(match_info, 1);
628
629 gchar *n1 = g_match_info_fetch(match_info, 3);
630
631 if(strstr(g_match_info_fetch(match_info, 2), "1/") != NULL)
632 *number1 = g_strdup_printf("1.0/%s", n1);
633 else
634 *number1 = n1;
635
636 if(*operator && strcmp(*operator, "") == 0)
637 {
638 dt_free(*operator);
639 }
640 }
641
642 g_match_info_free(match_info);
643 g_regex_unref(regex);
644}
645
646void dt_collection_get_makermodels(const gchar *filter, GList **sanitized, GList **exif)
647{
648 dt_collection_query_get_makermodels(filter, sanitized, exif);
649}
650
651gchar *dt_collection_get_makermodel(const char *exif_maker, const char *exif_model)
652{
653 char maker[64];
654 char model[64];
655 char alias[64];
656 maker[0] = model[0] = alias[0] = '\0';
657 dt_imageio_lookup_makermodel(exif_maker, exif_model,
658 maker, sizeof(maker),
659 model, sizeof(model),
660 alias, sizeof(alias));
661
662 // Create the makermodel by concatenation
663 gchar *makermodel = g_strdup_printf("%s %s", maker, model);
664 return makermodel;
665}
666
667GList *dt_collection_get_images_for_rule(const dt_collection_properties_t property, const char *text,
668 gboolean recursive)
669{
670 return dt_collection_query_get_images_for_rule(property, text, recursive);
671}
672
674{
676 if(!v) return;
677 g_free(v->name);
678 g_free(v);
679}
680
681
683{
684 // Whether the edited rule excludes itself from the value list is a property of how that rule
685 // is configured -- an OR rule does not limit the collection, so it must not limit the choices
686 // either. That decision is made here, where conf is readable, and passed as a fact.
687 gboolean apply_exclude = FALSE;
688 if(rule >= 0)
689 {
690 char confname[200];
691 snprintf(confname, sizeof(confname), "plugins/lighttable/collect/mode%1d", rule);
692 apply_exclude = (dt_conf_get_int(confname) != 1);
693 }
694
695 // The remaining preferences the value list depends on, resolved here where conf is readable.
696 gboolean metadata_hidden = FALSE;
698 {
700 const char *name = (const char *)dt_metadata_get_name(keyid);
701 char *setting = g_strdup_printf("plugins/lighttable/metadata/%s_flag", name);
702 metadata_hidden = dt_conf_get_int(setting) & DT_METADATA_FLAG_HIDDEN;
703 g_free(setting);
704 }
705
706 const char *filmroll_sort = dt_conf_get_string_const("plugins/collect/filmroll_sort");
707 const char *filmroll_order_by =
708 (strcmp(filmroll_sort, "id") == 0)
709 ? "film_rolls_id DESC"
710 : (dt_conf_get_bool("plugins/collect/descending") ? "folder DESC" : "folder");
711
712 const dt_collection_values_request_t req = { .property = property,
713 .exclude_rule = rule,
714 .apply_exclude = apply_exclude,
715 .metadata_hidden = metadata_hidden,
716 .filmroll_order_by = filmroll_order_by };
718}
719
720int dt_collection_serialize(char *buf, int bufsize)
721{
722 char confname[200];
723 int c;
724 const int num_rules = dt_conf_get_int("plugins/lighttable/collect/num_rules");
725 c = snprintf(buf, bufsize, "%d:", num_rules);
726 buf += c;
727 bufsize -= c;
728 for(int k = 0; k < num_rules; k++)
729 {
730 snprintf(confname, sizeof(confname), "plugins/lighttable/collect/mode%1d", k);
731 const int mode = dt_conf_get_int(confname);
732 c = snprintf(buf, bufsize, "%d:", mode);
733 buf += c;
734 bufsize -= c;
735 snprintf(confname, sizeof(confname), "plugins/lighttable/collect/item%1d", k);
736 const int item = dt_conf_get_int(confname);
737 c = snprintf(buf, bufsize, "%d:", item);
738 buf += c;
739 bufsize -= c;
740 snprintf(confname, sizeof(confname), "plugins/lighttable/collect/string%1d", k);
741 const char *str = dt_conf_get_string_const(confname);
742 // Fold the recursive flag back into the trailing '*' this wire format has always used, so
743 // dt_collection_deserialize() (and any consumer reading this string directly) sees exactly
744 // what get_query_string() expects, with no separate field to carry through.
745 gchar *str_recursive = NULL;
746 if(item == DT_COLLECTION_PROP_FOLDERS && str && str[0] != '\0' && str[strlen(str) - 1] != '*')
747 {
748 snprintf(confname, sizeof(confname), "plugins/lighttable/collect/recursive%1d", k);
749 if(dt_conf_get_bool(confname)) str_recursive = g_strconcat(str, "*", NULL);
750 }
751 const char *emit = str_recursive ? str_recursive : str;
752 if(emit && (emit[0] != '\0'))
753 c = snprintf(buf, bufsize, "%s$", emit);
754 else
755 c = snprintf(buf, bufsize, "%%$");
756 g_free(str_recursive);
757 buf += c;
758 bufsize -= c;
759 }
760 return 0;
761}
762
763void dt_collection_deserialize(const char *buf)
764{
765 int num_rules = 0;
766 sscanf(buf, "%d", &num_rules);
767 if(num_rules == 0)
768 {
769 dt_conf_set_int("plugins/lighttable/collect/num_rules", 1);
770 dt_conf_set_int("plugins/lighttable/collect/mode0", 0);
771 dt_conf_set_int("plugins/lighttable/collect/item0", 0);
772 dt_conf_set_string("plugins/lighttable/collect/string0", "%");
773 dt_conf_set_bool("plugins/lighttable/collect/recursive0", FALSE);
774 }
775 else
776 {
777 int mode = 0, item = 0;
778 dt_conf_set_int("plugins/lighttable/collect/num_rules", num_rules);
779 while(buf[0] != '\0' && buf[0] != ':') buf++;
780 if(buf[0] == ':') buf++;
781 char str[400], confname[200];
782 for(int k = 0; k < num_rules; k++)
783 {
784 const int n = sscanf(buf, "%d:%d:%399[^$]", &mode, &item, str);
785 if(n == 3)
786 {
787 snprintf(confname, sizeof(confname), "plugins/lighttable/collect/mode%1d", k);
788 dt_conf_set_int(confname, mode);
789 snprintf(confname, sizeof(confname), "plugins/lighttable/collect/item%1d", k);
790 dt_conf_set_int(confname, item);
791 // A FOLDERS rule's trailing '*' is the recursion marker (see dt_collection_serialize):
792 // pull it back out into its own flag here, setting AND clearing so a rule slot never
793 // inherits a stale flag left over from whatever collection previously occupied it.
795 {
796 const size_t len = strlen(str);
797 const gboolean recursive = len > 0 && str[len - 1] == '*';
798 if(recursive) str[len - 1] = '\0';
799 snprintf(confname, sizeof(confname), "plugins/lighttable/collect/recursive%1d", k);
800 dt_conf_set_bool(confname, recursive);
801 }
802 snprintf(confname, sizeof(confname), "plugins/lighttable/collect/string%1d", k);
803 dt_conf_set_string(confname, str);
804 }
805 else if(num_rules == 1)
806 {
807 snprintf(confname, sizeof(confname), "plugins/lighttable/collect/mode%1d", k);
808 dt_conf_set_int(confname, 0);
809 snprintf(confname, sizeof(confname), "plugins/lighttable/collect/item%1d", k);
810 dt_conf_set_int(confname, 0);
811 snprintf(confname, sizeof(confname), "plugins/lighttable/collect/string%1d", k);
812 dt_conf_set_string(confname, "%");
813 snprintf(confname, sizeof(confname), "plugins/lighttable/collect/recursive%1d", k);
814 dt_conf_set_bool(confname, FALSE);
815 break;
816 }
817 else
818 {
819 dt_conf_set_int("plugins/lighttable/collect/num_rules", k);
820 break;
821 }
822 while(buf[0] != '$' && buf[0] != '\0') buf++;
823 if(buf[0] == '$') buf++;
824 }
825 }
827}
828
830
835
836
838 dt_collection_properties_t changed_property, GList *list)
839{
840 // Import and removal paths shared with ansel-cli land here; without a GUI there is no
841 // collection to re-query and nobody listening. The one guard that keeps them CLI-clean.
842 if(IS_NULL_PTR(collection)) return;
843 int next = -1;
844 if(list)
845 {
846 // for changing offsets, thumbtable needs to know the first untouched imageid after the list
847 // we do this here
848
850 }
851
852 // Read the user's rules out of conf and hand them over as RULES. Composing SQL from them is
853 // the database module's; this is the only place that knows they live in conf at all.
854 char confname[200];
855
856 const int _n_r = dt_conf_get_int("plugins/lighttable/collect/num_rules");
857 const int num_rules = CLAMP(_n_r, 1, 10);
858
859 dt_collection_rule_t *rules = g_malloc0_n(num_rules, sizeof(dt_collection_rule_t));
860 gchar **texts = g_malloc0_n(num_rules, sizeof(gchar *));
861
862 for(int i = 0; i < num_rules; i++)
863 {
864 snprintf(confname, sizeof(confname), "plugins/lighttable/collect/item%1d", i);
865 rules[i].property = dt_conf_get_int(confname);
866 snprintf(confname, sizeof(confname), "plugins/lighttable/collect/string%1d", i);
867 texts[i] = dt_conf_get_string(confname);
868 rules[i].text = texts[i];
869 snprintf(confname, sizeof(confname), "plugins/lighttable/collect/mode%1d", i);
870 rules[i].mode = dt_conf_get_int(confname);
871 snprintf(confname, sizeof(confname), "plugins/lighttable/collect/recursive%1d", i);
872 rules[i].recursive = dt_conf_get_bool(confname);
873 }
874
875 dt_collection_set_rules(collection, rules, num_rules);
876
877 for(int i = 0; i < num_rules; i++) dt_free(texts[i]);
878 dt_free(texts);
879 dt_free(rules);
880
883
884 /* update query and at last the visual */
885 dt_collection_update(collection);
886
887 /* Update recent collections history before we raise the signal,
888 * since some signal listeners will need it */
890
891 /* raise signal of collection change, only if this is an original */
894 list, next);
895}
896
898{
899 // Restore everything as before
902}
903
904
906{
908 dt_free(message);
909 return FALSE;
910}
911
913{
914 if(IS_NULL_PTR(collection)) return;
915 /* collection hinting */
916 gchar *message;
917
918 const int c = dt_collection_get_count(collection);
920
921 if(cs == 1)
922 {
923 /* determine offset of the single selected image */
924 GList *selected_imgids = dt_selection_get_list(dt_selection_get_global());
925 int selected = -1;
926
927 if(selected_imgids)
928 {
929 selected = GPOINTER_TO_INT(selected_imgids->data);
931 selected++;
932 }
933 g_list_free(selected_imgids);
934 selected_imgids = NULL;
935 message = g_strdup_printf(_("%d image of %d (#%d) in current collection is selected"), cs, c, selected);
936 }
937 else
938 {
939 message = g_strdup_printf(
940 ngettext(
941 "%d image of %d in current collection is selected",
942 "%d images of %d in current collection are selected",
943 cs),
944 cs, c);
945 }
946
947 g_idle_add(dt_collection_hint_message_internal, message);
948}
949
950static inline void _dt_collection_change_view_after_import(const dt_view_t *current_view,
951 dt_collection_import_view_t view_policy,
952 const int32_t imgid)
953{
954 // An automatic import moves nobody. This is the caller's decision, taken from what the import
955 // IS (Studio Capture's folder survey imports on its own schedule, in whatever view the user
956 // happens to be), not from what is on screen when the job ends -- the survey keeps running
957 // after the user leaves the Studio Capture atelier, so gating on the current view would let a
958 // capture landing while the user edits in the darkroom throw them out of it.
959 if(view_policy == DT_COLLECTION_IMPORT_VIEW_KEEP) return;
960
961 // Studio Capture already shows every newly-imported image itself (see
962 // _studio_image_imported_callback() in views/studio_capture.c), without leaving the atelier:
963 // forcing a switch to darkroom/lighttable here on a manual import started from its own panel
964 // would fight that and kick the user out of a live shooting session.
965 if(!g_strcmp0(current_view->module_name, "studio_capture")) return;
966
967 // Name the image to open explicitly. This runs on the import job's thread, so the mouse-over
968 // id and the selection set just above cannot be relied on to still designate imgid by the time
969 // the GUI thread performs the switch -- the darkroom's own leave() rewrites the selection, and
970 // any pointer motion rewrites the mouse-over id. dt_ctl_open_image_in_darkroom() publishes the
971 // target and switches views in one GUI-thread callback instead.
972 if(view_policy == DT_COLLECTION_IMPORT_VIEW_IMAGE)
974 else if(g_strcmp0(current_view->module_name, "lighttable")) // if current view IS NOT "lighttable".
975 dt_ctl_switch_mode_to("lighttable");
976}
977
978// TRUE when there is no folder-browsing grid to talk about at all: neither lighttable nor
979// Studio Capture is the current atelier, or (lighttable only) the Collect module is not showing
980// the "Folders" tab. Factored out of _collection_can_switch_folder() so the atelier/tab
981// eligibility rule has exactly one implementation.
982static inline gboolean _collection_folder_ui_inactive(const dt_view_t *current_atelier)
983{
984 // Go out if we are not in lighttable or Studio Capture: those are the only two ateliers whose
985 // filmstrip/grid should follow newly-imported images into their folder. Studio Capture's own
986 // filmstrip is driven by the same dt_collection_get_global() query as lighttable's grid, so without
987 // this it never picks up an auto-imported capture that lands outside the currently browsed
988 // folder.
989 gboolean result = current_atelier && g_strcmp0(current_atelier->module_name, "lighttable")
990 && g_strcmp0(current_atelier->module_name, "studio_capture");
991
992 // Go out if the Collection module is not showing the "Folders" tab. Only applies to
993 // lighttable, the only atelier with a Collect module UI exposing that tab: Studio Capture has
994 // no such module, so this persisted, lighttable-specific tab selection must not gate it too.
995 const gboolean is_lighttable = current_atelier && !g_strcmp0(current_atelier->module_name, "lighttable");
996 if(is_lighttable)
997 result |= dt_conf_get_int("plugins/lighttable/collect/tab") != 0;
998
999 return result;
1000}
1001
1002gboolean dt_collection_get_browsed_folder(gchar *folder, size_t len, gboolean *recursive)
1003{
1005 if(_collection_folder_ui_inactive(current_atelier))
1006 return FALSE;
1007
1008 // tab == 0 (checked above) guarantees item0 is DT_COLLECTION_PROP_FOLDERS or _FILMROLL: the
1009 // Collect module's Folders tab always forces one of the two on rule 0 (libs/collect.c). Only
1010 // the Tree view (FOLDERS) supports recursion -- the flat List view (FILMROLL) never appends a
1011 // sub-folder wildcard to its query (database/collection_query.c), so recursive0 is meaningless
1012 // there even if it's still set from a previous Tree-view session.
1013 const gboolean is_folders = dt_conf_get_int("plugins/lighttable/collect/item0") == DT_COLLECTION_PROP_FOLDERS;
1014 *recursive = is_folders && dt_conf_get_bool("plugins/lighttable/collect/recursive0");
1015
1016 gchar *string0 = dt_conf_get_string("plugins/lighttable/collect/string0");
1017 const gboolean has_value = string0 && string0[0];
1018 if(has_value) g_strlcpy(folder, string0, len);
1019 dt_free(string0);
1020 return has_value;
1021}
1022
1023void dt_collection_notify_imported(const int32_t imgid, const gchar *known_image_folder, gint64 *last_refresh_us)
1024{
1025 // Throttled: every import job feeds this one image at a time, and a full
1026 // dt_collection_update_query() is expensive enough (rebuilds memory.collected_images, triggers
1027 // a full lighttable/thumbtable re-layout on the GUI thread) that firing it after every single
1028 // image kept the GUI thread permanently busy processing the backlog on a large import.
1029 const gint64 now = g_get_monotonic_time();
1030 if(now - *last_refresh_us <= 250000) return; // 250ms
1031 *last_refresh_us = now;
1032
1033 // Read fresh on every throttle-admitted call (so at most 4/s, not per image) rather than once
1034 // before the import loop starts: the user can change which folder is browsed while a long
1035 // import is still running, and a stale snapshot would keep comparing against wherever they
1036 // were looking when the job began, silently going quiet on the folder they navigated to.
1037 gchar browsed_folder[DT_PATH_MAX] = { 0 };
1038 gboolean browsed_folder_recursive = FALSE;
1039 const gboolean has_browsed_folder
1040 = dt_collection_get_browsed_folder(browsed_folder, sizeof(browsed_folder), &browsed_folder_recursive);
1041
1042 // Unknown scope (not browsing a single folder/film-roll): can't tell whether imgid is
1043 // relevant, so always do the real thing -- same as before this function existed.
1044 gboolean image_in_browsed_folder = TRUE;
1045 gchar image_folder_buf[DT_PATH_MAX] = { 0 };
1046 if(has_browsed_folder)
1047 {
1048 const gchar *image_folder = known_image_folder;
1049 if(!image_folder)
1050 {
1051 dt_get_dirname_from_imgid(image_folder_buf, imgid);
1052 image_folder = image_folder_buf;
1053 }
1054 const size_t browsed_len = strlen(browsed_folder);
1055 image_in_browsed_folder = !g_strcmp0(image_folder, browsed_folder)
1056 || (browsed_folder_recursive && g_str_has_prefix(image_folder, browsed_folder)
1057 && image_folder[browsed_len] == G_DIR_SEPARATOR);
1058 }
1059
1060 // Both branches below label the change DT_COLLECTION_CHANGE_BACKGROUND_SYNC: a background
1061 // import heartbeat is never the kind of event that should steal the user's scroll/focus or
1062 // reset grid/zoom preferences, whether or not it actually touches what's on screen. Listeners
1063 // that key off query_change to guard exactly that -- gui/dtgtk/thumbtable.c's
1064 // _dt_collection_changed_callback() (skips dt_thumbtable_schedule_focus(), still rebuilds grid
1065 // content off its own hash) and libs/tools/lighttable.c's same-named callback (skips its
1066 // unconditional zoom-reset entirely) -- both recognize it as "not a real [navigational] change".
1067 // query_change never affects dt_collection_update_query()'s own re-sync work, only what it
1068 // hands listeners, so this is free to pick regardless of which branch runs.
1069 if(image_in_browsed_folder)
1070 {
1071 // imgid lands in (or, if recursive, under) the browsed folder: a real
1072 // dt_collection_update_query() is required so memory.collected_images actually gains the new
1073 // image and the grid has something new to show.
1076 return;
1077 }
1078
1079 // imgid does not concern the folder currently browsed: skip the real re-query (it could not
1080 // have shown imgid there anyway) and raise the signal directly instead of going silent. Every
1081 // listener that keeps its own counts independently of memory.collected_images -- the Collect
1082 // module's tag/camera/lens lists (libs/collect.c queries main.images directly and never reads
1083 // memory.collected_images) -- still refreshes. gui/dtgtk/thumbtable.c's own hash (a function of
1084 // the query generation and memory.collected_images's row count) is untouched by this path, so
1085 // it does not rebuild grid content either.
1088}
1089
1090void dt_collection_load_filmroll(dt_collection_t *collection, const int32_t imgid,
1091 dt_collection_import_view_t view_policy, gboolean set_mouse_over)
1092{
1093 if(IS_NULL_PTR(collection)) return;
1095
1096 // Without a real image there is nothing to switch to or open.
1097 if(imgid == UNKNOWN_IMAGE)
1098 return;
1099
1100 // May the folder-browsing rules be re-pointed at the imported image's folder? Two questions,
1101 // and they gate this block only -- what comes after it (the hovered image, the selection, the
1102 // view switch) answers to the import's own policy, not to which atelier is on screen.
1103 //
1104 // Does the user expect the library to move at all? Only if they asked for this import. An
1105 // AUTOMATIC one (Studio Capture's folder survey) re-points the collection in Studio Capture's
1106 // own atelier, whose filmstrip is meant to follow the shooting session -- but nowhere else: the
1107 // survey outlives that atelier, and a capture landing while the user browses or edits elsewhere
1108 // must not drag the library onto the capture folder. Same reasoning as
1109 // DT_COLLECTION_IMPORT_VIEW_KEEP, applied to the collection instead of the view.
1110 //
1111 // Then, may rule 0 be overwritten with a folder? That is the Collect module's persisted tab,
1112 // and it is deliberately NOT a question about the current atelier: the collection is global, so
1113 // an import started from the darkroom (or the map, or print) must re-point it just the same, or
1114 // the imported image is nowhere to be found when the user gets back to the grid. On
1115 // "Collections" (tags) and "Queries" the rules belong to the user and overwriting one would
1116 // destroy the query they built. Studio Capture has no Collect module UI, hence no such rules.
1117 const gboolean is_studio_capture = !IS_NULL_PTR(current_atelier)
1118 && !g_strcmp0(current_atelier->module_name, "studio_capture");
1119 const gboolean follow_import_folder = is_studio_capture
1120 || (view_policy != DT_COLLECTION_IMPORT_VIEW_KEEP
1121 && dt_conf_get_int("plugins/lighttable/collect/tab") == 0);
1122
1123 if(follow_import_folder)
1124 {
1125 // Always the folder actually containing imgid (the first successfully imported image), for
1126 // every case -- copy or in-place, List or Tree view. This used to special-case in-place
1127 // imports in Tree view: if the user had selected exactly one top-level folder to import
1128 // recursively, it showed THAT folder (ui_last/import_first_selected_str) instead of drilling
1129 // down to wherever the first image actually landed, which is surprising and unpredictable
1130 // for a recursive import spanning several sub-folders (the sub-folder that ends up used
1131 // depended on file-chooser/last-browsed-directory state left over from a previous, unrelated
1132 // import, not on anything about this one).
1133 gchar dir[DT_PATH_MAX] = { 0 };
1134 dt_get_dirname_from_imgid(dir, imgid);
1135 if(!dt_util_dir_exist(dir)) dir[0] = 0;
1136
1137 // Don't append "*": it's the legacy encoding for "recursive" and would silently
1138 // override the user's current recursive/sub-folders setting on every import.
1139 dt_conf_set_string("plugins/lighttable/collect/string0", dir);
1140 dt_conf_set_int("plugins/lighttable/collect/num_rules", 1);
1141
1142 // Reload the collection with the current filmroll
1144 }
1145
1146 // Hovered image and selection belong to the user as long as they did not ask for this import.
1147 // Studio Capture points both at the capture it displays itself (_studio_set_image() in
1148 // views/studio_capture.c); doing it here too would, anywhere else, add an image the user never
1149 // asked for to their selection and hand it to the next darkroom entry -- try_enter() reads the
1150 // mouse-over id first.
1151 if(view_policy != DT_COLLECTION_IMPORT_VIEW_KEEP)
1152 {
1153 // Skippable: a caller that already pointed mouse_over_id at imgid earlier (e.g. right when it
1154 // was first known, before a long import job finishes) does not want it forced back here,
1155 // possibly clobbering whatever the user is hovering by now.
1156 if(set_mouse_over) dt_control_set_mouse_over_id(imgid);
1157
1158 // To scroll the lighttable automatically to this image,
1159 // it needs to be selected.
1161 }
1162
1163 // New images are untagged, that may need an update of the collection module for untagged count
1165
1166 if(!IS_NULL_PTR(current_atelier)) _dt_collection_change_view_after_import(current_atelier, view_policy, imgid);
1167}
1168
1169// clang-format off
1170// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
1171// vim: shiftwidth=2 expandtab tabstop=2 cindent
1172// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
1173// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
const char * dt_collection_name(dt_collection_properties_t prop)
Definition collection.c:390
void dt_collection_update_query(const dt_collection_t *collection, dt_collection_change_t query_change, dt_collection_properties_t changed_property, GList *list)
Definition collection.c:837
void dt_pop_collection()
Definition collection.c:246
void dt_culling_mode_to_selection()
Definition collection.c:897
void dt_collection_reset(const dt_collection_t *collection)
Definition collection.c:275
static dt_collection_recents_handler_t _recents_handler
Definition collection.c:829
void dt_collection_set_query_flags(const dt_collection_t *collection, dt_collection_query_flags_t flags)
Definition collection.c:330
void dt_collection_deserialize(const char *buf)
Definition collection.c:763
const dt_collection_params_t * dt_collection_params(const dt_collection_t *collection)
Definition collection.c:185
static char * _dt_collection_compute_datetime(const char *operator, const char *input)
Definition collection.c:500
void dt_collection_split_operator_exposure(const gchar *input, char **number1, char **number2, char **operator)
Definition collection.c:584
void dt_collection_set_text_filter(const dt_collection_t *collection, char *text_filter)
Definition collection.c:316
dt_collection_t * dt_collection_get_global(void)
Definition collection.c:138
static int dt_collection_image_offset_with_collection(const dt_collection_t *collection, int32_t imgid)
Definition collection.c:241
char * dt_collection_get_text_filter(const dt_collection_t *collection)
Definition collection.c:310
gboolean dt_collection_get_sort_descending(const dt_collection_t *collection)
Definition collection.c:384
void dt_selection_to_culling_mode()
Definition collection.c:256
void dt_collection_set_filter_flags(const dt_collection_t *collection, dt_collection_filter_flag_t flags)
Definition collection.c:303
static const char * _order_names[DT_IOP_ORDER_LAST]
Definition collection.c:122
void dt_collection_memory_update()
Definition collection.c:216
void dt_collection_split_operator_number(const gchar *input, char **number1, char **number2, char **operator)
Definition collection.c:455
void dt_collection_set_recents_handler(dt_collection_recents_handler_t handler)
Definition collection.c:831
dt_collection_filter_flag_t dt_collection_get_filter_flags(const dt_collection_t *collection)
Definition collection.c:297
void dt_collection_get_makermodels(const gchar *filter, GList **sanitized, GList **exif)
Definition collection.c:646
GList * dt_collection_get_images_for_rule(const dt_collection_properties_t property, const char *text, gboolean recursive)
Definition collection.c:667
void dt_collection_notify_imported(const int32_t imgid, const gchar *known_image_folder, gint64 *last_refresh_us)
int32_t dt_collection_get_nth(const dt_collection_t *collection, const int nth)
Definition collection.c:236
gboolean dt_collection_get_browsed_folder(gchar *folder, size_t len, gboolean *recursive)
dt_collection_query_flags_t dt_collection_get_query_flags(const dt_collection_t *collection)
Definition collection.c:324
void dt_collection_set_rules(const dt_collection_t *collection, const dt_collection_rule_t *rules, const int n_rules)
Definition collection.c:337
dt_collection_sort_t dt_collection_get_sort_field(const dt_collection_t *collection)
Definition collection.c:378
void dt_collection_set_tag_id(dt_collection_t *collection, const uint32_t tagid)
Definition collection.c:361
static dt_collection_t * _collection_global
Definition collection.c:136
void dt_collection_name_value_free(gpointer value)
Definition collection.c:673
gchar * dt_collection_get_makermodel(const char *exif_maker, const char *exif_model)
Definition collection.c:651
gboolean dt_collection_hint_message_internal(void *message)
Definition collection.c:905
void dt_push_collection()
Definition collection.c:251
void dt_collection_split_operator_datetime(const gchar *input, char **number1, char **number2, char **operator)
Definition collection.c:523
void dt_collection_hint_message(const dt_collection_t *collection)
Definition collection.c:912
void dt_collection_cleanup_global(void)
Definition collection.c:148
int dt_collection_serialize(char *buf, int bufsize)
Definition collection.c:720
static gboolean _collection_folder_ui_inactive(const dt_view_t *current_atelier)
Definition collection.c:982
static void _dt_collection_change_view_after_import(const dt_view_t *current_view, dt_collection_import_view_t view_policy, const int32_t imgid)
Definition collection.c:950
GList * dt_collection_get(const dt_collection_t *collection, const uint32_t limit)
Definition collection.c:231
void dt_collection_set_sort(const dt_collection_t *collection, dt_collection_sort_t sort, gboolean reverse)
Definition collection.c:367
static int _resolve_iop_order_name(const char *text)
Definition collection.c:113
uint32_t dt_collection_get_count(const dt_collection_t *collection)
Definition collection.c:269
void dt_collection_init_global(void)
Definition collection.c:143
int dt_collection_update(const dt_collection_t *collection)
Definition collection.c:198
dt_collection_t * dt_collection_new()
Definition collection.c:154
void dt_collection_load_filmroll(dt_collection_t *collection, const int32_t imgid, dt_collection_import_view_t view_policy, gboolean set_mouse_over)
GList * dt_collection_get_property_values(const dt_collection_properties_t property, const int rule)
Definition collection.c:682
GList * dt_collection_get_all(const dt_collection_t *collection, int limit)
Definition collection.c:442
void dt_collection_free(const dt_collection_t *collection)
Definition collection.c:168
dt_collection_properties_t
Definition collection.h:120
@ DT_COLLECTION_PROP_EXPOSURE
Definition collection.h:128
@ DT_COLLECTION_PROP_MODULE
Definition collection.h:147
@ DT_COLLECTION_PROP_RATING
Definition collection.h:149
@ DT_COLLECTION_PROP_QUERY
Definition collection.h:151
@ DT_COLLECTION_PROP_TIME
Definition collection.h:133
@ DT_COLLECTION_PROP_METADATA
Definition collection.h:142
@ DT_COLLECTION_PROP_GROUPING
Definition collection.h:143
@ DT_COLLECTION_PROP_TAG
Definition collection.h:140
@ DT_COLLECTION_PROP_FILMROLL
Definition collection.h:121
@ DT_COLLECTION_PROP_LENS
Definition collection.h:126
@ DT_COLLECTION_PROP_CAMERA
Definition collection.h:125
@ DT_COLLECTION_PROP_LAST
Definition collection.h:153
@ DT_COLLECTION_PROP_LOCAL_COPY
Definition collection.h:144
@ DT_COLLECTION_PROP_GEOTAGGING
Definition collection.h:139
@ DT_COLLECTION_PROP_FILENAME
Definition collection.h:123
@ DT_COLLECTION_PROP_ISO
Definition collection.h:130
@ DT_COLLECTION_PROP_COLORLABEL
Definition collection.h:141
@ DT_COLLECTION_PROP_UNDEF
Definition collection.h:155
@ DT_COLLECTION_PROP_DAY
Definition collection.h:132
@ DT_COLLECTION_PROP_ORDER
Definition collection.h:148
@ DT_COLLECTION_PROP_FOLDERS
Definition collection.h:122
@ DT_COLLECTION_PROP_APERTURE
Definition collection.h:127
@ DT_COLLECTION_PROP_IMPORT_TIMESTAMP
Definition collection.h:134
@ DT_COLLECTION_PROP_FOCAL_LENGTH
Definition collection.h:129
@ DT_COLLECTION_PROP_EXPORT_TIMESTAMP
Definition collection.h:136
@ DT_COLLECTION_PROP_CHANGE_TIMESTAMP
Definition collection.h:135
@ DT_COLLECTION_PROP_HISTORY
Definition collection.h:146
@ DT_COLLECTION_PROP_PRINT_TIMESTAMP
Definition collection.h:137
dt_collection_query_flags_t
Definition collection.h:69
@ COLLECTION_QUERY_USE_WHERE_EXT
Definition collection.h:73
#define COLLECTION_QUERY_FULL
Definition collection.h:76
dt_collection_filter_flag_t
Definition collection.h:79
@ COLLECTION_FILTER_ALL
Definition collection.h:96
void(* dt_collection_recents_handler_t)(void)
Definition collection.h:63
dt_collection_change_t
Definition collection.h:160
@ DT_COLLECTION_CHANGE_NEW_QUERY
Definition collection.h:162
@ DT_COLLECTION_CHANGE_BACKGROUND_SYNC
Definition collection.h:174
dt_collection_import_view_t
Definition collection.h:269
@ DT_COLLECTION_IMPORT_VIEW_KEEP
Definition collection.h:273
@ DT_COLLECTION_IMPORT_VIEW_IMAGE
Definition collection.h:277
dt_collection_sort_t
Definition collection.h:100
@ DT_COLLECTION_SORT_NONE
Definition collection.h:101
void dt_collection_query_set_order_names(const char *const *names, const int count)
void dt_collection_query_pop(void)
int dt_collection_query_set_rules(const dt_collection_params_t *params, const dt_collection_rule_t *rules, const int n_rules, const uint32_t tagid)
void dt_collection_query_set_order_resolver(dt_collection_query_order_resolver_t fn)
GList * dt_collection_query_get_property_values(const dt_collection_values_request_t *req)
void dt_collection_query_cleanup(void)
void dt_collection_query_push(void)
void dt_collection_query_get_makermodels(const gchar *filter, GList **sanitized, GList **exif)
void dt_collection_query_refresh_memory_table(void)
GList * dt_collection_query_get_images_for_rule(const dt_collection_properties_t property, const char *text, gboolean recursive)
uint32_t dt_collection_query_count(void)
int32_t dt_collection_query_find_neighbour(GList *imgids)
int32_t dt_collection_query_get_nth(const int nth)
GList * dt_collection_query_get_images(const uint32_t limit)
int dt_collection_query_image_offset(const int32_t imgid)
void dt_collection_query_restrict_to_selection(void)
const int t
const float v
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.
void dt_get_dirname_from_imgid(gchar *dir, const int32_t imgid)
void dt_ctl_switch_mode_to(const char *mode)
Definition control.c:701
void dt_control_set_mouse_over_id(int32_t value)
Definition control.c:994
void dt_ctl_open_image_in_darkroom(const int32_t imgid)
open one specific image in the darkroom, from any view and any thread.
Definition control.c:745
void dt_control_hinter_message(const struct dt_control_t *s, const char *message)
Definition control.c:981
struct dt_control_t * dt_control_get_global(void)
Definition darktable.c:651
struct dt_view_manager_t * dt_view_manager_get_global(void)
Definition darktable.c:599
struct dt_gui_gtk_t * dt_gui_get_global(void)
Definition darktable.c:523
gboolean dt_datetime_entry_to_exif_upper_bound(char *exif, const size_t exif_size, const char *entry)
Definition datetime.c:348
gboolean dt_datetime_entry_to_exif(char *exif, const size_t exif_size, const char *entry)
Definition datetime.c:333
#define DT_DATETIME_LENGTH
Definition datetime.h:38
GHashTable * selected
set of checked row labels, mirrored to conf on every change
GtkWidget * folder
destination folder chooser
const int res
Definition dtpthread.h:351
#define UNKNOWN_IMAGE
Definition image.h:78
gboolean dt_imageio_lookup_makermodel(const char *maker, const char *model, char *mk, int mk_len, char *md, int md_len, char *al, int al_len)
const char * maker
const char * model
const char * dt_iop_order_string(const dt_iop_order_t order)
Return the human-readable name for an IOP order enum value.
Definition iop_order.c:82
@ DT_IOP_ORDER_LAST
Definition iop_order.h:156
_lib_location_type_t type
Definition location.c:1
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
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
Definition mem_alloc.h:171
const char * dt_metadata_get_name_by_display_order(const uint32_t order)
const char * dt_metadata_get_name(const uint32_t keyid)
int dt_metadata_get_type_by_display_order(const uint32_t order)
dt_metadata_t dt_metadata_get_keyid_by_display_order(const uint32_t order)
@ DT_METADATA_FLAG_HIDDEN
Definition metadata.h:79
@ DT_METADATA_NUMBER
Definition metadata.h:57
@ DT_METADATA_TYPE_INTERNAL
Definition metadata.h:65
dt_mipmap_buffer_dsc_flags flags
Definition mipmap_cache.c:4
#define DT_PATH_MAX
Buffer size for a filesystem path anywhere in Ansel.
Definition paths.h:57
const char * name
Definition pdf.h:90
struct dt_selection_t * dt_selection_get_global(void)
Definition selection.c:80
void dt_selection_push(dt_selection_t *selection)
Definition selection.c:214
int dt_selection_get_length(struct dt_selection_t *selection)
Definition selection.c:193
GList * dt_selection_get_list(struct dt_selection_t *selection)
Definition selection.c:185
void dt_selection_clear(dt_selection_t *selection)
Definition selection.c:271
void dt_selection_select(dt_selection_t *selection, int32_t imgid)
Definition selection.c:279
void dt_selection_pop(dt_selection_t *selection)
Definition selection.c:230
#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_TAG_CHANGED
This signal is raised when a tag is added/deleted/changed
Definition signal.h:133
@ DT_SIGNAL_COLLECTION_CHANGED
This signal is raised when collection changed. To avoid leaking the list, dt_collection_t is connecte...
Definition signal.h:125
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
dt_collection_query_flags_t query_flags
Definition collection.h:192
dt_collection_sort_t sort
Definition collection.h:201
dt_collection_filter_flag_t filter_flags
Definition collection.h:195
dt_collection_properties_t property
Definition collection.h:183
const char * text
Definition collection.h:185
dt_collection_params_t params
Definition collection.h:212
unsigned int tagid
Definition collection.h:211
dt_collection_rule_t * rules
Definition collection.h:208
dt_collection_properties_t property
char module_name[64]
Definition view.h:155
#define MAX(a, b)
Definition thinplate.c:29
gboolean dt_util_dir_exist(const char *dir)
Definition utility.c:371
gchar * dt_util_dstrcat(gchar *str, const gchar *format,...)
Definition utility.c:99
const dt_view_t * dt_view_manager_get_current_view(dt_view_manager_t *vm)
Definition view.c:139