gimp r26052 - in branches/soc-2008-tagging: . app/core app/widgets



Author: aurisj
Date: Thu Jul  3 22:20:32 2008
New Revision: 26052
URL: http://svn.gnome.org/viewvc/gimp?rev=26052&view=rev

Log:
2008-07-04  Aurimas JuÅka  <aurisj svn gnome org>

	* app/widgets/gimptagentry.c: support tag autocompletion.

	* app/core/gimpfilteredcontainer.[ch]: maintain tag reference count
	of all items contained in source container.



Modified:
   branches/soc-2008-tagging/ChangeLog
   branches/soc-2008-tagging/app/core/gimpfilteredcontainer.c
   branches/soc-2008-tagging/app/core/gimpfilteredcontainer.h
   branches/soc-2008-tagging/app/widgets/gimptagentry.c

Modified: branches/soc-2008-tagging/app/core/gimpfilteredcontainer.c
==============================================================================
--- branches/soc-2008-tagging/app/core/gimpfilteredcontainer.c	(original)
+++ branches/soc-2008-tagging/app/core/gimpfilteredcontainer.c	Thu Jul  3 22:20:32 2008
@@ -53,6 +53,16 @@
                                                                 GimpFilteredContainer *filtered_container);
 static void         gimp_filtered_container_src_thaw           (GimpContainer         *src_container,
                                                                 GimpFilteredContainer *filtered_container);
+static void         gimp_filtered_container_tag_added          (GimpTagged            *tagged,
+                                                                GimpTag                tag,
+                                                                GimpFilteredContainer  *filtered_container);
+static void         gimp_filtered_container_tag_removed        (GimpTagged            *tagged,
+                                                                GimpTag                tag,
+                                                                GimpFilteredContainer  *filtered_container);
+static void         gimp_filtered_container_tagged_item_added  (GimpTagged             *tagged,
+                                                                GimpFilteredContainer  *filtered_container);
+static void         gimp_filtered_container_tagged_item_removed(GimpTagged             *tagged,
+                                                                GimpFilteredContainer  *filtered_container);
 
 
 G_DEFINE_TYPE (GimpFilteredContainer, gimp_filtered_container, GIMP_TYPE_LIST)
@@ -76,6 +86,7 @@
 {
   filtered_container->src_container             = NULL;
   filtered_container->filter                    = NULL;
+  filtered_container->tag_ref_counts            = NULL;
 }
 
 static void
@@ -136,6 +147,7 @@
 
   filtered_container->src_container = src_container;
   GIMP_CONTAINER (filtered_container)->children_type = children_type;
+  filtered_container->tag_ref_counts = g_hash_table_new (g_direct_hash, g_direct_equal);
 
   gimp_filtered_container_filter (filtered_container);
 
@@ -220,6 +232,9 @@
 gimp_filtered_container_filter (GimpFilteredContainer          *filtered_container)
 {
   gimp_container_foreach (filtered_container->src_container,
+                          (GFunc) gimp_filtered_container_tagged_item_added,
+                          filtered_container);
+  gimp_container_foreach (filtered_container->src_container,
                           (GFunc) add_filtered_item, filtered_container);
 }
 
@@ -228,6 +243,9 @@
                                  GimpObject            *obj,
                                  GimpFilteredContainer *filtered_container)
 {
+  gimp_filtered_container_tagged_item_added (GIMP_TAGGED (obj),
+                                             filtered_container);
+
   if (! gimp_container_frozen (src_container))
     {
       gimp_container_add (GIMP_CONTAINER (filtered_container), obj);
@@ -239,6 +257,9 @@
                                     GimpObject            *obj,
                                     GimpFilteredContainer *filtered_container)
 {
+  gimp_filtered_container_tagged_item_removed (GIMP_TAGGED (obj),
+                                               filtered_container);
+
   if (! gimp_container_frozen (src_container)
       && gimp_container_have (GIMP_CONTAINER (filtered_container), obj))
     {
@@ -250,8 +271,9 @@
 gimp_filtered_container_src_freeze (GimpContainer              *src_container,
                                     GimpFilteredContainer      *filtered_container)
 {
-  gimp_container_clear (GIMP_CONTAINER (filtered_container));
   gimp_container_freeze (GIMP_CONTAINER (filtered_container));
+  gimp_container_clear (GIMP_CONTAINER (filtered_container));
+  g_hash_table_remove_all (filtered_container->tag_ref_counts);
 }
 
 static void
@@ -261,3 +283,88 @@
   gimp_filtered_container_filter (filtered_container);
   gimp_container_thaw (GIMP_CONTAINER (filtered_container));
 }
+
+static void
+gimp_filtered_container_tagged_item_added (GimpTagged                  *tagged,
+                                           GimpFilteredContainer       *filtered_container)
+{
+  GList        *tag_iterator;
+
+  tag_iterator = gimp_tagged_get_tags (tagged);
+  while (tag_iterator)
+    {
+      gimp_filtered_container_tag_added (tagged,
+                                         GPOINTER_TO_UINT (tag_iterator->data),
+                                         filtered_container);
+      tag_iterator = g_list_next (tag_iterator);
+    }
+
+  g_signal_connect (tagged, "tag-added",
+                    G_CALLBACK (gimp_filtered_container_tag_added),
+                    filtered_container);
+  g_signal_connect (tagged, "tag-removed",
+                    G_CALLBACK (gimp_filtered_container_tag_removed),
+                    filtered_container);
+}
+
+static void
+gimp_filtered_container_tagged_item_removed (GimpTagged                *tagged,
+                                             GimpFilteredContainer     *filtered_container)
+{
+  GList        *tag_iterator;
+
+  g_signal_handlers_disconnect_by_func (tagged,
+                                        G_CALLBACK (gimp_filtered_container_tag_added),
+                                        filtered_container);
+  g_signal_handlers_disconnect_by_func (tagged,
+                                        G_CALLBACK (gimp_filtered_container_tag_removed),
+                                        filtered_container);
+
+  tag_iterator = gimp_tagged_get_tags (tagged);
+  while (tag_iterator)
+    {
+      gimp_filtered_container_tag_removed (tagged,
+                                           GPOINTER_TO_UINT (tag_iterator->data),
+                                           filtered_container);
+      tag_iterator = g_list_next (tag_iterator);
+    }
+
+}
+
+static void
+gimp_filtered_container_tag_added (GimpTagged            *tagged,
+                                   GimpTag                tag,
+                                   GimpFilteredContainer  *filtered_container)
+{
+  gint                  ref_count;
+
+  ref_count = GPOINTER_TO_INT (g_hash_table_lookup (filtered_container->tag_ref_counts,
+                                                    GUINT_TO_POINTER (tag)));
+  ref_count++;
+  g_hash_table_insert (filtered_container->tag_ref_counts,
+                       GUINT_TO_POINTER (tag),
+                       GINT_TO_POINTER (ref_count));
+}
+
+static void
+gimp_filtered_container_tag_removed (GimpTagged                  *tagged,
+                                     GimpTag                      tag,
+                                     GimpFilteredContainer       *filtered_container)
+{
+  gint                  ref_count;
+
+  ref_count = GPOINTER_TO_INT (g_hash_table_lookup (filtered_container->tag_ref_counts,
+                                                    GUINT_TO_POINTER (tag)));
+  ref_count--;
+  if (ref_count > 0)
+    {
+      g_hash_table_insert (filtered_container->tag_ref_counts,
+                           GUINT_TO_POINTER (tag),
+                           GINT_TO_POINTER (ref_count));
+    }
+  else
+    {
+      g_hash_table_remove (filtered_container->tag_ref_counts,
+                           GUINT_TO_POINTER (tag));
+    }
+}

Modified: branches/soc-2008-tagging/app/core/gimpfilteredcontainer.h
==============================================================================
--- branches/soc-2008-tagging/app/core/gimpfilteredcontainer.h	(original)
+++ branches/soc-2008-tagging/app/core/gimpfilteredcontainer.h	Thu Jul  3 22:20:32 2008
@@ -41,6 +41,7 @@
 
   GimpContainer        *src_container;
   GList                *filter;
+  GHashTable           *tag_ref_counts;
 };
 
 struct _GimpFilteredContainerClass

Modified: branches/soc-2008-tagging/app/widgets/gimptagentry.c
==============================================================================
--- branches/soc-2008-tagging/app/widgets/gimptagentry.c	(original)
+++ branches/soc-2008-tagging/app/widgets/gimptagentry.c	Thu Jul  3 22:20:32 2008
@@ -35,10 +35,17 @@
 
 #include "gimptagentry.h"
 
+#define TAG_SEPARATOR_STR   ","
+
 static void     gimp_tag_entry_activate        (GtkEntry          *entry,
                                                 gpointer           unused);
 static void     gimp_tag_entry_changed         (GtkEntry          *entry,
                                                 gpointer           unused);
+static void     gimp_tag_entry_insert_text     (GtkEditable       *editable,
+                                                gchar             *new_text,
+                                                gint               text_length,
+                                                gint              *position,
+                                                gpointer           user_data);
 
 static void     gimp_tag_entry_query_tag       (GimpTagEntry      *entry);
 
@@ -50,6 +57,16 @@
 
 static gchar ** gimp_tag_entry_parse_tags      (GimpTagEntry      *entry);
 
+static gchar*   gimp_tag_entry_get_completion_prefix     (GimpTagEntry         *entry);
+static GList *  gimp_tag_entry_get_completion_candidates (GimpTagEntry         *tag_entry,
+                                                          gchar               **used_tags,
+                                                          gchar                *prefix);
+static gchar *  gimp_tag_entry_get_completion_string     (GimpTagEntry         *tag_entry,
+                                                          GList                *candidates,
+                                                          gchar                *prefix);
+static gboolean gimp_tag_entry_auto_complete             (GimpTagEntry         *tag_entry);
+
+
 G_DEFINE_TYPE (GimpTagEntry, gimp_tag_entry, GTK_TYPE_ENTRY);
 
 #define parent_class gimp_tag_entry_parent_class
@@ -65,7 +82,7 @@
 gimp_tag_entry_init (GimpTagEntry *entry)
 {
   entry->tagged_container      = NULL;
-  entry->selected_items          = NULL;
+  entry->selected_items        = NULL;
   entry->mode                  = GIMP_TAG_ENTRY_MODE_QUERY;
 
   g_signal_connect (entry, "activate",
@@ -74,6 +91,9 @@
   g_signal_connect (entry, "changed",
                     G_CALLBACK (gimp_tag_entry_changed),
                     NULL);
+  g_signal_connect (entry, "insert-text",
+                    G_CALLBACK (gimp_tag_entry_insert_text),
+                    NULL);
 }
 
 GtkWidget *
@@ -98,9 +118,20 @@
                          gpointer               unused)
 {
   GimpTagEntry         *tag_entry;
+  gint                  selection_start;
+  gint                  selection_end;
 
   tag_entry = GIMP_TAG_ENTRY (entry);
 
+  gtk_editable_get_selection_bounds (GTK_EDITABLE (entry),
+                                     &selection_start, &selection_end);
+  if (selection_start != selection_end)
+    {
+      gtk_editable_select_region (GTK_EDITABLE (entry),
+                                  selection_end, selection_end);
+      return;
+    }
+
   if (tag_entry->mode == GIMP_TAG_ENTRY_MODE_ASSIGN)
     {
       gimp_tag_entry_assign_tags (GIMP_TAG_ENTRY (entry));
@@ -122,6 +153,17 @@
 }
 
 static void
+gimp_tag_entry_insert_text     (GtkEditable       *editable,
+                                gchar             *new_text,
+                                gint               text_length,
+                                gint              *position,
+                                gpointer           user_data)
+{
+  g_idle_add ((GSourceFunc)gimp_tag_entry_auto_complete,
+              editable);
+}
+
+static void
 gimp_tag_entry_query_tag (GimpTagEntry         *entry)
 {
   GimpTagEntry                 *tag_entry;
@@ -147,6 +189,51 @@
                                       query_list);
 }
 
+static gboolean
+gimp_tag_entry_auto_complete (GimpTagEntry     *tag_entry)
+{
+  gchar                *completion_prefix;
+  GList                *completion_candidates;
+  gchar               **tags;
+  gchar                *completion;
+  gint                  start_position;
+  gint                  end_position;
+  GtkEntry             *entry;
+
+  entry = GTK_ENTRY (tag_entry);
+
+  completion_prefix =
+      gimp_tag_entry_get_completion_prefix (GIMP_TAG_ENTRY (entry));
+  tags = gimp_tag_entry_parse_tags (GIMP_TAG_ENTRY (entry));
+  completion_candidates =
+      gimp_tag_entry_get_completion_candidates (GIMP_TAG_ENTRY (entry),
+                                                tags,
+                                                completion_prefix);
+  completion =
+      gimp_tag_entry_get_completion_string (GIMP_TAG_ENTRY (entry),
+                                            completion_candidates,
+                                            completion_prefix);
+
+  if (completion
+      && strlen (completion) > 0)
+    {
+      start_position = gtk_editable_get_position (GTK_EDITABLE (entry));
+      end_position = start_position;
+      gtk_editable_insert_text (GTK_EDITABLE (entry),
+                                completion, strlen (completion),
+                                &end_position);
+      gtk_editable_select_region (GTK_EDITABLE (entry),
+                                  start_position, end_position);
+    }
+
+  g_free (completion);
+  g_strfreev (tags);
+  g_list_free (completion_candidates);
+  g_free (completion_prefix);
+
+  return FALSE;
+}
+
 static void
 gimp_tag_entry_assign_tags (GimpTagEntry       *tag_entry)
 {
@@ -276,3 +363,171 @@
     }
 }
 
