? folding4.patch
? tests/test-fold
Index: docs/reference/tmpl/view.sgml
===================================================================
RCS file: /cvs/gnome/gtksourceview/docs/reference/tmpl/view.sgml,v
retrieving revision 1.4
diff -u -p -r1.4 view.sgml
--- docs/reference/tmpl/view.sgml	8 Mar 2005 10:47:58 -0000	1.4
+++ docs/reference/tmpl/view.sgml	30 Aug 2005 10:26:28 -0000
@@ -57,6 +57,11 @@ GtkSourceView
 
 </para>
 
+<!-- ##### ARG GtkSourceView:show-fold-regions ##### -->
+<para>
+
+</para>
+
 <!-- ##### ARG GtkSourceView:show-line-markers ##### -->
 <para>
 
@@ -78,6 +83,11 @@ GtkSourceView
 </para>
 
 <!-- ##### ARG GtkSourceView:tabs-width ##### -->
+<para>
+
+</para>
+
+<!-- ##### ARG GtkSourceView:expander-size ##### -->
 <para>
 
 </para>
Index: gtksourceview/Makefile.am
===================================================================
RCS file: /cvs/gnome/gtksourceview/gtksourceview/Makefile.am,v
retrieving revision 1.16
diff -u -p -r1.16 Makefile.am
--- gtksourceview/Makefile.am	21 Feb 2004 00:08:23 -0000	1.16
+++ gtksourceview/Makefile.am	30 Aug 2005 10:26:28 -0000
@@ -25,6 +25,7 @@ lib_LTLIBRARIES = libgtksourceview-1.0.l
 
 libgtksourceview_headers =			\
 	gtksourcebuffer.h			\
+	gtksourcefold.h				\
 	gtksourceiter.h				\
 	gtksourcetag.h				\
 	gtksourceview.h				\
@@ -38,6 +39,8 @@ libgtksourceview_headers =			\
 
 libgtksourceview_1_0_la_SOURCES = 		\
 	gtksourcebuffer.c 		\
+	gtksourcefold.c			\
+	gtksourcefold-private.h		\
 	gtksourceiter.c			\
 	gtksourcetag.c 			\
 	gtksourcetag-private.h 		\
Index: gtksourceview/gtksourcebuffer.c
===================================================================
RCS file: /cvs/gnome/gtksourceview/gtksourceview/gtksourcebuffer.c,v
retrieving revision 1.39
diff -u -p -r1.39 gtksourcebuffer.c
--- gtksourceview/gtksourcebuffer.c	21 Jun 2005 17:29:28 -0000	1.39
+++ gtksourceview/gtksourcebuffer.c	30 Aug 2005 10:26:29 -0000
@@ -33,6 +33,7 @@
 
 #include "gtksourceview-i18n.h"
 #include "gtksourcebuffer.h"
+#include "gtksourcefold-private.h"
 #include "gtksourcetag.h"
 #include "gtksourcetag-private.h"
 
@@ -139,6 +140,8 @@ struct _GtkSourceBufferPrivate 
 	GtkSourceLanguage     *language;
 
 	GtkSourceUndoManager  *undo_manager;
+	
+	GSList                *folds;
 };
 
 
