42#include <glib/gstdio.h>
72 gchar *tag_list = NULL;
73 for(GList *b = before; b; b = g_list_next(b))
75 if(!g_list_find(a, b->data))
80 if(tag_list) tag_list[strlen(tag_list) - 1] =
'\0';
87 gchar *tag_list = NULL;
88 for(GList *a = after; a; a = g_list_next(a))
90 if(!g_list_find(b, a->data))
95 " (SELECT (IFNULL(MAX(position),0) & 0xFFFFFFFF00000000) + (1 << 32)"
96 " FROM main.tagged_images)"
99 GPOINTER_TO_INT(a->data));
103 if(tag_list) tag_list[strlen(tag_list) - 1] =
'\0';
133 for(GList *list = (GList *)data; list; list = g_list_next(list))
140 *imgs = g_list_prepend(*imgs, GINT_TO_POINTER(undotags->
imgid));
150 g_list_free(undotags->
before);
152 g_list_free(undotags->
after);
153 undotags->
after = NULL;
159 GList *l = (GList *)data;
178 if(
id && g_strstr_len(
name, -1,
"darktable|") ==
name)
213 if (!tag_list)
return 0;
215 char *flatlist = NULL;
218 for (GList *taglist = tag_list; taglist ; taglist = g_list_next(taglist))
220 const guint tagid = ((
dt_tag_t *)taglist->data)->id;
223 if(flatlist && count > 1000)
225 flatlist[strlen(flatlist)-1] =
'\0';
228 tcount = tcount + count;
234 flatlist[strlen(flatlist)-1] =
'\0';
237 tcount = tcount + count;
249 if(
IS_NULL_PTR(new_tagname) || !new_tagname[0])
return;
272 for(
const GList *
t = tags;
t;
t = g_list_next(
t))
274 if(!g_list_find(*list,
t->data))
276 *list = g_list_prepend(*list,
t->data);
285 const int nb_ini = g_list_length(*list);
286 for(
const GList *
t = tags;
t;
t = g_list_next(
t))
288 *list = g_list_remove(*list,
t->data);
290 return (g_list_length(*list) != nb_ini);
310static gboolean
_tag_execute(
const GList *tags,
const GList *imgs, GList **undo,
const gboolean undo_on,
314 for(
const GList *images = imgs; images; images = g_list_next(images))
316 const int32_t image_id = GPOINTER_TO_INT(images->data);
318 undotags->
imgid = image_id;
331 undotags->
after = g_list_copy((GList *)tags);
334 if(dttags) undotags->
after = g_list_concat(undotags->
after, dttags);
338 undotags->
after = g_list_copy((GList *)tags);
348 *undo = g_list_append(*undo, undotags);
361 tags = g_list_prepend(tags, GINT_TO_POINTER(tagid));
377gboolean
dt_tag_attach(
const guint tagid,
const int32_t imgid,
const gboolean undo_on,
const gboolean group_on)
390 GList *imgs = g_list_append(NULL, GINT_TO_POINTER(imgid));
398gboolean
dt_tag_set_tags(
const GList *tags,
const GList *img,
const gboolean ignore_dt_tags,
399 const gboolean clear_on,
const gboolean undo_on)
422 gchar **tokens = g_strsplit(tags,
",", 0);
428 gchar **entry = tokens;
431 char *e = g_strstrip(*entry);
436 tagl = g_list_prepend(tagl, GINT_TO_POINTER(tagid));
467 tags = g_list_prepend(tags, GINT_TO_POINTER(tagid));
485gboolean
dt_tag_detach(
const guint tagid,
const int32_t imgid,
const gboolean undo_on,
const gboolean group_on)
491 imgs = g_list_prepend(imgs, GINT_TO_POINTER(imgid));
501 const gboolean group_on)
517 uint32_t nb_selected = 0;
528 if(imgid > 0 || nb_selected > 0)
534 for(GList *l = tags; l; l = g_list_next(l))
537 t->leave = g_strrstr(
t->tag,
"|");
538 t->leave =
t->leave ?
t->leave + 1 :
t->tag;
539 const uint32_t imgnb =
t->count;
543 *result = g_list_append(*result,
t);
553 if(!(imgid > 0))
return 0;
559 for(GList *l = tags; l; l = g_list_next(l))
562 t->leave = g_strrstr(
t->tag,
"|");
563 t->leave =
t->leave ?
t->leave + 1 :
t->tag;
564 *result = g_list_append(*result,
t);
577 return g_strcmp0(tuple_a->
tag, tuple_b->
tag);
585 return g_strcmp0(tuple_a->
leave, tuple_b->
leave);
601 for(GList *taglist = tags; taglist; taglist = g_list_next(taglist))
604 gchar *tag = ((
dt_tag_t *)taglist->data)->tag;
605 for(
char *letter = tag; *letter; letter++)
606 if(*letter ==
'|') *letter =
'\1';
609 for(GList *taglist = sorted_tags; taglist; taglist = g_list_next(taglist))
611 gchar *tag = ((
dt_tag_t *)taglist->data)->tag;
612 for(
char *letter = tag; *letter; letter++)
613 if(*letter ==
'\1') *letter =
'|';
625 GList *taglist = NULL;
632 if(count < 1)
return NULL;
634 for(GList *tag_iter = taglist; tag_iter; tag_iter = g_list_next(tag_iter))
639 gchar **pch = g_strsplit(
value,
"|", -1);
643 if(omit_tag_hierarchy)
646 for(; *iter && *(iter + 1); iter++);
647 if(*iter) tags = g_list_prepend(tags, g_strdup(*iter));
654 tags = g_list_prepend(tags, g_strdup(pch[j]));
669 GList *taglist = NULL;
674 if(count < 1)
return NULL;
676 for(GList *tag_iter = taglist; tag_iter; tag_iter = g_list_next(tag_iter))
679 tags = g_list_prepend(tags, g_strdup(
t->tag));
684 tags = g_list_reverse(tags);
692 images = g_strdup_printf(
"%d", imgid);
716 return ((g_strcmp0(ta->
tag, tb->
tag) == 0) &&
722 GList *taglist = NULL;
731 if(count < 1)
return NULL;
733 sorted_tags = g_list_reverse(sorted_tags);
736 if(export_private_tags)
738 for(GList *tagt = sorted_tags; tagt; tagt = g_list_next(tagt))
741 t->flags &= ~DT_TF_PRIVATE;
744 for(GList *sorted_iter = sorted_tags; sorted_iter; sorted_iter = g_list_next(sorted_iter))
750 gchar *tagname =
t->leave;
751 tags = g_list_prepend(tags, g_strdup(tagname));
755 if(!omit_tag_hierarchy)
757 GList *next = g_list_next(sorted_iter);
758 gchar *end = g_strrstr(
t->tag,
"|");
762 end = g_strrstr(
t->tag,
"|");
766 const gchar *tag = end ? end + 1 :
t->tag;
767 tags = g_list_prepend(tags, g_strdup(tag));
773 if (export_tag_synonyms)
775 gchar *synonyms =
t->synonym;
776 if (synonyms && synonyms[0])
778 gchar **tokens = g_strsplit(synonyms,
",", 0);
781 gchar **entry = tokens;
786 tags = g_list_append(tags, g_strdup(e));
802 GList *taglist = NULL;
807 if(count < 1)
return NULL;
810 for(GList *tag_iter = taglist; tag_iter; tag_iter = g_list_next(tag_iter))
815 tags = g_list_prepend(tags, g_strdup(
t->tag));
821 return g_list_reverse(tags);
837 for(GList *imgs = (GList *)img; imgs; imgs = g_list_next(imgs))
842 GList *result = NULL;
845 images[strlen(images) - 1] =
'\0';
855 const int nb_recent =
dt_conf_get_int(
"plugins/lighttable/tagging/nb_recent_tags");
856 const uint32_t confidence =
dt_conf_get_int(
"plugins/lighttable/tagging/confidence");
862 for(GList *l = tags; l; l = g_list_next(l))
865 t->leave = g_strrstr(
t->tag,
"|");
866 t->leave =
t->leave ?
t->leave + 1 :
t->tag;
867 *result = g_list_append(*result,
t);
897 for(GList *l = tags; l; l = g_list_next(l))
900 t->leave = g_strrstr(
t->tag,
"|");
901 t->leave =
t->leave ?
t->leave + 1 :
t->tag;
902 *result = g_list_append(*result,
t);
915 for(GList *l = tags; l; l = g_list_next(l))
918 t->leave = g_strrstr(
t->tag,
"|");
919 t->leave =
t->leave ?
t->leave + 1 :
t->tag;
920 *result = g_list_append(*result,
t);
930 gchar *synonyms = NULL;
931 for(
char *letter = synonyms_entry; *letter; letter++)
933 if(*letter ==
';' || *letter ==
'\n') *letter =
',';
934 if(*letter ==
'\r') *letter =
' ';
936 gchar **tokens = g_strsplit(synonyms_entry,
",", 0);
939 gchar **entry = tokens;
942 char *e = g_strstrip(*entry);
950 synonyms[strlen(synonyms) - 2] =
'\0';
963 if (!synonyms_entry)
return;
989 synonyms = g_strdup(synonym);
1005 if(result && *result)
1027 FILE *fd = g_fopen(filename,
"r");
1030 GList * hierarchy = NULL;
1035 guint previous_category_depth = 0;
1036 gboolean previous_category =
FALSE;
1037 gboolean previous_synonym =
FALSE;
1039 while(
getline(&line, &len, fd) != -1)
1043 while(*start ==
'\t' || *start ==
' ' || *start ==
',' || *start ==
';') start++;
1044 const int depth = start - line;
1046 char *end = line + strlen(line) - 1;
1047 while((*end ==
'\n' || *end ==
'\r' || *end ==
',' || *end ==
';') && end >= start)
1055 gboolean skip =
FALSE;
1056 gboolean category =
FALSE;
1057 gboolean synonym =
FALSE;
1058 if (*start ==
'[' && *end ==
']')
1064 else if (*start ==
'{' && *end ==
'}')
1081 char *tagname = g_strdup(start);
1091 GList *iter = g_list_nth(hierarchy, depth);
1094 GList *current = iter;
1095 iter = g_list_next(iter);
1096 hierarchy = g_list_delete_link(hierarchy, current);
1100 hierarchy = g_list_append(hierarchy, g_strdup(start));
1106 if (previous_category && (depth > previous_category_depth + 1))
1125 previous_category_depth = category ? depth : 0;
1126 previous_category = category;
1127 previous_synonym = synonym;
1162 FILE *fd = g_fopen(filename,
"w");
1171 gchar **hierarchy = NULL;
1172 for(GList *tag_elt = sorted_tags; tag_elt; tag_elt = g_list_next(tag_elt))
1174 const gchar *tag = ((
dt_tag_t *)tag_elt->data)->tag;
1175 const char *synonyms = ((
dt_tag_t *)tag_elt->data)->synonym;
1177 gchar **tokens = g_strsplit(tag,
"|", -1);
1181 for(common_start = 0; hierarchy && hierarchy[common_start] && tokens && tokens[common_start]; common_start++)
1183 if(g_strcmp0(hierarchy[common_start], tokens[common_start]))
break;
1186 g_strfreev(hierarchy);
1189 int tabs = common_start;
1190 for(
size_t i = common_start; tokens && tokens[
i];
i++, tabs++)
1192 for(
int j = 0; j < tabs; j++) fputc(
'\t', fd);
1197 fprintf(fd,
"[%s]\n", tokens[
i]);
1199 fprintf(fd,
"%s\n", tokens[
i]);
1200 if (synonyms && synonyms[0])
1202 gchar **tokens2 = g_strsplit(synonyms,
",", 0);
1205 gchar **entry = tokens2;
1210 for(
int j = 0; j < tabs+1; j++) fputc(
'\t', fd);
1211 fprintf(fd,
"{%s}\n", e);
1215 g_strfreev(tokens2);
1219 fprintf(fd,
"%s\n", tokens[
i]);
1223 g_strfreev(hierarchy);
1239 for(GList *l =
names; l; l = g_list_next(l))
1241 const char *tag = (
const char *)l->data;
1243 if (tagnb >= rootnb + level)
1245 gchar **pch = g_strsplit(tag,
"|", -1);
1246 char *subtag = pch[rootnb + level];
1247 gboolean valid =
TRUE;
1249 if(tags && strlen(tags) >= strlen(subtag) + 1)
1251 gchar *found = g_strstr_len(tags, strlen(tags), subtag);
1252 if(found && found[strlen(subtag)] ==
',')
1260 g_list_free_full(
names, g_free);
1262 if(tags) tags[strlen(tags) - 1] =
'\0';
1267 gboolean *descending)
1275 *sort = (
flags & ~DT_TF_DESCENDING) >> 16;
1289 const gboolean descending)
GList * dt_act_on_get_images()
static const dt_adaptation_t kind
int dt_conf_get_bool(const char *name)
int dt_conf_get_int(const char *name)
Integer for name, clamped to the bounds declared in the XML.
const char * dt_conf_get_string_const(const char *name)
Borrow the stored string for name without copying it.
GHashTable * names
GtkWidget* -> the name the application knows it by.
ssize_t getline(char **lineptr, size_t *n, FILE *stream)
void dt_grouping_add_grouped_images(GList **images)
_lib_location_type_t type
#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_mipmap_buffer_dsc_flags flags
struct dt_selection_t * dt_selection_get_global(void)
gchar * dt_selection_ids_to_string(struct dt_selection_t *selection)
int dt_selection_get_length(struct dt_selection_t *selection)
static const dt_aligned_pixel_simd_t value
void dt_tag_repository_delete_batch(const char *id_list)
Delete every tag in id_list and its attachments.
void dt_tag_repository_rename(const guint tagid, const char *new_name)
Rename tagid. The caller checks first that the new name is free.
GList * dt_tag_repository_get_with_usage(const uint32_t nb_selected)
Every user tag with how often it is used and how much of the selection carries it.
GList * dt_tag_repository_get_collection_tags(void)
User tags attached to at least one image of the current collection, by name.
gchar * dt_tag_repository_get_name(const guint tagid)
The name of tagid, newly allocated, or NULL.
int dt_tag_repository_count_attachments(const guint tagid)
How many attachment ROWS tagid has, or -1 if the count could not be read. See dt_tag_repository_count...
GList * dt_tag_repository_get_images(const guint tagid)
Images carrying tagid, GINT_TO_POINTER. Free with g_list_free().
GList * dt_tag_repository_get_suggestions(const uint32_t nb_selected, const int confidence, const char *recent_tags, const int nb_recent)
Tags worth suggesting for the current selection.
GList * dt_tag_repository_get_ids_for_images(const char *imgid_list, const dt_tag_kind_t kind)
Ids of the tags on a set of images, optionally restricted by kind.
void dt_tag_repository_mark_internal(const guint tagid)
Record tagid as an internal tag.
void dt_tag_repository_get_similar(const char *keyword, GList **tags, GList **imgids)
The tags matching keyword and the images carrying any of them.
guint dt_tag_repository_find_by_name(const char *name)
The id of the tag named name, or 0 when there is none.
GList * dt_tag_repository_get_attached_for_export(const int32_t imgid)
Tags attached to imgid for export, plus every ancestor on their paths.
GList * dt_tag_repository_get_names_under(const int32_t imgid, const char *category)
Names of the tags on imgid that start with category.
guint dt_tag_repository_find_by_name_nocase(const char *name)
The id of the tag whose name matches name case-INSENSITIVELY, or 0.
gint dt_tag_repository_get_flags(const guint tagid)
The flags word of tagid, or 0.
void dt_tag_repository_update_flags(const guint tagid, const gint set, const gint keep_mask)
Set the bits in set and clear those absent from keep_mask: flags = (IFNULL(flags,0) & keep_mask) | se...
void dt_tag_repository_count_similar(const char *keyword, int *tag_count, int *img_count)
How many tags match keyword, and how many images carry any of them.
GList * dt_tag_repository_get_images_in_list(const guint tagid, const char *imgid_list)
Images carrying tagid, restricted to imgid_list – a comma-separated list of decimal image ids compose...
void dt_tag_repository_delete(const guint tagid)
Delete tagid, its attachments, and its memory.darktable_tags entry.
void dt_tag_repository_cleanup(void)
Finalise whatever this repository still caches – today, nothing. See dt_colorlabel_repository_cleanup...
void dt_tag_repository_attach_batch(const char *values)
Attach rows given as the VALUES clause of the insert – "(imgid,tagid,pos),…". The position expression...
gchar * dt_tag_repository_get_synonyms(const guint tagid)
The synonyms of tagid, newly allocated, or NULL.
void dt_tag_repository_set_synonyms(const guint tagid, const char *synonyms)
Replace the synonyms of tagid.
void dt_tag_repository_rebuild_internal(void)
Rebuild the whole internal-tag cache from data.tags.
uint32_t dt_tag_repository_count_distinct_images(const guint tagid)
Distinct images carrying tagid.
void dt_tag_repository_set_flags(const guint tagid, const gint flags)
Replace the flags word of tagid.
gboolean dt_tag_repository_is_attached(const guint tagid, const int32_t imgid)
TRUE when tagid is attached to imgid.
void dt_tag_repository_detach_batch(const int32_t imgid, const char *tagid_list)
Detach every tag in tagid_list from imgid.
GList * dt_tag_repository_get_attached(const int32_t imgid, const gboolean ignore_internal)
Tags attached to one image or to the current selection, ordered by name.
guint dt_tag_repository_insert(const char *name)
Insert a tag named name and return its new id, or 0 on failure.
data.tags and main.tagged_images.
void dt_undo_end_group(dt_undo_t *self)
void dt_undo_start_group(dt_undo_t *self, dt_undo_type_t type)
void dt_undo_record(dt_undo_t *self, gpointer user_data, dt_undo_type_t type, dt_undo_data_t data, void(*undo)(gpointer user_data, dt_undo_type_t type, dt_undo_data_t item, dt_undo_action_t action, GList **imgs), void(*free_data)(gpointer data))
dt_undo_t * dt_undo_get_global(void)
guint dt_util_string_count_char(const char *text, const char needle)
gchar * dt_util_glist_to_str(const gchar *separator, GList *items)
gchar * dt_util_dstrcat(gchar *str, const gchar *format,...)
GList * dt_util_glist_uniq(GList *items)