? folding.patch
Index: docs/reference/tmpl/buffer.sgml
===================================================================
RCS file: /cvs/gnome/gtksourceview/docs/reference/tmpl/buffer.sgml,v
retrieving revision 1.5
diff -u -p -r1.5 buffer.sgml
--- docs/reference/tmpl/buffer.sgml	8 Mar 2005 10:47:58 -0000	1.5
+++ docs/reference/tmpl/buffer.sgml	15 Nov 2005 19:54:22 -0000
@@ -66,6 +66,22 @@ operation.
 @sourcebuffer: the object which received the signal.
 @arg1: TRUE if the buffer can now perform an undo.
 
+<!-- ##### SIGNAL GtkSourceBuffer::fold-added ##### -->
+<para>
+
+</para>
+
+@sourcebuffer: the object which received the signal.
+@arg1: 
+
+<!-- ##### SIGNAL GtkSourceBuffer::fold-remove ##### -->
+<para>
+
+</para>
+
+@sourcebuffer: the object which received the signal.
+@arg1: 
+
 <!-- ##### SIGNAL GtkSourceBuffer::highlight-updated ##### -->
 <para>
 Emitted whenever the syntax highlighting information has been updated,
@@ -99,6 +115,11 @@ one for the old location and one for the
 </para>
 
 <!-- ##### ARG GtkSourceBuffer:escape-char ##### -->
+<para>
+
+</para>
+
+<!-- ##### ARG GtkSourceBuffer:folds ##### -->
 <para>
 
 </para>
Index: docs/reference/tmpl/gtksourceview-unused.sgml
===================================================================
RCS file: /cvs/gnome/gtksourceview/docs/reference/tmpl/gtksourceview-unused.sgml,v
retrieving revision 1.3
diff -u -p -r1.3 gtksourceview-unused.sgml
--- docs/reference/tmpl/gtksourceview-unused.sgml	13 Oct 2004 09:16:11 -0000	1.3
+++ docs/reference/tmpl/gtksourceview-unused.sgml	15 Nov 2005 19:54:22 -0000
@@ -80,7 +80,21 @@
 </para>
 
 
+<!-- ##### SIGNAL GtkSourceBuffer::fold-removed ##### -->
+<para>
+
+</para>
+
+@sourcebuffer: the object which received the signal.
+@arg1: 
+
 <!-- ##### ARG GtkSourcePrintJob:numbers-font-desct ##### -->
+<para>
+
+</para>
+
+
+<!-- ##### ARG GtkSourceView:show-fold-regions ##### -->
 <para>
 
 </para>
Index: docs/reference/tmpl/view.sgml
===================================================================
RCS file: /cvs/gnome/gtksourceview/docs/reference/tmpl/view.sgml,v
retrieving revision 1.4
diff -u -p -r1.4 view.sgml
--- docs/reference/tmpl/view.sgml	8 Mar 2005 10:47:58 -0000	1.4
+++ docs/reference/tmpl/view.sgml	15 Nov 2005 19:54:22 -0000
@@ -82,6 +82,11 @@ GtkSourceView
 
 </para>
 
+<!-- ##### ARG GtkSourceView:expander-size ##### -->
+<para>
+
+</para>
+
 <!-- ##### FUNCTION gtk_source_view_new ##### -->
 <para>
 
Index: gtksourceview/Makefile.am
===================================================================
RCS file: /cvs/gnome/gtksourceview/gtksourceview/Makefile.am,v
retrieving revision 1.17.2.1
diff -u -p -r1.17.2.1 Makefile.am
--- gtksourceview/Makefile.am	18 Oct 2005 13:02:45 -0000	1.17.2.1
+++ gtksourceview/Makefile.am	15 Nov 2005 19:54:23 -0000
@@ -26,6 +26,7 @@ lib_LTLIBRARIES = libgtksourceview-1.0.l
 libgtksourceview_headers =			\
 	gtksourcebuffer.h			\
 	gtksourcefold.h				\
+	gtksourcefoldlabel.h			\
 	gtksourceiter.h				\
 	gtksourcetag.h				\
 	gtksourceview.h				\
@@ -41,6 +42,7 @@ libgtksourceview_1_0_la_SOURCES = 	\
 	gtksourcebuffer.c 		\
 	gtksourcefold.c			\
 	gtksourcefold-private.h		\
+	gtksourcefoldlabel.c		\
 	gtksourceiter.c			\
 	gtksourcetag.c 			\
 	gtksourcetag-private.h 		\
Index: gtksourceview/gtksourcebuffer.c
===================================================================
RCS file: /cvs/gnome/gtksourceview/gtksourceview/gtksourcebuffer.c,v
retrieving revision 1.39.6.2
diff -u -p -r1.39.6.2 gtksourcebuffer.c
--- gtksourceview/gtksourcebuffer.c	6 Nov 2005 20:41:03 -0000	1.39.6.2
+++ gtksourceview/gtksourcebuffer.c	15 Nov 2005 19:54:25 -0000
@@ -94,7 +94,8 @@ enum {
 	PROP_CHECK_BRACKETS,
 	PROP_HIGHLIGHT,
 	PROP_MAX_UNDO_LEVELS,
-	PROP_LANGUAGE
+	PROP_LANGUAGE,
+	PROP_FOLDS
 };
 
 struct _SyntaxDelimiter 
@@ -143,6 +144,7 @@ struct _GtkSourceBufferPrivate 
 
 	GtkSourceUndoManager  *undo_manager;
 	
+	gint                   enable_folds:1;
 	GList                 *folds;
 };
 
@@ -332,6 +334,14 @@ gtk_source_buffer_class_init (GtkSourceB
 							      GTK_TYPE_SOURCE_LANGUAGE,
 							      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));
