815 lines
22 KiB
C++
815 lines
22 KiB
C++
/*
|
|
* moofold.c
|
|
*
|
|
* Copyright (C) 2004-2010 by Yevgen Muntyan <emuntyan@users.sourceforge.net>
|
|
* Copyright (C) 2008 by Daniel Poelzleithner <mooedit@poelzi.org>
|
|
*
|
|
* This file is part of medit. medit is free software; you can
|
|
* redistribute it and/or modify it under the terms of the
|
|
* GNU Lesser General Public License as published by the
|
|
* Free Software Foundation; either version 2.1 of the License,
|
|
* or (at your option) any later version.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with medit. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "mooedit/mootext-private.h"
|
|
#include "marshals.h"
|
|
|
|
|
|
#ifdef MOO_DEBUG
|
|
#define WANT_CHECKS 1
|
|
#else
|
|
#define WANT_CHECKS 0
|
|
#endif
|
|
|
|
static void moo_fold_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
static void moo_fold_finalize (GObject *object);
|
|
static void moo_fold_free_recursively (MooFold *fold);
|
|
|
|
static int _moo_fold_get_end (MooFold *fold);
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_PARENT,
|
|
PROP_NEXT,
|
|
PROP_PREV,
|
|
PROP_CHILDREN,
|
|
PROP_N_CHILDREN,
|
|
PROP_MARK_START,
|
|
PROP_MARK_END,
|
|
PROP_COLLAPSED
|
|
};
|
|
|
|
/* MOO_TYPE_FOLD */
|
|
G_DEFINE_TYPE (MooFold, moo_fold, G_TYPE_OBJECT)
|
|
|
|
static void
|
|
moo_fold_class_init (MooFoldClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
gobject_class->finalize = moo_fold_finalize;
|
|
|
|
gobject_class->get_property = moo_fold_get_property;
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_PARENT,
|
|
g_param_spec_object ("parent",
|
|
"parent",
|
|
"parent",
|
|
MOO_TYPE_FOLD,
|
|
G_PARAM_READABLE));
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_NEXT,
|
|
g_param_spec_object ("next",
|
|
"next",
|
|
"next",
|
|
MOO_TYPE_FOLD,
|
|
G_PARAM_READABLE));
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_PREV,
|
|
g_param_spec_object ("previous",
|
|
"previous",
|
|
"previous",
|
|
MOO_TYPE_FOLD,
|
|
G_PARAM_READABLE));
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_MARK_START,
|
|
g_param_spec_object ("mark_start",
|
|
"Start Marker",
|
|
"Marker of the start Marker",
|
|
MOO_TYPE_LINE_MARK,
|
|
G_PARAM_READABLE));
|
|
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_MARK_END,
|
|
g_param_spec_object ("mark_end",
|
|
"End Marker",
|
|
"Marker of the end Marker",
|
|
MOO_TYPE_LINE_MARK,
|
|
G_PARAM_READABLE));
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_CHILDREN,
|
|
g_param_spec_object ("children",
|
|
"Children",
|
|
"Children ordered by Line",
|
|
MOO_TYPE_FOLD,
|
|
G_PARAM_READABLE));
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_N_CHILDREN,
|
|
g_param_spec_int ("n_children",
|
|
"Children Number",
|
|
"Number of children",
|
|
0, G_MAXINT, 0,
|
|
G_PARAM_READABLE));
|
|
|
|
|
|
g_object_class_install_property (gobject_class,
|
|
PROP_COLLAPSED,
|
|
g_param_spec_boolean ("collapsed",
|
|
"Fold Collapsed",
|
|
"True if the fold is collapsed",
|
|
FALSE,
|
|
G_PARAM_READABLE));
|
|
}
|
|
|
|
static void
|
|
moo_fold_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
MooFold *fold = MOO_FOLD (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_PARENT:
|
|
g_value_set_object (value, fold->parent);
|
|
break;
|
|
|
|
case PROP_NEXT:
|
|
g_value_set_object (value, fold->next);
|
|
break;
|
|
|
|
case PROP_PREV:
|
|
g_value_set_object (value, fold->prev);
|
|
break;
|
|
|
|
case PROP_MARK_START:
|
|
g_value_set_object (value, fold->start);
|
|
break;
|
|
|
|
case PROP_CHILDREN:
|
|
g_value_set_object (value, fold->children);
|
|
break;
|
|
|
|
case PROP_N_CHILDREN:
|
|
g_value_set_int (value, fold->n_children);
|
|
break;
|
|
|
|
case PROP_MARK_END:
|
|
g_value_set_object (value, fold->end);
|
|
break;
|
|
|
|
case PROP_COLLAPSED:
|
|
g_value_set_boolean (value, fold->collapsed);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
moo_fold_init (MooFold *fold)
|
|
{
|
|
fold->deleted = TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
moo_fold_finalize (GObject *object)
|
|
{
|
|
MooFold *fold = MOO_FOLD (object);
|
|
|
|
if (!fold->deleted)
|
|
g_critical ("oops, crash pending...");
|
|
|
|
G_OBJECT_CLASS (moo_fold_parent_class)->finalize (object);
|
|
}
|
|
|
|
|
|
static void
|
|
moo_fold_free_recursively (MooFold *fold)
|
|
{
|
|
MooFold *child;
|
|
|
|
g_return_if_fail (MOO_IS_FOLD (fold));
|
|
|
|
fold->deleted = TRUE;
|
|
|
|
child = fold->children;
|
|
fold->children = NULL;
|
|
|
|
for ( ; child != NULL; child = child->next)
|
|
moo_fold_free_recursively (child);
|
|
|
|
if (fold->start)
|
|
g_object_unref (fold->start);
|
|
if (fold->end)
|
|
g_object_unref (fold->end);
|
|
g_object_unref (fold);
|
|
}
|
|
|
|
|
|
int
|
|
_moo_fold_get_start (MooFold *fold)
|
|
{
|
|
g_return_val_if_fail (MOO_IS_FOLD (fold), -1);
|
|
g_return_val_if_fail (!_moo_fold_is_deleted (fold), -1);
|
|
return moo_line_mark_get_line (fold->start);
|
|
}
|
|
|
|
|
|
static int
|
|
_moo_fold_get_end (MooFold *fold)
|
|
{
|
|
g_return_val_if_fail (MOO_IS_FOLD (fold), -1);
|
|
g_return_val_if_fail (!_moo_fold_is_deleted (fold), -1);
|
|
return moo_line_mark_get_line (fold->end);
|
|
}
|
|
|
|
|
|
gboolean
|
|
_moo_fold_is_deleted (MooFold *fold)
|
|
{
|
|
g_return_val_if_fail (MOO_IS_FOLD (fold), TRUE);
|
|
return fold->deleted != 0;
|
|
}
|
|
|
|
|
|
#if WANT_CHECKS
|
|
|
|
static void
|
|
CHECK_LIST_MEMBER__ (MooFold *list,
|
|
MooFold *fold)
|
|
{
|
|
while (list)
|
|
{
|
|
if (list == fold)
|
|
return;
|
|
list = list->next;
|
|
}
|
|
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
|
|
static void
|
|
CHECK_FOLD (MooFoldTree *tree,
|
|
MooFold *fold)
|
|
{
|
|
g_assert (tree != NULL);
|
|
g_assert (MOO_IS_FOLD (fold));
|
|
g_assert (!fold->deleted);
|
|
|
|
while (fold->parent)
|
|
{
|
|
CHECK_LIST_MEMBER__ (fold->parent->children, fold);
|
|
fold = fold->parent;
|
|
}
|
|
|
|
CHECK_LIST_MEMBER__ (tree->folds, fold);
|
|
}
|
|
|
|
#else /* WANT_CHECKS */
|
|
#define CHECK_FOLD(tree,fold)
|
|
#endif
|
|
|
|
|
|
MooFoldTree *
|
|
_moo_fold_tree_new (MooTextBuffer *buffer)
|
|
{
|
|
MooFoldTree *tree = g_new0 (MooFoldTree, 1);
|
|
|
|
tree->buffer = buffer;
|
|
tree->consistent = TRUE;
|
|
|
|
return tree;
|
|
}
|
|
|
|
|
|
void
|
|
_moo_fold_tree_free (MooFoldTree *tree)
|
|
{
|
|
MooFold *child;
|
|
|
|
g_return_if_fail (tree != NULL);
|
|
|
|
for (child = tree->folds; child != NULL; child = child->next)
|
|
moo_fold_free_recursively (child);
|
|
|
|
g_free (tree);
|
|
}
|
|
|
|
|
|
inline static int
|
|
get_line_count (MooFoldTree *tree)
|
|
{
|
|
return gtk_text_buffer_get_line_count (GTK_TEXT_BUFFER (tree->buffer));
|
|
}
|
|
|
|
|
|
static MooLineMark *
|
|
fold_mark_new (MooFold *fold)
|
|
{
|
|
MooLineMark *mark = MOO_LINE_MARK (g_object_new (MOO_TYPE_LINE_MARK, (const char*) NULL));
|
|
_moo_line_mark_set_fold (mark, fold);
|
|
return mark;
|
|
}
|
|
|
|
|
|
static MooFold *
|
|
insert_fold (MooFoldTree *tree,
|
|
MooFold *parent,
|
|
int first_line,
|
|
int last_line)
|
|
{
|
|
MooFold *fold, *last, *new_fold;
|
|
|
|
g_assert (!parent || MOO_IS_FOLD (parent));
|
|
g_assert (!parent || _moo_fold_get_start (parent) < first_line);
|
|
g_assert (!parent || _moo_fold_get_end (parent) >= last_line);
|
|
|
|
last = NULL;
|
|
|
|
for (fold = (parent ? parent->children : tree->folds); fold != NULL; fold = fold->next)
|
|
{
|
|
int start, end;
|
|
|
|
start = _moo_fold_get_start (fold);
|
|
end = _moo_fold_get_end (fold);
|
|
|
|
if (!fold->next)
|
|
last = fold;
|
|
|
|
if (end < first_line)
|
|
continue;
|
|
|
|
if (start > last_line)
|
|
break;
|
|
|
|
if (start == first_line || end == first_line || start == last_line)
|
|
return NULL;
|
|
|
|
if (start < first_line)
|
|
{
|
|
if (end < last_line)
|
|
return NULL;
|
|
|
|
/* new fold is inserted into the old one */
|
|
return insert_fold (tree, fold, first_line, last_line);
|
|
}
|
|
else /* start > first_line */
|
|
{
|
|
if (end > last_line)
|
|
return NULL;
|
|
|
|
/* old fold is inserted into the new one */
|
|
|
|
new_fold = MOO_FOLD (g_object_new (MOO_TYPE_FOLD, (const char*) NULL));
|
|
new_fold->deleted = FALSE;
|
|
|
|
if (parent)
|
|
{
|
|
if (!parent->children || fold == parent->children)
|
|
parent->children = new_fold;
|
|
}
|
|
else
|
|
{
|
|
if (!tree->folds || fold == tree->folds)
|
|
tree->folds = new_fold;
|
|
}
|
|
|
|
new_fold->parent = parent;
|
|
fold->parent = new_fold;
|
|
new_fold->children = fold;
|
|
new_fold->n_children = 1;
|
|
|
|
if (fold->next)
|
|
{
|
|
fold->next->prev = new_fold;
|
|
new_fold->next = fold->next;
|
|
fold->next = NULL;
|
|
}
|
|
|
|
if (fold->prev)
|
|
{
|
|
fold->prev->next = new_fold;
|
|
new_fold->prev = fold->prev;
|
|
fold->prev = NULL;
|
|
}
|
|
|
|
new_fold->start = fold_mark_new (new_fold);
|
|
new_fold->end = fold_mark_new (new_fold);
|
|
|
|
moo_text_buffer_add_line_mark (tree->buffer, new_fold->start, first_line);
|
|
moo_text_buffer_add_line_mark (tree->buffer, new_fold->end, last_line);
|
|
|
|
CHECK_FOLD (tree, new_fold);
|
|
return new_fold;
|
|
}
|
|
}
|
|
|
|
/* insert new fold before <fold> or in the end if <fold> is NULL */
|
|
|
|
new_fold = MOO_FOLD (g_object_new (MOO_TYPE_FOLD, (const char*) NULL));
|
|
new_fold->deleted = FALSE;
|
|
|
|
new_fold->parent = parent;
|
|
new_fold->next = fold;
|
|
|
|
if (parent)
|
|
{
|
|
if (fold == parent->children)
|
|
parent->children = new_fold;
|
|
parent->n_children++;
|
|
}
|
|
else
|
|
{
|
|
if (fold == tree->folds)
|
|
tree->folds = new_fold;
|
|
tree->n_folds++;
|
|
}
|
|
|
|
if (fold)
|
|
{
|
|
if (fold->prev)
|
|
new_fold->prev = fold->prev;
|
|
fold->prev = new_fold;
|
|
}
|
|
else if (last)
|
|
{
|
|
g_assert (last->next == NULL);
|
|
last->next = new_fold;
|
|
new_fold->prev = last;
|
|
}
|
|
|
|
new_fold->start = fold_mark_new (new_fold);
|
|
new_fold->end = fold_mark_new (new_fold);
|
|
|
|
moo_text_buffer_add_line_mark (tree->buffer, new_fold->start, first_line);
|
|
moo_text_buffer_add_line_mark (tree->buffer, new_fold->end, last_line);
|
|
|
|
CHECK_FOLD (tree, new_fold);
|
|
return new_fold;
|
|
}
|
|
|
|
|
|
|
|
MooFold *
|
|
_moo_fold_tree_add (MooFoldTree *tree,
|
|
int first_line,
|
|
int last_line)
|
|
{
|
|
g_assert (tree != NULL);
|
|
g_assert (first_line >= 0 && first_line < get_line_count (tree));
|
|
g_assert (last_line >= 0 && last_line < get_line_count (tree));
|
|
g_assert (last_line > first_line);
|
|
return insert_fold (tree, NULL, first_line, last_line);
|
|
}
|
|
|
|
|
|
static void
|
|
fold_free (MooFold *fold)
|
|
{
|
|
fold->deleted = TRUE;
|
|
|
|
if (fold->start)
|
|
{
|
|
_moo_line_mark_set_fold (fold->start, NULL);
|
|
|
|
if (!moo_line_mark_get_deleted (fold->start))
|
|
moo_text_buffer_delete_line_mark (moo_line_mark_get_buffer (fold->start),
|
|
fold->start);
|
|
|
|
g_object_unref (fold->start);
|
|
fold->start = NULL;
|
|
}
|
|
|
|
if (fold->end)
|
|
{
|
|
_moo_line_mark_set_fold (fold->end, NULL);
|
|
|
|
if (!moo_line_mark_get_deleted (fold->end))
|
|
moo_text_buffer_delete_line_mark (moo_line_mark_get_buffer (fold->end),
|
|
fold->end);
|
|
|
|
g_object_unref (fold->end);
|
|
fold->end = NULL;
|
|
}
|
|
|
|
g_object_unref (fold);
|
|
}
|
|
|
|
|
|
void
|
|
_moo_fold_tree_remove (MooFoldTree *tree,
|
|
MooFold *fold)
|
|
{
|
|
guint n_children;
|
|
MooFold *children, *parent, *last;
|
|
|
|
CHECK_FOLD (tree, fold);
|
|
|
|
_moo_fold_tree_expand (tree, fold);
|
|
|
|
n_children = fold->n_children;
|
|
last = children = fold->children;
|
|
parent = fold->parent;
|
|
|
|
if (children)
|
|
{
|
|
while (TRUE)
|
|
{
|
|
g_assert (last->parent == fold);
|
|
last->parent = fold->parent;
|
|
if (!last->next)
|
|
break;
|
|
}
|
|
}
|
|
|
|
g_assert ((last && children) || (!last && !children));
|
|
|
|
if (parent)
|
|
{
|
|
if (fold == parent->children)
|
|
parent->children = children ? children : parent->children->next;
|
|
parent->n_children = parent->n_children + n_children - 1;
|
|
}
|
|
else
|
|
{
|
|
if (fold == tree->folds)
|
|
tree->folds = children ? children : tree->folds->next;
|
|
tree->n_folds = tree->n_folds + n_children - 1;
|
|
}
|
|
|
|
if (last)
|
|
last->next = fold->next;
|
|
if (fold->next)
|
|
fold->next->prev = last ? last : fold->prev;
|
|
if (children)
|
|
children->prev = fold->prev;
|
|
if (fold->prev)
|
|
fold->prev->next = children ? children : fold->next;
|
|
|
|
fold_free (fold);
|
|
}
|
|
|
|
|
|
static void
|
|
expand_check_visible (MooFoldTree *tree,
|
|
MooFold *fold)
|
|
{
|
|
GtkTextIter start, end;
|
|
GtkTextBuffer *buffer;
|
|
MooFold *child;
|
|
|
|
CHECK_FOLD (tree, fold);
|
|
|
|
buffer = GTK_TEXT_BUFFER (tree->buffer);
|
|
gtk_text_buffer_get_iter_at_line (buffer, &start,
|
|
_moo_fold_get_start (fold));
|
|
end = start;
|
|
gtk_text_iter_forward_line (&end);
|
|
gtk_text_buffer_remove_tag_by_name (buffer, MOO_FOLD_TAG, &start, &end);
|
|
|
|
if (fold->collapsed)
|
|
return;
|
|
|
|
for (child = fold->children; child != NULL; child = child->next)
|
|
{
|
|
int start_line, end_line;
|
|
|
|
if (child->prev)
|
|
start_line = _moo_fold_get_end (child->prev) + 1;
|
|
else
|
|
start_line = _moo_fold_get_start (fold) + 1;
|
|
|
|
end_line = _moo_fold_get_start (child);
|
|
|
|
gtk_text_buffer_get_iter_at_line (buffer, &start, start_line);
|
|
gtk_text_buffer_get_iter_at_line (buffer, &end, end_line);
|
|
gtk_text_iter_forward_line (&end);
|
|
gtk_text_buffer_remove_tag_by_name (buffer, MOO_FOLD_TAG, &start, &end);
|
|
|
|
expand_check_visible (tree, child);
|
|
|
|
if (!child->next)
|
|
break;
|
|
}
|
|
|
|
if (child)
|
|
{
|
|
if (_moo_fold_get_end (child) < _moo_fold_get_end (fold))
|
|
{
|
|
gtk_text_buffer_get_iter_at_line (buffer, &start,
|
|
_moo_fold_get_end (child) + 1);
|
|
gtk_text_buffer_get_iter_at_line (buffer, &end,
|
|
_moo_fold_get_end (fold));
|
|
gtk_text_iter_forward_line (&end);
|
|
gtk_text_buffer_remove_tag_by_name (buffer, MOO_FOLD_TAG, &start, &end);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
start = end;
|
|
gtk_text_buffer_get_iter_at_line (buffer, &end,
|
|
_moo_fold_get_end (fold));
|
|
gtk_text_iter_forward_line (&end);
|
|
gtk_text_buffer_remove_tag_by_name (buffer, MOO_FOLD_TAG, &start, &end);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
_moo_fold_tree_expand (MooFoldTree *tree,
|
|
MooFold *fold)
|
|
{
|
|
CHECK_FOLD (tree, fold);
|
|
|
|
if (!fold->collapsed)
|
|
return;
|
|
|
|
fold->collapsed = FALSE;
|
|
expand_check_visible (tree, fold);
|
|
}
|
|
|
|
#define MOO_FOLD_FOREACH_BEGIN(start, iter) \
|
|
do { \
|
|
MooFold *iter = (start); \
|
|
while (iter) \
|
|
{
|
|
|
|
#define MOO_FOLD_FOREACH_END(iter) \
|
|
if (iter->children) \
|
|
iter = iter->children; \
|
|
else if (iter->next) \
|
|
iter = iter->next; \
|
|
else \
|
|
{ \
|
|
while (iter->parent && !iter->parent->next) \
|
|
iter = iter->parent; \
|
|
iter = iter && iter->parent ? \
|
|
iter->parent->next : NULL; \
|
|
} \
|
|
} \
|
|
} while (0)
|
|
|
|
gboolean
|
|
_moo_fold_tree_expand_all (MooFoldTree *tree)
|
|
{
|
|
gboolean ret = FALSE;
|
|
MooFold *fold_first;
|
|
|
|
fold_first = NULL;
|
|
|
|
MOO_FOLD_FOREACH_BEGIN(tree->folds, fold)
|
|
{
|
|
if (!fold->deleted && fold->collapsed)
|
|
{
|
|
if (!fold_first)
|
|
fold_first = fold;
|
|
fold->collapsed = FALSE;
|
|
}
|
|
}
|
|
MOO_FOLD_FOREACH_END(fold);
|
|
|
|
if (fold_first)
|
|
{
|
|
GtkTextIter start, end;
|
|
GtkTextBuffer *buffer = GTK_TEXT_BUFFER (tree->buffer);
|
|
int line_first;
|
|
|
|
line_first = _moo_fold_get_start (fold_first);
|
|
|
|
gtk_text_buffer_get_iter_at_line (buffer, &start, line_first);
|
|
gtk_text_buffer_get_end_iter (buffer, &end);
|
|
|
|
gtk_text_buffer_remove_tag_by_name (buffer, MOO_FOLD_TAG, &start, &end);
|
|
|
|
ret = TRUE;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
_moo_fold_tree_collapse (MooFoldTree *tree,
|
|
MooFold *fold)
|
|
{
|
|
GtkTextIter start, end;
|
|
GtkTextBuffer *buffer;
|
|
|
|
CHECK_FOLD (tree, fold);
|
|
|
|
if (fold->collapsed)
|
|
return;
|
|
|
|
fold->collapsed = TRUE;
|
|
|
|
buffer = GTK_TEXT_BUFFER (tree->buffer);
|
|
gtk_text_buffer_get_iter_at_line (buffer, &start,
|
|
_moo_fold_get_start (fold) + 1);
|
|
gtk_text_buffer_get_iter_at_line (buffer, &end,
|
|
_moo_fold_get_end (fold));
|
|
gtk_text_iter_forward_line (&end);
|
|
gtk_text_buffer_apply_tag_by_name (buffer, MOO_FOLD_TAG, &start, &end);
|
|
}
|
|
|
|
gboolean
|
|
_moo_fold_tree_collapse_all (MooFoldTree *tree)
|
|
{
|
|
MooFold *fold;
|
|
gboolean ret = FALSE;
|
|
|
|
for (fold = tree->folds; fold != NULL; fold = fold->next)
|
|
{
|
|
if (!fold->collapsed)
|
|
ret = TRUE;
|
|
_moo_fold_tree_collapse (tree, fold);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
gboolean
|
|
_moo_fold_tree_toggle (MooFoldTree *tree)
|
|
{
|
|
gboolean seen_expanded = FALSE;
|
|
gboolean seen_collapsed = FALSE;
|
|
|
|
MOO_FOLD_FOREACH_BEGIN(tree->folds, fold)
|
|
{
|
|
if (!fold->deleted)
|
|
{
|
|
if (fold->collapsed)
|
|
seen_collapsed = TRUE;
|
|
else
|
|
seen_expanded = TRUE;
|
|
}
|
|
|
|
if (seen_collapsed && seen_expanded)
|
|
break;
|
|
}
|
|
MOO_FOLD_FOREACH_END(fold);
|
|
|
|
if (seen_collapsed)
|
|
{
|
|
_moo_fold_tree_expand_all (tree);
|
|
return TRUE;
|
|
}
|
|
else if (seen_expanded)
|
|
{
|
|
_moo_fold_tree_collapse_all (tree);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static GSList *
|
|
get_folds_in_range (MooFoldTree *tree,
|
|
MooFold *parent,
|
|
int first_line,
|
|
int last_line,
|
|
GSList *list)
|
|
{
|
|
MooFold *child;
|
|
|
|
if (parent && first_line <= _moo_fold_get_start (parent) && last_line >= _moo_fold_get_start (parent))
|
|
list = g_slist_prepend (list, parent);
|
|
|
|
for (child = parent ? parent->children : tree->folds; child != NULL; child = child->next)
|
|
{
|
|
if (last_line < _moo_fold_get_start (child))
|
|
break;
|
|
if (first_line >= _moo_fold_get_end (child))
|
|
continue;
|
|
list = get_folds_in_range (tree, child, first_line, last_line, list);
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
|
|
GSList *
|
|
_moo_fold_tree_get (MooFoldTree *tree,
|
|
int first_line,
|
|
int last_line)
|
|
{
|
|
GSList *list;
|
|
|
|
g_assert (tree != NULL);
|
|
g_assert (first_line >= 0 && first_line < get_line_count (tree));
|
|
g_assert (last_line >= 0 && last_line < get_line_count (tree));
|
|
g_assert (last_line >= first_line);
|
|
|
|
list = get_folds_in_range (tree, NULL, first_line, last_line, NULL);
|
|
return g_slist_reverse (list);
|
|
}
|