gtksourceview r1997 - in branches/code-folding-2: . gtksourceview tests
- From: muntyan svn gnome org
- To: svn-commits-list gnome org
- Subject: gtksourceview r1997 - in branches/code-folding-2: . gtksourceview tests
- Date: Sun, 3 Aug 2008 23:03:52 +0000 (UTC)
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]