+static gchar*
+gimp_tag_entry_get_completion_prefix (GimpTagEntry             *entry)
+{
+  gchar        *original_string;
+  gchar        *prefix_start;
+  gchar        *prefix;
+  gchar        *cursor;
+  gint          position;
+  gint          i;
+  gunichar      separator;
+  gunichar      c;
+
+  original_string = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
+  position = gtk_editable_get_position (GTK_EDITABLE (entry));
+  cursor = original_string;
+  prefix_start = original_string;
+  separator = g_utf8_get_char (TAG_SEPARATOR_STR);
+  for (i = 0; i < position; i++)
+    {
+      c = g_utf8_get_char (cursor);
+      cursor = g_utf8_next_char (cursor);
+      if (c == separator)
+        {
+          prefix_start = cursor;
+        }
+    }
+  do
+    {
+      c = g_utf8_get_char (cursor);
+      if (c == separator)
+        {
+          *cursor = '\0';
+          break;
+        }
+      cursor = g_utf8_next_char (cursor);
+    } while (c);
+
+  prefix = g_strdup (g_strstrip (prefix_start));
+  g_free (original_string);
+
+  return prefix;
+}
+
+static GList *
+gimp_tag_entry_get_completion_candidates (GimpTagEntry         *tag_entry,
+                                          gchar               **used_tags,
+                                          gchar                *prefix)
+{
+  GList        *candidates = NULL;
+  GList        *all_tags;
+  GList        *tag_iterator;
+  GimpTag       tag;
+  gint          i;
+  gint          length;
+
+  if (!prefix
+      || strlen (prefix) < 1)
+    {
+      return NULL;
+    }
+
+  all_tags = g_hash_table_get_keys (tag_entry->tagged_container->tag_ref_counts);
+  tag_iterator = all_tags;
+  length = g_strv_length (used_tags);
+  while (tag_iterator)
+    {
+      tag = GPOINTER_TO_UINT (tag_iterator->data);
+      if (g_str_has_prefix (g_quark_to_string (tag), prefix))
+        {
+          /* check if tag is not already entered */
+          for (i = 0; i < length; i++)
+            {
+              if (! strcmp (g_quark_to_string (tag), used_tags[i]))
+                {
+                  break;
+                }
+            }
+
+          if (i == length)
+            {
+              candidates = g_list_append (candidates, tag_iterator->data);
+            }
+        }
+      tag_iterator = g_list_next (tag_iterator);
+    }
+  g_list_free (all_tags);
+
+  return candidates;
+}
+
+static gchar *
+gimp_tag_entry_get_completion_string (GimpTagEntry             *tag_entry,
+                                      GList                    *candidates,
+                                      gchar                    *prefix)
+{
+  const gchar **completions;
+  guint         length;
+  guint         i;
+  GList        *candidate_iterator;
+  const gchar  *candidate_string;
+  gint          prefix_length;
+  gunichar      c;
+  gunichar      d;
+  gint          num_chars_match;
+  gchar        *completion;
+  gchar        *completion_end;
+  gint          completion_length;
+
+  if (! candidates)
+    {
+      return NULL;
+    }
+
+  prefix_length = strlen (prefix);
+  length = g_list_length (candidates);
+  if (length < 2)
+    {
+      candidate_string = g_quark_to_string (GPOINTER_TO_UINT (candidates->data));
+      return g_strdup (candidate_string + prefix_length);
+    }
+
+  completions = g_malloc (length * sizeof (gchar*));
+  candidate_iterator = candidates;
+  for (i = 0; i < length; i++)
+    {
+      candidate_string =
+          g_quark_to_string (GPOINTER_TO_UINT (candidate_iterator->data));
+      completions[i] = candidate_string + prefix_length;
+      candidate_iterator = g_list_next (candidate_iterator);
+    }
+
+  num_chars_match = 0;
+  do
+    {
+      c = g_utf8_get_char (completions[0]);
+      if (!c)
+        {
+          break;
+        }
+
+      for (i = 1; i < length; i++)
+        {
+          d = g_utf8_get_char (completions[i]);
+          if (c != d)
+            {
+              candidate_string = g_quark_to_string (GPOINTER_TO_UINT (candidates->data));
+              candidate_string += prefix_length;
+              completion_end = g_utf8_offset_to_pointer (candidate_string,
+                                                         num_chars_match);
+              completion_length = completion_end - candidate_string;
+              completion = g_malloc (completion_length + 1);
+              memcpy (completion, candidate_string, completion_length);
+              completion[completion_length] = '\0';
+
+              g_free (completions);
+              return completion;
+            }
+          completions[i] = g_utf8_next_char (completions[i]);
+        }
+      completions[0] = g_utf8_next_char (completions[0]);
+      num_chars_match++;
+    } while (c);
+  g_free (completions);
+
+  candidate_string = g_quark_to_string (GPOINTER_TO_UINT (candidates->data));
+  return g_strdup (candidate_string + prefix_length);
+}
+



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