@@ -380,6 +383,7 @@ gtk_source_buffer_init (GtkSourceBuffer 
 	priv->check_brackets = TRUE;
 	priv->bracket_mark = NULL;
 	priv->bracket_found = FALSE;
+	priv->folds = NULL;
 	
 	priv->markers = g_array_new (FALSE, FALSE, sizeof (GtkSourceMarker *));
 
@@ -480,6 +484,10 @@ gtk_source_buffer_constructor (GType    
 
 		gtk_source_tag_style_free (tag_style);
 
+		/* Create invisibility tag for folding lines. */
+		gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (source_buffer),
+					    INVISIBLE_LINE, "invisible", TRUE, NULL);
+
 		if (GTK_IS_SOURCE_TAG_TABLE (GTK_TEXT_BUFFER (source_buffer)->tag_table))
 		{
 			g_signal_connect (GTK_TEXT_BUFFER (source_buffer)->tag_table ,
@@ -3690,3 +3698,456 @@ gtk_source_buffer_get_prev_marker (GtkSo
 	return marker;
 }
 
+static gboolean
+insert_child_fold (GtkSourceBuffer *buffer,
+		   GtkSourceFold   *child,
+		   GtkSourceFold   *parent)
+{
+	GtkTextIter begin, end, pbegin, pend, iter;
+	
+	gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
+					  &begin, child->start_line);
+	gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
+					  &end, child->end_line);
+
+	gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
+					  &pbegin, parent->start_line);
+	gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
+					  &pend, parent->end_line);
+
+	g_message ("insert child (%d, %d) into parent (%d, %d)",
+		   gtk_text_iter_get_line (&begin),
+		   gtk_text_iter_get_line (&end),
+		   gtk_text_iter_get_line (&pbegin),
+		   gtk_text_iter_get_line (&pend));
+
+	/* check if the child fold is within the parent fold. */
+	if (gtk_text_iter_compare (&pbegin, &begin) == -1 &&
+	    gtk_text_iter_compare (&pend, &end) == 1)
+	{
+		/* fold already has children; try inserting it into one. */
+		if (parent->children != NULL)
+		{
+			GSList *folds = parent->children;
+			
+			while (folds != NULL)
+			{
+				GtkSourceFold *child_fold = folds->data;
+			
+				gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
+								  &iter, child_fold->start_line);
+				/* check if the current fold is past the new fold. */
+				if (gtk_text_iter_compare (&iter, &end) == 1)
+				{
+					g_message ("adding fold before child @ %d", gtk_text_iter_get_line (&iter));
+					parent->children = g_slist_insert_before (parent->children,
+										  folds, child);
+					child->parent = parent;
+					return TRUE;
+				}
+
+				/* try inserting the child fold recursively. */
+				if (insert_child_fold (buffer, child, child_fold))
+					return TRUE;
+				
+				folds = g_slist_next (folds);
+			}
+		}
+
+		/* fold is inside parent, but not a child of parent. */		
+		g_message ("adding fold to parent fold @ %d", gtk_text_iter_get_line (&pbegin));
+		parent->children = g_slist_append (parent->children, child);
+		child->parent = parent;
+		return TRUE;
+	}
+	/* check if the child fold overlaps the parent fold. */
+	else if (gtk_text_iter_compare (&pbegin, &begin) == 1 &&
+		 gtk_text_iter_compare (&pend, &end) == -1)
+	{
+		GtkSourceFold *sibling;
+		GSList *siblings, *reparent, *l;
+		
+		if (parent->parent != NULL)
+			siblings = g_slist_find (parent->parent->children, parent);
+		else
+			siblings = g_slist_find (buffer->priv->folds, parent);
+		
+		reparent = NULL;
+		reparent = g_slist_append (reparent, parent);
+
+		g_message ("child overlaps parent; need to reparent...");
+		
+		/* We need to determine which siblings the child overlaps. Those
+		 * siblings need to be reparented to the child. If the child
+		 * intersects a sibling, then this operation is invalid.
+		 *
+		 * |		<- parent fold
+		 * | |		<- child fold (to be inserted)
+		 * | | [	<- first sibling (reparent)
+		 * | |
+		 * | | [	<- second sibling (reparent)
+		 * | |
+		 * |
+		 * | [		<- third sibling (don't reparent)
+		 * |
+		 */
+		siblings = g_slist_next (siblings);
+		while (siblings != NULL)
+		{
+			sibling = siblings->data;
+		
+			gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
+							  &pend, sibling->end_line);
+			
+			/* check if we are past the last overlapped sibling. */
+			if (gtk_text_iter_compare (&end, &pend) == -1)
+			{
+				gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
+								  &pbegin, sibling->start_line);
+				if (gtk_text_iter_compare (&end, &pbegin) != -1)
+				{
+					g_warning ("Cannot add child fold: fold intersects with an existing fold.");
+					g_slist_free (reparent);
+					return FALSE;
+				}
+				
+				break;
+			}
+			
+			g_message ("reparenting @ %d", gtk_text_iter_get_line (&pend));
+			
+			reparent = g_slist_append (reparent, sibling);
+			siblings = g_slist_next (siblings);
+		}
+
+		/* reparent first sibling to child. */
+		sibling = reparent->data;
+		
+		/* if sibling->parent is NULL, then it's a root fold. */
+		if (sibling->parent == NULL) {
+			siblings = g_slist_find (buffer->priv->folds, sibling);
+			buffer->priv->folds = g_slist_insert_before (buffer->priv->folds,
+								     siblings, child);
+			buffer->priv->folds = g_slist_remove (buffer->priv->folds, sibling);
+			child->children = g_slist_append (child->children, sibling);
+			child->parent = NULL;
+			sibling->parent = child;
+		} else {
+			siblings = g_slist_find (sibling->parent->children, sibling);
+			sibling->parent->children = g_slist_insert_before (sibling->parent->children,
+									   siblings, child);
+			sibling->parent->children = g_slist_remove (sibling->parent->children, sibling);
+			child->children = g_slist_append (child->children, sibling);
+			child->parent = sibling->parent;
+			sibling->parent = child;
+		}
+		
+		/* reparent all the following siblings as well. */
+		l = g_slist_next (reparent);
+		while (l != NULL)
+		{
+			sibling = l->data;
+			
+			if (sibling->parent != NULL)
+				sibling->parent->children = g_slist_remove (sibling->parent->children,
+									    sibling);
+			else
+				buffer->priv->folds = g_slist_remove (buffer->priv->folds, sibling);
+			
+			child->children = g_slist_append (child->children, sibling);
+			sibling->parent = child;
+			
+			l = g_slist_next (l);
+		}
+		
+		g_slist_free (reparent);
+	
+		return TRUE;
+	}
+	else if (gtk_text_iter_in_range (&pbegin, &begin, &end) ||
+		 gtk_text_iter_in_range (&pend, &begin, &end))
+	{
+		g_warning ("Cannot add child fold: fold intersects with an existing fold.");
+	}
+	
+	return FALSE;
+}
+
+GtkSourceFold *
+gtk_source_buffer_add_fold (GtkSourceBuffer   *buffer,
+			    const GtkTextIter *begin,
+			    const GtkTextIter *end)
+{
+	GSList *folds;
+	GtkSourceFold *fold, *parent;
+	GtkTextIter iter;
+
+	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), NULL);
+	g_return_val_if_fail (begin != NULL, NULL);
+	g_return_val_if_fail (end != NULL, NULL);
+
+	g_message ("add fold @ %d, %d", gtk_text_iter_get_line (begin), gtk_text_iter_get_line (end));
+
+	fold = g_new0 (GtkSourceFold, 1);
+	fold->buffer = buffer;
+	fold->parent = NULL;
+	fold->children = NULL;
+	fold->folded = FALSE;
+	fold->prelighted = FALSE;
+	fold->animated = FALSE;
+	fold->expander_style = GTK_EXPANDER_EXPANDED;
+	
+	fold->start_line = gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (buffer),
+							NULL, begin, FALSE);
+	fold->end_line = gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (buffer),
+						      NULL, end, FALSE);
+	
+	/* Insert the fold either at the root level or as a child of an existing fold. */
+	folds = buffer->priv->folds;
+	while (folds != NULL) {
+		parent = folds->data;
+
+		/* check if the current fold is past the new fold. */
+		gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
+						  &iter, parent->start_line);
+		if (gtk_text_iter_compare (&iter, end) == 1)
+		{
+			g_message ("adding fold before root %d", gtk_text_iter_get_line (&iter)); 
+			buffer->priv->folds = g_slist_insert_before (buffer->priv->folds,
+								     folds, fold);
+			break;
+		}
+
+		/* try adding the child to all folds in the region. */
+		if (insert_child_fold (buffer, fold, parent))
+			break;
+	
+		folds = g_slist_next (folds);
+	}
+	
+	/* add the fold at the end of the list. */
+	if (folds == NULL) {
+		g_message ("adding fold at end of root");
+		buffer->priv->folds = g_slist_append (buffer->priv->folds, fold);
+	}
+	
+	return fold;
+}
+
+static void
+foreach_fold_region (gpointer data, gpointer user_data)
+{
+	gtk_source_buffer_remove_fold (GTK_SOURCE_BUFFER (user_data), data);
+}
+
+void
+gtk_source_buffer_remove_fold (GtkSourceBuffer *buffer,
+			       GtkSourceFold   *fold)
+{
+	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
+	g_return_if_fail (fold != NULL);
+	g_return_if_fail (g_slist_index (buffer->priv->folds, fold) != -1);
+	
+	if (fold->folded) {
+		/* FIXME */
+	}
+	
+	gtk_text_buffer_delete_mark (GTK_TEXT_BUFFER (buffer), fold->start_line);
+	gtk_text_buffer_delete_mark (GTK_TEXT_BUFFER (buffer), fold->end_line);
+	
+	buffer->priv->folds = g_slist_remove (buffer->priv->folds, fold);
+	
+	g_slist_foreach (buffer->priv->folds, foreach_fold_region, buffer);
+	
+	g_free (fold);
+}
+
+static void
+get_folds_in_region (GtkTextBuffer     *buffer,
+		     const GtkTextIter *begin,
+		     const GtkTextIter *end,
+		     GtkSourceFold     *fold,
+		     GSList           **list)
+{
+	GtkTextIter fbegin, fend;
+	GSList *children;
+
+	gtk_text_buffer_get_iter_at_mark (buffer, &fbegin, fold->start_line);
+	gtk_text_buffer_get_iter_at_mark (buffer, &fend, fold->end_line);
+
+	/* the region lies in the fold, so add any children in the region. */
+	if (gtk_text_iter_compare (&fbegin, begin) == -1 &&
+	    gtk_text_iter_compare (&fend, end) >= 0)
+	{
+		children = fold->children;
+		while (children != NULL) {
+			get_folds_in_region (buffer, begin, end, children->data, list);
+			children = g_slist_next (children);
+		}
+	}
+	/* the entire fold lies in the region, so add the fold + children. */
+	else if (gtk_text_iter_compare (&fbegin, begin) >= 0 &&
+	         gtk_text_iter_compare (&fend, end) <= 0)
+	{
+		*list = g_slist_append (*list, fold);
+		children = fold->children;
+		while (children != NULL) {
+			get_folds_in_region (buffer, begin, end, children->data, list);
+			children = g_slist_next (children);
+		}
+	}
+	/* start iter is in the region, so add the fold first. */
+	else if (gtk_text_iter_compare (&fbegin, begin) >= 0 &&
+	         gtk_text_iter_compare (&fbegin, end) <= 0)
+	{
+		*list = g_slist_append (*list, fold);
+		children = fold->children;
+		while (children != NULL) {
+			get_folds_in_region (buffer, begin, end, children->data, list);
+			children = g_slist_next (children);
+		}
+	}
+}
+
+/**
+ * gtk_source_buffer_get_folds_in_region:
+ * @buffer: a #GtkSourceBuffer.
+ * @begin: the begin point of the region.
+ * @end: the end point of the region>
+ * 
+ * Return a list of all folds in the specified region. This is a flattened list
+ * of the parent->child fold hierarchy. This function is mainly used in 
+ * gtk_source_view_get_lines to determine which folds to draw.
+ * 
+ * Return value: a #GSList of the #GtkSourceFold's in the region, or %NULL if
+ * there are no folds in the region.
+ **/
+GSList *
+_gtk_source_buffer_get_folds_in_region (GtkSourceBuffer   *buffer,
+				       const GtkTextIter *begin,
+				       const GtkTextIter *end)
+{
+	GSList *result, *folds;
+	GtkSourceFold *fold;
+
+	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), NULL);
+	g_return_val_if_fail (begin != NULL && end != NULL, NULL);
+
+	result = NULL;
+	folds = buffer->priv->folds;
+	while (folds != NULL)
+	{
+		fold = folds->data;
+
+		get_folds_in_region (GTK_TEXT_BUFFER (buffer), begin, end, fold, &result);
+
+		folds = g_slist_next (folds);
+	}
+
+	return result;
+}
+
+static GtkSourceFold *
+find_fold_at_line (GtkTextBuffer *buffer,
+		   GSList        *folds,
+		   gint           line)
+{
+	GtkSourceFold *fold;
+	GtkTextIter iter;
+	gint start_line, end_line;
+			
+	while (folds != NULL)
+	{
+		fold = folds->data;
+		
+		gtk_text_buffer_get_iter_at_mark (buffer, &iter, fold->start_line);
+		start_line = gtk_text_iter_get_line (&iter);
+		gtk_text_buffer_get_iter_at_mark (buffer, &iter, fold->end_line);
+		end_line = gtk_text_iter_get_line (&iter);
+		
+		if (line >= start_line && line <= end_line) {
+			GtkSourceFold *child;
+		
+			if (!fold->children)
+				return fold;
+			
+			child = find_fold_at_line (buffer, fold->children, line);
+			if (child)
+				return child;
+			else
+				return fold;
+		}
+	
+		folds = g_slist_next (folds);
+	}
+	
+	return NULL;
+}
+
+GtkSourceFold *
+_gtk_source_buffer_get_fold_at_line (GtkSourceBuffer *buffer,
+				    gint             line)
+{
+	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), NULL);
+	g_return_val_if_fail (line >= 0, NULL);
+
+	return find_fold_at_line (GTK_TEXT_BUFFER (buffer),
+				  buffer->priv->folds,
+				  line);
+}
+
+static GtkSourceFold *
+find_fold_at_iter (GtkTextBuffer     *buffer,
+		   GSList            *folds,
+		   const GtkTextIter *iter)
+{
+	GtkSourceFold *fold;
+	GtkTextIter start_iter, end_iter;
+	
+	while (folds != NULL)
+	{
+		fold = folds->data;
+		
+		gtk_text_buffer_get_iter_at_mark (buffer, &start_iter, fold->start_line);
+		gtk_text_buffer_get_iter_at_mark (buffer, &end_iter, fold->end_line);
+		
+		if (gtk_text_iter_compare (&start_iter, iter) <= 0 &&
+		    gtk_text_iter_compare (&end_iter, iter) >= 0)
+		{
+			GtkSourceFold *child;
+		
+			if (!fold->children)
+				return fold;
+			
+			child = find_fold_at_iter (buffer, fold->children, iter);
+			if (child)
+				return child;
+			else
+				return fold;
+		}
+	
+		folds = g_slist_next (folds);
+	}
+	
+	return NULL;
+}
+
+GtkSourceFold *
+gtk_source_buffer_get_fold_at_iter (GtkSourceBuffer   *buffer,
+				    const GtkTextIter *iter)
+{
+	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), NULL);
+	g_return_val_if_fail (iter != NULL, NULL);
+
+	return find_fold_at_iter (GTK_TEXT_BUFFER (buffer),
+				  buffer->priv->folds,
+				  iter);
+}
+
+const GSList *
+gtk_source_buffer_get_root_folds (GtkSourceBuffer *buffer)
+{
+	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), NULL);
+	
+	return buffer->priv->folds;
+}
Index: gtksourceview/gtksourcebuffer.h
===================================================================
RCS file: /cvs/gnome/gtksourceview/gtksourceview/gtksourcebuffer.h,v
retrieving revision 1.18
diff -u -p -r1.18 gtksourcebuffer.h
--- gtksourceview/gtksourcebuffer.h	6 Jun 2003 08:54:36 -0000	1.18
+++ gtksourceview/gtksourcebuffer.h	30 Aug 2005 10:26:29 -0000
@@ -29,6 +29,7 @@
 #include <gtk/gtk.h>
 #include <gtksourceview/gtksourcetagtable.h>
 #include <gtksourceview/gtksourcelanguage.h>
