SF.net SVN: geany:[3197] trunk

ntrel at users.sourceforge.net ntrel at xxxxx
Mon Nov 10 16:57:18 UTC 2008


Revision: 3197
          http://geany.svn.sourceforge.net/geany/?rev=3197&view=rev
Author:   ntrel
Date:     2008-11-10 16:57:18 +0000 (Mon, 10 Nov 2008)

Log Message:
-----------
Merge symbol-tree branch:
Apply patch (with some reworking) from Conrad Steenberg
(gnocci-man) to show methods as children of classes in the symbol
list, and for other tag types to group children by their parents
(thanks; #2083110).
This works for any filetype that TagManager can parse tag scopes
for.
Fix not allowing a leading underscore when using scope name prefix.
Fix symbol-tree branch bug: missing C++ constructor declaration
tags.
- Code changes:
Use TMTag instead of GeanySymbol so the symbol tree can read the
scope without parsing it.
Free tag list straight after use, instead of next time the list is
generated.
Use TMTag pointer tree model column instead of line number.

Modified Paths:
--------------
    trunk/ChangeLog
    trunk/THANKS
    trunk/src/about.c
    trunk/src/symbols.c
    trunk/src/treeviews.c
    trunk/src/treeviews.h

Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog	2008-11-10 12:28:06 UTC (rev 3196)
+++ trunk/ChangeLog	2008-11-10 16:57:18 UTC (rev 3197)
@@ -1,3 +1,25 @@
+2008-11-10  Nick Treleaven  <nick(dot)treleaven(at)btinternet(dot)com>
+
+ * src/about.c, src/treeviews.c, src/treeviews.h, src/symbols.c,
+   THANKS:
+   Merge symbol-tree branch:
+   Apply patch (with some reworking) from Conrad Steenberg
+   (gnocci-man) to show methods as children of classes in the symbol
+   list, and for other tag types to group children by their parents
+   (thanks; #2083110).
+   This works for any filetype that TagManager can parse tag scopes
+   for.
+   Fix not allowing a leading underscore when using scope name prefix.
+   Fix symbol-tree branch bug: missing C++ constructor declaration
+   tags.
+ - Code changes:
+   Use TMTag instead of GeanySymbol so the symbol tree can read the
+   scope without parsing it.
+   Free tag list straight after use, instead of next time the list is
+   generated.
+   Use TMTag pointer tree model column instead of line number.
+
+
 2008-11-08  Nick Treleaven  <nick(dot)treleaven(at)btinternet(dot)com>
 
  * src/editor.c, doc/geany.txt, doc/geany.html:
@@ -3,5 +25,5 @@
    Make Ctrl-click go to matching brace if there's no current word.
  * plugins/vcdiff.c, src/editor.c, src/plugindata.h, src/plugins.c:
-   Make Version Diff plugin set the indent type for diffs based on the 
+   Make Version Diff plugin set the indent type for diffs based on the
    current file's indent type.
    Add editor_set_indent_type() to the API.

Modified: trunk/THANKS
===================================================================
--- trunk/THANKS	2008-11-10 12:28:06 UTC (rev 3196)
+++ trunk/THANKS	2008-11-10 16:57:18 UTC (rev 3197)
@@ -51,6 +51,7 @@
 Andrew Rowland <weibullguy(at)charter(dot)net> - R filetype patch
 Bronisław Białek <after89(at)gmail(dot)com> - CSS parser update
 Roland Baudin <roland(dot)baudin(at)thalesaleniaspace(dot)com> - Matlab filetype patch
+Conrad Steenberg <gnocci-man(at)users(dot)sourceforge(dot)net> - symbol tree patch
 
 Translators:
 ------------

Modified: trunk/src/about.c
===================================================================
--- trunk/src/about.c	2008-11-10 12:28:06 UTC (rev 3196)
+++ trunk/src/about.c	2008-11-10 16:57:18 UTC (rev 3197)
@@ -79,7 +79,7 @@
 
 static const gchar *contributors =
 "Alexander Rodin, Andrew Rowland, Anh Phạm, blackdog, Bo Lorentsen, Bob Doan, Bronisław Białek, Catalin Marinas, "
-"Christoph Berg, Daniel Richard G., Dave Moore, Dirk Weber, Felipe Pena, François Cami, "
+"Christoph Berg, Conrad Steenberg, Daniel Richard G., Dave Moore, Dirk Weber, Felipe Pena, François Cami, "
 "Giuseppe Torelli, Guillaume Hoffmann, Jason Oster, Jean-François Wauthy, Jeff Pohlmeyer, "
 "John Gabriele, Josef Whiter, Kevin Ellwood, Kristoffer A. Tjernås, Marko Peric, Matti Mårds, "
 "Peter Strand, Pierre Joye, Rob van der Linde, Robert McGinley, S Jagannathan, Saleem Abdulrasool, "

Modified: trunk/src/symbols.c
===================================================================
--- trunk/src/symbols.c	2008-11-10 12:28:06 UTC (rev 3196)
+++ trunk/src/symbols.c	2008-11-10 16:57:18 UTC (rev 3197)
@@ -22,9 +22,9 @@
  */
 
 /*
- * Tagmanager related convenience functions.
- * Tagmanager parses tags in the current documents, known as the workspace, plus global tags,
- * which are lists of tags for each filetype. Global tags are loaded when a document with a
+ * Symbol Tree and TagManager-related convenience functions.
+ * TagManager parses tags for each document, and also adds them to the workspace (session).
+ * Global tags are lists of tags for each filetype, loaded when a document with a
  * matching filetype is first loaded.
  */
 
@@ -89,7 +89,9 @@
 
 static gchar *user_tags_dir;
 
+static GPtrArray *top_level_iter_names = NULL;
 
+
 static void html_tags_loaded(void);
 static void load_user_tags(filetype_id ft_id);
 
@@ -370,113 +372,60 @@
 }
 
 
-/* small struct to track tag name and type together */
-typedef struct GeanySymbol
+/* sort by name, then line */
+static gint compare_symbol(const TMTag *tag_a, const TMTag *tag_b)
 {
-	gchar	*str;
-	gint	 type;
-	gint	 line;
-} GeanySymbol;
-
-
-/* sort by name, line */
-static gint compare_symbol(const GeanySymbol *a, const GeanySymbol *b)
-{
 	gint ret;
 
-	if (a == NULL || b == NULL) return 0;
+	if (tag_a == NULL || tag_b == NULL)
+		return 0;
 
-	ret = strcmp(a->str, b->str);
+	ret = strcmp(tag_a->name, tag_b->name);
 	if (ret == 0)
 	{
-		return a->line - b->line;
+		return tag_a->atts.entry.line - tag_b->atts.entry.line;
 	}
 	return ret;
 }
 
 
 /* sort by line only */
-static gint compare_symbol_lines(const GeanySymbol *a, const GeanySymbol *b)
+static gint compare_symbol_lines(gconstpointer a, gconstpointer b)
 {
-	if (a == NULL || b == NULL) return 0;
+	const TMTag *tag_a = TM_TAG(a);
+	const TMTag *tag_b = TM_TAG(b);
 
-	return a->line - b->line;
+	if (a == NULL || b == NULL)
+		return 0;
+
+	return tag_a->atts.entry.line - tag_b->atts.entry.line;
 }
 
 
-static const GList *get_tag_list(GeanyDocument *doc, guint tag_types, gint sort_mode)
+static GList *get_tag_list(GeanyDocument *doc, guint tag_types)
 {
-	static GList *tag_names = NULL;
+	GList *tag_names = NULL;
+	TMTag *tag;
+	guint i;
 
-	if (doc != NULL && doc->tm_file &&
-		doc->tm_file->tags_array)
-	{
-		TMTag *tag;
-		guint i;
-		GeanySymbol *symbol;
-		gboolean doc_is_utf8 = FALSE;
-		gchar *utf8_name;
-		const gchar *cosep =
-			symbols_get_context_separator(FILETYPE_ID(doc->file_type));
+	g_return_val_if_fail(doc, NULL);
 
-		if (tag_names)
-		{
-			GList *tmp;
-			for (tmp = tag_names; tmp; tmp = g_list_next(tmp))
-			{
-				g_free(((GeanySymbol*)tmp->data)->str);
-				g_free(tmp->data);
-			}
-			g_list_free(tag_names);
-			tag_names = NULL;
-		}
+	if (!doc->tm_file || !doc->tm_file->tags_array)
+		return NULL;
 
-		/* encodings_convert_to_utf8_from_charset() fails with charset "None", so skip conversion
-		 * for None at this point completely */
-		if (utils_str_equal(doc->encoding, "UTF-8") ||
-			utils_str_equal(doc->encoding, "None"))
-			doc_is_utf8 = TRUE;
+	for (i = 0; i < doc->tm_file->tags_array->len; ++i)
+	{
+		tag = TM_TAG(doc->tm_file->tags_array->pdata[i]);
+		if (tag == NULL)
+			return NULL;
 
-		for (i = 0; i < (doc->tm_file)->tags_array->len; ++i)
+		if (tag->type & tag_types)
 		{
-			tag = TM_TAG((doc->tm_file)->tags_array->pdata[i]);
-			if (tag == NULL)
-				return NULL;
-
-			if (tag->type & tag_types)
-			{
-				if (! doc_is_utf8) utf8_name = encodings_convert_to_utf8_from_charset(tag->name,
-															(gsize)-1, doc->encoding, TRUE);
-				else utf8_name = tag->name;
-
-				if (utf8_name == NULL)
-					continue;
-
-				symbol = g_new0(GeanySymbol, 1);
-				if ((tag->atts.entry.scope != NULL) && isalpha(tag->atts.entry.scope[0]))
-				{
-					symbol->str = g_strconcat(tag->atts.entry.scope, cosep, utf8_name, NULL);
-				}
-				else
-				{
-					symbol->str = g_strdup(utf8_name);
-				}
-				symbol->type = tag->type;
-				symbol->line = tag->atts.entry.line;
-				tag_names = g_list_prepend(tag_names, symbol);
-
-				if (! doc_is_utf8) g_free(utf8_name);
-			}
+			tag_names = g_list_append(tag_names, tag);
 		}
-		if (sort_mode == SYMBOLS_SORT_BY_NAME)
-			tag_names = g_list_sort(tag_names, (GCompareFunc) compare_symbol);
-		else
-			tag_names = g_list_sort(tag_names, (GCompareFunc) compare_symbol_lines);
-
-		return tag_names;
 	}
-	else
-		return NULL;
+	tag_names = g_list_sort(tag_names, compare_symbol_lines);
+	return tag_names;
 }
 
 
@@ -513,13 +462,8 @@
 }
 
 
-/* Adds symbol list groups in (iter*, title) pairs.
- * The list must be ended with NULL. */
-static void G_GNUC_NULL_TERMINATED
-tag_list_add_groups(GtkTreeStore *tree_store, ...)
+static GdkPixbuf *get_tag_icon(const gchar *icon_name)
 {
-	va_list args;
-	GtkTreeIter *iter;
 	static GtkIconTheme *icon_theme = NULL;
 	static gint x, y;
 
@@ -537,7 +481,20 @@
 		g_free(path);
 #endif
 	}
+	return gtk_icon_theme_load_icon(icon_theme, icon_name, x, 0, NULL);
+}
 
