gtksourceview r1997 - in branches/code-folding-2: . gtksourceview tests



Author: muntyan
Date: Sun Aug  3 23:03:52 2008
New Revision: 1997
URL: http://svn.gnome.org/viewvc/gtksourceview?rev=1997&view=rev

Log:
2008-08-02  Yevgen Muntyan  <muntyan tamu edu>

	Started new code folding branch. This is Jeroen's folding+newhl branch,
	adapted for current code.

	* gtksourceview/gtksourcefoldlabel.c:
	* gtksourceview/gtksourcefoldlabel.h:
	* gtksourceview/gtksourcefold-private.h:
	* gtksourceview/gtksourcefold.c:
	* gtksourceview/gtksourcefold.h:
	* tests/test-fold.c
	New files.

	* gtksourceview/gtksourcebuffer.c
	* gtksourceview/gtksourceview.c
	* gtksourceview/gtksourcebuffer.h
	* gtksourceview/gtksourcebuffer-private.h
	* gtksourceview/Makefile.am
	* tests/test-widget.c
	Modified.



Added:
   branches/code-folding-2/gtksourceview/gtksourcebuffer-private.h
   branches/code-folding-2/gtksourceview/gtksourcefold-private.h
   branches/code-folding-2/gtksourceview/gtksourcefold.c
   branches/code-folding-2/gtksourceview/gtksourcefold.h
   branches/code-folding-2/gtksourceview/gtksourcefoldlabel.c
   branches/code-folding-2/gtksourceview/gtksourcefoldlabel.h
   branches/code-folding-2/tests/test-fold.c
Modified:
   branches/code-folding-2/ChangeLog
   branches/code-folding-2/gtksourceview/Makefile.am
   branches/code-folding-2/gtksourceview/gtksourcebuffer.c
   branches/code-folding-2/gtksourceview/gtksourcebuffer.h
   branches/code-folding-2/gtksourceview/gtksourceview.c
   branches/code-folding-2/tests/test-widget.c

Modified: branches/code-folding-2/gtksourceview/Makefile.am
==============================================================================
--- branches/code-folding-2/gtksourceview/Makefile.am	(original)
+++ branches/code-folding-2/gtksourceview/Makefile.am	Sun Aug  3 23:03:52 2008
@@ -19,6 +19,7 @@
 
 libgtksourceview_headers =			\
 	gtksourcebuffer.h			\
+	gtksourcefold.h				\
 	gtksourceiter.h				\
 	gtksourceview.h				\
 	gtksourcelanguage.h			\
@@ -31,6 +32,11 @@
 
 libgtksourceview_2_0_la_SOURCES = 	\
 	gtksourcebuffer.c 		\
+	gtksourcebuffer-private.h 	\
+	gtksourcefold.c			\
+	gtksourcefold-private.h		\
+	gtksourcefoldlabel.c		\
+	gtksourcefoldlabel.h		\
 	gtksourceiter.c			\
 	gtksourceview.c 		\
 	gtksourceundomanager.h 		\

Added: branches/code-folding-2/gtksourceview/gtksourcebuffer-private.h
==============================================================================
--- (empty file)
+++ branches/code-folding-2/gtksourceview/gtksourcebuffer-private.h	Sun Aug  3 23:03:52 2008
@@ -0,0 +1,47 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*-
+ *  gtksourcebuffer-private.h
+ *
+ *  Copyright (C) 2008 - Paolo Maggi, Paolo Borelli
+ *
+ *  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_BUFFER_PRIVATE_H__
+#define __GTK_SOURCE_BUFFER_PRIVATE_H__
+
+#include <gtksourceview/gtksourcebuffer.h>
+
+G_BEGIN_DECLS
+
+void			 _gtk_source_buffer_update_highlight	(GtkSourceBuffer        *buffer,
+								 const GtkTextIter      *start,
+								 const GtkTextIter      *end,
+								 gboolean                synchronous);
+
+GtkSourceMark		*_gtk_source_buffer_source_mark_next	(GtkSourceBuffer        *buffer,
+								 GtkSourceMark          *mark,
+								 const gchar            *category);
+GtkSourceMark		*_gtk_source_buffer_source_mark_prev	(GtkSourceBuffer        *buffer,
+								 GtkSourceMark          *mark,
+								 const gchar            *category);
+GList			*_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
+
+#endif /* __GTK_SOURCE_BUFFER_PRIVATE_H__ */

Modified: branches/code-folding-2/gtksourceview/gtksourcebuffer.c
==============================================================================
--- branches/code-folding-2/gtksourceview/gtksourcebuffer.c	(original)
+++ branches/code-folding-2/gtksourceview/gtksourcebuffer.c	Sun Aug  3 23:03:52 2008
@@ -34,6 +34,7 @@
 #include "gtksourceview-i18n.h"
 #include "gtksourcelanguage-private.h"
 #include "gtksourcebuffer.h"
+#include "gtksourcefold-private.h"
 #include "gtksourceundomanager.h"
 #include "gtksourceview-marshal.h"
 #include "gtksourceiter.h"
@@ -65,6 +66,8 @@
 enum {
 	HIGHLIGHT_UPDATED,
 	SOURCE_MARK_UPDATED,
+	FOLD_ADDED,
+	FOLD_REMOVE,
 	LAST_SIGNAL
 };
 
@@ -77,17 +80,21 @@
 	PROP_HIGHLIGHT_MATCHING_BRACKETS,
 	PROP_MAX_UNDO_LEVELS,
 	PROP_LANGUAGE,
-	PROP_STYLE_SCHEME
+	PROP_STYLE_SCHEME,
+	PROP_FOLDS
 };
 
 struct _GtkSourceBufferPrivate
 {
+	GList                 *folds;
+	gint                   enable_folds:1;
+
 	gint                   highlight_syntax:1;
 	gint                   highlight_brackets:1;
 
+	guint                  bracket_found:1;
 	GtkTextTag            *bracket_match_tag;
 	GtkTextMark           *bracket_mark;
-	guint                  bracket_found:1;
 
 	GArray                *source_marks;
 
@@ -103,6 +110,9 @@
 
 static guint 	 buffer_signals[LAST_SIGNAL];
 
+static GObject  *gtk_source_buffer_constructor          (GType                    type,
+							 guint                    n_construct_properties,
+							 GObjectConstructParam   *construct_param);
 static void 	 gtk_source_buffer_finalize		(GObject                 *object);
 static void 	 gtk_source_buffer_dispose		(GObject                 *object);
 static void      gtk_source_buffer_set_property         (GObject                 *object,
@@ -134,6 +144,9 @@
 static gboolean	 gtk_source_buffer_find_bracket_match_with_limit (GtkTextIter    *orig,
 								  gint            max_chars);
 
+static void	 gtk_source_buffer_real_remove_fold	(GtkSourceBuffer         *buffer,
+							 GtkSourceFold           *fold);
+
 static void
 gtk_source_buffer_class_init (GtkSourceBufferClass *klass)
 {
@@ -144,6 +157,7 @@
 	object_class 	= G_OBJECT_CLASS (klass);
 	tb_class	= GTK_TEXT_BUFFER_CLASS (klass);
 
+	object_class->constructor  = gtk_source_buffer_constructor;
 	object_class->finalize	   = gtk_source_buffer_finalize;
 	object_class->dispose	   = gtk_source_buffer_dispose;
 	object_class->get_property = gtk_source_buffer_get_property;
@@ -157,6 +171,9 @@
 	tb_class->mark_set	= gtk_source_buffer_real_mark_set;
 	tb_class->mark_deleted	= gtk_source_buffer_real_mark_deleted;
 
+	klass->fold_added       = NULL;
+	klass->fold_remove	= gtk_source_buffer_real_remove_fold;
+
 	/**
 	 * GtkSourceBuffer:highlight-syntax:
 	 *
@@ -242,6 +259,14 @@
 							      GTK_TYPE_SOURCE_STYLE_SCHEME,
 							      G_PARAM_READWRITE));
 
+	g_object_class_install_property (object_class,
+					 PROP_FOLDS,
+					 g_param_spec_boolean ("folds",
+							       _("Folds"),
+							       _("Whether folds are enabled"),
+							       FALSE,
+							       G_PARAM_READWRITE));
+
 	param_types[0] = GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE;
 	param_types[1] = GTK_TYPE_TEXT_ITER | G_SIGNAL_TYPE_STATIC_SCOPE;
 
@@ -271,6 +296,28 @@
 			   G_TYPE_NONE,
 			   1, GTK_TYPE_TEXT_MARK);
 
+	buffer_signals[FOLD_ADDED] =
+	    g_signal_new ("fold_added",
+			  G_OBJECT_CLASS_TYPE (object_class),
+			  G_SIGNAL_RUN_LAST,
+			  G_STRUCT_OFFSET (GtkSourceBufferClass, fold_added),
+			  NULL, NULL,
+			  _gtksourceview_marshal_VOID__BOXED,
+			  G_TYPE_NONE,
+			  1,
+			  GTK_TYPE_SOURCE_FOLD | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+	buffer_signals[FOLD_REMOVE] =
+	    g_signal_new ("fold_remove",
+			  G_OBJECT_CLASS_TYPE (object_class),
+			  G_SIGNAL_RUN_LAST,
+			  G_STRUCT_OFFSET (GtkSourceBufferClass, fold_remove),
+			  NULL, NULL,
+			  _gtksourceview_marshal_VOID__BOXED,
+			  G_TYPE_NONE,
+			  1,
+			  GTK_TYPE_SOURCE_FOLD | G_SIGNAL_TYPE_STATIC_SCOPE);
+
 	g_type_class_add_private (object_class, sizeof(GtkSourceBufferPrivate));
 }
 
@@ -290,6 +337,8 @@
 	priv->highlight_brackets = TRUE;
 	priv->bracket_mark = NULL;
 	priv->bracket_found = FALSE;
+	priv->enable_folds = FALSE;
+	priv->folds = NULL;
 
 	priv->source_marks = g_array_new (FALSE, FALSE, sizeof (GtkSourceMark *));
 
@@ -307,6 +356,25 @@
 			  buffer);
 }
 
+static GObject *
+gtk_source_buffer_constructor (GType                  type,
+			       guint                  n_construct_properties,
+			       GObjectConstructParam *construct_param)
+{
+	GObject *object;
+
+	object = G_OBJECT_CLASS (gtk_source_buffer_parent_class)->
+					constructor (type,
+						     n_construct_properties,
+						     construct_param);
+
+	/* Create invisibility tag for folding lines. */
+	gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (object),
+				    INVISIBLE_LINE, "invisible", TRUE, NULL);
+
+	return object;
+}
+
 static void
 gtk_source_buffer_finalize (GObject *object)
 {
@@ -402,6 +470,10 @@
 							    g_value_get_object (value));
 			break;
 
+		case PROP_FOLDS:
+			gtk_source_buffer_set_folds_enabled (source_buffer,
+							     g_value_get_boolean (value));
+
 		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 			break;
@@ -453,6 +525,9 @@
 			g_value_set_boolean (value, gtk_source_buffer_can_redo (source_buffer));
 			break;
 
+		case PROP_FOLDS:
+			g_value_set_boolean (value, source_buffer->priv->enable_folds);
+
 		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 			break;
@@ -618,6 +693,7 @@
 	GtkTextIter insert_iter;
 	gint start_offset, end_offset;
 	GtkSourceBuffer *source_buffer = GTK_SOURCE_BUFFER (buffer);
+	GtkSourceFold *fold;
 
 	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
 	g_return_if_fail (iter != NULL);
@@ -626,6 +702,18 @@
 
 	start_offset = gtk_text_iter_get_offset (iter);
 
+	/* XXX this is very wrong, this method isn't only for user interaction */
+	/* if the user tries to insert text into a folded section, don't insert
+	 * the text, but unfold the region.
+	 */
+	fold = gtk_source_buffer_get_fold_at_iter (GTK_SOURCE_BUFFER (buffer),
+						   iter);
+	if (fold != NULL && fold->folded)
+	{
+		gtk_source_fold_set_folded (fold, FALSE);
+		return;
+	}
+
 	/*
 	 * iter is invalidated when
 	 * insertion occurs (because the buffer contents change), but the
@@ -662,6 +750,48 @@
 	g_return_if_fail (gtk_text_iter_get_buffer (start) == buffer);
 	g_return_if_fail (gtk_text_iter_get_buffer (end) == buffer);
 
+	/* XXX same thing, this is wrong */
+	/* if the delete range intersects a folded region, don't delete any text;
+	 * unfold the region instead. */
+	if (source_buffer->priv->enable_folds)
+	{
+		GtkSourceFold *fold;
+
+		/* do we start in a folded region? */
+		fold = gtk_source_buffer_get_fold_at_iter (source_buffer, start);
+
+		if (fold != NULL && fold->folded)
+		{
+			GtkTextIter fold_begin;
+
+			/* If the start of delete range is the same as the start
+			 * of the fold, allow the delete to proceed. */
+			gtk_source_fold_get_bounds (fold, &fold_begin, NULL);
+			if (!gtk_text_iter_equal (start, &fold_begin))
+			{
+				gtk_source_fold_set_folded (fold, FALSE);
+				return;
+			}
+		}
+
+		/* do we end in a folded region? */
+		fold = gtk_source_buffer_get_fold_at_iter (source_buffer, end);
+
+		if (fold != NULL && fold->folded)
+		{
+			GtkTextIter fold_end;
+
+			/* If the end of delete range is the same as the end
+			 * of the fold, allow the delete to proceed. */
+			gtk_source_fold_get_bounds (fold, NULL, &fold_end);
+			if (!gtk_text_iter_equal (end, &fold_end))
+			{
+				gtk_source_fold_set_folded (fold, FALSE);
+				return;
+			}
+		}
+	}
+
 	gtk_text_iter_order (start, end);
 	offset = gtk_text_iter_get_offset (start);
 	length = gtk_text_iter_get_offset (end) - offset;
@@ -1736,7 +1866,7 @@
 	GtkTextIter iter;
 	GSList *res;
 
- 	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), NULL);
+	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), NULL);
 
 	gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer),
 					  &iter, line);
