{
}
+
+static gboolean bar_pane_keywords_dnd_can_move(GtkTreeModel *keyword_tree, GtkTreeIter *src_kw_iter, GtkTreeIter *dest_kw_iter)
+{
+ gchar *src_name;
+ GtkTreeIter parent;
+
+ if (dest_kw_iter && keyword_same_parent(keyword_tree, src_kw_iter, dest_kw_iter))
+ {
+ return TRUE; /* reordering of siblings is ok */
+ }
+ if (!dest_kw_iter && !gtk_tree_model_iter_parent(keyword_tree, &parent, src_kw_iter))
+ {
+ return TRUE; /* reordering of top-level siblings is ok */
+ }
+
+ src_name = keyword_get_name(keyword_tree, src_kw_iter);
+ if (keyword_exists(keyword_tree, NULL, dest_kw_iter, src_name, FALSE))
+ {
+ g_free(src_name);
+ return FALSE;
+ }
+ g_free(src_name);
+ return TRUE;
+}
+
+static gboolean bar_pane_keywords_dnd_skip_existing(GtkTreeModel *keyword_tree, GtkTreeIter *dest_kw_iter, GList **keywords)
+{
+ /* we have to find at least one keyword that does not already exist as a sibling of dest_kw_iter */
+ GList *work = *keywords;
+ while (work)
+ {
+ gchar *keyword = work->data;
+ if (keyword_exists(keyword_tree, NULL, dest_kw_iter, keyword, FALSE))
+ {
+ GList *next = work->next;
+ g_free(keyword);
+ *keywords = g_list_delete_link(*keywords, work);
+ work = next;
+ }
+ else
+ {
+ work = work->next;
+ }
+ }
+ return !!*keywords;
+}
+
static void bar_pane_keywords_dnd_receive(GtkWidget *tree_view, GdkDragContext *context,
gint x, gint y,
GtkSelectionData *selection_data, guint info,
if ((pos == GTK_TREE_VIEW_DROP_INTO_OR_BEFORE || pos == GTK_TREE_VIEW_DROP_INTO_OR_AFTER) &&
!gtk_tree_model_iter_has_child(keyword_tree, &dest_kw_iter))
{
+ /* the node has no children, all keywords can be added */
gtk_tree_store_append(GTK_TREE_STORE(keyword_tree), &new_kw_iter, &dest_kw_iter);
}
else
{
+ if (src_valid && !bar_pane_keywords_dnd_can_move(keyword_tree, &src_kw_iter, &dest_kw_iter))
+ {
+ /* the keyword can't be moved if the same name already exist */
+ return;
+ }
+ if (new_keywords && !bar_pane_keywords_dnd_skip_existing(keyword_tree, &dest_kw_iter, &new_keywords))
+ {
+ /* the keywords can't be added if the same name already exist */
+ return;
+ }
+
switch (pos)
{
case GTK_TREE_VIEW_DROP_INTO_OR_BEFORE:
break;
}
}
+
}
else
{
+ if (src_valid && !bar_pane_keywords_dnd_can_move(keyword_tree, &src_kw_iter, NULL))
+ {
+ /* the keyword can't be moved if the same name already exist */
+ return;
+ }
+ if (new_keywords && !bar_pane_keywords_dnd_skip_existing(keyword_tree, NULL, &new_keywords))
+ {
+ /* the keywords can't be added if the same name already exist */
+ return;
+ }
gtk_tree_store_append(GTK_TREE_STORE(keyword_tree), &new_kw_iter, NULL);
}
work = new_keywords;
while (work)
{
- keyword_set(GTK_TREE_STORE(keyword_tree), &new_kw_iter, work->data, TRUE);
+ gchar *keyword = work->data;
+ keyword_set(GTK_TREE_STORE(keyword_tree), &new_kw_iter, keyword, TRUE);
work = work->next;
+
if (work)
{
GtkTreeIter add;
if (cdd->edit_existing)
{
- if (keywords && keywords->data) /* there should be one keyword */
+ if (keywords && keywords->data && /* there should be one keyword */
+ !keyword_exists(keyword_tree, NULL, &kw_iter, keywords->data, TRUE))
{
keyword_set(GTK_TREE_STORE(keyword_tree), &kw_iter, keywords->data, cdd->is_keyword);
}
while (work)
{
GtkTreeIter add;
+ if (keyword_exists(keyword_tree, NULL, have_dest ? &kw_iter : NULL, work->data, FALSE))
+ {
+ work = work->next;
+ continue;
+ }
if (have_dest)
{
gtk_tree_store_insert_after(GTK_TREE_STORE(keyword_tree), &add, NULL, &kw_iter);
return ret;
}
+gboolean keyword_same_parent(GtkTreeModel *keyword_tree, GtkTreeIter *a, GtkTreeIter *b)
+{
+ GtkTreeIter parent_a;
+ GtkTreeIter parent_b;
+
+ gboolean valid_pa = gtk_tree_model_iter_parent(keyword_tree, &parent_a, a);
+ gboolean valid_pb = gtk_tree_model_iter_parent(keyword_tree, &parent_b, b);
+
+ if (valid_pa && valid_pb)
+ {
+ return keyword_compare(keyword_tree, &parent_a, &parent_b) == 0;
+ }
+ else
+ {
+ return (!valid_pa && !valid_pb); /* both are toplevel */
+ }
+}
+
+gboolean keyword_exists(GtkTreeModel *keyword_tree, GtkTreeIter *parent_ptr, GtkTreeIter *sibling, const gchar *name, gboolean exclude_sibling)
+{
+ GtkTreeIter parent;
+ GtkTreeIter iter;
+ gboolean toplevel = FALSE;
+ gboolean ret;
+ gchar *casefold;
+
+ if (parent_ptr)
+ {
+ parent = *parent_ptr;
+ }
+ else if (sibling)
+ {
+ toplevel = !gtk_tree_model_iter_parent(keyword_tree, &parent, sibling);
+ }
+ else
+ {
+ toplevel = TRUE;
+ }
+
+ if (!gtk_tree_model_iter_children(GTK_TREE_MODEL(keyword_tree), &iter, toplevel ? NULL : &parent)) return FALSE;
+
+ casefold = g_utf8_casefold(name, -1);
+ ret = FALSE;
+
+ while (TRUE)
+ {
+ if (!(exclude_sibling && sibling && keyword_compare(keyword_tree, &iter, sibling) == 0))
+ {
+ gchar *iter_casefold = keyword_get_casefold(keyword_tree, &iter);
+ ret = strcmp(casefold, iter_casefold) == 0;
+ g_free(iter_casefold);
+ }
+ if (ret) break;
+ if (!gtk_tree_model_iter_next(keyword_tree, &iter)) break;
+ }
+ g_free(casefold);
+ return ret;
+}
+
+
void keyword_copy(GtkTreeStore *keyword_tree, GtkTreeIter *to, GtkTreeIter *from)
{
gboolean keyword_get_is_keyword(GtkTreeModel *keyword_tree, GtkTreeIter *iter);
gboolean keyword_compare(GtkTreeModel *keyword_tree, GtkTreeIter *a, GtkTreeIter *b);
+gboolean keyword_same_parent(GtkTreeModel *keyword_tree, GtkTreeIter *a, GtkTreeIter *b);
+gboolean keyword_exists(GtkTreeModel *keyword_tree, GtkTreeIter *parent_ptr, GtkTreeIter *sibling, const gchar *name, gboolean exclude_sibling);
void keyword_copy(GtkTreeStore *keyword_tree, GtkTreeIter *to, GtkTreeIter *from);
void keyword_copy_recursive(GtkTreeStore *keyword_tree, GtkTreeIter *to, GtkTreeIter *from);