153#include <glib/gi18n.h>
154#include <glib/gstdio.h>
159#include <gio/gunixmounts.h>
161#ifdef GDK_WINDOWING_QUARTZ
168#define PARAM_STRING_SIZE 256
283static void tag_changed(gpointer instance, gpointer self);
330static const char *
const OP_TOKENS[] = {
"",
"<",
"<=",
">",
">=",
"<>" };
331static const char *
const OP_LABELS[] = {
"=",
"<",
"≤",
">",
"≥",
"≠" };
332#define COLLECT_N_OPS ((int)G_N_ELEMENTS(OP_TOKENS))
338 *
value = text ? text :
"";
340 if(g_str_has_prefix(text,
"<="))
345 else if(g_str_has_prefix(text,
">="))
350 else if(g_str_has_prefix(text,
"<>"))
355 else if(g_str_has_prefix(text,
"<"))
360 else if(g_str_has_prefix(text,
">"))
365 else if(g_str_has_prefix(text,
"="))
370 while(**
value ==
' ') (*value)++;
384 static const char *
v[] = {
"lighttable",
"map",
"print", NULL };
399 const int old_version,
int *new_version,
size_t *new_size)
401 if(old_version == 1 || old_version == 2)
414#define CLEAR_PARAMS(r) \
416 memset(¶ms, 0, sizeof(params)); \
418 params.rule[0].mode = 0; \
419 params.rule[0].item = r; \
422 GDateTime *now = g_date_time_new_now_local();
423 char *datetime_today = g_date_time_format(now,
"%Y:%m:%d");
424 GDateTime *gdt = g_date_time_add_days(now, -1);
425 char *datetime_24hrs = g_date_time_format(gdt,
"> %Y:%m:%d %H:%M");
426 g_date_time_unref(gdt);
427 gdt = g_date_time_add_days(now, -30);
428 char *datetime_30d = g_date_time_format(gdt,
"> %Y:%m:%d");
429 g_date_time_unref(gdt);
430 g_date_time_unref(now);
480 snprintf(
k,
sizeof(
k),
"plugins/lighttable/collect/item%1d",
n);
487 snprintf(
k,
sizeof(
k),
"plugins/lighttable/collect/item%1d",
n);
494 snprintf(
k,
sizeof(
k),
"plugins/lighttable/collect/mode%1d",
n);
501 snprintf(
k,
sizeof(
k),
"plugins/lighttable/collect/mode%1d",
n);
508 snprintf(
k,
sizeof(
k),
"plugins/lighttable/collect/recursive%1d",
n);
515 snprintf(
k,
sizeof(
k),
"plugins/lighttable/collect/recursive%1d",
n);
522 snprintf(
k,
sizeof(
k),
"plugins/lighttable/collect/string%1d",
n);
529 s[strlen(s) - 1] =
'\0';
539 snprintf(
k,
sizeof(
k),
"plugins/lighttable/collect/string%1d",
n);
548 const char *val = gtk_entry_get_text(GTK_ENTRY(dr->
text));
553 s = g_strconcat(
OP_TOKENS[idx], val, NULL);
565 const gboolean folders_visible =
d->folders_controls && gtk_widget_get_visible(
d->folders_controls);
578 g_signal_handlers_block_matched(dr->
text, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
entry_changed, NULL);
579 g_signal_handlers_block_matched(dr->
op_combo, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
_op_changed, NULL);
586 gtk_combo_box_set_active(GTK_COMBO_BOX(dr->
op_combo), idx);
587 gtk_entry_set_text(GTK_ENTRY(dr->
text), val);
591 gtk_entry_set_text(GTK_ENTRY(dr->
text), text);
594 gtk_editable_set_position(GTK_EDITABLE(dr->
text), -1);
597 g_signal_handlers_unblock_matched(dr->
op_combo, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
_op_changed, NULL);
598 g_signal_handlers_unblock_matched(dr->
text, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
entry_changed, NULL);
618 d->has_last_folders =
TRUE;
621 d->has_last_folders =
FALSE;
643 return d->rule +
d->active_rule;
649 d->active_rule = CLAMP(
d->active_rule, 0,
d->nb_rules - 1);
658 for(
int i = 0;
i <
n;
i++)
666 && !g_str_has_suffix(s,
"*") && !g_str_has_suffix(s,
"%"))
668 gchar *s2 = g_strconcat(s,
"*", NULL);
682 void *
p = malloc(*
size);
690 for(uint32_t
i = 0;
i <
p->rules;
i++)
737#define ADD_COLLECT_ENTRY(value) \
738 dt_bauhaus_combobox_add_full(w, dt_collection_name(value), DT_BAUHAUS_COMBOBOX_ALIGN_RIGHT, \
739 GUINT_TO_POINTER(value + 1), NULL, TRUE)
750 gchar *setting = g_strdup_printf(
"plugins/lighttable/metadata/%s_flag", name_);
779#undef ADD_COLLECT_ENTRY
789 for(; *list; list++) length++;
799 char **tokens = g_strsplit(path, G_DIR_SEPARATOR_S, -1);
802 if(!(g_ascii_isalpha(tokens[0][0]) && tokens[0][strlen(tokens[0]) - 1] ==
':'))
809 const unsigned int size = g_strv_length(tokens);
810 result = malloc(
sizeof(
char *) *
size);
811 for(
unsigned int i = 0;
i <
size;
i++) result[
i] = tokens[
i + 1];
844 gchar *folded = g_utf8_casefold(tag, -1);
845 const size_t len = strlen(folded);
846 char *result = g_malloc(len + 2);
847 if(!g_strcmp0(tag, _(
"not tagged")))
849 else if(g_str_has_prefix(tag,
"darktable|"))
853 memcpy(result + 1, folded, len + 1);
854 for(
char *iter = result + 1; *iter; iter++)
855 if(*iter ==
'|') *iter =
'\1';
861 GtkTreeIter *iter, gpointer data)
867 g_object_set(renderer,
"text",
name, NULL);
870 gchar *coltext = g_strdup_printf(
"%s (%d)",
name, count);
871 g_object_set(renderer,
"text", coltext, NULL);
882 gboolean visible =
FALSE;
883 gboolean was_visible;
886 gchar *haystack = g_utf8_strdown(str, -1);
894 gchar *
operator, * number, *number2;
898 const float nb1 = g_strtod(number, NULL);
899 const float nb2 = g_strtod(haystack, NULL);
900 if(
operator&& strcmp(
operator,
">") == 0)
901 visible = (nb2 > nb1);
902 else if(
operator&& strcmp(
operator,
">=") == 0)
903 visible = (nb2 >= nb1);
904 else if(
operator&& strcmp(
operator,
"<") == 0)
905 visible = (nb2 < nb1);
906 else if(
operator&& strcmp(
operator,
"<=") == 0)
907 visible = (nb2 <= nb1);
908 else if(
operator&& strcmp(
operator,
"<>") == 0)
909 visible = (nb1 != nb2);
910 else if(
operator&& number2 && strcmp(
operator,
"[]") == 0)
912 const float nb3 = g_strtod(number2, NULL);
913 visible = (nb2 >= nb1 && nb2 <= nb3);
916 visible = (nb1 == nb2);
925 for(
const GList *l = list; l; l = g_list_next(l))
927 const char *
name = (
char *)l->data;
928 if((visible = (g_strrstr(haystack,
name + (
name[0] ==
'%')) != NULL)))
break;
934 if(needle[0] ==
'%') needle++;
938 visible = (strchr(haystack, needle[0]) != NULL);
940 visible = (g_strrstr(haystack, needle) != NULL);
945 if(visible != was_visible)
956 if(g_str_has_suffix(needle,
"%")) needle[strlen(needle) - 1] =
'\0';
957 if(g_str_has_suffix(haystack,
"%")) haystack[strlen(haystack) - 1] =
'\0';
960 if(g_str_has_suffix(needle,
"*")) needle[strlen(needle) - 1] =
'\0';
961 if(g_str_has_suffix(needle,
"|")) needle[strlen(needle) - 1] =
'\0';
962 if(g_str_has_suffix(haystack,
"|")) haystack[strlen(haystack) - 1] =
'\0';
966 if(g_str_has_suffix(needle,
"*")) needle[strlen(needle) - 1] =
'\0';
967 if(g_str_has_suffix(needle,
"/")) needle[strlen(needle) - 1] =
'\0';
968 if(g_str_has_suffix(haystack,
"/")) haystack[strlen(haystack) - 1] =
'\0';
972 if(g_str_has_suffix(needle,
":")) needle[strlen(needle) - 1] =
'\0';
973 if(g_str_has_suffix(haystack,
":")) haystack[strlen(haystack) - 1] =
'\0';
982 gboolean cur_state, visible;
989 gchar *haystack = g_utf8_strdown(str, -1),
990 *needle = g_utf8_strdown(gtk_entry_get_text(GTK_ENTRY(dr->
text)), -1);
992 visible = (g_strrstr(haystack, needle) != NULL);
1004 GtkTreeIter parent, child = *iter;
1007 while(gtk_tree_model_iter_parent(
model, &parent, &child))
1029 for(
const char *
p = s;
p && *
p &&
n < 14;
p++)
1030 if(g_ascii_isdigit(*
p)) digits[
n++] = *
p;
1031 while(
n < 14) digits[
n++] = pad;
1033 return g_ascii_strtoull(digits, NULL, 10);
1049 const guint64 node_lo =
_date_key(str,
'0');
1050 const guint64 node_hi =
_date_key(str,
'9');
1052 const gboolean visible = (node_hi >=
r->lo) && (node_lo <= r->hi);
1057static gboolean
list_select(GtkTreeModel *
model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
1064 gchar *haystack = g_utf8_strdown(str, -1);
1065 gchar *needle = g_utf8_strdown(gtk_entry_get_text(GTK_ENTRY(dr->
text)), -1);
1066 if(strcmp(haystack, needle) == 0)
1068 gtk_tree_selection_select_path(gtk_tree_view_get_selection(
d->view), path);
1069 gtk_tree_view_scroll_to_cell(
d->view, path, NULL,
FALSE, 0.2, 0);
1077static gboolean
range_select(GtkTreeModel *
model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
1083 gchar *haystack = g_utf8_strdown(str, -1);
1084 gchar *needle = range->
path1 ? g_utf8_strdown(range->
stop, -1) : g_utf8_strdown(range->
start, -1);
1085 if(strcmp(haystack, needle) == 0)
1089 range->
path2 = gtk_tree_path_copy(path);
1096 range->
path1 = gtk_tree_path_copy(path);
1104static gboolean
tree_expand(GtkTreeModel *
model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
1108 gchar *str = NULL, *txt = NULL;
1109 gboolean startwildcard =
FALSE, expanded =
FALSE;
1112 gchar *haystack = g_utf8_strdown(str, -1);
1113 gchar *needle = g_utf8_strdown(gtk_entry_get_text(GTK_ENTRY(dr->
text)), -1);
1114 gchar *txt2 = g_utf8_strdown(txt, -1);
1121 if(g_str_has_prefix(needle,
"%")) startwildcard =
TRUE;
1124 if(reveal && g_strrstr(txt2, needle) != NULL)
1126 gtk_tree_view_expand_to_path(
d->view, path);
1130 if(strlen(needle) == 0)
1134 else if(strcmp(haystack, needle) == 0)
1143 && !gtk_tree_view_row_expanded(
d->view, path)
1144 && gtk_tree_model_iter_has_child(
model, iter);
1145 gtk_tree_view_expand_to_path(
d->view, path);
1146 gtk_tree_selection_select_path(gtk_tree_view_get_selection(
d->view), path);
1147 if(!reveals_children) gtk_tree_view_scroll_to_cell(
d->view, path, NULL,
FALSE, 0.2, 0);
1150 else if(startwildcard && g_strrstr(haystack, needle + 1) != NULL)
1152 gtk_tree_view_expand_to_path(
d->view, path);
1157 gtk_tree_view_expand_to_path(
d->view, path);
1174 GtkTreeIter child, iter;
1176 while(gtk_tree_model_iter_n_children(
model, level > 0 ? &iter : NULL) > 0)
1186 if(!gtk_tree_model_iter_parent(
model, &child, &iter)) level = 0;
1191 if(gtk_tree_model_iter_n_children(
model, level > 0 ? &iter : NULL) != 1)
break;
1192 gtk_tree_model_iter_children(
model, &child, level > 0 ? &iter : NULL);
1196 if(level <= 0)
return NULL;
1198 if(gtk_tree_model_iter_n_children(
model, &iter) == 0 && gtk_tree_model_iter_parent(
model, &child, &iter))
1199 return gtk_tree_model_get_path(
model, &child);
1200 return gtk_tree_model_get_path(
model, &iter);
1209 GtkTreeModel *filter = gtk_tree_model_filter_new(
model, path);
1210 gtk_tree_path_free(path);
1220 GHashTable *set = (GHashTable *)data;
1221 GtkTreeModel *
model = gtk_tree_view_get_model(
view);
1223 if(gtk_tree_model_get_iter(
model, &it, path))
1227 if(
p) g_hash_table_add(set,
p);
1242 if(
p && g_hash_table_contains(c->set,
p)) gtk_tree_view_expand_to_path(c->d->view, path);
1253 return g_strsplit(
name,
"|", -1);
1261 GtkTreeIter parent, child = *leaf;
1262 while(gtk_tree_model_iter_parent(
model, &parent, &child))
1274 GtkTreeIter *uncategorized, guint *index,
int count)
1276 if(strchr(
name,
'|') != NULL)
return FALSE;
1278 char *next_name = g_strdup(next_name_raw ? next_name_raw :
"");
1279 if(strlen(next_name) >= strlen(
name) + 1 && next_name[strlen(
name)] ==
'|') next_name[strlen(
name)] =
'\0';
1280 const gboolean leaf_toplevel = g_strcmp0(next_name,
name) && g_strcmp0(
name, _(
"not tagged"));
1282 if(!leaf_toplevel)
return FALSE;
1284 if(!uncategorized->stamp)
1306 GList *sorted_names = NULL;
1308 for(GList *
v = values;
v;
v = g_list_next(
v))
1315 char *name_folded = g_utf8_casefold(
name, -1);
1316 char *name_folded_slash = g_strconcat(name_folded, G_DIR_SEPARATOR_S, NULL);
1317 collate_key = g_utf8_collate_key_for_filename(name_folded_slash, -1);
1329 sorted_names = g_list_prepend(sorted_names, tuple);
1334 if(!
dt_conf_get_bool(
"plugins/collect/descending")) sorted_names = g_list_reverse(sorted_names);
1335 return sorted_names;
1342 gboolean no_uncategorized,
const char *format_separator)
1345 GtkTreeIter uncategorized = { 0 };
1346 char **last_tokens = NULL;
1347 int last_tokens_length = 0;
1348 GtkTreeIter last_parent = { 0 };
1355 const int count = tuple->
count;
1366 GtkTreeIter parent = last_parent;
1368 int common_length = 0;
1371 while(tokens[common_length] && last_tokens[common_length]
1372 && !g_strcmp0(tokens[common_length], last_tokens[common_length]))
1374 for(
int i = common_length;
i < last_tokens_length;
i++)
1376 gtk_tree_model_iter_parent(
model, &parent, &last_parent);
1377 last_parent = parent;
1385 for(
int i = 0;
i < common_length;
i++) pth =
dt_util_dstrcat(pth, format_separator, tokens[
i]);
1387 for(
char **token = &tokens[common_length]; *token; token++)
1393 gchar *pth2 = g_strdup(pth);
1394 pth2[strlen(pth2) - 1] =
'\0';
1395 const gboolean leaf = !*(token + 1);
1396 gtk_tree_store_insert_with_values(
store, &iter, common_length > 0 ? &parent : NULL, 0,
1412 if(last_tokens) g_strfreev(last_tokens);
1413 last_tokens = tokens;
1414 last_parent = parent;
1415 last_tokens_length = tokens_length;
1417 g_strfreev(last_tokens);
1425 char *format_separator =
"";
1430 format_separator =
"%s" G_DIR_SEPARATOR_S;
1434 format_separator =
"%s|";
1442 format_separator =
"%s:";
1448 GtkTreeModel *
model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(
d->treefilter));
1449 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(
model), GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
1450 GTK_SORT_ASCENDING);
1452 GHashTable *saved_expanded = NULL;
1454 if(
d->view_rule != property)
1457 saved_expanded = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
1460 g_object_ref(
model);
1461 g_object_unref(
d->treefilter);
1462 gtk_tree_view_set_model(GTK_TREE_VIEW(
d->view), NULL);
1463 gtk_tree_store_clear(GTK_TREE_STORE(
model));
1464 gtk_widget_hide(GTK_WIDGET(
d->view));
1476 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
d->view));
1477 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
1479 gtk_tree_view_set_model(GTK_TREE_VIEW(
d->view),
d->treefilter);
1480 gtk_widget_set_no_show_all(GTK_WIDGET(
d->view),
FALSE);
1481 gtk_widget_show_all(GTK_WIDGET(
d->view));
1483 g_object_unref(
model);
1484 d->view_rule = property;
1493 GRegex *regex = g_regex_new(
"^\\s*\\[\\s*(.*)\\s*;\\s*(.*)\\s*\\]\\s*$", 0, 0, NULL);
1494 GMatchInfo *match_info;
1495 g_regex_match_full(regex, gtk_entry_get_text(GTK_ENTRY(dr->
text)), -1, 0, 0, &match_info, NULL);
1496 if(g_match_info_get_match_count(match_info) == 3)
1499 range->
start = g_match_info_fetch(match_info, 2);
1500 range->
stop = g_match_info_fetch(match_info, 1);
1502 g_match_info_free(match_info);
1503 g_regex_unref(regex);
1518 gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(
d->view));
1523 gtk_tree_view_expand_all(
d->view);
1525 gtk_tree_view_collapse_all(
d->view);
1526 else if(saved_expanded)
1531 if(saved_expanded) g_hash_table_destroy(saved_expanded);
1536 gtk_tree_model_foreach(
d->treefilter, (GtkTreeModelForeachFunc)
range_select, range);
1538 gtk_tree_selection_select_range(gtk_tree_view_get_selection(
d->view), range->
path1, range->
path2);
1541 gtk_tree_path_free(range->
path1);
1542 gtk_tree_path_free(range->
path2);
1546 gtk_tree_model_foreach(
d->treefilter, (GtkTreeModelForeachFunc)
tree_expand, dr);
1557 GtkTreeModel *
model = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(
d->listfilter));
1558 if(
d->view_rule != property)
1561 g_object_unref(
d->listfilter);
1562 g_object_ref(
model);
1563 gtk_tree_view_set_model(GTK_TREE_VIEW(
d->view), NULL);
1564 gtk_list_store_clear(GTK_LIST_STORE(
model));
1565 gtk_widget_hide(GTK_WIDGET(
d->view));
1568 for(GList *
v = values;
v;
v = g_list_next(
v))
1577 gchar *text = g_strdup(
value);
1579 while(!g_utf8_validate(ptr, -1, (
const gchar **)&ptr)) ptr[0] =
'?';
1580 gchar *escaped_text = g_markup_escape_text(text, -1);
1582 gtk_list_store_append(GTK_LIST_STORE(
model), &iter);
1596 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(
d->view));
1598 gtk_tree_selection_set_mode(selection, multi ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE);
1600 gtk_tree_view_set_model(GTK_TREE_VIEW(
d->view),
d->listfilter);
1601 gtk_widget_set_no_show_all(GTK_WIDGET(
d->view),
FALSE);
1602 gtk_widget_show_all(GTK_WIDGET(
d->view));
1603 g_object_unref(
model);
1604 d->view_rule = property;
1617 gchar *needle = g_utf8_strdown(gtk_entry_get_text(GTK_ENTRY(dr->
text)), -1);
1618 if(g_str_has_suffix(needle,
"%")) needle[strlen(needle) - 1] =
'\0';
1624 gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(
d->view));
1628 GRegex *regex = g_regex_new(
"^\\s*\\[\\s*(.*)\\s*;\\s*(.*)\\s*\\]\\s*$", 0, 0, NULL);
1629 GMatchInfo *match_info;
1630 g_regex_match_full(regex, gtk_entry_get_text(GTK_ENTRY(dr->
text)), -1, 0, 0, &match_info, NULL);
1631 const int match_count = g_match_info_get_match_count(match_info);
1632 if(match_count == 3)
1635 range->
start = g_match_info_fetch(match_info, 1);
1636 range->
stop = g_match_info_fetch(match_info, 2);
1637 gtk_tree_model_foreach(
d->listfilter, (GtkTreeModelForeachFunc)
range_select, range);
1639 gtk_tree_selection_select_range(gtk_tree_view_get_selection(
d->view), range->
path1, range->
path2);
1642 gtk_tree_path_free(range->
path1);
1643 gtk_tree_path_free(range->
path2);
1647 gtk_tree_model_foreach(
d->listfilter, (GtkTreeModelForeachFunc)
list_select, dr);
1648 g_match_info_free(match_info);
1649 g_regex_unref(regex);
1652 gtk_tree_model_foreach(
d->listfilter, (GtkTreeModelForeachFunc)
list_select, dr);
1676 gchar *
n = g_strconcat(text,
"|%", NULL);
1682 gchar *
n = g_strconcat(text,
"*", NULL);
1700 gboolean descending =
FALSE;
1715 GtkTreeModel *
model = NULL;
1716 GtkTreeSelection *selection = gtk_tree_view_get_selection(
view);
1717 const int n_selected = gtk_tree_selection_count_selected_rows(selection);
1718 if(n_selected < 1)
return;
1720 GList *sels = gtk_tree_selection_get_selected_rows(selection, &
model);
1721 GtkTreePath *path1 = (GtkTreePath *)sels->data;
1722 if(!gtk_tree_model_get_iter(
model, &iter, path1))
1724 g_list_free_full(sels, (GDestroyNotify)gtk_tree_path_free);
1734 gboolean order_request =
FALSE;
1738 if(text && strlen(text) > 0)
1744 GtkTreePath *path2 = (GtkTreePath *)g_list_last(sels)->data;
1745 if(gtk_tree_model_get_iter(
model, &iter2, path2))
1750 ? g_strdup_printf(
"[%s;%s]", text2, text)
1751 : g_strdup_printf(
"[%s;%s]", text, text2);
1764 if(gtk_tree_model_iter_has_child(
model, &iter))
1772 g_list_free_full(sels, (GDestroyNotify)gtk_tree_path_free);
1774 g_signal_handlers_block_matched(active_rule->
text, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
entry_changed, NULL);
1775 gtk_entry_set_text(GTK_ENTRY(active_rule->
text), text);
1776 gtk_editable_set_position(GTK_EDITABLE(active_rule->
text), -1);
1777 g_signal_handlers_unblock_matched(active_rule->
text, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
entry_changed, NULL);
1827 GtkTreeModel *
model = NULL;
1828 GList *paths = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(
d->view), &
model);
1830 for(GList *l = paths; l; l = g_list_next(l))
1833 if(gtk_tree_model_get_iter(
model, &it, (GtkTreePath *)l->data))
1840 g_list_free_full(paths, (GDestroyNotify)gtk_tree_path_free);
1841 return g_list_reverse(
out);
1848 GHashTable *seen = g_hash_table_new(g_direct_hash, g_direct_equal);
1850 for(GList *l = rows; l; l = g_list_next(l))
1856 for(GList *
i = ids;
i;
i = g_list_next(
i))
1857 if(!g_hash_table_contains(seen,
i->data))
1859 g_hash_table_add(seen,
i->data);
1860 out = g_list_prepend(
out,
i->data);
1864 g_hash_table_destroy(seen);
1865 return g_list_reverse(
out);
1869static gboolean
_confirm(
const char *title,
const char *message)
1872 GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION,
1873 GTK_BUTTONS_YES_NO,
"%s", message);
1874 gtk_window_set_title(GTK_WINDOW(dialog), title);
1875#ifdef GDK_WINDOWING_QUARTZ
1878 const gint
res = gtk_dialog_run(GTK_DIALOG(dialog));
1879 gtk_widget_destroy(dialog);
1880 return res == GTK_RESPONSE_YES;
1883static gchar *
_ask_text(
const char *title,
const char *initial)
1887 = gtk_dialog_new_with_buttons(title, GTK_WINDOW(win), GTK_DIALOG_DESTROY_WITH_PARENT, _(
"_cancel"),
1888 GTK_RESPONSE_CANCEL, _(
"_ok"), GTK_RESPONSE_ACCEPT, NULL);
1889 GtkWidget *area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1891 gtk_entry_set_activates_default(GTK_ENTRY(entry),
TRUE);
1892 if(initial) gtk_entry_set_text(GTK_ENTRY(entry), initial);
1893 gtk_box_pack_start(GTK_BOX(area), entry,
TRUE,
TRUE, 0);
1894 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
1896 gtk_widget_show_all(dialog);
1897#ifdef GDK_WINDOWING_QUARTZ
1900 gchar *result = NULL;
1901 if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
1903 const gchar *
t = gtk_entry_get_text(GTK_ENTRY(entry));
1904 if(
t && *
t) result = g_strdup(
t);
1906 gtk_widget_destroy(dialog);
1922 g_list_free(imgids);
1929 const int n = g_list_length(rows);
1930 const gboolean single = (
n == 1);
1933 GtkFileChooserNative *
fc = gtk_file_chooser_native_new(
1934 single ? _(
"select the new location of this folder") : _(
"select the new parent folder"), GTK_WINDOW(win),
1935 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, _(
"_open"), _(
"_cancel"));
1936 if(single && first->
path) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(
fc), first->
path);
1938 if(gtk_native_dialog_run(GTK_NATIVE_DIALOG(
fc)) == GTK_RESPONSE_ACCEPT)
1940 gchar *uri = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(
fc));
1941 gchar *chosen = g_filename_from_uri(uri, NULL, NULL);
1945 for(GList *l = rows; l; l = g_list_next(l))
1953 gchar *base = g_path_get_basename(
r->path);
1954 gchar *dest = g_build_filename(chosen, base, NULL);
1974 const int n = g_list_length(rows);
1975 gchar *msg = g_strdup_printf(ngettext(
"Delete %d tag and detach it from all images?",
1976 "Delete %d tags and detach them from all images?",
n),
1978 const gboolean ok =
_confirm(_(
"delete tags"), msg);
1982 for(GList *l = rows; l; l = g_list_next(l))
2000 gchar *newname =
_ask_text(_(
"rename tag"),
r->path);
2038 const int n = g_list_length(
p->imgids);
2039 const float total = (
n > 0) ? (
float)(
n * (
max + 1)) : 1.0f;
2044 const int32_t imgid = GPOINTER_TO_INT(l->data);
2123 const collect_action_t *act = g_object_get_data(G_OBJECT(mi),
"collect-action");
2125 if(rows) act->
run(
d, rows);
2131 const int property =
d->view_rule;
2132 const int n = gtk_tree_selection_count_selected_rows(gtk_tree_view_get_selection(
d->view));
2137 for(
size_t i = 0;
i < G_N_ELEMENTS(
ACTIONS);
i++)
2140 if(!act->
enabled(property,
n))
continue;
2141 if(!act->
multi &&
n > 1)
continue;
2143 g_object_set_data(G_OBJECT(mi),
"collect-action", (gpointer)act);
2145 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
2150 gtk_widget_show_all(menu);
2151 gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent *)event);
2154 gtk_widget_destroy(menu);
2167 const int n = g_list_length(imgs);
2168 gchar *msg = g_strdup_printf(ngettext(
"Physically move %d image to\n%s ?\n\nFiles are moved on disk.",
2169 "Physically move %d images to\n%s ?\n\nFiles are moved on disk.",
n),
2171 const gboolean ok =
_confirm(_(
"move images"), msg);
2173 if(!ok)
return FALSE;
2178 const int32_t filmid = film.
id;
2187 for(GList *l = imgs; l; l = g_list_next(l))
2188 if(
dt_image_move(GPOINTER_TO_INT(l->data), filmid) != -1) moved++;
2205 if(!tagid)
return FALSE;
2230 const int imgs_nb = gtk_selection_data_get_length(sel) / (int)
sizeof(uint32_t);
2231 if(imgs_nb <= 0)
return FALSE;
2233 GtkTreePath *path = NULL;
2234 if(!gtk_tree_view_get_path_at_pos(tree,
x, y, &path, NULL, NULL, NULL))
return FALSE;
2236 GtkTreeModel *
model = gtk_tree_view_get_model(tree);
2238 gboolean ok =
FALSE;
2239 if(gtk_tree_model_get_iter(
model, &iter, path))
2241 const uint32_t *imgt = (
const uint32_t *)gtk_selection_data_get_data(sel);
2243 for(
int i = 0;
i < imgs_nb;
i++) imgs = g_list_prepend(imgs, GINT_TO_POINTER((
int)imgt[
i]));
2245 gchar *rowpath = NULL;
2251 gtk_tree_path_free(path);
2268 GtkTreeView *tree = GTK_TREE_VIEW(widget);
2269 GtkTreePath *path = NULL;
2271 && gtk_tree_view_get_path_at_pos(tree,
x, y, &path, NULL, NULL, NULL))
2273 gtk_tree_view_set_drag_dest_row(tree, path, GTK_TREE_VIEW_DROP_INTO_OR_BEFORE);
2274 gtk_tree_path_free(path);
2275 gdk_drag_status(context, GDK_ACTION_MOVE, time);
2279 gtk_tree_view_set_drag_dest_row(tree, NULL, 0);
2280 gdk_drag_status(context, 0, time);
2287 gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW(widget), NULL, 0);
2291 GtkSelectionData *selection_data, guint target_type, guint time,
2294 GtkTreeView *tree = GTK_TREE_VIEW(widget);
2295 g_signal_stop_emission_by_name(tree,
"drag-data-received");
2296 gtk_tree_view_set_drag_dest_row(tree, NULL, 0);
2300 d->suppress_row_activated =
TRUE;
2304 gtk_drag_finish(context, success,
FALSE, time);
2311 if(event->button != 3 || event->type != GDK_BUTTON_PRESS)
return FALSE;
2314 GtkTreeView *
view = GTK_TREE_VIEW(treeview);
2315 GtkTreeSelection *sel = gtk_tree_view_get_selection(
view);
2319 GtkTreePath *path = NULL;
2320 if(gtk_tree_view_get_path_at_pos(
view, (gint)event->x, (gint)event->y, &path, NULL, NULL, NULL) && path)
2322 if(!gtk_tree_selection_path_is_selected(sel, path))
2324 gtk_tree_selection_unselect_all(sel);
2325 gtk_tree_selection_select_path(sel, path);
2327 gtk_tree_path_free(path);
2331 if(gtk_tree_selection_count_selected_rows(sel) < 1)
return FALSE;
2345 if(
d->suppress_row_activated)
return;
2347 GdkEvent *ev = gtk_get_current_event();
2351 = (ev && (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_BUTTON_RELEASE))
2352 ? (GdkEventButton *)ev
2355 if(ev) gdk_event_free(ev);
2361 gtk_tree_view_scroll_to_cell(
view, path, NULL,
TRUE, 0.0, 0.0);
2372 gtk_widget_set_tooltip_text(dr->
text, _(
"use <, <=, >, >=, <>, =, [;] as operators"));
2374 gtk_widget_set_tooltip_text(dr->
text, _(
"use <, <=, >, >=, <>, =, [;] as operators\n"
2375 "star rating: 0-5\n"
2376 "rejected images: -1"));
2378 gtk_widget_set_tooltip_text(dr->
text,
2379 _(
"use <, <=, >, >=, <>, =, [;] as operators\n"
2380 "type dates in the form: YYYY:MM:DD hh:mm:ss.sss (only the year is mandatory)"));
2383 gtk_widget_set_tooltip_text(dr->
text, _(
"use `%' as wildcard and `,' to separate values"));
2386 gtk_widget_set_tooltip_text(dr->
text, _(
"use `%' as wildcard\n"
2387 "click to include hierarchy + sub-hierarchies (suffix `*')\n"
2388 "shift+click to include only the current hierarchy (no suffix)\n"
2389 "ctrl+click to include only sub-hierarchies (suffix `|%')"));
2392 gtk_widget_set_tooltip_text(dr->
text, _(
"use `%' as wildcard\n"
2393 "click to include location + sub-locations (suffix `*')\n"
2394 "shift+click to include only the current location (no suffix)\n"
2395 "ctrl+click to include only sub-locations (suffix `|%')"));
2398 gtk_widget_set_tooltip_text(dr->
text,
2399 _(
"use `%' as wildcard and append `*' to match sub-folders"));
2402 gtk_widget_set_tooltip_text(dr->
text, _(
"use `%' as wildcard"));
2404 gchar *tip = gtk_widget_get_tooltip_text(dr->
text);
2405 gtk_widget_set_tooltip_text(GTK_WIDGET(dr->
combo), tip);
2419 c->active_rule = dr->
num;
2432 c->active_rule = dr->
num;
2437 const gboolean transferable
2441 g_signal_handlers_block_matched(dr->
text, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
entry_changed, NULL);
2442 gtk_entry_set_text(GTK_ENTRY(dr->
text),
"");
2443 g_signal_handlers_unblock_matched(dr->
text, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
entry_changed, NULL);
2447 g_signal_handlers_block_matched(dr->
op_combo, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
_op_changed, NULL);
2448 gtk_combo_box_set_active(GTK_COMBO_BOX(dr->
op_combo), 0);
2449 g_signal_handlers_unblock_matched(dr->
op_combo, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
_op_changed, NULL);
2455 if(c->folders_controls && gtk_widget_get_visible(c->folders_controls))
2468 dr->
reveal = transferable;
2495 GtkTreeModel *
model = gtk_tree_view_get_model(GTK_TREE_VIEW(c->view));
2496 if(gtk_tree_model_iter_n_children(
model, NULL) == 1)
2499 if(gtk_tree_model_get_iter_first(
model, &iter))
2503 g_signal_handlers_block_matched(dr->
text, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
entry_changed, NULL);
2504 gtk_entry_set_text(GTK_ENTRY(dr->
text), text);
2505 gtk_editable_set_position(GTK_EDITABLE(dr->
text), -1);
2506 g_signal_handlers_unblock_matched(dr->
text, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
entry_changed, NULL);
2520 c->active_rule = dr->
num;
2541 const gboolean desc = gtk_toggle_button_get_active(b);
2544 gtk_widget_queue_draw(GTK_WIDGET(b));
2561 dt_conf_set_int(
"show_folder_levels", (
int)gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin)));
2568 dt_conf_set_bool(
"plugins/lighttable/tagging/no_uncategorized", gtk_toggle_button_get_active(b));
2590 const dt_lib_collect_mode_t mode = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem),
"menuitem_mode"));
2597 c->active_rule = active;
2605 const int num = dr->
num + 1;
2606 if(num < MAX_RULES && num > 0)
2607 _rule_set_mode(num, GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menuitem),
"menuitem_mode")));
2619 const int new_count = active - 1;
2621 if(c->active_rule > dr->
num) c->active_rule--;
2622 if(c->active_rule >= new_count) c->active_rule = new_count - 1;
2650 if(event->button != 1)
return FALSE;
2656 mi = gtk_menu_item_new_with_label(_(
"clear this rule"));
2657 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
2658 g_signal_connect(G_OBJECT(mi),
"activate", G_CALLBACK(
menuitem_clear), dr);
2660 if(dr->
num == active - 1)
2662 mi = gtk_menu_item_new_with_label(_(
"narrow down search"));
2664 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
2665 g_signal_connect(G_OBJECT(mi),
"activate", G_CALLBACK(
menuitem_mode), dr);
2667 mi = gtk_menu_item_new_with_label(_(
"add more images"));
2669 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
2670 g_signal_connect(G_OBJECT(mi),
"activate", G_CALLBACK(
menuitem_mode), dr);
2672 mi = gtk_menu_item_new_with_label(_(
"exclude images"));
2674 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
2675 g_signal_connect(G_OBJECT(mi),
"activate", G_CALLBACK(
menuitem_mode), dr);
2677 else if(dr->
num < active - 1)
2679 mi = gtk_menu_item_new_with_label(_(
"change to: and"));
2681 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
2684 mi = gtk_menu_item_new_with_label(_(
"change to: or"));
2686 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
2689 mi = gtk_menu_item_new_with_label(_(
"change to: except"));
2691 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mi);
2695 gtk_widget_show_all(GTK_WIDGET(menu));
2696 gtk_menu_popup_at_pointer(GTK_MENU(menu), (GdkEvent *)event);
2738 gtk_button_set_label(GTK_BUTTON(dr->
button),
"-");
2739 gtk_widget_set_tooltip_text(GTK_WIDGET(dr->
button), _(
"clear this rule"));
2743 gtk_button_set_label(GTK_BUTTON(dr->
button),
"+");
2744 gtk_widget_set_tooltip_text(GTK_WIDGET(dr->
button), _(
"clear this rule or add new rules"));
2752 gtk_widget_set_tooltip_text(GTK_WIDGET(dr->
button), _(
"clear this rule"));
2760 gtk_widget_set_no_show_all(
d->rule[
i].hbox,
TRUE);
2761 gtk_widget_hide(
d->rule[
i].hbox);
2763 gtk_widget_set_no_show_all(
d->folders_controls,
TRUE);
2764 gtk_widget_hide(
d->folders_controls);
2765 gtk_widget_set_no_show_all(
d->collections_controls,
TRUE);
2766 gtk_widget_hide(
d->collections_controls);
2767 gtk_widget_set_no_show_all(
d->raw_box,
TRUE);
2768 gtk_widget_hide(
d->raw_box);
2769 gtk_widget_set_no_show_all(GTK_WIDGET(
d->view),
FALSE);
2770 gtk_widget_show(GTK_WIDGET(
d->view));
2787 if(
d->has_last_folders)
2789 item =
d->last_folders_item;
2804 gtk_widget_set_no_show_all(
d->rule[0].hbox,
FALSE);
2805 gtk_widget_show_all(
d->rule[0].hbox);
2806 gtk_widget_show(
d->rule[0].combo);
2807 gtk_widget_hide(
d->rule[0].button);
2808 gtk_entry_set_placeholder_text(GTK_ENTRY(
d->rule[0].text), _(
"Search a folder..."));
2812 gtk_widget_set_no_show_all(
d->folders_controls,
FALSE);
2813 gtk_widget_show_all(
d->folders_controls);
2816 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
d->sort_dir), desc);
2824 gtk_spin_button_set_value(GTK_SPIN_BUTTON(
d->folder_levels),
2842 gtk_widget_set_no_show_all(
d->rule[0].hbox,
FALSE);
2843 gtk_widget_show_all(
d->rule[0].hbox);
2844 gtk_widget_hide(
d->rule[0].combo);
2845 gtk_widget_hide(
d->rule[0].button);
2846 gtk_entry_set_placeholder_text(GTK_ENTRY(
d->rule[0].text), _(
"Search a collection..."));
2850 gtk_widget_set_no_show_all(
d->collections_controls,
FALSE);
2851 gtk_widget_show_all(
d->collections_controls);
2852 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
d->no_uncategorized),
2860 gtk_widget_set_no_show_all(
d->raw_box,
FALSE);
2861 gtk_widget_show_all(
d->raw_box);
2862 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
d->raw_check), raw);
2867 gtk_entry_set_text(GTK_ENTRY(
d->raw_entry), s ? s :
"");
2869 gtk_widget_show(
d->raw_entry);
2870 gtk_widget_set_no_show_all(GTK_WIDGET(
d->view),
TRUE);
2871 gtk_widget_hide(GTK_WIDGET(
d->view));
2876 gtk_widget_hide(
d->raw_entry);
2877 for(
int i = 0;
i <
d->nb_rules;
i++)
2881 gtk_widget_set_no_show_all(
d->rule[
i].hbox,
FALSE);
2882 gtk_widget_show_all(
d->rule[
i].hbox);
2883 gtk_widget_show(
d->rule[
i].combo);
2884 gtk_widget_show(
d->rule[
i].button);
2886 gtk_entry_set_placeholder_text(GTK_ENTRY(
d->rule[
i].text), _(
"Search..."));
2898 if(gtk_toggle_button_get_active(b))
2941 if(
d->view_rule != -1)
return;
2955 g_signal_handlers_block_matched(
d->notebook, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
_on_tab_switch, NULL);
2956 gtk_notebook_set_current_page(GTK_NOTEBOOK(
d->notebook), tab);
2957 g_signal_handlers_unblock_matched(
d->notebook, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
_on_tab_switch, NULL);
2977 gboolean refresh =
TRUE;
2981 for(
int i = 0;
i <
d->nb_rules;
i++)
3014 gboolean uses_tag =
FALSE;
3015 for(
int i = 0;
i <
d->nb_rules;
i++)
3035static void geotag_changed(gpointer instance, GList *imgs,
const int locid, gpointer self)
3094 self->
data = (
void *)
d;
3105 dt_ui_notebook_page(GTK_NOTEBOOK(
d->notebook), _(
"Folders"), _(
"Browse and manage the folders known to Ansel"));
3107 dt_ui_notebook_page(GTK_NOTEBOOK(
d->notebook), _(
"Queries"), _(
"Build arbitrary collections"));
3108 gtk_widget_show_all(
d->notebook);
3109 gtk_box_pack_start(GTK_BOX(self->
widget),
d->notebook,
TRUE,
TRUE, 0);
3110 gtk_notebook_set_scrollable(GTK_NOTEBOOK(
d->notebook),
TRUE);
3111 g_signal_connect(G_OBJECT(
d->notebook),
"switch_page", G_CALLBACK(
_on_tab_switch), self);
3118 d->rule[
i].lib_collect = (
void *)
d;
3121 d->rule[
i].hbox = GTK_WIDGET(box);
3122 gtk_box_pack_start(GTK_BOX(self->
widget), GTK_WIDGET(box),
TRUE,
TRUE, 0);
3123 gtk_widget_set_name(GTK_WIDGET(box),
"lib-dtbutton");
3128 g_signal_connect(G_OBJECT(
d->rule[
i].combo),
"value-changed", G_CALLBACK(
combo_changed),
d->rule +
i);
3129 gtk_box_pack_start(box,
d->rule[
i].combo,
FALSE,
FALSE, 0);
3132 gtk_box_pack_start(box, GTK_WIDGET(hbox),
FALSE,
FALSE, 0);
3135 d->rule[
i].op_combo = gtk_combo_box_text_new();
3137 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(
d->rule[
i].op_combo),
OP_LABELS[
o]);
3138 gtk_combo_box_set_active(GTK_COMBO_BOX(
d->rule[
i].op_combo), 0);
3139 gtk_widget_set_no_show_all(
d->rule[
i].op_combo,
TRUE);
3140 gtk_widget_set_tooltip_text(
d->rule[
i].op_combo, _(
"comparison operator"));
3141 g_signal_connect(G_OBJECT(
d->rule[
i].op_combo),
"changed", G_CALLBACK(
_op_changed),
d->rule +
i);
3142 gtk_box_pack_start(hbox,
d->rule[
i].op_combo,
FALSE,
FALSE, 0);
3146 d->rule[
i].text = w;
3147 gtk_widget_add_events(w, GDK_FOCUS_CHANGE_MASK | GDK_KEY_PRESS_MASK);
3148 gtk_entry_set_placeholder_text(GTK_ENTRY(w), _(
"Search..."));
3149 g_signal_connect(G_OBJECT(w),
"focus-in-event", G_CALLBACK(
_entry_focus_in),
d->rule +
i);
3150 g_signal_connect(G_OBJECT(w),
"changed", G_CALLBACK(
entry_changed),
d->rule +
i);
3151 g_signal_connect(G_OBJECT(w),
"activate", G_CALLBACK(
entry_activated),
d->rule +
i);
3152 gtk_widget_set_name(GTK_WIDGET(w),
"lib-collect-entry");
3153 gtk_box_pack_start(hbox, w,
TRUE,
TRUE, 0);
3154 gtk_entry_set_width_chars(GTK_ENTRY(w), 5);
3156 d->rule[
i].button = gtk_button_new();
3157 gtk_widget_set_events(
d->rule[
i].button, GDK_BUTTON_PRESS_MASK);
3160 gtk_box_pack_start(hbox,
d->rule[
i].button,
FALSE,
FALSE, 0);
3165 d->recursive_check = gtk_check_button_new_with_label(_(
"include sub-folders"));
3168 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
d->recursive_check),
FALSE);
3170 gtk_box_pack_start(GTK_BOX(
d->folders_controls),
d->recursive_check,
FALSE,
FALSE, 0);
3178 gtk_widget_set_valign(
d->sort_by, GTK_ALIGN_CENTER);
3179 g_signal_connect(G_OBJECT(
d->sort_by),
"value-changed", G_CALLBACK(
_sort_by_changed),
d);
3180 gtk_box_pack_start(GTK_BOX(
d->folders_controls),
d->sort_by,
TRUE,
TRUE, 0);
3184 gtk_widget_set_tooltip_text(
d->sort_dir, _(
"toggle ascending / descending order"));
3186 gtk_box_pack_start(GTK_BOX(
d->folders_controls),
d->sort_dir,
FALSE,
FALSE, 0);
3188 d->folder_levels = gtk_spin_button_new_with_range(1, 5, 1);
3189 gtk_widget_set_tooltip_text(
d->folder_levels,
3190 _(
"number of folder levels to show in film-roll names, from the right"));
3192 gtk_box_pack_start(GTK_BOX(
d->folders_controls),
d->folder_levels,
FALSE,
FALSE, 0);
3193 gtk_box_pack_start(GTK_BOX(self->
widget),
d->folders_controls,
FALSE,
FALSE, 0);
3197 d->no_uncategorized = gtk_check_button_new_with_label(_(
"no 'uncategorized' group"));
3198 gtk_widget_set_tooltip_text(
d->no_uncategorized,
3199 _(
"do not group childless tags under an 'uncategorized' entry"));
3201 gtk_box_pack_start(GTK_BOX(
d->collections_controls),
d->no_uncategorized,
FALSE,
FALSE, 0);
3202 gtk_box_pack_start(GTK_BOX(self->
widget),
d->collections_controls,
FALSE,
FALSE, 0);
3206 d->raw_check = gtk_check_button_new_with_label(_(
"edit as raw SQL"));
3207 g_signal_connect(G_OBJECT(
d->raw_check),
"toggled", G_CALLBACK(
_raw_toggled),
d);
3208 gtk_box_pack_start(GTK_BOX(
d->raw_box),
d->raw_check,
FALSE,
FALSE, 0);
3209 d->raw_entry = gtk_entry_new();
3210 gtk_entry_set_placeholder_text(GTK_ENTRY(
d->raw_entry),
3211 _(
"SQL WHERE expression, e.g. iso > 800 AND lens LIKE '%50mm%'"));
3214 gtk_box_pack_start(GTK_BOX(
d->raw_box),
d->raw_entry,
FALSE,
FALSE, 0);
3218 GtkTreeView *
view = GTK_TREE_VIEW(gtk_tree_view_new());
3220 gtk_tree_view_set_headers_visible(
view,
FALSE);
3221 gtk_tree_view_set_activate_on_single_click(
view,
TRUE);
3222 gtk_widget_set_can_focus(GTK_WIDGET(
view),
TRUE);
3238 GtkTreeViewColumn *col = gtk_tree_view_column_new();
3239 gtk_tree_view_append_column(
view, col);
3240 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
3241 gtk_tree_view_column_pack_start(col, renderer,
TRUE);
3242 gtk_tree_view_column_set_cell_data_func(col, renderer,
tree_count_show, NULL, NULL);
3244 g_object_set(renderer,
"strikethrough",
TRUE,
"ellipsize", PANGO_ELLIPSIZE_MIDDLE, (gchar *)0);
3247 GtkTreeModel *listmodel = GTK_TREE_MODEL(
3249 G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INT));
3250 d->listfilter = gtk_tree_model_filter_new(listmodel, NULL);
3253 GtkTreeModel *treemodel = GTK_TREE_MODEL(
3255 G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_INT));
3256 d->treefilter = gtk_tree_model_filter_new(treemodel, NULL);
3258 g_object_unref(treemodel);
3262 gtk_box_pack_start(GTK_BOX(self->
widget),
3274 d->vmonitor = g_volume_monitor_get();
3275 g_signal_connect(G_OBJECT(
d->vmonitor),
"mount-changed", G_CALLBACK(
_mount_changed), self);
3276 g_signal_connect(G_OBJECT(
d->vmonitor),
"mount-added", G_CALLBACK(
_mount_changed), self);
3278 d->vmonitor = g_unix_mount_monitor_get();
3279 g_signal_connect(G_OBJECT(
d->vmonitor),
"mounts-changed", G_CALLBACK(
_mount_changed), self);
3312 g_object_unref(
d->treefilter);
3313 g_object_unref(
d->listfilter);
3314 g_object_unref(
d->vmonitor);
void dt_accels_disconnect_on_text_input(GtkWidget *widget)
Disconnect accels while a text or search entry has the focus, and reconnect them when it loses it....
Handle default and user-set shortcuts (accelerators)
GtkWidget * dt_gui_main_window(void)
void dt_gui_refocus_center()
void dt_bauhaus_combobox_clear(GtkWidget *widget)
void dt_bauhaus_combobox_set_selected_text_align(GtkWidget *widget, const dt_bauhaus_combobox_alignment_t text_align)
gboolean dt_bauhaus_combobox_set_from_value(GtkWidget *widget, int value)
int dt_bauhaus_combobox_get(GtkWidget *widget)
void dt_bauhaus_combobox_add_full(GtkWidget *widget, const char *text, dt_bauhaus_combobox_alignment_t align, gpointer data, void(free_func)(void *data), gboolean sensitive)
gpointer dt_bauhaus_combobox_get_data(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
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
static void get_properties(dt_lib_collect_rule_t *dr)
static gboolean _confirm(const char *title, const char *message)
void gui_reset(dt_lib_module_t *self)
int set_params(dt_lib_module_t *self, const void *params, int size)
static void _set_tooltip(dt_lib_collect_rule_t *dr)
static void _hide_all_widgets(dt_lib_collect_t *d)
static gboolean tree_expand(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
static void metadata_changed(gpointer instance, int type, gpointer self)
static void _folder_levels_changed(GtkWidget *spin, dt_lib_collect_t *d)
void * get_params(dt_lib_module_t *self, int *size)
static void _collect_expanded_cb(GtkTreeView *view, GtkTreePath *path, gpointer data)
static int _rule_get_mode(int n)
@ DT_LIB_COLLECT_COL_TOOLTIP
@ DT_LIB_COLLECT_COL_COUNT
@ DT_LIB_COLLECT_COL_UNREACHABLE
@ DT_LIB_COLLECT_NUM_COLS
@ DT_LIB_COLLECT_COL_FONT
@ DT_LIB_COLLECT_COL_INDEX
@ DT_LIB_COLLECT_COL_PATH
@ DT_LIB_COLLECT_COL_TEXT
@ DT_LIB_COLLECT_COL_VISIBLE
static void _view_drag_data_received(GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *selection_data, guint target_type, guint time, dt_lib_collect_t *d)
static void _mount_changed(GVolumeMonitor *volume_monitor, GMount *mount, dt_lib_module_t *self)
void * legacy_params(struct dt_lib_module_t *self, const void *const old_params, const size_t old_params_size, const int old_version, int *new_version, size_t *new_size)
static void _populate_collect_combo(GtkWidget *w)
static void geotag_changed(gpointer instance, GList *imgs, const int locid, gpointer self)
#define PARAM_STRING_SIZE
static const char * UNCATEGORIZED_TAG
static void _free_row(gpointer p)
static int _rules_count()
static gboolean _prerender_should_stop(dt_job_t *job)
static GList * _collect_sorted_tree_names(int property, int rule)
static void menuitem_clear(GtkMenuItem *menuitem, dt_lib_collect_rule_t *dr)
static void get_number_of_rules(dt_lib_collect_t *d)
static void _view_drag_leave(GtkWidget *widget, GdkDragContext *context, guint time, dt_lib_collect_t *d)
static void _action_activate(GtkMenuItem *mi, dt_lib_collect_t *d)
static void _sort_dir_toggled(GtkToggleButton *b, dt_lib_collect_t *d)
static void _on_tab_switch(GtkNotebook *nb, GtkWidget *page, guint page_num, dt_lib_module_t *self)
static void _raw_entry_activated(GtkWidget *entry, dt_lib_collect_t *d)
static void _raw_toggled(GtkToggleButton *b, dt_lib_collect_t *d)
static char ** _split_tree_name(int property, const char *name)
static void _configure_tab(dt_lib_collect_t *d, dt_collect_tab_t tab)
static void _view_row_activated(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *col, dt_lib_collect_t *d)
static void _rule_set_mode(int n, int mode)
static int32_t _prerender_job(dt_job_t *job)
static void _split_operator(const char *text, int *op_idx, const char **value)
static gboolean item_is_tree(int item)
static gboolean _drop_move_to_folder(dt_lib_collect_t *d, const char *folder, GList *imgs)
static void tag_changed(gpointer instance, gpointer self)
static gboolean _view_button_pressed(GtkWidget *treeview, GdkEventButton *event, dt_lib_collect_t *d)
static gboolean _en_tags(int property, int n)
static void _build_tree_store(GtkTreeStore *store, int property, GList *sorted_names, gboolean no_uncategorized, const char *format_separator)
static gboolean list_match_string(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
static int string_array_length(char **list)
static gboolean item_is_tag(int item)
static gboolean _view_drag_motion(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, dt_lib_collect_t *d)
static void _lib_collect_gui_update(dt_lib_module_t *self)
static gboolean _rule_get_recursive(int n)
static void _force_refresh(dt_lib_collect_t *d)
static void _op_changed(GtkWidget *w, dt_lib_collect_rule_t *dr)
static gboolean _view_popup_menu(GtkWidget *treeview, dt_lib_collect_t *d)
static void _combo_as_view_toggle(GtkWidget *combo)
static gboolean tree_match_string(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
void gui_cleanup(dt_lib_module_t *self)
static void _populate_list(dt_lib_collect_rule_t *dr)
static void row_activated(GtkTreeView *view, GtkTreePath *path, GdkEventButton *event, dt_lib_collect_t *d)
static void tree_count_show(GtkTreeViewColumn *col, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
static void _show_context_menu(dt_lib_collect_t *d, GdkEventButton *event)
static gboolean _drop_attach_tag(dt_lib_collect_t *d, const char *tagpath, GList *imgs)
static GList * _selected_rows(dt_lib_collect_t *d)
static char * tag_collate_key(char *tag)
static const char *const OP_LABELS[]
static gboolean _do_drop(dt_lib_collect_t *d, GtkTreeView *tree, gint x, gint y, GtkSelectionData *sel)
static void _act_tags_remove(dt_lib_collect_t *d, GList *rows)
static void _no_uncategorized_toggled(GtkToggleButton *b, dt_lib_collect_t *d)
static gboolean _clear_suppress_row_activated(gpointer d)
static gboolean _combo_set_active_collection(GtkWidget *combo, const int property)
static void _lib_collect_update_params(dt_lib_collect_t *d)
static gint sort_folder_tag(gconstpointer a, gconstpointer b)
static void _rule_set_recursive(int n, gboolean recursive)
static void menuitem_mode_change(GtkMenuItem *menuitem, dt_lib_collect_rule_t *dr)
static void entry_activated(GtkWidget *entry, dt_lib_collect_rule_t *dr)
static gboolean item_is_folder(int item)
void init_presets(dt_lib_module_t *self)
static void _rules_set_count(int n)
static GList * _rows_to_imgids(int property, GList *rows, gboolean recursive)
static void _act_folders_remove(dt_lib_collect_t *d, GList *rows)
static void _combo_as_collections(GtkWidget *combo)
static void _view_row_expanded(GtkTreeView *view, GtkTreeIter *iter, GtkTreePath *path, dt_lib_collect_t *d)
static void _prerender_free(void *p)
static dt_lib_collect_t * get_collect(dt_lib_collect_rule_t *r)
#define ADD_COLLECT_ENTRY(value)
uint32_t container(dt_lib_module_t *self)
static gboolean _entry_focus_in(GtkWidget *w, GdkEventFocus *event, dt_lib_collect_rule_t *dr)
static int is_time_property(int property)
static dt_lib_module_t * _self()
static gboolean _restore_expanded_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
static void set_properties(dt_lib_collect_rule_t *dr)
static void entry_changed(GtkEntry *entry, dt_lib_collect_rule_t *dr)
static void _act_tag_rename(dt_lib_collect_t *d, GList *rows)
static void filmrolls_removed(gpointer instance, gpointer self)
static const collect_action_t ACTIONS[]
static gboolean range_select(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
static void menuitem_mode(GtkMenuItem *menuitem, dt_lib_collect_rule_t *dr)
static int _rule_get_item(int n)
static void collection_updated(gpointer instance, dt_collection_change_t query_change, dt_collection_properties_t changed_property, gpointer imgs, int next, gpointer self)
static void preferences_changed(gpointer instance, gpointer self)
static void _act_folders_relocate(dt_lib_collect_t *d, GList *rows)
static void _rule_set_item(int n, int item)
static gboolean popup_button_callback(GtkWidget *widget, GdkEventButton *event, dt_lib_collect_rule_t *dr)
void gui_init(dt_lib_module_t *self)
static GtkTreeModel * _create_filtered_model(GtkTreeModel *model, dt_lib_collect_rule_t *dr)
static void _rule_set_string(int n, const char *s)
static void _commit_colllection()
const char ** views(dt_lib_module_t *self)
static gboolean _maybe_file_uncategorized(GtkTreeStore *store, const char *name, const char *next_name_raw, GtkTreeIter *uncategorized, guint *index, int count)
static gboolean list_select(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
static gboolean _en_folders(int property, int n)
static void combo_changed(GtkWidget *combo, dt_lib_collect_rule_t *dr)
static void tree_set_visibility(GtkTreeModel *model, gpointer data)
static void _set_rule_button(dt_lib_collect_rule_t *dr, gboolean last, gboolean active)
static gchar * _rule_get_string(int n)
static gchar * _ask_text(const char *title, const char *initial)
static dt_lib_collect_rule_t * get_active_rule(dt_lib_collect_t *d)
static void filmrolls_updated(gpointer instance, gpointer self)
static void _strip_recursion_marker(gchar *needle, gchar *haystack, int property)
static void _act_prerender(dt_lib_collect_t *d, GList *rows)
static gboolean _en_any(int property, int n)
static void _commit_quiet()
static void _update_op_combo(dt_lib_collect_rule_t *dr)
static void _recursive_toggled(GtkToggleButton *b, dt_lib_collect_t *d)
static const char *const OP_TOKENS[]
static gboolean item_is_numeric(int item)
static GtkTreePath * _folders_root_collapse_path(GtkTreeModel *model)
static gboolean tree_range_visible(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
static gboolean tree_reveal_func(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
static gboolean _en_tag_single(int property, int n)
static void update_view(dt_lib_collect_rule_t *dr)
static gboolean _en_folders_lighttable(int property, int n)
static void free_tuple(gpointer data)
static void _propagate_count_to_ancestors(GtkTreeStore *store, GtkTreeIter *leaf, int count)
static gboolean _adopt_tag_order(const char *text, int *order)
static void _sort_by_changed(GtkWidget *combo, dt_lib_collect_t *d)
static void _combo_as_full(GtkWidget *combo)
static char ** split_path(const char *path)
static guint64 _date_key(const char *s, char pad)
static int _combo_get_active_collection(GtkWidget *combo)
static gchar * _decorate_hierarchy(gchar *text, GdkEventButton *event)
static void _populate_tree(dt_lib_collect_rule_t *dr)
@ DT_LIB_COLLECT_MODE_AND_NOT
@ DT_LIB_COLLECT_MODE_AND
void dt_collection_update_query(const dt_collection_t *collection, dt_collection_change_t query_change, dt_collection_properties_t changed_property, GList *list)
void dt_collection_set_query_flags(const dt_collection_t *collection, dt_collection_query_flags_t flags)
dt_collection_t * dt_collection_get_global(void)
void dt_collection_memory_update()
void dt_collection_split_operator_number(const gchar *input, char **number1, char **number2, char **operator)
GList * dt_collection_get_images_for_rule(const dt_collection_properties_t property, const char *text, gboolean recursive)
void dt_collection_set_tag_id(dt_collection_t *collection, const uint32_t tagid)
void dt_collection_name_value_free(gpointer value)
GList * dt_collection_get_property_values(const dt_collection_properties_t property, const int rule)
dt_collection_properties_t
@ DT_COLLECTION_PROP_EXPOSURE
@ DT_COLLECTION_PROP_MODULE
@ DT_COLLECTION_PROP_RATING
@ DT_COLLECTION_PROP_QUERY
@ DT_COLLECTION_PROP_TIME
@ DT_COLLECTION_PROP_METADATA
@ DT_COLLECTION_PROP_GROUPING
@ DT_COLLECTION_PROP_FILMROLL
@ DT_COLLECTION_PROP_LENS
@ DT_COLLECTION_PROP_CAMERA
@ DT_COLLECTION_PROP_LOCAL_COPY
@ DT_COLLECTION_PROP_GEOTAGGING
@ DT_COLLECTION_PROP_FILENAME
@ DT_COLLECTION_PROP_COLORLABEL
@ DT_COLLECTION_PROP_UNDEF
@ DT_COLLECTION_PROP_ORDER
@ DT_COLLECTION_PROP_FOLDERS
@ DT_COLLECTION_PROP_APERTURE
@ DT_COLLECTION_PROP_IMPORT_TIMESTAMP
@ DT_COLLECTION_PROP_FOCAL_LENGTH
@ DT_COLLECTION_PROP_EXPORT_TIMESTAMP
@ DT_COLLECTION_PROP_CHANGE_TIMESTAMP
@ DT_COLLECTION_PROP_HISTORY
@ DT_COLLECTION_PROP_PRINT_TIMESTAMP
#define DT_COLLECTION_ORDER_FLAG
#define COLLECTION_QUERY_FULL
@ DT_COLLECTION_CHANGE_RELOAD
@ DT_COLLECTION_CHANGE_NEW_QUERY
@ DT_COLLECTION_SORT_FILENAME
@ DT_COLLECTION_SORT_NONE
struct _GtkWidget GtkWidget
GtkWidget, opaque, spelled exactly as GTK spells it.
const dt_colormatrix_t dt_aligned_pixel_t out
void dt_conf_set_bool(const char *name, int val)
int dt_conf_get_bool(const char *name)
gchar * dt_conf_get_string(const char *name)
Read the stored string for name as a private copy.
void dt_conf_set_int(const char *name, int val)
int dt_conf_get_int(const char *name)
Integer for name, clamped to the bounds declared in the XML.
void dt_conf_set_string(const char *name, const char *val)
const char * dt_conf_get_string_const(const char *name)
Borrow the stored string for name without copying it.
int32_t dt_image_move(const int32_t imgid, const int32_t filmid)
const char * dt_image_film_roll_name(const char *path)
void dt_image_synch_xmp(const int selected)
void dt_control_log(const char *msg,...)
void dt_control_queue_redraw_center()
Request a redraw of the centre view.
struct dt_control_t * dt_control_get_global(void)
gboolean dt_control_remove_images()
struct dt_view_manager_t * dt_view_manager_get_global(void)
struct dt_bauhaus_t * dt_bauhaus_get_global(void)
GHashTable * names
GtkWidget* -> the name the application knows it by.
GtkTreeStore * store
its model, owned by the view
GtkWidget * folder
destination folder chooser
GtkWidget * view
the tree view
static const GtkTargetEntry target_list_internal[]
static const guint n_targets_internal
void dt_film_init(dt_film_t *film)
void dt_film_relocate(const char *old_path, const char *new_path)
int dt_film_new(dt_film_t *film, const char *directory)
int32_t dt_film_get_id(const char *folder)
void dt_film_set_folder_status()
void dt_film_cleanup(dt_film_t *film)
dt_job_state_t dt_control_job_get_state(_dt_job_t *job)
dt_job_t * dt_control_job_create(dt_job_execute_callback execute, const char *msg,...)
int dt_control_add_job(dt_control_t *control, dt_job_queue_t queue_id, _dt_job_t *job)
void * dt_control_job_get_params(const _dt_job_t *job)
void dt_control_job_set_progress(dt_job_t *job, double value)
void dt_control_job_add_progress(dt_job_t *job, const char *message, gboolean cancellable)
void dt_control_job_set_params(_dt_job_t *job, void *params, dt_job_destroy_callback callback)
static const dt_iop_drawlayer_params_t * params
gboolean dt_handle_dialog_enter(GtkWidget *widget, GdkEventKey *event, gpointer data)
void dt_lib_presets_add(const char *name, const char *plugin_name, const int32_t version, const void *params, const int32_t params_size, gboolean readonly)
_lib_location_type_t type
float *const restrict const size_t k
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
static void dt_free_gpointer(gpointer ptr)
g_free() one pointer, with the signature GDestroyNotify wants.
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
dt_imageio_retval_t status
void dt_mimap_cache_evict(const int32_t imgid)
void dt_mipmap_get_cache_filename(char path[DT_PATH_MAX], dt_mipmap_size_t mip, const int32_t imgid)
#define dt_mipmap_cache_get(B, C, D, E, F)
#define dt_mipmap_cache_release(B)
#define DT_MODULE(MODVER)
Instantiate the version handshake every module must export. Use once, at file scope,...
GtkWidget * dt_ui_notebook_page(GtkNotebook *notebook, const char *text, const char *tooltip)
GtkNotebook * dt_ui_notebook_new()
void dt_osx_disallow_fullscreen(GtkWidget *widget)
#define DT_PATH_MAX
Buffer size for a filesystem path anywhere in Ansel.
struct dt_selection_t * dt_selection_get_global(void)
void dt_selection_select_list(struct dt_selection_t *selection, const GList *const l)
void dt_selection_clear(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,...)
struct dt_control_signal_t * dt_control_signal_get_global(void)
@ DT_SIGNAL_METADATA_CHANGED
This signal is raised when metadata status (shown/hidden) or value has changed.
@ DT_SIGNAL_IMAGES_ORDER_CHANGE
This signal is raised to request image order change.
@ DT_SIGNAL_FILMROLLS_CHANGED
This signal is raised when a filmroll is deleted/changed but not imported.
@ DT_SIGNAL_GEOTAG_CHANGED
This signal is raised when a geotag is added/deleted/changed
@ DT_SIGNAL_PREFERENCES_CHANGE
This signal is raised after preferences have been changed no parameters no return.
@ DT_SIGNAL_FILMROLLS_REMOVED
This signal is raised only when a filmroll is removed.
@ DT_SIGNAL_TAG_CHANGED
This signal is raised when a tag is added/deleted/changed
@ 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)
static const dt_aligned_pixel_simd_t value
const float uint32_t state[4]
gboolean(* enabled)(int property, int n)
void(* run)(dt_lib_collect_t *d, GList *rows)
dt_mipmap_size_t max_size
dt_lib_collect_params_rule_t rule[10]
GtkWidget * recursive_check
struct dt_lib_collect_params_t * params
GtkWidget * collections_controls
GVolumeMonitor * vmonitor
GtkTreeModel * treefilter
dt_lib_collect_rule_t rule[10]
GtkTreeModel * listfilter
GtkWidget * no_uncategorized
GtkWidget * folder_levels
gchar * last_folders_string
gboolean has_last_folders
gboolean last_folders_recursive
GtkWidget * folders_controls
gboolean suppress_row_activated
struct dt_view_manager_t::@61 proxy
struct dt_lib_module_t *void(* update)(struct dt_lib_module_t *)
struct dt_view_manager_t::@61::@63 module_collect
GList * dt_util_str_to_glist(const gchar *separator, const gchar *text)
gboolean dt_util_test_image_file(const char *filename)
gchar * dt_util_dstrcat(gchar *str, const gchar *format,...)
@ DT_UI_CONTAINER_PANEL_LEFT_CENTER