+
+/* Adds symbol list groups in (iter*, title) pairs.
+ * The list must be ended with NULL. */
+static void G_GNUC_NULL_TERMINATED
+tag_list_add_groups(GtkTreeStore *tree_store, ...)
+{
+	va_list args;
+	GtkTreeIter *iter;
+
+	g_return_if_fail(top_level_iter_names);
+
     va_start(args, tree_store);
     for (; iter = va_arg(args, GtkTreeIter*), iter != NULL;)
     {
@@ -547,30 +504,35 @@
 
 		if (icon_name)
 		{
-			icon = gtk_icon_theme_load_icon(icon_theme, icon_name, x, 0, NULL);
+			icon = get_tag_icon(icon_name);
 		}
 
     	g_assert(title != NULL);
+		g_ptr_array_add(top_level_iter_names, title);
+
 		gtk_tree_store_append(tree_store, iter, NULL);
 
 		if (G_IS_OBJECT(icon))
 		{
-			gtk_tree_store_set(tree_store, iter, SYMBOLS_COLUMN_ICON, icon,
-				SYMBOLS_COLUMN_NAME, title, -1);
+			gtk_tree_store_set(tree_store, iter, SYMBOLS_COLUMN_ICON, icon, -1);
 			g_object_unref(icon);
 		}
-		else
-			gtk_tree_store_set(tree_store, iter, SYMBOLS_COLUMN_NAME, title, -1);
+		gtk_tree_store_set(tree_store, iter, SYMBOLS_COLUMN_NAME, title, -1);
 	}
 	va_end(args);
 }
 
 
