// 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 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(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 :