[gtksourceview/gtksourcecompletion] Make the appending of items async.



commit 80d85752f5dbd11ef4a5951d59687c6549e09c23
Author: Ignacio Casal Quinteiro <icq gnome org>
Date:   Mon Apr 20 20:47:20 2009 +0200

    Make the appending of items async.
    
    Now items are added async so the popup will appear instantly instead of waiting to have all elements added.
---
 gtksourceview/gtksourcecompletion.c      |   94 +++++++-----
 gtksourceview/gtksourcecompletionmodel.c |  255 ++++++++++++++++++++++--------
 gtksourceview/gtksourcecompletionmodel.h |    7 +-
 3 files changed, 247 insertions(+), 109 deletions(-)

diff --git a/gtksourceview/gtksourcecompletion.c b/gtksourceview/gtksourcecompletion.c
index ef569e2..e00d178 100644
--- a/gtksourceview/gtksourcecompletion.c
+++ b/gtksourceview/gtksourcecompletion.c
@@ -76,6 +76,7 @@ enum
 	TEXT_VIEW_BUTTON_PRESS,
 	TEXT_BUFFER_DELETE_RANGE,
 	TEXT_BUFFER_INSERT_TEXT,
+	ROW_INSERTED,
 	LAST_EXTERNAL_SIGNAL
 };
 
@@ -118,6 +119,8 @@ struct _GtkSourceCompletionPrivate
 	GtkSourceCompletionProvider *filter_provider;
 	gchar *filter_criteria;
 	
+	gboolean inserting_data;
+	
 	gulong signals_ids[LAST_EXTERNAL_SIGNAL];
 };
 
