Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
metadata/metadata.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2010-2012, 2014-2016, 2020 Tobias Ellinghaus.
4 Copyright (C) 2011 Henrik Andersson.
5 Copyright (C) 2011-2012 johannes hanika.
6 Copyright (C) 2012 James C. McPherson.
7 Copyright (C) 2012 Richard Wonka.
8 Copyright (C) 2016 Roman Lebedev.
9 Copyright (C) 2019 Heiko Bauke.
10 Copyright (C) 2019-2021 Pascal Obry.
11 Copyright (C) 2019-2022 Philippe Weyland.
12 Copyright (C) 2020-2021 Aldric Renaudin.
13 Copyright (C) 2020 Chris Elston.
14 Copyright (C) 2020-2021 Hubert Kowalski.
15 Copyright (C) 2020 Sam Smith.
16 Copyright (C) 2021 Hanno Schwalm.
17 Copyright (C) 2021 Ralf Brown.
18 Copyright (C) 2022-2023, 2025-2026 Aurélien PIERRE.
19 Copyright (C) 2022 Martin Bařinka.
20
21 darktable is free software: you can redistribute it and/or modify
22 it under the terms of the GNU General Public License as published by
23 the Free Software Foundation, either version 3 of the License, or
24 (at your option) any later version.
25
26 darktable is distributed in the hope that it will be useful,
27 but WITHOUT ANY WARRANTY; without even the implied warranty of
28 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 GNU General Public License for more details.
30
31 You should have received a copy of the GNU General Public License
32 along with darktable. If not, see <http://www.gnu.org/licenses/>.
33*/
34
35#include "metadata/metadata.h"
36#include "common/act_on.h"
41#include "common/undo.h"
42#include "common/conf.h"
43
44#include <stdlib.h>
45#include "common/utility.h"
46#include <glib/gi18n.h>
47#include "common/image.h"
48#include "system/macros.h"
49#include "system/mem_alloc.h"
50
51// this array should contain all dt metadata
52
53// add the new metadata at the end when needed
54// Dependencies
55// Must match with dt_metadata_t in metadata.h.
56// Exif.cc: add the new metadata into dt_xmp_keys[]
57// libs/metadata.c increment version and change legacy_param() accordingly
58// CAUTION : key, subkey (last term of key) & name must be unique
59
60static const struct
61{
62 char *key;
63 char *name;
64 int type;
65 uint32_t display_order;
66} dt_metadata_def[] = {
67 // clang-format off
68 {"Xmp.dc.creator", N_("creator"), DT_METADATA_TYPE_USER, 2},
69 {"Xmp.dc.publisher", N_("publisher"), DT_METADATA_TYPE_USER, 3},
70 {"Xmp.dc.title", N_("title"), DT_METADATA_TYPE_USER, 0},
71 {"Xmp.dc.description", N_("description"), DT_METADATA_TYPE_USER, 1},
72 {"Xmp.dc.rights", N_("rights"), DT_METADATA_TYPE_USER, 4},
73 {"Xmp.acdsee.notes", N_("notes"), DT_METADATA_TYPE_USER, 5},
74 {"Xmp.darktable.version_name", N_("version name"), DT_METADATA_TYPE_OPTIONAL, 6},
75 {"Xmp.darktable.image_id", N_("image id"), DT_METADATA_TYPE_INTERNAL, 7}
76 // clang-format on
77};
78
80{
81 unsigned int nb = 0;
82 for(unsigned int i = 0; i < DT_METADATA_NUMBER; i++)
83 {
85 nb++;
86 }
87 return nb;
88}
89
90const char *dt_metadata_get_name_by_display_order(const uint32_t order)
91{
92 if(order < DT_METADATA_NUMBER)
93 {
94 for(unsigned int i = 0; i < DT_METADATA_NUMBER; i++)
95 {
96 if(order == dt_metadata_def[i].display_order)
97 return dt_metadata_def[i].name;
98 }
99 }
100 return NULL;
101}
102
104{
105 if(order < DT_METADATA_NUMBER)
106 {
107 for(unsigned int i = 0; i < DT_METADATA_NUMBER; i++)
108 {
109 if(order == dt_metadata_def[i].display_order)
110 return i;
111 }
112 }
113 return -1;
114}
115
117{
118 if(IS_NULL_PTR(name)) return -1;
119 for(unsigned int i = 0; i < DT_METADATA_NUMBER; i++)
120 {
122 return i;
123 }
124 return -1;
125}
126
128{
129 if(order < DT_METADATA_NUMBER)
130 {
131 for(unsigned int i = 0; i < DT_METADATA_NUMBER; i++)
132 {
133 if(order == dt_metadata_def[i].display_order)
134 return dt_metadata_def[i].type;
135 }
136 }
137 return 0;
138}
139
140const char *dt_metadata_get_name(const uint32_t keyid)
141{
142 if(keyid < DT_METADATA_NUMBER)
143 return dt_metadata_def[keyid].name;
144 else
145 return NULL;
146}
147
149{
150 if(IS_NULL_PTR(key)) return -1;
151 for(unsigned int i = 0; i < DT_METADATA_NUMBER; i++)
152 {
154 return i;
155 }
156 return -1;
157}
158
159const char *dt_metadata_get_key(const uint32_t keyid)
160{
161 if(keyid < DT_METADATA_NUMBER)
162 return dt_metadata_def[keyid].key;
163 else
164 return NULL;
165}
166
167const char *dt_metadata_get_subkey(const uint32_t keyid)
168{
169 if(keyid < DT_METADATA_NUMBER)
170 {
171 char *t = g_strrstr(dt_metadata_def[keyid].key, ".");
172 if(t) return t + 1;
173 }
174 return NULL;
175}
176
177const char *dt_metadata_get_key_by_subkey(const char *subkey)
178{
179 if(subkey)
180 {
181 for(unsigned int i = 0; i < DT_METADATA_NUMBER; i++)
182 {
183 char *t = g_strrstr(dt_metadata_def[i].key, ".");
184 if(t && !g_strcmp0(t + 1, subkey))
185 return dt_metadata_def[i].key;
186 }
187 }
188 return NULL;
189}
190
191int dt_metadata_get_type(const uint32_t keyid)
192{
193 if(keyid < DT_METADATA_NUMBER)
194 return dt_metadata_def[keyid].type;
195 else
196 return 0;
197}
198
200{
201 for(unsigned int i = 0; i < DT_METADATA_NUMBER; i++)
202 {
203 const int type = dt_metadata_get_type(i);
204 const char *name = (gchar *)dt_metadata_get_name(i);
205 char *setting = g_strdup_printf("plugins/lighttable/metadata/%s_flag", name);
207 {
208 // per default should be imported - ignored if "write_sidecar_files" set
211 {
212 // per default this one should be hidden
214 }
216 }
218 }
219}
220
221typedef struct dt_undo_metadata_t
222{
223 int32_t imgid;
224 GList *before; // list of key/value before
225 GList *after; // list of key/value after
227
228static GList *_list_find_custom(GList *list, gpointer data)
229{
230 for(GList *i = list; i; i = g_list_next(i))
231 {
232 if(i->data && !g_strcmp0(i->data, data))
233 return i;
234 i = g_list_next(i);
235 }
236 return NULL;
237}
238
240{
241 GList *b = before;
242 GList *a = after;
243 gchar *metadata_list = NULL;
244
245 while(b)
246 {
247 GList *same_key = _list_find_custom(a, b->data);
248 GList *b2 = g_list_next(b);
249 gboolean different_value = FALSE;
250 const char *value = (char *)b2->data; // if empty we can remove it
251 if(same_key)
252 {
254 different_value = g_strcmp0(same2->data, b2->data);
255 }
256 if(!same_key || different_value || !value[0])
257 {
258 metadata_list = dt_util_dstrcat(metadata_list, "%d,", atoi(b->data));
259 }
260 b = g_list_next(b);
261 b = g_list_next(b);
262 }
263 if(metadata_list) metadata_list[strlen(metadata_list) - 1] = '\0';
264 return metadata_list;
265}
266
267/* The rows that are in `after` but not in `before`, or whose value changed.
268 *
269 * Returns a GArray of dt_metadata_row_t whose `value` pointers borrow from `after`, so it
270 * must not outlive it. This used to build the insert's VALUES clause directly and escape
271 * each value for SQL itself, which was the only reason this file linked against sqlite at
272 * all. The escaping is the repository's now. */
273static GArray *_get_tb_added_metadata_rows(const int img, GList *before, GList *after)
274{
275 GList *b = before;
276 GList *a = after;
278
279 while(a)
280 {
281 GList *same_key = _list_find_custom(b, a->data);
282 GList *a2 = g_list_next(a);
283 gboolean different_value = FALSE;
284 const char *value = (char *)a2->data; // if empty we don't add it to database
285 if(same_key)
286 {
288 different_value = g_strcmp0(same2->data, a2->data);
289 }
290 if((!same_key || different_value) && value[0])
291 {
292 const dt_metadata_row_t row = { .imgid = GPOINTER_TO_INT(img),
293 .keyid = atoi(a->data),
294 .value = value };
295 g_array_append_val(rows, row);
296 }
297 a = g_list_next(a);
298 a = g_list_next(a);
299 }
300 return rows;
301}
302
303static void _bulk_remove_metadata(const int img, const gchar *metadata_list)
304{
305 dt_metadata_repository_remove(img, metadata_list);
306}
307
308static void _bulk_add_metadata(GArray *rows)
309{
310 dt_metadata_repository_add((const dt_metadata_row_t *)rows->data, rows->len);
311}
312
313static void _pop_undo_execute(const int32_t imgid, GList *before, GList *after)
314{
316 GArray *tobe_added_rows = _get_tb_added_metadata_rows(imgid, before, after);
317
320
323}
324
325static void _pop_undo(gpointer user_data, const dt_undo_type_t type, dt_undo_data_t data, const dt_undo_action_t action, GList **imgs)
326{
328 {
329 for(GList *list = (GList *)data; list; list = g_list_next(list))
330 {
332
333 GList *before = (action == DT_ACTION_UNDO) ? undometadata->after : undometadata->before;
334 GList *after = (action == DT_ACTION_UNDO) ? undometadata->before : undometadata->after;
335 _pop_undo_execute(undometadata->imgid, before, after);
336 *imgs = g_list_prepend(*imgs, GINT_TO_POINTER(undometadata->imgid));
337 }
338 }
339}
340
342{
344}
345
346static void _undo_metadata_free(gpointer data)
347{
348 dt_undo_metadata_t *metadata = (dt_undo_metadata_t *)data;
350 metadata->before = NULL;
352 metadata->after = NULL;
353 dt_free(metadata);
354}
355
356static void _metadata_undo_data_free(gpointer data)
357{
358 GList *l = (GList *)data;
360 l = NULL;
361}
362
363gchar *_cleanup_metadata_value(const gchar *value)
364{
365 char *v = NULL;
366 char *c = NULL;
367 if (value && value[0])
368 {
369 v = g_strdup(value);
370 c = v + strlen(v) - 1;
371 while(c >= v && *c == ' ') *c-- = '\0';
372 c = v;
373 while(*c == ' ') c++;
374 }
375 c = g_strdup(c ? c : ""); // avoid NULL value
376 dt_free(v);
377 return c;
378}
379
380GList *dt_metadata_get(const int id, const char *key, uint32_t *count)
381{
382 /* Four tables behind one function, because `key` is an XMP name and three XMP names are
383 * not metadata at all: the rating lives in main.images.flags, the subject in
384 * data.tags/main.tagged_images, the colour labels in main.color_labels. Each read goes
385 * to the repository that owns its table -- there is no "metadata" table that could
386 * answer all four.
387 *
388 * `id < 0` means "every selected image" rather than "no image", throughout. That is the
389 * convention the GUI callers pass in and it is preserved. */
390 const int keyid = dt_metadata_get_keyid(key);
391
392 GList *result = NULL;
393
394 // key not found in db. Maybe it's one of our "special" keys (rating, tags and colorlabels)?
395 if(keyid == -1)
396 {
397 if(strncmp(key, "Xmp.xmp.Rating", 14) == 0)
399 else if(strncmp(key, "Xmp.dc.subject", 14) == 0)
401 else if(strncmp(key, "Xmp.darktable.colorlabels", 25) == 0)
403 }
404 else
405 {
406 // So we got this far -- it has to be a generic key-value entry from meta_data
407 result = dt_metadata_repository_get_values(id, keyid);
408 }
409
410 if(!IS_NULL_PTR(count)) *count = g_list_length(result);
411 return result;
412}
413
418
419static void _metadata_add_metadata_to_list(GList **list, const GList *metadata)
420{
421 const GList *m = metadata;
422 while(m)
423 {
424 GList *m2 = g_list_next(m);
425 GList *same_key = _list_find_custom(*list, m->data);
427 gboolean different_value = FALSE;
428 if(same_key) different_value = g_strcmp0(same2->data, m2->data);
430 {
431 // same key but different value - replace the old value by the new one
432 dt_free(same2->data);
433 same2->data = g_strdup(m2->data);
434 }
435 else if(!same_key)
436 {
437 // new key for that image - append the new metadata item
438 *list = g_list_append(*list, g_strdup(m->data));
439 *list = g_list_append(*list, g_strdup(m2->data));
440 }
441 m = g_list_next(m);
442 m = g_list_next(m);
443 }
444}
445
446static void _metadata_remove_metadata_from_list(GList **list, const GList *metadata)
447{
448 // caution: metadata is a simple list here
449 for(const GList *m = metadata; m; m = g_list_next(m))
450 {
451 GList *same_key = _list_find_custom(*list, m->data);
452 if(same_key)
453 {
454 // same key for that image - remove metadata item
456 *list = g_list_remove_link(*list, same_key);
457 dt_free(same_key->data);
459 same_key = NULL;
460 *list = g_list_remove_link(*list, same2);
461 dt_free(same2->data);
463 same2 = NULL;
464 }
465 }
466}
467
474
475static void _metadata_execute(const GList *imgs, const GList *metadata, GList **undo,
476 const gboolean undo_on, const gint action)
477{
478 for(const GList *images = imgs; images; images = g_list_next(images))
479 {
480 const int32_t image_id = GPOINTER_TO_INT(images->data);
481
483 undometadata->imgid = image_id;
484 undometadata->before = dt_metadata_get_list_id(image_id);
485 switch(action)
486 {
487 case DT_MA_SET:
488 undometadata->after = metadata ? g_list_copy_deep((GList *)metadata, (GCopyFunc)g_strdup, NULL) : NULL;
489 break;
490 case DT_MA_ADD:
493 break;
494 case DT_MA_REMOVE:
497 break;
498 default:
500 break;
501 }
502
503 _pop_undo_execute(image_id, undometadata->before, undometadata->after);
504
505 if(undo_on)
506 *undo = g_list_append(*undo, undometadata);
507 else
509 }
510}
511
512void dt_metadata_set(const int32_t imgid, const char *key, const char *value, const gboolean undo_on)
513{
514 if(IS_NULL_PTR(key) || !imgid) return;
515
516 int keyid = dt_metadata_get_keyid(key);
517 if(keyid != -1) // known key
518 {
519 GList *imgs = NULL;
520 if(imgid == UNKNOWN_IMAGE)
521 imgs = dt_act_on_get_images();
522 else
523 imgs = g_list_prepend(imgs, GINT_TO_POINTER(imgid));
524 if(imgs)
525 {
526 GList *undo = NULL;
528
529 const gchar *ckey = g_strdup_printf("%d", keyid);
530 const gchar *cvalue = _cleanup_metadata_value(value);
531 GList *metadata = NULL;
532 metadata = g_list_append(metadata, (gpointer)ckey);
533 metadata = g_list_append(metadata, (gpointer)cvalue);
534
535 _metadata_execute(imgs, metadata, &undo, undo_on, DT_MA_ADD);
536
538 metadata = NULL;
539 g_list_free(imgs);
540 imgs = NULL;
541 if(undo_on)
542 {
545 }
546 }
547 }
548}
549
550void dt_metadata_set_import(const int32_t imgid, const char *key, const char *value)
551{
552 if(IS_NULL_PTR(key) || !imgid || imgid == UNKNOWN_IMAGE) return;
553
554 const int keyid = dt_metadata_get_keyid(key);
555
556 if(keyid != -1) // known key
557 {
558 // FIXME: what does XMP writing preference has to do with anything here ???
559 gboolean imported = dt_image_get_xmp_mode();
561 {
562 const gchar *name = dt_metadata_get_name(keyid);
563 char *setting = g_strdup_printf("plugins/lighttable/metadata/%s_flag", name);
566 }
567 if(imported)
568 {
569 GList *imgs = NULL;
570 imgs = g_list_prepend(imgs, GINT_TO_POINTER(imgid));
571 if(imgs)
572 {
573 GList *undo = NULL;
574
575 const gchar *ckey = g_strdup_printf("%d", keyid);
576 const gchar *cvalue = _cleanup_metadata_value(value);
577 GList *metadata = NULL;
578 metadata = g_list_append(metadata, (gpointer)ckey);
579 metadata = g_list_append(metadata, (gpointer)cvalue);
580
581 _metadata_execute(imgs, metadata, &undo, FALSE, DT_MA_ADD);
582
584 metadata = NULL;
585 g_list_free(imgs);
586 imgs = NULL;
587 }
588 }
589 }
590}
591
592void dt_metadata_set_list(const GList *imgs, GList *key_value, const gboolean undo_on)
593{
594 GList *metadata = NULL;
595 GList *kv = key_value;
596 while(kv)
597 {
598 const gchar *key = (const gchar *)kv->data;
599 const int keyid = dt_metadata_get_keyid(key);
600 if(keyid != -1) // known key
601 {
602 const gchar *ckey = g_strdup_printf("%d", keyid);
603 kv = g_list_next(kv);
604 const gchar *value = (const gchar *)kv->data;
605 kv = g_list_next(kv);
606 if(value)
607 {
608 metadata = g_list_append(metadata, (gchar *)ckey);
609 metadata = g_list_append(metadata, _cleanup_metadata_value(value));
610 }
611 }
612 else
613 {
614 kv = g_list_next(kv);
615 kv = g_list_next(kv);
616 }
617 }
618
619 if(metadata && imgs)
620 {
621 GList *undo = NULL;
623
624 _metadata_execute(imgs, metadata, &undo, undo_on, DT_MA_ADD);
625
626 if(undo_on)
627 {
630 }
631
633 metadata = NULL;
634 }
635}
636
637void dt_metadata_clear(const GList *imgs, const gboolean undo_on)
638{
639 // do not clear internal or hidden metadata
640 GList *metadata = NULL;
641 for(unsigned int i = 0; i < DT_METADATA_NUMBER; i++)
642 {
644 {
645 const gchar *name = dt_metadata_get_name(i);
646 char *setting = g_strdup_printf("plugins/lighttable/metadata/%s_flag", name);
647 const gboolean hidden = dt_conf_get_int(setting) & DT_METADATA_FLAG_HIDDEN;
649 if(!hidden)
650 {
651 // caution: metadata is a simple list here
652 metadata = g_list_prepend(metadata, g_strdup_printf("%d", i));
653 }
654 }
655 }
656
657 if(metadata)
658 {
659 metadata = g_list_reverse(metadata); // list was built in reverse order, so un-reverse it
660 GList *undo = NULL;
662
663 _metadata_execute(imgs, metadata, &undo, undo_on, DT_MA_REMOVE);
664
665 if(undo_on)
666 {
669 }
670
672 metadata = NULL;
673 }
674}
675
676void dt_metadata_set_list_id(const GList *img, const GList *metadata, const gboolean clear_on,
677 const gboolean undo_on)
678{
679 if(img)
680 {
681 GList *undo = NULL;
683
684 _metadata_execute(img, metadata, &undo, undo_on, clear_on ? DT_MA_SET : DT_MA_ADD);
685
686 if(undo_on)
687 {
690 }
691 }
692}
693
694int dt_metadata_already_imported(const char *filename, const char *datetime)
695{
696 if(!filename || IS_NULL_PTR(datetime))
697 return FALSE;
698 char *id = g_strconcat(filename, "-", datetime, NULL);
699 int32_t imgid = UNKNOWN_IMAGE;
701 dt_free(id);
702 return imgid;
703}
704
705// clang-format off
706// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
707// vim: shiftwidth=2 expandtab tabstop=2 cindent
708// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
709// clang-format on
GList * dt_act_on_get_images()
Definition act_on.c:38
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
#define m
Definition basecurve.c:282
GList * dt_colorlabel_repository_get_list(const int32_t imgid)
The colours set on imgid, as a list rather than a bitmask.
main.color_labels: which of the five colour labels are set on an image.
const int t
const float v
static const int row
int dt_conf_key_exists(const char *key)
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
gboolean dt_image_get_xmp_mode()
#define UNKNOWN_IMAGE
Definition image.h:77
const char flag
Definition image.h:285
GList * dt_image_repository_get_ratings(const int32_t imgid)
Star ratings, as main.images.flags encodes them: the low three bits, minus one.
Reading and writing one dt_image_t to and from the library database. The SQL half of what used to be ...
#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:65
static void dt_free_gpointer(gpointer ptr)
Definition mem_alloc.h:104
#define dt_free(ptr)
Definition mem_alloc.h:97
static void _undo_metadata_free(gpointer data)
int dt_metadata_already_imported(const char *filename, const char *datetime)
void dt_metadata_clear(const GList *imgs, const gboolean undo_on)
static void _pop_undo(gpointer user_data, const dt_undo_type_t type, dt_undo_data_t data, const dt_undo_action_t action, GList **imgs)
void dt_metadata_set_list_id(const GList *img, const GList *metadata, const gboolean clear_on, const gboolean undo_on)
static gchar * _get_tb_removed_metadata_string_values(GList *before, GList *after)
gchar * _cleanup_metadata_value(const gchar *value)
const char * dt_metadata_get_subkey(const uint32_t keyid)
GList * dt_metadata_get_list_id(const int id)
const char * dt_metadata_get_key_by_subkey(const char *subkey)
char * key
char * name
const char * dt_metadata_get_key(const uint32_t keyid)
static GArray * _get_tb_added_metadata_rows(const int img, GList *before, GList *after)
void dt_metadata_cleanup(void)
const char * dt_metadata_get_name_by_display_order(const uint32_t order)
dt_metadata_t dt_metadata_get_keyid_by_name(const char *name)
static GList * _list_find_custom(GList *list, gpointer data)
dt_tag_actions_t
@ DT_MA_REMOVE
@ DT_MA_ADD
@ DT_MA_SET
static void _bulk_add_metadata(GArray *rows)
static void _metadata_undo_data_free(gpointer data)
void dt_metadata_set(const int32_t imgid, const char *key, const char *value, const gboolean undo_on)
static const struct @53 dt_metadata_def[]
void dt_metadata_set_list(const GList *imgs, GList *key_value, const gboolean undo_on)
dt_metadata_t dt_metadata_get_keyid(const char *key)
const char * dt_metadata_get_name(const uint32_t keyid)
static void _metadata_add_metadata_to_list(GList **list, const GList *metadata)
static void _metadata_remove_metadata_from_list(GList **list, const GList *metadata)
void dt_metadata_init()
int type
GList * dt_metadata_get(const int id, const char *key, uint32_t *count)
static void _bulk_remove_metadata(const int img, const gchar *metadata_list)
unsigned int dt_metadata_get_nb_user_metadata()
void dt_metadata_set_import(const int32_t imgid, const char *key, const char *value)
int dt_metadata_get_type_by_display_order(const uint32_t order)
static void _metadata_execute(const GList *imgs, const GList *metadata, GList **undo, const gboolean undo_on, const gint action)
uint32_t display_order
static void _pop_undo_execute(const int32_t imgid, GList *before, GList *after)
int dt_metadata_get_type(const uint32_t keyid)
dt_metadata_t dt_metadata_get_keyid_by_display_order(const uint32_t order)
@ DT_METADATA_FLAG_IMPORTED
Definition metadata.h:81
@ DT_METADATA_FLAG_HIDDEN
Definition metadata.h:79
dt_metadata_t
Definition metadata.h:45
@ DT_METADATA_NUMBER
Definition metadata.h:57
@ DT_METADATA_TYPE_OPTIONAL
Definition metadata.h:64
@ DT_METADATA_TYPE_INTERNAL
Definition metadata.h:65
@ DT_METADATA_TYPE_USER
Definition metadata.h:63
GList * dt_metadata_repository_get_values(const int32_t imgid, const int keyid)
Values stored under keyid, ordered by value.
void dt_metadata_repository_remove(const int32_t imgid, const char *keyid_list)
Delete the rows of imgid whose key is in keyid_list, a comma-separated list of decimal key ids....
GList * dt_metadata_repository_get_all(const int32_t imgid)
Every (key, value) pair of imgid, as a flat list.
void dt_metadata_repository_cleanup(void)
Finalise the prepared statements. See dt_colorlabel_repository_cleanup().
int32_t dt_metadata_repository_find_image_by_value(const char *value)
The first image carrying value under any key, or -1.
void dt_metadata_repository_add(const dt_metadata_row_t *rows, const size_t count)
Insert count rows as a single multi-VALUES statement.
main.meta_data: the free-text image metadata (title, description, creator, publisher,...
static const dt_aligned_pixel_simd_t value
Definition simd.h:144
GList * dt_tag_repository_get_attached_names(const int32_t imgid)
Names of the tags attached to an image.
data.tags and main.tagged_images.
void dt_undo_end_group(dt_undo_t *self)
Definition undo.c:149
void dt_undo_start_group(dt_undo_t *self, dt_undo_type_t type)
Definition undo.c:134
void dt_undo_record(dt_undo_t *self, gpointer user_data, dt_undo_type_t type, dt_undo_data_t data, void(*undo)(gpointer user_data, dt_undo_type_t type, dt_undo_data_t item, dt_undo_action_t action, GList **imgs), void(*free_data)(gpointer data))
Definition undo.c:163
dt_undo_type_t
Definition undo.h:40
@ DT_UNDO_METADATA
Definition undo.h:48
dt_undo_t * dt_undo_get_global(void)
Definition darktable.c:638
void * dt_undo_data_t
Definition undo.h:68
dt_undo_action_t
Definition undo.h:63
@ DT_ACTION_UNDO
Definition undo.h:64
gchar * dt_util_dstrcat(gchar *str, const gchar *format,...)
Definition utility.c:98