Merge branch 'cairocanvas' of git.ardour.org:ardour/ardour into cairocanvas
[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 plot = _rect;
62         Rect visible = _canvas->visible_area();
63         Duple visible_end = canvas_to_item (Duple (visible.x1, visible.y1));
64
65         plot.x1 = min (plot.x1, visible_end.x);
66         plot.y1 = min (plot.y1, visible_end.y); 
67
68         if (_fill) {
69                 setup_fill_context (context);
70                 context->rectangle (plot.x0, plot.y0, plot.width(), plot.height());
71                 
72                 if (!_outline) {
73                         context->fill ();
74                 } else {
75                         
76                         /* special/common case: outline the entire rectangle is
77                          * requested, so just use the same path for the fill
78                          * and stroke.
79                          */
80
81                         if (_outline_what == What (LEFT|RIGHT|BOTTOM|TOP)) {
82                                 context->fill_preserve();
83                                 setup_outline_context (context);
84                                 context->stroke ();
85                         } else {
86                                 context->fill ();
87                         }
88                 }
89         } 
90         
91         if (_outline) {
92                 
93                 if (_outline_what == What (LEFT|RIGHT|BOTTOM|TOP)) {
94
95                         /* if we filled and use full outline, we are already done */
96
97                         if (!_fill) { 
98                                 context->rectangle (plot.x0, plot.y0, plot.width(), plot.height());
99                                 setup_outline_context (context);
100                                 context->stroke ();
101                         }
102                         
103                 } else {
104                         
105                         if (_outline_what & LEFT) {
106                                 context->move_to (plot.x0, plot.y0);
107                                 context->line_to (plot.x0, plot.y1);
108                         }
109                         
110                         if (_outline_what & BOTTOM) {
111                                 context->move_to (plot.x0, plot.y1);
112                                 context->line_to (plot.x1, plot.y1);
113                         }
114                         
115                         if (_outline_what & RIGHT) {
116                                 context->move_to (plot.x1, plot.y0);
117                                 context->line_to (plot.x1, plot.y1);
118                         }
119                         
120                         if (_outline_what & TOP) {
121                                 context->move_to (plot.x0, plot.y0);
122                                 context->line_to (plot.x1, plot.y0);
123                         }
124                         
125                         setup_outline_context (context);
126                         context->stroke ();
127                 }
128         }
129 }
130
131 void
132 Rectangle::compute_bounding_box () const
133 {
134         Rect r = _rect.fix ();
135         _bounding_box = boost::optional<Rect> (r.expand (_outline_width / 2));
136         
137         _bounding_box_dirty = false;
138 }
139
140 void
141 Rectangle::set (Rect const & r)
142 {
143         /* We don't update the bounding box here; it's just
144            as cheap to do it when asked.
145         */
146         
147         begin_change ();
148         
149         _rect = r;
150         
151         _bounding_box_dirty = true;
152         end_change ();
153
154         DEBUG_TRACE (PBD::DEBUG::CanvasItemsDirtied, "canvas item dirty: rectangle change (set)\n");
155 }
156
157 void
158 Rectangle::set_x0 (Coord x0)
159 {
160         begin_change ();
161
162         _rect.x0 = x0;
163
164         _bounding_box_dirty = true;
165         end_change ();
166
167         DEBUG_TRACE (PBD::DEBUG::CanvasItemsDirtied, "canvas item dirty: rectangle change (x0)\n");
168 }
169
170 void
171 Rectangle::set_y0 (Coord y0)
172 {
173         begin_change ();
174         
175         _rect.y0 = y0;
176
177         _bounding_box_dirty = true;
178         end_change();
179
180         DEBUG_TRACE (PBD::DEBUG::CanvasItemsDirtied, "canvas item dirty: rectangle change (y0)\n");
181 }
182
183 void
184 Rectangle::set_x1 (Coord x1)
185 {
186         begin_change ();
187         
188         _rect.x1 = x1;
189
190         _bounding_box_dirty = true;
191         end_change ();
192
193         DEBUG_TRACE (PBD::DEBUG::CanvasItemsDirtied, "canvas item dirty: rectangle change (x1)\n");
194 }
195
196 void
197 Rectangle::set_y1 (Coord y1)
198 {
199         begin_change ();
200
201         _rect.y1 = y1;
202
203         _bounding_box_dirty = true;
204         end_change ();
205
206         DEBUG_TRACE (PBD::DEBUG::CanvasItemsDirtied, "canvas item dirty: rectangle change (y1)\n");
207 }
208
209 void
210 Rectangle::set_outline_what (What what)
211 {
212         begin_change ();
213         
214         _outline_what = what;
215
216         end_change ();
217 }
218
219 void
220 Rectangle::set_outline_what (int what)
221 {
222         set_outline_what ((What) what);
223 }
224