@@ -499,7 +502,7 @@ do_refilter (GtkSourceCompletion *completion,
 	gtk_source_completion_model_refilter (completion->priv->model_proposals);
 	
 	/* Check if there are any proposals left */
-	if (finish_if_empty && 
+	if (finish_if_empty && !completion->priv->inserting_data &&
 	    gtk_source_completion_model_is_empty (completion->priv->model_proposals, FALSE))
 	{
 		gtk_source_completion_finish (completion);
@@ -805,19 +808,6 @@ selection_changed_cb (GtkTreeSelection    *selection,
 }
 
 static void
-add_proposal (GtkSourceCompletion         *completion,
-              GtkSourceCompletionProvider *provider,
-              GtkSourceCompletionProposal *proposal)
-{
-	GtkTreeIter iter;
-
-	gtk_source_completion_model_append (completion->priv->model_proposals,
-	                                    provider,
-	                                    proposal,
-	                                    &iter);
-}
-
-static void
 info_toggled_cb (GtkToggleButton     *widget,
 		 GtkSourceCompletion *completion)
 {
@@ -1380,6 +1370,9 @@ gtk_source_completion_finish_default (GtkSourceCompletion *completion)
 static void
 gtk_source_completion_popup_default (GtkSourceCompletion *completion)
 {
+	gtk_source_completion_utils_move_to_cursor (GTK_WINDOW (completion->priv->window),
+						    GTK_SOURCE_VIEW (completion->priv->view));
+	
 	gtk_widget_show (GTK_WIDGET (completion->priv->window));
 	gtk_widget_grab_focus (GTK_WIDGET (completion->priv->view));
 
@@ -1691,6 +1684,41 @@ selection_func (GtkTreeSelection    *selection,
 	}
 }
 
+static void
+on_row_inserted_cb (GtkTreeModel *tree_model,
+		    GtkTreePath  *path,
+		    GtkTreeIter  *iter,
+		    GtkSourceCompletion *completion)
+{
+	if (!GTK_WIDGET_VISIBLE (completion->priv->window))
+	{
+		update_selection_label (completion);
+	
+		if (!completion->priv->remember_info_visibility)
+			completion->priv->info_visible = FALSE;
+		
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (completion->priv->info_button),
+					      completion->priv->info_visible);
+	
+		g_signal_emit (completion, signals[POPUP], 0);
+		
+		g_signal_handler_disconnect (tree_model, completion->priv->signals_ids[ROW_INSERTED]);
+	}
+}
+
+static void
+on_items_added_cb (GtkSourceCompletionModel *model,
+		   GtkSourceCompletion      *completion)
+{
+	completion->priv->inserting_data = FALSE;
+
+	/* Check if there are any completions */
+	if (gtk_source_completion_model_is_empty (model, FALSE))
+	{
+		gtk_source_completion_finish (completion);
+	}
+}
+
 static GtkWidget *
 initialize_proposals_ui (GtkSourceCompletion *completion)
 {
@@ -1703,6 +1731,11 @@ initialize_proposals_ui (GtkSourceCompletion *completion)
 	completion->priv->model_proposals = 
 		gtk_source_completion_model_new ((GtkSourceCompletionModelVisibleFunc)proposals_filter_func, 
 		                                 completion);
+	
+	g_signal_connect (completion->priv->model_proposals,
+			  "items-added",
+			  G_CALLBACK (on_items_added_cb),
+			  completion);
 
 	gtk_source_completion_model_set_show_headers (completion->priv->model_proposals,
 				                      completion->priv->show_headers);
@@ -1923,12 +1956,16 @@ add_proposals (GtkSourceCompletion         *completion,
 
 	proposals = gtk_source_completion_provider_get_proposals (provider);
 	
+	completion->priv->inserting_data = TRUE;
+	
 	for (item = proposals; item; item = g_list_next (item))
 	{
 		if (GTK_IS_SOURCE_COMPLETION_PROPOSAL (item->data))
 		{
 			proposal = GTK_SOURCE_COMPLETION_PROPOSAL (item->data);
-			add_proposal (completion, provider, proposal);
+			gtk_source_completion_model_append (completion->priv->model_proposals,
+	                                                    provider,
+	                                                    proposal);
 			g_object_unref (proposal);
 		}
 	}
@@ -1990,28 +2027,11 @@ gtk_source_completion_popup (GtkSourceCompletion *completion,
 	completion->priv->active_providers = 
 		g_list_reverse (completion->priv->active_providers);
 	
-	/* Check if there are any completions */
-	if (gtk_source_completion_model_is_empty (completion->priv->model_proposals, FALSE))
-	{
-		gtk_source_completion_finish (completion);
-		return FALSE;
-	}
-
-	update_selection_label (completion);
-
-	gtk_source_completion_utils_move_to_cursor (GTK_WINDOW (completion->priv->window),
-						    GTK_SOURCE_VIEW (completion->priv->view));
-	
-	if (!GTK_WIDGET_VISIBLE (completion->priv->window))
-	{
-		if (!completion->priv->remember_info_visibility)
-			completion->priv->info_visible = FALSE;
-		
-		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (completion->priv->info_button),
-					      completion->priv->info_visible);
-	}
-	
-	g_signal_emit (completion, signals[POPUP], 0);
+	completion->priv->signals_ids[ROW_INSERTED] = 
+		g_signal_connect (completion->priv->model_proposals,
+				  "row-inserted",
+				  G_CALLBACK (on_row_inserted_cb),
+				  completion);
 	
 	return TRUE;
 }
diff --git a/gtksourceview/gtksourcecompletionmodel.c b/gtksourceview/gtksourcecompletionmodel.c
index b5b3014..2889325 100644
--- a/gtksourceview/gtksourcecompletionmodel.c
+++ b/gtksourceview/gtksourcecompletionmodel.c
@@ -1,5 +1,29 @@
+/*
+ * gtksourcecompletionmodel.c
+ * This file is part of gtksourcecompletion
+ *
+ * Copyright (C) 2009 - Jesse van den Kieboom
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, 
+ * Boston, MA  02110-1301  USA
+ */
+
 #include "gtksourcecompletionmodel.h"
 
+#define ITEMS_PER_CALLBACK 500
+
 #define GTK_SOURCE_COMPLETION_MODEL_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), GTK_TYPE_SOURCE_COMPLETION_MODEL, GtkSourceCompletionModelPrivate))
 
 typedef struct
@@ -33,8 +57,20 @@ struct _GtkSourceCompletionModelPrivate
 	gpointer filter_data;
 	
 	gboolean show_headers;
+	
+	guint idle_id;
+	GQueue *item_queue;
 };
 
+/* Signals */
+enum
+{
+	ITEMS_ADDED,
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
 static void tree_model_iface_init (gpointer g_iface, gpointer iface_data);
 
 G_DEFINE_TYPE_WITH_CODE (GtkSourceCompletionModel, 
@@ -387,6 +423,47 @@ tree_model_iface_init (gpointer g_iface,
 }
 
 static void
+free_node (ProposalNode *node)
+{
+	g_object_unref (node->provider);
+	
+	if (node->proposal != NULL)
+	{
+		if (node->changed_id != 0)
+		{
+			g_signal_handler_disconnect (node->proposal,
+						     node->changed_id);
+		}
+		g_object_unref (node->proposal);
+	}
+	
+	g_slice_free (ProposalNode, node);
+}
+
+static void
+gtk_source_completion_model_dispose (GObject *object)
+{
+	GtkSourceCompletionModel *model = GTK_SOURCE_COMPLETION_MODEL (object);
+	
+	if (model->priv->idle_id != 0)
+	{
+		g_source_remove (model->priv->idle_id);
+		model->priv->idle_id = 0;
+	}
+	
+	if (model->priv->item_queue != NULL)
+	{
+		g_queue_foreach (model->priv->item_queue,
+				 (GFunc)free_node, NULL);
+		g_queue_free (model->priv->item_queue);
+		
+		model->priv->item_queue = NULL;
+	}
+
+	G_OBJECT_CLASS (gtk_source_completion_model_parent_class)->dispose (object);
+}
+
+static void
 gtk_source_completion_model_finalize (GObject *object)
 {
 	GtkSourceCompletionModel *model = GTK_SOURCE_COMPLETION_MODEL (object);
@@ -402,8 +479,20 @@ gtk_source_completion_model_class_init (GtkSourceCompletionModelClass *klass)
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	
 	object_class->finalize = gtk_source_completion_model_finalize;
+	object_class->dispose = gtk_source_completion_model_dispose;
 
 	g_type_class_add_private (object_class, sizeof(GtkSourceCompletionModelPrivate));
+	
+	signals[ITEMS_ADDED] =
+		g_signal_new ("items-added",
+			      G_TYPE_FROM_CLASS (klass),
+			      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+			      G_STRUCT_OFFSET (GtkSourceCompletionModelClass, items_added),
+			      NULL, 
+			      NULL,
+			      g_cclosure_marshal_VOID__VOID, 
+			      G_TYPE_NONE,
+			      0);
 }
 
 static void
@@ -426,6 +515,10 @@ gtk_source_completion_model_init (GtkSourceCompletionModel *self)
 	                                                      g_direct_equal,
 	                                                      NULL,
 	                                                      free_num);
+	
+	self->priv->idle_id = 0;
+	self->priv->item_queue = g_queue_new ();
+	g_queue_init (self->priv->item_queue);
 }
 
 static void
@@ -502,20 +595,6 @@ node_update_filter_state (GtkSourceCompletionModel *model,
 }
 
 static void
-free_node (ProposalNode *node)
-{
-	g_object_unref (node->provider);
-	
-	if (node->proposal != NULL)
-	{
-		g_signal_handler_disconnect (node->proposal, node->changed_id);
-		g_object_unref (node->proposal);
-	}
-	
-	g_slice_free (ProposalNode, node);
-}
-
-static void
 update_show_headers (GtkSourceCompletionModel *model,
                      gboolean                  show)
 {
@@ -680,77 +759,109 @@ on_proposal_changed (GtkSourceCompletionProposal *proposal,
 	}
 }
 
-gboolean 
-gtk_source_completion_model_append (GtkSourceCompletionModel    *model,
-                                    GtkSourceCompletionProvider *provider,
-                                    GtkSourceCompletionProposal *proposal,
-                                    GtkTreeIter                 *iter)
+static gboolean
+idle_append (gpointer data)
 {
-	ProposalNode *node;
-	GList *item;
-	GtkTreePath *path;
-	ProposalNode *header = NULL;
+	GtkSourceCompletionModel *model = GTK_SOURCE_COMPLETION_MODEL (data);
 	HeaderInfo *info;
+	GtkTreePath *path;
+	GList *item;
+	gint i = 0;
 	
-	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_MODEL (model), FALSE);
-	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_PROVIDER (provider), FALSE);
-	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_PROPOSAL (proposal), FALSE);
-	g_return_val_if_fail (iter != NULL, FALSE);
-	
-	
-	if (g_hash_table_lookup (model->priv->num_per_provider, provider) == NULL)
+	while (i < ITEMS_PER_CALLBACK)
 	{
-		header = g_slice_new (ProposalNode);
-		header->model = model;
-		header->provider = g_object_ref (provider);
-		header->proposal = NULL;
-		header->filtered = GTK_SOURCE_COMPLETION_MODEL_FILTERED;
+		ProposalNode *node = (ProposalNode *)g_queue_pop_head (model->priv->item_queue);
+		ProposalNode *header = NULL;
+		GtkTreeIter iter;
+		
+		if (node == NULL)
+		{
+			/* If we are here we added all elements of the queue */
+			g_signal_emit (model, signals[ITEMS_ADDED], 0);
+			
+			model->priv->idle_id = 0;
+			
+			return FALSE;
+		}
+		
+		/* Check if it is a header */
+		if (g_hash_table_lookup (model->priv->num_per_provider, node->provider) == NULL)
+		{
+			header = g_slice_new (ProposalNode);
+			header->provider = g_object_ref (node->provider);
+			header->proposal = NULL;
+			header->filtered = GTK_SOURCE_COMPLETION_MODEL_FILTERED;
+			
+			append_list (model, header);
+			
+			info = g_slice_new (HeaderInfo);
+			info->item = model->priv->last;
+			info->num = 0;
+			info->visible_items = 0;
+			
+			g_hash_table_insert (model->priv->num_per_provider, node->provider, info);
+		}
+		
+		node_update_filter_state (model, node);
 		
-		append_list (model, header);
+		append_list (model, node);
 		
-		info = g_slice_new (HeaderInfo);
-		info->item = model->priv->last;
-		info->num = 0;
-		info->visible_items = 0;
+		item = model->priv->last;
+		iter.user_data = item;
+
+		num_inc (model, 
+			 node->provider, 
+			 !node->filtered || (node->filtered & GTK_SOURCE_COMPLETION_MODEL_COUNT),
+			 !node->filtered);
+
+		if (!node->filtered)
+		{
+			path = path_from_list (model, item);
+			gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
+			gtk_tree_path_free (path);
+			
+			if (header != NULL)
+			{
+				update_show_headers (model, TRUE);
+			}
+		}
 		
-		g_hash_table_insert (model->priv->num_per_provider, provider, info);
+		node->changed_id = g_signal_connect (node->proposal, 
+	                                             "changed", 
+	                                             G_CALLBACK (on_proposal_changed),
+	                                             item);
+		
+		i++;
 	}
+	
+	return TRUE;
+}
+
+void
+gtk_source_completion_model_append (GtkSourceCompletionModel    *model,
+                                    GtkSourceCompletionProvider *provider,
+                                    GtkSourceCompletionProposal *proposal)
+{
+	ProposalNode *node;
+	
+	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_MODEL (model));
+	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_PROVIDER (provider));
+	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_PROPOSAL (proposal));
+	
 
 	node = g_slice_new (ProposalNode);