-static void init_tag_list(GeanyDocument *doc)
+static void add_top_level_items(GeanyDocument *doc)
 {
 	filetype_id ft_id = doc->file_type->id;
 	GtkTreeStore *tag_store = doc->priv->tag_store;
 
+	if (top_level_iter_names == NULL)
+		top_level_iter_names = g_ptr_array_new();
+	else
+		g_ptr_array_set_size(top_level_iter_names, 0);
+
 	init_tag_iters();
 
 	switch (ft_id)
@@ -857,128 +819,364 @@
 }
 
 
-gboolean symbols_recreate_tag_list(GeanyDocument *doc, gint sort_mode)
+static const gchar *get_symbol_name(GeanyDocument *doc, const TMTag *tag,
+		gboolean found_parent)
 {
-	GList *tmp;
-	const GList *tags;
-	GtkTreeIter iter;
-	static gint prev_sort_mode = SYMBOLS_SORT_BY_NAME;
-	filetype_id ft_id;
+	gchar *utf8_name;
+	const gchar *scope = tag->atts.entry.scope;
+	static GString *buffer = NULL;	/* buffer will be small so we can keep it for reuse */
+	gboolean doc_is_utf8 = FALSE;
 
-	g_return_val_if_fail(doc != NULL, FALSE);
+	/* encodings_convert_to_utf8_from_charset() fails with charset "None", so skip conversion
+	 * for None at this point completely */
+	if (utils_str_equal(doc->encoding, "UTF-8") ||
+		utils_str_equal(doc->encoding, "None"))
+		doc_is_utf8 = TRUE;
 
-	ft_id = FILETYPE_ID(doc->file_type);
+	if (! doc_is_utf8)
+		utf8_name = encodings_convert_to_utf8_from_charset(tag->name,
+			(gsize)-1, doc->encoding, TRUE);
+	else
+		utf8_name = tag->name;
 
-	if (sort_mode == SYMBOLS_SORT_USE_PREVIOUS)
-		sort_mode = prev_sort_mode;
+	if (utf8_name == NULL)
+		return NULL;
+
+	if (!buffer)
+		buffer = g_string_new(NULL);
 	else
-		prev_sort_mode = sort_mode;
+		g_string_truncate(buffer, 0);
 
-	tags = get_tag_list(doc, tm_tag_max_t, sort_mode);
-	if (doc->tm_file == NULL || tags == NULL)
-		return FALSE;
+	/* check first char of scope is a wordchar */
+	if (!found_parent && scope &&
+		strpbrk(scope, GEANY_WORDCHARS) == scope)
+	{
+		const gchar *sep = symbols_get_context_separator(FILETYPE_ID(doc->file_type));
 
-	/* Make sure the model stays with us after the tree view unrefs it */
-	g_object_ref(GTK_TREE_MODEL(doc->priv->tag_store));
-	/* Detach model from view */
-	gtk_tree_view_set_model(GTK_TREE_VIEW(doc->priv->tag_tree), NULL);
-	/* Clear all contents */
-	gtk_tree_store_clear(doc->priv->tag_store);
+		g_string_append(buffer, scope);
+		g_string_append(buffer, sep);
+	}
+	g_string_append(buffer, utf8_name);
 
-	init_tag_list(doc);
-	for (tmp = (GList*)tags; tmp; tmp = g_list_next(tmp))
+	if (! doc_is_utf8)
+		g_free(utf8_name);
+
+	g_string_append_printf(buffer, " [%lu]", tag->atts.entry.line);
+
+	return buffer->str;
+}
+
+
+/* find the last word in "foo::bar::blah", e.g. "blah" */
+const gchar *get_parent_name(const TMTag *tag, filetype_id ft_id)
+{
+	const gchar *scope = tag->atts.entry.scope;
+	const gchar *separator = symbols_get_context_separator(ft_id);
+	const gchar *str, *ptr;
+
+	if (!scope)
+		return NULL;
+
+	str = scope;
+
+	while (1)
 	{
-		gchar buf[100];
-		const GeanySymbol *symbol = (GeanySymbol*)tmp->data;
-		GtkTreeIter *parent = NULL;
-		GdkPixbuf *icon = NULL;
+		ptr = strstr(str, separator);
+		if (ptr)
+		{
+			str = ptr + strlen(separator);
+		}
+		else
+			break;
+	}
 
-		g_snprintf(buf, sizeof(buf), "%s [%d]", symbol->str, symbol->line);
+	return NZV(str) ? str : NULL;
+}
 
