[gtksourceview] Implemented per-provider delay handling



commit dd41491a762dc05a6dabc16cc16c565430c2fb17
Author: Jesse van den Kieboom <jesse vandenkieboom epfl ch>
Date:   Thu Dec 31 19:22:58 2009 +0100

    Implemented per-provider delay handling

 .../words/gtksourcecompletionwords.c               |   30 +++-
 gtksourceview/gtksourcecompletion.c                |  231 ++++++++++++++++----
 2 files changed, 213 insertions(+), 48 deletions(-)
---
diff --git a/gtksourceview/completion-providers/words/gtksourcecompletionwords.c b/gtksourceview/completion-providers/words/gtksourcecompletionwords.c
index f4295cf..905508f 100644
--- a/gtksourceview/completion-providers/words/gtksourcecompletionwords.c
+++ b/gtksourceview/completion-providers/words/gtksourcecompletionwords.c
@@ -43,7 +43,8 @@ enum
 	PROP_ICON,
 	PROP_PROPOSALS_BATCH_SIZE,
 	PROP_SCAN_BATCH_SIZE,
-	PROP_MINIMUM_WORD_SIZE
+	PROP_MINIMUM_WORD_SIZE,
+	PROP_INTERACTIVE_DELAY
 };
 
 struct _GtkSourceCompletionWordsPrivate
@@ -66,6 +67,8 @@ struct _GtkSourceCompletionWordsPrivate
 	
 	GtkSourceCompletionWordsLibrary *library;
 	GList *buffers;
+
+	gint interactive_delay;
 };
 
 typedef struct