-	node->model = model;
 	node->provider = g_object_ref (provider);
 	node->proposal = g_object_ref (proposal);
-
-	node_update_filter_state (model, node);
+	node->changed_id = 0;
 	
-	append_list (model, node);
-	item = model->priv->last;
-	
-	iter->user_data = item;
+	g_queue_push_tail (model->priv->item_queue, node);
 	
-	num_inc (model, 
-	         node->provider, 
-	         !node->filtered || (node->filtered & GTK_SOURCE_COMPLETION_MODEL_COUNT),
-	         !node->filtered);
-
-	if (!node->filtered)
+	if (model->priv->idle_id == 0)
 	{
-		path = path_from_list (model, item);
-		gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, iter);
-		gtk_tree_path_free (path);
-		
-		if (header != NULL)
-		{
-			update_show_headers (model, TRUE);
-		}
+		model->priv->idle_id =
+			g_idle_add ((GSourceFunc)idle_append,
+				    model);
 	}
-	
-	node->changed_id = g_signal_connect (proposal, 
-	                                     "changed", 
-	                                     G_CALLBACK (on_proposal_changed),
-	                                     item);
-
-	return TRUE;
 }
 
 void
@@ -763,6 +874,12 @@ gtk_source_completion_model_clear (GtkSourceCompletionModel *model)
 	
 	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_MODEL (model));
 	