+#include <gtksourceview/gtksourcefold.h>
 
 G_BEGIN_DECLS
 
@@ -68,7 +69,6 @@ struct _GtkSourceBufferClass 
 	void (*_gtk_source_reserved1) 	(void);
 	void (*_gtk_source_reserved2) 	(void);
 	void (*_gtk_source_reserved3) 	(void);
-
 };
 
 #include <gtksourceview/gtksourcemarker.h>
@@ -141,6 +141,18 @@ GtkSourceMarker         *gtk_source_buff
 GtkSourceMarker         *gtk_source_buffer_get_prev_marker      (GtkSourceBuffer        *buffer,
 								 GtkTextIter            *iter);
 
+/* fold methods. */
+GtkSourceFold		*gtk_source_buffer_add_fold		(GtkSourceBuffer        *buffer,
+								 const GtkTextIter      *begin,
+								 const GtkTextIter      *end);
+void			 gtk_source_buffer_remove_fold		(GtkSourceBuffer        *buffer,
+								 GtkSourceFold          *fold);
+
+GtkSourceFold		*gtk_source_buffer_get_fold_at_iter	(GtkSourceBuffer        *buffer,
+								 const GtkTextIter      *iter);
+
+const GSList		*gtk_source_buffer_get_root_folds	(GtkSourceBuffer        *buffer);
+
 /* INTERNAL private stuff - not even exported from the library on
  * many platforms
  */
@@ -148,6 +160,14 @@ void 	                 _gtk_source_buffe
 								 const GtkTextIter      *start,
 								 const GtkTextIter      *end,
 								 gboolean                highlight_now);
+
+GSList			*_gtk_source_buffer_get_folds_in_region	(GtkSourceBuffer        *buffer,
+								 const GtkTextIter      *begin,
+								 const GtkTextIter      *end);
+
+GtkSourceFold		*_gtk_source_buffer_get_fold_at_line	(GtkSourceBuffer        *buffer,
+								 gint                    line);
+
 
 G_END_DECLS
 