@@ -406,6 +409,9 @@ gtk_source_completion_words_set_property (GObject      *object,
 			self->priv->minimum_word_size = g_value_get_uint (value);
 			update_buffers_minimum_word_size (self);
 		break;
+		case PROP_INTERACTIVE_DELAY:
+			self->priv->interactive_delay = g_value_get_int (value);
+		break;
 		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -437,6 +443,9 @@ gtk_source_completion_words_get_property (GObject    *object,
 		case PROP_MINIMUM_WORD_SIZE:
 			g_value_set_uint (value, self->priv->minimum_word_size);
 		break;
+		case PROP_INTERACTIVE_DELAY:
+			g_value_set_int (value, self->priv->interactive_delay);
+		break;
 		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -496,8 +505,18 @@ gtk_source_completion_words_class_init (GtkSourceCompletionWordsClass *klass)
 	                                                    _("The minimum word size to complete"),
 	                                                    2,
 	                                                    G_MAXUINT,
-	                                                    3,
+	                                                    2,
 	                                                    G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	g_object_class_install_property (object_class,
+	                                 PROP_INTERACTIVE_DELAY,
+	                                 g_param_spec_int ("interactive-delay",
+	                                                   _("Interactive Delay"),
+	                                                   _("The delay before initiating interactive completion"),
+	                                                   -1,
+	                                                   G_MAXINT,
+	                                                   50,
+	                                                   G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
 	
 	g_type_class_add_private (object_class, sizeof(GtkSourceCompletionWordsPrivate));
 }
@@ -523,6 +542,12 @@ gtk_source_completion_words_get_start_iter (GtkSourceCompletionProvider *provide
 	return TRUE;
 }
 
+static gint
+gtk_source_completion_words_get_interactive_delay (GtkSourceCompletionProvider *provider)
+{
+	return GTK_SOURCE_COMPLETION_WORDS (provider)->priv->interactive_delay;
+}
+
 static void
 gtk_source_completion_words_iface_init (GtkSourceCompletionProviderIface *iface)
 {
@@ -533,6 +558,7 @@ gtk_source_completion_words_iface_init (GtkSourceCompletionProviderIface *iface)
 	iface->match = gtk_source_completion_words_match;
 
 	iface->get_start_iter = gtk_source_completion_words_get_start_iter;
+	iface->get_interactive_delay = gtk_source_completion_words_get_interactive_delay;
 }
 
 static void 
diff --git a/gtksourceview/gtksourcecompletion.c b/gtksourceview/gtksourcecompletion.c
index 1e062ef..2fc5372 100644
--- a/gtksourceview/gtksourcecompletion.c
+++ b/gtksourceview/gtksourcecompletion.c
@@ -146,6 +146,10 @@ struct _GtkSourceCompletionPrivate
 
 	gulong signals_ids[LAST_EXTERNAL_SIGNAL];
 	gboolean select_first;
+
+	gint min_auto_complete_delay;
+	GList *auto_completion_selection;
+	GtkSourceCompletionContext *auto_completion_context;
 };
 
 static guint signals[LAST_SIGNAL] = { 0 };
@@ -1397,13 +1401,110 @@ update_typing_offsets (GtkSourceCompletion *completion)
 	completion->priv->typing_line_offset = gtk_text_iter_get_line_offset (&start);
 }
 
+static GList *
+select_providers (GtkSourceCompletion        *completion,
+                  GList                      *providers,
+                  GtkSourceCompletionContext *context)
+{
+	/* Select providers based on selection */
+	GList *selection = NULL;
+	
+	if (providers == NULL)
+	{
+		providers = completion->priv->providers;
+	}
+
+	while (providers)
+	{
+		GtkSourceCompletionProvider *provider = 
+			GTK_SOURCE_COMPLETION_PROVIDER (providers->data);
+
+		if (gtk_source_completion_provider_match (provider, context))
+		{
+			selection = g_list_prepend (selection, provider);
+		}
+		
+		providers = g_list_next (providers);
+	}
+	
+	return g_list_reverse (selection);
+}
+
+static gboolean
+auto_completion_final (GtkSourceCompletion *completion)
+{
+	/* Store and set to NULL because update_completion will cancel the last
+	   completion, which will also remove the timeout source which in turn
+	   would free these guys */
+	GtkSourceCompletionContext *context = completion->priv->auto_completion_context;
+	GList *selection = completion->priv->auto_completion_selection;
+
+	completion->priv->auto_completion_context = NULL;
+	completion->priv->auto_completion_selection = NULL;
+	
+	update_completion (completion, selection, context);
+
+	/* No need to free the context since it was a floating reference which
+	   has been taken over by update_completion */
+	g_list_free (selection);
+	return FALSE;
+}
+
+static void
+auto_completion_destroy (GtkSourceCompletion *completion)
+{
+	if (completion->priv->auto_completion_context != NULL)
+	{
+		g_object_ref_sink (completion->priv->auto_completion_context);
+		g_object_unref (completion->priv->auto_completion_context);
+	}
+
+	g_list_free (completion->priv->auto_completion_selection);
+
+	completion->priv->auto_completion_context = NULL;
+	completion->priv->auto_completion_selection = NULL;
+}
+
+static gint
+minimum_auto_complete_delay (GtkSourceCompletion *completion,
+                             GList               *providers)
+{
+	gint min_delay = completion->priv->auto_complete_delay;
+
+	while (providers)
+	{
+		GtkSourceCompletionProvider *provider = providers->data;
+		gint delay = gtk_source_completion_provider_get_interactive_delay (provider);
+
+		if (delay < 0)
+		{
+			delay = completion->priv->auto_complete_delay;
+		}
+
+		if (delay < min_delay)
+		{
+			min_delay = delay;
+		}
+
+		providers = g_list_next (providers);
+	}
+
+	return min_delay;
+}
+
 static gboolean
-show_auto_completion (GtkSourceCompletion *completion)
+auto_completion_prematch (GtkSourceCompletion *completion)
 {
 	GtkTextBuffer *buffer;
 	GtkTextIter iter;
 	GtkSourceCompletionContext *context;
-	
+	gint delay;
+	GList *selection;
+
+	/* Do a prematch on the available interactive providers and determine
+	   the minimum delay to the real selection that matches the current
+	   context */
+
 	completion->priv->show_timed_out_id = 0;
 	
 	if (GTK_WIDGET_VISIBLE (completion->priv->window))
@@ -1426,10 +1527,39 @@ show_auto_completion (GtkSourceCompletion *completion)
 	              "activation",
 	              GTK_SOURCE_COMPLETION_ACTIVATION_INTERACTIVE,
 	              NULL);
-	
-	gtk_source_completion_show (completion, 
-	                            completion->priv->interactive_providers, 
-	                            context);
+
+	g_signal_emit (completion, signals[POPULATE_CONTEXT], 0, context);
+
+	selection = select_providers (completion,
+	                              completion->priv->interactive_providers,
+	                              context);
+
+	if (selection == NULL)
+	{
+		g_object_ref_sink (context);
+		g_object_unref (context);
+
+		return FALSE;
+	}
+
+	/* Check the minimum delay on this set */
+	delay = minimum_auto_complete_delay (completion, selection);
+	completion->priv->auto_completion_context = context;
+	completion->priv->auto_completion_selection = selection;
+
+	if (delay > completion->priv->min_auto_complete_delay)
+	{
+		completion->priv->show_timed_out_id =
+			g_timeout_add_full (G_PRIORITY_DEFAULT,
+			                    delay - completion->priv->min_auto_complete_delay,
+			                    (GSourceFunc)auto_completion_final,
+			                    completion,
+			                    (GDestroyNotify)auto_completion_destroy);
+	}
+	else
+	{
+		auto_completion_final (completion);
+	}
 
 	return FALSE;
 }
@@ -1437,6 +1567,11 @@ show_auto_completion (GtkSourceCompletion *completion)
 static void
 interactive_do_show (GtkSourceCompletion *completion)
 {
+	if (completion->priv->interactive_providers == NULL)
+	{
+		return;
+	}
+
 	update_typing_offsets (completion);
 
 	if (completion->priv->show_timed_out_id != 0)
@@ -1444,10 +1579,12 @@ interactive_do_show (GtkSourceCompletion *completion)
 		g_source_remove (completion->priv->show_timed_out_id);
 	}
 