@@ -1790,9 +1920,9 @@
 	GSList *list;
 	GSList *l;
 
- 	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
- 	g_return_if_fail (start != NULL);
- 	g_return_if_fail (end != NULL);
+	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
+	g_return_if_fail (start != NULL);
+	g_return_if_fail (end != NULL);
 
 	iter = *start;
 
@@ -1827,3 +1957,711 @@
 	g_slist_free (list);
 }
 
+/**************************************************************************/
+/* Code folding
+ */
+
+gboolean
+gtk_source_buffer_get_folds_enabled (GtkSourceBuffer *buffer)
+{
+	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), FALSE);
+	return buffer->priv->enable_folds;
+}
+
+static void
+foreach_fold_region (gpointer data, gpointer user_data)
+{
+	gtk_source_buffer_real_remove_fold (GTK_SOURCE_BUFFER (user_data), data);
+}
+
+void
+gtk_source_buffer_set_folds_enabled (GtkSourceBuffer *buffer,
+				     gboolean         enable_folds)
+{
+	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
+
+	enable_folds = (enable_folds != FALSE);
+
+	if (buffer->priv->enable_folds == enable_folds)
+		return;
+
+	buffer->priv->enable_folds = enable_folds;
+
+	/* Remove all existing folds if folds are disabled. */
+	if (!enable_folds && buffer->priv->folds != NULL)
+	{
+		GList *folds = g_list_copy (buffer->priv->folds);
+		g_list_foreach (folds, foreach_fold_region, buffer);
+		g_list_free (folds);
+	}
+
+	g_object_notify (G_OBJECT (buffer), "folds");
+}
+
+static GQuark
+gtk_source_buffer_error_quark (void)
+{
+	static GQuark q = 0;
+
+	if (q == 0)
+		q = g_quark_from_static_string ("gtk-source-buffer-error-quark");
+
+	return q;
+}
+
+/**
+ * insert_child_fold:
+ * @buffer: a #GtkSourceBuffer.
+ * @child: the new #GtkSourceFold to insert.
+ * @parent: the #GtkSourceFold parent to which we are trying to insert the child.
+ * @error: a possible #GError returned through the recursive loop.
+ *
+ * Inserts the specified child #GtkSourceFold into the fold tree somewhere or
+ * returns FALSE. In the latter case, a #GError is also set to indicate an
+ * illegal operation.
+ *
+ * This method is called from gtk_source_buffer_add_fold initially with a
+ * top-level #GtkSourceFold. After that, the method calls itself recursively
+ * until it has either found a #GtkSourceFold where it can insert the child or
+ * detected an illegal operation and set @error.
+ *
+ * Return value: TRUE if the insertion was succesful, FALSE if not. @error is
+ * set when FALSE is returned.
+ **/
+static gboolean
+insert_child_fold (GtkSourceBuffer *buffer,
+		   GtkSourceFold   *child,
+		   GtkSourceFold   *parent,
+		   GError         **error)
+{
+	GtkTextIter begin, end, pbegin, pend, iter;
+
+	/* If error is set, then return immediately; don't recurse further. */
+	if (*error)
+		return FALSE;
+
+	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);
+
+	DEBUG (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)));
+
+	/* There are 3 major codepaths in this method:
+	 * 1. The child fold falls completely inside the parent fold. Try to add
+	 *    the child fold to children of the parent fold recursively. Otherwise
+	 *    append the child fold to the parent.
+	 * 2. The child fold overlapses the parent fold. This means that the
+	 *    start of the child fold is before the start of the parent and the
+	 *    end of the child is after the end of the parent. In this case,
+	 *    parent & child need to be reparented (parent becomes child and
+	 *    child becomes the parent. There's additional logic to check if any
+	 *    siblings of the parent need to be reparented.
+	 * 3. The child fold intersects with the parent fold. This is an illegal
+	 *    operation and will results in FALSE being returned through the
+	 *    recursion and the error parameter to be set.
+	 */
+
+	/* 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)
+	{
+		GList *folds = parent->children;
+		GList *last_fold = folds;
+
+		/* fold already has children; try inserting it into one. */
+		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)
+			{
+				DEBUG (g_message ("adding fold before child @ %d",
+						  gtk_text_iter_get_line (&iter)));
+				parent->children = g_list_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, error))
+				return TRUE;
+
+			if (*error)
+				return FALSE;
+
+			last_fold = folds;
+			folds = g_list_next (folds);
+		}
+
+		/* Fold is inside parent, but not inside a child of parent.
+		 * Append the child fold to the parent. If the child was added
+		 * succesfully already, then we never get to this point (we
+		 * return in the while loop above). */
+		DEBUG (g_message ("adding fold to parent fold @ %d",
+				  gtk_text_iter_get_line (&pbegin)));
+		if (last_fold == NULL)
+			parent->children = g_list_append (NULL, child);
+		else
+			parent->children = g_list_append (last_fold, 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;
+		GList *siblings, *reparent, *l, *first, *last;
+
+		/* If the parent is a root fold, the "siblings" are actually
+		 * the other root folds. Else just get the fold children. */
+		if (parent->parent != NULL)
+			siblings = g_list_find (parent->parent->children, parent);
+		else
+			siblings = g_list_find (buffer->priv->folds, parent);
+
+		reparent = g_list_append (NULL, parent);
+
+		DEBUG (g_message ("child overlaps parent; need to reparent..."));
+
+		/* We need to determine which siblings the child overlapses.
+		 * 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)
+		 * |
+		 */
+
+		/* The parent fold is the first in the list. Since we've already
+		 * added it to the reparent list, we skip it here. */
+		siblings = g_list_next (siblings);
+
+		/* First determine which children to reparent. */
+		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_set_error (error,
+						     gtk_source_buffer_error_quark (),
+						     0,
+						     "Cannot add child fold: new fold [%d-%d] intersects with [%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));
+					g_list_free (reparent);
+					return FALSE;
+				}
+
+				break;
+			}
+
+			DEBUG (g_message ("reparenting @ %d", gtk_text_iter_get_line (&pend)));
+
+			reparent = g_list_prepend (reparent, sibling);
+			siblings = g_list_next (siblings);
+		}
+
+		/* reparent first sibling to child. */
+		reparent = g_list_reverse (reparent);
+		sibling = reparent->data;
+
+		/* if sibling->parent is NULL, then it's a root fold. */
+		if (sibling->parent == NULL)
+		{
+			siblings = g_list_find (buffer->priv->folds, sibling);
+			g_return_val_if_fail (siblings != NULL, FALSE);
+			siblings->data = child;
+			child->children = g_list_append (child->children, sibling);
+			child->parent = NULL;
+			sibling->parent = child;
+		}
+		else
+		{
+			siblings = g_list_find (sibling->parent->children, sibling);
+			g_return_val_if_fail (siblings != NULL, FALSE);
+			siblings->data = child;
+			child->children = g_list_append (child->children, sibling);
+			child->parent = sibling->parent;
+			sibling->parent = child;
+		}
+
+		/* reparent all the following siblings as well. */
+		l = g_list_next (reparent);
+		first = last = NULL;
+		while (l != NULL)
+		{
+			sibling = l->data;
+
+			if (first == NULL)
+			{
+				if (sibling->parent != NULL)
+					first = g_list_find (sibling->parent->children,
+							     sibling);
+				else
+					first = g_list_find (buffer->priv->folds,
+							     sibling);
+
+				g_return_val_if_fail (first != NULL, FALSE);
+
+				last = first;
+			}
+
+			sibling->parent = child;
+
+			last = g_list_next (last);
+			l = g_list_next (l);
+		}
+
+		/* Check if there are any siblings left to reparent. */
+		if (first != NULL)
+		{
+			if (first->prev)
+				first->prev->next = last;
+
+			if (last)
+			{
+				last->prev->next = NULL;
+				last->prev = first->prev;
+			}
+
+			first->prev = NULL;
+
+			child->children = g_list_concat (child->children, first);
+		}
+
+		g_list_free (reparent);
+
+		return TRUE;
+	}
+	else if (gtk_text_iter_in_range (&pbegin, &begin, &end) ||
+		 gtk_text_iter_in_range (&pend, &begin, &end) ||
+		 gtk_text_iter_equal (&pbegin, &begin) ||
+		 gtk_text_iter_equal (&pend, &end))
+	{
+		g_set_error (error,
+			     gtk_source_buffer_error_quark (),
+			     0,
+			     "Cannot add child fold: new fold [%d-%d] intersects with [%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));
+	}
+
+	return FALSE;
+}
+
+GtkSourceFold *
+gtk_source_buffer_add_fold (GtkSourceBuffer   *buffer,
+			    const GtkTextIter *begin,
+			    const GtkTextIter *end)
+{
+	GList *folds, *last_fold;
+	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);
+
+	DEBUG (g_message ("add fold @ %d, %d",
+			  gtk_text_iter_get_line (begin),
+			  gtk_text_iter_get_line (end)));
+
+	fold = _gtk_source_fold_new (buffer, begin, end);
+
+	/* Insert the fold either at the root level or as a child of an existing fold. */
+	folds = buffer->priv->folds;
+	last_fold = folds;
+	while (folds != NULL)
+	{
+		GError *error = 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)
+		{
+			DEBUG (g_message ("adding fold before root %d", gtk_text_iter_get_line (&iter)));
+			buffer->priv->folds = g_list_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, &error))
+			break;
+
+		if (error != NULL)
+		{
+			g_critical (error->message);
+			g_error_free (error);
+			gtk_source_fold_free (fold);
+			return NULL;
+		}
+
+		last_fold = folds;
+		folds = g_list_next (folds);
+	}
+
+	/* add the fold at the end of the list. */
+	if (folds == NULL)
+	{
+		GList *dummy;
+
+		DEBUG (g_message ("adding fold at end of root"));
+		if (last_fold == NULL)
+			buffer->priv->folds = g_list_append (NULL, fold);
+		else
+			dummy = g_list_append (last_fold, fold);
+	}
+
+	g_signal_emit (G_OBJECT (buffer), buffer_signals [FOLD_ADDED], 0, fold);
+
+	return fold;
+}
+
+static void
+gtk_source_buffer_real_remove_fold (GtkSourceBuffer *buffer,
+				    GtkSourceFold   *fold)
+{
+	GList *l;
+
+	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
+	g_return_if_fail (fold != NULL);
+
+	if (fold->folded)
+		gtk_source_fold_set_folded (fold, FALSE);
+
+	gtk_text_buffer_delete_mark (GTK_TEXT_BUFFER (buffer), fold->start_line);
+	gtk_text_buffer_delete_mark (GTK_TEXT_BUFFER (buffer), fold->end_line);
+
+	l = g_list_find (buffer->priv->folds, fold);
+	if (l != NULL)
+		buffer->priv->folds = g_list_delete_link (buffer->priv->folds, l);
+
+	g_list_foreach (fold->children, foreach_fold_region, buffer);
+
+	gtk_source_fold_free (fold);
+}
+
+void
+gtk_source_buffer_remove_fold (GtkSourceBuffer *buffer,
+			       GtkSourceFold   *fold)
+{
+	g_signal_emit (G_OBJECT (buffer), buffer_signals [FOLD_REMOVE], 0, fold);
+}
+
+/**
+ * get_folds_in_region:
+ * @buffer: a #GtkSourceBuffer.
+ * @begin: the begin point of the region.
+ * @end: the end point of the region.
+ * @fold: #GtkSourceFold which might or might not lie in the region.
+ * @list: the list of #GtkSourceFold's in the region.
+ *
+ * This method is called from gtk_source_buffer_get_folds_in_region to create a
+ * list of #GtkSourceFold's of which the *start* lies in the specified region.
+ * This method recurses through the fold hierarchy to create the flattened list.
+ * See gtk_source_buffer_get_folds_in_region for more information.
+ **/
+static void
+get_folds_in_region (GtkTextBuffer     *buffer,
+		     const GtkTextIter *begin,
+		     const GtkTextIter *end,
+		     GtkSourceFold     *fold,
+		     GList            **list)
+{
+	GtkTextIter fbegin, fend;
+	GList *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 possible children in the region. */
+	if (gtk_text_iter_compare (&fbegin, begin) == -1 &&
+	    gtk_text_iter_compare (&fend, begin) == 1)
+	{
+		children = fold->children;
+		while (!fold->folded && children != NULL)
+		{
+			get_folds_in_region (buffer, begin, end, children->data, list);
+			children = g_list_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) ||
+	/* start iter is in the region, so add the fold first. */
+	         (gtk_text_iter_compare (&fbegin, begin) >= 0 &&
+	          gtk_text_iter_compare (&fbegin, end) <= 0))
+	{
+		*list = g_list_append (*list, fold);
+		children = fold->children;
+		while (!fold->folded && children != NULL)
+		{
+			get_folds_in_region (buffer, begin, end, children->data, list);
+			children = g_list_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.
+ *
+ * Returns 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.
+ *
+ * This method returns the folds of which the start lies in the region. So if
+ * a fold begins before the region, the fold itself isn't returned, but its
+ * children might.
+ *
+ * Return value: a #GList of the #GtkSourceFold's in the region, or %NULL if
+ * there are no folds in the region.
+ **/
+GList *
+_gtk_source_buffer_get_folds_in_region (GtkSourceBuffer   *buffer,
+					const GtkTextIter *begin,
+					const GtkTextIter *end)
+{
+	GList *result, *folds;
+
+	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)
+	{
+		GtkSourceFold *fold = folds->data;
+		GtkTextIter iter;
+
+		/* break when we are past the end of the region. */
+		gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
+						  &iter, fold->start_line);
+		if (gtk_text_iter_compare (&iter, end) == 1)
+			break;
+
+		get_folds_in_region (GTK_TEXT_BUFFER (buffer), begin, end, fold, &result);
+
+		folds = g_list_next (folds);
+	}
+
+	return result;
+}
+
+static void
+remove_folds_in_region (GtkTextBuffer     *buffer,
+			const GtkTextIter *begin,
+			const GtkTextIter *end,
+			GtkSourceFold     *fold)
+{
+	GtkTextIter fbegin, fend;
+	GList *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 remove possible children in the region. */
+	if (gtk_text_iter_compare (&fbegin, begin) == -1 &&
+	    gtk_text_iter_compare (&fend, begin) == 1)
+	{
+		children = fold->children;
+		while (!fold->folded && children != NULL)
+		{
+			remove_folds_in_region (buffer, begin, end, children->data);
+			children = g_list_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)
+	{
+		gtk_source_buffer_remove_fold (GTK_SOURCE_BUFFER (buffer), fold);
+	}
+}
+
+void
+gtk_source_buffer_remove_folds_in_region (GtkSourceBuffer   *buffer,
+					  const GtkTextIter *begin,
+					  const GtkTextIter *end)
+{
+	GList *folds;
+
+	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
+	g_return_if_fail (begin != NULL && end != NULL);
+
+	folds = buffer->priv->folds;
+	while (folds != NULL)
+	{
+		GtkSourceFold *fold = folds->data;
+		GtkTextIter iter;
+
+		/* break when we are past the end of the region. */
+		gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer),
+						  &iter, fold->start_line);
+		if (gtk_text_iter_compare (&iter, end) == 1)
+			break;
+
+		remove_folds_in_region (GTK_TEXT_BUFFER (buffer), begin, end, fold);
+
+		folds = g_list_next (folds);
+	}
+}
+
+static GtkSourceFold *
+find_fold_at_line (GtkTextBuffer *buffer,
+		   GList         *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);
+
+		/* The end iter of the fold is on the next line, so if the end
+		 * iter is at the start of the line, go back a line. */
+		if (gtk_text_iter_starts_line (&iter))
+			gtk_text_iter_backward_line (&iter);
+		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;
+		}
+		else if (line < start_line)
+		{
+			return NULL;
+		}
+
+		folds = g_list_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,
+		   GList             *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;
+		}
+		else if (gtk_text_iter_compare (iter, &start_iter) == -1)
+		{
+			return NULL;
+		}
+
+		folds = g_list_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 GList *
+gtk_source_buffer_get_root_folds (GtkSourceBuffer *buffer)
+{
+	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), NULL);
+
+	return buffer->priv->folds;
+}

