diff options
Diffstat (limited to 'src/layer-fns.cpp')
-rw-r--r-- | src/layer-fns.cpp | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/src/layer-fns.cpp b/src/layer-fns.cpp new file mode 100644 index 0000000..81c722d --- /dev/null +++ b/src/layer-fns.cpp @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Inkscape::SelectionDescriber - shows messages describing selection + * + * Authors: + * MenTaLguY <mental@rydia.net> + * Jon A. Cruz <jon@joncruz.org> + * + * Copyright (C) 2004 MenTaLguY + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "document.h" +#include "layer-fns.h" + +#include "object/sp-item-group.h" + +#include "util/find-last-if.h" + +#include "xml/repr.h" + +// TODO move the documentation comments into the .h file + +namespace Inkscape { + +namespace { + +static bool is_layer(SPObject &object) { + return SP_IS_GROUP(&object) && + SP_GROUP(&object)->layerMode() == SPGroup::LAYER; +} + +/** Finds the next sibling layer for a \a layer + * + * @returns NULL if there are no further layers under a parent + */ +static SPObject *next_sibling_layer(SPObject *layer) { + if (layer->parent == nullptr) { + return nullptr; + } + SPObject::ChildrenList &list = layer->parent->children; + auto l = std::find_if(++list.iterator_to(*layer), list.end(), &is_layer); + return l != list.end() ? &*l : nullptr; +} + +/** Finds the previous sibling layer for a \a layer + * + * @returns NULL if there are no further layers under a parent + */ +static SPObject *previous_sibling_layer(SPObject *layer) { + using Inkscape::Algorithms::find_last_if; + + SPObject::ChildrenList &list = layer->parent->children; + auto l = find_last_if(list.begin(), list.iterator_to(*layer), &is_layer); + return l != list.iterator_to(*layer) ? &*(l) : nullptr; +} + +/** Finds the first child of a \a layer + * + * @returns the layer itself if layer has no sublayers + */ +static SPObject *first_descendant_layer(SPObject *layer) { + while (true) { + auto first_descendant = std::find_if(layer->children.begin(), layer->children.end(), &is_layer); + if (first_descendant == layer->children.end()) { + break; + } + layer = &*first_descendant; + } + + return layer; +} + +/** Finds the last (topmost) child of a \a layer + * + * @returns NULL if layer has no sublayers + */ +static SPObject *last_child_layer(SPObject *layer) { + using Inkscape::Algorithms::find_last_if; + + auto l = find_last_if(layer->children.begin(), layer->children.end(), &is_layer); + return l != layer->children.end() ? &*l : nullptr; +} + +static SPObject *last_elder_layer(SPObject *root, SPObject *layer) { + using Inkscape::Algorithms::find_last_if; + SPObject *result = nullptr; + + while ( layer != root ) { + SPObject *sibling(previous_sibling_layer(layer)); + if (sibling) { + result = sibling; + break; + } + layer = layer->parent; + } + + return result; +} + +} + +/** Finds the next layer under \a root, relative to \a layer in + * depth-first order. + * + * @returns NULL if there are no further layers under \a root + */ +SPObject *next_layer(SPObject *root, SPObject *layer) { + g_return_val_if_fail(layer != nullptr, NULL); + SPObject *result = nullptr; + + SPObject *sibling = next_sibling_layer(layer); + if (sibling) { + result = first_descendant_layer(sibling); + } else if ( layer->parent != root ) { + result = layer->parent; + } + + return result; +} + + +/** Finds the previous layer under \a root, relative to \a layer in + * depth-first order. + * + * @returns NULL if there are no prior layers under \a root. + */ +SPObject *previous_layer(SPObject *root, SPObject *layer) { + using Inkscape::Algorithms::find_last_if; + + g_return_val_if_fail(layer != nullptr, NULL); + SPObject *result = nullptr; + + SPObject *child = last_child_layer(layer); + if (child) { + result = child; + } else if ( layer != root ) { + SPObject *sibling = previous_sibling_layer(layer); + if (sibling) { + result = sibling; + } else { + result = last_elder_layer(root, layer->parent); + } + } + + return result; +} + +/** +* Creates a new layer. Advances to the next layer id indicated + * by the string "layerNN", then creates a new group object of + * that id with attribute inkscape:groupmode='layer', and finally + * appends the new group object to \a root after object \a layer. + * + * \pre \a root should be either \a layer or an ancestor of it + */ +SPObject *create_layer(SPObject *root, SPObject *layer, LayerRelativePosition position) { + SPDocument *document = root->document; + + static int layer_suffix=1; + gchar *id=nullptr; + do { + g_free(id); + id = g_strdup_printf("layer%d", layer_suffix++); + } while (document->getObjectById(id)); + + Inkscape::XML::Document *xml_doc = document->getReprDoc(); + Inkscape::XML::Node *repr = xml_doc->createElement("svg:g"); + repr->setAttribute("inkscape:groupmode", "layer"); + repr->setAttribute("id", id); + g_free(id); + + if ( LPOS_CHILD == position ) { + root = layer; + SPObject *child_layer = Inkscape::last_child_layer(layer); + if ( nullptr != child_layer ) { + layer = child_layer; + } + } + + if ( root == layer ) { + root->getRepr()->appendChild(repr); + } else { + Inkscape::XML::Node *layer_repr = layer->getRepr(); + layer_repr->parent()->addChild(repr, layer_repr); + + if ( LPOS_BELOW == position ) { + SP_ITEM(document->getObjectByRepr(repr))->lowerOne(); + } + } + + return document->getObjectByRepr(repr); +} + +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : |