diff options
Diffstat (limited to 'src/snap-preferences.cpp')
-rw-r--r-- | src/snap-preferences.cpp | 411 |
1 files changed, 411 insertions, 0 deletions
diff --git a/src/snap-preferences.cpp b/src/snap-preferences.cpp new file mode 100644 index 0000000..f72220e --- /dev/null +++ b/src/snap-preferences.cpp @@ -0,0 +1,411 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Storing of snapping preferences. + * + * Authors: + * Diederik van Lierop <mail@diedenrezi.nl> + * + * Copyright (C) 2008 - 2011 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "inkscape.h" +#include "snap-preferences.h" + +Inkscape::SnapPreferences::SnapPreferences() : + _snap_enabled_globally(true), + _snap_postponed_globally(false), + _strict_snapping(true) +{ + // Check for powers of two; see the comments in snap-enums.h + g_assert((SNAPTARGET_BBOX_CATEGORY != 0) && !(SNAPTARGET_BBOX_CATEGORY & (SNAPTARGET_BBOX_CATEGORY - 1))); + g_assert((SNAPTARGET_DISTRIBUTION_CATEGORY != 0) && !(SNAPTARGET_DISTRIBUTION_CATEGORY & (SNAPTARGET_DISTRIBUTION_CATEGORY - 1))); + g_assert((SNAPTARGET_ALIGNMENT_CATEGORY != 0) && !(SNAPTARGET_ALIGNMENT_CATEGORY & (SNAPTARGET_ALIGNMENT_CATEGORY - 1))); + g_assert((SNAPTARGET_NODE_CATEGORY != 0) && !(SNAPTARGET_NODE_CATEGORY & (SNAPTARGET_NODE_CATEGORY - 1))); + g_assert((SNAPTARGET_DATUMS_CATEGORY != 0) && !(SNAPTARGET_DATUMS_CATEGORY & (SNAPTARGET_DATUMS_CATEGORY - 1))); + g_assert((SNAPTARGET_OTHERS_CATEGORY != 0) && !(SNAPTARGET_OTHERS_CATEGORY & (SNAPTARGET_OTHERS_CATEGORY - 1))); + + for (int & _active_snap_target : _active_snap_targets) { + _active_snap_target = -1; + } + clearTargetMask(); + + for (bool& b : _simple_snapping) { + b = false; + } +} + +bool Inkscape::SnapPreferences::get_simple_snap(Inkscape::SimpleSnap option) const { + auto index = static_cast<size_t>(option); + assert(index < size_t(Inkscape::SimpleSnap::_MaxEnumValue)); + return _simple_snapping[index]; +} + +void Inkscape::SnapPreferences::set_simple_snap(Inkscape::SimpleSnap option, bool enable) { + auto index = static_cast<size_t>(option); + assert(index < size_t(Inkscape::SimpleSnap::_MaxEnumValue)); + _simple_snapping[index] = enable; +} + +bool Inkscape::SnapPreferences::isAnyDatumSnappable() const +{ + return isTargetSnappable(SNAPTARGET_GUIDE, SNAPTARGET_GRID, SNAPTARGET_PAGE_BORDER); +} + +bool Inkscape::SnapPreferences::isAnyCategorySnappable() const +{ + return isTargetSnappable(SNAPTARGET_NODE_CATEGORY, SNAPTARGET_BBOX_CATEGORY, SNAPTARGET_OTHERS_CATEGORY) || isTargetSnappable(SNAPTARGET_GUIDE, SNAPTARGET_GRID, SNAPTARGET_PAGE_BORDER); +} + +void Inkscape::SnapPreferences::_mapTargetToArrayIndex(Inkscape::SnapTargetType &target, bool &always_on, bool &group_on) const +{ + if (target == SNAPTARGET_BBOX_CATEGORY || + target == SNAPTARGET_NODE_CATEGORY || + target == SNAPTARGET_OTHERS_CATEGORY || + target == SNAPTARGET_DATUMS_CATEGORY || + target == SNAPTARGET_ALIGNMENT_CATEGORY || + target == SNAPTARGET_DISTRIBUTION_CATEGORY) { + // These main targets should be handled separately, because otherwise we might call isTargetSnappable() + // for them (to check whether the corresponding group is on) which would lead to an infinite recursive loop + always_on = (target == SNAPTARGET_DATUMS_CATEGORY); + group_on = true; + return; + } + + if (target & SNAPTARGET_BBOX_CATEGORY) { + group_on = isTargetSnappable(SNAPTARGET_BBOX_CATEGORY); // Only if the group with bbox sources/targets has been enabled, then we might snap to any of the bbox targets + return; + } + + if (target & SNAPTARGET_NODE_CATEGORY) { + group_on = isTargetSnappable(SNAPTARGET_NODE_CATEGORY); // Only if the group with path/node sources/targets has been enabled, then we might snap to any of the nodes/paths + switch (target) { + case SNAPTARGET_RECT_CORNER: + target = SNAPTARGET_NODE_CUSP; + break; + case SNAPTARGET_ELLIPSE_QUADRANT_POINT: + target = SNAPTARGET_NODE_SMOOTH; + break; + case SNAPTARGET_PATH_GUIDE_INTERSECTION: + target = SNAPTARGET_PATH_INTERSECTION; + break; + // case SNAPTARGET_PATH_PERPENDICULAR: + // case SNAPTARGET_PATH_TANGENTIAL: + // target = SNAPTARGET_PATH; + break; + default: + break; + } + + return; + } + + if (target & SNAPTARGET_DATUMS_CATEGORY) { + group_on = true; // These snap targets cannot be disabled as part of a disabled group; + switch (target) { + // Some snap targets don't have their own toggle. These targets are called "secondary targets". We will re-map + // them to their cousin which does have a toggle, and which is called a "primary target" + case SNAPTARGET_GRID_INTERSECTION: + case SNAPTARGET_GRID_PERPENDICULAR: + target = SNAPTARGET_GRID; + break; + case SNAPTARGET_GUIDE_INTERSECTION: + case SNAPTARGET_GUIDE_ORIGIN: + case SNAPTARGET_GUIDE_PERPENDICULAR: + target = SNAPTARGET_GUIDE; + break; + case SNAPTARGET_PAGE_CORNER: + case SNAPTARGET_PAGE_CENTER: + target = SNAPTARGET_PAGE_BORDER; + break; + + // Some snap targets cannot be toggled at all, and are therefore always enabled + case SNAPTARGET_GRID_GUIDE_INTERSECTION: + always_on = true; // Doesn't have it's own button + break; + + // These are only listed for completeness + case SNAPTARGET_GRID: + case SNAPTARGET_GUIDE: + case SNAPTARGET_PAGE_BORDER: + case SNAPTARGET_DATUMS_CATEGORY: + break; + default: + g_warning("Snap-preferences warning: Undefined snap target (#%i)", target); + break; + } + return; + } + + if (target & SNAPTARGET_ALIGNMENT_CATEGORY) { + group_on = isTargetSnappable(SNAPTARGET_ALIGNMENT_CATEGORY); + return; + } + + if (target & SNAPTARGET_DISTRIBUTION_CATEGORY) { + group_on = isTargetSnappable(SNAPTARGET_DISTRIBUTION_CATEGORY); + return; + } + + if (target & SNAPTARGET_OTHERS_CATEGORY) { + // Only if the group with "other" snap sources/targets has been enabled, then we might snap to any of those targets + // ... but this doesn't hold for the page border, grids, and guides + group_on = isTargetSnappable(SNAPTARGET_OTHERS_CATEGORY); + switch (target) { + // Some snap targets don't have their own toggle. These targets are called "secondary targets". We will re-map + // them to their cousin which does have a toggle, and which is called a "primary target" + case SNAPTARGET_TEXT_ANCHOR: + target = SNAPTARGET_TEXT_BASELINE; + break; + + case SNAPTARGET_IMG_CORNER: // Doesn't have its own button, on if the group is on + target = SNAPTARGET_OTHERS_CATEGORY; + break; + // Some snap targets cannot be toggled at all, and are therefore always enabled + case SNAPTARGET_CONSTRAINED_ANGLE: + case SNAPTARGET_CONSTRAINT: + always_on = true; // Doesn't have it's own button + break; + + // These are only listed for completeness + case SNAPTARGET_OBJECT_MIDPOINT: + case SNAPTARGET_ROTATION_CENTER: + case SNAPTARGET_TEXT_BASELINE: + case SNAPTARGET_OTHERS_CATEGORY: + break; + default: + g_warning("Snap-preferences warning: Undefined snap target (#%i)", target); + break; + } + + return; + } + + if (target == SNAPTARGET_UNDEFINED ) { + g_warning("Snap-preferences warning: Undefined snaptarget (#%i)", target); + } else { + g_warning("Snap-preferences warning: Snaptarget not handled (#%i)", target); + } +} + +void Inkscape::SnapPreferences::setTargetSnappable(Inkscape::SnapTargetType const target, bool enabled) +{ + bool always_on = false; + bool group_on = false; // Only needed as a dummy + Inkscape::SnapTargetType index = target; + + _mapTargetToArrayIndex(index, always_on, group_on); + + if (always_on) { // If true, then this snap target is always active and cannot be toggled + // Catch coding errors + g_warning("Snap-preferences warning: Trying to enable/disable a snap target (#%i) that's always on by definition", index); + } else { + if (index == target) { // I.e. if it has not been re-mapped, then we have a primary target at hand + _active_snap_targets[index] = enabled; + } else { // If it has been re-mapped though, then this target does not have its own toggle button and should therefore not be set + g_warning("Snap-preferences warning: Trying to enable/disable a secondary snap target (#%i); only primary targets can be set", index); + } + } +} + +/** + * Set a target mask, which will turn off all other targets except the masked ones. + * + * target - The Snap Target to change the mask for. + * enabled - The mask setting to set. + * -1 means use user settings (turn off mask) + * 0 means mask with disabled, + * 1 means mask with enabled, + */ +void Inkscape::SnapPreferences::setTargetMask(Inkscape::SnapTargetType const target, int enabled) +{ + bool always_on = false; + bool group_on = false; // Only needed as a dummy + Inkscape::SnapTargetType index = target; + + _mapTargetToArrayIndex(index, always_on, group_on); + + _active_mask_targets[index] = enabled; +} + +/** + * Clear the target mask, this should be done in a four step process. + * + * snap_manager->snaprepfs->clearTargetMask(0); // Default all options to disabled + * snap_manager->snaprepfs->setTargetMask(SOME_TARGET, 1); + * snap_manager->freeSnap(...); + * snap_manager->snaprepfs->clearTargetMask(); // Turns off masking + */ +void Inkscape::SnapPreferences::clearTargetMask(int enabled) +{ + for (int & _active_mask_targets : _active_mask_targets) { + _active_mask_targets = enabled; + } +} + +bool Inkscape::SnapPreferences::isTargetSnappable(Inkscape::SnapTargetType const target) const +{ + bool always_on = false; + bool group_on = false; + Inkscape::SnapTargetType index = target; + + _mapTargetToArrayIndex(index, always_on, group_on); + + // Check masking first, it over-rides even group_on + if (_active_mask_targets[index] != -1) { + return _active_mask_targets[index]; + } + + if (group_on) { // If true, then this snap target is in a snap group that has been enabled (e.g. bbox group, nodes/paths group, or "others" group + if (always_on) { // If true, then this snap target is always active and cannot be toggled + return true; + } else { + if (_active_snap_targets[index] == -1) { + // Catch coding errors + g_warning("Snap-preferences warning: Using an uninitialized snap target setting (#%i)", index); + // This happens if setTargetSnappable() has not been called for this parameter, e.g. from within sp_namedview_set, + // or if this target index doesn't exist at all + } + return _active_snap_targets[index]; + } + } else { + return false; + } +} + +bool Inkscape::SnapPreferences::isTargetSnappable(Inkscape::SnapTargetType const target1, Inkscape::SnapTargetType const target2) const { + return isTargetSnappable(target1) || isTargetSnappable(target2); +} + +bool Inkscape::SnapPreferences::isTargetSnappable(Inkscape::SnapTargetType const target1, Inkscape::SnapTargetType const target2, Inkscape::SnapTargetType const target3) const { + return isTargetSnappable(target1) || isTargetSnappable(target2) || isTargetSnappable(target3); +} + +bool Inkscape::SnapPreferences::isTargetSnappable(Inkscape::SnapTargetType const target1, Inkscape::SnapTargetType const target2, Inkscape::SnapTargetType const target3, Inkscape::SnapTargetType const target4) const { + return isTargetSnappable(target1) || isTargetSnappable(target2) || isTargetSnappable(target3) || isTargetSnappable(target4); +} + +bool Inkscape::SnapPreferences::isTargetSnappable(Inkscape::SnapTargetType const target1, Inkscape::SnapTargetType const target2, Inkscape::SnapTargetType const target3, Inkscape::SnapTargetType const target4, Inkscape::SnapTargetType const target5) const { + return isTargetSnappable(target1) || isTargetSnappable(target2) || isTargetSnappable(target3) || isTargetSnappable(target4) || isTargetSnappable(target5); +} + +bool Inkscape::SnapPreferences::isSnapButtonEnabled(Inkscape::SnapTargetType const target) const +{ + bool always_on = false; // Only needed as a dummy + bool group_on = false; // Only needed as a dummy + Inkscape::SnapTargetType index = target; + + _mapTargetToArrayIndex(index, always_on, group_on); + + if (_active_snap_targets[index] == -1) { + // Catch coding errors + g_warning("Snap-preferences warning: Using an uninitialized snap target setting (#%i)", index); + // This happens if setTargetSnappable() has not been called for this parameter, e.g. from within sp_namedview_set, + // or if this target index doesn't exist at all + } else { + if (index == target) { // I.e. if it has not been re-mapped, then we have a primary target at hand, which does have its own toggle button + return _active_snap_targets[index]; + } else { // If it has been re-mapped though, then this target does not have its own toggle button and therefore the button status cannot be read + g_warning("Snap-preferences warning: Trying to determine the button status of a secondary snap target (#%i); However, only primary targets have a button", index); + } + } + + return false; +} + +Inkscape::SnapTargetType Inkscape::SnapPreferences::source2target(Inkscape::SnapSourceType source) const +{ + switch (source) + { + case SNAPSOURCE_UNDEFINED: + return SNAPTARGET_UNDEFINED; + case SNAPSOURCE_BBOX_CATEGORY: + return SNAPTARGET_BBOX_CATEGORY; + case SNAPSOURCE_BBOX_CORNER: + return SNAPTARGET_BBOX_CORNER; + case SNAPSOURCE_BBOX_MIDPOINT: + return SNAPTARGET_BBOX_MIDPOINT; + case SNAPSOURCE_BBOX_EDGE_MIDPOINT: + return SNAPTARGET_BBOX_EDGE_MIDPOINT; + case SNAPSOURCE_NODE_CATEGORY: + return SNAPTARGET_NODE_CATEGORY; + case SNAPSOURCE_NODE_SMOOTH: + return SNAPTARGET_NODE_SMOOTH; + case SNAPSOURCE_NODE_CUSP: + return SNAPTARGET_NODE_CUSP; + case SNAPSOURCE_LINE_MIDPOINT: + return SNAPTARGET_LINE_MIDPOINT; + case SNAPSOURCE_PATH_INTERSECTION: + return SNAPTARGET_PATH_INTERSECTION; + case SNAPSOURCE_RECT_CORNER: + return SNAPTARGET_RECT_CORNER; + case SNAPSOURCE_ELLIPSE_QUADRANT_POINT: + return SNAPTARGET_ELLIPSE_QUADRANT_POINT; + case SNAPSOURCE_DATUMS_CATEGORY: + return SNAPTARGET_DATUMS_CATEGORY; + case SNAPSOURCE_GUIDE: + return SNAPTARGET_GUIDE; + case SNAPSOURCE_GUIDE_ORIGIN: + return SNAPTARGET_GUIDE_ORIGIN; + case SNAPSOURCE_OTHERS_CATEGORY: + return SNAPTARGET_OTHERS_CATEGORY; + case SNAPSOURCE_ROTATION_CENTER: + return SNAPTARGET_ROTATION_CENTER; + case SNAPSOURCE_OBJECT_MIDPOINT: + return SNAPTARGET_OBJECT_MIDPOINT; + case SNAPSOURCE_IMG_CORNER: + return SNAPTARGET_IMG_CORNER; + case SNAPSOURCE_TEXT_ANCHOR: + return SNAPTARGET_TEXT_ANCHOR; + + case SNAPSOURCE_NODE_HANDLE: + case SNAPSOURCE_OTHER_HANDLE: + case SNAPSOURCE_CONVEX_HULL_CORNER: + // For these snapsources there doesn't exist an equivalent snap target + return SNAPTARGET_NODE_CATEGORY; + case SNAPSOURCE_GRID_PITCH: + return SNAPTARGET_GRID; + + case SNAPSOURCE_PAGE_CORNER: + return SNAPTARGET_PAGE_CORNER; + case SNAPSOURCE_PAGE_CENTER: + return SNAPTARGET_PAGE_CENTER; + + case SNAPSOURCE_ALIGNMENT_CATEGORY: + return SNAPTARGET_ALIGNMENT_CATEGORY; + case SNAPSOURCE_ALIGNMENT_BBOX_CORNER: + return SNAPTARGET_ALIGNMENT_BBOX_CORNER; + case SNAPSOURCE_ALIGNMENT_BBOX_MIDPOINT: + return SNAPTARGET_ALIGNMENT_BBOX_EDGE_MIDPOINT; + case SNAPSOURCE_ALIGNMENT_BBOX_EDGE_MIDPOINT: + return SNAPTARGET_ALIGNMENT_BBOX_EDGE_MIDPOINT; + case SNAPSOURCE_ALIGNMENT_PAGE_CENTER: + return SNAPTARGET_ALIGNMENT_PAGE_CENTER; + case SNAPSOURCE_ALIGNMENT_PAGE_CORNER: + return SNAPTARGET_ALIGNMENT_PAGE_CORNER; + case Inkscape::SNAPSOURCE_ALIGNMENT_HANDLE: + return SNAPTARGET_ALIGNMENT_HANDLE; + + default: + g_warning("Mapping of snap source to snap target undefined (#%i)", source); + return SNAPTARGET_UNDEFINED; + } +} + +bool Inkscape::SnapPreferences::isSourceSnappable(Inkscape::SnapSourceType const source) const +{ + return isTargetSnappable(source2target(source)); +} + + +/* + 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 : |