+	
 	buffer_signals[CAN_UNDO] =
 	    g_signal_new ("can_undo",
 			  G_OBJECT_CLASS_TYPE (object_class),
@@ -414,6 +424,7 @@ gtk_source_buffer_init (GtkSourceBuffer 
 	priv->check_brackets = TRUE;
 	priv->bracket_mark = NULL;
 	priv->bracket_found = FALSE;
+	priv->enable_folds = FALSE;
 	priv->folds = NULL;
 	
 	priv->markers = g_array_new (FALSE, FALSE, sizeof (GtkSourceMarker *));
@@ -443,7 +454,6 @@ gtk_source_buffer_init (GtkSourceBuffer 
 			  "can_redo",
 			  G_CALLBACK (gtk_source_buffer_can_redo_handler),
 			  buffer);
-
 }
 
 static void 
@@ -641,6 +651,11 @@ gtk_source_buffer_set_property (GObject 
 							g_value_get_object (value));
 			break;
 			
+		case PROP_FOLDS:
+			gtk_source_buffer_set_folds (source_buffer,
+						     g_value_get_boolean (value));
+			break;
+			
 		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 			break;
@@ -681,6 +696,10 @@ gtk_source_buffer_get_property (GObject 
 		case PROP_LANGUAGE:
 			g_value_set_object (value, source_buffer->priv->language);
 			break;
+		
+		case PROP_FOLDS:
+			g_value_set_boolean (value, source_buffer->priv->enable_folds);
+			break;
 			
 		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -3739,6 +3758,43 @@ gtk_source_buffer_error_quark (void)
 	return q;
 }
 
+gboolean
+gtk_source_buffer_get_folds (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 (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");
+}
+
 /**
  * insert_child_fold:
  * @buffer: a #GtkSourceBuffer.
@@ -4084,12 +4140,6 @@ gtk_source_buffer_add_fold (GtkSourceBuf
 }
 
 static void
-foreach_fold_region (gpointer data, gpointer user_data)
-{
-	gtk_source_buffer_real_remove_fold (GTK_SOURCE_BUFFER (user_data), data);
-}
-
-static void
 gtk_source_buffer_real_remove_fold (GtkSourceBuffer *buffer,
 				    GtkSourceFold   *fold)
 {
@@ -4098,16 +4148,15 @@ gtk_source_buffer_real_remove_fold (GtkS
 	g_return_if_fail (GTK_IS_SOURCE_BUFFER (buffer));
 	g_return_if_fail (fold != NULL);
 	
-	l = g_list_find (buffer->priv->folds, fold);
-	g_return_if_fail (l != 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);
 	
-	buffer->priv->folds = g_list_delete_link (buffer->priv->folds, l);
+	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);
 
Index: gtksourceview/gtksourcebuffer.h
===================================================================
RCS file: /cvs/gnome/gtksourceview/gtksourceview/gtksourcebuffer.h,v
retrieving revision 1.18.14.2
diff -u -p -r1.18.14.2 gtksourcebuffer.h
--- gtksourceview/gtksourcebuffer.h	6 Nov 2005 20:41:03 -0000	1.18.14.2
+++ gtksourceview/gtksourcebuffer.h	15 Nov 2005 19:54:25 -0000
@@ -147,6 +147,10 @@ GtkSourceMarker         *gtk_source_buff
 								 GtkTextIter            *iter);
 
 /* fold methods. */
+gboolean		 gtk_source_buffer_get_folds            (GtkSourceBuffer        *buffer);
+void			 gtk_source_buffer_set_folds		(GtkSourceBuffer        *buffer,
+								 gboolean                enable_folds);
+
 GtkSourceFold		*gtk_source_buffer_add_fold		(GtkSourceBuffer        *buffer,
 								 const GtkTextIter      *begin,
 								 const GtkTextIter      *end);
Index: gtksourceview/gtksourcefold.c
===================================================================
RCS file: /cvs/gnome/gtksourceview/gtksourceview/Attic/gtksourcefold.c,v
retrieving revision 1.1.2.2
diff -u -p -r1.1.2.2 gtksourcefold.c
--- gtksourceview/gtksourcefold.c	30 Oct 2005 14:01:21 -0000	1.1.2.2
+++ gtksourceview/gtksourcefold.c	15 Nov 2005 19:54:25 -0000
@@ -37,8 +37,10 @@ _gtk_source_fold_new (GtkSourceBuffer   
 	
 	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;
 }
@@ -72,14 +74,12 @@ gtk_source_fold_free (GtkSourceFold *fol
 	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);
-	else
-		g_object_unref (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);
-	else
-		g_object_unref (fold->end_line);
+	g_object_unref (fold->end_line);
 
 	if (fold->children)
 		g_list_free (fold->children);
@@ -186,7 +186,7 @@ gtk_source_fold_set_folded (GtkSourceFol
 	if (folded)
 	{
 		GtkTextIter insert;
-	
+
 		gtk_text_buffer_apply_tag_by_name (buffer, INVISIBLE_LINE,
 						   &begin, &end);
 
@@ -244,12 +244,14 @@ gtk_source_fold_get_bounds (GtkSourceFol
 {
 	GtkTextBuffer *buffer;
 	
-	g_return_if_fail (fold != NULL && begin != NULL && end != NULL);
+	g_return_if_fail (fold != NULL);
 
 	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 (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);
 }
 
 /**
Index: gtksourceview/gtksourcefoldlabel.c
===================================================================
RCS file: gtksourceview/gtksourcefoldlabel.c
diff -N gtksourceview/gtksourcefoldlabel.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gtksourceview/gtksourcefoldlabel.c	15 Nov 2005 19:54:25 -0000
@@ -0,0 +1,159 @@
+/*  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
+};
+
+#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;
+};
+
+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;
+			
+		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;
+			
+		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));
+}
+
+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 GTK_WIDGET (g_object_new (GTK_TYPE_SOURCE_FOLD_LABEL,
+					 "label", "..",
+					 "sensitive", FALSE,
+					 "sourceview", view,
+					 NULL));
+}
Index: gtksourceview/gtksourcefoldlabel.h
===================================================================
RCS file: gtksourceview/gtksourcefoldlabel.h
diff -N gtksourceview/gtksourcefoldlabel.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gtksourceview/gtksourcefoldlabel.h	15 Nov 2005 19:54:25 -0000
@@ -0,0 +1,63 @@
+/*  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);
+
+G_END_DECLS
+
+#endif  /* __GTK_SOURCE_FOLD_LABEL_H__ */
Index: gtksourceview/gtksourceview.c
===================================================================
RCS file: /cvs/gnome/gtksourceview/gtksourceview/gtksourceview.c,v
retrieving revision 1.40
diff -u -p -r1.40 gtksourceview.c
--- gtksourceview/gtksourceview.c	29 Aug 2005 16:04:40 -0000	1.40
+++ gtksourceview/gtksourceview.c	15 Nov 2005 19:54:26 -0000
@@ -36,6 +36,8 @@
 #include "gtksourceview-i18n.h"
 #include "gtksourceview-marshal.h"
 #include "gtksourceview.h"
+#include "gtksourcefold-private.h"
+#include "gtksourcefoldlabel.h"
 
 /*
 #define ENABLE_DEBUG
@@ -58,6 +60,9 @@
 #define DEFAULT_MARGIN			80
 #define MAX_MARGIN			200
 
+#define DEFAULT_EXPANDER_SIZE		12
+#define EXPANDER_EXTRA_PADDING		4
+
 /* Signals */
 enum {
 	UNDO,
@@ -83,6 +88,7 @@ struct _GtkSourceViewPrivate
 {
 	guint		 tabs_width;
 	gboolean 	 show_line_numbers;
+	gint		 line_numbers_width;
 	gboolean	 show_line_markers;
 	gboolean	 auto_indent;
 	gboolean	 insert_spaces;
@@ -96,6 +102,14 @@ struct _GtkSourceViewPrivate
 
 	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;
 };
 
 /* Implement DnD for application/x-color drops */
@@ -127,6 +141,14 @@ static void 	set_source_buffer 			(GtkSo
 static void	gtk_source_view_populate_popup 		(GtkTextView        *view,
 					    		 GtkMenu            *menu);
 
+static gboolean gtk_source_view_motion			(GtkWidget          *widget,
+							 GdkEventMotion     *event);
+
+static gboolean gtk_source_view_button_press		(GtkWidget          *widget,
+							 GdkEventButton     *event);
+static gboolean gtk_source_view_button_release		(GtkWidget          *widget,
+							 GdkEventButton     *event);
+
 static void	gtk_source_view_move_cursor		(GtkTextView        *text_view,
 							 GtkMovementStep     step,
 							 gint                count,
@@ -140,6 +162,7 @@ static void 	gtk_source_view_get_lines 	
 				       			 gint               last_y,
 				       			 GArray            *buffer_coords,
 				       			 GArray            *numbers,
+				       			 GHashTable        *folds,
 				       			 gint              *countp);
 static gint     gtk_source_view_expose 			(GtkWidget         *widget,
 							 GdkEventExpose    *event);
@@ -189,8 +212,11 @@ gtk_source_view_class_init (GtkSourceVie
 	object_class->get_property = gtk_source_view_get_property;
 	object_class->set_property = gtk_source_view_set_property;
 
+	widget_class->button_press_event = gtk_source_view_button_press;
+	widget_class->button_release_event = gtk_source_view_button_release;
 	widget_class->key_press_event = key_press_event;
 	widget_class->expose_event = gtk_source_view_expose;
+	widget_class->motion_notify_event = gtk_source_view_motion;
 	widget_class->style_set = gtk_source_view_style_set;
 
 	textview_class->populate_popup = gtk_source_view_populate_popup;
@@ -276,6 +302,15 @@ gtk_source_view_class_init (GtkSourceVie
 							       FALSE,
 							       G_PARAM_READWRITE));
 
+	gtk_widget_class_install_style_property (widget_class,
+						 g_param_spec_int ("expander-size",
+								   _("Expander Size"),
+								   _("Size of the expander arrow"),
+								   0,
+								   G_MAXINT,
+								   DEFAULT_EXPANDER_SIZE,
+								   G_PARAM_READABLE));
+
 	signals [UNDO] =
 		g_signal_new ("undo",
 			      G_TYPE_FROM_CLASS (klass),
@@ -460,11 +495,16 @@ gtk_source_view_init (GtkSourceView *vie
 	view->priv->margin = DEFAULT_MARGIN;
 	view->priv->cached_margin_width = -1;
 	view->priv->smart_home_end = TRUE;
+	view->priv->prelight_fold_line = -1;
+	view->priv->fold_button_down = FALSE;
+	view->priv->line_numbers_width = 0;
 	
 	view->priv->pixmap_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
 							  (GDestroyNotify) g_free,
 							  (GDestroyNotify) g_object_unref);
 
+	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);
 
@@ -591,6 +631,32 @@ marker_updated_cb (GtkSourceBuffer *buff
 }
 
 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)
+{
+	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 (buffer);
+	
+	gtk_widget_queue_draw (GTK_WIDGET (view));
+}
+
+static void
 set_source_buffer (GtkSourceView *view, GtkTextBuffer *buffer)
 {
 	/* keep our pointer to the source buffer in sync with
@@ -607,6 +673,12 @@ set_source_buffer (GtkSourceView *view, 
 		g_signal_handlers_disconnect_by_func (view->priv->source_buffer,
 						      marker_updated_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_remove_weak_pointer (G_OBJECT (view->priv->source_buffer),
 					      (gpointer *) &view->priv->source_buffer);
 	}
@@ -624,6 +696,21 @@ set_source_buffer (GtkSourceView *view, 
 				  "marker_updated",
 				  G_CALLBACK (marker_updated_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 (view->priv->source_buffer);
 	}
 	else 
 	{
@@ -703,6 +790,269 @@ gtk_source_view_populate_popup (GtkTextV
 
 }
 
+static gboolean
+gtk_source_view_motion (GtkWidget *widget, GdkEventMotion *event)
+{
+	GtkSourceView *view;
+	int x, y, y_buf;
+	GtkTextIter line_start;
+	GtkSourceFold *fold;
+	
+	view = GTK_SOURCE_VIEW (widget);
+	
+	if (view->priv->show_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 (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 (parent_class)->motion_notify_event (widget, event);
+	}
+	else
+	{
+		return GTK_WIDGET_CLASS (parent_class)->motion_notify_event (widget, event);
+	}
+}
+
+static gboolean
+gtk_source_view_button_press (GtkWidget *widget, GdkEventButton *event)
+{
+	GtkSourceView *view;
+	int y_buf;
+	GtkTextIter line_start;
+	GtkSourceFold *fold;
+	
+	view = GTK_SOURCE_VIEW (widget);
+	
+	if (view->priv->show_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;
+	}
+	else
+	{
+		return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
+	}
+}
+
+static gboolean
+fold_animation_timeout (GtkSourceView *view)
+{
+	gboolean finish = FALSE;
+	GtkSourceFold *fold;
+
+	fold = _gtk_source_buffer_get_fold_at_line (view->priv->source_buffer,
+						    view->priv->animate_fold_line);
+	
+	if (fold == NULL)
+	{
+		view->priv->animation_timeout = 0;
+		return FALSE;
+	}
+
+	GDK_THREADS_ENTER ();
+
+	if (fold->folded)
+	{
+		if (fold->expander_style == GTK_EXPANDER_EXPANDED)
+		{
+			fold->expander_style = GTK_EXPANDER_SEMI_COLLAPSED;
+		}
+		else
+		{
+			fold->expander_style = GTK_EXPANDER_COLLAPSED;
+			finish = TRUE;
+		}
+	}
+	else
+	{
+		if (fold->expander_style == GTK_EXPANDER_COLLAPSED)
+		{
+			fold->expander_style = GTK_EXPANDER_SEMI_EXPANDED;
+		}
+		else
+		{
+			fold->expander_style = GTK_EXPANDER_EXPANDED;
+			finish = TRUE;
+		}
+	}
+
+	if (finish)
+	{
+		view->priv->animation_timeout = 0;
+		view->priv->animate_fold_line = -1;
+		fold->animated = FALSE;
+	}
+
+	gtk_widget_queue_draw (GTK_WIDGET (view));
+
+	GDK_THREADS_LEAVE ();
+
+	return !finish;
+}
+
+static void
+start_fold_animation (GtkSourceView *view)
+{
+	if (view->priv->animation_timeout)
+		g_source_remove (view->priv->animation_timeout);
+
+	view->priv->animation_timeout =
+		g_timeout_add (50, (GSourceFunc) fold_animation_timeout, view);
+}
+
+static gboolean
+gtk_source_view_button_release (GtkWidget *widget, GdkEventButton *event)
+{
+	GtkSourceView *view;
+	int y_buf;
+	GtkTextIter line_start;
+	GtkSourceFold *fold;
+	
+	view = GTK_SOURCE_VIEW (widget);
+	
+	if (view->priv->show_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 (parent_class)->button_release_event (widget, event);
+	}
+}
+
 static void
 move_cursor (GtkTextView       *text_view,
 	     const GtkTextIter *new_location,
@@ -766,6 +1116,7 @@ gtk_source_view_move_cursor (GtkTextView
 		/* Find the iter of the last character on the line. */
 		if (!gtk_text_iter_ends_line (&cur))
 			gtk_text_iter_forward_to_line_end (&cur);
+
 		while (!gtk_text_iter_starts_line (&cur))
 		{
 			gunichar c;
@@ -815,41 +1166,96 @@ gtk_source_view_get_lines (GtkTextView  
 			   gint          last_y,
 			   GArray       *buffer_coords,
 			   GArray       *numbers,
+			   GHashTable   *fold_hash,
 			   gint         *countp)
 {
-	GtkTextIter iter;
+	GtkTextIter iter, iter2, start_fold;
+	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);
+	
+	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
-	*/
+	/* For each iter, get its location and add it to the arrays. Stop when
+	 * we pass last_y. */
 	count = 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);
+	}
+	
+	last_line_num = gtk_text_iter_get_line (&iter);
+	
   	while (!gtk_text_iter_is_end (&iter))
     	{
 		gint y, height;
-      
-		gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
 
+		gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
 		g_array_append_val (buffer_coords, y);
 		last_line_num = gtk_text_iter_get_line (&iter);
+
+		//g_message ("at line %d, %d", last_line_num, gtk_text_iter_get_line (&iter));
+
 		g_array_append_val (numbers, last_line_num);
+
+		/* check if there's a fold on the line. */
+		if (fold != NULL && gtk_text_iter_get_line (&iter) ==
+		    gtk_text_iter_get_line (&start_fold))
+		{
+			//g_message ("fold @ %d", last_line_num + 1);
+			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);
+				//g_message ("next fold @ %d", gtk_text_iter_get_line (&start_fold));
+			}
+			else
+			{
+				fold = NULL;
+			}
+		}
       	
 		++count;
 
 		if ((y + height) >= last_y)
 			break;
-      
-		gtk_text_iter_forward_line (&iter);
+
+		gtk_text_iter_forward_visible_line (&iter);
 	}
 
 	if (gtk_text_iter_is_end (&iter))
@@ -869,6 +1275,9 @@ gtk_source_view_get_lines (GtkTextView  
 		}
 	}
 
+	if (l != NULL)
+		g_list_free (l);
+
 	*countp = count;
 }
 
@@ -877,7 +1286,8 @@ draw_line_markers (GtkSourceView *view,
 		   GSList        *current_marker,
 		   gint          *line_number,
 		   gint           x,
-		   gint           y)
+		   gint           y,
+		   gint           line_height)
 {
 	GdkPixbuf *pixbuf, *composite;
 	GtkSourceMarker *marker;
@@ -948,7 +1358,7 @@ draw_line_markers (GtkSourceView *view,
 						   GTK_TEXT_WINDOW_LEFT);
 
 		gdk_draw_pixbuf (GDK_DRAWABLE (window), NULL, composite,
-				 0, 0, x, y,
+				 0, 0, x - width, y - ((height - line_height) / 2),
 				 width, height,
 				 GDK_RGB_DITHER_NORMAL, 0, 0);
 		g_object_unref (composite);
@@ -958,9 +1368,102 @@ draw_line_markers (GtkSourceView *view,
 }
 
 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 void
+move_fold_label (GtkTextView        *view,
+		 GtkSourceFold      *fold,
+		 GtkWidget          *label)
+{
+	GtkTextIter begin;
+	GdkRectangle rect;
+	int x, y;
+	
+	gtk_source_fold_get_bounds (fold, &begin, NULL);
+
+	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_text_view_move_child (view, label, x + 2, y);
+}
+
+static void
 gtk_source_view_paint_margin (GtkSourceView *view,
 			      GdkEventExpose *event)
 {
+	GtkWidget *widget;
 	GtkTextView *text_view;
 	GdkWindow *win;
 	PangoLayout *layout;
@@ -972,14 +1475,19 @@ gtk_source_view_paint_margin (GtkSourceV
 	gint y1, y2;
 	gint count;
 	gint margin_width;
-	gint text_width, x_pixmap;
+	gint text_width, text_height, x_pixmap;
 	gint i;
 	GtkTextIter cur;
 	gint cur_line;
+	GHashTable *folds;
+	GtkSourceFold *fold;
 
+	widget = GTK_WIDGET (view);
 	text_view = GTK_TEXT_VIEW (view);
 
-	if (!view->priv->show_line_numbers && !view->priv->show_line_markers)
+	if (!view->priv->show_line_numbers &&
+	    !view->priv->show_line_markers &&
+	    !view->priv->show_folds)
 	{
 		gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (text_view),
 						      GTK_TEXT_WINDOW_LEFT,
@@ -1011,6 +1519,7 @@ gtk_source_view_paint_margin (GtkSourceV
 
 	numbers = g_array_new (FALSE, FALSE, sizeof (gint));
 	pixels = g_array_new (FALSE, FALSE, sizeof (gint));
+	folds = g_hash_table_new (g_direct_hash, g_direct_equal);
 
 	/* get the line numbers and y coordinates. */
 	gtk_source_view_get_lines (text_view,
@@ -1018,8 +1527,9 @@ gtk_source_view_paint_margin (GtkSourceV
 				   y2,
 				   pixels,
 				   numbers,
+				   folds,
 				   &count);
-
+	
 	/* A zero-lined document should display a "1"; we don't need to worry about
 	scrolling effects of the text widget in this special case */
 	
@@ -1043,7 +1553,7 @@ gtk_source_view_paint_margin (GtkSourceV
 		    "%d", MAX (99, gtk_text_buffer_get_line_count (text_view->buffer)));
 	layout = gtk_widget_create_pango_layout (GTK_WIDGET (view), str);
 
-	pango_layout_get_pixel_size (layout, &text_width, NULL);
+	pango_layout_get_pixel_size (layout, &text_width, &text_height);
 	
 	pango_layout_set_width (layout, text_width);
 	pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT);
@@ -1054,11 +1564,16 @@ gtk_source_view_paint_margin (GtkSourceV
 	else
 		margin_width = 0;
 
-	x_pixmap = margin_width;
-	
-	if (view->priv->show_line_markers)
+	view->priv->line_numbers_width = margin_width;
+
+	if (view->priv->show_line_markers && !view->priv->show_line_numbers)
 		margin_width += GUTTER_PIXMAP;
 
+	x_pixmap = margin_width;
+
+	if (view->priv->show_folds)
+		margin_width += view->priv->expander_size;
+
 	g_return_if_fail (margin_width != 0);
 	
 	gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (text_view),
@@ -1097,16 +1612,29 @@ gtk_source_view_paint_margin (GtkSourceV
 		marker_line = gtk_source_marker_get_line (
 			GTK_SOURCE_MARKER (current_marker->data));
 	
-	
 	gtk_text_buffer_get_iter_at_mark (text_view->buffer, 
 					  &cur, 
 					  gtk_text_buffer_get_insert (text_view->buffer));
 
 	cur_line = gtk_text_iter_get_line (&cur) + 1;
 	
+	/* 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);
+	}
+	
 	while (i < count) 
 	{
-		gint pos;
+		gint pos, line;
+		gboolean markers_present;
 		
 		gtk_text_view_buffer_to_window_coords (text_view,
 						       GTK_TEXT_WINDOW_LEFT,
@@ -1115,10 +1643,28 @@ gtk_source_view_paint_margin (GtkSourceV
 						       NULL,
 						       &pos);
 
-		if (view->priv->show_line_numbers) 
+		line = g_array_index (numbers, gint, i);
+
+		/* Advance to the next mark if we are past the current one. This
+		 * happens when a mark is inside a folded sourcefold.
+		 */
+		if (current_marker && marker_line < line)
 		{
-			gint line_to_paint = g_array_index (numbers, gint, i) + 1;
-			
+			current_marker = g_slist_next (current_marker);
+			if (current_marker)
+				marker_line = gtk_source_marker_get_line (
+					GTK_SOURCE_MARKER (current_marker->data));
+		}
+
+		markers_present = view->priv->show_line_markers &&
+				  current_marker &&
+				  marker_line == line;
+
+		/* Either draw the line number or marker; don't draw both. */
+		if (view->priv->show_line_numbers && !markers_present)
+		{
+			gint line_to_paint = line + 1;
+		
 			if (line_to_paint == cur_line)
 			{
 				gchar *markup;
@@ -1147,29 +1693,84 @@ gtk_source_view_paint_margin (GtkSourceV
 					  pos,
 					  layout);
 		}
-
-		if (view->priv->show_line_markers && current_marker) 
+		else if (markers_present)
+		{
+			/* draw markers for the line */
+			current_marker = draw_line_markers (view,
+							    current_marker,
+							    &marker_line,
+							    x_pixmap,
+							    pos,
+							    text_height);
+		}
+		
+		if (view->priv->show_folds && g_hash_table_size (folds) > 0)
 		{
-			if (marker_line == g_array_index (numbers, gint, i))
+			fold = g_hash_table_lookup (folds, GINT_TO_POINTER (line));
+			
+			if (fold != NULL)
 			{
-				/* draw markers for the line */
-				current_marker = draw_line_markers (view,
-								    current_marker,
-								    &marker_line,
-								    x_pixmap,
-								    pos);
+				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)
+				{
+					gtk_widget_hide (fold_label);
+				}
+				else if (fold_label != NULL)
+				{
+					move_fold_label (text_view, fold, fold_label);
+					
+					gtk_widget_show (fold_label);
+				}
+				else 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);
+					
+					gtk_widget_show (fold_label);
+				}
 			}
 		}
 
 		++i;
 	}
 
-	/* we should have used all markers */
-	g_assert (current_marker == NULL);
-	
 	g_slist_free (markers);
 	g_array_free (pixels, TRUE);
 	g_array_free (numbers, TRUE);
+	g_hash_table_destroy (folds);
 
 	g_object_unref (G_OBJECT (layout));
 }
@@ -1195,7 +1796,7 @@ gtk_source_view_expose (GtkWidget      *
 	}
 	
 	/* check if the expose event is for the text window first, and
-	 * make sure the visible region is highlighted */
+	 * make sure the visible region is prelighted */
 	if (event->window == gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT) &&
 	    view->priv->source_buffer != NULL) 
 	{
@@ -1226,7 +1827,6 @@ gtk_source_view_expose (GtkWidget      *
 	{
 		gint lines;
 
-		/* FIXME: could it be a performances problem? - Paolo */
 		lines = gtk_text_buffer_get_line_count (text_view->buffer);
 
 		if (view->priv->old_lines != lines)
@@ -2174,6 +2774,12 @@ gtk_source_view_style_set (GtkWidget *wi
 		(* GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
 
 	view = GTK_SOURCE_VIEW (widget);
+	
+	gtk_widget_style_get (widget,
+			      "expander-size", &view->priv->expander_size,
+			      NULL);
+	//view->priv->expander_size += EXPANDER_EXTRA_PADDING;
+  
 	if (previous_style)
 	{
 		/* If previous_style is NULL this is the initial
@@ -2187,3 +2793,20 @@ gtk_source_view_style_set (GtkWidget *wi
 	}
 }
 
+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);
+	}
+}
Index: gtksourceview/gtksourceview.h
===================================================================
RCS file: /cvs/gnome/gtksourceview/gtksourceview/gtksourceview.h,v
retrieving revision 1.10
diff -u -p -r1.10 gtksourceview.h
--- gtksourceview/gtksourceview.h	8 Jan 2005 22:02:30 -0000	1.10
+++ gtksourceview/gtksourceview.h	15 Nov 2005 19:54:26 -0000
@@ -39,7 +39,6 @@ G_BEGIN_DECLS
 #define GTK_IS_SOURCE_VIEW_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_SOURCE_VIEW))
 #define GTK_SOURCE_VIEW_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_SOURCE_VIEW, GtkSourceViewClass))
 
-
 typedef struct _GtkSourceView GtkSourceView;
 typedef struct _GtkSourceViewClass GtkSourceViewClass;
 
@@ -69,7 +68,7 @@ GType		 gtk_source_view_get_type 		(void
 
 /* Constructors */
 GtkWidget 	*gtk_source_view_new 			(void);
-GtkWidget 	*gtk_source_view_new_with_buffer	(GtkSourceBuffer *buffer);
+GtkWidget 	*gtk_source_view_new_with_buffer		(GtkSourceBuffer *buffer);
 
 /* Properties */
 void 		 gtk_source_view_set_show_line_numbers 	(GtkSourceView   *view,
@@ -80,7 +79,7 @@ void 		 gtk_source_view_set_show_line_ma
 							 gboolean         show);
 gboolean	 gtk_source_view_get_show_line_markers  (GtkSourceView   *view);
 
-void 		 gtk_source_view_set_tabs_width 	(GtkSourceView   *view, 
+void 		 gtk_source_view_set_tabs_width		(GtkSourceView   *view, 
 							 guint            width);
 guint            gtk_source_view_get_tabs_width         (GtkSourceView   *view);
 
Index: tests/test-widget.c
===================================================================
RCS file: /cvs/gnome/gtksourceview/tests/test-widget.c,v
retrieving revision 1.11
diff -u -p -r1.11 test-widget.c
--- tests/test-widget.c	8 Jan 2005 22:02:30 -0000	1.11
+++ tests/test-widget.c	15 Nov 2005 19:54:27 -0000
@@ -58,6 +58,8 @@ static void       numbers_toggled_cb    
 						  gpointer         user_data);
 static void       markers_toggled_cb             (GtkAction       *action,
 						  gpointer         user_data);
+static void       folds_toggled_cb               (GtkAction       *action,
+						  gpointer         user_data);
 static void       margin_toggled_cb              (GtkAction       *action,
 						  gpointer         user_data);
 static void       hl_line_toggled_cb             (GtkAction       *action,
@@ -102,6 +104,9 @@ static GtkToggleActionEntry toggle_entri
 	{ "ShowMarkers", NULL, "Show _Markers", NULL,
 	  "Toggle visibility of markers in the left margin",
 	  G_CALLBACK (markers_toggled_cb), FALSE },
+	{ "ShowFolds", NULL, "Show _Folds", NULL,
+	  "Toggle visibility of line folds left margin",
+	  G_CALLBACK (folds_toggled_cb), FALSE },
 	{ "ShowMargin", NULL, "Show M_argin", NULL,
 	  "Toggle visibility of right margin indicator",
 	  G_CALLBACK (margin_toggled_cb), FALSE },
@@ -135,6 +140,7 @@ static const gchar *view_ui_description 
 "      <separator/>"
 "      <menuitem action=\"ShowNumbers\"/>"
 "      <menuitem action=\"ShowMarkers\"/>"
+"      <menuitem action=\"ShowFolds\"/>"
 "      <menuitem action=\"ShowMargin\"/>"
 "      <menuitem action=\"HlLine\"/>"
 "      <separator/>"
@@ -390,7 +396,7 @@ finished_cb (GtkSourcePrintJob *job, gpo
 
 	g_print ("\n");
 	gjob = gtk_source_print_job_get_print_job (job);
-	preview = gnome_print_job_preview_new (gjob, "test-widget print preview");
+	preview = gnome_print_job_preview_new (gjob, (guchar *)"test-widget print preview");
  	g_object_unref (gjob); 
  	g_object_unref (job);
 	
@@ -419,6 +425,18 @@ markers_toggled_cb (GtkAction *action, g
 }
 
 static void
+folds_toggled_cb (GtkAction *action, gpointer user_data)
+{
+	GtkSourceBuffer *buffer;
+
+	g_return_if_fail (GTK_IS_TOGGLE_ACTION (action) && GTK_IS_SOURCE_VIEW (user_data));
+
+	buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (user_data)));
+	gtk_source_buffer_set_folds (buffer,
+		gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
+}
+
+static void
 margin_toggled_cb (GtkAction *action, gpointer user_data)
 {
 	g_return_if_fail (GTK_IS_TOGGLE_ACTION (action) && GTK_IS_SOURCE_VIEW (user_data));
@@ -702,7 +720,8 @@ button_press_cb (GtkWidget *widget, GdkE
 					     NULL);
 		
 		line_end = line_start;
-		gtk_text_iter_forward_to_line_end (&line_end);
+		if (!gtk_text_iter_ends_line (&line_end))
+			gtk_text_iter_forward_to_line_end (&line_end);
 
 		/* get the markers already in the line */
 		marker_list = gtk_source_buffer_get_markers_in_region (buffer,
@@ -755,6 +774,7 @@ create_view_window (GtkSourceBuffer *buf
 	GtkActionGroup *action_group;
 	GtkUIManager *ui_manager;
 	GError *error;
+	GtkIconTheme *theme;
 
 	g_return_val_if_fail (GTK_IS_SOURCE_BUFFER (buffer), NULL);
 	g_return_val_if_fail (from == NULL || GTK_IS_SOURCE_VIEW (from), NULL);
@@ -767,11 +787,12 @@ create_view_window (GtkSourceBuffer *buf
 
 	/* view */
 	view = gtk_source_view_new_with_buffer (buffer);
+	g_object_set_data (G_OBJECT (window), "view", view);
 	
 	g_signal_connect (buffer, "mark_set", G_CALLBACK (move_cursor_cb), view);
 	g_signal_connect (buffer, "changed", G_CALLBACK (update_cursor_position), view);
-	g_signal_connect (view, "button-press-event", G_CALLBACK (button_press_cb), NULL);
 	g_signal_connect (window, "delete-event", (GCallback) window_deleted_cb, view);
+	g_signal_connect_after (view, "button-press-event", G_CALLBACK (button_press_cb), NULL);
 
 	/* action group and UI manager */
 	action_group = gtk_action_group_new ("ViewActions");
@@ -805,6 +826,9 @@ create_view_window (GtkSourceBuffer *buf
 	/* misc widgets */
 	vbox = gtk_vbox_new (0, FALSE);
 	sw = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+					GTK_POLICY_AUTOMATIC,
+					GTK_POLICY_AUTOMATIC);
 	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
                                              GTK_SHADOW_IN);
 	pos_label = gtk_label_new ("Position");
@@ -819,7 +843,7 @@ create_view_window (GtkSourceBuffer *buf
 	gtk_box_pack_start (GTK_BOX (vbox), pos_label, FALSE, FALSE, 0);
 
 	/* setup view */
-	font_desc = pango_font_description_from_string ("monospace 10");
+	font_desc = pango_font_description_from_string ("Bitstream Vera Sans Mono 8");
 	if (font_desc != NULL)
 	{
 		gtk_widget_modify_font (view, font_desc);
@@ -840,6 +864,10 @@ create_view_window (GtkSourceBuffer *buf
 		gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
 					      gtk_source_view_get_show_line_markers (from));
 		
+		action = gtk_action_group_get_action (action_group, "ShowFolds");
+		gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
+					      gtk_source_buffer_get_folds (buffer));
+		
 		action = gtk_action_group_get_action (action_group, "ShowMargin");
 		gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action),
 					      gtk_source_view_get_show_margin (from));
@@ -865,9 +893,13 @@ create_view_window (GtkSourceBuffer *buf
 		g_free (tmp);
 	}
 
+	theme = gtk_icon_theme_get_default ();
+
 	/* add marker pixbufs */
-	error = NULL;	
-	if ((pixbuf = gdk_pixbuf_new_from_file (DATADIR "/pixmaps/apple-green.png", &error)))
+	error = NULL;
+	pixbuf = gtk_icon_theme_load_icon (theme, GTK_STOCK_STOP,
+					   GTK_ICON_SIZE_MENU, 0, &error);
+	if (pixbuf != NULL)
 	{
 		gtk_source_view_set_marker_pixbuf (GTK_SOURCE_VIEW (view), MARKER_TYPE_1, pixbuf);
 		g_object_unref (pixbuf);
@@ -877,10 +909,12 @@ create_view_window (GtkSourceBuffer *buf
 		g_message ("could not load marker 1 image.  Spurious messages might get triggered: %s",
 		error->message);
 		g_error_free (error);
-	} 
+	}
 	
 	error = NULL;
-	if ((pixbuf = gdk_pixbuf_new_from_file (DATADIR "/pixmaps/apple-red.png", &error)))
+	pixbuf = gtk_icon_theme_load_icon (theme, GTK_STOCK_GO_FORWARD,
+					   GTK_ICON_SIZE_MENU, 0, &error);
+	if (pixbuf != NULL)
 	{
 		gtk_source_view_set_marker_pixbuf (GTK_SOURCE_VIEW (view), MARKER_TYPE_2, pixbuf);
 		g_object_unref (pixbuf);
@@ -937,6 +971,9 @@ create_main_window (GtkSourceBuffer *buf
 	action = gtk_action_group_get_action (action_group, "ShowMarkers");
 	gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
 
+	action = gtk_action_group_get_action (action_group, "ShowFolds");
+	gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
+
 	action = gtk_action_group_get_action (action_group, "ShowMargin");
 	gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE);
 
@@ -977,6 +1014,8 @@ main (int argc, char *argv[])
 	GtkWidget *window;
 	GtkSourceLanguagesManager *lm;
 	GtkSourceBuffer *buffer;
+	GtkSourceView *view;
+	GtkTextIter begin, end;
 	
 	/* initialization */
 	gtk_init (&argc, &argv);
@@ -987,13 +1026,50 @@ main (int argc, char *argv[])
 	buffer = create_source_buffer (lm);
 	g_object_unref (lm);
 
-	if (argc > 1)
-		open_file (buffer, argv [1]);
-	else
-		open_file (buffer, "../gtksourceview/gtksourcebuffer.c");
-
 	/* create first window */
 	window = create_main_window (buffer);
+	view = GTK_SOURCE_VIEW (g_object_get_data (G_OBJECT (window), "view"));
+
+	if (argc > 1) {
+		open_file (buffer, argv [1]);
+	} else {
+		open_file (buffer, "test.c");
+
+		gtk_source_buffer_set_folds (buffer, TRUE);
+
+		/* main() fold. */		
+		gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer), &begin, 33);
+		gtk_text_iter_forward_to_line_end (&begin);
+		gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer), &end, 67);
+		gtk_source_buffer_add_fold (buffer, &begin, &end);
+
+		/* doit() fold. */		
+		gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer), &begin, 18);
+		gtk_text_iter_forward_to_line_end (&begin);
+		gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer), &end, 22);
+		gtk_source_fold_set_folded (
+			gtk_source_buffer_add_fold (buffer, &begin, &end),
+			TRUE);
+
+		/* nested while (...) fold. */
+		gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer), &begin, 59);
+		gtk_text_iter_forward_to_line_end (&begin);
+		gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer), &end, 62);
+		gtk_source_buffer_add_fold (buffer, &begin, &end);
+		
+		/* nested if (...) fold. */
+		gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer), &begin, 51);
+		gtk_text_iter_forward_to_line_end (&begin);
+		gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer), &end, 54);
+		gtk_source_buffer_add_fold (buffer, &begin, &end);
+		
+		/* nested if (DEBUG) fold. This should cause a reparent of the previous fold. */
+		gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer), &begin, 50);
+		gtk_text_iter_forward_to_line_end (&begin);
+		gtk_text_buffer_get_iter_at_line (GTK_TEXT_BUFFER (buffer), &end, 55);
+		gtk_source_buffer_add_fold (buffer, &begin, &end);
+	}
+
 	gtk_window_set_default_size (GTK_WINDOW (window), 500, 500);
 	gtk_widget_show (window);
 
