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