diff options
Diffstat (limited to '')
-rw-r--r-- | src/selcue.cpp | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/src/selcue.cpp b/src/selcue.cpp new file mode 100644 index 0000000..814b187 --- /dev/null +++ b/src/selcue.cpp @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Helper object for showing selected items + * + * Authors: + * bulia byak <bulia@users.sf.net> + * Carl Hetherington <inkscape@carlh.net> + * Abhishek Sharma + * + * Copyright (C) 2004 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "selcue.h" + +#include "desktop.h" +#include "selection.h" +#include "text-editing.h" + +#include "display/control/canvas-item-ctrl.h" +#include "display/control/canvas-item-rect.h" +#include "display/control/canvas-item-guideline.h" + +#include "libnrtype/Layout-TNG.h" + +#include "object/sp-flowtext.h" +#include "object/sp-text.h" + +Inkscape::SelCue::BoundingBoxPrefsObserver::BoundingBoxPrefsObserver(SelCue &sel_cue) : + Observer("/tools/bounding_box"), + _sel_cue(sel_cue) +{ +} + +void Inkscape::SelCue::BoundingBoxPrefsObserver::notify(Preferences::Entry const &val) +{ + _sel_cue._boundingBoxPrefsChanged(static_cast<int>(val.getBool())); +} + +Inkscape::SelCue::SelCue(SPDesktop *desktop) + : _desktop(desktop), + _bounding_box_prefs_observer(*this) +{ + _selection = _desktop->getSelection(); + + _sel_changed_connection = _selection->connectChanged( + sigc::hide(sigc::mem_fun(*this, &Inkscape::SelCue::_newItemBboxes)) + ); + + { + void(SelCue::*modifiedSignal)() = &SelCue::_updateItemBboxes; + _sel_modified_connection = _selection->connectModified( + sigc::hide(sigc::hide(sigc::mem_fun(*this, modifiedSignal))) + ); + } + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + _updateItemBboxes(prefs); + prefs->addObserver(_bounding_box_prefs_observer); +} + +Inkscape::SelCue::~SelCue() +{ + _sel_changed_connection.disconnect(); + _sel_modified_connection.disconnect(); + + for (auto & item : _item_bboxes) { + delete item; + } + _item_bboxes.clear(); + + for (auto & item : _item_lines) { + delete item; + } + _item_lines.clear(); + + for (auto & item : _text_baselines) { + delete item; + } + _text_baselines.clear(); +} + +void Inkscape::SelCue::_updateItemBboxes() +{ + _updateItemBboxes(Inkscape::Preferences::get()); +} + +void Inkscape::SelCue::_updateItemBboxes(Inkscape::Preferences *prefs) +{ + gint mode = prefs->getInt("/options/selcue/value", MARK); + if (mode == NONE) { + return; + } + + g_return_if_fail(_selection != nullptr); + + int prefs_bbox = prefs->getBool("/tools/bounding_box"); + + _updateItemBboxes(mode, prefs_bbox); +} + +void Inkscape::SelCue::_updateItemBboxes(gint mode, int prefs_bbox) +{ + auto items = _selection->items(); + if (_item_bboxes.size() != (unsigned int) boost::distance(items)) { + _newItemBboxes(); + return; + } + + int bcount = 0; + for (auto item : items) { + + auto canvas_item = _item_bboxes[bcount++]; + + if (canvas_item) { + Geom::OptRect const b = (prefs_bbox == 0) ? + item->desktopVisualBounds() : item->desktopGeometricBounds(); + + if (b) { + auto ctrl = dynamic_cast<CanvasItemCtrl *>(canvas_item); + if (ctrl) { + ctrl->set_position(Geom::Point(b->min().x(), b->max().y())); + } + auto rect = dynamic_cast<CanvasItemRect *>(canvas_item); + if (rect) { + rect->set_rect(*b); + } + canvas_item->show(); + } else { // no bbox + canvas_item->hide(); + } + } + } + + _newItemLines(); + _newTextBaselines(); +} + + +void Inkscape::SelCue::_newItemBboxes() +{ + for (auto & item : _item_bboxes) { + delete item; + } + _item_bboxes.clear(); + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + gint mode = prefs->getInt("/options/selcue/value", MARK); + if (mode == NONE) { + return; + } + + g_return_if_fail(_selection != nullptr); + + int prefs_bbox = prefs->getBool("/tools/bounding_box"); + + auto items = _selection->items(); + for (auto item : items) { + + Geom::OptRect const bbox = (prefs_bbox == 0) ? + item->desktopVisualBounds() : item->desktopGeometricBounds(); + + if (bbox) { + Inkscape::CanvasItem *canvas_item = nullptr; + + if (mode == MARK) { + auto ctrl = new Inkscape::CanvasItemCtrl(_desktop->getCanvasControls(), + Inkscape::CANVAS_ITEM_CTRL_TYPE_SHAPER, + Geom::Point(bbox->min().x(), bbox->max().y())); + ctrl->set_fill(0x000000ff); + ctrl->set_stroke(0x0000000ff); + canvas_item = ctrl; + + } else if (mode == BBOX) { + auto rect = new Inkscape::CanvasItemRect(_desktop->getCanvasControls(), *bbox); + rect->set_stroke(0xffffffa0); + rect->set_shadow(0x0000c0a0, 1); + rect->set_dashed(true); + rect->set_inverted(false); + canvas_item = rect; + } + + if (canvas_item) { + canvas_item->set_pickable(false); + canvas_item->set_z_position(0); // Just low enough to not get in the way of other draggable knots. + canvas_item->show(); + _item_bboxes.emplace_back(canvas_item); + } + } + } + + _newItemLines(); + _newTextBaselines(); +} + +/** + * Create any required visual-only guide lines related to the selection. + */ +void Inkscape::SelCue::_newItemLines() +{ + for (auto canvas_item : _item_lines) { + delete canvas_item; + } + _item_lines.clear(); + + auto bbox = _selection->preferredBounds(); + + // Show a set of lines where the anchor is. + if (_selection->has_anchor && bbox) { + auto anchor = Geom::Scale(_selection->anchor_x, _selection->anchor_y); + auto point = bbox->min() + (bbox->dimensions() * anchor); + for (bool horz : { false, true }) { + auto line = new Inkscape::CanvasItemGuideLine(_desktop->getCanvasGuides(), "", point, Geom::Point(!horz, horz)); + line->set_z_position(0); + line->show(); + line->set_stroke(0xddddaa11); + line->set_inverted(true); + _item_lines.emplace_back(line); + } + } +} + +void Inkscape::SelCue::_newTextBaselines() +{ + for (auto canvas_item : _text_baselines) { + delete canvas_item; + } + _text_baselines.clear(); + + auto items = _selection->items(); + for (auto item : items) { + std::optional<Geom::Point> pt; + if (auto text = dynamic_cast<SPText *>(item)) { + pt = text->getBaselinePoint(); + } + if (auto flow = dynamic_cast<SPFlowtext *>(item)) { + pt = flow->getBaselinePoint(); + } + if (pt) { + auto canvas_item = new Inkscape::CanvasItemCtrl(_desktop->getCanvasControls(), + Inkscape::CANVAS_ITEM_CTRL_SHAPE_SQUARE, + (*pt) * item->i2dt_affine()); + canvas_item->set_size(5); + canvas_item->set_stroke(0x000000ff); + canvas_item->set_fill(0x00000000); + canvas_item->set_z_position(0); + canvas_item->show(); + _text_baselines.emplace_back(canvas_item); + } + } +} + +void Inkscape::SelCue::_boundingBoxPrefsChanged(int prefs_bbox) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + gint mode = prefs->getInt("/options/selcue/value", MARK); + if (mode == NONE) { + return; + } + + g_return_if_fail(_selection != nullptr); + + _updateItemBboxes(mode, prefs_bbox); +} + +/* + 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 : |