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 Rectangle::Rectangle (Group* parent, Rect const & rect)
42         : Item (parent)
43         , Outline (parent)
44         , Fill (parent)
45         , _rect (rect)
46         , _outline_what ((What) (LEFT | RIGHT | TOP | BOTTOM))
47 {
48         
49 }
50
51 void
52 Rectangle::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
53 {
54         Rect self = item_to_window (_rect);
55         boost::optional<Rect> r = self.intersection (area);
56
57         if (!r) {
58                 std::cerr << whatami() << '/' << name << " not covered by render area! ... " << self << " vs. " << area << std::endl;
59                 return;
60         }
61
62         Rect draw = r.get ();
63
64         if (_fill) {
65                 if (_stops.empty()) {
66                         setup_fill_context (context);
67                 } else {
68                         setup_gradient_context (context, self, Duple (draw.x0, draw.y0));
69                 }
70
71                 context->rectangle (draw.x0, draw.y0, draw.width(), draw.height());
72                 context->fill ();
73         } 
74         
75         if (_outline) {
76
77                 setup_outline_context (context);
78                 
79                 if (_outline_what == What (LEFT|RIGHT|BOTTOM|TOP)) {
80                         
81                         /* outline must be on pixels (hence 0.5 offset) and
82                            must be WITHIN coordinates of rect, not outside it
83                            (hence the -2.0 size adjustment, since we use 1
84                            pixel on each side for the outline)
85                         */
86
87                         context->rectangle (self.x0 + 0.5, self.y0 + 0.5, self.width() - 1.0, self.height() - 1.0);
88
89                 } else {
90
91                         // context->set_line_cap (Cairo::LINE_CAP_SQUARE);
92                         
93                         /* see the cairo FAQ on single pixel lines to see why we do
94                          * this expansion of the perimeter.
95                          */
96
97                         if (_outline_what & LEFT) {
98                                 /* vertical line: move x-coordinate by 0.5 pixels */
99                                 context->move_to (self.x0 + 0.5, self.y0);
100                                 context->line_to (self.x0 + 0.5, self.y1);
101                         }
102                         
103                         if (_outline_what & BOTTOM) {
104                                 /* horizontal line: move y-coordinate by 0.5 pixels */
105                                 context->move_to (self.x0, self.y1 - 0.5);
106                                 context->line_to (self.x1, self.y1 - 0.5);
107                         }
108                         
109                         if (_outline_what & RIGHT) {
110                                 /* vertical line: move x-coordinate by 0.5 pixels */
111                                 context->move_to (self.x1 - 0.5, self.y0);
112                                 context->line_to (self.x1 - 0.5, self.y1);
113                         }
114                         
115                         if (_outline_what & TOP) {
116                                 /* horizontal line: move y-coordinate by 0.5 pixels */
117                                 context->move_to (self.x0, self.y0 + 0.5);
118                                 context->line_to (self.x1, self.y0 + 0.5);
119                         }
120                 }
121                 
122                 context->stroke ();
123         }
124 }
125
126 void
127 Rectangle::compute_bounding_box () const
128 {
129         if (!_rect.empty()) {
130                 Rect r = _rect.fix ();
131
132                 /* our outlines are always inside our coordinates, but we have
133                  * to ensure that our bounding box fully *contains* the
134                  * rectangle
135                  *
136                  * XXX: or something like that, waffle.
137                  *
138                  */
139                 _bounding_box = _rect.fix ();
140         }
141
142         _bounding_box_dirty = false;
143 }
144
145 void
146 Rectangle::set (Rect const & r)
147 {
148         /* We don't update the bounding box here; it's just
149            as cheap to do it when asked.
150         */
151         
152         begin_change ();
153         
154         _rect = r;
155         
156         _bounding_box_dirty = true;
157         end_change ();
158
159         DEBUG_TRACE (PBD::DEBUG::CanvasItemsDirtied, "canvas item dirty: rectangle change (set)\n");
160 }
161
162 void
163 Rectangle::set_x0 (Coord x0)
164 {
165         begin_change ();
166
167         _rect.x0 = x0;
168
169         _bounding_box_dirty = true;
170         end_change ();
171
172         DEBUG_TRACE (PBD::DEBUG::CanvasItemsDirtied, "canvas item dirty: rectangle change (x0)\n");
173 }
174
175 void
176 Rectangle::set_y0 (Coord y0)
177 {
178         begin_change ();
179         
180         _rect.y0 = y0;
181
182         _bounding_box_dirty = true;
183         end_change();
184
185         DEBUG_TRACE (PBD::DEBUG::CanvasItemsDirtied, "canvas item dirty: rectangle change (y0)\n");
186 }
187
188 void
189 Rectangle::set_x1 (Coord x1)
190 {
191         begin_change ();
192         
193         _rect.x1 = x1;
194
195         _bounding_box_dirty = true;
196         end_change ();
197
198         DEBUG_TRACE (PBD::DEBUG::CanvasItemsDirtied, "canvas item dirty: rectangle change (x1)\n");
199 }
200
201 void
202 Rectangle::set_y1 (Coord y1)
203 {
204         begin_change ();
205
206         _rect.y1 = y1;
207
208         _bounding_box_dirty = true;
209         end_change ();
210
211         DEBUG_TRACE (PBD::DEBUG::CanvasItemsDirtied, "canvas item dirty: rectangle change (y1)\n");
212 }
213
214 void
215 Rectangle::set_outline_what (What what)
216 {
217         begin_change ();
218         
219         _outline_what = what;
220
221         end_change ();
222 }
223
224 void
225 Rectangle::set_outline_what (int what)
226 {
227         set_outline_what ((What) what);
228 }
229