When rendering a Canvas::Rectangle take its _position into account, even though in...
[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         /* In general, a Rectangle will have a _position of (0,0) within its
55            parent, and its extent is actually defined by _rect. But in the
56            unusual case that _position is set to something other than (0,0),
57            we should take that into account when rendering.
58         */
59         Rect self = item_to_window (_rect.translate (_position));
60         boost::optional<Rect> r = self.intersection (area);
61
62         if (!r) {
63                 return;
64         }
65
66         Rect draw = r.get ();
67
68         if (_fill && !_transparent) {
69                 if (_stops.empty()) {
70                         setup_fill_context (context);
71                 } else {
72                         setup_gradient_context (context, self, Duple (draw.x0, draw.y0));
73                 }
74
75                 context->rectangle (draw.x0, draw.y0, draw.width(), draw.height());
76                 context->fill ();
77         } 
78         
79         if (_outline) {
80
81                 setup_outline_context (context);
82                 
83                 /* see the cairo FAQ on single pixel lines to see why we do
84                  * the 0.5 pixel additions.
85                  */
86
87                 if (_outline_what == What (LEFT|RIGHT|BOTTOM|TOP)) {
88                         
89                         context->rectangle (self.x0 + 0.5, self.y0 + 0.5, self.width() - 1.0, self.height() - 1.0);
90
91                 } else {
92
93                         if (_outline_what & LEFT) {
94                                 /* vertical line: move x-coordinate by 0.5 pixels */
95                                 context->move_to (self.x0 + 0.5, self.y0);
96                                 context->line_to (self.x0 + 0.5, self.y1);
97                         }
98                         
99                         if (_outline_what & TOP) {
100                                 /* horizontal line: move y-coordinate by 0.5 pixels */
101                                 context->move_to (self.x0, self.y0 + 0.5);
102                                 context->line_to (self.x1, self.y0 + 0.5);
103                         }
104
105                         if (_outline_what & BOTTOM) {
106                                 /* horizontal line: move y-coordinate by 0.5 pixels */
107                                 context->move_to (self.x0, self.y1 - 0.5);
108                                 context->line_to (self.x1, self.y1 - 0.5);
109                         }
110                         
111                         if (_outline_what & RIGHT) {
112                                 /* vertical line: move x-coordinate by 0.5 pixels */
113                                 context->move_to (self.x1 + 0.5, self.y0);
114                                 context->line_to (self.x1 + 0.5, self.y1);
115                         }
116                         
117                 }
118                 
119                 context->stroke ();
120         }
121 }
122
123 void
124 Rectangle::compute_bounding_box () const
125 {
126         if (!_rect.empty()) {
127                 Rect r = _rect.fix ();
128                 /* take into acount the 0.5 addition to the bounding
129                    box for the right and bottom edges, see ::render() above
130                 */
131
132                 r.x1 += 1.0; // XXX this makes no sense but is necessary
133                 r.y1 += 0.5;
134
135                 _bounding_box = r;
136         }
137
138         _bounding_box_dirty = false;
139 }
140
141 void
142 Rectangle::set (Rect const & r)
143 {
144         /* We don't update the bounding box here; it's just
145            as cheap to do it when asked.
146         */
147
148         if (r != _rect) {
149                 
150                 begin_change ();
151                 
152                 _rect = r;
153                 
154                 _bounding_box_dirty = true;
155                 end_change ();
156         }
157 }
158
159 void
160 Rectangle::set_x0 (Coord x0)
161 {
162         if (x0 != _rect.x0) {
163                 begin_change ();
164                 
165                 _rect.x0 = x0;
166                 
167                 _bounding_box_dirty = true;
168                 end_change ();
169         }
170 }
171
172 void
173 Rectangle::set_y0 (Coord y0)
174 {
175         if (y0 != _rect.y0) {
176                 begin_change ();
177                 
178                 _rect.y0 = y0;
179                 
180                 _bounding_box_dirty = true;
181                 end_change();
182         }
183 }
184
185 void
186 Rectangle::set_x1 (Coord x1)
187 {
188         if (x1 != _rect.x1) {
189                 begin_change ();
190                 
191                 _rect.x1 = x1;
192                 
193                 _bounding_box_dirty = true;
194                 end_change ();
195         }
196 }
197
198 void
199 Rectangle::set_y1 (Coord y1)
200 {
201         if (y1 != _rect.y1) {
202                 begin_change ();
203                 
204                 _rect.y1 = y1;
205                 
206                 _bounding_box_dirty = true;
207                 end_change ();
208         }
209 }
210
211 void
212 Rectangle::set_outline_what (What what)
213 {
214         if (what != _outline_what) {
215                 begin_visual_change ();
216                 _outline_what = what;
217                 end_visual_change ();
218         }
219 }
220