summaryrefslogtreecommitdiffstats
path: root/src/ui/tool/control-point.h
blob: 4de5e58475b667e365d55f5630c8c46b28595992 (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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/** @file
 * Desktop-bound visual control object
 */
/* Authors:
 *   Krzysztof Kosiński <tweenk.pl@gmail.com>
 *
 * Copyright (C) 2009 Authors
 * Released under GNU GPL, read the file 'COPYING' for more information
 */

#ifndef SEEN_UI_TOOL_CONTROL_POINT_H
#define SEEN_UI_TOOL_CONTROL_POINT_H

#include <boost/utility.hpp>
#include <sigc++/sigc++.h>
#include <gdkmm.h>
#include <gtkmm.h>
#include <2geom/point.h>

#include "display/display-forward.h"
#include "forward.h"
#include "util/accumulators.h"
#include "display/sodipodi-ctrl.h"

namespace Inkscape {
namespace UI {

// most of the documentation is in the .cpp file

class ControlPoint : boost::noncopyable, public sigc::trackable {
public:
    typedef Inkscape::Util::ReverseInterruptible RInt;
    typedef Inkscape::Util::Interruptible Int;
    // these have to be public, because GCC doesn't allow protected types in constructors,
    // even if the constructors are protected themselves.
    struct ColorEntry {
        guint32 fill;
        guint32 stroke;
    };
    struct ColorSet {
        ColorEntry normal;
        ColorEntry mouseover;
        ColorEntry clicked;
    };
    enum State {
        STATE_NORMAL,
        STATE_MOUSEOVER,
        STATE_CLICKED
    };

    virtual ~ControlPoint();
    
    /// @name Adjust the position of the control point
    /// @{
    /** Current position of the control point. */
    Geom::Point const &position() const { return _position; }
    operator Geom::Point const &() { return _position; }
    virtual void move(Geom::Point const &pos);
    virtual void setPosition(Geom::Point const &pos);
    virtual void transform(Geom::Matrix const &m);
    /// @}
    
    /// @name Toggle the point's visibility
    /// @{
    bool visible() const;
    virtual void setVisible(bool v);
    /// @}
    
    /// @name Transfer grab from another event handler
    /// @{
    void transferGrab(ControlPoint *from, GdkEventMotion *event);
    /// @}

    /// @name Receive notifications about control point events
    /// @{
    /*sigc::signal<void, Geom::Point const &, Geom::Point &, GdkEventMotion*> signal_dragged;
    sigc::signal<bool, GdkEventButton*>::accumulated<RInt> signal_clicked;
    sigc::signal<bool, GdkEventButton*>::accumulated<RInt> signal_doubleclicked;
    sigc::signal<bool, GdkEventMotion*>::accumulated<Int> signal_grabbed;
    sigc::signal<void, GdkEventButton*> signal_ungrabbed;*/
    /// @}

    /// @name Inspect the state of the control point
    /// @{
    State state() { return _state; }
    bool mouseovered() { return this == mouseovered_point; }
    /// @}

    static ControlPoint *mouseovered_point;
    static sigc::signal<void, ControlPoint*> signal_mouseover_change;
    static Glib::ustring format_tip(char const *format, ...) G_GNUC_PRINTF(1,2);

    // temporarily public, until snap delay is refactored a little
    virtual bool _eventHandler(GdkEvent *event);

protected:
    ControlPoint(SPDesktop *d, Geom::Point const &initial_pos, Gtk::AnchorType anchor,
        SPCtrlShapeType shape, unsigned int size, ColorSet *cset = 0, SPCanvasGroup *group = 0);
    ControlPoint(SPDesktop *d, Geom::Point const &initial_pos, Gtk::AnchorType anchor,
        Glib::RefPtr<Gdk::Pixbuf> pixbuf, ColorSet *cset = 0, SPCanvasGroup *group = 0);

    /// @name Handle control point events in subclasses
    /// @{
    /**
     * Called when the user moves the point beyond the drag tolerance with the first button held
     * down. Return true if you called transferGrab() during this method.
     * @param event Motion event when drag tolerance was exceeded */
    virtual bool grabbed(GdkEventMotion *event);
    /**
     * Called while dragging, but before moving the knot to new position.
     * @param pos Old position, always equal to position()
     * @param new_pos New position (after drag). This is passed as a non-const reference,
     *   so you can change it from the handler - that's how constrained dragging is implemented.
     * @param event Motion event */
    virtual void dragged(Geom::Point &new_pos, GdkEventMotion *event);
    /**
     * @var ControlPoint::signal_ungrabbed
     * Emitted when the control point finishes a drag.
     * @param event Button release event
     */
    virtual void ungrabbed(GdkEventButton *event);
    /**
     * Called when the control point is clicked, at mouse button release. Your override should
     * return true if the click had some effect. If it did nothing, return false. Improperly
     * implementing this method can cause the default context menu not to appear when a control
     * point is right-clicked.
     * @param event Button release event */
    virtual bool clicked(GdkEventButton *event);
    /**
     * Called when the control point is doubleclicked, at mouse button release.
     * @param event Button release event */
    virtual bool doubleclicked(GdkEventButton *);
    /// @}

    /// @name Manipulate the control point's appearance in subclasses
    /// @{
    virtual void _setState(State state);
    void _setColors(ColorEntry c);

    unsigned int _size() const;
    SPCtrlShapeType _shape() const;
    GtkAnchorType _anchor() const;
    Glib::RefPtr<Gdk::Pixbuf> _pixbuf();

    void _setSize(unsigned int size);
    void _setShape(SPCtrlShapeType shape);
    void _setAnchor(GtkAnchorType anchor);
    void _setPixbuf(Glib::RefPtr<Gdk::Pixbuf>);
    /// @}

    virtual Glib::ustring _getTip(unsigned /*state*/) { return ""; }
    virtual Glib::ustring _getDragTip(GdkEventMotion */*event*/) { return ""; }
    virtual bool _hasDragTips() { return false; }

    SPDesktop *const _desktop; ///< The desktop this control point resides on.
    SPCanvasItem * _canvas_item; ///< Visual representation of the control point.
    ColorSet *_cset; ///< Colors used to represent the point
    State _state;

    static int const _grab_event_mask;
    static Geom::Point const &_last_click_event_point() { return _drag_event_origin; }
    static Geom::Point const &_last_drag_origin() { return _drag_origin; }

private:
    ControlPoint(ControlPoint const &other);
    void operator=(ControlPoint const &other);

    static int _event_handler(SPCanvasItem *item, GdkEvent *event, ControlPoint *point);
    static void _setMouseover(ControlPoint *, unsigned state);
    static void _clearMouseover();
    bool _updateTip(unsigned state);
    bool _updateDragTip(GdkEventMotion *event);
    void _setDefaultColors();
    void _commonInit();

    Geom::Point _position; ///< Current position in desktop coordinates
    gulong _event_handler_connection;

    static Geom::Point _drag_event_origin;
    static Geom::Point _drag_origin;
    static bool _event_grab;
    static bool _drag_initiated;
};

extern ControlPoint::ColorSet invisible_cset;


} // 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:textwidth=99 :