Index: gtksourceview/gtksourcefold-private.h
===================================================================
RCS file: gtksourceview/gtksourcefold-private.h
diff -N gtksourceview/gtksourcefold-private.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gtksourceview/gtksourcefold-private.h	30 Aug 2005 10:26:29 -0000
@@ -0,0 +1,64 @@
+/*  gtksourcefold-private.h
+ *
+ *  Copyright (C) 2005 - Jeroen Zwartepoorte <jeroen.zwartepoorte@gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_SOURCE_FOLD_PRIVATE_H__
+#define __GTK_SOURCE_FOLD_PRIVATE_H__
+
+#include "gtksourcefold.h"
+#include "gtksourcebuffer.h"
+
+G_BEGIN_DECLS
+
+#define INVISIBLE_LINE "GtkSourceBuffer:InvisibleLine"
+
+struct _GtkSourceFold
+{
+	/* Needed for sanity check since GtkSourceFold is a boxed type. */
+	GtkSourceBuffer *buffer;
+	
+	/* Markers for the start & end of the fold. */
+	GtkTextMark	*start_line;
+	GtkTextMark	*end_line;
+	
+	/* Add reference to parent fold; needed for reparenting. */
+	GtkSourceFold	*parent;
+	
+	/* List of child folds; sorted by appearance. */
+	GSList		*children;
+	
+	/* TRUE if the fold has collapsed. */
+	gboolean	 folded;
+	
+	/* TRUE if the user moves the mouse over the expander arrow; draw the
+	 * expander filled to indicate the mouse over. */
+	gboolean	 prelighted;
+	
+	/* TRUE if the user expanded/collapsed a fold using the GUI; animate
+	 * the collapse/expansion of the fold. */
+	gboolean	 animated;
+	
+	/* Style of the expander arrow; if animated is set, this will gradually
+	 * increase to show the fold is collapsing/expanding. */
+	GtkExpanderStyle expander_style;
+};
+
+G_END_DECLS
+
+#endif  /* __GTK_SOURCE_FOLD_PRIVATE_H__ */
+
Index: gtksourceview/gtksourcefold.c
===================================================================
RCS file: gtksourceview/gtksourcefold.c
diff -N gtksourceview/gtksourcefold.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gtksourceview/gtksourcefold.c	30 Aug 2005 10:26:29 -0000
@@ -0,0 +1,185 @@
+/*  gtksourcefold.c
+ *
+ *  Copyright (C) 2005 - Jeroen Zwartepoorte <jeroen.zwartepoorte@gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gtksourcefold.h"
+#include "gtksourcefold-private.h"
+
+GType
+gtk_source_fold_get_type (void)
+{
+	static GType our_type = 0;
+
+	if (our_type == 0)
+		our_type = g_boxed_type_register_static ("GtkSourceFold",
+							 (GBoxedCopyFunc) gtk_source_fold_copy,
+							 (GBoxedFreeFunc) gtk_source_fold_free);
+
+	return our_type;
+}
+
+void
+gtk_source_fold_free (GtkSourceFold *fold)
+{
+	if (!fold)
+		return;
+
+	/* FIXME: add a check that we're not freeing a fold that is in the buffer. */
+	
+	if (fold->children) {
+		g_slist_free (fold->children);
+		fold->children = NULL;
+	}
+	
+	g_free (fold);
+}
+
+GtkSourceFold *
+gtk_source_fold_copy (const GtkSourceFold *fold)
+{
+	GtkSourceFold *copy;
+
+	g_return_val_if_fail (fold != NULL, NULL);
+
+	copy = g_new (GtkSourceFold, 1);
+	copy->buffer = fold->buffer;
+	g_object_ref (fold->start_line);
+	copy->start_line = fold->start_line;
+	g_object_ref (fold->end_line);
+	copy->end_line = fold->end_line;
+	copy->children = g_slist_copy (fold->children);
+	copy->folded = fold->folded;
+	copy->prelighted = fold->prelighted;
+	copy->animated = fold->animated;
+	copy->expander_style = fold->expander_style;
+	
+	return copy;
+}
+
+gboolean
+gtk_source_fold_get_folded (GtkSourceFold *fold)
+{
+	g_return_val_if_fail (fold != NULL, FALSE);
+
+	return fold->folded;
+}
+
+static void
+reapply_invisibleline_tag (GtkTextBuffer *buffer,
+			   GSList        *folds)
+{
+	GtkSourceFold *fold;
+	GtkTextIter begin, end;
+	
+	while (folds != NULL) {
+		fold = folds->data;
+		
+		if (fold->folded) {
+			gtk_text_buffer_get_iter_at_mark (buffer, &begin,
+							  fold->start_line);	
+			gtk_text_buffer_get_iter_at_mark (buffer, &end,
+							  fold->end_line);
+			gtk_text_buffer_apply_tag_by_name (buffer, INVISIBLE_LINE,
+							   &begin, &end);
+		} else if (fold->children != NULL) {
+			reapply_invisibleline_tag (buffer, fold->children);
+		}
+	
+		folds = g_slist_next (folds);
+	}
+}
+
+void
+gtk_source_fold_set_folded (GtkSourceFold *fold,
+			    gboolean       folded)
+{
+	GtkTextBuffer *buffer;
+	GtkTextIter begin, end, insert;
+
+	g_return_if_fail (fold != NULL);
+
+	folded = (folded != FALSE);
+	
+	if (fold->folded == folded)
+		return;
+	
+	fold->folded = folded;
+
+	buffer = GTK_TEXT_BUFFER (fold->buffer);
+
+	gtk_text_buffer_get_iter_at_mark (buffer, &begin, fold->start_line);	
+	gtk_text_buffer_get_iter_at_mark (buffer, &end, fold->end_line);
+	
+	if (folded) {
+		gtk_text_buffer_apply_tag_by_name (buffer, INVISIBLE_LINE,
+						   &begin, &end);
+
+		gtk_text_buffer_get_iter_at_mark (buffer, &insert,
+						  gtk_text_buffer_get_insert (buffer));
+		
+		/* make the cursor visible again if it was inside the fold. */
+		if (gtk_text_iter_in_range (&insert, &begin, &end)) {
+			if (!gtk_text_iter_forward_visible_cursor_position (&insert))
+				gtk_text_iter_backward_visible_cursor_position (&insert);
+		
+			gtk_text_buffer_place_cursor (buffer, &insert);
+		}
+	
+		if (!fold->animated)
+			fold->expander_style = GTK_EXPANDER_COLLAPSED;
+	} else {
+		gtk_text_buffer_remove_tag_by_name (buffer, INVISIBLE_LINE,
+						    &begin, &end);
+		
+		/* reapply the invisibleline tag to collapsed children. */
+		if (fold->children != NULL)
+			reapply_invisibleline_tag (buffer, fold->children);
+		
+		if (!fold->animated)
+			fold->expander_style = GTK_EXPANDER_EXPANDED;
+	}
+}
+
+void
+gtk_source_fold_get_bounds (GtkSourceFold *fold,
+			    GtkTextIter   *begin,
+			    GtkTextIter   *end)
+{
+	g_return_if_fail (fold != NULL && begin != NULL && end != NULL);
+	
+	gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (fold->buffer),
+					  begin, fold->start_line);
+	gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (fold->buffer),
+					  end, fold->end_line);
+}
+
+GtkSourceFold *
+gtk_source_fold_get_parent (GtkSourceFold *fold)
+{
+	g_return_val_if_fail (fold != NULL, NULL);
+	
+	return fold->parent;
+}
+
+const GSList *
+gtk_source_fold_get_children (GtkSourceFold *fold)
+{
+	g_return_val_if_fail (fold != NULL, NULL);
+
+	return fold->children;
+}
Index: gtksourceview/gtksourcefold.h
===================================================================
RCS file: gtksourceview/gtksourcefold.h
diff -N gtksourceview/gtksourcefold.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gtksourceview/gtksourcefold.h	30 Aug 2005 10:26:29 -0000
@@ -0,0 +1,50 @@
+/*  gtksourcefold.h
+ *
+ *  Copyright (C) 2005 - Jeroen Zwartepoorte <jeroen.zwartepoorte@gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_SOURCE_FOLD_H__
+#define __GTK_SOURCE_FOLD_H__
+
+#include <gtk/gtktextiter.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SOURCE_FOLD		(gtk_source_fold_get_type ())
+
+typedef struct _GtkSourceFold		GtkSourceFold;
+
+GType			 gtk_source_fold_get_type	(void) G_GNUC_CONST;
+
+GtkSourceFold		*gtk_source_fold_copy		(const GtkSourceFold *fold);
+void			 gtk_source_fold_free		(GtkSourceFold       *fold);
+
+gboolean		 gtk_source_fold_get_folded	(GtkSourceFold       *fold);
+void			 gtk_source_fold_set_folded	(GtkSourceFold       *fold,
+							 gboolean             folded);
+
+void                     gtk_source_fold_get_bounds	(GtkSourceFold       *fold,
+							 GtkTextIter         *begin,
+							 GtkTextIter         *end);
+
+GtkSourceFold		*gtk_source_fold_get_parent	(GtkSourceFold       *fold);
+
+const GSList		*gtk_source_fold_get_children	(GtkSourceFold       *fold);
+
+G_END_DECLS
+
+#endif  /* __GTK_SOURCE_FOLD_H__ */
Index: gtksourceview/gtksourceview.c
===================================================================
RCS file: /cvs/gnome/gtksourceview/gtksourceview/gtksourceview.c,v
retrieving revision 1.40
diff -u -p -r1.40 gtksourceview.c
--- gtksourceview/gtksourceview.c	29 Aug 2005 16:04:40 -0000	1.40
+++ gtksourceview/gtksourceview.c	30 Aug 2005 10:26:30 -0000
@@ -36,6 +36,7 @@
 #include "gtksourceview-i18n.h"
 #include "gtksourceview-marshal.h"
 #include "gtksourceview.h"
+#include "gtksourcefold-private.h"
 
 /*
 #define ENABLE_DEBUG
@@ -58,6 +59,9 @@
 #define DEFAULT_MARGIN			80
 #define MAX_MARGIN			200
 
+#define DEFAULT_EXPANDER_SIZE		12
+#define EXPANDER_EXTRA_PADDING		4
+
 /* Signals */
 enum {
 	UNDO,
@@ -76,7 +80,8 @@ enum {
 	PROP_SHOW_MARGIN,
 	PROP_MARGIN,
 	PROP_SMART_HOME_END,
-	PROP_HIGHLIGHT_CURRENT_LINE
+	PROP_HIGHLIGHT_CURRENT_LINE,
+	PROP_SHOW_FOLD_REGIONS
 };
 
 struct _GtkSourceViewPrivate
@@ -96,6 +101,13 @@ struct _GtkSourceViewPrivate
 
 	GtkSourceBuffer *source_buffer;
 	gint		 old_lines;
+	
+	gboolean	 show_fold_regions;
+	gint		 expander_size;
+	gint		 prelight_fold_line;
+	gboolean	 fold_button_down;
+	guint		 animation_timeout;
+	gint		 animate_fold_line;
 };
 
 /* Implement DnD for application/x-color drops */
