1 /*
2 This file is part of darktable,
3 Copyright (C) 2010-2021 darktable developers.
4
5 darktable 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 darktable 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 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "common/metadata.h"
20 #include "common/debug.h"
21 #include "common/collection.h"
22 #include "common/undo.h"
23 #include "common/grouping.h"
24 #include "control/conf.h"
25 #include "views/view.h"
26 #include "control/signal.h"
27
28 #include <stdlib.h>
29
30 // this array should contain all dt metadata
31 // add the new metadata at the end when needed
32 // Dependencies
33 // Must match with dt_metadata_t in metadata.h.
34 // Exif.cc: add the new metadata into dt_xmp_keys[]
35 // libs/metadata.c increment version and change legacy_param() accordingly
36 // CAUTION : key, subkey (last term of key) & name must be unique
37
38 static const struct
39 {
40 char *key;
41 char *name;
42 int type;
43 uint32_t display_order;
44 } dt_metadata_def[] = {
45 // clang-format off
46 {"Xmp.dc.creator", N_("creator"), DT_METADATA_TYPE_USER, 2},
47 {"Xmp.dc.publisher", N_("publisher"), DT_METADATA_TYPE_USER, 3},
48 {"Xmp.dc.title", N_("title"), DT_METADATA_TYPE_USER, 0},
49 {"Xmp.dc.description", N_("description"), DT_METADATA_TYPE_USER, 1},
50 {"Xmp.dc.rights", N_("rights"), DT_METADATA_TYPE_USER, 4},
51 {"Xmp.acdsee.notes", N_("notes"), DT_METADATA_TYPE_USER, 5},
52 {"Xmp.darktable.version_name", N_("version name"), DT_METADATA_TYPE_OPTIONAL, 6}
53 // clang-format on
54 };
55
dt_metadata_get_name_by_display_order(const uint32_t order)56 const char *dt_metadata_get_name_by_display_order(const uint32_t order)
57 {
58 if(order < DT_METADATA_NUMBER)
59 {
60 for(unsigned int i = 0; i < DT_METADATA_NUMBER; i++)
61 {
62 if(order == dt_metadata_def[i].display_order)
63 return dt_metadata_def[i].name;
64 }
65 }
66 return NULL;
67 }
68
dt_metadata_get_keyid_by_display_order(const uint32_t order)69 dt_metadata_t dt_metadata_get_keyid_by_display_order(const uint32_t order)
70 {
71 if(order < DT_METADATA_NUMBER)
72 {
73 for(unsigned int i = 0; i < DT_METADATA_NUMBER; i++)
74 {
75 if(order == dt_metadata_def[i].display_order)
76 return i;
77 }
78 }
79 return -1;
80 }
81
dt_metadata_get_keyid_by_name(const char * name)82 dt_metadata_t dt_metadata_get_keyid_by_name(const char* name)
83 {
84 if(!name) return -1;
85 for(unsigned int i = 0; i < DT_METADATA_NUMBER; i++)
86 {
87 if(strncmp(name, dt_metadata_def[i].name, strlen(dt_metadata_def[i].name)) == 0)
88 return i;
89 }
90 return -1;
91 }
92
dt_metadata_get_type_by_display_order(const uint32_t order)93 int dt_metadata_get_type_by_display_order(const uint32_t order)
94 {
95 if(order < DT_METADATA_NUMBER)
96 {
97 for(unsigned int i = 0; i < DT_METADATA_NUMBER; i++)
98 {
99 if(order == dt_metadata_def[i].display_order)
100 return dt_metadata_def[i].type;
101 }
102 }
103 return 0;
104 }
105
dt_metadata_get_name(const uint32_t keyid)106 const char *dt_metadata_get_name(const uint32_t keyid)
107 {
108 if(keyid < DT_METADATA_NUMBER)
109 return dt_metadata_def[keyid].name;
110 else
111 return NULL;
112 }
113
dt_metadata_get_keyid(const char * key)114 dt_metadata_t dt_metadata_get_keyid(const char* key)
115 {
116 if(!key) return -1;
117 for(unsigned int i = 0; i < DT_METADATA_NUMBER; i++)
118 {
119 if(strncmp(key, dt_metadata_def[i].key, strlen(dt_metadata_def[i].key)) == 0)
120 return i;
121 }
122 return -1;
123 }
124
dt_metadata_get_key(const uint32_t keyid)125 const char *dt_metadata_get_key(const uint32_t keyid)
126 {
127 if(keyid < DT_METADATA_NUMBER)
128 return dt_metadata_def[keyid].key;
129 else
130 return NULL;
131 }
132
dt_metadata_get_subkey(const uint32_t keyid)133 const char *dt_metadata_get_subkey(const uint32_t keyid)
134 {
135 if(keyid < DT_METADATA_NUMBER)
136 {
137 char *t = g_strrstr(dt_metadata_def[keyid].key, ".");
138 if(t) return t + 1;
139 }
140 return NULL;
141 }
142
dt_metadata_get_key_by_subkey(const char * subkey)143 const char *dt_metadata_get_key_by_subkey(const char *subkey)
144 {
145 if(subkey)
146 {
147 for(unsigned int i = 0; i < DT_METADATA_NUMBER; i++)
148 {
149 char *t = g_strrstr(dt_metadata_def[i].key, ".");
150 if(t && !g_strcmp0(t + 1, subkey))
151 return dt_metadata_def[i].key;
152 }
153 }
154 return NULL;
155 }
156
dt_metadata_get_type(const uint32_t keyid)157 int dt_metadata_get_type(const uint32_t keyid)
158 {
159 if(keyid < DT_METADATA_NUMBER)
160 return dt_metadata_def[keyid].type;
161 else
162 return 0;
163 }
164
dt_metadata_init()165 void dt_metadata_init()
166 {
167 for(unsigned int i = 0; i < DT_METADATA_NUMBER; i++)
168 {
169 const int type = dt_metadata_get_type(i);
170 const char *name = (gchar *)dt_metadata_get_name(i);
171 char *setting = dt_util_dstrcat(NULL, "plugins/lighttable/metadata/%s_flag", name);
172 if(!dt_conf_key_exists(setting))
173 {
174 // per default should be imported - ignored if "write_sidecar_files" set
175 uint32_t flag = DT_METADATA_FLAG_IMPORTED;
176 if(type == DT_METADATA_TYPE_OPTIONAL)
177 {
178 // per default this one should be hidden
179 flag |= DT_METADATA_FLAG_HIDDEN;
180 }
181 dt_conf_set_int(setting, flag);
182 }
183 g_free(setting);
184 }
185 }
186
187 typedef struct dt_undo_metadata_t
188 {
189 int imgid;
190 GList *before; // list of key/value before
191 GList *after; // list of key/value after
192 } dt_undo_metadata_t;
193
_list_find_custom(GList * list,gpointer data)194 static GList *_list_find_custom(GList *list, gpointer data)
195 {
196 for(GList *i = list; i; i = g_list_next(i))
197 {
198 if(i->data && !g_strcmp0(i->data, data))
199 return i;
200 i = g_list_next(i);
201 }
202 return NULL;
203 }
204
_get_tb_removed_metadata_string_values(GList * before,GList * after)205 static gchar *_get_tb_removed_metadata_string_values(GList *before, GList *after)
206 {
207 GList *b = before;
208 GList *a = after;
209 gchar *metadata_list = NULL;
210
211 while(b)
212 {
213 GList *same_key = _list_find_custom(a, b->data);
214 GList *b2 = g_list_next(b);
215 gboolean different_value = FALSE;
216 const char *value = (char *)b2->data; // if empty we can remove it
217 if(same_key)
218 {
219 GList *same2 = g_list_next(same_key);
220 different_value = g_strcmp0(same2->data, b2->data);
221 }
222 if(!same_key || different_value || !value[0])
223 {
224 metadata_list = dt_util_dstrcat(metadata_list, "%d,", atoi(b->data));
225 }
226 b = g_list_next(b);
227 b = g_list_next(b);
228 }
229 if(metadata_list) metadata_list[strlen(metadata_list) - 1] = '\0';
230 return metadata_list;
231 }
232
_get_tb_added_metadata_string_values(const int img,GList * before,GList * after)233 static gchar *_get_tb_added_metadata_string_values(const int img, GList *before, GList *after)
234 {
235 GList *b = before;
236 GList *a = after;
237 gchar *metadata_list = NULL;
238
239 while(a)
240 {
241 GList *same_key = _list_find_custom(b, a->data);
242 GList *a2 = g_list_next(a);
243 gboolean different_value = FALSE;
244 const char *value = (char *)a2->data; // if empty we don't add it to database
245 if(same_key)
246 {
247 GList *same2 = g_list_next(same_key);
248 different_value = g_strcmp0(same2->data, a2->data);
249 }
250 if((!same_key || different_value) && value[0])
251 {
252 char *escaped_text = sqlite3_mprintf("%q", value);
253 metadata_list = dt_util_dstrcat(metadata_list, "(%d,%d,'%s'),", GPOINTER_TO_INT(img), atoi(a->data), escaped_text);
254 sqlite3_free(escaped_text);
255 }
256 a = g_list_next(a);
257 a = g_list_next(a);
258 }
259 if(metadata_list) metadata_list[strlen(metadata_list) - 1] = '\0';
260 return metadata_list;
261 }
262
_bulk_remove_metadata(const int img,const gchar * metadata_list)263 static void _bulk_remove_metadata(const int img, const gchar *metadata_list)
264 {
265 if(img > 0 && metadata_list)
266 {
267 char *query = NULL;
268 sqlite3_stmt *stmt;
269 query = dt_util_dstrcat(query, "DELETE FROM main.meta_data WHERE id = %d AND key IN (%s)", img, metadata_list);
270 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), query, -1, &stmt, NULL);
271 sqlite3_step(stmt);
272 sqlite3_finalize(stmt);
273 g_free(query);
274 }
275 }
276
_bulk_add_metadata(gchar * metadata_list)277 static void _bulk_add_metadata(gchar *metadata_list)
278 {
279 if(metadata_list)
280 {
281 char *query = NULL;
282 sqlite3_stmt *stmt;
283 query = dt_util_dstrcat(query, "INSERT INTO main.meta_data (id, key, value) VALUES %s", metadata_list);
284 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), query, -1, &stmt, NULL);
285 sqlite3_step(stmt);
286 sqlite3_finalize(stmt);
287 g_free(query);
288 }
289 }
290
_pop_undo_execute(const int imgid,GList * before,GList * after)291 static void _pop_undo_execute(const int imgid, GList *before, GList *after)
292 {
293 gchar *tobe_removed_list = _get_tb_removed_metadata_string_values(before, after);
294 gchar *tobe_added_list = _get_tb_added_metadata_string_values(imgid, before, after);
295
296 _bulk_remove_metadata(imgid, tobe_removed_list);
297 _bulk_add_metadata(tobe_added_list);
298
299 g_free(tobe_removed_list);
300 g_free(tobe_added_list);
301 }
302
_pop_undo(gpointer user_data,const dt_undo_type_t type,dt_undo_data_t data,const dt_undo_action_t action,GList ** imgs)303 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)
304 {
305 if(type == DT_UNDO_METADATA)
306 {
307 for(GList *list = (GList *)data; list; list = g_list_next(list))
308 {
309 dt_undo_metadata_t *undometadata = (dt_undo_metadata_t *)list->data;
310
311 GList *before = (action == DT_ACTION_UNDO) ? undometadata->after : undometadata->before;
312 GList *after = (action == DT_ACTION_UNDO) ? undometadata->before : undometadata->after;
313 _pop_undo_execute(undometadata->imgid, before, after);
314 *imgs = g_list_prepend(*imgs, GINT_TO_POINTER(undometadata->imgid));
315 }
316
317 DT_DEBUG_CONTROL_SIGNAL_RAISE(darktable.signals, DT_SIGNAL_MOUSE_OVER_IMAGE_CHANGE);
318 }
319 }
320
dt_metadata_get_list_id(const int id)321 GList *dt_metadata_get_list_id(const int id)
322 {
323 GList *metadata = NULL;
324 sqlite3_stmt *stmt;
325 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
326 "SELECT key, value FROM main.meta_data WHERE id=?1", -1, &stmt, NULL);
327 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
328 while(sqlite3_step(stmt) == SQLITE_ROW)
329 {
330 const gchar *value = (const char *)sqlite3_column_text(stmt, 1);
331 const gchar *ckey = dt_util_dstrcat(NULL, "%d", sqlite3_column_int(stmt, 0));
332 const gchar *cvalue = g_strdup(value ? value : ""); // to avoid NULL value
333 metadata = g_list_append(metadata, (gpointer)ckey);
334 metadata = g_list_append(metadata, (gpointer)cvalue);
335 }
336 sqlite3_finalize(stmt);
337 return metadata;
338 }
339
_undo_metadata_free(gpointer data)340 static void _undo_metadata_free(gpointer data)
341 {
342 dt_undo_metadata_t *metadata = (dt_undo_metadata_t *)data;
343 g_list_free_full(metadata->before, g_free);
344 g_list_free_full(metadata->after, g_free);
345 g_free(metadata);
346 }
347
_metadata_undo_data_free(gpointer data)348 static void _metadata_undo_data_free(gpointer data)
349 {
350 GList *l = (GList *)data;
351 g_list_free_full(l, _undo_metadata_free);
352 }
353
_cleanup_metadata_value(const gchar * value)354 gchar *_cleanup_metadata_value(const gchar *value)
355 {
356 char *v = NULL;
357 char *c = NULL;
358 if (value && value[0])
359 {
360 v = g_strdup(value);
361 c = v + strlen(v) - 1;
362 while(c >= v && *c == ' ') *c-- = '\0';
363 c = v;
364 while(*c == ' ') c++;
365 }
366 c = g_strdup(c ? c : ""); // avoid NULL value
367 g_free(v);
368 return c;
369 }
370
dt_metadata_get(const int id,const char * key,uint32_t * count)371 GList *dt_metadata_get(const int id, const char *key, uint32_t *count)
372 {
373 GList *result = NULL;
374 sqlite3_stmt *stmt;
375 uint32_t local_count = 0;
376
377 const int keyid = dt_metadata_get_keyid(key);
378 // key not found in db. Maybe it's one of our "special" keys (rating, tags and colorlabels)?
379 if(keyid == -1)
380 {
381 if(strncmp(key, "Xmp.xmp.Rating", 14) == 0)
382 {
383 if(id == -1)
384 {
385 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT flags FROM main.images WHERE id IN "
386 "(SELECT imgid FROM main.selected_images)",
387 -1, &stmt, NULL);
388 }
389 else // single image under mouse cursor
390 {
391 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db), "SELECT flags FROM main.images WHERE id = ?1",
392 -1, &stmt, NULL);
393 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
394 }
395 while(sqlite3_step(stmt) == SQLITE_ROW)
396 {
397 local_count++;
398 int stars = sqlite3_column_int(stmt, 0);
399 stars = (stars & 0x7) - 1;
400 result = g_list_prepend(result, GINT_TO_POINTER(stars));
401 }
402 sqlite3_finalize(stmt);
403 }
404 else if(strncmp(key, "Xmp.dc.subject", 14) == 0)
405 {
406 if(id == -1)
407 {
408 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
409 "SELECT name FROM data.tags t JOIN main.tagged_images i ON "
410 "i.tagid = t.id WHERE imgid IN "
411 "(SELECT imgid FROM main.selected_images)",
412 -1, &stmt, NULL);
413 }
414 else // single image under mouse cursor
415 {
416 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
417 "SELECT name FROM data.tags t JOIN main.tagged_images i ON "
418 "i.tagid = t.id WHERE imgid = ?1",
419 -1, &stmt, NULL);
420 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
421 }
422 while(sqlite3_step(stmt) == SQLITE_ROW)
423 {
424 local_count++;
425 result = g_list_prepend(result, g_strdup((char *)sqlite3_column_text(stmt, 0)));
426 }
427 sqlite3_finalize(stmt);
428 }
429 else if(strncmp(key, "Xmp.darktable.colorlabels", 25) == 0)
430 {
431 if(id == -1)
432 {
433 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
434 "SELECT color FROM main.color_labels WHERE imgid IN "
435 "(SELECT imgid FROM main.selected_images)",
436 -1, &stmt, NULL);
437 }
438 else // single image under mouse cursor
439 {
440 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
441 "SELECT color FROM main.color_labels WHERE imgid=?1 ORDER BY color",
442 -1, &stmt, NULL);
443 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
444 }
445 while(sqlite3_step(stmt) == SQLITE_ROW)
446 {
447 local_count++;
448 result = g_list_prepend(result, GINT_TO_POINTER(sqlite3_column_int(stmt, 0)));
449 }
450 sqlite3_finalize(stmt);
451 }
452 if(count != NULL) *count = local_count;
453 return g_list_reverse(result);
454 }
455
456 // So we got this far -- it has to be a generic key-value entry from meta_data
457 if(id == -1)
458 {
459 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
460 "SELECT value FROM main.meta_data WHERE id IN "
461 "(SELECT imgid FROM main.selected_images) AND key = ?1 ORDER BY value",
462 -1, &stmt, NULL);
463 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, keyid);
464 }
465 else // single image under mouse cursor
466 {
467 DT_DEBUG_SQLITE3_PREPARE_V2(dt_database_get(darktable.db),
468 "SELECT value FROM main.meta_data WHERE id = ?1 AND key = ?2 ORDER BY value", -1,
469 &stmt, NULL);
470 DT_DEBUG_SQLITE3_BIND_INT(stmt, 1, id);
471 DT_DEBUG_SQLITE3_BIND_INT(stmt, 2, keyid);
472 }
473 while(sqlite3_step(stmt) == SQLITE_ROW)
474 {
475 local_count++;
476 char *value = (char *)sqlite3_column_text(stmt, 0);
477 result = g_list_prepend(result, g_strdup(value ? value : "")); // to avoid NULL value
478 }
479 sqlite3_finalize(stmt);
480 if(count != NULL) *count = local_count;
481 return g_list_reverse(result); // list was built in reverse order, so un-reverse it
482 }
483
_metadata_add_metadata_to_list(GList ** list,const GList * metadata)484 static void _metadata_add_metadata_to_list(GList **list, const GList *metadata)
485 {
486 const GList *m = metadata;
487 while(m)
488 {
489 GList *m2 = g_list_next(m);
490 GList *same_key = _list_find_custom(*list, m->data);
491 GList *same2 = g_list_next(same_key);
492 gboolean different_value = FALSE;
493 if(same_key) different_value = g_strcmp0(same2->data, m2->data);
494 if(same_key && different_value)
495 {
496 // same key but different value - replace the old value by the new one
497 g_free(same2->data);
498 same2->data = g_strdup(m2->data);
499 }
500 else if(!same_key)
501 {
502 // new key for that image - append the new metadata item
503 *list = g_list_append(*list, g_strdup(m->data));
504 *list = g_list_append(*list, g_strdup(m2->data));
505 }
506 m = g_list_next(m);
507 m = g_list_next(m);
508 }
509 }
510
_metadata_remove_metadata_from_list(GList ** list,const GList * metadata)511 static void _metadata_remove_metadata_from_list(GList **list, const GList *metadata)
512 {
513 // caution: metadata is a simple list here
514 for(const GList *m = metadata; m; m = g_list_next(m))
515 {
516 GList *same_key = _list_find_custom(*list, m->data);
517 if(same_key)
518 {
519 // same key for that image - remove metadata item
520 GList *same2 = g_list_next(same_key);
521 *list = g_list_remove_link(*list, same_key);
522 g_free(same_key->data);
523 g_list_free(same_key);
524 *list = g_list_remove_link(*list, same2);
525 g_free(same2->data);
526 g_list_free(same2);
527 }
528 }
529 }
530
531 typedef enum dt_tag_actions_t
532 {
533 DT_MA_SET = 0,
534 DT_MA_ADD,
535 DT_MA_REMOVE
536 } dt_tag_actions_t;
537
_metadata_execute(const GList * imgs,const GList * metadata,GList ** undo,const gboolean undo_on,const gint action)538 static void _metadata_execute(const GList *imgs, const GList *metadata, GList **undo,
539 const gboolean undo_on, const gint action)
540 {
541 for(const GList *images = imgs; images; images = g_list_next(images))
542 {
543 const int image_id = GPOINTER_TO_INT(images->data);
544
545 dt_undo_metadata_t *undometadata = (dt_undo_metadata_t *)malloc(sizeof(dt_undo_metadata_t));
546 undometadata->imgid = image_id;
547 undometadata->before = dt_metadata_get_list_id(image_id);
548 switch(action)
549 {
550 case DT_MA_SET:
551 undometadata->after = metadata ? g_list_copy_deep((GList *)metadata, (GCopyFunc)g_strdup, NULL) : NULL;
552 break;
553 case DT_MA_ADD:
554 undometadata->after = g_list_copy_deep(undometadata->before, (GCopyFunc)g_strdup, NULL);
555 _metadata_add_metadata_to_list(&undometadata->after, metadata);
556 break;
557 case DT_MA_REMOVE:
558 undometadata->after = g_list_copy_deep(undometadata->before, (GCopyFunc)g_strdup, NULL);
559 _metadata_remove_metadata_from_list(&undometadata->after, metadata);
560 break;
561 default:
562 undometadata->after = g_list_copy_deep(undometadata->before, (GCopyFunc)g_strdup, NULL);
563 break;
564 }
565
566 _pop_undo_execute(image_id, undometadata->before, undometadata->after);
567
568 if(undo_on)
569 *undo = g_list_append(*undo, undometadata);
570 else
571 _undo_metadata_free(undometadata);
572 }
573 }
574
dt_metadata_set(const int imgid,const char * key,const char * value,const gboolean undo_on)575 void dt_metadata_set(const int imgid, const char *key, const char *value, const gboolean undo_on)
576 {
577 if(!key || !imgid) return;
578
579 int keyid = dt_metadata_get_keyid(key);
580 if(keyid != -1) // known key
581 {
582 GList *imgs = NULL;
583 if(imgid == -1)
584 imgs = g_list_copy((GList *)dt_view_get_images_to_act_on(TRUE, TRUE, FALSE));
585 else
586 imgs = g_list_prepend(imgs, GINT_TO_POINTER(imgid));
587 if(imgs)
588 {
589 GList *undo = NULL;
590 if(undo_on) dt_undo_start_group(darktable.undo, DT_UNDO_METADATA);
591
592 const gchar *ckey = dt_util_dstrcat(NULL, "%d", keyid);
593 const gchar *cvalue = _cleanup_metadata_value(value);
594 GList *metadata = NULL;
595 metadata = g_list_append(metadata, (gpointer)ckey);
596 metadata = g_list_append(metadata, (gpointer)cvalue);
597
598 _metadata_execute(imgs, metadata, &undo, undo_on, DT_MA_ADD);
599
600 g_list_free_full(metadata, g_free);
601 g_list_free(imgs);
602 if(undo_on)
603 {
604 dt_undo_record(darktable.undo, NULL, DT_UNDO_METADATA, undo, _pop_undo, _metadata_undo_data_free);
605 dt_undo_end_group(darktable.undo);
606 }
607 }
608 }
609 }
610
dt_metadata_set_import(const int imgid,const char * key,const char * value)611 void dt_metadata_set_import(const int imgid, const char *key, const char *value)
612 {
613 if(!key || !imgid || imgid == -1) return;
614
615 const int keyid = dt_metadata_get_keyid(key);
616
617 if(keyid != -1) // known key
618 {
619 gboolean imported = dt_conf_get_bool("write_sidecar_files");
620 if(!imported && dt_metadata_get_type(keyid) != DT_METADATA_TYPE_INTERNAL)
621 {
622 const gchar *name = dt_metadata_get_name(keyid);
623 char *setting = dt_util_dstrcat(NULL, "plugins/lighttable/metadata/%s_flag", name);
624 imported = dt_conf_get_int(setting) & DT_METADATA_FLAG_IMPORTED;
625 g_free(setting);
626 }
627 if(imported)
628 {
629 GList *imgs = NULL;
630 imgs = g_list_prepend(imgs, GINT_TO_POINTER(imgid));
631 if(imgs)
632 {
633 GList *undo = NULL;
634
635 const gchar *ckey = dt_util_dstrcat(NULL, "%d", keyid);
636 const gchar *cvalue = _cleanup_metadata_value(value);
637 GList *metadata = NULL;
638 metadata = g_list_append(metadata, (gpointer)ckey);
639 metadata = g_list_append(metadata, (gpointer)cvalue);
640
641 _metadata_execute(imgs, metadata, &undo, FALSE, DT_MA_ADD);
642
643 g_list_free_full(metadata, g_free);
644 g_list_free(imgs);
645 }
646 }
647 }
648 }
649
dt_metadata_set_list(const GList * imgs,GList * key_value,const gboolean undo_on)650 void dt_metadata_set_list(const GList *imgs, GList *key_value, const gboolean undo_on)
651 {
652 GList *metadata = NULL;
653 GList *kv = key_value;
654 while(kv)
655 {
656 const gchar *key = (const gchar *)kv->data;
657 const int keyid = dt_metadata_get_keyid(key);
658 if(keyid != -1) // known key
659 {
660 const gchar *ckey = dt_util_dstrcat(NULL, "%d", keyid);
661 kv = g_list_next(kv);
662 const gchar *value = (const gchar *)kv->data;
663 kv = g_list_next(kv);
664 if(value)
665 {
666 metadata = g_list_append(metadata, (gchar *)ckey);
667 metadata = g_list_append(metadata, _cleanup_metadata_value(value));
668 }
669 }
670 else
671 {
672 kv = g_list_next(kv);
673 kv = g_list_next(kv);
674 }
675 }
676
677 if(metadata && imgs)
678 {
679 GList *undo = NULL;
680 if(undo_on) dt_undo_start_group(darktable.undo, DT_UNDO_METADATA);
681
682 _metadata_execute(imgs, metadata, &undo, undo_on, DT_MA_ADD);
683
684 if(undo_on)
685 {
686 dt_undo_record(darktable.undo, NULL, DT_UNDO_METADATA, undo, _pop_undo, _metadata_undo_data_free);
687 dt_undo_end_group(darktable.undo);
688 }
689
690 g_list_free_full(metadata, g_free);
691 }
692 }
693
dt_metadata_clear(const GList * imgs,const gboolean undo_on)694 void dt_metadata_clear(const GList *imgs, const gboolean undo_on)
695 {
696 // do not clear internal or hidden metadata
697 GList *metadata = NULL;
698 for(unsigned int i = 0; i < DT_METADATA_NUMBER; i++)
699 {
700 if(dt_metadata_get_type(i) != DT_METADATA_TYPE_INTERNAL)
701 {
702 const gchar *name = dt_metadata_get_name(i);
703 char *setting = dt_util_dstrcat(NULL, "plugins/lighttable/metadata/%s_flag", name);
704 const gboolean hidden = dt_conf_get_int(setting) & DT_METADATA_FLAG_HIDDEN;
705 g_free(setting);
706 if(!hidden)
707 {
708 // caution: metadata is a simple list here
709 metadata = g_list_prepend(metadata, dt_util_dstrcat(NULL, "%d", i));
710 }
711 }
712 }
713
714 if(metadata)
715 {
716 metadata = g_list_reverse(metadata); // list was built in reverse order, so un-reverse it
717 GList *undo = NULL;
718 if(undo_on) dt_undo_start_group(darktable.undo, DT_UNDO_METADATA);
719
720 _metadata_execute(imgs, metadata, &undo, undo_on, DT_MA_REMOVE);
721
722 if(undo_on)
723 {
724 dt_undo_record(darktable.undo, NULL, DT_UNDO_METADATA, undo, _pop_undo, _metadata_undo_data_free);
725 dt_undo_end_group(darktable.undo);
726 }
727
728 g_list_free_full(metadata, g_free);
729 }
730 }
731
dt_metadata_set_list_id(const GList * img,const GList * metadata,const gboolean clear_on,const gboolean undo_on)732 void dt_metadata_set_list_id(const GList *img, const GList *metadata, const gboolean clear_on,
733 const gboolean undo_on)
734 {
735 if(img)
736 {
737 GList *undo = NULL;
738 if(undo_on) dt_undo_start_group(darktable.undo, DT_UNDO_METADATA);
739
740 _metadata_execute(img, metadata, &undo, undo_on, clear_on ? DT_MA_SET : DT_MA_ADD);
741
742 if(undo_on)
743 {
744 dt_undo_record(darktable.undo, NULL, DT_UNDO_METADATA, undo, _pop_undo, _metadata_undo_data_free);
745 dt_undo_end_group(darktable.undo);
746 }
747 }
748 }
749
750 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
751 // vim: shiftwidth=2 expandtab tabstop=2 cindent
752 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
753