Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
export_metadata.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2019 luzpaz.
4 Copyright (C) 2019 parafin.
5 Copyright (C) 2019-2020 Pascal Obry.
6 Copyright (C) 2019-2022 Philippe Weyland.
7 Copyright (C) 2019 Sam Smith.
8 Copyright (C) 2020 Marco.
9 Copyright (C) 2021 Hubert Kowalski.
10 Copyright (C) 2021 Ralf Brown.
11 Copyright (C) 2022 Aldric Renaudin.
12 Copyright (C) 2022 Martin Bařinka.
13 Copyright (C) 2022 Victor Forsiuk.
14 Copyright (C) 2025 Aurélien PIERRE.
15
16 darktable is free software: you can redistribute it and/or modify
17 it under the terms of the GNU General Public License as published by
18 the Free Software Foundation, either version 3 of the License, or
19 (at your option) any later version.
20
21 darktable is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
25
26 You should have received a copy of the GNU General Public License
27 along with darktable. If not, see <http://www.gnu.org/licenses/>.
28*/
29#include "bauhaus/bauhaus.h"
30#include "common/darktable.h"
31#include "gui/gdkkeys.h"
32#include "common/debug.h"
34#include "control/conf.h"
35#include "control/control.h"
36#include "control/signal.h"
37#include "dtgtk/button.h"
38
39#include "gui/gtk.h"
40#include "gui/gtkentry.h"
41#include "libs/lib.h"
42#include "libs/lib_api.h"
43#ifdef GDK_WINDOWING_QUARTZ
44#include "osx/osx.h"
45#endif
46#include <gdk/gdkkeysyms.h>
47#include <gtk/gtk.h>
48#include <stdlib.h>
49
58
70
71const GList *dt_exif_get_exiv2_taglist();
72
73// find a string on the list
74static gboolean _find_metadata_iter_per_text(GtkTreeModel *model, GtkTreeIter *iter, gint col, const char *text)
75{
76 if(IS_NULL_PTR(text)) return FALSE;
77 GtkTreeIter it;
78 gboolean valid = gtk_tree_model_get_iter_first(model, &it);
79 char *name;
80 while (valid)
81 {
82 gtk_tree_model_get(model, &it, col, &name, -1);
83 const gboolean found = g_strcmp0(text, name) == 0;
85 if(found)
86 {
87 if (iter) *iter = it;
88 return TRUE;
89 }
90 valid = gtk_tree_model_iter_next(model, &it);
91 }
92 return FALSE;
93}
94
95// add selected metadata tag to formula list
97{
98 GtkTreeIter iter;
99 GtkTreeModel *model = gtk_tree_view_get_model(view);
100 GtkTreeSelection *selection = gtk_tree_view_get_selection(view);
101 if(gtk_tree_selection_get_selected(selection, &model, &iter))
102 {
103 char *tagname;
104 gtk_tree_model_get(model, &iter, DT_LIB_EXPORT_METADATA_COL_XMP, &tagname, -1);
105 if (!_find_metadata_iter_per_text(GTK_TREE_MODEL(d->liststore), NULL, DT_LIB_EXPORT_METADATA_COL_XMP, tagname))
106 {
107 gtk_list_store_append(d->liststore, &iter);
108 gtk_list_store_set(d->liststore, &iter, DT_LIB_EXPORT_METADATA_COL_XMP, tagname,
110 selection = gtk_tree_view_get_selection(d->view);
111 gtk_tree_selection_select_iter(selection, &iter);
112 }
113 dt_free(tagname);
114 }
115}
116
117// choice of a metadata tag
118static gboolean _click_on_metadata_list(GtkWidget *view, GdkEventButton *event, dt_lib_export_metadata_t *d)
119{
120 if(event->type == GDK_2BUTTON_PRESS && event->button == 1)
121 {
122
123 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
124 GtkTreePath *path = NULL;
125 // Get tree path for row that was clicked
126 if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(view), (gint)event->x, (gint)event->y, &path, NULL, NULL, NULL))
127 {
128 gtk_tree_selection_select_path(selection, path);
129 if(event->type == GDK_2BUTTON_PRESS && event->button == 1)
130 {
131 _add_selected_metadata(GTK_TREE_VIEW(view), d);
132 gtk_tree_path_free(path);
133 return TRUE;
134 }
135 }
136 gtk_tree_path_free(path);
137 }
138 return FALSE;
139}
140
141// routine to set individual visibility flag
142static gboolean _set_matching_tag_visibility(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, dt_lib_export_metadata_t *d)
143{
144 gboolean visible;
145 gchar *tagname = NULL;
146 gtk_tree_model_get(model, iter, DT_LIB_EXPORT_METADATA_COL_XMP, &tagname, -1);
147 if (!d->sel_entry_text[0])
148 visible = TRUE;
149 else
150 {
151 gchar *haystack = g_utf8_strdown(tagname, -1);
152 gchar *needle = g_utf8_strdown(d->sel_entry_text, -1);
153 visible = (g_strrstr(haystack, needle) != NULL);
154 dt_free(haystack);
155 dt_free(needle);
156 }
157 gtk_list_store_set(GTK_LIST_STORE(model), iter, DT_LIB_EXPORT_METADATA_COL_VISIBLE, visible, -1);
158 dt_free(tagname);
159 return FALSE;
160}
161
162// set the metadata tag visibility aligned with filter
163static void _tag_name_changed(GtkEntry *entry, dt_lib_export_metadata_t *d)
164{
165 d->sel_entry_text = gtk_entry_get_text(GTK_ENTRY(d->sel_entry));
166 GtkTreeModel *model = gtk_tree_view_get_model(d->sel_view);
167 GtkTreeModel *store = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(model));
168 gtk_tree_model_foreach(store, (GtkTreeModelForeachFunc)_set_matching_tag_visibility, d);
169}
170
171// dialog to add metadata tag into the formula list
172static void _add_tag_button_clicked(GtkButton *button, dt_lib_export_metadata_t *d)
173{
174 GtkWidget *dialog = gtk_dialog_new_with_buttons(_("select tag"), GTK_WINDOW(d->dialog), GTK_DIALOG_DESTROY_WITH_PARENT,
175 _("add"), GTK_RESPONSE_ACCEPT, _("done"), GTK_RESPONSE_NONE, NULL);
176 g_signal_connect(dialog, "key-press-event", G_CALLBACK(dt_handle_dialog_enter), NULL);
177 gtk_window_set_default_size(GTK_WINDOW(dialog), 300, -1);
178 GtkWidget *area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
179 GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
180 gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
181 gtk_container_add(GTK_CONTAINER(area), vbox);
182
183 GtkWidget *entry = gtk_entry_new();
185 d->sel_entry = entry;
186 gtk_entry_set_text(GTK_ENTRY(entry), "");
187 gtk_widget_set_tooltip_text(entry, _("list filter"));
188 gtk_box_pack_start(GTK_BOX(vbox), entry, TRUE, TRUE, 0);
189 g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(_tag_name_changed), d);
190
191 GtkWidget *w = gtk_scrolled_window_new(NULL, NULL);
192 gtk_widget_set_size_request(w, DT_PIXEL_APPLY_DPI(500), DT_PIXEL_APPLY_DPI(300));
193 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
194 dt_gui_add_class(w, "dt_recessed_scroll");
195 gtk_box_pack_start(GTK_BOX(vbox), w, TRUE, TRUE, 0);
196 GtkTreeView *view = GTK_TREE_VIEW(gtk_tree_view_new());
197 d->sel_view = view;
198 gtk_container_add(GTK_CONTAINER(w), GTK_WIDGET(view));
199 gtk_widget_set_tooltip_text(GTK_WIDGET(view), _("list of available tags. click 'add' button or double-click on tag to add the selected one"));
200 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(view), GTK_SELECTION_SINGLE);
201 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
202 GtkTreeViewColumn *col = gtk_tree_view_column_new_with_attributes(_("tag"), renderer, "text", 0, NULL);
203 gtk_tree_view_append_column(view, col);
204 renderer = gtk_cell_renderer_text_new();
205 col = gtk_tree_view_column_new_with_attributes(_("type"), renderer, "text", 1, NULL);
206 gtk_tree_view_append_column(view, col);
207 GtkListStore *liststore = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
208 GtkTreeModel *model = gtk_tree_model_filter_new(GTK_TREE_MODEL(liststore), NULL);
209 gtk_tree_model_filter_set_visible_column(GTK_TREE_MODEL_FILTER(model), DT_LIB_EXPORT_METADATA_COL_VISIBLE);
210
211 // populate the metadata tag list with exiv2 information
212 for(GList *tag = d->taglist; tag; tag = g_list_next(tag))
213 {
214 GtkTreeIter iter;
215 gtk_list_store_append(liststore, &iter);
216 const char *tagname = tag->data;
217 char *type = g_strstr_len(tagname, -1, ",");
218 if(type)
219 {
220 type[0] = '\0';
221 type++;
222 }
223 gtk_list_store_set(liststore, &iter, DT_LIB_EXPORT_METADATA_COL_XMP, tagname, DT_LIB_EXPORT_METADATA_COL_TYPE, type,
225 if(type)
226 {
227 type--;
228 type[0] = ',';
229 }
230 }
231
232 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(liststore), DT_LIB_EXPORT_METADATA_COL_XMP, GTK_SORT_ASCENDING);
233 gtk_tree_view_set_model(view, model);
234 g_object_unref(model);
235 g_signal_connect(G_OBJECT(view), "button-press-event", G_CALLBACK(_click_on_metadata_list), (gpointer)d);
236
237 #ifdef GDK_WINDOWING_QUARTZ
239 #endif
240 gtk_widget_show_all(dialog);
241 while (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
242 {
244 }
245 gtk_widget_destroy(dialog);
246}
247
249{
250 GtkTreeIter iter;
251 GtkTreeModel *model = GTK_TREE_MODEL(d->liststore);
252 GtkTreeSelection *selection = gtk_tree_view_get_selection(d->view);
253 if(gtk_tree_selection_get_selected(selection, &model, &iter))
254 {
255 gtk_list_store_remove(d->liststore, &iter);
256 }
257}
258
260{
262}
263
264static gboolean _key_press_on_list(GtkWidget *widget, GdkEventKey *event, dt_lib_export_metadata_t *d)
265{
266 guint key = dt_keys_mainpad_alternatives(event->keyval);
267
268 if(event->type == GDK_KEY_PRESS && (key == GDK_KEY_Delete) && !event->state)
269 {
271 return TRUE;
272 }
273 return FALSE;
274}
275
276static void _tags_toggled(GtkToggleButton *dttag, dt_lib_export_metadata_t *d)
277{
278 const gboolean tags = gtk_toggle_button_get_active(dttag);
279 gtk_widget_set_sensitive(d->private, tags);
280 gtk_widget_set_sensitive(d->synonyms, tags);
281 gtk_widget_set_sensitive(d->omithierarchy, tags);
282}
283
284static void _formula_edited(GtkCellRenderer *renderer, gchar *path, gchar *new_text, dt_lib_export_metadata_t *d)
285{
286 GtkTreeIter iter;
287 if (gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(d->liststore), &iter, path))
288 gtk_list_store_set(d->liststore, &iter, DT_LIB_EXPORT_METADATA_COL_FORMULA, new_text, -1);
289}
290
291static void _formula_editing_started(GtkCellRenderer *renderer, GtkCellEditable *editable,
292 char *path, dt_lib_export_metadata_t *d)
293{
295}
296
297char *dt_lib_export_metadata_configuration_dialog(char *metadata_presets, const gboolean ondisk)
298{
300
302 GtkWidget *dialog = gtk_dialog_new_with_buttons(_("edit metadata exportation"), GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT,
303 _("cancel"), GTK_RESPONSE_NONE, _("save"), GTK_RESPONSE_ACCEPT, NULL);
304 d->dialog = dialog;
305 g_signal_connect(dialog, "key-press-event", G_CALLBACK(dt_handle_dialog_enter), NULL);
306
307 gtk_window_set_default_size(GTK_WINDOW(dialog), 300, -1);
308 GtkWidget *area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
309
310 GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
311 gtk_container_add(GTK_CONTAINER(area), hbox);
312
313 // general info
314 GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
315 gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
316 gtk_container_add(GTK_CONTAINER(hbox), vbox);
317 GtkWidget *label = gtk_label_new(_("general settings"));
318 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
319 GtkWidget *vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
320 gtk_box_pack_start(GTK_BOX(vbox), vbox2, FALSE, TRUE, 0);
321
322 GtkWidget *exiftag = gtk_check_button_new_with_label(_("EXIF data"));
323 gtk_widget_set_tooltip_text(exiftag, _("export EXIF metadata"));
324 gtk_box_pack_start(GTK_BOX(vbox2), exiftag, FALSE, TRUE, 0);
325 GtkWidget *dtmetadata = gtk_check_button_new_with_label(_("metadata"));
326 gtk_widget_set_tooltip_text(dtmetadata, _("export dt xmp metadata (from metadata editor module)"));
327 gtk_box_pack_start(GTK_BOX(vbox2), dtmetadata, FALSE, TRUE, 0);
328
329 GtkWidget *calculated;
330 if (!ondisk)
331 {
332 GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
333 gtk_box_pack_start(GTK_BOX(vbox2), box, FALSE, TRUE, 0);
334 GtkWidget *vbox3 = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
335 gtk_box_pack_start(GTK_BOX(box), vbox3, FALSE, TRUE, 10);
336 calculated = gtk_check_button_new_with_label(_("only embedded"));
337 gtk_widget_set_tooltip_text(calculated, _("per default the interface sends some (limited) metadata beside the image to remote storage.\n"
338 "to avoid this and let only image embedded dt xmp metadata, check this flag.\n"
339 "if remote storage doesn't understand dt xmp metadata, you can use calculated metadata instead"));
340 gtk_box_pack_start(GTK_BOX(vbox3), calculated, FALSE, TRUE, 0);
341 }
342
343 GtkWidget *geotag = gtk_check_button_new_with_label(_("geo tags"));
344 gtk_widget_set_tooltip_text(geotag, _("export geo tags"));
345 gtk_box_pack_start(GTK_BOX(vbox2), geotag, FALSE, TRUE, 0);
346 GtkWidget *dttag = gtk_check_button_new_with_label(_("tags"));
347 gtk_widget_set_tooltip_text(dttag, _("export tags (to Xmp.dc.Subject)"));
348 gtk_box_pack_start(GTK_BOX(vbox2), dttag, FALSE, TRUE, 0);
349 g_signal_connect(G_OBJECT(dttag), "clicked", G_CALLBACK(_tags_toggled), (gpointer)d);
350
351 GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
352 gtk_box_pack_start(GTK_BOX(vbox2), box, FALSE, TRUE, 0);
353 GtkWidget *vbox3 = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
354 gtk_box_pack_start(GTK_BOX(box), vbox3, FALSE, TRUE, 10);
355 d->private = gtk_check_button_new_with_label(_("private tags"));
356 gtk_widget_set_tooltip_text(d->private, _("export private tags"));
357 gtk_box_pack_start(GTK_BOX(vbox3), d->private, FALSE, TRUE, 0);
358 d->synonyms = gtk_check_button_new_with_label(_("synonyms"));
359 gtk_widget_set_tooltip_text(d->synonyms, _("export tags synonyms"));
360 gtk_box_pack_start(GTK_BOX(vbox3), d->synonyms, FALSE, TRUE, 0);
361 d->omithierarchy = gtk_check_button_new_with_label(_("omit hierarchy"));
362 gtk_widget_set_tooltip_text(d->omithierarchy, _("only the last part of the hierarchical tags is included. can be useful if categories are not used"));
363 gtk_box_pack_start(GTK_BOX(vbox3), d->omithierarchy, FALSE, TRUE, 0);
364
365 GtkWidget *hierarchical = gtk_check_button_new_with_label(_("hierarchical tags"));
366 gtk_widget_set_tooltip_text(hierarchical, _("export hierarchical tags (to Xmp.lr.Hierarchical Subject)"));
367 gtk_box_pack_start(GTK_BOX(vbox2), hierarchical, FALSE, TRUE, 0);
368 GtkWidget *dthistory = gtk_check_button_new_with_label(_("develop history"));
369 gtk_widget_set_tooltip_text(dthistory, _("export dt development data (recovery purpose in case of loss of database or xmp file)"));
370 gtk_box_pack_start(GTK_BOX(vbox2), dthistory, FALSE, TRUE, 0);
371
372 // specific rules
373 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, DT_GUI_BOX_SPACING);
374 gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
375 gtk_container_add(GTK_CONTAINER(hbox), vbox);
376 label = gtk_label_new(_("per metadata settings"));
377 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
378
379 GtkWidget *w = gtk_scrolled_window_new(NULL, NULL);
380 gtk_widget_set_size_request(w, DT_PIXEL_APPLY_DPI(450), DT_PIXEL_APPLY_DPI(100));
381 gtk_widget_set_hexpand(w, TRUE);
382 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
383 dt_gui_add_class(w, "dt_recessed_scroll");
384 gtk_box_pack_start(GTK_BOX(vbox), w, TRUE, TRUE, 0);
385 GtkTreeView *view = GTK_TREE_VIEW(gtk_tree_view_new());
386 d->view = view;
387 gtk_container_add(GTK_CONTAINER(w), GTK_WIDGET(view));
388 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(view), GTK_SELECTION_SINGLE);
389 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
390 GtkTreeViewColumn *col = gtk_tree_view_column_new_with_attributes(_("redefined tag"), renderer, "text", 0, NULL);
391 gtk_tree_view_append_column(view, col);
392 renderer = gtk_cell_renderer_text_new();
393 g_object_set(renderer, "editable", TRUE, NULL);
394 g_signal_connect(G_OBJECT(renderer), "edited", G_CALLBACK(_formula_edited), (gpointer)d);
395 g_signal_connect(renderer, "editing-started" , G_CALLBACK(_formula_editing_started), (gpointer)d);
396 col = gtk_tree_view_column_new_with_attributes(_("formula"), renderer, "text", 2, NULL);
397 gtk_tree_view_append_column(view, col);
398 gtk_widget_set_tooltip_text(GTK_WIDGET(view),
399 _("list of calculated metadata\n"
400 "click on '+' button to select and add new metadata\n"
401 "if formula is empty, the corresponding metadata is removed from exported file,\n"
402 "if formula is \'=\', the EXIF metadata is exported even if EXIF data are disabled\n"
403 "otherwise the corresponding metadata is calculated and added to exported file\n"
404 "click on formula cell to edit\n"
405 "type '$(' to activate the completion and see the list of variables"));
406 g_signal_connect(G_OBJECT(view), "key_press_event", G_CALLBACK(_key_press_on_list), (gpointer)d);
407
408 GtkListStore *liststore = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
409 d->liststore = liststore;
410 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(liststore), DT_LIB_EXPORT_METADATA_COL_XMP, GTK_SORT_ASCENDING);
411 gtk_tree_view_set_model(view, GTK_TREE_MODEL(liststore));
412 g_object_unref(liststore);
413 d->taglist = (GList *)dt_exif_get_exiv2_taglist();
414 GList *list = dt_util_str_to_glist("\1", metadata_presets);
415 int32_t flags = 0;
416 if (list)
417 {
418 char *flags_hexa = list->data;
419 flags = strtol(flags_hexa, NULL, 16);
420 list = g_list_remove(list, flags_hexa);
421 dt_free(flags_hexa);
422 if (list)
423 {
424 for (GList *tags = list; tags; tags = g_list_next(tags))
425 {
426 GtkTreeIter iter;
427 const char *tagname = (char *)tags->data;
428 tags = g_list_next(tags);
429 if (IS_NULL_PTR(tags)) break;
430 const char *formula = (char *)tags->data;
431 gtk_list_store_append(d->liststore, &iter);
432 gtk_list_store_set(d->liststore, &iter, DT_LIB_EXPORT_METADATA_COL_XMP, tagname,
434 }
435 }
436 }
437 g_list_free_full(list, dt_free_gpointer);
438 list = NULL;
439
440 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(exiftag), flags & DT_META_EXIF);
441 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dtmetadata), flags & DT_META_METADATA);
442 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(geotag), flags & DT_META_GEOTAG);
443 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dttag), flags & DT_META_TAG);
444 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->private), flags & DT_META_PRIVATE_TAG);
445 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->synonyms), flags & DT_META_SYNONYMS_TAG);
446 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(d->omithierarchy), flags & DT_META_OMIT_HIERARCHY);
447 _tags_toggled(GTK_TOGGLE_BUTTON(dttag), d);
448 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(hierarchical), flags & DT_META_HIERARCHICAL_TAG);
449 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dthistory), flags & DT_META_DT_HISTORY);
450 if (!ondisk)
451 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(calculated), flags & DT_META_CALCULATED);
452
453 box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, DT_GUI_BOX_SPACING);
454 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, TRUE, 0);
455
457 gtk_widget_set_tooltip_text(button, _("add an output metadata tag"));
458 gtk_box_pack_end(GTK_BOX(box), button, FALSE, TRUE, 0);
459 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(_add_tag_button_clicked), (gpointer)d);
460
462 gtk_widget_set_tooltip_text(button, _("delete metadata tag"));
463 gtk_box_pack_end(GTK_BOX(box), button, FALSE, TRUE, 0);
464 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(_delete_tag_button_clicked), (gpointer)d);
465
466#ifdef GDK_WINDOWING_QUARTZ
468#endif
469 gtk_widget_show_all(dialog);
470
471 char *newlist = metadata_presets;
472 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
473 {
474 const gint newflags = (
475 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(exiftag)) ? DT_META_EXIF : 0) |
476 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dtmetadata)) ? DT_META_METADATA : 0) |
477 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(geotag)) ? DT_META_GEOTAG : 0) |
478 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dttag)) ? DT_META_TAG : 0) |
479 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->private)) ? DT_META_PRIVATE_TAG : 0) |
480 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->synonyms)) ? DT_META_SYNONYMS_TAG : 0) |
481 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(d->omithierarchy)) ? DT_META_OMIT_HIERARCHY : 0) |
482 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hierarchical)) ? DT_META_HIERARCHICAL_TAG : 0) |
483 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dthistory)) ? DT_META_DT_HISTORY : 0) |
484 (!ondisk ? (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(calculated)) ? DT_META_CALCULATED : 0) : 0)
485 );
486
487 newlist = g_strdup_printf("%x", newflags);
488 GtkTreeIter iter;
489 gboolean valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(d->liststore), &iter);
490 while(valid)
491 {
492 char *tagname, *formula;
493 gtk_tree_model_get(GTK_TREE_MODEL(d->liststore), &iter, DT_LIB_EXPORT_METADATA_COL_XMP, &tagname,
495 // metadata presets are stored into a single string with '\1' as a separator
496 newlist = dt_util_dstrcat(newlist,"\1%s\1%s", tagname, formula);
497 dt_free(tagname);
498 dt_free(formula);
499 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL(d->liststore), &iter);
500 }
501 dt_free(metadata_presets);
503 }
504 gtk_widget_destroy(dialog);
505 dt_free(d);
506 return newlist;
507}
508
509// clang-format off
510// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
511// vim: shiftwidth=2 expandtab tabstop=2 cindent
512// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
513// clang-format on
#define TRUE
Definition ashift_lsd.c:162
#define FALSE
Definition ashift_lsd.c:158
GtkWidget * dtgtk_button_new(DTGTKCairoPaintIconFunc paint, gint paintflags, void *paintdata)
Definition button.c:134
char * key
int type
char * name
uint32_t view(const dt_view_t *self)
Definition darkroom.c:227
darktable_t darktable
Definition darktable.c:181
static void dt_free_gpointer(gpointer ptr)
Definition darktable.h:463
#define dt_free(ptr)
Definition darktable.h:456
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:281
int store(dt_imageio_module_storage_t *self, dt_imageio_module_data_t *sdata, const int32_t imgid, dt_imageio_module_format_t *format, dt_imageio_module_data_t *fdata, const int num, const int total, const gboolean high_quality, const gboolean export_masks, dt_colorspaces_color_profile_type_t icc_type, const gchar *icc_filename, dt_iop_color_intent_t icc_intent, dt_export_metadata_t *metadata)
Definition disk.c:252
void dtgtk_cairo_paint_minus_simple(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_plus_simple(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
static void _add_tag_button_clicked(GtkButton *button, dt_lib_export_metadata_t *d)
static void _add_selected_metadata(GtkTreeView *view, dt_lib_export_metadata_t *d)
static gboolean _find_metadata_iter_per_text(GtkTreeModel *model, GtkTreeIter *iter, gint col, const char *text)
static gboolean _click_on_metadata_list(GtkWidget *view, GdkEventButton *event, dt_lib_export_metadata_t *d)
static void _remove_tag_from_list(dt_lib_export_metadata_t *d)
static void _delete_tag_button_clicked(GtkButton *button, dt_lib_export_metadata_t *d)
static void _formula_editing_started(GtkCellRenderer *renderer, GtkCellEditable *editable, char *path, dt_lib_export_metadata_t *d)
static gboolean _set_matching_tag_visibility(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, dt_lib_export_metadata_t *d)
static gboolean _key_press_on_list(GtkWidget *widget, GdkEventKey *event, dt_lib_export_metadata_t *d)
dt_lib_tagging_cols_t
@ DT_LIB_EXPORT_METADATA_COL_FORMULA
@ DT_LIB_EXPORT_METADATA_COL_TYPE
@ DT_LIB_EXPORT_METADATA_COL_VISIBLE
@ DT_LIB_EXPORT_METADATA_COL_XMP
@ DT_LIB_EXPORT_METADATA_NUM_COLS
char * dt_lib_export_metadata_configuration_dialog(char *metadata_presets, const gboolean ondisk)
static void _tag_name_changed(GtkEntry *entry, dt_lib_export_metadata_t *d)
static void _formula_edited(GtkCellRenderer *renderer, gchar *path, gchar *new_text, dt_lib_export_metadata_t *d)
const GList * dt_exif_get_exiv2_taglist()
Definition exif.cc:356
static void _tags_toggled(GtkToggleButton *dttag, dt_lib_export_metadata_t *d)
static guint dt_keys_mainpad_alternatives(const guint key_val)
Remap keypad keys to usual mainpad ones.
Definition gdkkeys.h:113
void dt_accels_disconnect_on_text_input(GtkWidget *widget)
Disconnects accels when a text or search entry gets the focus, and reconnects them when it looses it....
Definition gtk.c:3225
void dt_gui_add_class(GtkWidget *widget, const gchar *class_name)
Definition gtk.c:133
GtkWidget * dt_ui_main_window(dt_ui_t *ui)
get the main window widget
#define DT_GUI_BOX_SPACING
Definition gtk.h:109
#define DT_PIXEL_APPLY_DPI(value)
Definition gtk.h:90
void dt_gtkentry_setup_completion(GtkEntry *entry, const dt_gtkentry_completion_spec *compl_list, const char *trigger_char)
Definition gtkentry.c:173
const dt_gtkentry_completion_spec * dt_gtkentry_get_default_path_compl_list()
Definition gtkentry.c:197
const char * model
gboolean dt_handle_dialog_enter(GtkWidget *widget, GdkEventKey *event, gpointer data)
Definition lib.c:1551
void dt_lib_export_metadata_set_conf(const char *metadata_presets)
@ DT_META_OMIT_HIERARCHY
@ DT_META_HIERARCHICAL_TAG
@ DT_META_TAG
@ DT_META_CALCULATED
@ DT_META_EXIF
@ DT_META_METADATA
@ DT_META_DT_HISTORY
@ DT_META_PRIVATE_TAG
@ DT_META_GEOTAG
@ DT_META_SYNONYMS_TAG
dt_mipmap_buffer_dsc_flags flags
Definition mipmap_cache.c:4
void dt_osx_disallow_fullscreen(GtkWidget *widget)
Definition osx.mm:104
struct _GtkWidget GtkWidget
Definition splash.h:29
struct dt_gui_gtk_t * gui
Definition darktable.h:775
dt_ui_t * ui
Definition gtk.h:164
GList * dt_util_str_to_glist(const gchar *separator, const gchar *text)
Definition utility.c:830
gchar * dt_util_dstrcat(gchar *str, const gchar *format,...)
Definition utility.c:95