7abf13216b4e06e99f9874bfeda438852740884b
[ardour.git] / libs / canvas / rectangle.cc
1 /*
2     Copyright (C) 2011-2013 Paul Davis
3     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 #include <iostream>
21 #include <cairomm/context.h>
22 #include "pbd/stacktrace.h"
23 #include "pbd/compose.h"
24
25 #include "canvas/canvas.h"
26 #include "canvas/rectangle.h"
27 #include "canvas/debug.h"
28 #include "canvas/utils.h"
29
30 using namespace std;
31 using namespace ArdourCanvas;
32
33 Rectangle::Rectangle (Group* parent)
34         : Item (parent)
35         , Outline (parent)
36         , Fill (parent)
37         , _outline_what ((What) (LEFT | RIGHT | TOP | BOTTOM))
38 {
39
40 }
41
42 Rectangle::Rectangle (Group* parent, Rect const & rect)
43         : Item (parent)
44         , Outline (parent)
45         , Fill (parent)
46         , _rect (rect)
47         , _outline_what ((What) (LEFT | RIGHT | TOP | BOTTOM))
48 {
49         
50 }
51
52 void
53 Rectangle::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
54 {
55         /* Cairo goes a little (!) wrong when asked to fill/stroke rectangles that
56          * extend way beyond the surface boundaries. To avoid this issue,
57          * clamp what we are drawing using the absolute end of the visible
58          * canvas, converting to item-space coordinates, of course.
59          */
60
61         Rect self = item_to_window (_rect);
62         boost::optional<Rect> draw = self.intersection (area);
63
64         if (_fill && draw) {
65                 setup_fill_context (context);
66
67                 context->rectangle (draw->x0, draw->y0, draw->width(), draw->height());
68                 
69                 if (!_outline) {
70                         context->fill ();
71                 } else {
72                         
73                         /* special/common case: outline the entire rectangle is
74                          * requested, so just use the same path for the fill
75                          * and stroke.
76                          */
77
78                         if (_outline_what == What (LEFT|RIGHT|BOTTOM|TOP)) {
79                                 context->fill_preserve();
80                                 setup_outline_context (context);
81                                 context->stroke ();
82                         } else {
83                                 context->fill ();
84                         }
85                 }
86         } 
87         
88         if (_outline) {
89                 
90                 setup_outline_context (context);
91
92                 if (_outline_what == What (LEFT|RIGHT|BOTTOM|TOP)) {
93
94                         /* if we filled and use full outline, we are already
95                          * done. otherwise, draw the frame here.
96                          */
97
98                         if (!_fill) { 
99                                 context->rectangle (draw->x0, draw->y0, draw->width(), draw->height());
100                                 context->stroke ();
101                         }
102                         
103                 } else {
104                         
105                         if (_outline_what & LEFT) {
106                                 context->move_to (draw->x0, draw->y0);
107                                 context->line_to (draw->x0, draw->y1);
108                         }
109                         
110                         if (_outline_what & BOTTOM) {
111                                 context->move_to (draw->x0, draw->y1);
112                                 context->line_to (draw->x1, draw->y1);
113                         }
114                         
115                         if (_outline_what & RIGHT) {
116                                 context->move_to (draw->x1, draw->y0);
117                                 context->line_to (draw->x1, draw->y1);
118                         }
119                         
120                         if (_outline_what & TOP) {
121                                 context->move_to (draw->x0, draw->y0);
122                                 context->line_to (draw->x1, draw->y0);
123                         }
124                         
125                         context->stroke ();
126                 }
127         }
128 }
129
130 void
131 Rectangle::compute_bounding_box () const
132 {
133         Rect r = _rect.fix ();
134         _bounding_box = boost::optional<Rect> (r.expand (_outline_width / 2));
135         
136         _bounding_box_dirty = false;
137 }
138
139 void
140 Rectangle::set (Rect const & r)
141 {
142         /* We don't update the bounding box here; it's just
143            as cheap to do it when asked.
144         */
145         
146         begin_change ();
147         
148         _rect = r;
149         
150         _bounding_box_dirty = true;
151         end_change ();
152
153         DEBUG_TRACE (PBD::DEBUG::CanvasItemsDirtied, "canvas item dirty: rectangle change (set)\n");
154 }
155
156 void
157 Rectangle::set_x0 (Coord x0)
158 {
159         begin_change ();
160
161         _rect.x0 = x0;
162
163         _bounding_box_dirty = true;
164         end_change ();
165
166         DEBUG_TRACE (PBD::DEBUG::CanvasItemsDirtied, "canvas item dirty: rectangle change (x0)\n");
167 }
168
169 void
170 Rectangle::set_y0 (Coord y0)
171 {
172         begin_change ();
173         
174         _rect.y0 = y0;
175
176         _bounding_box_dirty = true;
177         end_change();
178
179         DEBUG_TRACE (PBD::DEBUG::CanvasItemsDirtied, "canvas item dirty: rectangle change (y0)\n");
180 }
181
182 void
183 Rectangle::set_x1 (Coord x1)
184 {
185         begin_change ();
186         
187         _rect.x1 = x1;
188
189         _bounding_box_dirty = true;
190         end_change ();
191
192         DEBUG_TRACE (PBD::DEBUG::CanvasItemsDirtied, "canvas item dirty: rectangle change (x1)\n");
193 }
194
195 void
196 Rectangle::set_y1 (Coord y1)
197 {
198         begin_change ();
199
200         _rect.y1 = y1;
201
202         _bounding_box_dirty = true;
203         end_change ();
204
205         DEBUG_TRACE (PBD::DEBUG::CanvasItemsDirtied, "canvas item dirty: rectangle change (y1)\n");
206 }
207
208 void
209 Rectangle::set_outline_what (What what)
210 {
211         begin_change ();
212         
213         _outline_what = what;
214
215         end_change ();
216 }
217
218 void
219 Rectangle::set_outline_what (int what)
220 {
221         set_outline_what ((What) what);
222 }
223