[gimp] app: Add support for tag assignment on multiple selected items



commit 8e87f06e204575e03822b1b195b74743f41d85eb
Author: Aurimas Juška <aurimas juska gmail com>
Date:   Fri Aug 14 22:25:22 2009 +0300

    app: Add support for tag assignment on multiple selected items
    
    Tag assignment has been redesigned according to Peter Sikking's
    recommendations. When multiple resources are selected, common tags
    to all of them are listed in tag asignment widget. On assignment
    it is detected which tags were added and removed from common tag set
    and the selected resources are updated accordingly (instead of
    replacing their previous tags).

 app/widgets/gimpdatafactoryview.c |    9 +--
 app/widgets/gimptagentry.c        |  143 ++++++++++++++++++++++++++----------
 app/widgets/gimptagentry.h        |    1 +
 3 files changed, 107 insertions(+), 46 deletions(-)
---
diff --git a/app/widgets/gimpdatafactoryview.c b/app/widgets/gimpdatafactoryview.c
index 30c4af8..531f16c 100644
--- a/app/widgets/gimpdatafactoryview.c
+++ b/app/widgets/gimpdatafactoryview.c
@@ -333,13 +333,10 @@ gimp_data_factory_view_select_item (GimpContainerEditor *editor,
 
   if (view->priv->assign_tag_entry)
     {
-      GList *active_items = NULL;
-
-      if (viewable)
-        {
-          active_items = g_list_append (active_items, viewable);
-        }
+      GList             *active_items = NULL;
+      GimpContainerView *container_view = GIMP_CONTAINER_VIEW (editor->view);
 
+      gimp_container_view_get_selected (container_view, &active_items);
       gimp_tag_entry_set_selected_items (GIMP_TAG_ENTRY (view->priv->assign_tag_entry),
                                          active_items);
 
diff --git a/app/widgets/gimptagentry.c b/app/widgets/gimptagentry.c
index 0f7cd09..1f8c5ee 100644
--- a/app/widgets/gimptagentry.c
+++ b/app/widgets/gimptagentry.c
@@ -97,6 +97,9 @@ static gboolean gimp_tag_entry_query_tag                 (GimpTagEntry     *entr
 static void     gimp_tag_entry_assign_tags               (GimpTagEntry     *entry);
 static void     gimp_tag_entry_load_selection            (GimpTagEntry     *entry,
                                                           gboolean          sort);
+static void     gimp_tag_entry_find_common_tags          (gpointer          key,
+                                                          gpointer          value,
+                                                          gpointer          user_data);
 
 static gchar *  gimp_tag_entry_get_completion_prefix     (GimpTagEntry     *entry);
 static GList *  gimp_tag_entry_get_completion_candidates (GimpTagEntry     *entry,
@@ -178,6 +181,7 @@ gimp_tag_entry_init (GimpTagEntry *entry)
 {
   entry->container            = NULL;
   entry->selected_items       = NULL;
+  entry->common_tags          = NULL;
   entry->tab_completion_index = -1;
   entry->mode                 = GIMP_TAG_ENTRY_MODE_QUERY;
   entry->description_shown    = FALSE;
@@ -221,6 +225,12 @@ gimp_tag_entry_dispose (GObject *object)
       entry->selected_items = NULL;
     }
 
+  if (entry->common_tags)
+    {
+      g_list_free (entry->common_tags);
+      entry->common_tags = NULL;
+    }
+
   if (entry->recent_list)
     {
       g_list_foreach (entry->recent_list, (GFunc) g_free, NULL);
@@ -728,8 +738,12 @@ gimp_tag_entry_assign_tags (GimpTagEntry *tag_entry)
   gint      count;
   gint      i;
   GimpTag  *tag;
-  GList    *tag_list = NULL;
-  GList    *list;
+  GList    *resource_iter;
+  GList    *tag_iter;
+  GList    *dont_remove_list = NULL;
+  GList    *remove_list      = NULL;
+  GList    *add_list         = NULL;
+  GList    *common_tags      = NULL;
 
   parsed_tags = gimp_tag_entry_parse_tags (tag_entry);
 
@@ -739,17 +753,51 @@ gimp_tag_entry_assign_tags (GimpTagEntry *tag_entry)
       tag = gimp_tag_new (parsed_tags[i]);
       if (tag)
         {
-          tag_list = g_list_append (tag_list, tag);
+          if (g_list_find_custom (tag_entry->common_tags, tag, gimp_tag_compare_func))
+            {
+              dont_remove_list = g_list_prepend (dont_remove_list, tag);
+            }
+          else
+            {
+              add_list = g_list_prepend (add_list, tag);
+            }
+
+          common_tags = g_list_prepend (common_tags, tag);
         }
     }
   g_strfreev (parsed_tags);
 
-  for (list = tag_entry->selected_items; list; list = g_list_next (list))
+  /* find common tags which were removed. */
+  for (tag_iter = tag_entry->common_tags; tag_iter; tag_iter = g_list_next (tag_iter))
+    {
+      if (! g_list_find_custom (dont_remove_list, tag_iter->data, gimp_tag_compare_func))
+        {
+          remove_list = g_list_prepend (remove_list, tag_iter->data);
+        }
+    }
+  g_list_free (dont_remove_list);
+
+  for (resource_iter = tag_entry->selected_items; resource_iter;
+       resource_iter = g_list_next (resource_iter))
     {
-      gimp_tagged_set_tags (list->data, tag_list);
+      GimpTagged       *tagged = GIMP_TAGGED (resource_iter->data);
+
+      for (tag_iter = remove_list; tag_iter; tag_iter = g_list_next (tag_iter))
+        {
+          gimp_tagged_remove_tag (tagged, GIMP_TAG (tag_iter->data));
+        }
+      for (tag_iter = add_list; tag_iter; tag_iter = g_list_next (tag_iter))
+        {
+          gimp_tagged_add_tag (tagged, GIMP_TAG (tag_iter->data));
+        }
     }
 
-  g_list_free (tag_list);
+  g_list_free (add_list);
+  g_list_free (remove_list);
+
+  /* common tags list with changes applied. */
+  g_list_free (tag_entry->common_tags);
+  tag_entry->common_tags = common_tags;
 }
 
 /**
@@ -832,8 +880,6 @@ void
 gimp_tag_entry_set_selected_items (GimpTagEntry *tag_entry,
                                    GList        *items)
 {
-  GList *list;
-
   g_return_if_fail (GIMP_IS_TAG_ENTRY (tag_entry));
 
   if (tag_entry->selected_items)
@@ -842,32 +888,17 @@ gimp_tag_entry_set_selected_items (GimpTagEntry *tag_entry,
       tag_entry->selected_items = NULL;
     }
 
-  tag_entry->selected_items = g_list_copy (items);
-
-  for (list = tag_entry->selected_items; list; list = g_list_next (list))
+  if (tag_entry->common_tags)
     {
-      if (gimp_tagged_get_tags (GIMP_TAGGED (list->data))
-          && gimp_container_have (GIMP_CONTAINER (tag_entry->container),
-                                  GIMP_OBJECT (list->data)))
-        {
-          break;
-        }
+      g_list_free (tag_entry->common_tags);
+      tag_entry->common_tags = NULL;
     }
 
+  tag_entry->selected_items = g_list_copy (items);
+
   if (tag_entry->mode == GIMP_TAG_ENTRY_MODE_ASSIGN)
     {
-      if (list)
-        {
-          gimp_tag_entry_load_selection (tag_entry, TRUE);
-          gimp_tag_entry_toggle_desc (tag_entry, FALSE);
-        }
-      else
-        {
-          tag_entry->internal_operation++;
-          gtk_editable_delete_text (GTK_EDITABLE (tag_entry), 0, -1);
-          tag_entry->internal_operation--;
-          gimp_tag_entry_toggle_desc (tag_entry, TRUE);
-        }
+      gimp_tag_entry_load_selection (tag_entry, TRUE);
     }
 }
 
@@ -875,26 +906,45 @@ static void
 gimp_tag_entry_load_selection (GimpTagEntry *tag_entry,
                                gboolean      sort)
 {
-  GimpTagged *selected_item;
-  GList      *tag_list;
   GList      *list;
   gint        insert_pos;
+  GHashTable *refcounts;
+  GList      *resource;
+  GList      *tag;
 
   tag_entry->internal_operation++;
   gtk_editable_delete_text (GTK_EDITABLE (tag_entry), 0, -1);
   tag_entry->internal_operation--;
 
   if (! tag_entry->selected_items)
-    return;
+    {
+      gimp_tag_entry_toggle_desc (tag_entry, FALSE);
+      return;
+    }
 
-  selected_item = GIMP_TAGGED (tag_entry->selected_items->data);
-  insert_pos = 0;
+  refcounts = g_hash_table_new ((GHashFunc) gimp_tag_get_hash,
+                                (GEqualFunc) gimp_tag_equals);
 
-  tag_list = g_list_copy (gimp_tagged_get_tags (selected_item));
-  if (sort)
-    tag_list = g_list_sort (tag_list, gimp_tag_compare_func);
+  /* find set of tags common to all resources. */
+  for (resource = tag_entry->selected_items; resource; resource = g_list_next (resource))
+    {
+      for (tag = gimp_tagged_get_tags (GIMP_TAGGED (resource->data)); tag;
+           tag = g_list_next (tag))
+        {
+          /* count refcount for each tag */
+          guint refcount = GPOINTER_TO_UINT (g_hash_table_lookup (refcounts, tag->data));
+          g_hash_table_insert (refcounts, tag->data, GUINT_TO_POINTER (refcount + 1));
+        }
+    }
+
+  g_hash_table_foreach (refcounts, gimp_tag_entry_find_common_tags,tag_entry);
 
-  for (list = tag_list; list; list = g_list_next (list))
+  g_hash_table_destroy (refcounts);
+
+  tag_entry->common_tags = g_list_sort (tag_entry->common_tags, gimp_tag_compare_func);
+
+  insert_pos = gtk_editable_get_position (GTK_EDITABLE (tag_entry));
+  for (list = tag_entry->common_tags; list; list = g_list_next (list))
     {
       GimpTag *tag = list->data;
       gchar   *text;
@@ -911,11 +961,24 @@ gimp_tag_entry_load_selection (GimpTagEntry *tag_entry,
       g_free (text);
     }
 
-  g_list_free (tag_list);
-
   gimp_tag_entry_commit_tags (tag_entry);
 }
 
+static void
+gimp_tag_entry_find_common_tags (gpointer key,
+                                 gpointer value,
+                                 gpointer user_data)
+{
+  guint         ref_count = GPOINTER_TO_UINT (value);
+  GimpTagEntry *tag_entry = GIMP_TAG_ENTRY (user_data);
+
+  /* FIXME: more efficient list length */
+  if (ref_count == g_list_length (tag_entry->selected_items))
+    {
+      tag_entry->common_tags = g_list_prepend (tag_entry->common_tags, key);
+    }
+}
+
 static gchar *
 gimp_tag_entry_get_completion_prefix (GimpTagEntry *entry)
 {
diff --git a/app/widgets/gimptagentry.h b/app/widgets/gimptagentry.h
index 97d30e7..a16e5bd 100644
--- a/app/widgets/gimptagentry.h
+++ b/app/widgets/gimptagentry.h
@@ -51,6 +51,7 @@ struct _GimpTagEntry
    */
   GString                *mask;
   GList                  *selected_items;
+  GList                  *common_tags;
   GList                  *recent_list;
   gint                    tab_completion_index;
   gint                    internal_operation;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]