1 /*
2 This file is part of darktable,
3 Copyright (C) 2019-2020 darktable developers.
4
5
6 darktable is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 darktable is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with darktable. If not, see <http://www.gnu.org/licenses/>.
18 */
19 #include "bauhaus/bauhaus.h"
20 #include "common/darktable.h"
21 #include "common/debug.h"
22 #include "common/imageio_module.h"
23 #include "control/conf.h"
24 #include "control/control.h"
25 #include "control/signal.h"
26 #include "dtgtk/button.h"
27 #include "gui/accelerators.h"
28 #include "gui/gtk.h"
29 #include "gui/gtkentry.h"
30 #include "libs/lib.h"
31 #include "libs/lib_api.h"
32 #ifdef GDK_WINDOWING_QUARTZ
33 #include "osx/osx.h"
34 #endif
35 #include <gdk/gdkkeysyms.h>
36 #include <gtk/gtk.h>
37 #include <stdlib.h>
38
39 typedef enum dt_lib_tagging_cols_t
40 {
41 DT_LIB_EXPORT_METADATA_COL_XMP = 0,
42 DT_LIB_EXPORT_METADATA_COL_TYPE,
43 DT_LIB_EXPORT_METADATA_COL_FORMULA,
44 DT_LIB_EXPORT_METADATA_COL_VISIBLE,
45 DT_LIB_EXPORT_METADATA_NUM_COLS
46 } dt_lib_tagging_cols_t;
47
48 typedef struct dt_lib_export_metadata_t
49 {
50 GtkTreeView *view;
51 GtkListStore *liststore;
52 GtkWidget *dialog;
53 GtkTreeView *sel_view;
54 GtkWidget *sel_entry;
55 const gchar *sel_entry_text;
56 GList *taglist;
57 } dt_lib_export_metadata_t;
58
59 const GList *dt_exif_get_exiv2_taglist();
60
61 // find a string on the list
find_metadata_iter_per_text(GtkTreeModel * model,GtkTreeIter * iter,gint col,const char * text)62 static gboolean find_metadata_iter_per_text(GtkTreeModel *model, GtkTreeIter *iter, gint col, const char *text)
63 {
64 if(!text) return FALSE;
65 GtkTreeIter it;
66 gboolean valid = gtk_tree_model_get_iter_first(model, &it);
67 char *name;
68 while (valid)
69 {
70 gtk_tree_model_get(model, &it, col, &name, -1);
71 const gboolean found = g_strcmp0(text, name) == 0;
72 g_free(name);
73 if(found)
74 {
75 if (iter) *iter = it;
76 return TRUE;
77 }
78 valid = gtk_tree_model_iter_next(model, &it);
79 }
80 return FALSE;
81 }
82
83 // add selected metadata tag to formula list
add_selected_metadata(GtkTreeView * view,dt_lib_export_metadata_t * d)84 static void add_selected_metadata(GtkTreeView *view, dt_lib_export_metadata_t *d)
85 {
86 GtkTreeIter iter;
87 GtkTreeModel *model = gtk_tree_view_get_model(view);
88 GtkTreeSelection *selection = gtk_tree_view_get_selection(view);
89 if(gtk_tree_selection_get_selected(selection, &model, &iter))
90 {
91 char *tagname;
92 gtk_tree_model_get(model, &iter, DT_LIB_EXPORT_METADATA_COL_XMP, &tagname, -1);
93 if (!find_metadata_iter_per_text(GTK_TREE_MODEL(d->liststore), NULL, DT_LIB_EXPORT_METADATA_COL_XMP, tagname))
94 {
95 gtk_list_store_append(d->liststore, &iter);
96 gtk_list_store_set(d->liststore, &iter, DT_LIB_EXPORT_METADATA_COL_XMP, tagname,
97 DT_LIB_EXPORT_METADATA_COL_FORMULA, "", -1);
98 selection = gtk_tree_view_get_selection(d->view);
99 gtk_tree_selection_select_iter(selection, &iter);
100 }
101 g_free(tagname);
102 }
103 }
104
105 // choice of a metadata tag
click_on_metadata_list(GtkWidget * view,GdkEventButton * event,dt_lib_export_metadata_t * d)106 static gboolean click_on_metadata_list(GtkWidget *view, GdkEventButton *event, dt_lib_export_metadata_t *d)
107 {
108 if(event->type == GDK_2BUTTON_PRESS && event->button == 1)
109 {
110
111 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
112 GtkTreePath *path = NULL;
113 // Get tree path for row that was clicked
114 if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(view), (gint)event->x, (gint)event->y, &path, NULL, NULL, NULL))
115 {
116 gtk_tree_selection_select_path(selection, path);
117 if(event->type == GDK_2BUTTON_PRESS && event->button == 1)
118 {
119 add_selected_metadata(GTK_TREE_VIEW(view), d);
120 gtk_tree_path_free(path);
121 return TRUE;
122 }
123 }
124 gtk_tree_path_free(path);
125 }
126 return FALSE;
127 }
128
129 // routine to set individual visibility flag
set_matching_tag_visibility(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,dt_lib_export_metadata_t * d)130 static gboolean set_matching_tag_visibility(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, dt_lib_export_metadata_t *d)
131 {
132 gboolean visible;
133 gchar *tagname = NULL;
134 gtk_tree_model_get(model, iter, DT_LIB_EXPORT_METADATA_COL_XMP, &tagname, -1);
135 if (!d->sel_entry_text[0])
136 visible = TRUE;
137 else
138 {
139 gchar *haystack = g_utf8_strdown(tagname, -1);
140 gchar *needle = g_utf8_strdown(d->sel_entry_text, -1);
141 visible = (g_strrstr(haystack, needle) != NULL);
142 g_free(haystack);
143 g_free(needle);
144 }
145 gtk_list_store_set(GTK_LIST_STORE(model), iter, DT_LIB_EXPORT_METADATA_COL_VISIBLE, visible, -1);
146 g_free(tagname);
147 return FALSE;
148 }
149
150 // set the metadata tag visibility aligned with filter
_tag_name_changed(GtkEntry * entry,dt_lib_export_metadata_t * d)151 static void _tag_name_changed(GtkEntry *entry, dt_lib_export_metadata_t *d)
152 {
153 d->sel_entry_text = gtk_entry_get_text(GTK_ENTRY(d->sel_entry));
154 GtkTreeModel *model = gtk_tree_view_get_model(d->sel_view);
155 GtkTreeModel *store = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(model));
156 gtk_tree_model_foreach(store, (GtkTreeModelForeachFunc)set_matching_tag_visibility, d);
157 }
158
159 // dialog to add metadata tag into the formula list
add_tag_button_clicked(GtkButton * button,dt_lib_export_metadata_t * d)160 static void add_tag_button_clicked(GtkButton *button, dt_lib_export_metadata_t *d)
161 {
162 GtkWidget *dialog = gtk_dialog_new_with_buttons(_("select tag"), GTK_WINDOW(d->dialog), GTK_DIALOG_DESTROY_WITH_PARENT,
163 _("add"), GTK_RESPONSE_YES, _("done"), GTK_RESPONSE_NONE, NULL);
164 gtk_window_set_default_size(GTK_WINDOW(dialog), 300, -1);
165 GtkWidget *area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
166 GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
167 gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
168 gtk_container_add(GTK_CONTAINER(area), vbox);
169
170 GtkWidget *entry = gtk_entry_new();
171 d->sel_entry = entry;
172 gtk_entry_set_text(GTK_ENTRY(entry), "");
173 gtk_widget_set_tooltip_text(entry, _("list filter"));
174 gtk_box_pack_start(GTK_BOX(vbox), entry, TRUE, TRUE, 0);
175 g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(_tag_name_changed), d);
176
177 GtkWidget *w = gtk_scrolled_window_new(NULL, NULL);
178 gtk_widget_set_size_request(w, DT_PIXEL_APPLY_DPI(500), DT_PIXEL_APPLY_DPI(300));
179 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
180 gtk_box_pack_start(GTK_BOX(vbox), w, TRUE, TRUE, 0);
181 GtkTreeView *view = GTK_TREE_VIEW(gtk_tree_view_new());
182 d->sel_view = view;
183 gtk_container_add(GTK_CONTAINER(w), GTK_WIDGET(view));
184 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"));
185 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(view), GTK_SELECTION_SINGLE);
186 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
187 GtkTreeViewColumn *col = gtk_tree_view_column_new_with_attributes(_("tag"), renderer, "text", 0, NULL);
188 gtk_tree_view_append_column(view, col);
189 renderer = gtk_cell_renderer_text_new();
190 col = gtk_tree_view_column_new_with_attributes(_("type"), renderer, "text", 1, NULL);
191 gtk_tree_view_append_column(view, col);
192 GtkListStore *liststore = gtk_list_store_new(4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
193 GtkTreeModel *model = gtk_tree_model_filter_new(GTK_TREE_MODEL(liststore), NULL);
194 gtk_tree_model_filter_set_visible_column(GTK_TREE_MODEL_FILTER(model), DT_LIB_EXPORT_METADATA_COL_VISIBLE);
195
196 // populate the metadata tag list with exiv2 information
197 for(GList *tag = d->taglist; tag; tag = g_list_next(tag))
198 {
199 GtkTreeIter iter;
200 gtk_list_store_append(liststore, &iter);
201 const char *tagname = tag->data;
202 char *type = g_strstr_len(tagname, -1, ",");
203 if(type)
204 {
205 type[0] = '\0';
206 type++;
207 }
208 gtk_list_store_set(liststore, &iter, DT_LIB_EXPORT_METADATA_COL_XMP, tagname, DT_LIB_EXPORT_METADATA_COL_TYPE, type,
209 DT_LIB_EXPORT_METADATA_COL_VISIBLE, TRUE, -1);
210 if(type)
211 {
212 type--;
213 type[0] = ',';
214 }
215 }
216
217 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(liststore), DT_LIB_EXPORT_METADATA_COL_XMP, GTK_SORT_ASCENDING);
218 gtk_tree_view_set_model(view, model);
219 g_object_unref(model);
220 g_signal_connect(G_OBJECT(view), "button-press-event", G_CALLBACK(click_on_metadata_list), (gpointer)d);
221
222 #ifdef GDK_WINDOWING_QUARTZ
223 dt_osx_disallow_fullscreen(dialog);
224 #endif
225 gtk_widget_show_all(dialog);
226 while (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_YES)
227 {
228 add_selected_metadata(view, d);
229 }
230 gtk_widget_destroy(dialog);
231 }
232
remove_tag_from_list(dt_lib_export_metadata_t * d)233 static void remove_tag_from_list(dt_lib_export_metadata_t *d)
234 {
235 GtkTreeIter iter;
236 GtkTreeModel *model = GTK_TREE_MODEL(d->liststore);
237 GtkTreeSelection *selection = gtk_tree_view_get_selection(d->view);
238 if(gtk_tree_selection_get_selected(selection, &model, &iter))
239 {
240 gtk_list_store_remove(d->liststore, &iter);
241 }
242 }
243
delete_tag_button_clicked(GtkButton * button,dt_lib_export_metadata_t * d)244 static void delete_tag_button_clicked(GtkButton *button, dt_lib_export_metadata_t *d)
245 {
246 remove_tag_from_list(d);
247 }
248
key_press_on_list(GtkWidget * widget,GdkEventKey * event,dt_lib_export_metadata_t * d)249 static gboolean key_press_on_list(GtkWidget *widget, GdkEventKey *event, dt_lib_export_metadata_t *d)
250 {
251 if(event->type == GDK_KEY_PRESS && event->keyval == GDK_KEY_Delete && !event->state)
252 {
253 remove_tag_from_list(d);
254 return TRUE;
255 }
256 return FALSE;
257 }
258
formula_edited(GtkCellRenderer * renderer,gchar * path,gchar * new_text,dt_lib_export_metadata_t * d)259 static void formula_edited(GtkCellRenderer *renderer, gchar *path, gchar *new_text, dt_lib_export_metadata_t *d)
260 {
261 GtkTreeIter iter;
262 if (gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(d->liststore), &iter, path))
263 gtk_list_store_set(d->liststore, &iter, DT_LIB_EXPORT_METADATA_COL_FORMULA, new_text, -1);
264 }
265
dt_lib_export_metadata_configuration_dialog(char * metadata_presets,const gboolean ondisk)266 char *dt_lib_export_metadata_configuration_dialog(char *metadata_presets, const gboolean ondisk)
267 {
268 dt_lib_export_metadata_t *d = calloc(1, sizeof(dt_lib_export_metadata_t));
269
270 GtkWidget *win = dt_ui_main_window(darktable.gui->ui);
271 GtkWidget *dialog = gtk_dialog_new_with_buttons(_("edit metadata exportation"), GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT,
272 _("cancel"), GTK_RESPONSE_NONE, _("save"), GTK_RESPONSE_YES, NULL);
273 d->dialog = dialog;
274 gtk_window_set_default_size(GTK_WINDOW(dialog), 300, -1);
275 GtkWidget *area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
276
277 GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
278 gtk_container_add(GTK_CONTAINER(area), hbox);
279
280 // general info
281 GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
282 gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
283 gtk_container_add(GTK_CONTAINER(hbox), vbox);
284 GtkWidget *label = gtk_label_new(_("general settings"));
285 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
286 GtkWidget *vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
287 gtk_box_pack_start(GTK_BOX(vbox), vbox2, FALSE, TRUE, 0);
288
289 GtkWidget *exiftag = gtk_check_button_new_with_label(_("exif data"));
290 gtk_widget_set_tooltip_text(exiftag, _("export exif metadata"));
291 gtk_box_pack_start(GTK_BOX(vbox2), exiftag, FALSE, TRUE, 0);
292 GtkWidget *dtmetadata = gtk_check_button_new_with_label(_("metadata"));
293 gtk_widget_set_tooltip_text(dtmetadata, _("export dt xmp metadata (from metadata editor module)"));
294 gtk_box_pack_start(GTK_BOX(vbox2), dtmetadata, FALSE, TRUE, 0);
295
296 GtkWidget *calculated;
297 if (!ondisk)
298 {
299 GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
300 gtk_box_pack_start(GTK_BOX(vbox2), box, FALSE, TRUE, 0);
301 GtkWidget *vbox3 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
302 gtk_box_pack_start(GTK_BOX(box), vbox3, FALSE, TRUE, 10);
303 calculated = gtk_check_button_new_with_label(_("only embedded"));
304 gtk_widget_set_tooltip_text(calculated, _("per default the interface sends some (limited) metadata beside the image to remote storage.\n"
305 "to avoid this and let only image embedded dt xmp metadata, check this flag.\n"
306 "if remote storage doesn't understand dt xmp metadata, you can use calculated metadata instead"));
307 gtk_box_pack_start(GTK_BOX(vbox3), calculated, FALSE, TRUE, 0);
308 }
309
310 GtkWidget *geotag = gtk_check_button_new_with_label(_("geo tags"));
311 gtk_widget_set_tooltip_text(geotag, _("export geo tags"));
312 gtk_box_pack_start(GTK_BOX(vbox2), geotag, FALSE, TRUE, 0);
313 GtkWidget *dttag = gtk_check_button_new_with_label(_("tags"));
314 gtk_widget_set_tooltip_text(dttag, _("export tags (to Xmp.dc.Subject)"));
315 gtk_box_pack_start(GTK_BOX(vbox2), dttag, FALSE, TRUE, 0);
316
317 GtkWidget *box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
318 gtk_box_pack_start(GTK_BOX(vbox2), box, FALSE, TRUE, 0);
319 GtkWidget *vbox3 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
320 gtk_box_pack_start(GTK_BOX(box), vbox3, FALSE, TRUE, 10);
321 GtkWidget *private = gtk_check_button_new_with_label(_("private tags"));
322 gtk_widget_set_tooltip_text(private, _("export private tags"));
323 gtk_box_pack_start(GTK_BOX(vbox3), private, FALSE, TRUE, 0);
324 GtkWidget *synonyms = gtk_check_button_new_with_label(_("synonyms"));
325 gtk_widget_set_tooltip_text(synonyms, _("export tags synonyms"));
326 gtk_box_pack_start(GTK_BOX(vbox3), synonyms, FALSE, TRUE, 0);
327 GtkWidget *omithierarchy = gtk_check_button_new_with_label(_("omit hierarchy"));
328 gtk_widget_set_tooltip_text(omithierarchy, _("only the last part of the hierarchical tags is included. can be useful if categories are not used"));
329 gtk_box_pack_start(GTK_BOX(vbox3), omithierarchy, FALSE, TRUE, 0);
330
331 GtkWidget *hierarchical = gtk_check_button_new_with_label(_("hierarchical tags"));
332 gtk_widget_set_tooltip_text(hierarchical, _("export hierarchical tags (to Xmp.lr.Hierarchical Subject)"));
333 gtk_box_pack_start(GTK_BOX(vbox2), hierarchical, FALSE, TRUE, 0);
334 GtkWidget *dthistory = gtk_check_button_new_with_label(_("develop history"));
335 gtk_widget_set_tooltip_text(dthistory, _("export dt development data (recovery purpose in case of loss of database or xmp file)"));
336 gtk_box_pack_start(GTK_BOX(vbox2), dthistory, FALSE, TRUE, 0);
337
338 // specific rules
339 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
340 gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
341 gtk_container_add(GTK_CONTAINER(hbox), vbox);
342 label = gtk_label_new(_("per metadata settings"));
343 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 0);
344
345 GtkWidget *w = gtk_scrolled_window_new(NULL, NULL);
346 gtk_widget_set_size_request(w, DT_PIXEL_APPLY_DPI(450), DT_PIXEL_APPLY_DPI(100));
347 gtk_widget_set_hexpand(w, TRUE);
348 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
349 gtk_box_pack_start(GTK_BOX(vbox), w, TRUE, TRUE, 0);
350 GtkTreeView *view = GTK_TREE_VIEW(gtk_tree_view_new());
351 d->view = view;
352 gtk_container_add(GTK_CONTAINER(w), GTK_WIDGET(view));
353 gtk_widget_set_tooltip_text(GTK_WIDGET(view), _("list of available tags"));
354 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(view), GTK_SELECTION_SINGLE);
355 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
356 GtkTreeViewColumn *col = gtk_tree_view_column_new_with_attributes(_("redefined tag"), renderer, "text", 0, NULL);
357 gtk_tree_view_append_column(view, col);
358 renderer = gtk_cell_renderer_text_new();
359 g_object_set(renderer, "editable", TRUE, NULL);
360 g_signal_connect(G_OBJECT(renderer), "edited", G_CALLBACK(formula_edited), (gpointer)d);
361 col = gtk_tree_view_column_new_with_attributes(_("formula"), renderer, "text", 2, NULL);
362 gtk_tree_view_append_column(view, col);
363 char *tooltip_text = dt_gtkentry_build_completion_tooltip_text(
364 _("list of calculated metadata\n"
365 "if formula is empty, the corresponding metadata is removed from exported file,\n"
366 "if formula is \'=\', the exif metadata is exported even if exif data are disabled\n"
367 "otherwise the corresponding metadata is calculated and added to exported file\n"
368 "click on formula cell to edit. recognized variables:\n"),
369 dt_gtkentry_get_default_path_compl_list());
370 gtk_widget_set_tooltip_text(GTK_WIDGET(view), tooltip_text);
371 g_free(tooltip_text);
372 g_signal_connect(G_OBJECT(view), "key_press_event", G_CALLBACK(key_press_on_list), (gpointer)d);
373
374 GtkListStore *liststore = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
375 d->liststore = liststore;
376 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(liststore), DT_LIB_EXPORT_METADATA_COL_XMP, GTK_SORT_ASCENDING);
377 gtk_tree_view_set_model(view, GTK_TREE_MODEL(liststore));
378 g_object_unref(liststore);
379 d->taglist = (GList *)dt_exif_get_exiv2_taglist();
380 GList *list = dt_util_str_to_glist("\1", metadata_presets);
381 int32_t flags = 0;
382 if (list)
383 {
384 char *flags_hexa = list->data;
385 flags = strtol(flags_hexa, NULL, 16);
386 list = g_list_remove(list, flags_hexa);
387 g_free(flags_hexa);
388 if (list)
389 {
390 for (GList *tags = list; tags; tags = g_list_next(tags))
391 {
392 GtkTreeIter iter;
393 const char *tagname = (char *)tags->data;
394 tags = g_list_next(tags);
395 if (!tags) break;
396 const char *formula = (char *)tags->data;
397 gtk_list_store_append(d->liststore, &iter);
398 gtk_list_store_set(d->liststore, &iter, DT_LIB_EXPORT_METADATA_COL_XMP, tagname,
399 DT_LIB_EXPORT_METADATA_COL_FORMULA, formula, -1);
400 }
401 }
402 }
403 g_list_free_full(list, g_free);
404
405 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(exiftag), flags & DT_META_EXIF);
406 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dtmetadata), flags & DT_META_METADATA);
407 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(geotag), flags & DT_META_GEOTAG);
408 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dttag), flags & DT_META_TAG);
409 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(private), flags & DT_META_PRIVATE_TAG);
410 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(synonyms), flags & DT_META_SYNONYMS_TAG);
411 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(hierarchical), flags & DT_META_HIERARCHICAL_TAG);
412 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dthistory), flags & DT_META_DT_HISTORY);
413 if (!ondisk)
414 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(calculated), flags & DT_META_CALCULATED);
415 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(omithierarchy), flags & DT_META_OMIT_HIERARCHY);
416
417 box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
418 gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, TRUE, 0);
419
420 GtkWidget *button = dtgtk_button_new(dtgtk_cairo_paint_plus_simple, CPF_STYLE_FLAT, NULL);
421 gtk_widget_set_tooltip_text(button, _("add an output metadata tag"));
422 gtk_box_pack_end(GTK_BOX(box), button, FALSE, TRUE, 0);
423 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(add_tag_button_clicked), (gpointer)d);
424
425 button = dtgtk_button_new(dtgtk_cairo_paint_minus_simple, CPF_STYLE_FLAT, NULL);
426 gtk_widget_set_tooltip_text(button, _("delete metadata tag"));
427 gtk_box_pack_end(GTK_BOX(box), button, FALSE, TRUE, 0);
428 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(delete_tag_button_clicked), (gpointer)d);
429
430 #ifdef GDK_WINDOWING_QUARTZ
431 dt_osx_disallow_fullscreen(dialog);
432 #endif
433 gtk_widget_show_all(dialog);
434
435 char *newlist = metadata_presets;
436 if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_YES)
437 {
438 const gint newflags = (
439 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(exiftag)) ? DT_META_EXIF : 0) |
440 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dtmetadata)) ? DT_META_METADATA : 0) |
441 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(geotag)) ? DT_META_GEOTAG : 0) |
442 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dttag)) ? DT_META_TAG : 0) |
443 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(private)) ? DT_META_PRIVATE_TAG : 0) |
444 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(synonyms)) ? DT_META_SYNONYMS_TAG : 0) |
445 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hierarchical)) ? DT_META_HIERARCHICAL_TAG : 0) |
446 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dthistory)) ? DT_META_DT_HISTORY : 0) |
447 (!ondisk ? (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(calculated)) ? DT_META_CALCULATED : 0) : 0) |
448 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(omithierarchy)) ? DT_META_OMIT_HIERARCHY : 0)
449 );
450
451 newlist = dt_util_dstrcat(NULL,"%x", newflags);
452 GtkTreeIter iter;
453 gboolean valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(d->liststore), &iter);
454 while(valid)
455 {
456 char *tagname, *formula;
457 gtk_tree_model_get(GTK_TREE_MODEL(d->liststore), &iter, DT_LIB_EXPORT_METADATA_COL_XMP, &tagname,
458 DT_LIB_EXPORT_METADATA_COL_FORMULA, &formula, -1);
459 // metadata presets are stored into a single string with '\1' as a separator
460 newlist = dt_util_dstrcat(newlist,"\1%s\1%s", tagname, formula);
461 g_free(tagname);
462 g_free(formula);
463 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL(d->liststore), &iter);
464 }
465 g_free(metadata_presets);
466 dt_lib_export_metadata_set_conf(newlist);
467 }
468 gtk_widget_destroy(dialog);
469 free(d);
470 return newlist;
471 }
472
473 // modelines: These editor modelines have been set for all relevant files by tools/update_modelines.sh
474 // vim: shiftwidth=2 expandtab tabstop=2 cindent
475 // kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
476