gtksourceview r2280 - in branches/gtksourcecompletion: gtksourceview tests



Author: jessevdk
Date: Mon Apr 13 23:12:12 2009
New Revision: 2280
URL: http://svn.gnome.org/viewvc/gtksourceview?rev=2280&view=rev

Log:
Implemented showing headers of providers in the completion model
Implemented provider icons


Modified:
   branches/gtksourcecompletion/gtksourceview/gtksourcecompletion.c
   branches/gtksourcecompletion/gtksourceview/gtksourcecompletionmodel.c
   branches/gtksourcecompletion/gtksourceview/gtksourcecompletionmodel.h
   branches/gtksourcecompletion/gtksourceview/gtksourcecompletionprovider.c
   branches/gtksourcecompletion/gtksourceview/gtksourcecompletionprovider.h
   branches/gtksourcecompletion/tests/completion-simple.c
   branches/gtksourcecompletion/tests/gsc-provider-test.c
   branches/gtksourcecompletion/tests/gsc-provider-test.h

Modified: branches/gtksourcecompletion/gtksourceview/gtksourcecompletion.c
==============================================================================
--- branches/gtksourcecompletion/gtksourceview/gtksourcecompletion.c	(original)
+++ branches/gtksourcecompletion/gtksourceview/gtksourcecompletion.c	Mon Apr 13 23:12:12 2009
@@ -60,6 +60,7 @@
 	PROP_MANAGE_KEYS,
 	PROP_REMEMBER_INFO_VISIBILITY,
 	PROP_SELECT_ON_SHOW,
+	PROP_SHOW_HEADERS,
 	
 	PROP_MINIMUM_AUTO_COMPLETE_LENGTH,
 	PROP_AUTO_COMPLETE_DELAY
@@ -83,6 +84,7 @@
 	GtkWidget *selection_label;
 	GtkWidget *bottom_bar;
 	GtkWidget *default_info;
+	GtkWidget *selection_image;
 	
 	GtkWidget *tree_view_proposals;
 	GtkSourceCompletionModel *model_proposals;
@@ -92,6 +94,7 @@
 	gboolean remember_info_visibility;
 	gboolean info_visible;
 	gboolean select_on_show;
+	gboolean show_headers;
 	
 	/* Completion management */
 	GtkSourceView *view;
@@ -164,7 +167,11 @@
 	return activated;
 }
 
-typedef gboolean (*ProposalSelector)(GtkTreeModel *model, GtkTreeIter *iter, gboolean hasselection, gpointer userdata);
+typedef gboolean (*ProposalSelector)(GtkSourceCompletion *completion,
+                                     GtkTreeModel        *model, 
+                                     GtkTreeIter         *iter, 
+                                     gboolean             hasselection, 
+                                     gpointer             userdata);
 
 static gboolean
 select_proposal (GtkSourceCompletion *completion,
@@ -193,7 +200,7 @@
 	
 	hasselection = gtk_tree_selection_get_selected (selection, NULL, &iter);
 	
-	if (selector (model, &iter, hasselection, userdata))
+	if (selector (completion, model, &iter, hasselection, userdata))
 	{
 		gtk_tree_selection_select_iter (selection, &iter);
 		
@@ -212,77 +219,162 @@
 	return TRUE;
 }
 
+static void
+scroll_to_iter (GtkSourceCompletion *completion,
+                GtkTreeModel        *model,
+                GtkTreeIter         *iter)
+{
+	GtkTreePath *path;
+
+	path = gtk_tree_model_get_path (model, iter);
+	gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (completion->priv->tree_view_proposals),
+				      path, 
+				      NULL, 
+				      FALSE, 
+				      0, 
+				      0);
+	gtk_tree_path_free (path);
+}
+
 static gboolean
-selector_first (GtkTreeModel *model,
-                GtkTreeIter  *iter,
-                gboolean      hasselection,
-                gpointer      userdata)
+selector_first (GtkSourceCompletion *completion,
+                GtkTreeModel        *model,
+                GtkTreeIter         *iter,
+                gboolean             hasselection,
+                gpointer             userdata)
 {
-	return gtk_tree_model_get_iter_first (model, iter);
+	gboolean ret;
+	gboolean hasfirst;
+	GtkTreeIter first;
+	
+	ret = gtk_tree_model_get_iter_first (model, iter);
+	hasfirst = ret;
+	first = *iter;
+	
+	while (ret && gtk_source_completion_model_iter_is_header (
+			GTK_SOURCE_COMPLETION_MODEL (model), iter))
+	{
+		ret = gtk_tree_model_iter_next (model, iter);
+	}
+	
+	if (hasfirst && !ret)
+	{
+		scroll_to_iter (completion, model, &first);
+	}
+	
+	return ret;
 }
 
 static gboolean
-selector_last (GtkTreeModel *model,
-               GtkTreeIter  *iter,
-               gboolean      hasselection,
-               gpointer      userdata)
+selector_last (GtkSourceCompletion *completion,
+               GtkTreeModel        *model,
+               GtkTreeIter         *iter,
+               gboolean             hasselection,
+               gpointer             userdata)
 {
-	gint num = gtk_tree_model_iter_n_children (model, NULL);
-	return gtk_tree_model_iter_nth_child (model, iter, NULL, num - 1);
+	gboolean ret;
+	gboolean haslast;
+	GtkTreeIter last;
+
+	ret = gtk_source_completion_model_iter_last (GTK_SOURCE_COMPLETION_MODEL (model),
+	                                             iter);
+	
+	haslast = ret;
+	last = *iter;
+
+	while (ret && gtk_source_completion_model_iter_is_header (
+			GTK_SOURCE_COMPLETION_MODEL (model), iter))
+	{
+		ret = gtk_source_completion_model_iter_previous (GTK_SOURCE_COMPLETION_MODEL (model), 
+		                                                 iter);
+	}
+	
+	if (haslast && !ret)
+	{
+		scroll_to_iter (completion, model, &last);
+	}
+	
+	return ret;
 }
 
 static gboolean
