diff options
Diffstat (limited to '')
-rw-r--r-- | src/display/control/canvas-item.cpp | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/src/display/control/canvas-item.cpp b/src/display/control/canvas-item.cpp new file mode 100644 index 0000000..6670b19 --- /dev/null +++ b/src/display/control/canvas-item.cpp @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * Abstract base class for on-canvas control items. + */ + +/* + * Author: + * Tavmjong Bah + * + * Copyright (C) 2020 Tavmjong Bah + * + * Rewrite of SPCanvasItem + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "canvas-item.h" + +#include "canvas-item-group.h" + +#include "ui/widget/canvas.h" + +namespace Inkscape { + +CanvasItem::CanvasItem(CanvasItemGroup *group) + : _name("CanvasItem") +{ + if (group) { + group->add(this); + _parent = group; + _canvas = group->get_canvas(); + _affine = group->get_affine(); + } +} + +CanvasItem::~CanvasItem() +{ + if (_parent) { + _parent->remove(this, false); // remove() should not delete this or we'll double delete! + } + + // Clear canvas of item. + request_redraw(); + + // Clear any pointers to this object in canvas. + _canvas->canvas_item_destructed(this); +} + +bool CanvasItem::is_descendant_of(CanvasItem *ancestor) +{ + auto item = this; + while (item) { + if (item == ancestor) { + return true; + } + item = item->get_parent(); + } + return false; +} + +int CanvasItem::get_z_position() +{ + if (!_parent) { + std::cerr << "CanvasItem::get_z_position: No parent!" << std::endl; + return -1; + } + + size_t position = 0; + for (auto it = _parent->items.begin(); it != _parent->items.end(); ++it, ++position) { + if (&*it == this) { + return position; + } + } + + std::cerr << "CanvasItem::get_z_position: item not found!" << std::endl; + return -1; +} + +void CanvasItem::set_z_position(unsigned int n) +{ + if (!_parent) { + std::cerr << "CanvasItem::set_z_position: No parent!" << std::endl; + } + + if (n == 0) { + this->lower_to_bottom(); // Low cost operation + return; + } + + if (n > _parent->items.size() - 2) { + this->raise_to_top(); // Low cost operation + return; + } + + _parent->items.erase(_parent->items.iterator_to(*this)); + + size_t position = 0; + for (auto it = _parent->items.begin(); it != _parent->items.end(); ++it, ++position) { + if (position == n) { + _parent->items.insert(it, *this); + break; + } + } +} + +void CanvasItem::raise_to_top() +{ + if (!_parent) { + std::cerr << "CanvasItem::raise_to_top: No parent!" << std::endl; + } + + _parent->items.erase(_parent->items.iterator_to(*this)); + _parent->items.push_back(*this); +} + +void CanvasItem::lower_to_bottom() +{ + if (!_parent) { + std::cerr << "CanvasItem::lower_to_bottom: No parent!" << std::endl; + } + + _parent->items.erase(_parent->items.iterator_to(*this)); + _parent->items.push_front(*this); +} + +// Indicate geometry changed and bounds needs recalculating. +void CanvasItem::request_update() +{ + _need_update = true; + if (_parent) { + _parent->request_update(); + } else { + _canvas->request_update(); + } +} + +void CanvasItem::show() +{ + if (_visible) { + return; // Already visible. + } + + _visible = true; + // update bounds when visibility changes + request_update(); + request_redraw(); +} + +int CanvasItem::grab(Gdk::EventMask event_mask, GdkCursor *cursor) +{ + return grab(event_mask, Glib::wrap(cursor)); +} + +// Grab all events! TODO: Return boolean +int CanvasItem::grab(Gdk::EventMask event_mask, Glib::RefPtr<Gdk::Cursor> cursor) +{ +#ifdef CANVAS_ITEM_DEBUG + std::cout << "CanvasItem::grab: " << _name << std::endl; +#endif + // Don't grab if we already have a grabbed item! + if (_canvas->get_grabbed_canvas_item()) { + return -1; + } + + auto const display = Gdk::Display::get_default(); + auto const seat = display->get_default_seat(); + auto const window = _canvas->get_window(); + seat->grab(window, Gdk::SEAT_CAPABILITY_ALL_POINTING, false, cursor, nullptr); + + _canvas->set_grabbed_canvas_item(this, event_mask); + _canvas->set_current_canvas_item(this); // So that all events go to grabbed item. + return 0; +} + +void CanvasItem::ungrab() +{ +#ifdef CANVAS_ITEM_DEBUG + std::cout << "CanvasItem::ungrab: " << _name << std::endl; +#endif + if (_canvas->get_grabbed_canvas_item() != this) { + return; // Sanity check + } + + _canvas->set_grabbed_canvas_item(nullptr, (Gdk::EventMask)0); // Zero mask + + auto const display = Gdk::Display::get_default(); + auto const seat = display->get_default_seat(); + seat->ungrab(); +} + +void CanvasItem::hide() +{ + if (!_visible) { + return; // Already hidden + } + + _visible = false; + // update bounds when visibility changes + request_update(); + request_redraw(); +} + +void CanvasItem::set_fill(guint32 rgba) +{ + if (_fill != rgba) { + _fill = rgba; + request_redraw(); + } +} + +void CanvasItem::set_stroke(guint32 rgba) +{ + if (_stroke != rgba) { + _stroke = rgba; + request_redraw(); + } +} + +void CanvasItem::request_redraw() { + if (_canvas) { + // Queue redraw request + _canvas->redraw_area(_bounds); + } +} + +} // Namespace Inkscape + +void canvas_item_print_tree(Inkscape::CanvasItem *item) +{ + static int level = 0; + if (level == 0) { + std::cout << "Canvas Item Tree" << std::endl; + } + + std::cout << "CC: "; + for (unsigned i = 0; i < level; ++i) { + std::cout << " "; + } + + std::cout << item->get_z_position() << ": " << item->get_name() << std::endl; + + auto group = dynamic_cast<Inkscape::CanvasItemGroup *>(item); + if (group) { + ++level; + for (auto & item : group->items) { + canvas_item_print_tree(&item); + } + --level; + } +} + +/* + 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 : |