Index: tests/test.c
===================================================================
RCS file: tests/test.c
diff -N tests/test.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/test.c	15 Nov 2005 19:54:27 -0000
@@ -0,0 +1,67 @@
+/*
+ * This is a test of gtksourceview's capabilities. Notice the new highlighting
+ * engine that supports sub-languages like GTK+-like @ comments.
+ */
+
+#include <gtk/gtk.h>
+
+#if 0
+
+#if GTK_SOURCE_VIEW_IS_SMART_ABOUT_NESTED_IFDEFS
+#define HOORAY
+#else
+#define GET_A_REAL_EDITOR
+#endif
+
+#endif /* #if 0 */
+
+static void
+doit (void)
+{
+	g_message ("Just do it!");
+}
+
+/**
+ * main:
+ * @argc: the number of command-line arguments given to the program + 1.
+ * @argv: the arguments specified on the command-line.
+ *
+ * Displays a basic #GtkWindow.
+ *
+ * Return value: 0 if the program completed succesfully.
+ **/
+int
+main (int argc, char *argv[])
+{
+	GtkWidget *window;
+	gchar *title;
+	
+	gtk_init (&argc, &argv);
+	
+	title = "GtkSourceView test";
+	
+	/* There should be a breakpoint on the next line. */
+	window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+	
+	/* Try hovering over "title" to see it's value (tooltip). */
+	gtk_window_set_title (GTK_WINDOW (window), title);
+	
+	gtk_window_set_default_size (GTK_WINDOW (window), 500, 500);
+	
+	if (DEBUG) {
+		if (argc > 1) {
+			g_message ("%d arguments where given at startup", argc - 1);
+		}
+	}
+	
+	/* The next line is bookmarked. */
+	gtk_widget_show_all (window);
+
+	while (gtk_events_pending ()) {
+		gtk_main_iteration ();
+	}
+
+	gtk_main ();
+	
+	return 0;
+}