-selector_previous (GtkTreeModel *model,
-                   GtkTreeIter  *iter,
-                   gboolean      hasselection,
-                   gpointer      userdata)
+selector_previous (GtkSourceCompletion *completion,
+                   GtkTreeModel        *model,
+                   GtkTreeIter         *iter,
+                   gboolean             hasselection,
+                   gpointer             userdata)
 {
 	gint num = GPOINTER_TO_INT (userdata);
-	GtkTreePath *path;
 	gboolean ret = FALSE;
-	
+	GtkTreeIter next;
+	GtkTreeIter last;
+
 	if (!hasselection)
 	{
-		return selector_last (model, iter, hasselection, userdata);
+		return selector_last (completion, model, iter, hasselection, userdata);
 	}
 	
-	path = gtk_tree_model_get_path (model, iter);
-	
-	while (num > 0 && gtk_tree_path_prev (path))
+	next = *iter;
+	last = *iter;
+
+	while (num > 0 && gtk_source_completion_model_iter_previous (
+				GTK_SOURCE_COMPLETION_MODEL (model), &next))
 	{
-		ret = TRUE;
-		--num;
+		if (!gtk_source_completion_model_iter_is_header (GTK_SOURCE_COMPLETION_MODEL (model),
+		                                                 &next))
+		{
+			ret = TRUE;
+			*iter = next;
+			--num;
+		}
+
+		last = next;
 	}
 	
-	ret = ret && gtk_tree_model_get_iter (model, iter, path);
-	gtk_tree_path_free (path);
+	if (!ret)
+	{
+		scroll_to_iter (completion, model, &last);
+	}
 	
 	return ret;
 }
 
 static gboolean