@@ -127,6 +139,14 @@ static void 	set_source_buffer 			(GtkSo
 static void	gtk_source_view_populate_popup 		(GtkTextView        *view,
 					    		 GtkMenu            *menu);
 
+static gboolean gtk_source_view_motion			(GtkWidget          *widget,
+							 GdkEventMotion     *event);
+
+static gboolean gtk_source_view_button_press		(GtkWidget          *widget,
+							 GdkEventButton     *event);
+static gboolean gtk_source_view_button_release		(GtkWidget          *widget,
+							 GdkEventButton     *event);
+
 static void	gtk_source_view_move_cursor		(GtkTextView        *text_view,
 							 GtkMovementStep     step,
 							 gint                count,
@@ -140,6 +160,7 @@ static void 	gtk_source_view_get_lines 	
 				       			 gint               last_y,
 				       			 GArray            *buffer_coords,
 				       			 GArray            *numbers,
+				       			 GHashTable        *folds,
 				       			 gint              *countp);
 static gint     gtk_source_view_expose 			(GtkWidget         *widget,
 							 GdkEventExpose    *event);
@@ -189,8 +210,11 @@ gtk_source_view_class_init (GtkSourceVie
 	object_class->get_property = gtk_source_view_get_property;
 	object_class->set_property = gtk_source_view_set_property;
 
+	widget_class->button_press_event = gtk_source_view_button_press;
+	widget_class->button_release_event = gtk_source_view_button_release;
 	widget_class->key_press_event = key_press_event;
 	widget_class->expose_event = gtk_source_view_expose;
+	widget_class->motion_notify_event = gtk_source_view_motion;
 	widget_class->style_set = gtk_source_view_style_set;
 
 	textview_class->populate_popup = gtk_source_view_populate_popup;
@@ -216,6 +240,14 @@ gtk_source_view_class_init (GtkSourceVie
 							       G_PARAM_READWRITE));
 
 	g_object_class_install_property (object_class,
+					 PROP_SHOW_FOLD_REGIONS,
+					 g_param_spec_boolean ("show_fold_regions",
+							       _("Show Fold Regions"),
+							       _("Whether to display fold regions"),
+							       FALSE,
+							       G_PARAM_READWRITE));
+
+	g_object_class_install_property (object_class,
 					 PROP_TABS_WIDTH,
 					 g_param_spec_uint ("tabs_width",
 							    _("Tabs Width"),
@@ -276,6 +308,15 @@ gtk_source_view_class_init (GtkSourceVie
 							       FALSE,
 							       G_PARAM_READWRITE));
 
+	gtk_widget_class_install_style_property (widget_class,
+						 g_param_spec_int ("expander-size",
+								   _("Expander Size"),
+								   _("Size of the expander arrow"),
+								   0,
+								   G_MAXINT,
+								   DEFAULT_EXPANDER_SIZE,
+								   G_PARAM_READABLE));
+
 	signals [UNDO] =
 		g_signal_new ("undo",
 			      G_TYPE_FROM_CLASS (klass),
@@ -337,6 +378,11 @@ gtk_source_view_set_property (GObject   
 							       g_value_get_boolean (value));
 			break;
 			
+		case PROP_SHOW_FOLD_REGIONS:
+			gtk_source_view_set_show_fold_regions (view,
+							       g_value_get_boolean (value));
+			break;
+			
 		case PROP_TABS_WIDTH:
 			gtk_source_view_set_tabs_width (view, 
 							g_value_get_uint (value));
@@ -406,6 +452,12 @@ gtk_source_view_get_property (GObject   
 
 			break;
 			
+		case PROP_SHOW_FOLD_REGIONS:
+			g_value_set_boolean (value,
+					     gtk_source_view_get_show_fold_regions (view));
+
+			break;
+			
 		case PROP_TABS_WIDTH:
 			g_value_set_uint (value,
 					  gtk_source_view_get_tabs_width (view));
@@ -460,6 +512,8 @@ gtk_source_view_init (GtkSourceView *vie
 	view->priv->margin = DEFAULT_MARGIN;
 	view->priv->cached_margin_width = -1;
 	view->priv->smart_home_end = TRUE;
+	view->priv->prelight_fold_line = -1;
+	view->priv->fold_button_down = FALSE;
 	
 	view->priv->pixmap_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
 							  (GDestroyNotify) g_free,
@@ -703,6 +757,258 @@ gtk_source_view_populate_popup (GtkTextV
 
 }
 
+static gboolean
+gtk_source_view_motion (GtkWidget *widget, GdkEventMotion *event)
+{
+	GtkSourceView *view;
+	int x, y, y_buf;
+	GtkTextIter line_start;
+	GtkSourceFold *fold;
+	
+	view = GTK_SOURCE_VIEW (widget);
+	
+	if (view->priv->show_fold_regions && event->is_hint &&
+	    event->window == gtk_text_view_get_window (GTK_TEXT_VIEW (view),
+						       GTK_TEXT_WINDOW_LEFT))
+	{
+		gboolean redraw = FALSE;
+	
+		/* disable prelight on previous fold */
+		if (view->priv->prelight_fold_line != -1) {
+			fold = _gtk_source_buffer_get_fold_at_line (view->priv->source_buffer,
+								    view->priv->prelight_fold_line);
+			
+			if (fold != NULL) {
+				fold->prelighted = FALSE;
+				redraw = TRUE;
+			}
+			
+			view->priv->prelight_fold_line = -1;
+		}
+			
+		/* Calling get_pointer will generate a new motion event the
+		   next time we move the pointer. */
+		gdk_window_get_pointer (event->window, &x, &y, NULL);
+		
+		gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (view),
+						       GTK_TEXT_WINDOW_LEFT,
+						       x, y, NULL, &y_buf);
+		
+		gtk_text_view_get_line_at_y (GTK_TEXT_VIEW (view),
+					     &line_start,
+					     y_buf,
+					     NULL);
+
+		//g_message ("motion at line %d", gtk_text_iter_get_line (&line_start));
+
+		fold = _gtk_source_buffer_get_fold_at_line (view->priv->source_buffer,
+							    gtk_text_iter_get_line (&line_start));
+		
+		/* check if the starting fold is on the same line as the cursor */	  
+		if (fold != NULL) {
+			GtkTextIter fold_start;
+			
+			gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (view->priv->source_buffer),
+							  &fold_start, fold->start_line);
+
+			//g_message ("fold present @ %d", gtk_text_iter_get_line (&fold_start));
+
+			if (gtk_text_iter_get_line (&line_start) ==
+			    gtk_text_iter_get_line (&fold_start))
+			{
+				fold->prelighted = TRUE;
+				redraw = TRUE;
+				view->priv->prelight_fold_line = gtk_text_iter_get_line (&line_start);
+			}
+		}
+		
+		if (redraw)
+			gtk_widget_queue_draw (widget);
+		
+		return TRUE;
+	}
+	else if (view->priv->show_fold_regions && event->is_hint &&
+	         view->priv->prelight_fold_line != -1)
+	{
+		fold = _gtk_source_buffer_get_fold_at_line (view->priv->source_buffer,
+							    view->priv->prelight_fold_line);
+							  
+		/* disable prelight on previous fold */
+		if (fold != NULL) {
+			fold->prelighted = FALSE;
+			gtk_widget_queue_draw (widget);
+		}
+		
+		view->priv->prelight_fold_line = -1;
+
+		return GTK_WIDGET_CLASS (parent_class)->motion_notify_event (widget, event);
+	}
+	else
+	{
+		return GTK_WIDGET_CLASS (parent_class)->motion_notify_event (widget, event);
+	}
+}
+
+static gboolean
+gtk_source_view_button_press (GtkWidget *widget, GdkEventButton *event)
+{
+	GtkSourceView *view;
+	int y_buf;
+	GtkTextIter line_start;
+	GtkSourceFold *fold;
+	
+	view = GTK_SOURCE_VIEW (widget);
+	
+	if (view->priv->show_fold_regions && event->button == 1 &&
+	    event->window == gtk_text_view_get_window (GTK_TEXT_VIEW (view),
+						       GTK_TEXT_WINDOW_LEFT))
+	{
+		gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (view),
+						       GTK_TEXT_WINDOW_LEFT,
+						       event->x, event->y,
+						       NULL, &y_buf);
+		
+		gtk_text_view_get_line_at_y (GTK_TEXT_VIEW (view),
+					     &line_start,
+					     y_buf,
+					     NULL);
+
+		fold = _gtk_source_buffer_get_fold_at_line (view->priv->source_buffer,
+							    gtk_text_iter_get_line (&line_start));
+		
+		if (fold != NULL)
+		{
+			GtkTextIter fold_start;
+			
+			gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (view->priv->source_buffer),
+							  &fold_start, fold->start_line);
+
+			if (gtk_text_iter_get_line (&line_start) ==
+			    gtk_text_iter_get_line (&fold_start))
+			{
+				view->priv->fold_button_down = TRUE;
+			}
+		}
+
+		return TRUE;
+	}
+	else
+	{
+		return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
+	}
+}
+
+static gboolean
+fold_animation_timeout (GtkSourceView *view)
+{
+	gboolean finish = FALSE;
+	GtkSourceFold *fold;
+
+
+	fold = _gtk_source_buffer_get_fold_at_line (view->priv->source_buffer,
+						    view->priv->animate_fold_line);
+	
+	if (fold == NULL)
+	{
+		view->priv->animation_timeout = 0;
+		return FALSE;
+	}
+
+	GDK_THREADS_ENTER ();
+
+	if (fold->folded)
+	{
+		if (fold->expander_style == GTK_EXPANDER_EXPANDED)
+		{
+			fold->expander_style = GTK_EXPANDER_SEMI_COLLAPSED;
+		}
+		else
+		{
+			fold->expander_style = GTK_EXPANDER_COLLAPSED;
+			finish = TRUE;
+		}
+	}
+	else
+	{
+		if (fold->expander_style == GTK_EXPANDER_COLLAPSED)
+		{
+			fold->expander_style = GTK_EXPANDER_SEMI_EXPANDED;
+		}
+		else
+		{
+			fold->expander_style = GTK_EXPANDER_EXPANDED;
+			finish = TRUE;
+		}
+	}
+
+	if (finish)
+	{
+		view->priv->animation_timeout = 0;
+		view->priv->animate_fold_line = -1;
+		fold->animated = FALSE;
+	}
+
+	gtk_widget_queue_draw (GTK_WIDGET (view));
+
+	GDK_THREADS_LEAVE ();
+
+	return !finish;
+}
+
+static void
+start_fold_animation (GtkSourceView *view)
+{
+	if (view->priv->animation_timeout)
+		g_source_remove (view->priv->animation_timeout);
+
+	view->priv->animation_timeout =
+		g_timeout_add (50, (GSourceFunc) fold_animation_timeout, view);
+}
+
+static gboolean
+gtk_source_view_button_release (GtkWidget *widget, GdkEventButton *event)
+{
+	GtkSourceView *view;
+	int y_buf;
+	GtkTextIter line_start;
+	GtkSourceFold *fold;
+	
+	view = GTK_SOURCE_VIEW (widget);
+	
+	if (view->priv->show_fold_regions && event->button == 1 &&
+	    view->priv->fold_button_down &&
+	    event->window == gtk_text_view_get_window (GTK_TEXT_VIEW (view),
+						       GTK_TEXT_WINDOW_LEFT))
+	{
+		gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (view),
+						       GTK_TEXT_WINDOW_LEFT,
+						       event->x, event->y,
+						       NULL, &y_buf);
+		
+		gtk_text_view_get_line_at_y (GTK_TEXT_VIEW (view),
+					     &line_start,
+					     y_buf,
+					     NULL);
+
+		fold = _gtk_source_buffer_get_fold_at_line (view->priv->source_buffer,
+							    gtk_text_iter_get_line (&line_start));
+		
+		if (fold != NULL) {
+			fold->animated = TRUE;
+			gtk_source_fold_set_folded (fold, !fold->folded);
+			view->priv->animate_fold_line = gtk_text_iter_get_line (&line_start);
+			start_fold_animation (view);
+			view->priv->fold_button_down = FALSE;
+		}
+
+		return TRUE;
+	}
+	else
+	{
+		return GTK_WIDGET_CLASS (parent_class)->button_release_event (widget, event);
+	}
+}
+
 static void
 move_cursor (GtkTextView       *text_view,
 	     const GtkTextIter *new_location,
@@ -815,18 +1121,32 @@ gtk_source_view_get_lines (GtkTextView  
 			   gint          last_y,
 			   GArray       *buffer_coords,
 			   GArray       *numbers,
+			   GHashTable   *fold_hash,
 			   gint         *countp)
 {
-	GtkTextIter iter;
+	GtkTextIter iter, iter2, start_fold;
+	GSList *folds, *l;
+	GtkSourceFold *fold = NULL;
 	gint count;
 	gint size;
-      	gint last_line_num = -1;	
+      	gint last_line_num = -1;
 
 	g_array_set_size (buffer_coords, 0);
 	g_array_set_size (numbers, 0);
   
-	/* Get iter at first y */
+	/* get iter at first and last y */
 	gtk_text_view_get_line_at_y (text_view, &iter, first_y, NULL);
+	gtk_text_view_get_line_at_y (text_view, &iter2, last_y, NULL);
+	
+	//g_message ("from %d to %d", gtk_text_iter_get_line (&iter), gtk_text_iter_get_line (&iter2));
+
+	/* forward to line end so we match all folds on the line. */
+	gtk_text_iter_forward_to_line_end (&iter2);
+	
+	/* get a flattened list of all folds in the area */
+	folds = _gtk_source_buffer_get_folds_in_region (GTK_SOURCE_VIEW (text_view)->priv->source_buffer,
+						        &iter, &iter2);
+	l = folds;
 
 	/* For each iter, get its location and add it to the arrays.
 	 * Stop when we pass last_y
@@ -834,22 +1154,60 @@ gtk_source_view_get_lines (GtkTextView  
 	count = 0;
   	size = 0;
 
+	/* start with the first fold in the region */
+	if (g_slist_length (folds) > 0) {
+		fold = folds->data;
+
+		gtk_text_buffer_get_iter_at_mark (text_view->buffer,
+						  &start_fold,
+						  fold->start_line);
+		
+	}
+
+	last_line_num = gtk_text_iter_get_line (&iter);
+	
   	while (!gtk_text_iter_is_end (&iter))
     	{
 		gint y, height;
-      
-		gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
 
+		gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
 		g_array_append_val (buffer_coords, y);
 		last_line_num = gtk_text_iter_get_line (&iter);
+
+		//g_message ("at line %d, %d", last_line_num, gtk_text_iter_get_line (&iter));
+
 		g_array_append_val (numbers, last_line_num);
+
+		/* check if there's a fold on the line. */
+		if (fold != NULL && gtk_text_iter_get_line (&iter) ==
+		    gtk_text_iter_get_line (&start_fold))
+		{
+			g_hash_table_insert (fold_hash,
+					     GINT_TO_POINTER (last_line_num),
+					     fold);
+
+			/* advance to the next fold (if it exists) */
+			folds = g_slist_next (folds);
+			if (folds != NULL)
+			{
+				fold = folds->data;
+
+				gtk_text_buffer_get_iter_at_mark (text_view->buffer,
+								  &start_fold,
+								  fold->start_line);
+			}
+			else
+			{
+				fold = NULL;
+			}
+		}
       	
 		++count;
 
 		if ((y + height) >= last_y)
 			break;
-      
-		gtk_text_iter_forward_line (&iter);
+
+		gtk_text_iter_forward_visible_line (&iter);
 	}
 
 	if (gtk_text_iter_is_end (&iter))
@@ -869,6 +1227,9 @@ gtk_source_view_get_lines (GtkTextView  
 		}
 	}
 
+	if (l != NULL)
+		g_slist_free (l);
+
 	*countp = count;
 }
 