Modified: branches/code-folding-2/gtksourceview/gtksourcebuffer.h
==============================================================================
--- branches/code-folding-2/gtksourceview/gtksourcebuffer.h	(original)
+++ branches/code-folding-2/gtksourceview/gtksourcebuffer.h	Sun Aug  3 23:03:52 2008
@@ -40,6 +40,7 @@
 #define GTK_IS_SOURCE_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SOURCE_BUFFER))
 #define GTK_SOURCE_BUFFER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SOURCE_BUFFER, GtkSourceBufferClass))
 
+typedef struct _GtkSourceFold			GtkSourceFold;
 typedef struct _GtkSourceBuffer			GtkSourceBuffer;
 typedef struct _GtkSourceBufferClass		GtkSourceBufferClass;
 typedef struct _GtkSourceBufferPrivate		GtkSourceBufferPrivate;
@@ -55,13 +56,16 @@
 {
 	GtkTextBufferClass parent_class;
 
+	void (*fold_added)  (GtkSourceBuffer *buffer,
+			     GtkSourceFold   *fold);
+	void (*fold_remove) (GtkSourceBuffer *buffer,
+			     GtkSourceFold   *fold);
+
 	/* Padding for future expansion */
 	void (*_gtk_source_reserved1) (void);
 	void (*_gtk_source_reserved2) (void);
 	void (*_gtk_source_reserved3) (void);
 	void (*_gtk_source_reserved4) (void);
-	void (*_gtk_source_reserved5) (void);
-	void (*_gtk_source_reserved6) (void);
 };
 
 GType           	 gtk_source_buffer_get_type 		(void) G_GNUC_CONST;
@@ -134,6 +138,28 @@
 								 const GtkTextIter      *start,
 								 const GtkTextIter      *end,
 								 const gchar            *category);
+
+/* fold methods. */
+gboolean		 gtk_source_buffer_get_folds_enabled	(GtkSourceBuffer        *buffer);
+void			 gtk_source_buffer_set_folds_enabled	(GtkSourceBuffer        *buffer,
+								 gboolean                enable_folds);
+
+GtkSourceFold		*gtk_source_buffer_add_fold		(GtkSourceBuffer        *buffer,
+								 const GtkTextIter      *begin,
+								 const GtkTextIter      *end);
+void			 gtk_source_buffer_remove_fold		(GtkSourceBuffer        *buffer,
+								 GtkSourceFold          *fold);
+
+void			 gtk_source_buffer_remove_folds_in_region
+ 								(GtkSourceBuffer        *buffer,
+								 const GtkTextIter      *begin,
+								 const GtkTextIter      *end);
+
+GtkSourceFold		*gtk_source_buffer_get_fold_at_iter	(GtkSourceBuffer        *buffer,
+								 const GtkTextIter      *iter);
+
+const GList		*gtk_source_buffer_get_root_folds	(GtkSourceBuffer        *buffer);
+
 /* private */
 void			 _gtk_source_buffer_update_highlight	(GtkSourceBuffer        *buffer,
 								 const GtkTextIter      *start,
@@ -141,11 +167,17 @@
 								 gboolean                synchronous);
 
 GtkSourceMark		*_gtk_source_buffer_source_mark_next	(GtkSourceBuffer        *buffer,
-								 GtkSourceMark          *mark, 
+								 GtkSourceMark          *mark,
 								 const gchar            *category);
 GtkSourceMark		*_gtk_source_buffer_source_mark_prev	(GtkSourceBuffer        *buffer,
-								 GtkSourceMark          *mark, 
+								 GtkSourceMark          *mark,
 								 const gchar            *category);
