diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:29:01 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:29:01 +0000 |
commit | 35a96bde514a8897f6f0fcc41c5833bf63df2e2a (patch) | |
tree | 657d15a03cc46bd099fc2c6546a7a4ad43815d9f /src/ui/control-manager.cpp | |
parent | Initial commit. (diff) | |
download | inkscape-35a96bde514a8897f6f0fcc41c5833bf63df2e2a.tar.xz inkscape-35a96bde514a8897f6f0fcc41c5833bf63df2e2a.zip |
Adding upstream version 1.0.2.upstream/1.0.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/ui/control-manager.cpp')
-rw-r--r-- | src/ui/control-manager.cpp | 509 |
1 files changed, 509 insertions, 0 deletions
diff --git a/src/ui/control-manager.cpp b/src/ui/control-manager.cpp new file mode 100644 index 0000000..0330b5b --- /dev/null +++ b/src/ui/control-manager.cpp @@ -0,0 +1,509 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Central facade for accessing and managing on-canvas controls. + * + * Author: + * Jon A. Cruz <jon@joncruz.org> + * + * Copyright 2012 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "control-manager.h" + +#include <algorithm> +#include <set> + +#include <glib-object.h> + +#include "display/sodipodi-ctrl.h" // for SP_TYPE_CTRL +#include "display/sp-ctrlline.h" +#include "display/sp-ctrlcurve.h" +#include "preferences.h" + +using Inkscape::ControlFlags; + +namespace { + +// Note: The following operator overloads are local to this file at the moment to discourage flag manipulation elsewhere. +/* +ControlFlags operator |(ControlFlags lhs, ControlFlags rhs) +{ + return static_cast<ControlFlags>(static_cast<int>(lhs) | static_cast<int>(rhs)); +} +*/ + +ControlFlags operator &(ControlFlags lhs, ControlFlags rhs) +{ + return static_cast<ControlFlags>(static_cast<int>(lhs) & static_cast<int>(rhs)); +} + +ControlFlags operator ^(ControlFlags lhs, ControlFlags rhs) +{ + return static_cast<ControlFlags>(static_cast<int>(lhs) ^ static_cast<int>(rhs)); +} + +ControlFlags& operator ^=(ControlFlags &lhs, ControlFlags rhs) +{ + lhs = lhs ^ rhs; + return lhs; +} + +} // namespace + +// Default color for line: +#define LINE_COLOR_PRIMARY 0x0000ff7f +#define LINE_COLOR_SECONDARY 0xff00007f +#define LINE_COLOR_TERTIARY 0xffff007f + +namespace Inkscape { + +class ControlManagerImpl +{ +public: + ControlManagerImpl(ControlManager &manager); + + ~ControlManagerImpl() = default; + + SPCanvasItem *createControl(SPCanvasGroup *parent, ControlType type); + + void setControlSize(int size, bool force = false); + + void track(SPCanvasItem *anchor); + + sigc::connection connectCtrlSizeChanged(const sigc::slot<void> &slot); + + void updateItem(SPCanvasItem *item); + + bool setControlType(SPCanvasItem *item, ControlType type); + + bool setControlResize(SPCanvasItem *item, int ctrlResize); + + void setSelected(SPCanvasItem *item, bool selected); + +private: + static void thingFinalized(gpointer data, GObject *wasObj); + + void thingFinalized(GObject *wasObj); + + class PrefListener : public Inkscape::Preferences::Observer + { + public: + PrefListener(ControlManagerImpl &manager) : Inkscape::Preferences::Observer("/options/grabsize/value"), _mgr(manager) {} + ~PrefListener() override = default; + + void notify(Inkscape::Preferences::Entry const &val) override { + int size = val.getIntLimited(3, 1, 7); + _mgr.setControlSize(size); + } + + ControlManagerImpl &_mgr; + }; + + + ControlManager &_manager; + sigc::signal<void> _sizeChangedSignal; + PrefListener _prefHook; + int _size; // Size from the grabsize preference + int _resize; // Way size should change from grabsize + std::vector<SPCanvasItem *> _itemList; + std::map<Inkscape::ControlType, std::vector<unsigned int> > _sizeTable; + std::map<Inkscape::ControlType, GType> _typeTable; + std::map<Inkscape::ControlType, SPCtrlShapeType> _ctrlToShape; + std::set<Inkscape::ControlType> _resizeOnSelect; +}; + +ControlManagerImpl::ControlManagerImpl(ControlManager &manager) : + _manager(manager), + _sizeChangedSignal(), + _prefHook(*this), + _size(3), + _resize(3), + _itemList(), + _sizeTable() +{ + _typeTable[CTRL_TYPE_UNKNOWN] = SP_TYPE_CTRL; + _typeTable[CTRL_TYPE_LPE] = SP_TYPE_CTRL; + _typeTable[CTRL_TYPE_ADJ_HANDLE] = SP_TYPE_CTRL; + _typeTable[CTRL_TYPE_ANCHOR] = SP_TYPE_CTRL; + _typeTable[CTRL_TYPE_INVISIPOINT] = SP_TYPE_CTRL; + _typeTable[CTRL_TYPE_NODE_AUTO] = SP_TYPE_CTRL; + _typeTable[CTRL_TYPE_NODE_CUSP] = SP_TYPE_CTRL; + _typeTable[CTRL_TYPE_NODE_SMOOTH] = SP_TYPE_CTRL; + _typeTable[CTRL_TYPE_NODE_SYMETRICAL] = SP_TYPE_CTRL; + + _typeTable[CTRL_TYPE_LINE] = SP_TYPE_CTRLLINE; + + // ------- + _ctrlToShape[CTRL_TYPE_UNKNOWN] = SP_CTRL_SHAPE_DIAMOND; + _ctrlToShape[CTRL_TYPE_LPE] = SP_CTRL_SHAPE_DIAMOND; + _ctrlToShape[CTRL_TYPE_NODE_CUSP] = SP_CTRL_SHAPE_DIAMOND; + _ctrlToShape[CTRL_TYPE_NODE_SMOOTH] = SP_CTRL_SHAPE_SQUARE; + _ctrlToShape[CTRL_TYPE_NODE_AUTO] = SP_CTRL_SHAPE_CIRCLE; + _ctrlToShape[CTRL_TYPE_NODE_SYMETRICAL] = SP_CTRL_SHAPE_SQUARE; + + _ctrlToShape[CTRL_TYPE_ADJ_HANDLE] = SP_CTRL_SHAPE_CIRCLE; + _ctrlToShape[CTRL_TYPE_INVISIPOINT] = SP_CTRL_SHAPE_SQUARE; + + // ------- + + _resizeOnSelect.insert(CTRL_TYPE_NODE_AUTO); + _resizeOnSelect.insert(CTRL_TYPE_NODE_CUSP); + _resizeOnSelect.insert(CTRL_TYPE_NODE_SMOOTH); + _resizeOnSelect.insert(CTRL_TYPE_NODE_SYMETRICAL); + + // ------- + + // The size of the controls is determined by the grabsize preference; see the "Handle size" parameter in + // the input/output group, on the "input devices" tab; this parameter ranges from 1 to 7; When selecting a control, we + // increase the size by an additional 2 pixels, if _resizeOnSelect is true (see setSelected()) + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->addObserver(_prefHook); + + _size = prefs->getIntLimited("/options/grabsize/value", 3, 1, 7); + + // _sizeTable will have odd numbers, which allow for pixel perfect alignment (e.g. relative to grids + // or guides, which are 1 px wide. It is not possible to accurately center a control to them if the + // control has an even width). + { + unsigned int sizes[] = {7, 7, 7, 7, 7, 7, 7}; + _sizeTable[CTRL_TYPE_UNKNOWN] = std::vector<unsigned int>(sizes, sizes + (sizeof(sizes) / sizeof(sizes[0]))); + } + { + unsigned int sizes[] = {3, 5, 7, 9, 11, 13, 15}; + _sizeTable[CTRL_TYPE_ADJ_HANDLE] = std::vector<unsigned int>(sizes, sizes + (sizeof(sizes) / sizeof(sizes[0]))); + } + { + unsigned int sizes[] = {3, 5, 7, 9, 11, 13, 15}; + _sizeTable[CTRL_TYPE_ANCHOR] = std::vector<unsigned int>(sizes, sizes + (sizeof(sizes) / sizeof(sizes[0]))); + } + { + unsigned int sizes[] = {5, 7, 9, 11, 13, 15, 17}; + _sizeTable[CTRL_TYPE_POINT] = std::vector<unsigned int>(sizes, sizes + (sizeof(sizes) / sizeof(sizes[0]))); + _sizeTable[CTRL_TYPE_ROTATE] = std::vector<unsigned int>(sizes, sizes + (sizeof(sizes) / sizeof(sizes[0]))); + _sizeTable[CTRL_TYPE_SIZER] = std::vector<unsigned int>(sizes, sizes + (sizeof(sizes) / sizeof(sizes[0]))); + _sizeTable[CTRL_TYPE_SHAPER] = std::vector<unsigned int>(sizes, sizes + (sizeof(sizes) / sizeof(sizes[0]))); + } + { + unsigned int sizes[] = {5, 7, 9, 11, 13, 15, 17}; + _sizeTable[CTRL_TYPE_NODE_AUTO] = std::vector<unsigned int>(sizes, sizes + (sizeof(sizes) / sizeof(sizes[0]))); + _sizeTable[CTRL_TYPE_NODE_CUSP] = std::vector<unsigned int>(sizes, sizes + (sizeof(sizes) / sizeof(sizes[0]))); + } + { + unsigned int sizes[] = {3, 5, 7, 9, 11, 13, 15}; + _sizeTable[CTRL_TYPE_NODE_SMOOTH] = std::vector<unsigned int>(sizes, sizes + (sizeof(sizes) / sizeof(sizes[0]))); + _sizeTable[CTRL_TYPE_NODE_SYMETRICAL] = std::vector<unsigned int>(sizes, sizes + (sizeof(sizes) / sizeof(sizes[0]))); + } + { + unsigned int sizes[] = {1, 1, 1, 1, 1, 1, 1}; + _sizeTable[CTRL_TYPE_INVISIPOINT] = std::vector<unsigned int>(sizes, sizes + (sizeof(sizes) / sizeof(sizes[0]))); + } + { + unsigned int sizes[] = { 5, 7, 9, 11, 13, 15, 17 }; + _sizeTable[CTRL_TYPE_LPE] = std::vector<unsigned int>(sizes, sizes + (sizeof(sizes) / sizeof(sizes[0]))); + } +} + + +void ControlManagerImpl::setControlSize(int size, bool force) +{ + if ((size < 1) || (size > 7)) { + g_warning("Illegal logical size set: %d", size); + } else if (force || (size != _size)) { + _size = size; + + for (auto & it : _itemList) + { + if (it) { + updateItem(it); + } + } + + //_sizeChangedSignal.emit(); + } +} + +SPCanvasItem *ControlManagerImpl::createControl(SPCanvasGroup *parent, ControlType type) +{ + SPCanvasItem *item = nullptr; + unsigned int targetSize = _sizeTable[type][_size - 1]; + switch (type) + { + case CTRL_TYPE_ADJ_HANDLE: + item = sp_canvas_item_new(parent, SP_TYPE_CTRL, + "shape",SP_CTRL_SHAPE_CIRCLE, + "size", targetSize, + "filled", 1, + "fill_color", 0xffffff7f, + "stroked", 1, + "stroke_color", 0x0000ff7f, + NULL); + break; + case CTRL_TYPE_ANCHOR: + item = sp_canvas_item_new(parent, SP_TYPE_CTRL, + "size", targetSize, + "filled", 1, + "fill_color", 0xffffff7f, + "stroked", 1, + "stroke_color", 0x000000ff, + NULL); + break; + case CTRL_TYPE_NODE_AUTO: + case CTRL_TYPE_NODE_CUSP: + case CTRL_TYPE_NODE_SMOOTH: + case CTRL_TYPE_NODE_SYMETRICAL: + { + SPCtrlShapeType shape = _ctrlToShape[_ctrlToShape.count(type) ? type : CTRL_TYPE_UNKNOWN]; + item = sp_canvas_item_new(parent, SP_TYPE_CTRL, + "shape", shape, + "size", targetSize, + NULL); + break; + } + case CTRL_TYPE_INVISIPOINT: + item = sp_canvas_item_new(parent, SP_TYPE_CTRL, + "shape", SP_CTRL_SHAPE_SQUARE, + "size", targetSize, + NULL); + break; + case CTRL_TYPE_UNKNOWN: + case CTRL_TYPE_LPE: + default: + item = sp_canvas_item_new(parent, SP_TYPE_CTRL, nullptr); + } + if (item) { + item->ctrlType = type; + } + return item; +} + +void ControlManagerImpl::track(SPCanvasItem *item) +{ + g_object_weak_ref( G_OBJECT(item), ControlManagerImpl::thingFinalized, this ); + + _itemList.push_back(item); + + setControlSize(_size, true); +} + +sigc::connection ControlManagerImpl::connectCtrlSizeChanged(const sigc::slot<void> &slot) +{ + return _sizeChangedSignal.connect(slot); +} + +void ControlManagerImpl::updateItem(SPCanvasItem *item) +{ + if (item) { + unsigned int target = _sizeTable[item->ctrlType][_size - 1] + item->ctrlResize; + g_object_set(item, "size", target, NULL); + + sp_canvas_item_request_update(item); + } +} + +bool ControlManagerImpl::setControlType(SPCanvasItem *item, ControlType type) +{ + bool accepted = false; + if (item && (item->ctrlType == type)) { + // nothing to do + accepted = true; + } else if (item) { + if (_ctrlToShape.count(type) && (_typeTable[type] == _typeTable[item->ctrlType])) { // compatible? + unsigned int targetSize = _sizeTable[type][_size - 1] + item->ctrlResize; + SPCtrlShapeType targetShape = _ctrlToShape[type]; + + g_object_set(item, "shape", targetShape, "size", targetSize, NULL); + item->ctrlType = type; + accepted = true; + } + } + + return accepted; +} + +bool ControlManagerImpl::setControlResize(SPCanvasItem *item, int ctrlResize) +{ + // _sizeTable will have odd numbers, which allow for pixel perfect alignment (e.g. relative to grids + // or guides, which are 1 px wide. It is not possible to accurately center a control to them if the + // control has an even width). ctrlResize should therefore be an even number, such that the sum (targetSize) + // is also odd + if(item) { + item->ctrlResize = ctrlResize; + unsigned int targetSize = _sizeTable[item->ctrlType][_size - 1] + item->ctrlResize; + g_object_set(item, "size", targetSize, NULL); + return true; + } + return false; +} + +void ControlManagerImpl::setSelected(SPCanvasItem *item, bool selected) +{ + if (_manager.isSelected(item) != selected) { + item->ctrlFlags ^= CTRL_FLAG_SELECTED; // toggle, since we know it is different + + if (selected && _resizeOnSelect.count(item->ctrlType)) { + item->ctrlResize = 2; + } else { + item->ctrlResize = 0; + } + + // TODO refresh colors + unsigned int targetSize = _sizeTable[item->ctrlType][_size - 1] + item->ctrlResize; + g_object_set(item, "size", targetSize, NULL); + } +} + +void ControlManagerImpl::thingFinalized(gpointer data, GObject *wasObj) +{ + if (data) { + reinterpret_cast<ControlManagerImpl *>(data)->thingFinalized(wasObj); + } +} + +void ControlManagerImpl::thingFinalized(GObject *wasObj) +{ + SPCanvasItem *wasItem = reinterpret_cast<SPCanvasItem *>(wasObj); + if (wasItem) + { + std::vector<SPCanvasItem *>::iterator it = std::find(_itemList.begin(), _itemList.end(), wasItem); + if (it != _itemList.end()) + { + _itemList.erase(it); + } + } +} + + +// ---------------------------------------------------- + +ControlManager::ControlManager() : + _impl(new ControlManagerImpl(*this)) +{ +} + +ControlManager::~ControlManager() += default; + +ControlManager &ControlManager::getManager() +{ + static ControlManager instance; + + return instance; +} + + +SPCanvasItem *ControlManager::createControl(SPCanvasGroup *parent, ControlType type) +{ + return _impl->createControl(parent, type); +} + +SPCtrlLine *ControlManager::createControlLine(SPCanvasGroup *parent, CtrlLineType type) +{ + SPCtrlLine *line = SP_CTRLLINE(sp_canvas_item_new(parent, SP_TYPE_CTRLLINE, nullptr)); + if (line) { + line->ctrlType = CTRL_TYPE_LINE; + + line->setRgba32((type == CTLINE_PRIMARY) ? LINE_COLOR_PRIMARY : + (type == CTLINE_SECONDARY) ? LINE_COLOR_SECONDARY : LINE_COLOR_TERTIARY); + line->setCoords(0, 0, 0, 0); + } + return line; +} + +SPCtrlLine *ControlManager::createControlLine(SPCanvasGroup *parent, Geom::Point const &p1, Geom::Point const &p2, CtrlLineType type) +{ + SPCtrlLine *line = createControlLine(parent, type); + if (line) { + line->setCoords(p1, p2); + } + return line; +} + +SPCtrlCurve *ControlManager::createControlCurve(SPCanvasGroup *parent, Geom::Point const &p0, Geom::Point const &p1, Geom::Point const &p2, Geom::Point const &p3, CtrlLineType type) +{ + SPCtrlCurve *line = SP_CTRLCURVE(sp_canvas_item_new(parent, SP_TYPE_CTRLCURVE, nullptr)); + if (line) { + line->ctrlType = CTRL_TYPE_LINE; + + line->setRgba32((type == CTLINE_PRIMARY) ? LINE_COLOR_PRIMARY : + (type == CTLINE_SECONDARY) ? LINE_COLOR_SECONDARY : LINE_COLOR_TERTIARY); + line->setCoords(p0, p1, p2, p3); + } + return line; +} + +void ControlManager::track(SPCanvasItem *item) +{ + _impl->track(item); +} + +sigc::connection ControlManager::connectCtrlSizeChanged(const sigc::slot<void> &slot) +{ + return _impl->connectCtrlSizeChanged(slot); +} + +void ControlManager::updateItem(SPCanvasItem *item) +{ + return _impl->updateItem(item); +} + +bool ControlManager::setControlType(SPCanvasItem *item, ControlType type) +{ + return _impl->setControlType(item, type); +} + +bool ControlManager::setControlResize(SPCanvasItem *item, int ctrlResize) +{ + return _impl->setControlResize(item, ctrlResize); +} + +bool ControlManager::isActive(SPCanvasItem *item) const +{ + return (item->ctrlFlags & CTRL_FLAG_ACTIVE) != 0; +} + +void ControlManager::setActive(SPCanvasItem *item, bool active) +{ + if (isActive(item) != active) { + item->ctrlFlags ^= CTRL_FLAG_ACTIVE; // toggle, since we know it is different + // TODO refresh size/colors + } +} + +bool ControlManager::isPrelight(SPCanvasItem *item) const +{ + return (item->ctrlFlags & CTRL_FLAG_PRELIGHT) != 0; +} + +void ControlManager::setPrelight(SPCanvasItem *item, bool prelight) +{ + if (isPrelight(item) != prelight) { + item->ctrlFlags ^= CTRL_FLAG_PRELIGHT; // toggle, since we know it is different + // TODO refresh size/colors + } +} + +bool ControlManager::isSelected(SPCanvasItem *item) const +{ + return (item->ctrlFlags & CTRL_FLAG_SELECTED) != 0; +} + +void ControlManager::setSelected(SPCanvasItem *item, bool selected) +{ + _impl->setSelected(item, selected); +} + +} // namespace Inkscape + +/* + 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 : |