+	/* Install first handler to do the match on the minimum auto complete
+	   delay */
 	completion->priv->show_timed_out_id = 
-		g_timeout_add (completion->priv->auto_complete_delay,
-			       (GSourceFunc)show_auto_completion,
-			       completion);
+		g_timeout_add (completion->priv->min_auto_complete_delay,
+		               (GSourceFunc)auto_completion_prematch,
+		               completion);
 }
 
 static void
@@ -1533,12 +1670,11 @@ buffer_mark_set_cb (GtkTextBuffer       *buffer,
 	
 	/* Repopulate completion at the new iterator */
 	g_object_set (completion->priv->context,
-	              "iter", iter,
-                      NULL);
+	              "iter", iter, NULL);
 
 	update_completion (completion,
-		           completion->priv->active_providers,
-		           completion->priv->context);
+	                   completion->priv->active_providers,
+	                   completion->priv->context);
 }
 
 static void
@@ -1692,6 +1828,14 @@ gtk_source_completion_finalize (GObject *object)
 }
 
 static void
+update_min_auto_complete_delay (GtkSourceCompletion *completion)
+{
+	completion->priv->min_auto_complete_delay =
+		minimum_auto_complete_delay (completion,
+		                             completion->priv->interactive_providers);
+}
+
+static void
 gtk_source_completion_get_property (GObject    *object,
 				    guint       prop_id,
 				    GValue     *value,
@@ -1780,6 +1924,7 @@ gtk_source_completion_set_property (GObject      *object,
 			break;
 		case PROP_AUTO_COMPLETE_DELAY:
 			completion->priv->auto_complete_delay = g_value_get_uint (value);
+			update_min_auto_complete_delay (completion);
 			break;
 		case PROP_PROPOSAL_PAGE_SIZE:
 			completion->priv->proposal_page_size = g_value_get_uint (value);
@@ -2948,35 +3093,6 @@ _gtk_source_completion_add_proposals (GtkSourceCompletion         *completion,
 	}
 }
 
-static GList *
-select_providers (GtkSourceCompletion        *completion,
-                  GList                      *providers,
-                  GtkSourceCompletionContext *context)
-{
-	/* Select providers based on selection */
-	GList *selection = NULL;
-	
-	if (providers == NULL)
-	{
-		providers = completion->priv->providers;
-	}
-
-	while (providers)
-	{
-		GtkSourceCompletionProvider *provider = 
-			GTK_SOURCE_COMPLETION_PROVIDER (providers->data);
-
-		if (gtk_source_completion_provider_match (provider, context))
-		{
-			selection = g_list_prepend (selection, provider);
-		}
-		
-		providers = g_list_next (providers);
-	}
-	
-	return g_list_reverse (selection);
-}
-
 /**
  * gtk_source_completion_show:
  * @completion: A #GtkSourceCompletion
@@ -3126,8 +3242,21 @@ gtk_source_completion_add_provider (GtkSourceCompletion          *completion,
 	if (gtk_source_completion_provider_get_activation (provider) &
 	    GTK_SOURCE_COMPLETION_ACTIVATION_INTERACTIVE)
 	{
-		completion->priv->interactive_providers = g_list_append (completion->priv->interactive_providers,
-	                                                                 provider);
+		gint delay = gtk_source_completion_provider_get_interactive_delay (provider);
+
+		completion->priv->interactive_providers =
+				g_list_append (completion->priv->interactive_providers,
+		                               provider);
+
+		if (delay >= 0 && delay < completion->priv->min_auto_complete_delay)
+		{
+			completion->priv->min_auto_complete_delay = delay;
+		}
+		else if (delay < 0 && completion->priv->auto_complete_delay <
+		                      completion->priv->min_auto_complete_delay)
+		{
+			completion->priv->min_auto_complete_delay = completion->priv->auto_complete_delay;
+		}
 	}
 
 	if (error)
@@ -3168,8 +3297,18 @@ gtk_source_completion_remove_provider (GtkSourceCompletion          *completion,
 		if (gtk_source_completion_provider_get_activation (provider) &
 		    GTK_SOURCE_COMPLETION_ACTIVATION_INTERACTIVE)
 		{
-			completion->priv->interactive_providers = g_list_remove (completion->priv->interactive_providers,
-		                                                                 provider);
+			gint delay = gtk_source_completion_provider_get_interactive_delay (provider);
+		
+			completion->priv->interactive_providers =
+					g_list_remove (completion->priv->interactive_providers,
+			                               provider);
+
+			if (delay == completion->priv->min_auto_complete_delay ||
+			    (delay == -1 && completion->priv->min_auto_complete_delay ==
+			                    completion->priv->auto_complete_delay))
+			{
+				update_min_auto_complete_delay (completion);
+			}
 		}
 
 		g_object_unref (provider);



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