+GList			*_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
 
 #endif /* __GTK_SOURCE_BUFFER_H__ */

Added: branches/code-folding-2/gtksourceview/gtksourcefold-private.h
==============================================================================
--- (empty file)
+++ branches/code-folding-2/gtksourceview/gtksourcefold-private.h	Sun Aug  3 23:03:52 2008
@@ -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"
+
+G_BEGIN_DECLS
+
+#define INVISIBLE_LINE "GtkSourceBuffer:InvisibleLine"
+
+struct _GtkSourceFold
+{
+	/* 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. */
+	GList		*children;
+
+	/* Style of the expander arrow; if animated is set, this will gradually
+	 * increase to show the fold is collapsing/expanding. */
+	GtkExpanderStyle expander_style;
+
+	/* TRUE if the fold has collapsed. */
+	gint		 folded : 1;
+
+	/* TRUE if the user moves the mouse over the expander arrow; draw the
+	 * expander filled to indicate the mouse over. */
+	gint		 prelighted : 1;
+
+	/* TRUE if the user expanded/collapsed a fold using the GUI; animate
+	 * the collapse/expansion of the fold. */
+	gint		 animated : 1;
+};
+
+GtkSourceFold	*_gtk_source_fold_new	(GtkSourceBuffer	*buffer,
+					 const GtkTextIter	*begin,
+					 const GtkTextIter	*end);
+
+G_END_DECLS
+
+#endif  /* __GTK_SOURCE_FOLD_PRIVATE_H__ */

Added: branches/code-folding-2/gtksourceview/gtksourcefold.c
==============================================================================
--- (empty file)
+++ branches/code-folding-2/gtksourceview/gtksourcefold.c	Sun Aug  3 23:03:52 2008
@@ -0,0 +1,326 @@
+/*
+ *  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"
+
+GtkSourceFold *
+_gtk_source_fold_new (GtkSourceBuffer   *buffer,
+		      const GtkTextIter *begin,
+		      const GtkTextIter *end)
+{
+	GtkSourceFold *fold;
+
+	fold = g_new0 (GtkSourceFold, 1);
+	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);
+	g_object_ref (fold->start_line);
+	fold->end_line = gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (buffer),
+						      NULL, end, FALSE);
+	g_object_ref (fold->end_line);
+
+	return fold;
+}
+
+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;
+}
+
+/**
+ * gtk_source_fold_free:
+ * @fold: a #GtkSourceFold.
+ *
+ * Free a fold that was created using gtk_source_fold_copy. Useful for language
+ * bindings. Do not use otherwise.
+ **/
+void
+gtk_source_fold_free (GtkSourceFold *fold)
+{
+	if (!fold)
+		return;
+
+	if (!gtk_text_mark_get_deleted (fold->start_line))
+		gtk_text_buffer_delete_mark (gtk_text_mark_get_buffer (fold->start_line),
+					     fold->start_line);
+	g_object_unref (fold->start_line);
+
+	if (!gtk_text_mark_get_deleted (fold->end_line))
+		gtk_text_buffer_delete_mark (gtk_text_mark_get_buffer (fold->end_line),
+					     fold->end_line);
+	g_object_unref (fold->end_line);
+
+	if (fold->children)
+		g_list_free (fold->children);
+
+	g_free (fold);
+}
+
+/**
+ * gtk_source_fold_copy:
+ * @fold: a #GtkSourceFold.
+ *
+ * Copy the specified fold. Useful for language bindings. Do not use otherwise.
+ *
+ * Return value: a copy of the specified #GtkSourceFold.
+ **/
+GtkSourceFold *
+gtk_source_fold_copy (const GtkSourceFold *fold)
+{
+	GtkSourceFold *copy;
+
+	g_return_val_if_fail (fold != NULL, NULL);
+
+	copy = g_new (GtkSourceFold, 1);
+	*copy = *fold;
+	copy->children = g_list_copy (fold->children);
+	g_object_ref (copy->start_line);
+	g_object_ref (copy->end_line);
+
+	return copy;
+}
+
+/**
+ * gtk_source_fold_get_folded:
+ * @fold: a #GtkSourceFold.
+ *
+ * Return value: TRUE if the fold is currently collapsed. FALSE if it is
+ * expanded.
+ **/
+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,
+			   GList         *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_list_next (folds);
+	}
+}
+
+static void
+collapse_fold (GtkTextBuffer *buffer,
+	       GtkSourceFold *fold,
+	       GtkTextIter   *begin,
+	       GtkTextIter   *end)
+{
+	GtkTextIter insert;
+
+	/* if the starting point of the fold has no text before it on the line,
+	 * then only hide part of the line so the user still sees something. */
+	if (gtk_text_iter_starts_sentence (begin))
+		gtk_text_iter_forward_to_line_end (begin);
+
+	/* hide the entire line that contains the end of the fold. */
+	if (!gtk_text_iter_starts_line (end))
+		gtk_text_iter_forward_line (end);
+
+	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 the fold collapse is animated, the style is gradually
+	 * updated from a timeout handler in the view. If it isn't
+	 * animated we need to set the style here. This needed when
+	 * the user collapses the fold using the API instead of the GUI. */
+	if (!fold->animated)
+		fold->expander_style = GTK_EXPANDER_COLLAPSED;
+}
+
+static void
+expand_fold (GtkTextBuffer *buffer,
+	     GtkSourceFold *fold,
+	     GtkTextIter   *begin,
+	     GtkTextIter   *end)
+{
+	/* unhide the text after the fold, but still on the same line. */
+	if (!gtk_text_iter_starts_line (end))
+		gtk_text_iter_forward_line (end);
+
+	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 the fold expansion is animated, the style is gradually
+	 * updated from a timeout handler in the view. If it isn't
+	 * animated we need to set the style here. This needed when
+	 * the user expands the fold using the API instead of the GUI. */
+	if (!fold->animated)
+		fold->expander_style = GTK_EXPANDER_EXPANDED;
+}
+
+/**
+ * gtk_source_fold_set_folded:
+ * @fold: a #GtkSourceFold.
+ * @folded: a gboolean.
+ *
+ * Collapse the fold when folded is TRUE. Expand the fold otherwise.
+ **/
+void
+gtk_source_fold_set_folded (GtkSourceFold *fold,
+			    gboolean       folded)
+{
+	GtkTextBuffer *buffer;
+	GtkTextIter begin, end;
+
+	g_return_if_fail (fold != NULL);
+
+	folded = (folded != FALSE);
+
+	if (fold->folded == folded)
+		return;
+
+	fold->folded = folded;
+
+	buffer = gtk_text_mark_get_buffer (fold->start_line);
+
+	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)
+		collapse_fold (buffer, fold, &begin, &end);
+	else
+		expand_fold (buffer, fold, &begin, &end);
+}
+
+/**
+ * gtk_source_fold_get_bounds:
+ * @fold: a #GtkSourceFold.
+ * @begin: a #GtkTextIter.
+ * @end: a #GtkTextIter.
+ *
+ * Returns the bounds of the fold (begin, end) using the provided #GtkTextIters.
+ **/
+void
+gtk_source_fold_get_bounds (GtkSourceFold *fold,
+			    GtkTextIter   *begin,
+			    GtkTextIter   *end)
+{
+	GtkTextBuffer *buffer;
+
+	g_return_if_fail (fold != NULL);
+
+	if (gtk_text_mark_get_deleted (fold->start_line))
+		g_message ("starting mark DELETED!");
+
+	buffer = gtk_text_mark_get_buffer (fold->start_line);
+
+	if (begin != NULL)
+		gtk_text_buffer_get_iter_at_mark (buffer, begin, fold->start_line);
+	if (end != NULL)
+		gtk_text_buffer_get_iter_at_mark (buffer, end, fold->end_line);
+}
+
+/**
+ * gtk_source_fold_get_buffer:
+ * @fold: a #GtkSourceFold.
+ *
+ * Return value: the #GtkSourceBuffer that this fold is part of.
+ **/
+GtkSourceBuffer *
+gtk_source_fold_get_buffer (GtkSourceFold *fold)
+{
+	g_return_val_if_fail (fold != NULL, NULL);
+
+	return GTK_SOURCE_BUFFER (gtk_text_mark_get_buffer (fold->start_line));
+}
+
+/**
+ * gtk_source_fold_get_parent:
+ * @fold: a #GtkSourceFold.
+ *
+ * Return value: the parent #GtkSourceFold, or NULL if this is a root fold.
+ **/
+GtkSourceFold *
+gtk_source_fold_get_parent (GtkSourceFold *fold)
+{
+	g_return_val_if_fail (fold != NULL, NULL);
+
+	return fold->parent;
+}
+
+/**
+ * gtk_source_fold_get_children:
+ * @fold: a #GtkSourceFold.
+ *
+ * Return value: the list of fold children, sorted by appearance.
+ **/
+const GList *
+gtk_source_fold_get_children (GtkSourceFold *fold)
+{
+	g_return_val_if_fail (fold != NULL, NULL);
+
+	return fold->children;
+}

Added: branches/code-folding-2/gtksourceview/gtksourcefold.h
==============================================================================
--- (empty file)
+++ branches/code-folding-2/gtksourceview/gtksourcefold.h	Sun Aug  3 23:03:52 2008
@@ -0,0 +1,51 @@
+/*
+ *  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 <gtksourceview/gtksourcebuffer.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SOURCE_FOLD (gtk_source_fold_get_type ())
+
+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);
+
+GtkSourceBuffer		*gtk_source_fold_get_buffer	(GtkSourceFold       *fold);
+
+GtkSourceFold		*gtk_source_fold_get_parent	(GtkSourceFold       *fold);
+
+const GList		*gtk_source_fold_get_children	(GtkSourceFold       *fold);
+
+G_END_DECLS
+
+#endif  /* __GTK_SOURCE_FOLD_H__ */

