summaryrefslogtreecommitdiffstats
path: root/src/ui/widget/canvas.h
blob: f2a434ade61970ec4dd8ea110425c1d498b92ed2 (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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
// SPDX-License-Identifier: GPL-2.0-or-later

#ifndef INKSCAPE_UI_WIDGET_CANVAS_H
#define INKSCAPE_UI_WIDGET_CANVAS_H
/*
 * Authors:
 *   Tavmjong Bah
 *   PBS <pbs3141@gmail.com>
 *
 * Copyright (C) 2022 Authors
 *
 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <memory>
#include <gtkmm.h>
#include <2geom/rect.h>
#include <2geom/int-rect.h>
#include "display/rendermode.h"

class SPDesktop;

namespace Inkscape {

class CanvasItem;
class CanvasItemGroup;
class Drawing;

namespace UI {
namespace Widget {

class CanvasPrivate;

/**
 * A Gtk::DrawingArea widget for Inkscape's canvas.
 */ 
class Canvas : public Gtk::DrawingArea
{
    using parent_type = Gtk::DrawingArea;

public:

    Canvas();
    ~Canvas() override;

    /* Configuration */

    // Desktop (Todo: Remove.)
    void set_desktop(SPDesktop *desktop) { _desktop = desktop; }
    SPDesktop *get_desktop() const { return _desktop; }

    // Drawing
    void set_drawing(Inkscape::Drawing *drawing);

    // Canvas item root
    CanvasItemGroup *get_canvas_item_root() const { return _canvas_item_root; }

    // Geometry
    void set_pos   (const Geom::IntPoint &pos);
    void set_pos   (const Geom::Point    &fpos) { set_pos(fpos.round()); }
    void set_affine(const Geom::Affine   &affine);
    const Geom::IntPoint& get_pos   () const {return _pos;}
    const Geom::Affine&   get_affine() const {return _affine;}

    // Background
    void set_background_color(guint32 rgba);
    void set_background_checkerboard(guint32 rgba = 0xC4C4C4FF, bool use_alpha = false);

    //  Rendering modes
    void set_render_mode(Inkscape::RenderMode mode);
    void set_color_mode (Inkscape::ColorMode  mode);
    void set_split_mode (Inkscape::SplitMode  mode);
    Inkscape::RenderMode get_render_mode() const { return _render_mode; }
    Inkscape::ColorMode  get_color_mode()  const { return _color_mode; }
    Inkscape::SplitMode  get_split_mode()  const { return _split_mode; }

    // CMS
    void set_cms_key(std::string key) {
        _cms_key = std::move(key);
        _cms_active = !_cms_key.empty();
    }
    const std::string& get_cms_key() const { return _cms_key; }
    void set_cms_active(bool active) { _cms_active = active; }
    bool get_cms_active() const { return _cms_active; }

    /* Observers */

    // Geometry
    Geom::IntPoint get_dimensions() const;
    bool world_point_inside_canvas(Geom::Point const &world) const; // desktop-events.cpp
    Geom::Point canvas_to_world(Geom::Point const &window) const;
    Geom::IntRect get_area_world() const;

    // State
    bool is_dragging() const { return _is_dragging; } // selection-chemistry.cpp

    // Encapsulation leaks
    Cairo::RefPtr<Cairo::ImageSurface> get_backing_store() const; // canvas-item-rotate.cpp
    Cairo::RefPtr<Cairo::Pattern>      get_background_pattern() const { return _background; } // canvas-item-rect.cpp, canvas-item-ctrl.cpp

    /* Methods */

    // Invalidation
    void redraw_all();                                // Mark everything as having changed.
    void redraw_area(Geom::Rect& area);               // Mark a rectangle of world space as having changed.
    void redraw_area(int x0, int y0, int x1, int y1);
    void redraw_area(Geom::Coord x0, Geom::Coord y0, Geom::Coord x1, Geom::Coord y1);
    void request_update();                            // Mark geometry as needing recalculation.

    // Gobblers (tool-base.cpp)
    int gobble_key_events(guint keyval, guint mask);
    void gobble_motion_events(guint mask);

    // Callback run on destructor of any canvas item
    void canvas_item_destructed(Inkscape::CanvasItem *item);

    // State
    Inkscape::CanvasItem *get_current_canvas_item() const { return _current_canvas_item; }
    void                  set_current_canvas_item(Inkscape::CanvasItem *item) {
        _current_canvas_item = item;
    }
    Inkscape::CanvasItem *get_grabbed_canvas_item() const { return _grabbed_canvas_item; }
    void                  set_grabbed_canvas_item(Inkscape::CanvasItem *item, Gdk::EventMask mask) {
        _grabbed_canvas_item = item;
        _grabbed_event_mask = mask;
    }
    void set_drawing_disabled(bool disable); // Disable during path ops, etc.
    void set_all_enter_events(bool on) { _all_enter_events = on; }

protected:

    void get_preferred_width_vfunc (int &minimum_width,  int &natural_width ) const override;
    void get_preferred_height_vfunc(int &minimum_height, int &natural_height) const override;

    // Event handlers
    bool on_scroll_event        (GdkEventScroll*  ) override;
    bool on_button_event        (GdkEventButton*  );
    bool on_button_press_event  (GdkEventButton*  ) override;
    bool on_button_release_event(GdkEventButton*  ) override;
    bool on_enter_notify_event  (GdkEventCrossing*) override;
    bool on_leave_notify_event  (GdkEventCrossing*) override;
    bool on_focus_in_event      (GdkEventFocus*   ) override;
    bool on_key_press_event     (GdkEventKey*     ) override;
    bool on_key_release_event   (GdkEventKey*     ) override;
    bool on_motion_notify_event (GdkEventMotion*  ) override;

    void on_realize() override;
    void on_unrealize() override;
    void on_size_allocate(Gtk::Allocation&) override;
    bool on_draw(const Cairo::RefPtr<Cairo::Context>&) override;

private:

    /* Configuration */

    // Desktop
    SPDesktop *_desktop = nullptr;

    // Drawing
    Inkscape::Drawing *_drawing = nullptr;

    // Canvas item root
    CanvasItemGroup *_canvas_item_root = nullptr;

    // Geometry
    Geom::IntPoint _pos;   ///< Coordinates of top-left pixel of canvas view within canvas.
    Geom::Affine _affine;  ///< The affine that we have been requested to draw at.

    // Background
    Cairo::RefPtr<Cairo::Pattern> _background; ///< The background of the widget.

    // Rendering modes
    Inkscape::RenderMode _render_mode = Inkscape::RenderMode::NORMAL;
    Inkscape::SplitMode _split_mode = Inkscape::SplitMode::NORMAL;
    Inkscape::ColorMode _color_mode = Inkscape::ColorMode::NORMAL;

    // CMS
    std::string _cms_key;
    bool _cms_active = false;

    /* Internal state */

    // Event handling/item picking
    GdkEvent _pick_event;         ///< Event used to find currently selected item.
    bool     _in_repick;          ///< For tracking recursion of pick_current_item().
    bool     _left_grabbed_item;  ///< ?
    bool     _all_enter_events;   ///< Keep all enter events. Only set true in connector-tool.cpp.
    bool     _is_dragging;        ///< Used in selection-chemistry to block undo/redo.
    int      _state;              ///< Last known modifier state (SHIFT, CTRL, etc.).

    Inkscape::CanvasItem *_current_canvas_item;      ///< Item containing cursor, nullptr if none.
    Inkscape::CanvasItem *_current_canvas_item_new;  ///< Item to become _current_item, nullptr if none.
    Inkscape::CanvasItem *_grabbed_canvas_item;      ///< Item that holds a pointer grab; nullptr if none.
    Gdk::EventMask _grabbed_event_mask;

    // Drawing
    bool _drawing_disabled = false;  ///< Disable drawing during critical operations
    bool _need_update = true; // Set true so setting CanvasItem bounds are calculated at least once.

    // Split view
    Inkscape::SplitDirection _split_direction;
    Geom::Point _split_position;
    Inkscape::SplitDirection _hover_direction;
    bool _split_dragging;
    Geom::Point _split_drag_start;

    void add_clippath(const Cairo::RefPtr<Cairo::Context>&);
    void set_cursor();

    // Opaque pointer to implementation
    friend class CanvasPrivate;
    std::unique_ptr<CanvasPrivate> d;
};

} // namespace Widget
} // namespace UI
} // namespace Inkscape

#endif // INKSCAPE_UI_WIDGET_CANVAS_H

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