-selector_next (GtkTreeModel *model,
-               GtkTreeIter  *iter,
-               gboolean      hasselection,
-               gpointer      userdata)
+selector_next (GtkSourceCompletion *completion,
+               GtkTreeModel        *model,
+               GtkTreeIter         *iter,
+               gboolean             hasselection,
+               gpointer             userdata)
 {
 	gint num = GPOINTER_TO_INT (userdata);
 	gboolean ret = FALSE;
 	GtkTreeIter next;
-
+	GtkTreeIter last;
+	
 	if (!hasselection)
 	{
-		return selector_first (model, iter, hasselection, userdata);
+		return selector_first (completion, model, iter, hasselection, userdata);
 	}
 	
 	next = *iter;
+	last = *iter;
 
 	while (num > 0 && gtk_tree_model_iter_next (model, &next))
 	{
-		ret = TRUE;
+		if (!gtk_source_completion_model_iter_is_header (GTK_SOURCE_COMPLETION_MODEL (model),
+		                                                 &next))
+		{
+			ret = TRUE;
+			*iter = next;
+			--num;
+		}
 		
-		*iter = next;
-		--num;
+		last = next;
+	}
+	
+	if (!ret)
+	{
+		scroll_to_iter (completion, model, &last);
 	}
 	
 	return ret;
@@ -347,12 +439,17 @@
 	if (completion->priv->filter_provider == NULL)
 	{
 		name = g_strdup_printf("[<i>%s</i>]", _("All"));
+		
+		gtk_image_clear (GTK_IMAGE (completion->priv->selection_image));
 	}
 	else
 	{
 		name = g_markup_escape_text (
 			gtk_source_completion_provider_get_name (completion->priv->filter_provider),
 			-1);
+
+		gtk_image_set_from_pixbuf (GTK_IMAGE (completion->priv->selection_image),
+                           (GdkPixbuf *)gtk_source_completion_provider_get_icon (completion->priv->filter_provider));
 	}
 	
 	if (num > 1)
@@ -1176,6 +1273,9 @@
 		case PROP_SELECT_ON_SHOW:
 			g_value_set_boolean (value, completion->priv->select_on_show);
 			break;
+		case PROP_SHOW_HEADERS:
+			g_value_set_boolean (value, completion->priv->show_headers);
+			break;
 		case PROP_AUTO_COMPLETE_DELAY:
 			g_value_set_uint (value, completion->priv->auto_complete_delay);
 			break;
@@ -1216,6 +1316,15 @@
 		case PROP_SELECT_ON_SHOW:
 			completion->priv->select_on_show = g_value_get_boolean (value);
 			break;
+		case PROP_SHOW_HEADERS:
+			completion->priv->show_headers = g_value_get_boolean (value);
+			
+			if (completion->priv->model_proposals != NULL)
+			{
+				gtk_source_completion_model_set_show_headers (completion->priv->model_proposals,
+				                                              completion->priv->show_headers);
+			}
+			break;
 		case PROP_AUTO_COMPLETE_DELAY:
 			completion->priv->auto_complete_delay = g_value_get_uint (value);
 			break;
@@ -1305,6 +1414,20 @@
 							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
 
 	/**
+	 * GtkSourceCompletion:show-headers:
+	 *
+	 * %TRUE if providers should be shown as separator headers in the
+	 * popup when there are proposals from multiple providers
+	 */
+	g_object_class_install_property (object_class,
+					 PROP_SHOW_HEADERS,
+					 g_param_spec_boolean ("show-headers",
+							      _("Show Headers"),
+							      _("Show provider headers when proposals from multiple providers are available"),
+							      TRUE,
+							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+	/**
 	 * GtkSourceCompletion:auto-complete-delay:
 	 *
 	 * The auto completion delay when typing
@@ -1362,6 +1485,127 @@
 
 }
 
+static void
+render_proposal_icon_func (GtkTreeViewColumn   *column,
+                           GtkCellRenderer     *cell,
+                           GtkTreeModel        *model,
+                           GtkTreeIter         *iter,
+                           GtkSourceCompletion *completion)
+{
+	gboolean isheader;
+	GdkPixbuf *icon;
+	GtkStyle *style;
+	
+	isheader = gtk_source_completion_model_iter_is_header (completion->priv->model_proposals, 
+	                                                       iter);
+	
+	style = gtk_widget_get_style (GTK_WIDGET (completion->priv->tree_view_proposals));
+	
+	if (isheader)
+	{
+		g_object_set (cell, 
+		              "cell-background-gdk", &(style->bg[GTK_STATE_INSENSITIVE]), 
+		              NULL);
+	}
+	else
+	{
+		g_object_set (cell,
+		              "cell-background-set", FALSE,
+		              NULL);
+	}
+	
+	gtk_tree_model_get (model, 
+	                    iter, 
+	                    GTK_SOURCE_COMPLETION_MODEL_COLUMN_ICON,
+	                    &icon,
+	                    -1);
+
+	g_object_set (cell, "pixbuf", icon, NULL);
+	
+	if (icon)
+	{
+		g_object_unref (icon);
+	}
+}
+
+static void
+render_proposal_text_func (GtkTreeViewColumn   *column,
+                           GtkCellRenderer     *cell,
+                           GtkTreeModel        *model,
+                           GtkTreeIter         *iter,
+                           GtkSourceCompletion *completion)
+{
+	gchar *name;
+	GtkSourceCompletionProvider *provider;
+	gboolean isheader;
+	GtkStyle *style;
+	
+	isheader = gtk_source_completion_model_iter_is_header (completion->priv->model_proposals, 
+		                                               iter);
+
+	if (isheader)
+	{
+		gtk_tree_model_get (model, 
+		                    iter, 
+		                    GTK_SOURCE_COMPLETION_MODEL_COLUMN_PROVIDER, 
+		                    &provider, 
+		                    -1);
+		
+		name = g_strdup_printf ("<b>%s</b>", 
+		                        g_markup_escape_text (gtk_source_completion_provider_get_name (provider),
+		                                              -1));
+
+		style = gtk_widget_get_style (GTK_WIDGET (completion->priv->tree_view_proposals));
+
+		g_object_set (cell, 
+		              "markup", name,
+		              "background-gdk", &(style->bg[GTK_STATE_INSENSITIVE]), 
+		              "foreground-gdk", &(style->fg[GTK_STATE_INSENSITIVE]), 
+		              NULL);
+		g_free (name);
+		
+		g_object_unref (provider);
+	}
+	else
+	{
+		gtk_tree_model_get (model, 
+		                    iter, 
+		                    GTK_SOURCE_COMPLETION_MODEL_COLUMN_LABEL, 
+		                    &name, 
+		                    -1);
+
+		g_object_set (cell, 
+		              "text", name, 
+		              "background-set", FALSE, 
+		              "foreground-set", FALSE,
+		              NULL);
+
+		g_free (name);
+	}
+}
+
+static gboolean
+selection_func (GtkTreeSelection    *selection,
+                GtkTreeModel        *model,
+                GtkTreePath         *path,
+                gboolean             path_currently_selected,
+                GtkSourceCompletion *completion)
+{
+	GtkTreeIter iter;
+	
+	gtk_tree_model_get_iter (model, &iter, path);
+	
+	if (gtk_source_completion_model_iter_is_header (completion->priv->model_proposals,
+	                                                &iter))
+	{
+		return path_currently_selected;
+	}
+	else
+	{
+		return TRUE;
+	}
+}
+
 static GtkWidget *
 initialize_proposals_ui (GtkSourceCompletion *completion)
 {
@@ -1375,9 +1619,13 @@
 		gtk_source_completion_model_new ((GtkSourceCompletionModelVisibleFunc)proposals_filter_func, 
 		                                 completion);
 
+	gtk_source_completion_model_set_show_headers (completion->priv->model_proposals,
+				                      completion->priv->show_headers);
 	tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (completion->priv->model_proposals));
 	completion->priv->tree_view_proposals = tree_view;
 	
+	gtk_tree_view_set_show_expanders (GTK_TREE_VIEW (tree_view), FALSE);
+	
 	gtk_widget_show (tree_view);
 	g_object_set (tree_view, "can-focus", FALSE, NULL);
 
@@ -1388,12 +1636,22 @@
 	renderer = gtk_cell_renderer_pixbuf_new ();
 	
 	gtk_tree_view_column_pack_start (column, renderer, FALSE);
-	gtk_tree_view_column_set_attributes (column, renderer, "pixbuf", GTK_SOURCE_COMPLETION_MODEL_COLUMN_ICON, NULL);
+
+	gtk_tree_view_column_set_cell_data_func (column,
+	                                         renderer,
+	                                         (GtkTreeCellDataFunc)render_proposal_icon_func,
+	                                         completion,
+	                                         NULL);
 	
 	renderer = gtk_cell_renderer_text_new ();
 	gtk_tree_view_column_pack_start (column, renderer, TRUE);
+	
+	gtk_tree_view_column_set_cell_data_func (column,
+	                                         renderer,
+	                                         (GtkTreeCellDataFunc)render_proposal_text_func,
+	                                         completion,
+	                                         NULL);
 
-	gtk_tree_view_column_set_attributes (column, renderer, "text", GTK_SOURCE_COMPLETION_MODEL_COLUMN_LABEL, NULL);
 	gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
 
 	g_signal_connect (tree_view,
@@ -1402,6 +1660,10 @@
 			  completion);
 	
 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tree_view));
+	gtk_tree_selection_set_select_function (selection,
+	                                        (GtkTreeSelectionFunc)selection_func,
+	                                        completion,
+	                                        NULL);
 	g_signal_connect (selection,
 			  "changed",
 			  G_CALLBACK (selection_changed_cb),
@@ -1469,7 +1731,14 @@
 			  FALSE, 
 			  TRUE, 
 			  10);
-
+			  
+	completion->priv->selection_image = gtk_image_new ();
+	gtk_widget_show (completion->priv->selection_image);
+	gtk_box_pack_end (GTK_BOX (completion->priv->bottom_bar),
+	                  completion->priv->selection_image,
+	                  FALSE,
+	                  TRUE,
+	                  0);
 
 	container = initialize_proposals_ui (completion);
 

Modified: branches/gtksourcecompletion/gtksourceview/gtksourcecompletionmodel.c
==============================================================================
--- branches/gtksourcecompletion/gtksourceview/gtksourcecompletionmodel.c	(original)
+++ branches/gtksourcecompletion/gtksourceview/gtksourcecompletionmodel.c	Mon Apr 13 23:12:12 2009
@@ -10,6 +10,13 @@
 	GtkSourceCompletionModelFilterFlag filtered;
 } ProposalNode;
 
+typedef struct
+{
+	GList *item;
+	guint num;
+	guint visible_items;
+} HeaderInfo;
+
 struct _GtkSourceCompletionModelPrivate
 {
 	GType column_types[GTK_SOURCE_COMPLETION_MODEL_N_COLUMNS];
@@ -21,6 +28,8 @@
 	
 	GtkSourceCompletionModelVisibleFunc filter;
 	gpointer filter_data;
+	
+	gboolean show_headers;
 };
 
 static void tree_model_iface_init (gpointer g_iface, gpointer iface_data);
@@ -198,7 +207,18 @@
 			g_value_set_string (value, gtk_source_completion_proposal_get_label (node->proposal));
 			break;
 		case GTK_SOURCE_COMPLETION_MODEL_COLUMN_ICON:
-			g_value_set_object (value, (gpointer)gtk_source_completion_proposal_get_icon (node->proposal));
+			if (node->proposal == NULL)
+			{
+				g_value_set_object (value, 
+				                    (gpointer)gtk_source_completion_provider_get_icon (
+				                    	node->provider));
+			}
+			else
+			{
+				g_value_set_object (value, 
+				                    (gpointer)gtk_source_completion_proposal_get_icon (
+				                    	node->proposal));
+			}
 			break;
 	}
 }
@@ -386,7 +406,7 @@
 static void
 free_num (gpointer data)
 {
-	g_slice_free (guint, data);
+	g_slice_free (HeaderInfo, data);
 }
 
 static void
@@ -411,32 +431,24 @@
          gboolean                            inc_local,
          gboolean                            inc_global)
 {
-	guint *num;
+	HeaderInfo *info;
+	
+	info = g_hash_table_lookup (model->priv->num_per_provider, provider);
 	
 	if (inc_global)
 	{
 		++model->priv->num;
+		
+		if (info != NULL)
+		{
+			++info->visible_items;
+		}
 	}
 	
-	if (!inc_local)
-	{
-		return;
-	}
-
-	num = g_hash_table_lookup (model->priv->num_per_provider, provider);
-	
-	if (num != NULL)
+	if (inc_local && info != NULL)
 	{
-		++(*num);
+		++(info->num);
 	}
-	else
-	{
-		num = g_slice_new (guint);
-		*num = 1;
-
-		g_hash_table_insert (model->priv->num_per_provider, provider, num);
-	}
-	
 }
 
 static void
@@ -445,23 +457,23 @@
          gboolean                            dec_local,
          gboolean                            dec_global)
 {
-	guint *num;
+	HeaderInfo *info;
+	
+	info = g_hash_table_lookup (model->priv->num_per_provider, provider);
 	
 	if (dec_global)
 	{
 		--model->priv->num;
+		
+		if (info != NULL)
+		{
+			--info->visible_items;
+		}
 	}
 
-	if (!dec_local)
+	if (dec_local && info != NULL && info->num > 0)
 	{
-		return;
-	}
-	
-	num = g_hash_table_lookup (model->priv->num_per_provider, provider);
-	
-	if (num != NULL && *num > 0)
-	{
-		--(*num);
+		--(info->num);
 	}
 }
 
@@ -471,6 +483,11 @@
 {
 	GtkSourceCompletionModelFilterFlag ret;
 	
+	if (node->proposal == NULL)
+	{
+		return node->filtered;
+	}
+	
 	ret = node->filtered;
 	
 	node->filtered = model->priv->filter (model, 
@@ -485,47 +502,128 @@
 free_node (ProposalNode *node)
 {
 	g_object_unref (node->provider);
-	g_object_unref (node->proposal);
+	
+	if (node->proposal != NULL)
+	{
+		g_object_unref (node->proposal);
+	}
 	
 	g_slice_free (ProposalNode, node);
 }
 
 static void
-remove_node (GtkSourceCompletionModel *model,
-             GList                    *item)
+update_show_headers (GtkSourceCompletionModel *model,
+                     gboolean                  show)
 {
+	GtkSourceCompletionProvider *provider;
+	HeaderInfo *info;
+	guint num = 0;
+	GList *items = NULL;
+	GList *item;
+	GHashTableIter hiter;
+	
 	ProposalNode *node;
 	GtkTreePath *path;
-	GtkSourceCompletionModelFilterFlag filtered;
-	GtkSourceCompletionProvider *provider;
+	GtkTreeIter iter;
 	
-	path = path_from_list (model, item);
-
-	node = (ProposalNode *)item->data;
-	provider = g_object_ref (node->provider);
+	if (!model->priv->show_headers)
+	{
+		return;
+	}
+	
+	/* Check headers */
+	g_hash_table_iter_init (&hiter, model->priv->num_per_provider);
+	
+	while (g_hash_table_iter_next (&hiter, (gpointer *)&provider, (gpointer *)&info))
+	{
+		if (info->visible_items > 0)
+		{
+			node = (ProposalNode *)info->item->data;
+			++num;
+			
+			if (show && node->filtered)
+			{
+				items = g_list_append (items, info);
+			}
+			
+			if (!show && !node->filtered)
+			{
+				items = g_list_append (items, info);
+			}
+		}
+		
+		if (show && num > 1)
+			break;
+	}
 
-	filtered = node->filtered;
-	free_node (node);
+	if (show && num > 1 && items != NULL)
+	{
+		for (item = items; item; item = g_list_next (item))
+		{
+			info = (HeaderInfo *)item->data;
+			node = (ProposalNode *)info->item->data;
+			
+			node->filtered = GTK_SOURCE_COMPLETION_MODEL_NONE;
+			iter.user_data = info->item;
+			
+			num_inc (model, node->provider, FALSE, TRUE);
 
-	if (item == model->priv->last)
+			path = path_from_list (model, info->item);
+			gtk_tree_model_row_inserted (GTK_TREE_MODEL (model),
+			                             path,
+			                             &iter);
+			gtk_tree_path_free (path);
+		}
+	}
+	
+	if (!show && num <= 1 && items)
 	{
-		model->priv->last = item->prev;
+		info = (HeaderInfo *)items->data;
+		node = (ProposalNode *)info->item->data;
+		
+		num_dec (model, node->provider, FALSE, TRUE);
+
+		node->filtered = GTK_SOURCE_COMPLETION_MODEL_FILTERED;
+		path = path_from_list (model, info->item);
+		gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
+		gtk_tree_path_free (path);
 	}
 
-	model->priv->store = g_list_remove_link (model->priv->store, item);
-	
-	num_dec (model, 
-	         provider, 
-	         !filtered || (filtered & GTK_SOURCE_COMPLETION_MODEL_COUNT),
-	         !filtered);
+	g_list_free (items);
+}
+
+static void
+refilter_headers (GtkSourceCompletionModel *model)
+{
+	GtkSourceCompletionProvider *provider;
+	HeaderInfo *info;
+	GHashTableIter hiter;
+	ProposalNode *node;
+	GtkTreePath *path;
+
+	g_hash_table_iter_init (&hiter, model->priv->num_per_provider);
 	
-	if (!filtered)
+	while (g_hash_table_iter_next (&hiter, (gpointer *)&provider, (gpointer *)&info))
 	{
-		gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
+		node = (ProposalNode *)info->item->data;
+		
+		if (!node->filtered)
+		{
+			node->filtered = GTK_SOURCE_COMPLETION_MODEL_FILTERED;
+			num_dec (model, provider, FALSE, TRUE);
+			
+			path = path_from_list (model, info->item);
+			gtk_tree_model_row_deleted (GTK_TREE_MODEL (model),
+			                            path);
+			gtk_tree_path_free (path);
+		}
 	}
 
-	gtk_tree_path_free (path);
-	g_object_unref (provider);
+	if (model->priv->show_headers)
+	{
+		update_show_headers (model, TRUE);
+		return;
+	}
 }
 
 /* Public */
@@ -541,6 +639,26 @@
 	return model;
 }
 
+static void
+append_list (GtkSourceCompletionModel *model,
+             ProposalNode             *node)
+{
+	GList *item;
+	
+	item = g_list_append (model->priv->last, node);
+	
+	if (model->priv->store == NULL)
+	{
+		model->priv->store = item;
+	}
+	else
+	{
+		item = item->next;
+	}
+	
+	model->priv->last = item;
+}
+
 gboolean 
 gtk_source_completion_model_append (GtkSourceCompletionModel    *model,
                                     GtkSourceCompletionProvider *provider,
@@ -550,31 +668,41 @@
 	ProposalNode *node;
 	GList *item;
 	GtkTreePath *path;
+	ProposalNode *header = NULL;
+	HeaderInfo *info;
 	
 	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)
+	{
+		header = g_slice_new (ProposalNode);
+		header->provider = g_object_ref (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, provider, info);
+	}
+
 	node = g_slice_new (ProposalNode);
 	node->provider = g_object_ref (provider);
 	node->proposal = g_object_ref (proposal);
 
 	node_update_filter_state (model, node);
 	
+	append_list (model, node);
 	item = model->priv->last;
-	item = g_list_append (item, node);
 	
-	if (model->priv->store == NULL)
-	{
-		model->priv->store = item;
-	}
-	else
-	{
-		item = item->next;
-	}
-	
-	model->priv->last = item;
 	iter->user_data = item;
 	
 	num_inc (model, 
@@ -585,26 +713,18 @@
 	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);
+		}
 	}
 	
 	return TRUE;
 }
 
