diff options
author | Patrick McDermott <pj@pehjota.net> | 2017-10-23 02:27:43 (EDT) |
---|---|---|
committer | Patrick McDermott <pj@pehjota.net> | 2017-10-23 02:27:43 (EDT) |
commit | 08b956e032e3257e04a0dfc90496437b98a7a176 (patch) | |
tree | 6d9c8f84c6295035517907b96e711de9076c40d4 | |
parent | 4dc8025b8f2eb770280548198720cdbb5bd053d5 (diff) | |
download | marquee-08b956e032e3257e04a0dfc90496437b98a7a176.zip marquee-08b956e032e3257e04a0dfc90496437b98a7a176.tar.gz marquee-08b956e032e3257e04a0dfc90496437b98a7a176.tar.bz2 |
MqTree: New type
-rw-r--r-- | src/local.mk | 1 | ||||
-rw-r--r-- | src/tree.c | 226 | ||||
-rw-r--r-- | src/tree.h | 85 |
3 files changed, 312 insertions, 0 deletions
diff --git a/src/local.mk b/src/local.mk index 0748c45..fa1f6ca 100644 --- a/src/local.mk +++ b/src/local.mk @@ -8,6 +8,7 @@ marquee_SOURCES += \ %reldir%/notebook.c \ %reldir%/tab-label.c \ %reldir%/tab-page.c \ + %reldir%/tree.c \ %reldir%/web-settings.c \ %reldir%/web-view.c \ %reldir%/window.c diff --git a/src/tree.c b/src/tree.c new file mode 100644 index 0000000..618503d --- /dev/null +++ b/src/tree.c @@ -0,0 +1,226 @@ +/* + * Tree data structure + * + * Copyright (C) 2017 Patrick McDermott + * + * This file is part of Marquee. + * + * Marquee 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 3 of the License, or + * (at your option) any later version. + * + * Marquee 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 Marquee. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "tree.h" + +#include <glib.h> + +MqTree * +mq_tree_insert_root_allocated(MqTree *node, gpointer data) +{ + node->root = node; + node->data = data; + + return node; +} + +static void +update_sizes(MqTree *node, gint step) +{ + if (node) { + node->size += step; + update_sizes(node->parent, step); + } +} + +static void +update_positions(MqTree *node, gint step) +{ + for (; node; node = node->next) { + node->position += step; + if (node->first_child) { + update_positions(node->first_child, step); + } + if (node->parent && node->parent->next) { + update_positions(node->parent->next, step); + } + } +} + +MqTree * +mq_tree_insert_child_allocated(MqTree *node, MqTree *parent, gpointer data) +{ + /* Copy ancestor pointers. */ + node->root = parent->root; + node->parent = parent; + + if (parent->last_child) { + /* Parent has children, so get position from sibling and append + * to list of children. */ + node->position = parent->last_child->position; + node->prev = parent->last_child; + parent->last_child->next = node; + } else { + /* Parent has no children, so get position from parent and add + * first child to parent. */ + node->position = parent->position; + parent->first_child = node; + } + parent->last_child = node; + update_positions(node, 1); + + node->size = 0; + update_sizes(node, 1); + + node->data = data; + + return node; +} + +MqTree * +mq_tree_insert_sibling_allocated(MqTree *node, MqTree *sibling, gpointer data) +{ + /* Copy ancestor pointers. */ + node->root = sibling->root; + node->parent = sibling->parent; + + /* Link to next sibling, if any. */ + if (sibling->next) { + node->next = sibling->next; + sibling->next->prev = node; + } + + /* Link to previous sibling. */ + node->prev = sibling; + sibling->next = node; + + node->size = 0; + update_sizes(node, 1); + + node->position = sibling->position; + update_positions(node, 1); + + node->data = data; + + return node; +} + +void +mq_tree_remove(MqTree *node) +{ + /* TODO */ + node = node; +} + +MqTree * +mq_tree_seek(MqTree *node, gint offset) +{ + /* XXX: Only forward seeking from a tree's root is supported. */ + g_assert(node && node->position == 0); + g_assert(offset > 0); + + /* Skip forward to the containing subtree. */ + while (node && node->position + node->size <= offset) { + node = node->next; + } + + /* Check whether we've gone past the end of the tree. */ + if (!node) { + return NULL; + } + + /* Check whether the sibling we've reached is the node we want. */ + if (node->position == offset) { + return node; + } + + /* Recurse down the subtree. */ + return mq_tree_seek(node->first_child, offset); +} + +static gboolean +foreach(MqTree *node, gboolean (*cb)(MqTree *node, va_list ap), va_list ap) +{ + va_list aq; + gboolean ret; + + ret = MQ_TREE_CONTINUE; + for (; node; node = node->next) { + /* XXX: Warning: Does not skip root node */ + va_copy(ap, aq); + ret = cb(node, aq); + va_end(aq); + if (ret == MQ_TREE_STOP) { + break; + } + + if (node->first_child) { + va_copy(ap, aq); + ret = foreach(node->first_child, cb, aq); + va_end(aq); + if (ret == MQ_TREE_STOP) { + break; + } + } + + if (node->parent && node->parent->next) { + va_copy(ap, aq); + ret = foreach(node->parent->next, cb, aq); + va_end(aq); + if (ret == MQ_TREE_STOP) { + break; + } + } + } + + return ret; +} + +void +mq_tree_foreach(MqTree *node, gboolean (*cb)(MqTree *node, va_list ap), ...) +{ + va_list ap; + + va_start(ap, cb); + foreach(node->root->first_child, cb, ap); + va_end(ap); +} + +static gboolean +foreach_child(MqTree *node, gboolean (*cb)(MqTree *node, va_list ap), + va_list ap) +{ + va_list aq; + gboolean ret; + + ret = MQ_TREE_CONTINUE; + for (node = node->first_child; node; node = node->next) { + va_copy(ap, aq); + ret = cb(node, aq); + va_end(aq); + if (ret == MQ_TREE_STOP) { + break; + } + } + + return ret; +} + +void +mq_tree_foreach_child(MqTree *node, gboolean (*cb)(MqTree *node, va_list ap), + ...) +{ + va_list ap; + + va_start(ap, cb); + foreach_child(node->root->first_child, cb, ap); + va_end(ap); +} diff --git a/src/tree.h b/src/tree.h new file mode 100644 index 0000000..2fb41fe --- /dev/null +++ b/src/tree.h @@ -0,0 +1,85 @@ +/* + * Tree data structure + * + * Copyright (C) 2017 Patrick McDermott + * + * This file is part of Marquee. + * + * Marquee 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 3 of the License, or + * (at your option) any later version. + * + * Marquee 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 Marquee. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef MQ_TREE_H +#define MQ_TREE_H + +typedef struct _MqTree MqTree; + +#include <glib.h> + +G_BEGIN_DECLS + +#define MQ_TREE(obj) (MqTree *) (obj) + +#define MQ_TREE_CONTINUE TRUE +#define MQ_TREE_STOP FALSE + +struct _MqTree { + MqTree *root; + MqTree *parent; + MqTree *prev; + MqTree *next; + MqTree *first_child; + MqTree *last_child; + gint size; + gint position; + gpointer data; +}; + +MqTree * +mq_tree_insert_root_allocated(MqTree *node, gpointer data); + +MqTree * +mq_tree_insert_child_allocated(MqTree *node, MqTree *parent, gpointer data); + +MqTree * +mq_tree_insert_sibling_allocated(MqTree *node, MqTree *sibling, gpointer data); + +#define mq_tree_insert_root(data) \ + mq_tree_insert_root_allocated(g_new0(MqTree, 1), data) +#define mq_tree_insert_child(parent, data) \ + mq_tree_insert_child_allocated(g_new0(MqTree, 1), parent, data) +#define mq_tree_insert_sibling(sibling, data) \ + mq_tree_insert_sibling_allocated(g_new0(MqTree, 1), sibling, data) + +void +mq_tree_remove(MqTree *node); + +#define mq_tree_size(node) ((node) ? (((MqTree *) (node))->size) : NULL) +#define mq_tree_position(node) ((node) ? (((MqTree *) (node))->position) : NULL) +#define mq_tree_root(node) ((node) ? (((MqTree *) (node))->root) : NULL) +#define mq_tree_previous(node) ((node) ? (((MqTree *) (node))->prev) : NULL) +#define mq_tree_next(node) ((node) ? (((MqTree *) (node))->prev) : NULL) + +MqTree * +mq_tree_seek(MqTree *node, gint offset); + +void +mq_tree_foreach(MqTree *node, gboolean (*cb)(MqTree *node, va_list ap), ...); + +void +mq_tree_foreach_child(MqTree *node, gboolean (*cb)(MqTree *node, va_list ap), + ...); + +G_END_DECLS + +#endif /* MQ_TREE_H */ |