-		switch (symbol->type)
+
+static GtkTreeIter *get_tag_type_iter(TMTagType tag_type, filetype_id ft_id)
+{
+	GtkTreeIter *iter = NULL;
+
+	switch (tag_type)
+	{
+		case tm_tag_prototype_t:
+		case tm_tag_method_t:
+		case tm_tag_function_t:
 		{
-			case tm_tag_prototype_t:
-			case tm_tag_method_t:
-			case tm_tag_function_t:
+			iter = &tv_iters.tag_function;
+			break;
+		}
+		case tm_tag_macro_t:
+		case tm_tag_macro_with_arg_t:
+		{
+			iter = &tv_iters.tag_macro;
+			break;
+		}
+		case tm_tag_class_t:
+		{
+			iter = &tv_iters.tag_class;
+			break;
+		}
+		case tm_tag_member_t:
+		case tm_tag_field_t:
+		{
+			iter = &tv_iters.tag_member;
+			break;
+		}
+		case tm_tag_typedef_t:
+		case tm_tag_enum_t:
+		{
+			/* TODO separate C-like types here also */
+			if (ft_id == GEANY_FILETYPES_HAXE)
 			{
-				if (tv_iters.tag_function.stamp == -1) break;
-				parent = &(tv_iters.tag_function);
+				iter = &tv_iters.tag_type;
 				break;
 			}
-			case tm_tag_macro_t:
-			case tm_tag_macro_with_arg_t:
-			{
-				if (tv_iters.tag_macro.stamp == -1) break;
-				parent = &(tv_iters.tag_macro);
-				break;
-			}
-			case tm_tag_class_t:
-			{
-				if (tv_iters.tag_class.stamp == -1) break;
-				parent = &(tv_iters.tag_class);
-				break;
-			}
-			case tm_tag_member_t:
-			case tm_tag_field_t:
-			{
-				if (tv_iters.tag_member.stamp == -1) break;
-				parent = &(tv_iters.tag_member);
-				break;
-			}
-			case tm_tag_typedef_t:
-			case tm_tag_enum_t:
-			{
-				/** TODO separate C-like types here also */
-				if (ft_id == GEANY_FILETYPES_HAXE)
-				{
-					if (tv_iters.tag_type.stamp == -1) break;
-					parent = &(tv_iters.tag_type);
-					break;
-				}
-			}
-			case tm_tag_union_t:
-			case tm_tag_struct_t:
-			case tm_tag_interface_t:
-			{
-				if (tv_iters.tag_struct.stamp == -1) break;
-				parent = &(tv_iters.tag_struct);
-				break;
-			}
-			case tm_tag_variable_t:
-			{
-				if (tv_iters.tag_variable.stamp == -1) break;
-				parent = &(tv_iters.tag_variable);
-				break;
-			}
-			case tm_tag_namespace_t:
-			case tm_tag_package_t:
-			{
-				if (tv_iters.tag_namespace.stamp == -1) break;
-				parent = &(tv_iters.tag_namespace);
-				break;
-			}
-			default:
-			{
-				if (tv_iters.tag_other.stamp == -1) break;
-				parent = &(tv_iters.tag_other);
-			}
+			/* fall through */
 		}
+		case tm_tag_union_t:
+		case tm_tag_struct_t:
+		case tm_tag_interface_t:
+		{
+			iter = &tv_iters.tag_struct;
+			break;
+		}
+		case tm_tag_variable_t:
+		{
+			iter = &tv_iters.tag_variable;
+			break;
+		}
+		case tm_tag_namespace_t:
+		case tm_tag_package_t:
+		{
+			iter = &tv_iters.tag_namespace;
+			break;
+		}
+		default:
+		{
+			iter = &tv_iters.tag_other;
+		}
+	}
+	if (iter->stamp != -1)
+		return iter;
+	else
+		return NULL;
+}
 