Added: branches/code-folding-2/gtksourceview/gtksourcefoldlabel.c
==============================================================================
--- (empty file)
+++ branches/code-folding-2/gtksourceview/gtksourcefoldlabel.c	Sun Aug  3 23:03:52 2008
@@ -0,0 +1,226 @@
+/*
+ *  gtksourcefoldlabel.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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n.h>
+#include "gtksourcefoldlabel.h"
+
+/* Properties */
+enum {
+	PROP_0,
+	PROP_SOURCE_VIEW,
+	PROP_X,
+	PROP_Y
+};
+
+#define GTK_SOURCE_FOLD_LABEL_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), GTK_TYPE_SOURCE_FOLD_LABEL, GtkSourceFoldLabelPrivate))
+
+struct _GtkSourceFoldLabelPrivate
+{
+	GtkSourceView *source_view;
+	int            x;
+	int            y;
+};
+
+G_DEFINE_TYPE(GtkSourceFoldLabel, _gtk_source_fold_label, GTK_TYPE_LABEL)
+
+static void
+gtk_source_fold_label_get_property (GObject    *object,
+				    guint       prop_id,
+				    GValue     *value,
+				    GParamSpec *pspec)
+{
+	GtkSourceFoldLabel *label;
+
+	g_return_if_fail (GTK_IS_SOURCE_FOLD_LABEL (object));
+
+	label = GTK_SOURCE_FOLD_LABEL (object);
+
+	switch (prop_id)
+	{
+		case PROP_SOURCE_VIEW:
+			g_value_set_object (value, label->priv->source_view);
+			break;
+
+		case PROP_X:
+			g_value_set_int (value, label->priv->x);
+			break;
+
+		case PROP_Y:
+			g_value_set_int (value, label->priv->y);
+			break;
+
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+			break;
+	}
+}
+
+static void
+set_view (GtkSourceFoldLabel *label, GtkSourceView *view)
+{
+	PangoContext *ctx;
+	PangoFontDescription *font_desc;
+
+	g_return_if_fail (GTK_IS_SOURCE_VIEW (view));
+
+	ctx = gtk_widget_get_pango_context (GTK_WIDGET (view));
+	font_desc = pango_context_get_font_description (ctx);
+	gtk_widget_modify_font (GTK_WIDGET (label), font_desc);
+}
+
+static void
+gtk_source_fold_label_set_property (GObject      *object,
+				    guint         prop_id,
+				    const GValue *value,
+				    GParamSpec   *pspec)
+{
+	GtkSourceFoldLabel *label;
+
+	g_return_if_fail (GTK_IS_SOURCE_FOLD_LABEL (object));
+
+	label = GTK_SOURCE_FOLD_LABEL (object);
+
+	switch (prop_id)
+	{
+		case PROP_SOURCE_VIEW:
+			set_view (label, g_value_get_object (value));
+			break;
+
+		case PROP_X:
+			label->priv->x = g_value_get_int (value);
+			break;
+
+		case PROP_Y:
+			label->priv->y = g_value_get_int (value);
+			break;
+
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+			break;
+	}
+}
+
+static gint
+gtk_source_fold_label_expose (GtkWidget      *widget,
+			      GdkEventExpose *event)
+{
+	GTK_WIDGET_CLASS (_gtk_source_fold_label_parent_class)->expose_event (widget, event);
+
+	gdk_draw_rectangle (event->window,
+			    widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+			    FALSE,
+			    widget->allocation.x,
+			    widget->allocation.y,
+			    widget->allocation.width - 1,
+			    widget->allocation.height - 1);
+
+	return TRUE;
+}
+
+static void
+_gtk_source_fold_label_class_init (GtkSourceFoldLabelClass *klass)
+{
+	GObjectClass *object_class;
+	GtkWidgetClass *widget_class;
+
+	object_class = G_OBJECT_CLASS (klass);
+	widget_class = GTK_WIDGET_CLASS (klass);
+
+	object_class->get_property = gtk_source_fold_label_get_property;
+	object_class->set_property = gtk_source_fold_label_set_property;
+
+	widget_class->expose_event = gtk_source_fold_label_expose;
+
+	g_type_class_add_private (object_class, sizeof (GtkSourceFoldLabelPrivate));
+
+	g_object_class_install_property (object_class,
+					 PROP_SOURCE_VIEW,
+					 g_param_spec_object ("sourceview",
+							      _("Sourceview"),
+							      _("The Sourceview this label is shown in"),
+							      GTK_TYPE_SOURCE_VIEW,
+							      G_PARAM_READWRITE));
+
+	g_object_class_install_property (object_class,
+					 PROP_X,
+					 g_param_spec_int ("x",
+							   _("X coordinates for label"),
+							   _("The horizontal position where the label is shown"),
+							   -1,
+							   G_MAXINT,
+							   -1,
+							   G_PARAM_READWRITE));
+
+	g_object_class_install_property (object_class,
+					 PROP_Y,
+					 g_param_spec_int ("y",
+							   _("Y coordinates for label"),
+							   _("The vertical position where the label is shown"),
+							   -1,
+							   G_MAXINT,
+							   -1,
+							   G_PARAM_READWRITE));
+}
+
+static void
+_gtk_source_fold_label_init (GtkSourceFoldLabel *label)
+{
+	label->priv = GTK_SOURCE_FOLD_LABEL_GET_PRIVATE (label);
+}
+
+GtkWidget *
+_gtk_source_fold_label_new (GtkSourceView *view)
+{
+	return g_object_new (GTK_TYPE_SOURCE_FOLD_LABEL,
+			     "label", "..",
+			     "sensitive", FALSE,
+			     "sourceview", view,
+			     "x", -1,
+			     "y", -1,
+			     NULL);
+}
+
+void
+_gtk_source_fold_label_get_position (GtkSourceFoldLabel *label,
+				     int                *x,
+				     int                *y)
+{
+	g_return_if_fail (GTK_IS_SOURCE_FOLD_LABEL (label));
+
+	if (x != NULL)
+		*x = label->priv->x;
+	if (y != NULL)
+		*y = label->priv->y;
+}
+
+void
+_gtk_source_fold_label_set_position (GtkSourceFoldLabel *label,
+				     int                 x,
+				     int                 y)
+{
+	g_return_if_fail (GTK_IS_SOURCE_FOLD_LABEL (label));
+
+	label->priv->x = x;
+	label->priv->y = y;
+}

Added: branches/code-folding-2/gtksourceview/gtksourcefoldlabel.h
==============================================================================
--- (empty file)
+++ branches/code-folding-2/gtksourceview/gtksourcefoldlabel.h	Sun Aug  3 23:03:52 2008
@@ -0,0 +1,71 @@
+/*
+ *  gtksourcefoldlabel.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_LABEL_H__
+#define __GTK_SOURCE_FOLD_LABEL_H__
+
+#include <gtksourceview/gtksourceview.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_SOURCE_FOLD_LABEL		(_gtk_source_fold_label_get_type ())
+#define GTK_SOURCE_FOLD_LABEL(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_SOURCE_FOLD_LABEL, GtkSourceFoldLabel))
+#define GTK_SOURCE_FOLD_LABEL_CLASS(klass)	(G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_SOURCE_FOLD_LABEL, GtkSourceFoldLabelClass))
+#define GTK_IS_SOURCE_FOLD_LABEL(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_SOURCE_FOLD_LABEL))
+#define GTK_IS_SOURCE_FOLD_LABEL_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SOURCE_FOLD_LABEL))
+#define GTK_SOURCE_FOLD_LABEL_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SOURCE_FOLD_LABEL, GtkSourceFoldLabelClass))
+
+typedef struct _GtkSourceFoldLabel		GtkSourceFoldLabel;
+typedef struct _GtkSourceFoldLabelClass		GtkSourceFoldLabelClass;
+typedef struct _GtkSourceFoldLabelPrivate	GtkSourceFoldLabelPrivate;
+
+struct _GtkSourceFoldLabel
+{
+	GtkLabel label;
+
+	/*< private > */
+	GtkSourceFoldLabelPrivate *priv;
+};
+
+struct _GtkSourceFoldLabelClass
+{
+	GtkLabelClass parent_class;
+
+	/* Padding for future expansion */
+	void (*_gtk_source_reserved1) 	(void);
+	void (*_gtk_source_reserved2) 	(void);
+	void (*_gtk_source_reserved3) 	(void);
+};
+
+
+GType			 _gtk_source_fold_label_get_type	(void) G_GNUC_CONST;
+
+GtkWidget		*_gtk_source_fold_label_new		(GtkSourceView      *view);
+
+void			 _gtk_source_fold_label_get_position	(GtkSourceFoldLabel *label,
+								 int                *x,
+								 int                *y);
+void			 _gtk_source_fold_label_set_position	(GtkSourceFoldLabel *label,
+								 int                 x,
+								 int                 y);
+
+G_END_DECLS
+
+#endif  /* __GTK_SOURCE_FOLD_LABEL_H__ */

Modified: branches/code-folding-2/gtksourceview/gtksourceview.c
==============================================================================
--- branches/code-folding-2/gtksourceview/gtksourceview.c	(original)
+++ branches/code-folding-2/gtksourceview/gtksourceview.c	Sun Aug  3 23:03:52 2008
@@ -39,6 +39,8 @@
 #include "gtksourceview-typebuiltins.h"
 #include "gtksourcemark.h"
 #include "gtksourceview.h"
+#include "gtksourcefold-private.h"
+#include "gtksourcefoldlabel.h"
 
 /*
 #define ENABLE_DEBUG
@@ -75,6 +77,9 @@
 #define RIGHT_MARING_LINE_ALPHA		40
 #define RIGHT_MARING_OVERLAY_ALPHA	15
 
+#define DEFAULT_EXPANDER_SIZE		12
+#define EXPANDER_EXTRA_PADDING		4
+
 /* Signals */
 enum {
 	UNDO,
@@ -98,12 +103,23 @@
 	PROP_INDENT_ON_TAB
 };
 
+typedef struct _FoldLabelLocation FoldLabelLocation;
+
+struct _FoldLabelLocation
+{
+	GtkSourceView	*view;
+	GtkTextIter	 start;
+	GtkTextIter	 end;
+	gboolean         updated;
+};
+
 struct _GtkSourceViewPrivate
 {
 	guint		 tab_width;
 	gboolean	 tabs_set;
 	gint		 indent_width;
 	gboolean 	 show_line_numbers;
+	gint		 line_numbers_width;
 	gboolean	 show_line_marks;
 	gboolean	 auto_indent;
 	gboolean	 insert_spaces;
@@ -125,6 +141,14 @@
 
 	GtkSourceBuffer *source_buffer;
 	gint		 old_lines;
+
+	gboolean	 show_folds;
+	gint		 expander_size;
+	gint		 prelight_fold_line;
+	gboolean	 fold_button_down;
+	guint		 animation_timeout;
+	gint		 animate_fold_line;
+	GHashTable      *fold_labels;
 };
 
 
@@ -176,13 +200,18 @@
 				       			 gint               last_y,
 				       			 GArray            *buffer_coords,
 				       			 GArray            *numbers,
+							 GHashTable	   *fold_hash,
 				       			 gint              *countp);
 static gint     gtk_source_view_expose 			(GtkWidget         *widget,
 							 GdkEventExpose    *event);
 static gboolean	gtk_source_view_key_press_event		(GtkWidget         *widget,
 							 GdkEventKey       *event);
-static gboolean	gtk_source_view_button_press_event	(GtkWidget         *widget,
+static gboolean	gtk_source_view_button_press		(GtkWidget         *widget,
 							 GdkEventButton    *event);
+static gboolean gtk_source_view_button_release		(GtkWidget          *widget,
+							 GdkEventButton     *event);
+static gboolean gtk_source_view_motion_notify		(GtkWidget          *widget,
+							 GdkEventMotion     *event);
 static void 	view_dnd_drop 				(GtkTextView       *view,
 							 GdkDragContext    *context,
 							 gint               x,
@@ -235,7 +264,9 @@
 	object_class->set_property = gtk_source_view_set_property;
 
 	widget_class->key_press_event = gtk_source_view_key_press_event;
-	widget_class->button_press_event = gtk_source_view_button_press_event;
+	widget_class->button_press_event = gtk_source_view_button_press;
+	widget_class->button_release_event = gtk_source_view_button_release;
+	widget_class->motion_notify_event = gtk_source_view_motion_notify;
 	widget_class->expose_event = gtk_source_view_expose;
 	widget_class->style_set = gtk_source_view_style_set;
 	widget_class->realize = gtk_source_view_realize;
@@ -380,6 +411,15 @@
 							       TRUE,
 							       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),
@@ -678,6 +718,11 @@
 	view->priv->smart_home_end = GTK_SOURCE_SMART_HOME_END_DISABLED;
 	view->priv->right_margin_pos = DEFAULT_RIGHT_MARGIN_POSITION;
 	view->priv->cached_right_margin_pos = -1;
+	view->priv->line_numbers_width = 0;
+
+	view->priv->prelight_fold_line = -1;
+	view->priv->fold_button_down = FALSE;
+	view->priv->fold_labels = g_hash_table_new (g_direct_hash, g_direct_equal);
 
 	gtk_text_view_set_left_margin (GTK_TEXT_VIEW (view), 2);
 	gtk_text_view_set_right_margin (GTK_TEXT_VIEW (view), 2);
@@ -745,6 +790,8 @@
 	if (view->priv->mark_categories)
 		g_hash_table_destroy (view->priv->mark_categories);
 
+	g_hash_table_destroy (view->priv->fold_labels);
+
 	set_source_buffer (view, NULL);
 
 	G_OBJECT_CLASS (gtk_source_view_parent_class)->finalize (object);
@@ -842,6 +889,43 @@
 }
 
 static void
+fold_added_cb (GtkSourceBuffer *buffer,
+	       GtkSourceFold   *fold,
+	       GtkSourceView   *view)
+{
+	gtk_widget_queue_draw (GTK_WIDGET (view));
+}
+
+static void
+fold_remove_cb (GtkSourceBuffer *buffer,
+		GtkSourceFold   *fold,
+		GtkSourceView   *view)
+{
+	GtkSourceFoldLabel *label = g_hash_table_lookup (view->priv->fold_labels, fold);
+
+	if (label != NULL)
+	{
+		if (GTK_WIDGET_VISIBLE (label))
+			gtk_widget_hide (GTK_WIDGET (label));
+
+		g_hash_table_remove (view->priv->fold_labels, fold);
+
+		/* FIXME: this causes excessive redrawing? */
+		gtk_widget_queue_draw (GTK_WIDGET (view));
+	}
+}
+
+static void
+notify_folds_cb (GtkSourceBuffer *buffer,
+		 GParamSpec      *param,
+		 GtkSourceView   *view)
+{
+	view->priv->show_folds = gtk_source_buffer_get_folds_enabled (buffer);
+
+	gtk_widget_queue_draw (GTK_WIDGET (view));
+}
+
+static void
 set_source_buffer (GtkSourceView *view,
 		   GtkTextBuffer *buffer)
 {
@@ -859,6 +943,12 @@
 		g_signal_handlers_disconnect_by_func (view->priv->source_buffer,
 						      buffer_style_scheme_changed_cb,
 						      view);
+		g_signal_handlers_disconnect_by_func (view->priv->source_buffer,
+						      fold_added_cb,
+						      view);
+		g_signal_handlers_disconnect_by_func (view->priv->source_buffer,
+						      fold_remove_cb,
+						      view);
 		g_object_unref (view->priv->source_buffer);
 	}
 
@@ -878,6 +968,21 @@
 				  "notify::style-scheme",
 				  G_CALLBACK (buffer_style_scheme_changed_cb),
 				  view);
+		g_signal_connect (buffer,
+				  "fold_added",
+				  G_CALLBACK (fold_added_cb),
+				  view);
+		g_signal_connect (buffer,
+				  "fold_remove",
+				  G_CALLBACK (fold_remove_cb),
+				  view);
+		g_signal_connect (buffer,
+				  "notify::folds",
+				  G_CALLBACK (notify_folds_cb),
+				  view);
+
+		view->priv->show_folds =
+			gtk_source_buffer_get_folds_enabled (view->priv->source_buffer);
 	}
 	else
 	{
@@ -1140,44 +1245,96 @@
 			   gint          last_y,
 			   GArray       *buffer_coords,
 			   GArray       *numbers,
+			   GHashTable   *fold_hash,
 			   gint         *countp)
 {
-	GtkTextIter iter;
+	GtkTextIter iter, iter2, start_fold;
+	GList *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);
 
-	/* For each iter, get its location and add it to the arrays.
-	 * Stop when we pass last_y */
+	DEBUG (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. */
 	count = 0;
-  	size = 0;
+	size = 0;
+
+	/* Start with the first fold in the region. */
+	if (folds != NULL)
+	{
+		fold = folds->data;
+
+		gtk_text_buffer_get_iter_at_mark (text_view->buffer,
+						  &start_fold,
+						  fold->start_line);
+	}
 
-  	while (!gtk_text_iter_is_end (&iter))
-    	{
+	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);
-
 		g_array_append_val (buffer_coords, y);
 		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_list_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))
-    	{
+	{
 		gint y, height;
 		gint line_num;
 
@@ -1185,7 +1342,11 @@
 
 		line_num = gtk_text_iter_get_line (&iter);
 
-		if (line_num != last_line_num)
+		/* Only add the line number if we started at the last line or
+		 * if we didn't add the line number already in the previous
+		 * while loop (line_num != last_line_num).
+		 */
+		if (count == 0 || line_num != last_line_num)
 		{
 			g_array_append_val (buffer_coords, y);
 			g_array_append_val (numbers, line_num);
@@ -1193,6 +1354,9 @@
 		}
 	}
 
+	if (l != NULL)
+		g_list_free (l);
+
 	*countp = count;
 }
 
@@ -1307,9 +1471,202 @@
 }
 
 static void
+draw_fold_line (GtkSourceView *view,
+		GtkTextIter   *cur,
+		gint           text_width,
+		gint           text_height,
+		GtkSourceFold *fold)
+{
+	GtkWidget *widget;
+	GtkTextView *text_view;
+	GdkWindow *win;
+	int x, y, win_y, y1, y2, height;
+
+	widget = GTK_WIDGET (view);
+	text_view = GTK_TEXT_VIEW (view);
+
+	win = gtk_text_view_get_window (text_view,
+					GTK_TEXT_WINDOW_LEFT);
+
+	x = text_width + 3 + (view->priv->expander_size / 2);
+
+	/* the line starts at the next line. */
+	gtk_text_buffer_get_iter_at_mark (text_view->buffer,
+					  cur,
+					  fold->start_line);
+	gtk_text_iter_forward_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,
+					       &y1);
+
+	/* calculate the end of the line. */
+	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);
+
+	y2 = win_y + (text_height / 2);
+
+	/* vertical line. */
+	gdk_draw_line (win,
+		       widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+		       x, y1, x, y2);
+
+	/* horizontal line indicating the end of the fold. */
+	gdk_draw_line (win,
+		       widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+		       x, y2, x + (view->priv->expander_size / 2) - 2, y2);
+}
+
+static gboolean
+move_fold_label (GtkTextView        *view,
+		 GtkSourceFold      *fold,
+		 GtkWidget          *label)
+{
+	GtkTextIter begin;
+	GdkRectangle rect;
+	int x, y, old_x, old_y;
+
+	gtk_source_fold_get_bounds (fold, &begin, NULL);
+
+	/* if there's no text before the start of the fold, show the fold label
+	 * after the text on the line. This ties in with how GtkSourceFold shows
+	 * a collapsed fold: the first line remains visible when there's no text
+	 * before the start of the fold. */
+	if (gtk_text_iter_starts_sentence (&begin) && !gtk_text_iter_ends_line (&begin))
+		gtk_text_iter_forward_to_line_end (&begin);
+
+	gtk_text_view_get_iter_location (view, &begin, &rect);
+
+	gtk_text_view_buffer_to_window_coords (view,
+					       GTK_TEXT_WINDOW_TEXT,
+					       rect.x, rect.y,
+					       &x, &y);
+
+	_gtk_source_fold_label_get_position (GTK_SOURCE_FOLD_LABEL (label),
+					     &old_x, &old_y);
+
+	/* Only update if the position has really changed. */
+	if (GTK_WIDGET_VISIBLE (label) && old_x == x && old_y == y)
+		return FALSE;
+
+	_gtk_source_fold_label_set_position (GTK_SOURCE_FOLD_LABEL (label), x, y);
+
+	/* Position the label 2 pixels to the right of the last character. */
+	gtk_text_view_move_child (view, label, x + 2, y);
+
+	if (!GTK_WIDGET_VISIBLE (label))
+		gtk_widget_show (label);
+
+	return TRUE;
+}
+
+static void
+foreach_fold_label (GtkSourceFold     *fold,
+		    GtkWidget         *label,
+		    FoldLabelLocation *location)
+{
+	GtkTextIter fold_start;
+
+	/* If the fold isn't collapsed, don't bother. */
+	if (!gtk_source_fold_get_folded (fold))
+		return;
+
+	gtk_source_fold_get_bounds (fold, &fold_start, NULL);
+
+	/* If the label is in the visible range, update its location. */
+	if (gtk_text_iter_compare (&fold_start, &location->start) != -1 &&
+	    gtk_text_iter_compare (&fold_start, &location->end) != 1)
+	{
+		gboolean updated = move_fold_label (GTK_TEXT_VIEW (location->view),
+						    fold, label);
+
+		/* Set the updated flag so we queue a redraw. */
+		if (!location->updated && updated)
+			location->updated = TRUE;
+	}
+	else if (GTK_WIDGET_VISIBLE (label))
+	{
+		/* If the label was visible, but no longer is, queue a redraw. */
+		gtk_widget_hide (label);
+		location->updated = TRUE;
+	}
+}
+
+static void
+update_fold_label_locations (GtkSourceView *view)
+{
+	FoldLabelLocation location;
+	int y;
+
+	location.view = view;
+	location.updated = FALSE;
+
+	/* Get the visible line range in the textview. */
+	gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (view),
+					       GTK_TEXT_WINDOW_TEXT,
+					       0,
+					       0,
+					       NULL,
+					       &y);
+
+	gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (view), &location.start, 0, y);
+
+	gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (view),
+					       GTK_TEXT_WINDOW_TEXT,
+					       0,
+					       GTK_WIDGET (view)->allocation.height,
+					       NULL,
+					       &y);
+
+	gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (view), &location.end, 0, y);
+
+	/* Update the fold label positions. */
+	g_hash_table_foreach (view->priv->fold_labels,
+			      (GHFunc) foreach_fold_label,
+			      &location);
+
+	/* When scrolling, we can't just update the fold label positions and *not*
+	 * redraw the visible area. If we don't redraw, we get ghosting effects
+	 * when scrolling.
+	 */
+	if (location.updated)
+	{
+		//gtk_widget_queue_draw (GTK_WIDGET (view));
+		gdk_window_invalidate_rect (gtk_text_view_get_window (GTK_TEXT_VIEW (view),
+								      GTK_TEXT_WINDOW_TEXT),
+					    NULL, TRUE);
+	}
+}
+
+static void
 gtk_source_view_paint_margin (GtkSourceView *view,
 			      GdkEventExpose *event)
 {
+	GtkWidget *widget;
 	GtkTextView *text_view;
 	GdkWindow *win;
 	PangoLayout *layout;
@@ -1319,14 +1676,19 @@
 	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_marks)