-gboolean 
-gtk_source_completion_model_remove (GtkSourceCompletionModel    *model,
-                                    GtkTreeIter                 *iter)
-{
-	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_MODEL (model), FALSE);
-	g_return_val_if_fail (iter != NULL, FALSE);
-	g_return_val_if_fail (iter->user_data != NULL, FALSE);
-	
-	remove_node (model, (GList *)iter->user_data);
-	return TRUE;
-}
-
 void
 gtk_source_completion_model_clear (GtkSourceCompletionModel *model)
 {
@@ -634,7 +754,7 @@
 		
 		num_dec (model, 
 		         node->provider, 
-		         !filtered || (filtered & GTK_SOURCE_COMPLETION_MODEL_COUNT),
+		         (!filtered || (filtered & GTK_SOURCE_COMPLETION_MODEL_COUNT)) && node->proposal != NULL,
 		         !filtered);
 		
 		if (!filtered)
@@ -704,6 +824,7 @@
 	}
 
 	gtk_tree_path_free (path);
+	refilter_headers (model);
 }
 
 gboolean
@@ -744,3 +865,81 @@
 	}
 }
 
+void 
+gtk_source_completion_model_set_show_headers (GtkSourceCompletionModel *model,
+					      gboolean                  show_headers)
+{
+	g_return_if_fail (GTK_IS_SOURCE_COMPLETION_MODEL (model));
+	
+	if (model->priv->show_headers != show_headers)
+	{
+		model->priv->show_headers = show_headers;
+		refilter_headers (model);
+	}
+}
+
+gboolean
+gtk_source_completion_model_iter_is_header (GtkSourceCompletionModel *model,
+                                            GtkTreeIter              *iter)
+{
+	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_MODEL (model), FALSE);
+	g_return_val_if_fail (iter != NULL, FALSE);
+	g_return_val_if_fail (iter->user_data != NULL, FALSE);
+
+	return node_from_iter (iter)->proposal == NULL;
+}
+
+gboolean
+gtk_source_completion_model_iter_previous (GtkSourceCompletionModel *model,
+                                           GtkTreeIter              *iter)
+{
+	GList *item;
+	
+	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_MODEL (model), FALSE);
+	g_return_val_if_fail (iter != NULL, FALSE);
+	g_return_val_if_fail (iter->user_data != NULL, FALSE);
+	
+	item = iter->user_data;
+	
+	do
+	{
+		item = g_list_previous (item);
+	} while (item && ((ProposalNode *)item->data)->filtered);
+
+	
+	if (item != NULL)
+	{
+		iter->user_data = item;
+		return TRUE;
+	}
+	else
+	{
+		return FALSE;
+	}
+}
+
+gboolean
+gtk_source_completion_model_iter_last (GtkSourceCompletionModel *model,
+                                       GtkTreeIter              *iter)
+{
+	GList *item;
+	
+	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_MODEL (model), FALSE);
+	g_return_val_if_fail (iter != NULL, FALSE);
+	
+	item = model->priv->last;
+	iter->user_data = item;
+
+	if (!((ProposalNode *)item->data)->filtered)
+	{
+		return TRUE;
+	}
+	else if (item != NULL)
+	{
+		return gtk_source_completion_model_iter_previous (model, iter);
+	}
+	else
+	{
+		return FALSE;
+	}
+}