+	/* Clear the queue of missing elements to append */
+	g_queue_foreach (model->priv->item_queue,
+			 (GFunc)free_node, NULL);
+	g_queue_clear (model->priv->item_queue);
+	model->priv->idle_id = 0;
+	
 	path = gtk_tree_path_new_first ();
 	list = model->priv->store;
 	
diff --git a/gtksourceview/gtksourcecompletionmodel.h b/gtksourceview/gtksourcecompletionmodel.h
index b885e02..ba65a13 100644
--- a/gtksourceview/gtksourcecompletionmodel.h
+++ b/gtksourceview/gtksourcecompletionmodel.h
@@ -49,6 +49,8 @@ struct _GtkSourceCompletionModel {
 
 struct _GtkSourceCompletionModelClass {
 	GObjectClass parent_class;
+	
+	void	(*items_added)	(GtkSourceCompletionModel *model);
 };
 
 typedef enum
@@ -78,10 +80,9 @@ GtkSourceCompletionModel *
 		gtk_source_completion_model_new 	(GtkSourceCompletionModelVisibleFunc func,
 							 gpointer                            userdata);
 
-gboolean 	gtk_source_completion_model_append 	(GtkSourceCompletionModel           *model,
+void		gtk_source_completion_model_append 	(GtkSourceCompletionModel           *model,
 							 GtkSourceCompletionProvider        *provider,
-							 GtkSourceCompletionProposal        *proposal,
-							 GtkTreeIter                        *iter);
+							 GtkSourceCompletionProposal        *proposal);
 
 gboolean	gtk_source_completion_model_is_empty 	(GtkSourceCompletionModel           *model,
                                                          gboolean                            invisible);



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