+	if (!view->priv->show_line_numbers &&
+	    !view->priv->show_line_marks &&
+	    !view->priv->show_folds)
 	{
 		gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (text_view),
 						      GTK_TEXT_WINDOW_LEFT,
@@ -1358,6 +1720,7 @@
 
 	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,
@@ -1365,6 +1728,7 @@
 				   y2,
 				   pixels,
 				   numbers,
+				   folds,
 				   &count);
 
 	/* A zero-lined document should display a "1"; we don't need to worry about
@@ -1390,7 +1754,7 @@
 		    "%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);
@@ -1401,13 +1765,17 @@
 	else
 		margin_width = 0;
 
+	view->priv->line_numbers_width = margin_width;
+
+	if (view->priv->show_folds)
+		margin_width += view->priv->expander_size;
+
 	x_pixmap = margin_width;
 
 	if (view->priv->show_line_marks)
 		margin_width += GUTTER_PIXMAP;
 
-	/* no line & no marks case is short circuited before */
-	g_assert (margin_width != 0);
+	g_return_if_fail (margin_width != 0);
 
 	gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (text_view),
 					      GTK_TEXT_WINDOW_LEFT,
@@ -1419,6 +1787,19 @@
 
 	cur_line = gtk_text_iter_get_line (&cur);
 
+	/* It can happen that only part of the fold line was drawn. When the
+	 * view is scrolled downwards, a part of the fold line still needs to be
+	 * drawn. That check is performed here.
+	 */
+	if (view->priv->prelight_fold_line != -1 &&
+	    view->priv->prelight_fold_line < g_array_index (numbers, gint, i))
+	{
+		fold = _gtk_source_buffer_get_fold_at_line (view->priv->source_buffer,
+							    view->priv->prelight_fold_line);
+		if (fold != NULL)
+			draw_fold_line (view, &cur, text_width, text_height, fold);
+	}
+
 	for (i = 0; i < count; ++i)
 	{
 		gint pos;
@@ -1480,8 +1861,63 @@
 				g_slist_free (marks);
 			}
 		}
+
+		if (view->priv->show_folds && g_hash_table_size (folds) > 0)
+		{
+			fold = g_hash_table_lookup (folds, GINT_TO_POINTER (line_to_paint));
+
+			if (fold != NULL)
+			{
+				GtkStateType state = GTK_WIDGET_STATE (view);
+				GtkWidget *fold_label;
+
+				/* draw a vertical line to highlight the fold. */
+				if (fold->prelighted && !fold->folded)
+					draw_fold_line (view, &cur, text_width, text_height, fold);
+
+				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);
+
+				/* Add or update the fold label. */
+				fold_label = g_hash_table_lookup (view->priv->fold_labels,
+								  fold);
+
+				if (fold_label == NULL && fold->folded)
+				{
+					fold_label = _gtk_source_fold_label_new (view);
+
+					g_hash_table_insert (view->priv->fold_labels,
+							     fold, fold_label);
+
+					gtk_text_view_add_child_in_window (text_view,
+									   fold_label,
+									   GTK_TEXT_WINDOW_TEXT,
+									   0,
+									   0);
+
+					move_fold_label (text_view, fold, fold_label);
+				}
+				/* Hide the label if the fold has expanded. */
+				else if (fold_label != NULL && !fold->folded &&
+					 GTK_WIDGET_VISIBLE (fold_label))
+				{
+					gtk_widget_hide (fold_label);
+				}
+			}
+		}
 	}
 
+	g_hash_table_destroy (folds);
 	g_array_free (pixels, TRUE);
 	g_array_free (numbers, TRUE);
 
@@ -1620,6 +2056,17 @@
 					    height);
 		}
 
+		/* Since fold labels aren't anchored, we need to update the position
+		 * manually as the textview is scrolled. Also, this applies to all fold
+		 * labels in the visible textview, not just the part that is being painted.
+		 */
+		if (view->priv->show_folds &&
+		    (event->window == gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT)) &&
+		    g_hash_table_size (view->priv->fold_labels) > 0)
+		{
+			update_fold_label_locations (view);
+		}
+
 		/* Have GtkTextView draw the text first. */
 		if (GTK_WIDGET_CLASS (gtk_source_view_parent_class)->expose_event)
 			event_handled =
@@ -2775,7 +3222,7 @@
 		}
 
 		insert_tab_or_spaces (view, &s, &e);
- 		return TRUE;
+		return TRUE;
 	}
 
 	/* Alt+up/down moves the lines */
@@ -2790,6 +3237,110 @@
 	return GTK_WIDGET_CLASS (gtk_source_view_parent_class)->key_press_event (widget, event);
 }
 
+static gboolean
+gtk_source_view_motion_notify (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_folds && 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);
+
+		/* If the cursor is not over the fold margin, return. */
+		if (x < view->priv->line_numbers_width)
+		{
+			if (redraw)
+				gtk_widget_queue_draw (widget);
+
+			return GTK_WIDGET_CLASS (gtk_source_view_parent_class)->
+					motion_notify_event (widget, event);
+		}
+
+		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);
+
+		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);
+
+			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_folds && 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 (gtk_source_view_parent_class)->
+				motion_notify_event (widget, event);
+	}
+	else
+	{
+		return GTK_WIDGET_CLASS (gtk_source_view_parent_class)->
+				motion_notify_event (widget, event);
+	}
+}
+
 static void
 extend_selection_to_line (GtkTextBuffer *buf, GtkTextIter *line_start)
 {
@@ -2833,16 +3384,52 @@
 }
 
 static gboolean