Modified: branches/gtksourcecompletion/gtksourceview/gtksourcecompletionmodel.h
==============================================================================
--- branches/gtksourcecompletion/gtksourceview/gtksourcecompletionmodel.h	(original)
+++ branches/gtksourcecompletion/gtksourceview/gtksourcecompletionmodel.h	Mon Apr 13 23:12:12 2009
@@ -83,9 +83,6 @@
 							 GtkSourceCompletionProposal        *proposal,
 							 GtkTreeIter                        *iter);
 
-gboolean 	gtk_source_completion_model_remove 	(GtkSourceCompletionModel           *model, 
-							 GtkTreeIter                        *iter);
-
 gboolean	gtk_source_completion_model_is_empty 	(GtkSourceCompletionModel           *model,
                                                          gboolean                            invisible);
 
@@ -96,6 +93,18 @@
 
 void		gtk_source_completion_model_refilter	(GtkSourceCompletionModel           *model);
 
+void 		gtk_source_completion_model_set_show_headers (GtkSourceCompletionModel      *model,
+							      gboolean                       show_headers);
+
+gboolean	gtk_source_completion_model_iter_is_header (GtkSourceCompletionModel        *model,
+                                                            GtkTreeIter                     *iter);
+
+gboolean 	gtk_source_completion_model_iter_previous (GtkSourceCompletionModel         *model,
+							   GtkTreeIter                      *iter);
+							 
+gboolean 	gtk_source_completion_model_iter_last 	(GtkSourceCompletionModel           *model,
+							 GtkTreeIter                        *iter);
+
 G_END_DECLS
 
 #endif /* __GTK_SOURCE_COMPLETION_MODEL_H__ */

