67#ifdef GDK_WINDOWING_QUARTZ
70#include <gdk/gdkkeysyms.h>
73#define FLOATING_ENTRY_WIDTH DT_PIXEL_APPLY_DPI(150)
92 gboolean tree_flag, suggestion_flag, sort_count_flag, hide_path_flag,
dttags_flag;
138 GdkModifierType mods, gpointer user_data);
140 GdkModifierType mods, gpointer user_data);
149 static const char *
v[] = {
"lighttable",
"map",
"studio_capture", NULL};
162 const gboolean user_tag = !g_str_has_prefix(path,
"darktable|") || g_str_has_prefix(path,
"darktable|style|");
169 GtkTreeSelection *selection = gtk_tree_view_get_selection(
view);
170 gtk_tree_selection_unselect_all(selection);
178 GList *rows = gtk_tree_selection_get_selected_rows(selection,
model);
179 gboolean found =
FALSE;
182 found = gtk_tree_model_get_iter(*
model, iter, (GtkTreePath *)rows->data);
183 g_list_free_full(rows, (GDestroyNotify)gtk_tree_path_free);
199 GtkTreeIter parent, child = *iter;
200 while(gtk_tree_model_iter_parent(
model, &parent, &child))
204 gtk_tree_store_set(GTK_TREE_STORE(
model), &parent,
214 gchar *tagname = NULL;
215 gchar *synonyms = NULL;
221 if(synonyms && synonyms[0]) tagname =
dt_util_dstrcat(tagname,
", %s", synonyms);
222 gchar *haystack = g_utf8_strdown(tagname, -1);
223 gchar *needle = g_utf8_strdown(
d->keyword, -1);
224 visible = (g_strrstr(haystack, needle) != NULL);
240 GtkTreeIter parent, child = *iter;
245 while(gtk_tree_model_iter_parent(
model, &parent, &child))
257 if(
d->attached_tree_flag)
260 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
d->attached_treestore),
DT_TAG_SORT_PATH_ID, GTK_SORT_ASCENDING);
263 if(force &&
d->sort_count_flag)
266 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
d->attached_liststore),
DT_TAG_SORT_NAME_ID, GTK_SORT_ASCENDING);
269 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
d->attached_liststore), sort, GTK_SORT_ASCENDING);
277 if(force &&
d->sort_count_flag)
280 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
d->dictionary_liststore),
DT_TAG_SORT_NAME_ID, GTK_SORT_ASCENDING);
283 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
d->dictionary_liststore), sort, GTK_SORT_ASCENDING);
286 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
d->dictionary_treestore),
DT_TAG_SORT_PATH_ID, GTK_SORT_ASCENDING);
291 const char *tagname,
const gboolean needle)
293 gboolean found =
FALSE;
301 gchar *haystack = g_utf8_strdown(path, -1);
302 found = g_strstr_len(haystack, strlen(haystack), tagname) != NULL;
306 found = !g_strcmp0(tagname, path);
308 if(found)
return found;
309 GtkTreeIter child, parent = *iter;
310 if(gtk_tree_model_iter_children(
model, &child, &parent))
319 }
while(gtk_tree_model_iter_next(
model, iter));
325 GtkTreeModel *
model = gtk_tree_view_get_model(
view);
326 GtkTreePath *path = gtk_tree_model_get_path(
model, iter);
327 gtk_tree_view_expand_to_path(
view, path);
328 gtk_tree_view_scroll_to_cell(
view, path, NULL,
TRUE, 0.5, 0.5);
329 gtk_tree_path_free(path);
332 GtkTreeSelection *selection = gtk_tree_view_get_selection(
view);
333 gtk_tree_selection_select_iter(selection, iter);
339 const gboolean needle,
const gboolean select)
343 char *lt = g_strdup(tagname);
344 char *
t = g_strstrip(lt);
346 GtkTreeModel *
model = gtk_tree_view_get_model(
view);
347 if(gtk_tree_model_get_iter_first(
model, &iter))
358 gchar *needle = g_utf8_strdown(keyword, -1);
368 GtkTreeModel *
model = gtk_tree_view_get_model(
view);
369 gboolean valid = gtk_tree_model_get_iter_first(
model, &iter);
371 for(; valid &&
i < index -1;
i++)
372 valid = gtk_tree_model_iter_next(
model, &iter);
373 for(; valid; valid = gtk_tree_model_iter_previous(
model, &iter))
387 GtkTreeModel *
model = gtk_tree_view_get_model(
view);
388 gboolean valid = gtk_tree_model_get_iter_first(
model, &iter);
390 for(; valid &&
i < index;
i++)
391 valid = gtk_tree_model_iter_next(
model, &iter);
392 for(; valid; valid = gtk_tree_model_iter_next(
model, &iter))
414 gboolean no_sel =
FALSE;
417 const gboolean build_tree = which ?
d->tree_flag :
d->attached_tree_flag;
424 view =
d->attached_view;
426 store = build_tree ? GTK_TREE_MODEL(
d->attached_treestore) : GTK_TREE_MODEL(
d->attached_liststore);
431 if(!
d->tree_flag &&
d->suggestion_flag)
435 view =
d->dictionary_view;
436 model = gtk_tree_view_get_model(GTK_TREE_VIEW(
view));
438 store = GTK_TREE_MODEL(
d->dictionary_treestore);
440 store = GTK_TREE_MODEL(
d->dictionary_liststore);
443 gtk_tree_view_set_model(GTK_TREE_VIEW(
view), NULL);
445 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
store), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
448 gtk_tree_store_clear(GTK_TREE_STORE(
store));
450 char **last_tokens = NULL;
451 int last_tokens_length = 0;
452 GtkTreeIter last_parent = { 0 };
455 for(GList *taglist = tags; taglist; taglist = g_list_next(taglist))
457 const gchar *tag = ((
dt_tag_t *)taglist->data)->tag;
460 tokens = g_strsplit(tag,
"|", -1);
464 GtkTreeIter parent = last_parent;
465 const int tokens_length = g_strv_length(tokens);
466 int common_length = 0;
469 while(tokens[common_length] && last_tokens[common_length] &&
470 !g_strcmp0(tokens[common_length], last_tokens[common_length]))
476 for(
int i = common_length;
i < last_tokens_length;
i++)
478 gtk_tree_model_iter_parent(GTK_TREE_MODEL(
store), &parent, &last_parent);
479 last_parent = parent;
485 for(
int i = 0;
i < common_length;
i++)
488 for(
char **token = &tokens[common_length]; *token; token++)
491 gchar *pth2 = g_strdup(pth);
492 pth2[strlen(pth2) - 1] =
'\0';
493 gtk_tree_store_insert(GTK_TREE_STORE(
store), &iter, common_length > 0 ? &parent : NULL, -1);
494 gtk_tree_store_set(GTK_TREE_STORE(
store), &iter,
497 ((
dt_tag_t *)taglist->data)->id : 0,
500 ((
dt_tag_t *)taglist->data)->count : 0,
506 if(((
dt_tag_t *)taglist->data)->select)
515 if(last_tokens) g_strfreev(last_tokens);
516 last_tokens = tokens;
517 last_parent = parent;
518 last_tokens_length = tokens_length;
521 g_strfreev(last_tokens);
523 if(which &&
d->keyword[0])
527 gtk_tree_view_set_model(GTK_TREE_VIEW(
view),
model);
529 else gtk_tree_view_set_model(GTK_TREE_VIEW(
view),
model);
530 g_object_unref(
model);
534 gtk_list_store_clear(GTK_LIST_STORE(
store));
535 if(count > 0 && tags)
537 for (GList *tag = tags; tag; tag = g_list_next(tag))
539 const char *subtag = g_strrstr(((
dt_tag_t *)tag->data)->tag,
"|");
540 gtk_list_store_append(GTK_LIST_STORE(
store), &iter);
541 gtk_list_store_set(GTK_LIST_STORE(
store), &iter,
554 if(which &&
d->keyword[0])
558 gtk_tree_view_set_model(GTK_TREE_VIEW(
view),
model);
559 g_object_unref(
model);
566 if(!which &&
d->attached_tree_flag)
567 gtk_tree_view_expand_all(GTK_TREE_VIEW(
d->attached_view));
573 GtkTreeModel *
model, GtkTreeIter *iter,
574 gpointer data, gboolean dictionary_view)
585 gtk_tree_model_get(
model, iter,
592 const gboolean hide = dictionary_view ? (
d->tree_flag ?
TRUE :
d->hide_path_flag) :
d->attached_tree_flag;
594 if((dictionary_view && !count) || (!dictionary_view && count <= 1))
596 coltext = g_markup_printf_escaped(istag ?
"%s" :
"<i>%s</i>", hide ?
name : path);
600 coltext = g_markup_printf_escaped(istag ?
"%s (%d)" :
"<i>%s</i> (%d)", hide ?
name : path, count);
602 g_object_set(renderer,
"markup", coltext, NULL);
609 GtkTreeModel *
model, GtkTreeIter *iter,
616 GtkTreeModel *
model, GtkTreeIter *iter,
649 d->collection[0] =
'\0';
661 if(!
d->collection[0])
683 GtkTreeIter child, parent = *iter;
684 if(gtk_tree_model_iter_children(
model, &child, &parent))
690 }
while (gtk_tree_model_iter_next(
model, iter));
697 GtkTreeIter child, parent = *iter;
706 if(gtk_tree_model_iter_children(
model, &child, &parent))
708 }
while (!root && gtk_tree_model_iter_next(
model, &parent));
714 GtkTreeIter child, parent = *iter;
717 if(gtk_tree_model_iter_children(
model, &child, &parent))
723 gtk_tree_store_set(GTK_TREE_STORE(
model), &parent,
728 }
while (!root && gtk_tree_model_iter_next(
model, &parent));
734 GtkTreeIter child, parent = *iter;
737 if(GTK_IS_TREE_STORE(
model))
739 gtk_tree_store_set(GTK_TREE_STORE(
model), &parent,
741 if(gtk_tree_model_iter_children(
model, &child, &parent))
746 gtk_list_store_set(GTK_LIST_STORE(
model), &parent,
749 }
while (!root && gtk_tree_model_iter_next(
model, &parent));
756 GtkTreeIter child = *iter;
757 while (gtk_tree_model_iter_parent(
model, parent, &child))
789 if(gtk_tree_model_get_iter_first(
model, &parent))
803 if(gtk_tree_model_get_iter_first(
model, &parent))
806 for (GList *tag = tags; tag; tag = g_list_next(tag))
808 GtkTreeIter iter = parent;
811 if(GTK_IS_TREE_STORE(
model))
813 gtk_tree_store_set(GTK_TREE_STORE(
model), &iter,
819 gtk_list_store_set(GTK_LIST_STORE(
model), &iter,
835 GtkTreeIter child, parent = *iter;
836 gboolean valid =
TRUE;
839 if(gtk_tree_model_iter_children(
model, &child, &parent))
841 GtkTreeIter tobedel = parent;
842 valid = gtk_tree_model_iter_next(
model, &parent);
845 gtk_tree_store_set(GTK_TREE_STORE(
model), &tobedel,
858 gtk_tree_store_remove(GTK_TREE_STORE(
model), &tobedel);
859 }
while (!root && valid);
866 guint pathlen = strlen(path);
867 gboolean valid = gtk_tree_model_get_iter_first(
model, &child);
872 GtkTreeIter tobedel = child;
873 valid = gtk_tree_model_iter_next (
model, &child);
874 if(strlen(path2) >= pathlen)
876 char letter = path2[pathlen];
877 path2[pathlen] =
'\0';
878 if(g_strcmp0(path, path2) == 0)
880 path2[pathlen] = letter;
881 gtk_list_store_remove(GTK_LIST_STORE(
model), &tobedel);
894 if(!
d->tree_flag &&
d->suggestion_flag)
900 : GTK_TREE_MODEL(
d->dictionary_liststore));
908 const gchar *beg = g_strrstr(gtk_entry_get_text(
d->dict_entry),
",");
911 beg = gtk_entry_get_text(
d->dict_entry);
914 if(*beg ==
',') beg++;
915 if(*beg ==
' ') beg++;
917 g_strlcpy(
d->keyword, beg,
sizeof(
d->keyword));
926 if(g_str_has_prefix(tagname, oldtagname))
928 if(strlen(tagname) == strlen(oldtagname))
933 char *subtag = g_strrstr(to->
newtagname,
"|");
934 subtag = (!subtag) ? newtagname : subtag + 1;
944 else if(strlen(tagname) > strlen(oldtagname) && tagname[strlen(oldtagname)] ==
'|')
947 char *newpath = g_strconcat(newtagname, &tagname[strlen(oldtagname)] , NULL);
968 GtkTreeModel *
model = gtk_tree_view_get_model(
view);
969 GtkTreeModel *
store = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(
model));
971 if(gtk_tree_model_get_iter_first(
store, &iter))
977 gtk_tree_store_set(GTK_TREE_STORE(
store), &iter,
984 gtk_list_store_set(GTK_LIST_STORE(
store), &iter,
1006 for(GList *taglist = tags; taglist; taglist = g_list_next(taglist))
1013 *
size = strlen(params);
1014 params[*
size-1]=
'\0';
1024 const char *buf = (
char *)params;
1027 gchar **tokens = g_strsplit(buf,
",", 0);
1031 gchar **entry = tokens;
1034 const guint tagid = strtoul(*entry, NULL, 0);
1035 tags = g_list_prepend(tags, GINT_TO_POINTER(tagid));
1043 gboolean change =
FALSE;
1044 for(GList *tag = tags; tag; tag = g_list_next(tag))
1067 GtkTreeModel *
model = NULL;
1068 GList *rows = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(
view), &
model);
1069 for(GList *
r = rows;
r;
r = g_list_next(
r))
1072 if(gtk_tree_model_get_iter(
model, &iter, (GtkTreePath *)
r->data))
1076 if(tagid > 0) ids = g_list_prepend(ids, GUINT_TO_POINTER(tagid));
1079 g_list_free_full(rows, (GDestroyNotify)gtk_tree_path_free);
1090 gboolean changed =
FALSE;
1091 for(GList *
t = tagids;
t;
t = g_list_next(
t))
1093 const guint tagid = GPOINTER_TO_UINT(
t->data);
1094 if(tagid <= 0)
continue;
1100 g_list_free(affected);
1116 GtkTreeModel *
model = gtk_tree_view_get_model(GTK_TREE_VIEW(
d->attached_view));
1117 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
d->attached_view));
1122 if(tagid <= 0)
return;
1133 model = gtk_tree_view_get_model(GTK_TREE_VIEW(
d->dictionary_view));
1134 if(gtk_tree_model_get_iter_first(
model, &iter))
1138 GtkTreeIter store_iter;
1139 GtkTreeModel *
store = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(
model));
1140 gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(
model),
1141 &store_iter, &iter);
1165 g_list_free(tagids);
1172 GtkTreeModel *
model = gtk_tree_view_get_model(GTK_TREE_VIEW(
d->attached_view));
1174 GList *tagids = NULL;
1175 if(gtk_tree_model_get_iter_first(
model, &iter))
1182 if(tagid > 0) tagids = g_list_prepend(tagids, GUINT_TO_POINTER(tagid));
1183 }
while(gtk_tree_model_iter_next(
model, &iter));
1186 g_list_free(tagids);
1193 menu = gtk_menu_new();
1196 GtkTreeModel *
model = gtk_tree_view_get_model(GTK_TREE_VIEW(
d->attached_view));
1197 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
d->attached_view));
1198 const gint sel_cnt = gtk_tree_selection_count_selected_rows(selection);
1207 menuitem = gtk_menu_item_new_with_label(_(
"attach tag to all"));
1209 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1210 menuitem = gtk_separator_menu_item_new();
1211 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1217 menuitem = gtk_menu_item_new_with_label(sel_cnt > 1 ? _(
"detach tags") : _(
"detach tag"));
1218 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1222 menuitem = gtk_menu_item_new_with_label(_(
"detach all tags"));
1223 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
1226 gtk_widget_show_all(GTK_WIDGET(menu));
1228 gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent *)event);
1233 const gchar *path_str, GdkRectangle *background,
1234 GdkRectangle *cell_area, GtkCellRendererState
flags,
1238 GtkTreeModel *
model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
1239 GtkTreePath *path = gtk_tree_path_new_from_string(path_str);
1241 if(path && gtk_tree_model_get_iter(
model, &iter, path))
1247 GList *one = g_list_prepend(NULL, GUINT_TO_POINTER(tagid));
1252 if(path) gtk_tree_path_free(path);
1259 GtkTreeModel *
model, GtkTreeIter *iter, gpointer data)
1263 g_object_set(renderer,
"icon-name",
"edit-delete-symbolic",
1272 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
view));
1273 GtkTreeModel *
model = gtk_tree_view_get_model(GTK_TREE_VIEW(
d->attached_view));
1274 GtkTreePath *path = NULL;
1275 const gboolean on_row =
1276 gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(
view), (gint)event->x, (gint)event->y, &path, NULL, NULL, NULL);
1278 gboolean valid_tag =
FALSE;
1280 if(on_row && gtk_tree_model_get_iter(
model, &iter, path))
1284 if(event->type == GDK_BUTTON_PRESS && event->button == 3)
1289 if(!gtk_tree_selection_path_is_selected(selection, path))
1291 gtk_tree_selection_unselect_all(selection);
1292 gtk_tree_selection_select_path(selection, path);
1296 gtk_tree_selection_unselect_all(selection);
1298 if(path) gtk_tree_path_free(path);
1303 if(event->type == GDK_2BUTTON_PRESS && event->button == 1 && valid_tag)
1309 GList *one = g_list_prepend(NULL, GUINT_TO_POINTER(tagid));
1313 if(path) gtk_tree_path_free(path);
1318 if(event->type == GDK_BUTTON_PRESS && event->button == 1 && on_row && !valid_tag)
1320 gtk_tree_path_free(path);
1324 if(path) gtk_tree_path_free(path);
1334 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
view));
1335 if(
key == GDK_KEY_Delete && gtk_tree_selection_count_selected_rows(selection) > 0)
1339 g_list_free(tagids);
1342 if(
key == GDK_KEY_Tab)
1344 gtk_tree_selection_unselect_all(selection);
1345 gtk_widget_grab_focus(GTK_WIDGET(
d->entry));
1348 else if(
key == GDK_KEY_ISO_Left_Tab)
1350 gtk_tree_selection_unselect_all(selection);
1360 const gchar *tag = gtk_entry_get_text(src);
1373 gtk_entry_set_text(src,
"");
1381 char *tagname = strrchr(
d->last_tag,
',');
1384 tagname ? tagname + 1 :
d->last_tag,
FALSE,
TRUE);
1400 case GDK_KEY_Return:
1403 case GDK_KEY_Escape:
1406 case GDK_KEY_ISO_Left_Tab:
1410 gtk_entry_set_text(GTK_ENTRY(entry),
"");
1411 gtk_widget_grab_focus(GTK_WIDGET(
d->attached_view));
1426 GtkTreeModel *
model = gtk_tree_view_get_model(
d->dictionary_view);
1427 GtkTreeModel *
store = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(
model));
1429 if(
d->tree_flag &&
d->keyword[0])
1440 int res = GTK_RESPONSE_YES;
1447 GtkTreeModel *
model = NULL;
1448 GtkTreeView *
view =
d->dictionary_view;
1449 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
view));
1458 if(tag_count == 0)
return;
1461 GtkWidget *dialog = gtk_dialog_new_with_buttons( _(
"delete node?"), GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT,
1462 _(
"cancel"), GTK_RESPONSE_NONE, _(
"delete"), GTK_RESPONSE_YES, NULL);
1463 gtk_window_set_default_size(GTK_WINDOW(dialog), 300, -1);
1464 GtkWidget *area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1466 gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
1467 gtk_container_add(GTK_CONTAINER(area), vbox);
1468 text = g_strdup_printf(_(
"selected: %s"), tagname);
1469 label = gtk_label_new(text);
1470 gtk_box_pack_start(GTK_BOX(vbox), label,
FALSE,
TRUE, 0);
1474 gtk_box_pack_start(GTK_BOX(vbox), box,
FALSE,
TRUE, 0);
1475 text = g_strdup_printf(ngettext(
"<u>%d</u> tag will be deleted",
"<u>%d</u> tags will be deleted", tag_count), tag_count);
1476 label = gtk_label_new(NULL);
1477 gtk_label_set_markup(GTK_LABEL(label), text);
1478 gtk_box_pack_start(GTK_BOX(box), label,
FALSE,
TRUE, 0);
1480 text = g_strdup_printf(ngettext(
"<u>%d</u> image will be updated",
"<u>%d</u> images will be updated", img_count), img_count);
1481 label = gtk_label_new(NULL);
1482 gtk_label_set_markup(GTK_LABEL(label), text);
1483 gtk_box_pack_start(GTK_BOX(box), label,
FALSE,
TRUE, 0);
1486#ifdef GDK_WINDOWING_QUARTZ
1489 gtk_widget_show_all(dialog);
1491 res = gtk_dialog_run(GTK_DIALOG(dialog));
1492 GtkWindow *dialog_parent = gtk_window_get_transient_for(GTK_WINDOW(dialog));
1493 gtk_widget_destroy(dialog);
1495 if(res != GTK_RESPONSE_YES)
1501 GList *tag_family = NULL;
1502 GList *tagged_images = NULL;
1510 GtkTreeIter store_iter;
1511 GtkTreeModel *
store = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(
model));
1512 gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(
model),
1513 &store_iter, &iter);
1519 g_list_free(tagged_images);
1520 tagged_images = NULL;
1536 GtkTreeModel *
model = NULL;
1537 GtkTreeView *
view =
d->dictionary_view;
1538 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
view));
1545 GtkWidget *dialog = gtk_dialog_new_with_buttons(_(
"create tag"), GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT,
1546 _(
"cancel"), GTK_RESPONSE_NONE, _(
"save"), GTK_RESPONSE_YES, NULL);
1547 gtk_window_set_default_size(GTK_WINDOW(dialog), 300, -1);
1548 GtkWidget *area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1550 gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
1551 gtk_container_add(GTK_CONTAINER(area), vbox);
1554 gtk_box_pack_start(GTK_BOX(vbox), box,
FALSE,
TRUE, 0);
1555 label = gtk_label_new(_(
"name: "));
1556 gtk_box_pack_start(GTK_BOX(box), label,
FALSE,
TRUE, 0);
1559 gtk_box_pack_end(GTK_BOX(box), entry,
TRUE,
TRUE, 0);
1564 GtkTextBuffer *buffer = NULL;
1566 gtk_box_pack_start(GTK_BOX(vbox), vbox2,
FALSE,
TRUE, 0);
1568 text = g_strdup_printf(_(
"add to: \"%s\" "), path);
1569 parent = gtk_check_button_new_with_label(text);
1570 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(parent),
TRUE);
1571 gtk_box_pack_end(GTK_BOX(vbox2), parent,
FALSE,
TRUE, 0);
1574 category = gtk_check_button_new_with_label(_(
"category"));
1575 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(category),
FALSE);
1576 gtk_box_pack_end(GTK_BOX(vbox2), category,
FALSE,
TRUE, 0);
1577 private = gtk_check_button_new_with_label(_(
"private"));
1578 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
private),
FALSE);
1579 gtk_box_pack_end(GTK_BOX(vbox2),
private,
FALSE,
TRUE, 0);
1582 gtk_box_pack_end(GTK_BOX(vbox), box,
TRUE,
TRUE, 0);
1583 label = gtk_label_new(_(
"synonyms: "));
1584 gtk_box_pack_start(GTK_BOX(box), label,
FALSE,
TRUE, 0);
1585 GtkWidget *synonyms = gtk_text_view_new();
1588 gtk_box_pack_end(GTK_BOX(box), synonyms,
TRUE,
TRUE, 0);
1589 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(synonyms), GTK_WRAP_WORD);
1590 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(synonyms));
1592#ifdef GDK_WINDOWING_QUARTZ
1595 gtk_widget_show_all(dialog);
1597 if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_YES)
1599 const char *newtag = gtk_entry_get_text(GTK_ENTRY(entry));
1600 char *message = NULL;
1602 message = _(
"empty tag is not allowed, aborting");
1603 char *new_tagname = NULL;
1604 const gboolean root = !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(parent));
1607 new_tagname = g_strdup(path);
1610 else new_tagname = g_strdup(newtag);
1613 message = _(
"tag name already exists. aborting.");
1616 GtkWidget *warning_dialog = gtk_message_dialog_new(GTK_WINDOW(dialog), GTK_DIALOG_MODAL,
1617 GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
"%s", message);
1618 gtk_dialog_run(GTK_DIALOG(warning_dialog));
1619 gtk_widget_destroy(warning_dialog);
1620 GtkWindow *dialog_parent = gtk_window_get_transient_for(GTK_WINDOW(dialog));
1621 gtk_widget_destroy(dialog);
1626 guint new_tagid = 0;
1629 const gint new_flags = ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(category)) ?
DT_TF_CATEGORY : 0) |
1630 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
private)) ?
DT_TF_PRIVATE : 0));
1632 GtkTextIter start, end;
1633 gtk_text_buffer_get_start_iter(buffer, &start);
1634 gtk_text_buffer_get_end_iter(buffer, &end);
1635 gchar *new_synonyms_list = gtk_text_buffer_get_text(buffer, &start, &end,
FALSE);
1636 if(new_tagid && new_synonyms_list && new_synonyms_list[0])
1645 GtkWindow *dialog_parent = gtk_window_get_transient_for(GTK_WINDOW(dialog));
1646 gtk_widget_destroy(dialog);
1657 char *synonyms_list;
1662 GtkTreeModel *
model = NULL;
1663 GtkTreeView *
view =
d->dictionary_view;
1664 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
view));
1669 char *subtag = g_strrstr(tagname,
"|");
1670 if(subtag) subtag = subtag + 1;
1682 GtkWidget *dialog = gtk_dialog_new_with_buttons(_(
"edit"), GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT,
1683 _(
"cancel"), GTK_RESPONSE_NONE, _(
"save"), GTK_RESPONSE_YES, NULL);
1684 gtk_window_set_default_size(GTK_WINDOW(dialog), 300, -1);
1685 GtkWidget *area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1687 gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
1688 gtk_container_add(GTK_CONTAINER(area), vbox);
1689 text = g_strdup_printf(_(
"selected: %s"), tagname);
1690 label = gtk_label_new(text);
1691 gtk_box_pack_start(GTK_BOX(vbox), label,
FALSE,
TRUE, 0);
1695 gtk_box_pack_start(GTK_BOX(vbox), box,
FALSE,
TRUE, 0);
1696 text = g_strdup_printf(ngettext(
"<u>%d</u> tag will be updated",
"<u>%d</u> tags will be updated", tag_count), tag_count);
1697 label = gtk_label_new(NULL);
1698 gtk_label_set_markup(GTK_LABEL(label), text);
1699 gtk_box_pack_start(GTK_BOX(box), label,
FALSE,
TRUE, 0);
1701 text = g_strdup_printf(ngettext(
"<u>%d</u> image will be updated",
"<u>%d</u> images will be updated", img_count), img_count);
1702 label = gtk_label_new(NULL);
1703 gtk_label_set_markup(GTK_LABEL(label), text);
1704 gtk_box_pack_start(GTK_BOX(box), label,
FALSE,
TRUE, 0);
1708 gtk_box_pack_start(GTK_BOX(vbox), box,
FALSE,
TRUE, 0);
1709 label = gtk_label_new(_(
"name: "));
1710 gtk_box_pack_start(GTK_BOX(box), label,
FALSE,
TRUE, 0);
1713 gtk_entry_set_text(GTK_ENTRY(entry), subtag ? subtag : tagname);
1714 gtk_box_pack_end(GTK_BOX(box), entry,
TRUE,
TRUE, 0);
1718 GtkWidget *
private = gtk_check_button_new_with_label(_(
"private"));
1719 GtkTextBuffer *buffer = NULL;
1724 gtk_box_pack_start(GTK_BOX(vbox), vbox2,
FALSE,
TRUE, 0);
1726 category = gtk_check_button_new_with_label(_(
"category"));
1728 gtk_box_pack_end(GTK_BOX(vbox2), category,
FALSE,
TRUE, 0);
1730 gtk_box_pack_end(GTK_BOX(vbox2),
private,
FALSE,
TRUE, 0);
1733 gtk_box_pack_end(GTK_BOX(vbox), box,
TRUE,
TRUE, 0);
1734 label = gtk_label_new(_(
"synonyms: "));
1735 gtk_box_pack_start(GTK_BOX(box), label,
FALSE,
TRUE, 0);
1736 GtkWidget *synonyms = gtk_text_view_new();
1739 gtk_box_pack_end(GTK_BOX(box), synonyms,
TRUE,
TRUE, 0);
1740 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(synonyms), GTK_WRAP_WORD);
1741 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(synonyms));
1742 if(synonyms_list) gtk_text_buffer_set_text(buffer, synonyms_list, -1);
1745#ifdef GDK_WINDOWING_QUARTZ
1748 gtk_widget_show_all(dialog);
1750 if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_YES)
1752 const char *newtag = gtk_entry_get_text(GTK_ENTRY(entry));
1753 if(g_strcmp0(newtag, subtag ? subtag : tagname) != 0)
1756 char *message = NULL;
1758 message = _(
"empty tag is not allowed, aborting");
1759 if(strchr(newtag,
'|') != 0)
1760 message = _(
"'|' character is not allowed for renaming tag.\nto modify the hierarchy use rename path instead. Aborting.");
1763 GtkWidget *warning_dialog = gtk_message_dialog_new(GTK_WINDOW(dialog), GTK_DIALOG_MODAL,
1764 GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
"%s", message);
1765 gtk_dialog_run(GTK_DIALOG(warning_dialog));
1766 gtk_widget_destroy(warning_dialog);
1767 GtkWindow *dialog_parent = gtk_window_get_transient_for(GTK_WINDOW(dialog));
1768 gtk_widget_destroy(dialog);
1774 GList *tag_family = NULL;
1775 GList *tagged_images = NULL;
1778 const int tagname_len = strlen(tagname);
1779 char *new_prefix_tag;
1782 const int subtag_len = strlen(subtag);
1783 const char letter = tagname[tagname_len - subtag_len];
1784 tagname[tagname_len - subtag_len] =
'\0';
1785 new_prefix_tag = g_strconcat(tagname, newtag, NULL);
1786 tagname[tagname_len - subtag_len] = letter;
1789 new_prefix_tag = (
char *)newtag;
1792 gboolean tagname_exists =
FALSE;
1793 for (GList *taglist = tag_family; taglist && !tagname_exists; taglist = g_list_next(taglist))
1795 char *new_tagname = g_strconcat(new_prefix_tag, &((
dt_tag_t *)taglist->data)->tag[tagname_len], NULL);
1799 GtkWidget *warning_dialog = gtk_message_dialog_new(GTK_WINDOW(dialog), GTK_DIALOG_MODAL,
1800 GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
1801 _(
"at least one new tag name (%s) already exists, aborting"), new_tagname);
1802 gtk_dialog_run(GTK_DIALOG(warning_dialog));
1803 gtk_widget_destroy(warning_dialog);
1809 GtkWindow *dialog_parent = gtk_window_get_transient_for(GTK_WINDOW(dialog));
1810 gtk_widget_destroy(dialog);
1819 for (GList *taglist = tag_family; taglist; taglist = g_list_next(taglist))
1821 char *new_tagname = g_strconcat(new_prefix_tag, &((
dt_tag_t *)taglist->data)->tag[tagname_len], NULL);
1827 GtkTreeModel *
store = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(
model));
1833 GtkSortType sort_order;
1834 gtk_tree_sortable_get_sort_column_id(GTK_TREE_SORTABLE(
store), &sort_column, &sort_order);
1835 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
store), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID, GTK_SORT_ASCENDING);
1837 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
store), sort_column, sort_order);
1847 g_list_free(tagged_images);
1848 tagged_images = NULL;
1853 gint new_flags = ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(category)) ?
DT_TF_CATEGORY : 0) |
1854 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
private)) ?
DT_TF_PRIVATE : 0));
1855 GtkTextIter start, end;
1856 gtk_text_buffer_get_start_iter(buffer, &start);
1857 gtk_text_buffer_get_end_iter(buffer, &end);
1858 gchar *new_synonyms_list = gtk_text_buffer_get_text(buffer, &start, &end,
FALSE);
1861 GtkTreeIter store_iter;
1862 GtkTreeModel *
store = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(
model));
1863 gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(
model),
1864 &store_iter, &iter);
1874 if(new_synonyms_list && g_strcmp0(synonyms_list, new_synonyms_list) != 0)
1886 GtkWindow *dialog_parent = gtk_window_get_transient_for(GTK_WINDOW(dialog));
1887 gtk_widget_destroy(dialog);
1898 gboolean success =
FALSE;
1899 GList *tag_family = NULL;
1900 GList *tagged_images = NULL;
1904 const int tagname_len = strlen(tagname);
1905 gboolean tagname_exists =
FALSE;
1906 for(GList *taglist = tag_family; taglist && !tagname_exists; taglist = g_list_next(taglist))
1908 char *new_tagname = g_strconcat(newtag, &((
dt_tag_t *)taglist->data)->tag[tagname_len], NULL);
1918 GtkWidget *warning_dialog = gtk_message_dialog_new(GTK_WINDOW(win), GTK_DIALOG_MODAL,
1919 GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
1920 _(
"at least one new tagname (%s) already exists, aborting."), new_tagname);
1921 gtk_dialog_run(GTK_DIALOG(warning_dialog));
1922 gtk_widget_destroy(warning_dialog);
1929 for (GList *taglist = tag_family; taglist; taglist = g_list_next(taglist))
1931 char *new_tagname = g_strconcat(newtag, &((
dt_tag_t *)taglist->data)->tag[tagname_len], NULL);
1943 g_list_free(tagged_images);
1944 tagged_images = NULL;
1959 GtkTreeModel *
model = NULL;
1960 GtkTreeView *
view =
d->dictionary_view;
1961 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
view));
1970 if(tag_count == 0)
return;
1973 GtkWidget *dialog = gtk_dialog_new_with_buttons(_(
"change path"), GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT,
1974 _(
"cancel"), GTK_RESPONSE_NONE, _(
"save"), GTK_RESPONSE_YES, NULL);
1975 gtk_window_set_default_size(GTK_WINDOW(dialog), 300, -1);
1976 GtkWidget *area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1978 gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
1979 gtk_container_add(GTK_CONTAINER(area), vbox);
1980 text = g_strdup_printf(_(
"selected: %s"), tagname);
1981 label = gtk_label_new(text);
1982 gtk_box_pack_start(GTK_BOX(vbox), label,
FALSE,
TRUE, 0);
1986 gtk_box_pack_start(GTK_BOX(vbox), box,
FALSE,
TRUE, 0);
1987 text = g_strdup_printf(ngettext(
"<u>%d</u> tag will be updated",
"<u>%d</u> tags will be updated", tag_count), tag_count);
1988 label = gtk_label_new(NULL);
1989 gtk_label_set_markup(GTK_LABEL(label), text);
1990 gtk_box_pack_start(GTK_BOX(box), label,
FALSE,
TRUE, 0);
1992 text = g_strdup_printf(ngettext(
"<u>%d</u> image will be updated",
"<u>%d</u> images will be updated", img_count), img_count);
1993 label = gtk_label_new(NULL);
1994 gtk_label_set_markup(GTK_LABEL(label), text);
1995 gtk_box_pack_start(GTK_BOX(box), label,
FALSE,
TRUE, 0);
2000 gtk_entry_set_text(GTK_ENTRY(entry), tagname);
2001 gtk_box_pack_start(GTK_BOX(vbox), entry,
FALSE,
TRUE, 0);
2003#ifdef GDK_WINDOWING_QUARTZ
2006 gtk_widget_show_all(dialog);
2008 if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_YES)
2010 const char *newtag = gtk_entry_get_text(GTK_ENTRY(entry));
2011 if(g_strcmp0(newtag, tagname) == 0)
2013 char *message = NULL;
2015 message = _(
"empty tag is not allowed, aborting");
2016 if(strchr(newtag,
'|') == &newtag[0] || strchr(newtag,
'|') == &newtag[strlen(newtag)-1] || strstr(newtag,
"||"))
2017 message = _(
"'|' misplaced, empty tag is not allowed, aborting");
2020 GtkWidget *warning_dialog = gtk_message_dialog_new(GTK_WINDOW(dialog), GTK_DIALOG_MODAL,
2021 GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE,
"%s", message);
2022 gtk_dialog_run(GTK_DIALOG(warning_dialog));
2023 gtk_widget_destroy(warning_dialog);
2024 GtkWindow *dialog_parent = gtk_window_get_transient_for(GTK_WINDOW(dialog));
2025 gtk_widget_destroy(dialog);
2032 GtkWindow *dialog_parent = gtk_window_get_transient_for(GTK_WINDOW(dialog));
2033 gtk_widget_destroy(dialog);
2042 GtkTreeModel *
model = gtk_tree_view_get_model(GTK_TREE_VIEW(
d->dictionary_view));
2043 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
d->dictionary_view));
2065 if(
d->collection[0])
2070 d->collection[0] =
'\0';
2078 GtkTreeModel *
model = gtk_tree_view_get_model(GTK_TREE_VIEW(
d->dictionary_view));
2079 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
d->dictionary_view));
2084 gtk_entry_set_text(
d->dict_entry, tag);
2086 gtk_entry_grab_focus_without_selecting(
d->dict_entry);
2094 const guint nb = g_list_length(tagids);
2097 guint img_count = 0;
2098 for(GList *
t = tagids;
t;
t = g_list_next(
t))
2101 if(img_count > 0 ||
dt_conf_get_bool(
"plugins/lighttable/tagging/ask_before_delete_tag"))
2104 GtkWidget *dialog = gtk_dialog_new_with_buttons(_(
"delete tag?"), GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT,
2105 _(
"cancel"), GTK_RESPONSE_NONE, _(
"delete"), GTK_RESPONSE_YES, NULL);
2106 gtk_window_set_default_size(GTK_WINDOW(dialog), 300, -1);
2107 GtkWidget *area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
2108 gchar *text = g_strdup_printf(ngettext(
"do you really want to delete %d tag?\n%d image is assigned these tags!",
2109 "do you really want to delete %d tags?\n%d images are assigned these tags!",
2110 img_count), nb, img_count);
2112 gtk_container_set_border_width(GTK_CONTAINER(area), 8);
2113 gtk_container_add(GTK_CONTAINER(area), label);
2115#ifdef GDK_WINDOWING_QUARTZ
2118 gtk_widget_show_all(dialog);
2119 const int res = gtk_dialog_run(GTK_DIALOG(dialog));
2120 GtkWindow *dialog_parent = gtk_window_get_transient_for(GTK_WINDOW(dialog));
2121 gtk_widget_destroy(dialog);
2123 if(res != GTK_RESPONSE_YES)
return;
2126 GList *affected = NULL;
2127 for(GList *
t = tagids;
t;
t = g_list_next(
t))
2129 const guint tagid = GPOINTER_TO_UINT(
t->data);
2145 g_list_free(affected);
2154 g_list_free(tagids);
2164 GtkTreeModel *
model = NULL;
2165 GtkTreeView *
view =
d->dictionary_view;
2166 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
view));
2183 GtkTreeIter iter, child;
2184 GtkTreeModel *
model = gtk_tree_view_get_model(GTK_TREE_VIEW(
d->dictionary_view));
2185 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
d->dictionary_view));
2186 const gint sel_cnt = gtk_tree_selection_count_selected_rows(selection);
2187 if(sel_cnt == 0)
return;
2195 menuitem = gtk_menu_item_new_with_label(_(
"delete tags"));
2197 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
2198 gtk_widget_show_all(GTK_WIDGET(menu));
2199 gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent *)event);
2206 gtk_tree_model_get(
model, &iter,
2210 if(
d->tree_flag || !
d->suggestion_flag)
2212 menuitem = gtk_menu_item_new_with_label(_(
"create tag..."));
2213 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
2218 menuitem = gtk_menu_item_new_with_label(_(
"delete tag"));
2219 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
2223 if(gtk_tree_model_iter_children(
model, &child, &iter))
2225 menuitem = gtk_menu_item_new_with_label(_(
"delete node"));
2226 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
2230 menuitem = gtk_menu_item_new_with_label(_(
"edit..."));
2231 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
2238 menuitem = gtk_menu_item_new_with_label(_(
"change path..."));
2239 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
2243 if(
d->tree_flag && !tagid)
2245 menuitem = gtk_separator_menu_item_new();
2246 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
2248 menuitem = gtk_menu_item_new_with_label(_(
"set as a tag"));
2249 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
2253 if(!
d->suggestion_flag)
2255 menuitem = gtk_separator_menu_item_new();
2256 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
2259 menuitem = gtk_menu_item_new_with_label(_(
"copy to entry"));
2261 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
2263 if(
d->collection[0])
2265 char *collection = g_malloc(4096);
2267 if(g_strcmp0(
d->collection, collection) == 0)
d->collection[0] =
'\0';
2270 if(count ||
d->collection[0])
2272 menuitem = gtk_separator_menu_item_new();
2273 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
2276 menuitem = gtk_menu_item_new_with_label(_(
"go to tag collection"));
2278 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
2280 if(
d->collection[0])
2282 menuitem = gtk_menu_item_new_with_label(_(
"go back to work"));
2284 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
2287 gtk_widget_show_all(GTK_WIDGET(menu));
2289 gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent *)event);
2298 const int button_pressed = (
event->type == GDK_BUTTON_PRESS) ? event->button : 0;
2299 const gboolean shift_pressed =
dt_modifier_is(event->state, GDK_SHIFT_MASK);
2303 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
view));
2304 GtkTreePath *path = NULL;
2306 if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(
view), (gint)event->x, (gint)event->y, &path, NULL, NULL, NULL))
2310 GtkTreeModel *
model = gtk_tree_view_get_model(
d->dictionary_view);
2312 gtk_tree_model_get_iter(
model, &iter, path);
2314 gtk_tree_model_get(
model, &iter,
2320 d->drag.tagname = tagname;
2321 if(
d->drag.path) gtk_tree_path_free(
d->drag.path);
2322 d->drag.path = path;
2323 if(
d->drag.lastpath) gtk_tree_path_free(
d->drag.lastpath);
2324 d->drag.lastpath = NULL;
2330 if(!gtk_tree_selection_path_is_selected(selection, path))
2332 gtk_tree_selection_unselect_all(selection);
2333 gtk_tree_selection_select_path(selection, path);
2336 gtk_tree_path_free(path);
2341 gtk_tree_selection_select_path(selection, path);
2342 gtk_tree_view_expand_row(GTK_TREE_VIEW(
view), path,
TRUE);
2343 gtk_tree_path_free(path);
2347 gtk_tree_path_free(path);
2357 GtkTreeModel *
model = gtk_tree_view_get_model(GTK_TREE_VIEW(
d->dictionary_view));
2358 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
view));
2359 gboolean res =
FALSE;
2364 GtkTreePath *path = gtk_tree_model_get_path(
model, &iter);
2371 gtk_tree_view_collapse_all(GTK_TREE_VIEW(
view));
2373 gtk_tree_view_collapse_row(GTK_TREE_VIEW(
view), path);
2380 gtk_tree_view_expand_row(GTK_TREE_VIEW(
view), path,
2388 gtk_tree_path_free(path);
2390 if(event->keyval == GDK_KEY_Tab)
2392 gtk_tree_selection_unselect_all(selection);
2395 else if(event->keyval == GDK_KEY_ISO_Left_Tab)
2397 gtk_tree_selection_unselect_all(selection);
2398 gtk_widget_grab_focus(GTK_WIDGET(
d->dict_entry));
2407 gboolean res =
FALSE;
2408 GtkTreePath *path = NULL;
2410 if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(treeview),
x, y, &path, NULL, NULL, NULL))
2412 GtkTreeModel *
model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
2414 if(gtk_tree_model_get_iter(
model, &iter, path))
2419 char *synonyms = NULL;
2426 gchar *text = g_strdup_printf(_(
"%s"), tagname);
2428 text =
dt_util_dstrcat(text,
"synonyms: %s", (synonyms && synonyms[0]) ? synonyms :
" - ");
2429 gtk_tooltip_set_text(
tooltip, text);
2438 gtk_tree_path_free(path);
2448 last_dirname = g_get_home_dir();
2452 GtkFileChooserNative *filechooser = gtk_file_chooser_native_new(
2453 _(
"select a keyword file"), GTK_WINDOW(win), GTK_FILE_CHOOSER_ACTION_OPEN,
2454 _(
"_import"), _(
"_cancel"));
2456 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(filechooser), last_dirname);
2457 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(filechooser),
FALSE);
2459 if(gtk_native_dialog_run(GTK_NATIVE_DIALOG(filechooser)) == GTK_RESPONSE_ACCEPT)
2461 char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filechooser));
2462 char *dirname = g_path_get_dirname(filename);
2463 dt_conf_set_string(
"plugins/lighttable/tagging/last_import_export_location", dirname);
2470 g_object_unref(filechooser);
2476 GDateTime *now = g_date_time_new_now_local();
2477 char *export_filename = g_date_time_format(now,
"darktable_tags_%F_%H-%M.txt");
2481 last_dirname = g_get_home_dir();
2485 GtkFileChooserNative *filechooser = gtk_file_chooser_native_new(
2486 _(
"select file to export to"), GTK_WINDOW(win), GTK_FILE_CHOOSER_ACTION_SAVE,
2487 _(
"_export"), _(
"_cancel"));
2489 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(filechooser),
TRUE);
2490 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(filechooser), last_dirname);
2491 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(filechooser), export_filename);
2493 if(gtk_native_dialog_run(GTK_NATIVE_DIALOG(filechooser)) == GTK_RESPONSE_ACCEPT)
2495 char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(filechooser));
2496 char *dirname = g_path_get_dirname(filename);
2497 dt_conf_set_string(
"plugins/lighttable/tagging/last_import_export_location", dirname);
2504 g_date_time_unref(now);
2506 g_object_unref(filechooser);
2512 GtkTreeModel *
model = gtk_tree_view_get_model(GTK_TREE_VIEW(
d->dictionary_view));
2516 d->suggestion_flag =
dt_conf_get_bool(
"plugins/lighttable/tagging/nosuggestion");
2517 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
d->toggle_suggestion_button),
d->suggestion_flag);
2519 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
d->toggle_tree_button),
d->tree_flag);
2523 if(
model == GTK_TREE_MODEL(
d->dictionary_listfilter))
2525 g_object_ref(
model);
2526 gtk_tree_view_set_model(GTK_TREE_VIEW(
d->dictionary_view), NULL);
2527 GtkTreeModel *
store = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(
model));
2528 gtk_list_store_clear(GTK_LIST_STORE(
store));
2529 gtk_tree_view_set_model(GTK_TREE_VIEW(
d->dictionary_view), GTK_TREE_MODEL(
d->dictionary_treefilter));
2530 g_object_unref(
d->dictionary_treefilter);
2532 gtk_widget_set_sensitive(GTK_WIDGET(
d->toggle_suggestion_button),
FALSE);
2536 if(
model == GTK_TREE_MODEL(
d->dictionary_treefilter))
2538 g_object_ref(
model);
2539 gtk_tree_view_set_model(GTK_TREE_VIEW(
d->dictionary_view), NULL);
2540 GtkTreeModel *
store = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(
model));
2541 gtk_tree_store_clear(GTK_TREE_STORE(
store));
2542 gtk_tree_view_set_model(GTK_TREE_VIEW(
d->dictionary_view), GTK_TREE_MODEL(
d->dictionary_listfilter));
2543 g_object_unref(
d->dictionary_listfilter);
2545 gtk_widget_set_sensitive(GTK_WIDGET(
d->toggle_suggestion_button),
TRUE);
2550 gtk_drag_source_set(GTK_WIDGET(
d->dictionary_view), GDK_BUTTON1_MASK,
2553 gtk_drag_source_unset(GTK_WIDGET(
d->dictionary_view));
2555 d->sort_count_flag =
dt_conf_get_bool(
"plugins/lighttable/tagging/listsortedbycount");
2560 d->hide_path_flag =
dt_conf_get_bool(
"plugins/lighttable/tagging/hidehierarchy");
2562 d->attached_tree_flag =
dt_conf_get_bool(
"plugins/lighttable/tagging/attached_treeview");
2566 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
d->dttags_check),
d->dttags_flag);
2574 const gboolean new_state = !
dt_conf_get_bool(
"plugins/lighttable/tagging/nosuggestion");
2583 const gboolean new_state = !
dt_conf_get_bool(
"plugins/lighttable/tagging/treeview");
2595 return (count_b - count_a);
2606 const gboolean sort = g_strcmp0(tag_a, tag_b);
2620 for(
char *letter = tag_a; *letter; letter++)
2621 if(*letter ==
'|') *letter =
'\1';
2624 tag_a = g_strdup(
"");
2628 for(
char *letter = tag_b; *letter; letter++)
2629 if(*letter ==
'|') *letter =
'\1';
2632 tag_b = g_strdup(
"");
2634 const gboolean sort = g_strcmp0(tag_a, tag_b);
2664 d->dttags_flag = gtk_toggle_button_get_active(source);
2673 gtk_entry_set_text(
d->entry,
"");
2674 gtk_entry_set_text(
d->dict_entry,
"");
2692 if(!
d->completion_store)
return;
2693 gtk_list_store_clear(
d->completion_store);
2696 for(GList *
t = tags;
t;
t = g_list_next(
t))
2701 gtk_list_store_append(
d->completion_store, &iter);
2712 if(!
d->collection_tags)
return;
2713 g_hash_table_remove_all(
d->collection_tags);
2716 for(GList *
t = tags;
t;
t = g_list_next(
t))
2720 g_hash_table_add(
d->collection_tags, g_strdup(tag->tag));
2727 const int column = gtk_entry_completion_get_text_column(completion);
2730 if(gtk_tree_model_get_column_type(
model, column) != G_TYPE_STRING)
return TRUE;
2732 GtkEditable *e = (GtkEditable *)gtk_entry_completion_get_entry(completion);
2733 if(!GTK_IS_EDITABLE(e))
2738 gtk_tree_model_get(
model, iter, column, &tag, -1);
2740 gint cut_off, cur_pos = gtk_editable_get_position(e);
2742 gchar *currentText = gtk_editable_get_chars(e, 0, -1);
2743 const gchar *lastTag = g_strrstr(currentText,
",");
2750 cut_off = (int)(g_utf8_strlen(currentText, -1) - g_utf8_strlen(lastTag, -1))+1;
2754 gtk_editable_delete_text(e, cut_off, cur_pos);
2756 gtk_editable_insert_text(e, tag, -1, &cur_pos);
2757 gtk_editable_set_position(e, cur_pos);
2765 gboolean res =
FALSE;
2769 GtkEditable *e = (GtkEditable *)gtk_entry_completion_get_entry(completion);
2771 if(!GTK_IS_EDITABLE(e))
2776 const gint cur_pos = gtk_editable_get_position(e);
2777 const gboolean onLastTag = (g_strstr_len(&
key[cur_pos], -1,
",") == NULL);
2783 GtkTreeModel *
model = gtk_entry_completion_get_model(completion);
2784 const int column = gtk_entry_completion_get_text_column(completion);
2786 if(gtk_tree_model_get_column_type(
model, column) != G_TYPE_STRING)
2791 const gchar *lastTag = g_strrstr(
key,
",");
2802 gtk_tree_model_get(
model, iter, column, &tag, -1);
2806 if(lastTag[0] ==
'\0')
2808 res = (
d &&
d->collection_tags && tag) ? g_hash_table_contains(
d->collection_tags, tag) :
FALSE;
2815 char *normalized = g_utf8_normalize(tag, -1, G_NORMALIZE_ALL);
2818 char *casefold = g_utf8_casefold(normalized, -1);
2821 res = g_strstr_len(casefold, -1, lastTag) != NULL;
2840 if(!
d->drag.root)
return;
2841 GtkTreeModel *
model = GTK_TREE_MODEL(
d->dictionary_treestore);
2843 gtk_tree_model_get_iter_first(
model, &iter);
2847 gtk_tree_store_remove(
d->dictionary_treestore, &iter);
2855 GtkTreeView *tree = GTK_TREE_VIEW(widget);
2859 cairo_surface_t *row_pix = gtk_tree_view_create_row_drag_icon(tree,
d->drag.path);
2865 gtk_drag_set_icon_surface(context, row_pix);
2866 cairo_surface_destroy(row_pix);
2867 gtk_tree_path_free(
d->drag.path);
2868 d->drag.path = NULL;
2869 d->drag.tag_source =
TRUE;
2877 gtk_selection_data_set(selection_data, gtk_selection_data_get_target(selection_data),
2882 GtkSelectionData *selection_data, guint target_type, guint time,
2886 GtkTreeView *tree = GTK_TREE_VIEW(widget);
2888 g_signal_stop_emission_by_name(tree,
"drag-data-received");
2889 gboolean success =
FALSE;
2893 GtkTreePath *path = NULL;
2894 if(gtk_tree_view_get_path_at_pos(tree,
x, y, &path, NULL, NULL, NULL))
2897 GtkTreeModel *
model = gtk_tree_view_get_model(tree);
2900 gtk_tree_model_get_iter(
model, &iter, path);
2903 const gboolean root = (
name &&
name[0] ==
'\0');
2904 char *
leave = g_strrstr(
d->drag.tagname,
"|");
2911 gtk_tree_path_free(path);
2917 GtkTreePath *path = NULL;
2918 const int imgs_nb = gtk_selection_data_get_length(selection_data) /
sizeof(uint32_t);
2919 if(imgs_nb && gtk_tree_view_get_path_at_pos(tree,
x, y, &path, NULL, NULL, NULL))
2921 const uint32_t *imgt = (uint32_t *)gtk_selection_data_get_data(selection_data);
2923 for(
int i = 0;
i < imgs_nb;
i++)
2925 imgs = g_list_prepend(imgs, GINT_TO_POINTER(imgt[
i]));
2927 GtkTreeModel *
model = gtk_tree_view_get_model(tree);
2929 gtk_tree_model_get_iter(
model, &iter, path);
2940 gtk_tree_path_free(path);
2944 gtk_drag_finish(context, success,
FALSE, time);
2950 if(
d->drag.lastpath)
2952 gtk_tree_view_expand_row(
d->dictionary_view,
d->drag.lastpath,
FALSE);
2960 if(
d->drag.scroll_timeout)
2962 GdkRectangle visible;
2963 gtk_tree_view_get_visible_rect(
d->dictionary_view, &visible);
2965 gtk_tree_view_convert_bin_window_to_tree_coords(
d->dictionary_view,
2966 0, 0, NULL, &top_ty);
2969 if(
d->drag.tag_source && !
d->drag.root &&
d->drag.last_y < 5 && top_ty < 1)
2972 gtk_tree_store_prepend(
d->dictionary_treestore, &iter, NULL);
2973 gtk_tree_store_set(
d->dictionary_treestore, &iter,
2982 d->drag.root =
TRUE;
2984 else if(
d->drag.root &&
d->drag.last_y >= 20)
2987 if(
d->drag.last_y < 5)
2990 gtk_tree_view_scroll_to_point(
d->dictionary_view, 0, top_ty - 25 < 0 ? 0 : top_ty - 25);
2992 else if(
d->drag.last_y > visible.height - 5)
2994 gtk_tree_view_scroll_to_point(
d->dictionary_view, 0, top_ty + 25);
3004 GtkTreeView *tree = GTK_TREE_VIEW(widget);
3005 GtkTreePath *path = NULL;
3007 if(gtk_tree_view_get_path_at_pos(tree,
x, y, &path, NULL, NULL, NULL))
3009 if(
IS_NULL_PTR(
d->drag.lastpath) || ((
d->drag.lastpath) && gtk_tree_path_compare(
d->drag.lastpath, path) != 0))
3011 GtkTreeViewColumn *col = gtk_tree_view_get_column(
d->dictionary_view, 0);
3012 const int sel_width = gtk_tree_view_column_get_width(col);
3015 if(!gtk_tree_view_row_expanded(tree, path))
3020 GtkTreeSelection *selection = gtk_tree_view_get_selection(
d->dictionary_view);
3021 gtk_tree_selection_select_path(selection, path);
3023 if(
d->drag.scroll_timeout == 0)
3032 if(
d->drag.lastpath)
3033 gtk_tree_path_free(
d->drag.lastpath);
3034 d->drag.lastpath = path;
3044 GtkTreeSelection *selection = gtk_tree_view_get_selection(
d->dictionary_view);
3045 gtk_tree_selection_unselect_all(selection);
3046 if(
d->drag.scroll_timeout)
3047 g_source_remove(
d->drag.scroll_timeout);
3048 d->drag.scroll_timeout = 0;
3049 d->drag.tag_source =
FALSE;
3057 gtk_widget_show_all(
d->manage_window);
3058 gtk_window_present(GTK_WINDOW(
d->manage_window));
3065 if(!
d->tree_flag &&
d->suggestion_flag)
3072 dt_conf_set_int(
"plugins/lighttable/tagging/confidence", gtk_spin_button_get_value_as_int(spin));
3079 dt_conf_set_int(
"plugins/lighttable/tagging/nb_recent_tags", gtk_spin_button_get_value_as_int(spin));
3085static void _entry_clear_icon(GtkEntry *entry, GtkEntryIconPosition pos, GdkEvent *event, gpointer user_data)
3087 if(pos == GTK_ENTRY_ICON_SECONDARY) gtk_entry_set_text(entry,
"");
3100 self->
data = (
void *)
d;
3110 GtkTreeModel *
model;
3111 GtkListStore *liststore;
3112 GtkTreeStore *treestore;
3113 GtkTreeViewColumn *col;
3114 GtkCellRenderer *renderer;
3125 gtk_widget_set_valign(
d->attached_view_combo, GTK_ALIGN_CENTER);
3126 gtk_widget_set_tooltip_text(
d->attached_view_combo,
3127 _(
"show the attached tags as a flat list or as a hierarchical tree"));
3129 gtk_box_pack_start(hbox,
d->attached_view_combo,
TRUE,
TRUE, 0);
3137 gtk_widget_set_valign(
d->sort_combo, GTK_ALIGN_CENTER);
3138 gtk_widget_set_tooltip_text(
d->sort_combo, _(
"sort the tags by name or by image count"));
3139 g_signal_connect(G_OBJECT(
d->sort_combo),
"value-changed", G_CALLBACK(
_sort_combo_changed), self);
3140 gtk_box_pack_start(hbox,
d->sort_combo,
TRUE,
TRUE, 0);
3142 gtk_box_pack_start(GTK_BOX(self->
widget), GTK_WIDGET(hbox),
FALSE,
TRUE, 0);
3148 gtk_box_pack_start(GTK_BOX(self->
widget), GTK_WIDGET(box),
TRUE,
TRUE, 0);
3149 view = GTK_TREE_VIEW(gtk_tree_view_new());
3153 gtk_box_pack_start(box, w,
TRUE,
TRUE, 0);
3154 d->attached_view =
view;
3155 gtk_tree_view_set_enable_search(
view,
FALSE);
3156 gtk_tree_view_set_headers_visible(
view,
FALSE);
3158 G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_BOOLEAN);
3165 d->attached_liststore = liststore;
3168 G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_BOOLEAN);
3171 d->attached_treestore = treestore;
3172 g_object_set(G_OBJECT(
view),
"has-tooltip",
TRUE, NULL);
3175 col = gtk_tree_view_column_new();
3176 gtk_tree_view_append_column(
view, col);
3177 renderer = gtk_cell_renderer_text_new();
3178 g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_MIDDLE, (gchar *)0);
3179 gtk_tree_view_column_pack_start(col, renderer,
TRUE);
3181 gtk_tree_view_column_set_expand(col,
TRUE);
3182 gtk_tree_view_set_expander_column(
view, col);
3185 col = gtk_tree_view_column_new();
3186 gtk_tree_view_append_column(
view, col);
3188 g_object_set(renderer,
"mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
3189 gtk_tree_view_column_pack_end(col, renderer,
FALSE);
3193 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(
view), GTK_SELECTION_MULTIPLE);
3194 gtk_tree_view_set_model(
view, GTK_TREE_MODEL(liststore));
3197 gtk_widget_set_tooltip_text(GTK_WIDGET(
view), _(
"attached tags,"
3198 "\nclick the trash icon, press Delete or double-click to detach"
3199 "\nselect several tags and right-click to detach them in bulk,"
3200 "\npress Tab to give the focus to entry,"
3201 "\nctrl-wheel scroll to resize the window"));
3206#define NEW_TOGGLE_BUTTON(paint, callback, tooltip, action) \
3207 button = dtgtk_togglebutton_new(paint, 0, NULL); \
3208 gtk_widget_set_tooltip_text(button, tooltip); \
3209 gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, TRUE, 0); \
3210 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(callback), self); \
3214 gtk_box_pack_start(GTK_BOX(self->
widget), GTK_WIDGET(box),
FALSE,
TRUE, 0);
3217 gtk_box_reorder_child(GTK_BOX(self->
widget), GTK_WIDGET(box), 1);
3221 w = gtk_entry_new();
3223 gtk_entry_set_text(GTK_ENTRY(w),
"");
3224 gtk_entry_set_width_chars(GTK_ENTRY(w), 0);
3226 gtk_entry_set_placeholder_text(GTK_ENTRY(w), _(
"enter or pick a tag, Enter to attach"));
3227 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(w), GTK_ENTRY_ICON_SECONDARY,
"edit-clear-symbolic");
3228 gtk_entry_set_icon_tooltip_text(GTK_ENTRY(w), GTK_ENTRY_ICON_SECONDARY, _(
"clear entry"));
3229 g_signal_connect(G_OBJECT(w),
"icon-press", G_CALLBACK(
_entry_clear_icon), NULL);
3230 gtk_box_pack_start(hbox, w,
TRUE,
TRUE, 0);
3231 gtk_widget_add_events(GTK_WIDGET(w), GDK_KEY_RELEASE_MASK);
3232 d->entry = GTK_ENTRY(w);
3236 gtk_widget_set_tooltip_text(
d->validate_button, _(
"attach this tag to the selected images"));
3237 gtk_box_pack_end(GTK_BOX(hbox),
d->validate_button,
FALSE,
TRUE, 0);
3239 gtk_box_pack_start(box, GTK_WIDGET(hbox),
FALSE,
TRUE, 0);
3247 GtkEntryCompletion *completion = gtk_entry_completion_new();
3248 gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(
d->completion_store));
3250 gtk_entry_completion_set_inline_completion(completion,
TRUE);
3251 gtk_entry_completion_set_popup_set_width(completion,
FALSE);
3252 gtk_entry_completion_set_minimum_key_length(completion, 1);
3253 g_signal_connect(G_OBJECT(completion),
"match-selected", G_CALLBACK(
_match_selected_func), self);
3255 gtk_entry_set_completion(
d->entry, completion);
3256 g_object_unref(completion);
3258 g_object_unref(
d->completion_store);
3263 g_signal_connect(G_OBJECT(
d->entry),
"key-press-event", G_CALLBACK(
_enter_key_pressed), (gpointer)self);
3266 d->manage_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3267#ifdef GDK_WINDOWING_QUARTZ
3270 gtk_window_set_title(GTK_WINDOW(
d->manage_window), _(
"manage tags"));
3274 g_signal_connect(G_OBJECT(
d->manage_window),
"delete-event", G_CALLBACK(gtk_widget_hide_on_delete), NULL);
3278 gtk_container_add(GTK_CONTAINER(
d->manage_window), GTK_WIDGET(box));
3281 w = gtk_entry_new();
3283 gtk_entry_set_text(GTK_ENTRY(w),
"");
3284 gtk_entry_set_width_chars(GTK_ENTRY(w), 0);
3285 gtk_entry_set_placeholder_text(GTK_ENTRY(w), _(
"type to filter the tag dictionary below"));
3286 gtk_entry_set_icon_from_icon_name(GTK_ENTRY(w), GTK_ENTRY_ICON_SECONDARY,
"edit-clear-symbolic");
3287 gtk_entry_set_icon_tooltip_text(GTK_ENTRY(w), GTK_ENTRY_ICON_SECONDARY, _(
"clear entry"));
3288 g_signal_connect(G_OBJECT(w),
"icon-press", G_CALLBACK(
_entry_clear_icon), NULL);
3289 gtk_widget_add_events(GTK_WIDGET(w), GDK_KEY_RELEASE_MASK);
3290 g_signal_connect(G_OBJECT(w),
"changed", G_CALLBACK(
_tag_name_changed), (gpointer)self);
3291 d->dict_entry = GTK_ENTRY(w);
3292 gtk_box_pack_start(box, w,
FALSE,
TRUE, 0);
3295 view = GTK_TREE_VIEW(gtk_tree_view_new());
3299 gtk_box_pack_start(box, w,
TRUE,
TRUE, 0);
3300 d->dictionary_view =
view;
3301 gtk_tree_view_set_enable_search(
view,
FALSE);
3302 gtk_tree_view_set_headers_visible(
view,
FALSE);
3304 G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_BOOLEAN);
3311 d->dictionary_liststore = liststore;
3312 model = gtk_tree_model_filter_new(GTK_TREE_MODEL(liststore), NULL);
3314 d->dictionary_listfilter = GTK_TREE_MODEL_FILTER(
model);
3316 G_TYPE_STRING, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_BOOLEAN);
3319 d->dictionary_treestore = treestore;
3320 model = gtk_tree_model_filter_new(GTK_TREE_MODEL(treestore), NULL);
3322 d->dictionary_treefilter = GTK_TREE_MODEL_FILTER(
model);
3324 col = gtk_tree_view_column_new();
3325 gtk_tree_view_append_column(
view, col);
3326 renderer = gtk_cell_renderer_text_new();
3327 g_object_set(renderer,
"ellipsize", PANGO_ELLIPSIZE_MIDDLE, (gchar *)0);
3328 gtk_tree_view_column_pack_start(col, renderer,
TRUE);
3330 gtk_tree_view_set_expander_column(
view, col);
3332 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(
view), GTK_SELECTION_MULTIPLE);
3333 gtk_widget_set_tooltip_text(GTK_WIDGET(
view), _(
"tag dictionary,"
3334 "\nright-click to create, edit, delete tags,"
3335 "\nselect several tags and right-click to delete them in bulk,"
3336 "\nshift+click to fully expand the selected tag,"
3337 "\nctrl-scroll to resize the window"));
3341 gtk_tree_view_set_model(
view, GTK_TREE_MODEL(
d->dictionary_listfilter));
3342 g_object_unref(
d->dictionary_listfilter);
3343 g_object_set(G_OBJECT(
view),
"has-tooltip",
TRUE, NULL);
3349 d->drag.path = NULL;
3350 d->drag.tagname = NULL;
3351 d->drag.scroll_timeout = 0;
3352 d->drag.expand_timeout = 0;
3354 d->drag.tag_source =
FALSE;
3355 gtk_drag_dest_set(GTK_WIDGET(
d->dictionary_view), GTK_DEST_DEFAULT_ALL,
3357 g_signal_connect(
d->dictionary_view,
"drag-data-get", G_CALLBACK(
_event_dnd_get), self);
3358 g_signal_connect(
d->dictionary_view,
"drag-data-received", G_CALLBACK(
_event_dnd_received), self);
3359 g_signal_connect_after(
d->dictionary_view,
"drag-begin", G_CALLBACK(
_event_dnd_begin), self);
3360 g_signal_connect_after(
d->dictionary_view,
"drag-end", G_CALLBACK(
_event_dnd_end), self);
3361 g_signal_connect(
d->dictionary_view,
"drag-motion", G_CALLBACK(
_event_dnd_motion), self);
3368 gtk_box_pack_start(hbox,
d->new_button,
TRUE,
TRUE, 0);
3371 gtk_box_pack_start(hbox,
d->import_button,
TRUE,
TRUE, 0);
3374 gtk_box_pack_start(hbox,
d->export_button,
TRUE,
TRUE, 0);
3377 _(
"toggle list / tree view"), N_(
"tree"));
3379 _(
"toggle list with / without suggestion"), N_(
"suggestion"));
3381 gtk_box_pack_start(box, GTK_WIDGET(hbox),
FALSE,
TRUE, 0);
3390 GtkWidget *lbl = gtk_label_new(_(
"suggested tags level of confidence"));
3391 gtk_widget_set_halign(lbl, GTK_ALIGN_START);
3392 gtk_widget_set_hexpand(lbl,
TRUE);
3393 GtkWidget *spin = gtk_spin_button_new_with_range(0, 100, 1);
3394 gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin),
dt_conf_get_int(
"plugins/lighttable/tagging/confidence"));
3395 gtk_widget_set_tooltip_text(spin, _(
"level of confidence to include a tag in the suggestions list:"
3396 "\n0: all associated tags, 99: 99% matching tags,"
3397 "\n100: only the recently-used tags (faster)"));
3399 gtk_grid_attach(GTK_GRID(grid), lbl, 0, 0, 1, 1);
3400 gtk_grid_attach(GTK_GRID(grid), spin, 1, 0, 1, 1);
3402 GtkWidget *lbl2 = gtk_label_new(_(
"number of recently attached tags"));
3403 gtk_widget_set_halign(lbl2, GTK_ALIGN_START);
3404 gtk_widget_set_hexpand(lbl2,
TRUE);
3405 GtkWidget *spin2 = gtk_spin_button_new_with_range(-1, 1000, 1);
3406 gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin2),
dt_conf_get_int(
"plugins/lighttable/tagging/nb_recent_tags"));
3407 gtk_widget_set_tooltip_text(spin2, _(
"number of recently attached tags shown in the suggestions list"
3408 "\n(-1 disables the recent-tags list)"));
3410 gtk_grid_attach(GTK_GRID(grid), lbl2, 0, 1, 1, 1);
3411 gtk_grid_attach(GTK_GRID(grid), spin2, 1, 1, 1, 1);
3413 gtk_box_pack_start(box, grid,
FALSE,
FALSE, 0);
3417 d->dttags_check = gtk_check_button_new_with_label(_(
"Show system tags"));
3418 gtk_widget_set_tooltip_text(
d->dttags_check, _(
"show the tags automatically attached by Ansel (names starting with “Ansel|”)"));
3421 gtk_box_pack_start(GTK_BOX(self->
widget),
d->dttags_check,
FALSE,
TRUE, 0);
3423#undef NEW_TOGGLE_BUTTON
3435 d->collection = g_malloc(4096);
3436 d->collection_tags = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
3447 N_(
"Tag"), GDK_KEY_t, GDK_CONTROL_MASK,
FALSE,
3448 _(
"Opens the quick tagging entry"));
3451 N_(
"Redo last tag"), GDK_KEY_t, GDK_MOD1_MASK,
FALSE,
3452 _(
"Re-applies the last used tag"));
3455 N_(
"Tag"), GDK_KEY_t, GDK_CONTROL_MASK,
FALSE,
3456 _(
"Opens the quick tagging entry"));
3459 N_(
"Redo last tag"), GDK_KEY_t, GDK_MOD1_MASK,
FALSE,
3460 _(
"Re-applies the last used tag"));
3486 if(
d->manage_window) gtk_widget_destroy(
d->manage_window);
3487 if(
d->collection_tags) g_hash_table_destroy(
d->collection_tags);
3489 if(
d->attached_liststore) g_object_unref(
d->attached_liststore);
3490 if(
d->attached_treestore) g_object_unref(
d->attached_treestore);
3496 if(
d->drag.path) gtk_tree_path_free(
d->drag.path);
3508 case GDK_KEY_Escape:
3509 g_list_free(
d->floating_tag_imgs);
3510 d->floating_tag_imgs = NULL;
3511 gtk_widget_destroy(
d->floating_tag_window);
3516 case GDK_KEY_Return:
3518 const gchar *tag = gtk_entry_get_text(GTK_ENTRY(entry));
3521 g_list_free(
d->floating_tag_imgs);
3522 d->floating_tag_imgs = NULL;
3529 gtk_widget_destroy(
d->floating_tag_window);
3541 gtk_widget_destroy(GTK_WIDGET(user_data));
3550 GdkModifierType mods, gpointer user_data)
3575 GdkModifierType mods, gpointer user_data)
3581 dt_control_log(_(
"tag shortcut is not active with tag tree view. please switch to list view"));
3590 gdk_window_get_origin(gtk_widget_get_window(center), &px, &py);
3592 w = gdk_window_get_width(gtk_widget_get_window(center));
3593 h = gdk_window_get_height(gtk_widget_get_window(center));
3606 d->floating_tag_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3607#ifdef GDK_WINDOWING_QUARTZ
3611 gtk_widget_set_can_focus(
d->floating_tag_window,
TRUE);
3612 gtk_window_set_decorated(GTK_WINDOW(
d->floating_tag_window),
FALSE);
3613 gtk_window_set_type_hint(GTK_WINDOW(
d->floating_tag_window), GDK_WINDOW_TYPE_HINT_POPUP_MENU);
3614 gtk_window_set_transient_for(GTK_WINDOW(
d->floating_tag_window), GTK_WINDOW(
window));
3615 gtk_widget_set_opacity(
d->floating_tag_window, 0.8);
3616 gtk_window_move(GTK_WINDOW(
d->floating_tag_window),
x, y);
3621 gtk_widget_add_events(entry, GDK_FOCUS_CHANGE_MASK);
3623 GtkEntryCompletion *completion = gtk_entry_completion_new();
3624 gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(
d->completion_store));
3626 gtk_entry_completion_set_inline_completion(completion,
TRUE);
3627 gtk_entry_completion_set_popup_set_width(completion,
FALSE);
3628 gtk_entry_completion_set_minimum_key_length(completion, 0);
3629 g_signal_connect(G_OBJECT(completion),
"match-selected", G_CALLBACK(
_match_selected_func), self);
3631 gtk_entry_set_completion(GTK_ENTRY(entry), completion);
3633 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
3634 gtk_container_add(GTK_CONTAINER(
d->floating_tag_window), entry);
3638 gtk_widget_show_all(
d->floating_tag_window);
3639 gtk_widget_grab_focus(entry);
3640 gtk_window_present(GTK_WINDOW(
d->floating_tag_window));
3647 const int length =
dt_conf_get_int(
"plugins/lighttable/tagging/nb_recent_tags");
3648 if(length == -1)
return length;
3649 else if(length >= 10/2)
return length * 2;
3665 char *
p = (
char *)list;
3667 for(;*
p !=
'\0';
p++)
3675 char *list2 = g_strdup(list);
3678 p = g_strrstr(list2,
"','");
3690 GtkWidget *mi = gtk_menu_item_new_with_label(_(
"manage tags..."));
3692 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
3698 d->last_tag = g_strdup(tagnames);
3710 for(GList *tag = ntags; tag; tag = g_list_next(tag))
3712 char *escaped = sqlite3_mprintf(
"%q", (
char *)tag->data);
3713 GList *found = g_list_find_custom(tags, escaped, (GCompareFunc)g_strcmp0);
3716 tags = g_list_remove_link(tags, found);
3721 tags = g_list_prepend(tags, g_strdup(escaped));
3722 sqlite3_free(escaped);
3730 if(g_list_length(tags) > nb_recent)
gchar * dt_accels_build_path(const gchar *scope, const gchar *feature)
void dt_accels_remove_accel(dt_accels_t *accels, const char *path, gpointer data)
Recursively remove all accels for all shortcuts containing path. This is unneeded for accels attached...
void dt_accels_new_action_shortcut(dt_accels_t *accels, gboolean(*action_callback)(GtkAccelGroup *group, GObject *acceleratable, guint keyval, GdkModifierType mods, gpointer user_data), gpointer data, GtkAccelGroup *accel_group, const gchar *action_scope, const gchar *action_name, guint key_val, GdkModifierType accel_mods, const gboolean lock, const char *description)
Register a new shortcut for a generic action, setting up its path, default keys and accel group....
GList * dt_act_on_get_images()
int button_pressed(struct dt_iop_module_t *self, double x, double y, double pressure, int which, int type, uint32_t state)
void dt_bauhaus_combobox_set_selected_text_align(GtkWidget *widget, const dt_bauhaus_combobox_alignment_t text_align)
int dt_bauhaus_combobox_get(GtkWidget *widget)
void dt_bauhaus_combobox_set(GtkWidget *widget, const int pos)
void dt_bauhaus_widget_set_label(GtkWidget *widget, const char *label)
GtkWidget * dt_bauhaus_combobox_new(dt_bauhaus_t *bh, dt_gui_module_t *self)
void dt_bauhaus_combobox_add(GtkWidget *widget, const char *text)
@ DT_BAUHAUS_COMBOBOX_ALIGN_RIGHT
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
void dt_collection_deserialize(const char *buf)
int dt_collection_serialize(char *buf, int bufsize)
dt_collection_properties_t
void dt_image_synch_xmps(const GList *img)
void dt_image_synch_xmp(const int selected)
void dt_conf_set_bool(const char *name, int val)
int dt_conf_get_bool(const char *name)
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
void dt_conf_set_string(const char *name, const char *val)
const char * dt_conf_get_string_const(const char *name)
int32_t dt_control_get_mouse_over_id()
void dt_control_log(const char *msg,...)
void leave(dt_view_t *self)
uint32_t view(const dt_view_t *self)
#define dt_gui_freeze_begin()
#define dt_gui_freeze_end()
#define DT_MODULE(MODVER)
static void dt_free_gpointer(gpointer ptr)
void void void gboolean dt_gui_widgets_suppressed(void)
static gboolean dt_modifier_is(const GdkModifierType state, const GdkModifierType desired_modifier_mask)
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
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)
static const GtkTargetEntry target_list_tags_dest[]
static const guint n_targets_tags
static const guint n_targets_tags_dest
static const GtkTargetEntry target_list_tags[]
void dtgtk_cairo_paint_treelist(cairo_t *cr, gint x, gint y, gint w, gint h, gint flags, void *data)
void dtgtk_cairo_paint_check_mark(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 guint dt_keys_mainpad_alternatives(const guint key_val)
Remap keypad keys to usual mainpad ones.
void dt_gui_textview_set_padding(GtkTextView *textview)
Apply the standard recessed-input text padding to a GtkTextView.
void dt_gui_refocus_parent(GtkWindow *parent)
GtkWidget * dt_ui_scroll_wrap(GtkWidget *w, gint min_size, char *config_str, dt_ui_resize_mode_t mode)
Wrap a scrollable content widget in a recessed, vertically resizable scrolled window.
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....
GtkWidget * dt_ui_center(dt_ui_t *ui)
get the center drawable widget
GtkWidget * dt_ui_main_window(dt_ui_t *ui)
get the main window widget
#define DT_GUI_BOX_SPACING
#define DT_PIXEL_APPLY_DPI(value)
GtkCellRenderer * dtgtk_cell_renderer_button_new(void)
void dt_lib_cancel_postponed_update(dt_lib_module_t *mod)
GtkWidget * dt_action_button_new(dt_lib_module_t *self, const gchar *label, gpointer callback, gpointer data, const gchar *tooltip, guint accel_key, GdkModifierType mods)
void dt_lib_queue_postponed_update(dt_lib_module_t *mod, void(*update_fn)(dt_lib_module_t *self))
dt_mipmap_buffer_dsc_flags flags
void dt_osx_disallow_fullscreen(GtkWidget *widget)
int dt_selection_get_length(struct dt_selection_t *selection)
GList * dt_selection_get_list(struct dt_selection_t *selection)
void dt_control_signal_unblock_by_func(const struct dt_control_signal_t *ctlsig, GCallback cb, gpointer user_data)
void dt_control_signal_block_by_func(const struct dt_control_signal_t *ctlsig, GCallback cb, gpointer user_data)
#define DT_DEBUG_CONTROL_SIGNAL_DISCONNECT(ctlsig, cb, user_data)
#define DT_DEBUG_CONTROL_SIGNAL_RAISE(ctlsig, signal,...)
@ DT_SIGNAL_MOUSE_OVER_IMAGE_CHANGE
This signal is raised when mouse hovers over image thumbs both on lighttable and in the filmstrip....
@ DT_SIGNAL_TAG_CHANGED
This signal is raised when a tag is added/deleted/changed
@ DT_SIGNAL_SELECTION_CHANGED
This signal is raised when the selection is changed no param, no returned value.
@ DT_SIGNAL_COLLECTION_CHANGED
This signal is raised when collection changed. To avoid leaking the list, dt_collection_t is connecte...
#define DT_DEBUG_CONTROL_SIGNAL_CONNECT(ctlsig, signal, cb, user_data)
struct _GtkWidget GtkWidget
const float uint32_t state[4]
struct dt_gui_gtk_t * gui
struct dt_selection_t * selection
struct dt_control_signal_t * signals
struct dt_bauhaus_t * bauhaus
GtkAccelGroup * map_accels
GtkAccelGroup * lighttable_accels
GtkWidget * floating_tag_window
GtkWidget * manage_window
GtkListStore * attached_liststore
GList * floating_tag_imgs
GtkWidget * toggle_suggestion_button
GtkTreeStore * attached_treestore
GtkWidget * export_button
GtkWidget * attached_view_combo
GtkTreeModelFilter * dictionary_listfilter
GHashTable * collection_tags
gboolean attached_tree_flag
GtkTreeView * attached_view
static void _lib_tagging_redraw_callback(gpointer instance, dt_lib_module_t *self)
static gboolean _attached_key_pressed(GtkWidget *view, GdkEventKey *event, dt_lib_module_t *self)
static gint _sort_tree_tag_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, dt_lib_module_t *self)
static gint _sort_tree_count_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, dt_lib_module_t *self)
static void _export_button_clicked(GtkButton *button, dt_lib_module_t *self)
void gui_reset(dt_lib_module_t *self)
static gboolean _enter_key_pressed(GtkWidget *entry, GdkEventKey *event, dt_lib_module_t *self)
int set_params(dt_lib_module_t *self, const void *params, int size)
static void _lib_tagging_tags_changed_callback(gpointer instance, dt_lib_module_t *self)
static GList * _selected_tagids(GtkTreeView *view)
static gboolean _dnd_scroll_timeout(dt_lib_module_t *self)
static gboolean _select_previous_user_attached_tag(const int index, GtkTreeView *view)
static void _pop_menu_dictionary_delete_node(GtkWidget *menuitem, dt_lib_module_t *self)
void * get_params(dt_lib_module_t *self, int *size)
static void _tree_tagname_show(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer data, gboolean dictionary_view)
void set_preferences(void *menu, dt_lib_module_t *self)
static void _pop_menu_attached_detach(GtkWidget *menuitem, dt_lib_module_t *self)
static gboolean _find_tag_iter_tagname(GtkTreeModel *model, GtkTreeIter *iter, const char *tagname, const gboolean needle)
static gint _sort_tree_path_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, dt_lib_module_t *self)
static void _toggle_tree_button_callback(GtkToggleButton *source, dt_lib_module_t *self)
static gboolean _set_matching_tag_visibility(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, dt_lib_module_t *self)
static void _update_sel_on_tree(GtkTreeModel *model)
static void _sort_combo_changed(GtkWidget *combo, dt_lib_module_t *self)
static void _validate_button_clicked(GtkButton *button, dt_lib_module_t *self)
static gboolean _row_tooltip_setup(GtkWidget *treeview, gint x, gint y, gboolean kb_mode, GtkTooltip *tooltip, dt_lib_module_t *self)
static gboolean _dictionary_key_pressed(GtkWidget *view, GdkEventKey *event, dt_lib_module_t *self)
static void _refresh_collection_tags(dt_lib_module_t *self)
static gboolean _update_tag_name_per_name(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, dt_tag_op_t *to)
static void _event_dnd_get(GtkWidget *widget, GdkDragContext *context, GtkSelectionData *selection_data, const guint target_type, const guint time, dt_lib_module_t *self)
static void _new_button_clicked(GtkButton *button, dt_lib_module_t *self)
static void _dnd_clear_root(dt_lib_module_t *self)
static void _tag_name_changed(GtkEntry *entry, dt_lib_module_t *self)
static gboolean _lib_tagging_tag_redo_accel(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval, GdkModifierType mods, gpointer user_data)
Re-apply the most recent tag expression to the current target images.
static void _detach_tagids(GList *tagids, dt_lib_module_t *self)
static void _update_attached_count(const int tagid, GtkTreeView *view, const gboolean tree_flag)
#define NEW_TOGGLE_BUTTON(paint, callback, tooltip, action)
static void _unselect_all_in_view(GtkTreeView *view)
static void _import_button_clicked(GtkButton *button, dt_lib_module_t *self)
static void _pop_menu_dictionary_change_path(GtkWidget *menuitem, dt_lib_module_t *self)
static gboolean _tree_reveal_func(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
static void _size_recent_tags_list()
static void _propagate_sel_to_parents(GtkTreeModel *model, GtkTreeIter *iter)
static void _update_layout(dt_lib_module_t *self)
static void _create_tag_from_entry(dt_lib_module_t *self, GtkEntry *src)
static void _pop_menu_dictionary_delete_selected(GtkWidget *menuitem, dt_lib_module_t *self)
static void _tree_tagname_show_attached(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
void gui_cleanup(dt_lib_module_t *self)
static void _attached_delete_icon_show(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
static void _pop_menu_dictionary_edit_tag(GtkWidget *menuitem, dt_lib_module_t *self)
static gboolean _select_next_user_attached_tag(const int index, GtkTreeView *view)
static void _show_manage_window(GtkMenuItem *menuitem, dt_lib_module_t *self)
static void _pop_menu_attached_attach_to_all(GtkWidget *menuitem, dt_lib_module_t *self)
static void _entry_clear_icon(GtkEntry *entry, GtkEntryIconPosition pos, GdkEvent *event, gpointer user_data)
static void _pop_menu_dictionary(GtkWidget *treeview, GdkEventButton *event, dt_lib_module_t *self)
static void _set_keyword(dt_lib_module_t *self)
static void _reset_sel_on_path_full(GtkTreeModel *model, GtkTreeIter *iter, const gboolean root)
static void _dttags_check_toggled(GtkToggleButton *source, dt_lib_module_t *self)
static void _sort_attached_list(dt_lib_module_t *self, gboolean force)
static void _event_dnd_begin(GtkWidget *widget, GdkDragContext *context, dt_lib_module_t *self)
static void _recent_tags_changed(GtkSpinButton *spin, dt_lib_module_t *self)
static void _show_keyword_on_view(GtkTreeView *view, const char *keyword, const gboolean select)
static gboolean _apply_rename_path(GtkWidget *dialog, const char *tagname, const char *newtag, dt_lib_module_t *self)
static void _calculate_sel_on_tree(GtkTreeModel *model, GtkTreeIter *iter)
static void _lib_selection_changed_callback(gpointer instance, dt_lib_module_t *self)
static void _find_root_iter_iter(GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *parent)
void init_presets(dt_lib_module_t *self)
static void _pop_menu_dictionary_goto_collection_back(GtkWidget *menuitem, dt_lib_module_t *self)
static gboolean _completion_match_func(GtkEntryCompletion *completion, const gchar *key, GtkTreeIter *iter, gpointer user_data)
static void _pop_menu_dictionary_set_as_tag(GtkWidget *menuitem, dt_lib_module_t *self)
static gboolean _is_user_tag(GtkTreeModel *model, GtkTreeIter *iter)
static void _pop_menu_dictionary_create_tag(GtkWidget *menuitem, dt_lib_module_t *self)
static void _attached_view_combo_changed(GtkWidget *combo, dt_lib_module_t *self)
static void _show_tag_on_view(GtkTreeView *view, const char *tagname, const gboolean needle, const gboolean select)
static void _confidence_changed(GtkSpinButton *spin, dt_lib_module_t *self)
static void _refresh_completion_store(dt_lib_module_t *self)
static void _init_treeview(dt_lib_module_t *self, const int which)
static void _collection_updated_callback(gpointer instance, dt_collection_change_t query_change, dt_collection_properties_t changed_property, gpointer imgs, int next, dt_lib_module_t *self)
uint32_t container(dt_lib_module_t *self)
static void _postponed_update(dt_lib_module_t *self)
@ DT_LIB_TAGGING_COL_SYNONYM
@ DT_LIB_TAGGING_NUM_COLS
@ DT_LIB_TAGGING_COL_VISIBLE
@ DT_LIB_TAGGING_COL_PATH
@ DT_LIB_TAGGING_COL_COUNT
@ DT_LIB_TAGGING_COL_FLAGS
#define FLOATING_ENTRY_WIDTH
static void _reset_sel_on_path(GtkTreeModel *model, GtkTreeIter *iter, const gboolean root)
static void _pop_menu_attached_detach_all(GtkWidget *menuitem, dt_lib_module_t *self)
static gboolean _selection_get_first(GtkTreeSelection *selection, GtkTreeModel **model, GtkTreeIter *iter)
static void _delete_tagids(GList *tagids, dt_lib_module_t *self)
static int _get_recent_tags_list_length()
static void _refresh_suggestions(dt_lib_module_t *self)
static void _toggle_suggestion_button_callback(GtkToggleButton *source, dt_lib_module_t *self)
static void _event_dnd_received(GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *selection_data, guint target_type, guint time, dt_lib_module_t *self)
static void _tree_selection_changed(GtkTreeSelection *treeselection, gpointer data)
static void _event_dnd_end(GtkWidget *widget, GdkDragContext *context, dt_lib_module_t *self)
static void _pop_menu_dictionary_goto_tag_collection(GtkWidget *menuitem, dt_lib_module_t *self)
void gui_init(dt_lib_module_t *self)
static gboolean _lib_tagging_tag_key_press(GtkWidget *entry, GdkEventKey *event, dt_lib_module_t *self)
static gboolean _lib_tagging_tag_show_accel(GtkAccelGroup *accel_group, GObject *accelerable, guint keyval, GdkModifierType mods, gpointer user_data)
Open the floating quick-tag entry used by the historical tagging shortcut, now registered in the acti...
static void _delete_tree_path(GtkTreeModel *model, GtkTreeIter *iter, gboolean root, gboolean tree)
const char ** views(dt_lib_module_t *self)
static gboolean _lib_tagging_tag_destroy(GtkWidget *widget, GdkEvent *event, gpointer user_data)
static void _calculate_sel_on_path(GtkTreeModel *model, GtkTreeIter *iter, const gboolean root)
static gboolean _attached_delete_icon_activated(GtkCellRenderer *cell, GdkEvent *event, GtkWidget *treeview, const gchar *path_str, GdkRectangle *background, GdkRectangle *cell_area, GtkCellRendererState flags, gpointer user_data)
static void _tree_tagname_show_dictionary(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
static gboolean _click_on_view_dictionary(GtkWidget *view, GdkEventButton *event, dt_lib_module_t *self)
static gboolean _dnd_expand_timeout(dt_lib_module_t *self)
static void _raise_signal_tag_changed(dt_lib_module_t *self)
static void _save_last_tag_used(const char *tags, dt_lib_tagging_t *d)
static gboolean _event_dnd_motion(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, dt_lib_module_t *self)
static gboolean _match_selected_func(GtkEntryCompletion *completion, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
static void _pop_menu_attached(GtkWidget *treeview, GdkEventButton *event, dt_lib_module_t *self)
static void _pop_menu_dictionary_copy_tag(GtkWidget *menuitem, dt_lib_module_t *self)
static gboolean _find_tag_iter_tagid(GtkTreeModel *model, GtkTreeIter *iter, const gint tagid)
static void _update_atdetach_buttons(dt_lib_module_t *self)
static void _sort_dictionary_list(dt_lib_module_t *self, gboolean force)
static void _show_iter_on_view(GtkTreeView *view, GtkTreeIter *iter, const gboolean select)
static gboolean _click_on_view_attached(GtkWidget *view, GdkEventButton *event, dt_lib_module_t *self)
GList * dt_util_str_to_glist(const gchar *separator, const gchar *text)
gchar * dt_util_glist_to_str(const gchar *separator, GList *items)
gchar * dt_util_dstrcat(gchar *str, const gchar *format,...)
@ DT_UI_CONTAINER_PANEL_LEFT_CENTER