-gtk_source_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
+gtk_source_view_button_press (GtkWidget *widget, GdkEventButton *event)
 {
 	GtkSourceView *view;
 	GtkTextBuffer *buf;
 	int y_buf;
 	GtkTextIter line_start;
+	GtkSourceFold *fold;
 
 	view = GTK_SOURCE_VIEW (widget);
 	buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
 
+	if (view->priv->show_folds && event->button == 1 &&
+	    event->window == gtk_text_view_get_window (GTK_TEXT_VIEW (view),
+						       GTK_TEXT_WINDOW_LEFT) &&
+	    event->x >= view->priv->line_numbers_width)
+	{
+		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;
+	}
+
 	if (view->priv->show_line_numbers &&
 	    (event->window == gtk_text_view_get_window (GTK_TEXT_VIEW (view),
 						       GTK_TEXT_WINDOW_LEFT)))
@@ -2887,6 +3474,119 @@
 	return GTK_WIDGET_CLASS (gtk_source_view_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_folds && event->button == 1 &&
+	    view->priv->fold_button_down &&
+	    event->window == gtk_text_view_get_window (GTK_TEXT_VIEW (view),
+						       GTK_TEXT_WINDOW_LEFT) &&
+	    event->x >= view->priv->line_numbers_width)
+	{
+		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 (gtk_source_view_parent_class)->
+				button_release_event (widget, event);
+	}
+}
+
 /**
  * gtk_source_view_get_auto_indent:
  * @view: a #GtkSourceView.
@@ -3257,6 +3957,12 @@
 		GTK_WIDGET_CLASS (gtk_source_view_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
@@ -3425,3 +4131,22 @@
 			view->priv->style_scheme_applied = FALSE;
 	}
 }
+
+static void
+expand_folds (GtkSourceBuffer *buffer, GList *folds)
+{
+	GtkSourceFold *fold;
+	GList *children;
+
+	while (folds != NULL)
+	{
+		fold = folds->data;
+		children = fold->children;
+
+		expand_folds (buffer, children);
+
+		gtk_source_fold_set_folded (fold, FALSE);
+
+		folds = g_list_next (folds);
+	}
+}

Added: branches/code-folding-2/tests/test-fold.c
==============================================================================
--- (empty file)
+++ branches/code-folding-2/tests/test-fold.c	Sun Aug  3 23:03:52 2008
@@ -0,0 +1,383 @@
+/*
+ *  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 =
+	"Test case 1\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15";
+
+static GtkSourceFold *
+add_fold (GtkSourceBuffer *buffer,
+	  int start_line,
+	  int end_line)
+{
+	GtkTextIter begin, end;
+
+	gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer),
+					  &begin, start_line);
+	gtk_text_iter_forward_to_line_end (&begin);
+	gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer),
+					  &end, end_line + 1);
+	return gtk_source_buffer_add_fold (buffer, &begin, &end);
+}
+
+static void
+reset_buffer (GtkSourceBuffer *buffer)
+{
+	GtkTextIter start, end;
+
+	gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (buffer), &start, &end);
+	gtk_text_buffer_delete (GTK_TEXT_BUFFER (buffer), &start, &end);
+
+	gtk_text_buffer_insert_at_cursor (GTK_TEXT_BUFFER (buffer),
+					  text, strlen (text));
+}
+
+static void
+test1 (GtkSourceBuffer *buffer)
+{
+	g_assert (add_fold (buffer, 5, 10) != NULL);
+	g_assert (add_fold (buffer, 6, 8) != NULL);
+}
+
+static void
+test2 (GtkSourceBuffer *buffer)
+{
+	g_assert (add_fold (buffer, 5, 10) != NULL);
+	g_assert (add_fold (buffer, 6, 7) != NULL);
+	g_assert (add_fold (buffer, 8, 9) != NULL);
+}
+
+static void
+test3 (GtkSourceBuffer *buffer)
+{
+	g_assert (add_fold (buffer, 5, 10) != NULL);
+	/* fails, intersects at begin. */
+	g_assert (add_fold (buffer, 4, 6) == NULL);
+}
+
+static void
+test4 (GtkSourceBuffer *buffer)
+{
+	g_assert (add_fold (buffer, 5, 10) != NULL);
+	/* fails, same beginpoint. */
+	g_assert (add_fold (buffer, 5, 6) == NULL);
+}
+
+static void
+test5 (GtkSourceBuffer *buffer)
+{
+	g_assert (add_fold (buffer, 5, 10) != NULL);
+	/* fails, intersects at end. */
+	g_assert (add_fold (buffer, 7, 11) == NULL);
+}
+
+static void
+test6 (GtkSourceBuffer *buffer)
+{
+	g_assert (add_fold (buffer, 5, 10) != NULL);
+	/* fails, same endpoint. */
+	g_assert (add_fold (buffer, 7, 10) == NULL);
+}
+
+static void
+test7 (GtkSourceBuffer *buffer)
+{
+	g_assert (add_fold (buffer, 5, 10) != NULL);
+	g_assert (add_fold (buffer, 3, 12) != NULL);
+}
+
+static void
+prepare8 (GtkSourceBuffer *buffer)
+{
+	reset_buffer (buffer);
+	g_assert (add_fold (buffer, 5, 10) != NULL);
+	g_assert (add_fold (buffer, 3, 12) != NULL);
+}
+
+static void
+test8 (GtkSourceBuffer *buffer)
+{
+	/* test1 */
+	prepare8 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	g_assert (add_fold (buffer, 6, 8) != NULL);
+
+	/* test2 */
+	prepare8 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	g_assert (add_fold (buffer, 6, 7) != NULL);
+	g_assert (add_fold (buffer, 8, 9) != NULL);
+
+	/* test3 */
+	prepare8 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	/* fails, intersects at begin. */
+	g_assert (add_fold (buffer, 4, 6) == NULL);
+
+	/* test4 */
+	prepare8 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	/* fails, same beginpoint. */
+	g_assert (add_fold (buffer, 5, 6) == NULL);
+
+	/* test5 */
+	prepare8 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	/* fails, intersects at end. */
+	g_assert (add_fold (buffer, 7, 11) == NULL);
+
+	/* test6 */
+	prepare8 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	/* fails, same endpoint. */
+	g_assert (add_fold (buffer, 7, 10) == NULL);
+}
+
+static void
+prepare9 (GtkSourceBuffer *buffer)
+{
+	reset_buffer (buffer);
+	g_assert (add_fold (buffer, 0, 2) != NULL);
+	g_assert (add_fold (buffer, 5, 10) != NULL);
+}
+
+static void
+test9 (GtkSourceBuffer *buffer)
+{
+	/* test1 */
+	prepare9 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	g_assert (add_fold (buffer, 6, 8) != NULL);
+
+	/* test2 */
+	prepare9 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	g_assert (add_fold (buffer, 6, 7) != NULL);
+	g_assert (add_fold (buffer, 8, 9) != NULL);
+
+	/* test3 */
+	prepare9 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	/* fails, intersects at begin. */
+	g_assert (add_fold (buffer, 4, 6) == NULL);
+
+	/* test4 */
+	prepare9 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	/* fails, same beginpoint. */
+	g_assert (add_fold (buffer, 5, 6) == NULL);
+
+	/* test5 */
+	prepare9 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	/* fails, intersects at end. */
+	g_assert (add_fold (buffer, 7, 11) == NULL);
+
+	/* test6 */
+	prepare9 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	/* fails, same endpoint. */
+	g_assert (add_fold (buffer, 7, 10) == NULL);
+
+	/* test7 */
+	prepare9 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	g_assert (add_fold (buffer, 3, 12) != NULL);
+}
+
+static void
+prepare10 (GtkSourceBuffer *buffer)
+{
+	reset_buffer (buffer);
+	g_assert (add_fold (buffer, 5, 10) != NULL);
+	g_assert (add_fold (buffer, 13, 14) != NULL);
+}
+
+static void
+test10 (GtkSourceBuffer *buffer)
+{
+	/* test1 */
+	prepare10 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	g_assert (add_fold (buffer, 6, 8) != NULL);
+
+	/* test2 */
+	prepare10 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	g_assert (add_fold (buffer, 6, 7) != NULL);
+	g_assert (add_fold (buffer, 8, 9) != NULL);
+
+	/* test3 */
+	prepare10 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	/* fails, intersects at begin. */
+	g_assert (add_fold (buffer, 4, 6) == NULL);
+
+	/* test4 */
+	prepare10 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	/* fails, same beginpoint. */
+	g_assert (add_fold (buffer, 5, 6) == NULL);
+
+	/* test5 */
+	prepare10 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	/* fails, intersects at end. */
+	g_assert (add_fold (buffer, 7, 11) == NULL);
+
+	/* test6 */
+	prepare10 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	/* fails, same endpoint. */
+	g_assert (add_fold (buffer, 7, 10) == NULL);
+
+	/* test7 */
+	prepare10 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	g_assert (add_fold (buffer, 3, 12) != NULL);
+}
+
+static void
+prepare11 (GtkSourceBuffer *buffer)
+{
+	reset_buffer (buffer);
+	g_assert (add_fold (buffer, 0, 2) != NULL);
+	g_assert (add_fold (buffer, 5, 10) != NULL);
+	g_assert (add_fold (buffer, 13, 14) != NULL);
+}
+
+static void
+test11 (GtkSourceBuffer *buffer)
+{
+	/* test1 */
+	prepare11 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	g_assert (add_fold (buffer, 6, 8) != NULL);
+
+	/* test2 */
+	prepare11 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	g_assert (add_fold (buffer, 6, 7) != NULL);
+	g_assert (add_fold (buffer, 8, 9) != NULL);
+
+	/* test3 */
+	prepare11 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	/* fails, intersects at begin. */
+	g_assert (add_fold (buffer, 4, 6) == NULL);
+
+	/* test4 */
+	prepare11 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	/* fails, same beginpoint. */
+	g_assert (add_fold (buffer, 5, 6) == NULL);
+
+	/* test5 */
+	prepare11 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	/* fails, intersects at end. */
+	g_assert (add_fold (buffer, 7, 11) == NULL);
+
+	/* test6 */
+	prepare11 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	/* fails, same endpoint. */
+	g_assert (add_fold (buffer, 7, 10) == NULL);
+
+	/* test7 */
+	prepare11 (buffer);
+	/* fails, fold already exists. */
+	g_assert (add_fold (buffer, 5, 10) == NULL);
+	g_assert (add_fold (buffer, 3, 12) != NULL);
+}
+
+static void
+run_tests (GtkSourceBuffer *buffer)
+{
+	g_message ("Starting test...");
+
+	reset_buffer (buffer);
+	test1 (buffer);
+	reset_buffer (buffer);
+	test2 (buffer);
+	reset_buffer (buffer);
+	test3 (buffer);
+	reset_buffer (buffer);
+	test4 (buffer);
+	reset_buffer (buffer);
+	test5 (buffer);
+	reset_buffer (buffer);
+	test6 (buffer);
+	reset_buffer (buffer);
+	test7 (buffer);
+	reset_buffer (buffer);
+	test8 (buffer);
+	reset_buffer (buffer);
+	test9 (buffer);
+	reset_buffer (buffer);
+	test10 (buffer);
+	reset_buffer (buffer);
+	test11 (buffer);
+
+	g_message ("Test finished succesfully!");
+}
+
+int
+main (int argc, char *argv[])
+{
+	GtkSourceBuffer *buffer;
+
+	gtk_init (&argc, &argv);
+
+	buffer = gtk_source_buffer_new (NULL);
+
+	run_tests (buffer);
+
+	return 0;
+}

Modified: branches/code-folding-2/tests/test-widget.c
==============================================================================
--- branches/code-folding-2/tests/test-widget.c	(original)
+++ branches/code-folding-2/tests/test-widget.c	Sun Aug  3 23:03:52 2008
@@ -76,6 +76,8 @@
 						  gpointer         user_data);
 static void       hl_bracket_toggled_cb          (GtkAction       *action,
 						  gpointer         user_data);
+static void       folds_toggled_cb		 (GtkAction       *action,
+						  gpointer         user_data);
 static void       hl_line_toggled_cb             (GtkAction       *action,
 						  gpointer         user_data);
 static void       wrap_lines_toggled_cb          (GtkAction       *action,
@@ -91,6 +93,8 @@
 						  GtkAction       *current,
 						  gpointer         user_data);
 
+static void	  add_folds			 (GtkSourceBuffer *buffer);
+
 static GtkWidget *create_view_window             (GtkSourceBuffer *buffer,
 						  GtkSourceView   *from);
 
@@ -128,6 +132,9 @@
 	{ "ShowMarks", NULL, "Show Line _Marks", NULL,
 	  "Toggle visibility of marks in the left margin",
 	  G_CALLBACK (marks_toggled_cb), FALSE },
+	{ "FoldsEnabled", NULL, "Folds Enabled", NULL,
+	  "Folds enabled",
+	  G_CALLBACK (folds_toggled_cb), FALSE },
 	{ "ShowMargin", NULL, "Show Right M_argin", NULL,
 	  "Toggle visibility of right margin indicator",
 	  G_CALLBACK (margin_toggled_cb), FALSE },
@@ -187,6 +194,7 @@
 "      <menuitem action=\"HlBracket\"/>"
 "      <menuitem action=\"ShowNumbers\"/>"
 "      <menuitem action=\"ShowMarks\"/>"
+"      <menuitem action=\"FoldsEnabled\"/>"
 "      <menuitem action=\"ShowMargin\"/>"
 "      <menuitem action=\"HlLine\"/>"
 "      <menuitem action=\"WrapLines\"/>"
@@ -550,6 +558,30 @@
 	return success;
 }
 
+static void
+add_folds (GtkSourceBuffer *buffer)
+{
+	GtkTextBuffer *text_buffer = GTK_TEXT_BUFFER (buffer);
+	GtkTextIter start, end;
+
+	if (!gtk_source_buffer_get_folds_enabled (buffer))
+		return;
+
+	gtk_text_buffer_get_start_iter (text_buffer, &start);
+	end = start;
+	gtk_text_buffer_get_iter_at_line (text_buffer, &end, 15);
+	gtk_source_buffer_add_fold (buffer, &start, &end);
+
+// 	gtk_text_buffer_get_start_iter (text_buffer, &start);
+// 	end = start;
+// 	gtk_text_buffer_get_iter_at_line (text_buffer, &end, 4);
+// 	gtk_source_buffer_add_fold (buffer, &start, &end);
+
+	gtk_text_buffer_get_iter_at_line (text_buffer, &start, 5);
+	end = start;
+	gtk_text_buffer_get_iter_at_line (text_buffer, &end, 10);
+	gtk_source_buffer_add_fold (buffer, &start, &end);
+}
 
 /* View action callbacks -------------------------------------------------------- */
 
@@ -592,6 +624,18 @@
 }
 
 static void
+folds_toggled_cb (GtkAction *action, gpointer user_data)
+{
+	GtkTextBuffer *buffer;
+	g_return_if_fail (GTK_IS_TOGGLE_ACTION (action) && GTK_IS_SOURCE_VIEW (user_data));
+	buffer = gtk_text_view_get_buffer (user_data);
+	gtk_source_buffer_set_folds_enabled (
+		GTK_SOURCE_BUFFER (buffer),
+		gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
+	add_folds (GTK_SOURCE_BUFFER (buffer));
+}
+
+static void
 hl_line_toggled_cb (GtkAction *action, gpointer user_data)
 {
 	g_return_if_fail (GTK_IS_TOGGLE_ACTION (action) && GTK_IS_SOURCE_VIEW (user_data));
@@ -1216,7 +1260,7 @@
 
 	g_signal_connect (buffer, "mark-set", G_CALLBACK (move_cursor_cb), view);
 	g_signal_connect (buffer, "changed", G_CALLBACK (update_cursor_position), view);
-	g_signal_connect (view, "button-press-event", G_CALLBACK (button_press_cb), NULL);
+	g_signal_connect_after (view, "button-press-event", G_CALLBACK (button_press_cb), NULL);
 	g_signal_connect (window, "delete-event", (GCallback) window_deleted_cb, view);
 
 	/* action group and UI manager */
@@ -1399,6 +1443,9 @@
 	action = gtk_action_group_get_action (action_group, "HlBracket");
 	gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
 
+	action = gtk_action_group_get_action (action_group, "FoldsEnabled");
+	gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), FALSE);
+
 	action = gtk_action_group_get_action (action_group, "ShowNumbers");
 	gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
 



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