-		if (parent)
+
+static void add_tree_tag(GeanyDocument *doc, const TMTag *tag, GHashTable *parent_hash)
+{
+	filetype_id ft_id = FILETYPE_ID(doc->file_type);
+	GtkTreeStore *tree_store = doc->priv->tag_store;
+	GtkTreeIter *parent = NULL;
+
+	parent = get_tag_type_iter(tag->type, ft_id);
+
+	if (parent)
+	{
+		const gchar *name;
+		const gchar *parent_name = get_parent_name(tag, ft_id);
+		GtkTreeIter iter;
+		GtkTreeIter *icon_iter = NULL, *child = NULL;
+		GdkPixbuf *icon = NULL;
+
+		child = &iter;
+		icon_iter = (parent != &tv_iters.tag_other) ? parent : &tv_iters.tag_variable;
+
+		gtk_tree_model_get(GTK_TREE_MODEL(tree_store), icon_iter,
+			SYMBOLS_COLUMN_ICON, &icon, -1);
+
+		if (parent_name)
 		{
-			gtk_tree_model_get(GTK_TREE_MODEL(doc->priv->tag_store), parent,
-		 	                   SYMBOLS_COLUMN_ICON, &icon, -1);
-			gtk_tree_store_append(doc->priv->tag_store, &iter, parent);
-			gtk_tree_store_set(doc->priv->tag_store, &iter,
-		 	                  SYMBOLS_COLUMN_ICON, icon,
-                              SYMBOLS_COLUMN_NAME, buf,
-                              SYMBOLS_COLUMN_LINE, symbol->line, -1);
+			GtkTreeIter *parent_search =
+				(GtkTreeIter *)g_hash_table_lookup(parent_hash, parent_name);
 
-			if (G_LIKELY(G_IS_OBJECT(icon)))
-				g_object_unref(icon);
+			if (parent_search)
+				parent = parent_search;
+			else
+				parent_name = NULL;
 		}
+
+		/* check if the current tag is a parent of other tags */
+		if (g_hash_table_lookup_extended(parent_hash, tag->name, NULL, NULL) &&
+			!utils_str_equal(tag->name, parent_name))	/* prevent Foo::Foo from making parent = child */
+		{
+			GtkTreeIter *new_iter = g_new0(GtkTreeIter, 1);
+
+			/* set an iter value for the hash key */
+			g_hash_table_insert(parent_hash, tag->name, new_iter);
+			/* instead of ignoring the appended child iter below, use the one in the hash table */
+			child = new_iter;
+		}
+
+		gtk_tree_store_append(tree_store, child, parent);
+
+		name = get_symbol_name(doc, tag, (parent_name != NULL));
+		gtk_tree_store_set(tree_store, child,
+			SYMBOLS_COLUMN_ICON, icon,
+			SYMBOLS_COLUMN_NAME, name,
+			SYMBOLS_COLUMN_TAG, tag, -1);
+
+		if (G_LIKELY(G_IS_OBJECT(icon)))
+			g_object_unref(icon);
 	}
+	else
+		geany_debug("Missing symbol-tree parent iter for type %d!", tag->type);
+}
+
+
+static void add_tree_tags(GeanyDocument *doc, const GList *tags)
+{
+	const GList *item;
+	GHashTable *parent_hash;
+
+	/* Create a hash table "parent_tag_name":(GtkTreeIter*) */
+	parent_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free);
+
+	/* find and store all parent names in the hash table */
+	for (item = tags; item; item = g_list_next(item))
+	{
+		const TMTag *tag = item->data;
+		const gchar *name = get_parent_name(tag, FILETYPE_ID(doc->file_type));
+
+		if (name)
+			g_hash_table_insert(parent_hash, (gpointer)name, NULL);
+	}
+	for (item = tags; item; item = g_list_next(item))
+	{
+		const TMTag *tag = item->data;
+
+		add_tree_tag(doc, tag, parent_hash);
+	}
+	g_hash_table_destroy(parent_hash);
+}
+
+
+/* @param item Must be a (gpointer*) for implementation reasons.
+ * @example gchar *name = *item; (for when the GPtrArray contains char pointers). */
+#define foreach_ptr_array(item, ptr_array) \
+	for (item = ptr_array->pdata; item < &ptr_array->pdata[ptr_array->len]; item++)
+
+/* we don't want to sort 1st-level nodes, but we can't return 0 because the tree sort
+ * is not stable, so the order is already lost. */
+static gint compare_top_level_names(const gchar *a, const gchar *b)
+{
+	gpointer *item;
+
+	foreach_ptr_array(item, top_level_iter_names)
+	{
+		const gchar *name = *item;
+
+		if (utils_str_equal(name, a))
+			return -1;
+		if (utils_str_equal(name, b))
+			return 1;
+	}
+	g_warning("Couldn't find top level node '%s' or '%s'!", a, b);
+	return 0;
+}
+
+
+static gboolean tag_has_missing_parent(const TMTag *tag, GtkTreeStore *store,
+		GtkTreeIter *iter)
+{
+	/* if the tag has a parent tag, it should be at depth >= 2 */
+	return NZV(tag->atts.entry.scope) &&
+		gtk_tree_store_iter_depth(store, iter) == 1;
+}
+
+
+static gint tree_sort_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b,
+		gpointer user_data)
+{
+	gboolean sort_by_name = GPOINTER_TO_INT(user_data);
+	const TMTag *tag_a, *tag_b;
+
+	gtk_tree_model_get(model, a, SYMBOLS_COLUMN_TAG, &tag_a, -1);
+	gtk_tree_model_get(model, b, SYMBOLS_COLUMN_TAG, &tag_b, -1);
+
+	/* Check if the iters can be sorted based on tag name and line, not tree item name.
+	 * Sort by tree name if the scope was prepended, e.g. 'ScopeNameWithNoTag::TagName'. */
+	if (tag_a && !tag_has_missing_parent(tag_a, GTK_TREE_STORE(model), a) &&
+		tag_b && !tag_has_missing_parent(tag_b, GTK_TREE_STORE(model), b))
+	{
+		return sort_by_name ? compare_symbol(tag_a, tag_b) :
+			compare_symbol_lines(tag_a, tag_b);
+	}
+	else
+	{
+		gchar *astr, *bstr;
+		gint cmp;
+
+		gtk_tree_model_get(model, a, SYMBOLS_COLUMN_NAME, &astr, -1);
+		gtk_tree_model_get(model, b, SYMBOLS_COLUMN_NAME, &bstr, -1);
+
+		/* if a is toplevel, b must be also */
+		if (gtk_tree_store_iter_depth(GTK_TREE_STORE(model), a) == 0)
+		{
+			cmp = compare_top_level_names(astr, bstr);
+		}
+		else
+		{
+			cmp = strcmp(astr, bstr);
+
+			/* sort duplicate 'ScopeName::OverloadedTagName' items by line as well */
+			if (tag_a && tag_b)
+				if (!sort_by_name ||
+					(utils_str_equal(tag_a->name, tag_b->name) &&
+						utils_str_equal(tag_a->atts.entry.scope, tag_b->atts.entry.scope)))
+					cmp = compare_symbol_lines(tag_a, tag_b);
+		}
+		g_free(astr);
+		g_free(bstr);
+		return cmp;
+	}
+}
+
+
+static void sort_tree(GtkTreeStore *store, gboolean sort_by_name)
+{
+	gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(store), 1, tree_sort_func,
+		GINT_TO_POINTER(sort_by_name), NULL);
+
+	gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store), 1, GTK_SORT_ASCENDING);
+}
+
+
+gboolean symbols_recreate_tag_list(GeanyDocument *doc, gint sort_mode)
+{
+	GList *tags;
+	static gint prev_sort_mode = SYMBOLS_SORT_BY_NAME;
+
+	g_return_val_if_fail(doc != NULL, FALSE);
+
+	tags = get_tag_list(doc, tm_tag_max_t);
+	if (tags == NULL)
+		return FALSE;
+
+	/* Make sure the model stays with us after the tree view unrefs it */
+	g_object_ref(GTK_TREE_MODEL(doc->priv->tag_store));
+	/* Detach model from view */
+	gtk_tree_view_set_model(GTK_TREE_VIEW(doc->priv->tag_tree), NULL);
+	/* Clear all contents */
+	gtk_tree_store_clear(doc->priv->tag_store);
+
+	/* add grandparent type iters */
+	add_top_level_items(doc);
+
+	add_tree_tags(doc, tags);
+	g_list_free(tags);
+
 	hide_empty_rows(doc->priv->tag_store);
+
+	if (sort_mode == SYMBOLS_SORT_USE_PREVIOUS)
+		sort_mode = prev_sort_mode;
+	else
+		prev_sort_mode = sort_mode;
+
+	sort_tree(doc->priv->tag_store, sort_mode == SYMBOLS_SORT_BY_NAME);
+
 	/* Re-attach model to view */
 	gtk_tree_view_set_model(GTK_TREE_VIEW(doc->priv->tag_tree),
 		GTK_TREE_MODEL(doc->priv->tag_store));

Modified: trunk/src/treeviews.c
===================================================================
--- trunk/src/treeviews.c	2008-11-10 12:28:06 UTC (rev 3196)
+++ trunk/src/treeviews.c	2008-11-10 16:57:18 UTC (rev 3197)
@@ -190,7 +190,7 @@
 		if (doc->priv->tag_tree == NULL)
 		{
 			doc->priv->tag_store = gtk_tree_store_new(
-				SYMBOLS_N_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_INT);
+				SYMBOLS_N_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER);
 			doc->priv->tag_tree = gtk_tree_view_new();
 			prepare_taglist(doc->priv->tag_tree, doc->priv->tag_store);
 			gtk_widget_show(doc->priv->tag_tree);
@@ -640,7 +640,13 @@
 
 	if (gtk_tree_selection_get_selected(selection, &model, &iter))
 	{
-		gtk_tree_model_get(model, &iter, SYMBOLS_COLUMN_LINE, &line, -1);
+		const TMTag *tag;
+
+		gtk_tree_model_get(model, &iter, SYMBOLS_COLUMN_TAG, &tag, -1);
+		if (!tag)
+			return FALSE;
+
+		line = tag->atts.entry.line;
 		if (line > 0)
 		{
 			GeanyDocument *doc = document_get_current();

Modified: trunk/src/treeviews.h
===================================================================
--- trunk/src/treeviews.h	2008-11-10 12:28:06 UTC (rev 3196)
+++ trunk/src/treeviews.h	2008-11-10 16:57:18 UTC (rev 3197)
@@ -41,7 +41,7 @@
 {
 	SYMBOLS_COLUMN_ICON,
 	SYMBOLS_COLUMN_NAME,
-	SYMBOLS_COLUMN_LINE,
+	SYMBOLS_COLUMN_TAG,
 	SYMBOLS_N_COLUMNS
 };
 


This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.



More information about the Commits mailing list