@@ -961,6 +1322,7 @@ static void
 gtk_source_view_paint_margin (GtkSourceView *view,
 			      GdkEventExpose *event)
 {
+	GtkWidget *widget;
 	GtkTextView *text_view;
 	GdkWindow *win;
 	PangoLayout *layout;
@@ -972,14 +1334,19 @@ gtk_source_view_paint_margin (GtkSourceV
 	gint y1, y2;
 	gint count;
 	gint margin_width;
-	gint text_width, x_pixmap;
+	gint text_width, text_height, x_pixmap;
 	gint i;
 	GtkTextIter cur;
 	gint cur_line;
+	GHashTable *folds;
+	GtkSourceFold *fold;
 
+	widget = GTK_WIDGET (view);
 	text_view = GTK_TEXT_VIEW (view);
 
-	if (!view->priv->show_line_numbers && !view->priv->show_line_markers)
+	if (!view->priv->show_line_numbers &&
+	    !view->priv->show_line_markers &&
+	    !view->priv->show_fold_regions)
 	{
 		gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (text_view),
 						      GTK_TEXT_WINDOW_LEFT,
@@ -1011,6 +1378,7 @@ gtk_source_view_paint_margin (GtkSourceV
 
 	numbers = g_array_new (FALSE, FALSE, sizeof (gint));
 	pixels = g_array_new (FALSE, FALSE, sizeof (gint));
+	folds = g_hash_table_new (g_direct_hash, g_direct_equal);
 
 	/* get the line numbers and y coordinates. */
 	gtk_source_view_get_lines (text_view,
@@ -1018,8 +1386,9 @@ gtk_source_view_paint_margin (GtkSourceV
 				   y2,
 				   pixels,
 				   numbers,
+				   folds,
 				   &count);
-
+	
 	/* A zero-lined document should display a "1"; we don't need to worry about
 	scrolling effects of the text widget in this special case */
 	
@@ -1043,7 +1412,7 @@ gtk_source_view_paint_margin (GtkSourceV
 		    "%d", MAX (99, gtk_text_buffer_get_line_count (text_view->buffer)));
 	layout = gtk_widget_create_pango_layout (GTK_WIDGET (view), str);
 
-	pango_layout_get_pixel_size (layout, &text_width, NULL);
+	pango_layout_get_pixel_size (layout, &text_width, &text_height);
 	
 	pango_layout_set_width (layout, text_width);
 	pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT);
@@ -1059,6 +1428,9 @@ gtk_source_view_paint_margin (GtkSourceV
 	if (view->priv->show_line_markers)
 		margin_width += GUTTER_PIXMAP;
 
+	if (view->priv->show_fold_regions)
+		margin_width += view->priv->expander_size;
+
 	g_return_if_fail (margin_width != 0);
 	
 	gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (text_view),
@@ -1097,7 +1469,6 @@ gtk_source_view_paint_margin (GtkSourceV
 		marker_line = gtk_source_marker_get_line (
 			GTK_SOURCE_MARKER (current_marker->data));
 	
-	
 	gtk_text_buffer_get_iter_at_mark (text_view->buffer, 
 					  &cur, 
 					  gtk_text_buffer_get_insert (text_view->buffer));
@@ -1147,6 +1518,67 @@ gtk_source_view_paint_margin (GtkSourceV
 					  pos,
 					  layout);
 		}
+		
+		if (view->priv->show_fold_regions && g_hash_table_size (folds) > 0)
+		{
+			gint line_to_paint = g_array_index (numbers, gint, i);
+			fold = g_hash_table_lookup (folds, GINT_TO_POINTER (line_to_paint));
+			
+			if (fold != NULL)
+			{
+				GtkStateType state = GTK_WIDGET_STATE (view);
+			
+				if (fold->prelighted && !fold->folded)
+				{
+					int x, y, win_y, y1, y2, height;
+					
+					gtk_text_buffer_get_iter_at_mark (text_view->buffer,
+									  &cur,
+									  fold->end_line);
+					
+					/* if the end of the fold is at the start of the
+					 * line, the fold actually ended on the previous line. */
+					if (gtk_text_iter_starts_line (&cur))
+						gtk_text_iter_backward_visible_line (&cur);
+				
+					gtk_text_view_get_line_yrange (text_view,
+								       &cur, &y,
+								       &height);
+					
+					gtk_text_view_buffer_to_window_coords (text_view,
+									       GTK_TEXT_WINDOW_TEXT,
+									       0,
+									       y,
+									       NULL,
+									       &win_y);
+
+					x = text_width + 3 + (view->priv->expander_size / 2);
+					y1 = pos + (text_height / 2);
+					y2 = win_y + (text_height / 2);
+
+					gdk_draw_line (win,
+						       widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+						       x, y1, x, y2);
+
+					gdk_draw_line (win,
+						       widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+						       x, y2, x + (view->priv->expander_size / 2) - 2, y2);
+				}
+			
+				if (fold->prelighted)
+					state = GTK_STATE_PRELIGHT;
+
+				gtk_paint_expander (GTK_WIDGET (view)->style,
+						    win,
+						    state,
+						    NULL,
+						    GTK_WIDGET (view),
+						    NULL,
+						    text_width + 4 + (view->priv->expander_size / 2),
+						    pos + (text_height / 2),
+						    fold->expander_style);
+			}
+		}
 
 		if (view->priv->show_line_markers && current_marker) 
 		{
@@ -1170,6 +1602,7 @@ gtk_source_view_paint_margin (GtkSourceV
 	g_slist_free (markers);
 	g_array_free (pixels, TRUE);
 	g_array_free (numbers, TRUE);
+	g_hash_table_destroy (folds);
 
 	g_object_unref (G_OBJECT (layout));
 }
@@ -1195,7 +1628,7 @@ gtk_source_view_expose (GtkWidget      *
 	}
 	
 	/* check if the expose event is for the text window first, and
-	 * make sure the visible region is highlighted */
+	 * make sure the visible region is prelighted */
 	if (event->window == gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT) &&
 	    view->priv->source_buffer != NULL) 
 	{
@@ -2174,6 +2607,12 @@ gtk_source_view_style_set (GtkWidget *wi
 		(* GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
 
 	view = GTK_SOURCE_VIEW (widget);
+	
+	gtk_widget_style_get (widget,
+			      "expander-size", &view->priv->expander_size,
+			      NULL);
+	//view->priv->expander_size += EXPANDER_EXTRA_PADDING;
+  
 	if (previous_style)
 	{
 		/* If previous_style is NULL this is the initial
@@ -2187,3 +2626,54 @@ gtk_source_view_style_set (GtkWidget *wi
 	}
 }
 
+static void
+expand_folds (GtkSourceBuffer *buffer, GSList *folds)
+{
+	GtkSourceFold *fold;
+	GSList *children;
+
+	while (folds != NULL) {
+		fold = folds->data;
+		children = fold->children;
+		
+		expand_folds (buffer, children);
+		
+		gtk_source_fold_set_folded (fold, FALSE);
+	
+		folds = g_slist_next (folds);
+	}
+}
+
+void
+gtk_source_view_set_show_fold_regions (GtkSourceView *view,
+				       gboolean       enable)
+{
+	const GSList *folds;
+
+	g_return_if_fail (GTK_IS_SOURCE_VIEW (view));
+
+	enable = (enable != FALSE);
+
+	if (view->priv->show_fold_regions == enable)
+		return;
+
+	view->priv->show_fold_regions = enable;
+
+	/* expand all folds when hiding the fold gutter. */
+	if (!enable) {
+		folds = gtk_source_buffer_get_root_folds (view->priv->source_buffer);
+		expand_folds (view->priv->source_buffer, (GSList *)folds);
+	}
+
+	gtk_widget_queue_draw (GTK_WIDGET (view));
+
+	g_object_notify (G_OBJECT (view), "show_fold_regions");
+}
+
+gboolean
+gtk_source_view_get_show_fold_regions (GtkSourceView *view)
+{
+	g_return_val_if_fail (GTK_IS_SOURCE_VIEW (view), FALSE);
+
+	return view->priv->show_fold_regions;
+}
Index: gtksourceview/gtksourceview.h
===================================================================
RCS file: /cvs/gnome/gtksourceview/gtksourceview/gtksourceview.h,v
retrieving revision 1.10
diff -u -p -r1.10 gtksourceview.h
--- gtksourceview/gtksourceview.h	8 Jan 2005 22:02:30 -0000	1.10
+++ gtksourceview/gtksourceview.h	30 Aug 2005 10:26:30 -0000
@@ -39,7 +39,6 @@ G_BEGIN_DECLS
 #define GTK_IS_SOURCE_VIEW_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SOURCE_VIEW))
 #define GTK_SOURCE_VIEW_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SOURCE_VIEW, GtkSourceViewClass))
 
-
 typedef struct _GtkSourceView GtkSourceView;
 typedef struct _GtkSourceViewClass GtkSourceViewClass;
 
@@ -69,7 +68,7 @@ GType		 gtk_source_view_get_type 		(void
 
 /* Constructors */
 GtkWidget 	*gtk_source_view_new 			(void);
-GtkWidget 	*gtk_source_view_new_with_buffer	(GtkSourceBuffer *buffer);
+GtkWidget 	*gtk_source_view_new_with_buffer		(GtkSourceBuffer *buffer);
 
 /* Properties */
 void 		 gtk_source_view_set_show_line_numbers 	(GtkSourceView   *view,
@@ -80,7 +79,7 @@ void 		 gtk_source_view_set_show_line_ma
 							 gboolean         show);
 gboolean	 gtk_source_view_get_show_line_markers  (GtkSourceView   *view);
 
-void 		 gtk_source_view_set_tabs_width 	(GtkSourceView   *view, 
+void 		 gtk_source_view_set_tabs_width		(GtkSourceView   *view, 
 							 guint            width);
 guint            gtk_source_view_get_tabs_width         (GtkSourceView   *view);
 
@@ -117,6 +116,10 @@ GdkPixbuf	*gtk_source_view_get_marker_pi
 void		 gtk_source_view_set_smart_home_end	(GtkSourceView   *view,
 							 gboolean         enable);
 gboolean	 gtk_source_view_get_smart_home_end	(GtkSourceView   *view);
+
+void		 gtk_source_view_set_show_fold_regions	(GtkSourceView   *view,
+							 gboolean         enable);
+gboolean	 gtk_source_view_get_show_fold_regions	(GtkSourceView   *view);
 
 G_END_DECLS
 #endif				/* end of SOURCE_VIEW_H__ */
Index: tests/Makefile.am
===================================================================
RCS file: /cvs/gnome/gtksourceview/tests/Makefile.am,v
retrieving revision 1.7
diff -u -p -r1.7 Makefile.am
--- tests/Makefile.am	21 Feb 2004 00:08:25 -0000	1.7
+++ tests/Makefile.am	30 Aug 2005 10:26:30 -0000
@@ -8,7 +8,13 @@ INCLUDES = 				\
 	$(TESTS_CFLAGS)
 
 # testing executables
-noinst_PROGRAMS = test-widget testregion
+noinst_PROGRAMS = test-fold test-widget testregion
+
+test_fold_SOURCES = test-fold.c
+
+test_fold_LDADD = \
+	$(top_builddir)/gtksourceview/libgtksourceview-1.0.la \
+	$(DEP_LIBS)
 
 test_widget_SOURCES = test-widget.c
 
Index: tests/test-fold.c
===================================================================
RCS file: tests/test-fold.c
diff -N tests/test-fold.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/test-fold.c	30 Aug 2005 10:26:30 -0000
@@ -0,0 +1,139 @@
+/*  test-fold.c
+ *
+ *  Copyright (C) 2005 - Jeroen Zwartepoorte <jeroen.zwartepoorte@gmail.com>
+ *
+ *  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <gtk/gtk.h>
+#include <gtksourceview/gtksourceview.h>
+
+static const char *text =
+"0\n"
+"0\n"
+"1\n"
+"2\n"
+"2\n"
+"2\n"
+"2\n"
+"1\n"
+"2\n"
+"3\n"
+"3\n"
+"2\n"
+"1\n"
+"0\n"
+"0";
+
+#define NFOLDS 4
+
+/*static int folds [NFOLDS][2] = {
+	{ 2, 12 },
+	{ 3, 6 },
+	{ 8, 11 },
+	{ 9, 10 },
+};*/
+
+static int folds [NFOLDS][2] = {
+	{ 9, 10 },
+	{ 8, 11 },
+	{ 3, 6 },
+	{ 2, 12 },
+};
+
+static gboolean
+window_deleted_cb (GtkWidget *widget, GdkEvent *event)
+{
+	gtk_main_quit ();
+	
+	return TRUE;
+}
+
+static GtkSourceBuffer *
+create_buffer (void)
+{
+	GtkSourceBuffer *buffer;
+	GtkTextIter begin, end;
+	int i;
+	
+	buffer = gtk_source_buffer_new (NULL);
+	
+	gtk_text_buffer_insert_at_cursor (GTK_TEXT_BUFFER (buffer),
+					  text, strlen (text));
+	
+	for (i = 0; i < NFOLDS; i++) {
+		gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer),
+						  &begin, folds[i][0]);
+		gtk_text_iter_forward_to_line_end (&begin);
+		gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer),
+						  &end, folds[i][1] + 1);
+		gtk_source_buffer_add_fold (buffer, &begin, &end);
+	}
+	
+	return buffer;
+}
+
+static GtkSourceView *
+create_view (GtkSourceBuffer *buffer)
+{
+	GtkSourceView *view;
+	
+	view = GTK_SOURCE_VIEW (gtk_source_view_new_with_buffer (buffer));
+	
+	gtk_source_view_set_show_line_numbers (view, TRUE);
+	gtk_source_view_set_show_fold_regions (view, TRUE);
+	gtk_source_view_set_highlight_current_line (view, TRUE);
+	gtk_source_view_set_show_margin (view, TRUE);
+	
+	return view;
+}
+
+int
+main (int argc, char *argv[])
+{
+	GtkWidget *window, *sw;
+	GtkSourceBuffer *buffer;
+	GtkSourceView *view;
+	
+	/* initialization */
+	gtk_init (&argc, &argv);
+	
+	/* create GUI */
+	window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+	gtk_container_set_border_width (GTK_CONTAINER (window), 0);
+	gtk_window_set_title (GTK_WINDOW (window), "GtkSourceView Demo");
+	
+	sw = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+					GTK_POLICY_AUTOMATIC,
+					GTK_POLICY_AUTOMATIC);
+	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
+                                             GTK_SHADOW_IN);
+	gtk_container_add (GTK_CONTAINER (window), sw);
+	buffer = create_buffer ();
+	view = create_view (buffer);
+	gtk_container_add (GTK_CONTAINER (sw), GTK_WIDGET (view));
+
+	g_signal_connect (window, "delete-event", (GCallback) window_deleted_cb, view);
+
+	gtk_window_set_default_size (GTK_WINDOW (window), 500, 500);
+	gtk_widget_show_all (window);
+
+	/* ... and action! */
+	gtk_main ();
+
+	return 0;
+}
Index: tests/test-widget.c
===================================================================
RCS file: /cvs/gnome/gtksourceview/tests/test-widget.c,v
retrieving revision 1.11
diff -u -p -r1.11 test-widget.c
--- tests/test-widget.c	8 Jan 2005 22:02:30 -0000	1.11
+++ tests/test-widget.c	30 Aug 2005 10:26:30 -0000
@@ -413,7 +413,7 @@ static void
 markers_toggled_cb (GtkAction *action, gpointer user_data)
 {
 	g_return_if_fail (GTK_IS_TOGGLE_ACTION (action) && GTK_IS_SOURCE_VIEW (user_data));
-	gtk_source_view_set_show_line_markers (
+	gtk_source_view_set_show_fold_regions (
 		GTK_SOURCE_VIEW (user_data),
 		gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
 }
@@ -767,6 +767,7 @@ create_view_window (GtkSourceBuffer *buf
 
 	/* view */
 	view = gtk_source_view_new_with_buffer (buffer);
+	g_object_set_data (G_OBJECT (window), "view", view);
 	
 	g_signal_connect (buffer, "mark_set", G_CALLBACK (move_cursor_cb), view);
 	g_signal_connect (buffer, "changed", G_CALLBACK (update_cursor_position), view);
@@ -805,6 +806,9 @@ create_view_window (GtkSourceBuffer *buf
 	/* misc widgets */
 	vbox = gtk_vbox_new (0, FALSE);
 	sw = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+					GTK_POLICY_AUTOMATIC,
+					GTK_POLICY_AUTOMATIC);
 	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
                                              GTK_SHADOW_IN);
 	pos_label = gtk_label_new ("Position");
@@ -819,7 +823,7 @@ create_view_window (GtkSourceBuffer *buf
 	gtk_box_pack_start (GTK_BOX (vbox), pos_label, FALSE, FALSE, 0);
 
 	/* setup view */
-	font_desc = pango_font_description_from_string ("monospace 10");
+	font_desc = pango_font_description_from_string ("Bitstream Vera Sans Mono 8");
 	if (font_desc != NULL)
 	{
 		gtk_widget_modify_font (view, font_desc);
@@ -977,6 +981,8 @@ main (int argc, char *argv[])
 	GtkWidget *window;
 	GtkSourceLanguagesManager *lm;
 	GtkSourceBuffer *buffer;
+	GtkSourceView *view;
+	GtkTextIter begin, end;
 	
 	/* initialization */
 	gtk_init (&argc, &argv);
@@ -987,13 +993,46 @@ main (int argc, char *argv[])
 	buffer = create_source_buffer (lm);
 	g_object_unref (lm);
 
-	if (argc > 1)
-		open_file (buffer, argv [1]);
-	else
-		open_file (buffer, "../gtksourceview/gtksourcebuffer.c");
-
 	/* create first window */
 	window = create_main_window (buffer);
+	view = GTK_SOURCE_VIEW (g_object_get_data (G_OBJECT (window), "view"));
+
+	if (argc > 1) {
+		open_file (buffer, argv [1]);
+	} else {
+		open_file (buffer, "test.c");
+
+		/* main() fold. */		
+		gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer), &begin, 33);
+		gtk_text_iter_forward_to_line_end (&begin);
+		gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer), &end, 67);
+		gtk_source_buffer_add_fold (buffer, &begin, &end);
+
+		/* doit() fold. */		
+		gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer), &begin, 18);
+		gtk_text_iter_forward_to_line_end (&begin);
+		gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer), &end, 22);
+		gtk_source_buffer_add_fold (buffer, &begin, &end);
+
+		/* nested while (...) fold. */
+		gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer), &begin, 59);
+		gtk_text_iter_forward_to_line_end (&begin);
+		gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer), &end, 62);
+		gtk_source_buffer_add_fold (buffer, &begin, &end);
+		
+		/* nested if (...) fold. */
+		gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer), &begin, 51);
+		gtk_text_iter_forward_to_line_end (&begin);
+		gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer), &end, 54);
+		gtk_source_buffer_add_fold (buffer, &begin, &end);
+		
+		/* nested if (DEBUG) fold. This should cause a reparent of the previous fold. */
+		gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer), &begin, 50);
+		gtk_text_iter_forward_to_line_end (&begin);
+		gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer), &end, 55);
+		gtk_source_buffer_add_fold (buffer, &begin, &end);
+	}
+
 	gtk_window_set_default_size (GTK_WINDOW (window), 500, 500);
 	gtk_widget_show (window);
 