Modified: branches/gtksourcecompletion/gtksourceview/gtksourcecompletionprovider.c
==============================================================================
--- branches/gtksourcecompletion/gtksourceview/gtksourcecompletionprovider.c	(original)
+++ branches/gtksourcecompletion/gtksourceview/gtksourcecompletionprovider.c	Mon Apr 13 23:12:12 2009
@@ -40,6 +40,12 @@
 	g_return_val_if_reached (NULL);
 }
 
+static const GdkPixbuf *
+gtk_source_completion_provider_get_icon_default (GtkSourceCompletionProvider *provider)
+{
+	return NULL;
+}
+
 static GList *
 gtk_source_completion_provider_get_proposals_default (GtkSourceCompletionProvider *provider)
 {
@@ -80,6 +86,8 @@
 	static gboolean initialized = FALSE;
 	
 	iface->get_name = gtk_source_completion_provider_get_name_default;
+	iface->get_icon = gtk_source_completion_provider_get_icon_default;
+
 	iface->get_proposals = gtk_source_completion_provider_get_proposals_default;
 	iface->filter_proposal = gtk_source_completion_provider_filter_proposal_default;
 	iface->can_auto_complete = gtk_source_completion_provider_can_auto_complete_default;
@@ -140,6 +148,13 @@
 	return GTK_SOURCE_COMPLETION_PROVIDER_GET_INTERFACE (provider)->get_name (provider);
 }
 
