summaryrefslogtreecommitdiffstats
path: root/src/ui/tool/control-point-selection.h
blob: dc58211214579419ea92ca3782c72abb5a542c0e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// SPDX-License-Identifier: GPL-2.0-or-later
/** @file
 * Control point selection - stores a set of control points and applies transformations
 * to them
 */
/* Authors:
 *   Krzysztof Kosiński <tweenk.pl@gmail.com>
 *
 * Copyright (C) 2009 Authors
 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
 */

#ifndef SEEN_UI_TOOL_CONTROL_POINT_SELECTION_H
#define SEEN_UI_TOOL_CONTROL_POINT_SELECTION_H

#include <list>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <optional>
#include <cstddef>
#include <sigc++/sigc++.h>
#include <2geom/forward.h>
#include <2geom/point.h>
#include <2geom/rect.h>
#include "ui/tool/commit-events.h"
#include "ui/tool/manipulator.h"
#include "ui/tool/node-types.h"
#include "snap-candidate.h"

class SPDesktop;

namespace Inkscape {
class CanvasItemGroup;
namespace UI {
class TransformHandleSet;
class SelectableControlPoint;
}
}

namespace Inkscape {
namespace UI {

class ControlPointSelection : public Manipulator, public sigc::trackable {
public:
    ControlPointSelection(SPDesktop *d, Inkscape::CanvasItemGroup *th_group);
    ~ControlPointSelection() override;
    typedef std::unordered_set<SelectableControlPoint *> set_type;
    typedef set_type Set; // convenience alias

    typedef set_type::iterator iterator;
    typedef set_type::const_iterator const_iterator;
    typedef set_type::size_type size_type;
    typedef SelectableControlPoint *value_type;
    typedef SelectableControlPoint *key_type;

    // size
    bool empty() { return _points.empty(); }
    size_type size() { return _points.size(); }

    // iterators
    iterator begin() { return _points.begin(); }
    const_iterator begin() const { return _points.begin(); }
    iterator end() { return _points.end(); }
    const_iterator end() const { return _points.end(); }

    // insert
    std::pair<iterator, bool> insert(const value_type& x, bool notify = true, bool to_update = true);
    template <class InputIterator>
    void insert(InputIterator first, InputIterator last) {
        for (; first != last; ++first) {
            insert(*first, false, false);
        }
        _update();
        signal_selection_changed.emit(std::vector<key_type>(first, last), true);
    }

    // erase
    void clear();
    void erase(iterator pos, bool to_update = true);
    size_type erase(const key_type& k, bool notify = true);
    void erase(iterator first, iterator last);

    // find
    iterator find(const key_type &k) {
        return _points.find(k);
    }

    // Sometimes it is very useful to keep a list of all selectable points.
    set_type const &allPoints() const { return _all_points; }
    set_type &allPoints() { return _all_points; }
    // ...for example in these methods. Another useful case is snapping.
    void selectAll();
    void selectArea(Geom::Rect const &, bool invert = false);
    void invertSelection();
    void spatialGrow(SelectableControlPoint *origin, int dir);

    bool event(Inkscape::UI::Tools::ToolBase *, GdkEvent *) override;

    void transform(Geom::Affine const &m);
    void align(Geom::Dim2 d, AlignTargetNode target = AlignTargetNode::MID_NODE);
    void distribute(Geom::Dim2 d);

    Geom::OptRect pointwiseBounds();
    Geom::OptRect bounds();

    bool transformHandlesEnabled() { return _handles_visible; }
    void showTransformHandles(bool v, bool one_node);
    // the two methods below do not modify the state; they are for use in manipulators
    // that need to temporarily hide the handles, for example when moving a node
    void hideTransformHandles();
    void restoreTransformHandles();
    void toggleTransformHandlesMode();

    sigc::signal<void> signal_update;
    // It turns out that emitting a signal after every point is selected or deselected is not too efficient,
    // so this can be done in a massive group once the selection is finally changed.
    sigc::signal<void, std::vector<SelectableControlPoint *>, bool> signal_selection_changed;
    sigc::signal<void, CommitEvent> signal_commit;

    void getOriginalPoints(std::vector<Inkscape::SnapCandidatePoint> &pts);
    void getUnselectedPoints(std::vector<Inkscape::SnapCandidatePoint> &pts);
    void setOriginalPoints();
    //the purpose of this list is to keep track of first and last selected
    std::list<SelectableControlPoint *> _points_list;

private:
    // The functions below are invoked from SelectableControlPoint.
    // Previously they were connected to handlers when selecting, but this
    // creates problems when dragging a point that was not selected.
    void _pointGrabbed(SelectableControlPoint *);
    void _pointDragged(Geom::Point &, GdkEventMotion *);
    void _pointUngrabbed();
    bool _pointClicked(SelectableControlPoint *, GdkEventButton *);
    void _mouseoverChanged();

    void _update();
    void _updateTransformHandles(bool preserve_center);
    void _updateBounds();
    bool _keyboardMove(GdkEventKey const &, Geom::Point const &);
    bool _keyboardRotate(GdkEventKey const &, int);
    bool _keyboardScale(GdkEventKey const &, int);
    bool _keyboardFlip(Geom::Dim2);
    void _keyboardTransform(Geom::Affine const &);
    void _commitHandlesTransform(CommitEvent ce);
    double _rotationRadius(Geom::Point const &);

    set_type _points;

    set_type _all_points;
    std::unordered_map<SelectableControlPoint *, Geom::Point> _original_positions;
    std::unordered_map<SelectableControlPoint *, Geom::Affine> _last_trans;
    std::optional<double> _rot_radius;
    std::optional<double> _mouseover_rot_radius;
    Geom::OptRect _bounds;
    TransformHandleSet *_handles;
    SelectableControlPoint *_grabbed_point, *_farthest_point;
    unsigned _dragging         : 1;
    unsigned _handles_visible  : 1;
    unsigned _one_node_handles : 1;

    friend class SelectableControlPoint;
};

} // namespace UI
} // namespace Inkscape

#endif

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