initial redesign of canvas scrolling to facilitate independent x- and y-axis scrollin...
[ardour.git] / libs / canvas / canvas / item.h
1 /*
2     Copyright (C) 2011-2013 Paul Davis
3     Original Author: Carl Hetherington <cth@carlh.net>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #ifndef __CANVAS_ITEM_H__
21 #define __CANVAS_ITEM_H__
22
23 #include <stdint.h>
24
25 #include <gdk/gdk.h>
26
27 #include <cairomm/context.h>
28
29 #include "pbd/signals.h"
30
31 #include "canvas/visibility.h"
32 #include "canvas/types.h"
33
34 namespace ArdourCanvas
35 {
36
37 class Canvas;
38 class Group;
39 class Rect;     
40
41 /** The parent class for anything that goes on the canvas.
42  *
43  *  Items have a position, which is expressed in the coordinates of the parent.
44  *  They also have a bounding box, which describes the area in which they have
45  *  drawable content, which is expressed in their own coordinates (whose origin
46  *  is at the item position).
47  *
48  *  Any item that is being displayed on a canvas has a pointer to that canvas,
49  *  and all except the `root group' have a pointer to their parent group.
50  */
51         
52 class LIBCANVAS_API Item
53 {
54 public:
55         Item (Canvas *);
56         Item (Group *);
57         Item (Group *, Duple);
58         virtual ~Item ();
59
60         void redraw () const;
61
62         /** Render this item to a Cairo context.
63          *  @param area Area to draw, in **window** coordinates
64          *
65          *  Items must convert their own coordinates into window coordinates
66          *  because Cairo is limited to a fixed point coordinate space that
67          *  does not extend as far as the Ardour timeline. All rendering must
68          *  be done using coordinates that do not exceed the (rough) limits
69          *  of the canvas' window, to avoid odd errors within Cairo as it
70          *  converts doubles into its fixed point format and then tesselates
71          *  the results.
72          */
73         virtual void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const = 0;
74
75         virtual void add_items_at_point (Duple, std::vector<Item const *>& items) const {
76                 items.push_back (this);
77         }
78
79         virtual bool covers (Duple const &) const;
80
81         /** Update _bounding_box and _bounding_box_dirty */
82         virtual void compute_bounding_box () const = 0;
83
84         void grab ();
85         void ungrab ();
86         
87         void unparent ();
88         void reparent (Group *);
89
90         /** @return Parent group, or 0 if this is the root group */
91         Group* parent () const {
92                 return _parent;
93         }
94     
95         uint32_t depth() const;
96         const Item* closest_ancestor_with (const Item& other) const;
97         bool common_ancestor_within (uint32_t, const Item& other) const;
98
99         /** returns true if this item is an ancestor of @param candidate,
100          * and false otherwise. 
101          */
102         bool is_ancestor_of (const Item& candidate) const {
103                 return candidate.is_descendant_of (*this);
104         }
105         /** returns true if this Item is a descendant of @param candidate,
106          * and false otherwise. 
107          */
108         bool is_descendant_of (const Item& candidate) const;
109
110         void set_position (Duple);
111         void set_x_position (Coord);
112         void set_y_position (Coord);
113         void move (Duple);
114
115         enum ScrollSensitivity {
116                 ScrollsVertically = 0x1,
117                 ScrollsHorizontally = 0x2
118         };
119         
120         void set_scroll_sensitivity (ScrollSensitivity s);
121         ScrollSensitivity scroll_sensitivity () const { return _scroll_sensitivity; }
122
123         virtual void scroll_to (Duple const& d);
124         Duple scroll_offset() const { return _scroll_offset; }
125
126         /** @return Position of this item in the parent's coordinates */
127         Duple position () const {
128                 return _position;
129         }
130
131         boost::optional<Rect> bounding_box () const;
132         Coord height() const;
133         Coord width() const;
134
135         Duple item_to_parent (Duple const &) const;
136         Rect item_to_parent (Rect const &) const;
137         Duple parent_to_item (Duple const &) const;
138         Rect parent_to_item (Rect const &) const;
139
140         /* XXX: it's a pity these two aren't the same form as item_to_parent etc.,
141            but it makes a bit of a mess in the rest of the code if they are not.
142         */
143         void canvas_to_item (Coord &, Coord &) const;
144         void item_to_canvas (Coord &, Coord &) const;
145
146         Duple item_to_window (Duple const&, bool rounded = true) const;
147         Duple window_to_item (Duple const&) const;
148         Rect item_to_window (Rect const&) const;
149         Rect window_to_item (Rect const&) const;
150         
151         void raise_to_top ();
152         void raise (int);
153         void lower_to_bottom ();
154
155         void hide ();
156         void show ();
157
158         /** @return true if this item is visible (ie it will be rendered),
159          *  otherwise false
160          */
161         bool visible () const {
162                 return _visible;
163         }
164
165         /** @return Our canvas, or 0 if we are not attached to one */
166         Canvas* canvas () const {
167                 return _canvas;
168         }
169
170         void set_ignore_events (bool);
171         bool ignore_events () const {
172                 return _ignore_events;
173         }
174
175         void set_data (std::string const &, void *);
176         void* get_data (std::string const &) const;
177         
178         /* This is a sigc++ signal because it is solely
179            concerned with GUI stuff and is thus single-threaded
180         */
181
182         template <class T>
183         struct EventAccumulator {
184                 typedef T result_type;
185                 template <class U>
186                 result_type operator () (U first, U last) {
187                         while (first != last) {
188                                 if (*first) {
189                                         return true;
190                                 }
191                                 ++first;
192                         }
193                         return false;
194                 }
195         };
196         
197         sigc::signal1<bool, GdkEvent*, EventAccumulator<bool> > Event;
198
199 #ifdef CANVAS_DEBUG
200         std::string name;
201 #endif
202         
203 #ifdef CANVAS_COMPATIBILITY
204         void grab_focus ();
205 #endif  
206
207         virtual void dump (std::ostream&) const;
208         std::string whatami() const;
209
210 protected:
211
212         /** To be called at the beginning of any property change that
213          *  may alter the bounding box of this item
214          */
215         void begin_change ();
216         /** To be called at the endof any property change that
217          *  may alter the bounding box of this item
218          */
219         void end_change ();
220         /** To be called at the beginning of any property change that
221          *  does NOT alter the bounding box of this item
222          */
223         void begin_visual_change ();
224         /** To be called at the endof any property change that
225          *  does NOT alter the bounding box of this item
226          */
227         void end_visual_change ();
228
229         Canvas* _canvas;
230         /** parent group; may be 0 if we are the root group or if we have been unparent()ed */
231         Group* _parent;
232         /** position of this item in parent coordinates */
233         Duple _position;
234         /** true if this item is visible (ie to be drawn), otherwise false */
235         bool _visible;
236         /** our bounding box before any change that is currently in progress */
237         boost::optional<Rect> _pre_change_bounding_box;
238
239         /** our bounding box; may be out of date if _bounding_box_dirty is true */
240         mutable boost::optional<Rect> _bounding_box;
241         /** true if _bounding_box might be out of date, false if its definitely not */
242         mutable bool _bounding_box_dirty;
243
244         /* XXX: this is a bit grubby */
245         std::map<std::string, void *> _data;
246
247 private:
248         void init ();
249
250         bool _ignore_events;
251         ScrollSensitivity _scroll_sensitivity;
252         Duple _scroll_offset;
253 };
254
255 extern LIBCANVAS_API std::ostream& operator<< (std::ostream&, const ArdourCanvas::Item&);
256
257 }
258
259
260 #endif