+const GdkPixbuf *
+gtk_source_completion_provider_get_icon (GtkSourceCompletionProvider *provider)
+{
+	g_return_val_if_fail (GTK_IS_SOURCE_COMPLETION_PROVIDER (provider), NULL);
+	return GTK_SOURCE_COMPLETION_PROVIDER_GET_INTERFACE (provider)->get_icon (provider);
+}
+
 /**
  * gtk_source_completion_provider_get_proposals:
  * @provider: The #GtkSourceCompletionProvider

Modified: branches/gtksourcecompletion/gtksourceview/gtksourcecompletionprovider.h
==============================================================================
--- branches/gtksourcecompletion/gtksourceview/gtksourcecompletionprovider.h	(original)
+++ branches/gtksourcecompletion/gtksourceview/gtksourcecompletionprovider.h	Mon Apr 13 23:12:12 2009
@@ -45,6 +45,7 @@
 	GTypeInterface g_iface;
 	
 	const gchar	*(*get_name)       	(GtkSourceCompletionProvider *provider);
+	const GdkPixbuf	*(*get_icon)       	(GtkSourceCompletionProvider *provider);
 	GList 		*(*get_proposals) 	(GtkSourceCompletionProvider *provider);
 	gboolean 	 (*filter_proposal) 	(GtkSourceCompletionProvider *provider,
 						 GtkSourceCompletionProposal *proposal,
@@ -63,6 +64,8 @@
 
 const gchar	*gtk_source_completion_provider_get_name	(GtkSourceCompletionProvider *provider);
 
+const GdkPixbuf	*gtk_source_completion_provider_get_icon	(GtkSourceCompletionProvider *provider);
+
 GList		*gtk_source_completion_provider_get_proposals	(GtkSourceCompletionProvider *provider);
 
 gboolean	 gtk_source_completion_provider_filter_proposal	(GtkSourceCompletionProvider *provider,

Modified: branches/gtksourcecompletion/tests/completion-simple.c
==============================================================================
--- branches/gtksourcecompletion/tests/completion-simple.c	(original)
+++ branches/gtksourcecompletion/tests/completion-simple.c	Mon Apr 13 23:12:12 2009
@@ -156,21 +156,53 @@
 	return window;
 }
 
+static GdkPixbuf *
+get_icon_from_theme (const gchar *name)
+{
+	GtkIconTheme *theme;
+	gint width;
+	
+	theme = gtk_icon_theme_get_default ();
+
+	gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &width, NULL);
+	return gtk_icon_theme_load_icon (theme,
+	                                 name,
+	                                 width,
+	                                 GTK_ICON_LOOKUP_USE_BUILTIN,
+	                                 NULL);
+}
+
 static void
 create_completion(void)
 {
 	GscProviderTest *prov_test1;
 	GscProviderDevhelp *prov_devhelp;
+	GdkPixbuf *icon;
 	
 	comp = gtk_source_view_get_completion (GTK_SOURCE_VIEW (view));
 	
-	prov_test1 = gsc_provider_test_new ("Test 1");
+	icon = get_icon_from_theme (GTK_STOCK_NETWORK);
+
+	prov_test1 = gsc_provider_test_new ("Networking", icon);
+
+	if (icon != NULL)
+	{
+		g_object_unref (icon);
+	}
+
 	prov_devhelp = gsc_provider_devhelp_new ();
 	
 	gtk_source_completion_add_provider (comp, GTK_SOURCE_COMPLETION_PROVIDER (prov_test1));
 	//gtk_source_completion_add_provider (comp, GTK_SOURCE_COMPLETION_PROVIDER (prov_devhelp));
 	
-	prov_test1 = gsc_provider_test_new ("Test 2");
+	icon = get_icon_from_theme (GTK_STOCK_OPEN);
+	prov_test1 = gsc_provider_test_new ("Open Files", icon);
+	
+	if (icon != NULL)
+	{
+		g_object_unref (icon);
+	}
+	
 	gtk_source_completion_add_provider (comp, GTK_SOURCE_COMPLETION_PROVIDER (prov_test1));
 }
 

Modified: branches/gtksourcecompletion/tests/gsc-provider-test.c
==============================================================================
--- branches/gtksourcecompletion/tests/gsc-provider-test.c	(original)
+++ branches/gtksourcecompletion/tests/gsc-provider-test.c	Mon Apr 13 23:12:12 2009
@@ -27,6 +27,8 @@
 struct _GscProviderTestPrivate
 {
 	gchar *name;
+	GdkPixbuf *icon;
+	GdkPixbuf *proposal_icon;
 };
 
 G_DEFINE_TYPE_WITH_CODE (GscProviderTest,
@@ -41,6 +43,13 @@
 	return GSC_PROVIDER_TEST (self)->priv->name;
 }
 
+static const GdkPixbuf * 
+gsc_provider_test_get_icon (GtkSourceCompletionProvider *self)
+{
+	return GSC_PROVIDER_TEST (self)->priv->icon;
+}
+
+
 static GList *
 append_item (GList *list, const gchar *name, GdkPixbuf *icon, const gchar *info)
 {
@@ -53,12 +62,13 @@
 static GList *
 gsc_provider_test_get_proposals (GtkSourceCompletionProvider *base)
 {
+	GscProviderTest *provider = GSC_PROVIDER_TEST (base);
 	GList *list = NULL;
 	
-	list = append_item (list, "aa", NULL, "Info proposal 1.1");
-	list = append_item (list, "ab", NULL, "Info proposal 1.2");
-	list = append_item (list, "bc", NULL, "Info proposal 1.3");
-	list = append_item (list, "bd", NULL, "Info proposal 1.3");
+	list = append_item (list, "aa", provider->priv->proposal_icon, "Info proposal 1.1");
+	list = append_item (list, "ab", provider->priv->proposal_icon, "Info proposal 1.2");
+	list = append_item (list, "bc", provider->priv->proposal_icon, "Info proposal 1.3");
+	list = append_item (list, "bd", provider->priv->proposal_icon, "Info proposal 1.3");
 	
 	return list;
 }
@@ -86,6 +96,16 @@
 	GscProviderTest *provider = GSC_PROVIDER_TEST (object);
 	
 	g_free (provider->priv->name);
+	
+	if (provider->priv->icon != NULL)
+	{
+		g_object_unref (provider->priv->icon);
+	}
+	
+	if (provider->priv->proposal_icon != NULL)
+	{
+		g_object_unref (provider->priv->proposal_icon);
+	}
 
 	G_OBJECT_CLASS (gsc_provider_test_parent_class)->finalize (object);
 }
@@ -105,6 +125,8 @@
 gsc_provider_test_iface_init (GtkSourceCompletionProviderIface *iface)
 {
 	iface->get_name = gsc_provider_test_get_name;
+	iface->get_icon = gsc_provider_test_get_icon;
+
 	iface->get_proposals = gsc_provider_test_get_proposals;
 	iface->filter_proposal = gsc_provider_test_filter_proposal;
 	iface->can_auto_complete = gsc_provider_test_can_auto_complete;
@@ -113,14 +135,33 @@
 static void 
 gsc_provider_test_init (GscProviderTest * self)
 {
+	GtkIconTheme *theme;
+	gint width;
+	
 	self->priv = GSC_PROVIDER_TEST_GET_PRIVATE (self);
+	
+	theme = gtk_icon_theme_get_default ();
+
+	gtk_icon_size_lookup (GTK_ICON_SIZE_MENU, &width, NULL);
+	self->priv->proposal_icon = gtk_icon_theme_load_icon (theme,
+	                                                      GTK_STOCK_YES,
+	                                                      width,
+	                                                      GTK_ICON_LOOKUP_USE_BUILTIN,
+	                                                      NULL);
 }
 
 GscProviderTest *
-gsc_provider_test_new (const gchar *name)
+gsc_provider_test_new (const gchar *name,
+                       GdkPixbuf   *icon)
 {
 	GscProviderTest *ret = g_object_new (GSC_TYPE_PROVIDER_TEST, NULL);
 	
 	ret->priv->name = g_strdup (name);
+	
+	if (icon != NULL)
+	{
+		ret->priv->icon = g_object_ref (icon);
+	}
+
 	return ret;
 }

Modified: branches/gtksourcecompletion/tests/gsc-provider-test.h
==============================================================================
--- branches/gtksourcecompletion/tests/gsc-provider-test.h	(original)
+++ branches/gtksourcecompletion/tests/gsc-provider-test.h	Mon Apr 13 23:12:12 2009
@@ -52,7 +52,8 @@
 
 GType		 gsc_provider_test_get_type	(void) G_GNUC_CONST;
 
-GscProviderTest *gsc_provider_test_new (const gchar *name);
+GscProviderTest *gsc_provider_test_new (const gchar *name,
+                                        GdkPixbuf   *icon);
 
 G_END_DECLS
 



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