Index: tests/test.c
===================================================================
RCS file: tests/test.c
diff -N tests/test.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/test.c	30 Aug 2005 10:26:30 -0000
@@ -0,0 +1,67 @@
+/*
+ * This is a test of gtksourceview's capabilities. Notice the new highlighting
+ * engine that supports sub-languages like GTK+-like @ comments.
+ */
+
+#include <gtk/gtk.h>
+
+#if 0
+
+#if GTK_SOURCE_VIEW_IS_SMART_ABOUT_NESTED_IFDEFS
+#define HOORAY
+#else
+#define GET_A_REAL_EDITOR
+#endif
+
+#endif /* #if 0 */
+
+static void
+doit (void)
+{
+	g_message ("Just do it!");
+}
+
+/**
+ * main:
+ * @argc: the number of command-line arguments given to the program + 1.
+ * @argv: the arguments specified on the command-line.
+ *
+ * Displays a basic #GtkWindow.
+ *
+ * Return value: 0 if the program completed succesfully.
+ **/
+int
+main (int argc, char *argv[])
+{
+	GtkWidget *window;
+	gchar *title;
+	
+	gtk_init (&argc, &argv);
+	
+	title = "GtkSourceView test";
+	
+	/* There should be a breakpoint on the next line. */
+	window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+	
+	/* Try hovering over "title" to see it's value (tooltip). */
+	gtk_window_set_title (GTK_WINDOW (window), title);
+	
+	gtk_window_set_default_size (GTK_WINDOW (window), 500, 500);
+	
+	if (DEBUG) {
+		if (argc > 1) {
+			g_message ("%d arguments where given at startup", argc - 1);
+		}
+	}
+	
+	/* The next line is bookmarked. */
+	gtk_widget_show_all (window);
+
+	while (gtk_events_pending ()) {
+		gtk_main_iteration ();
+	}
+
+	gtk_main ();
+	
+	return 0;
+}
