more changes for rectangle outlining
[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 (Canvas* c)
34         : Item (c)
35         , _outline_what ((What) (LEFT | RIGHT | TOP | BOTTOM))
36 {
37 }
38
39 Rectangle::Rectangle (Canvas* c, Rect const & rect)
40         : Item (c)
41         , _rect (rect)
42         , _outline_what ((What) (LEFT | RIGHT | TOP | BOTTOM))
43 {
44 }
45
46 Rectangle::Rectangle (Item* parent)
47         : Item (parent)
48         , _outline_what ((What) (LEFT | RIGHT | TOP | BOTTOM))
49 {
50 }
51
52 Rectangle::Rectangle (Item* parent, Rect const & rect)
53         : Item (parent)
54         , _rect (rect)
55         , _outline_what ((What) (LEFT | RIGHT | TOP | BOTTOM))
56 {
57 }
58
59 void
60 Rectangle::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
61 {
62         /* In general, a Rectangle will have a _position of (0,0) within its
63            parent, and its extent is actually defined by _rect. But in the
64            unusual case that _position is set to something other than (0,0),
65            we should take that into account when rendering.
66         */
67         Rect self = item_to_window (_rect.translate (_position));
68         boost::optional<Rect> r = self.intersection (area);
69
70         if (!r) {
71                 return;
72         }
73
74         Rect draw = r.get ();
75
76         if (_fill && !_transparent) {
77                 if (_stops.empty()) {
78                         setup_fill_context (context);
79                 } else {
80                         setup_gradient_context (context, self, Duple (draw.x0, draw.y0));
81                 }
82
83                 context->rectangle (draw.x0, draw.y0, draw.width(), draw.height());
84                 context->fill ();
85         } 
86         
87         if (_outline) {
88
89                 setup_outline_context (context);
90                 
91                 /* the goal here is that if the border is 1 pixel
92                  * thick, it will precisely align with the corner
93                  * coordinates of the rectangle. So if the rectangle
94                  * has a left edge at 0 and a right edge at 10, then
95                  * the left edge must span -0.5..+0.5, the right edge
96                  * must span 9.5..10.5 (i.e. the single full color
97                  * pixel is precisely aligned with 0 and 10
98                  * respectively).
99                  *
100                  * we have to shift left/up in all cases, which means
101                  * subtraction along both axes (i.e. edge at
102                  * N, outline must start at N-0.5). 
103                  *
104                  * see the cairo FAQ on single pixel lines to see why we do
105                  * the 0.5 pixel additions.
106                  */
107
108                 self = self.translate (Duple (-0.5, -0.5));
109
110                 std::cerr << "Outline using " << self << " from " << _rect << std::endl;
111                 
112                 if (_outline_what == What (LEFT|RIGHT|BOTTOM|TOP)) {
113                         
114                         context->rectangle (self.x0, self.y0, self.width(), self.height());
115
116                 } else {
117
118                         if (_outline_what & LEFT) {
119                                 context->move_to (self.x0, self.y0);
120                                 context->line_to (self.x0, self.y1);
121                         }
122                         
123                         if (_outline_what & TOP) {
124                                 context->move_to (self.x0, self.y0);
125                                 context->line_to (self.x1, self.y0);
126                         }
127
128                         if (_outline_what & BOTTOM) {
129                                 context->move_to (self.x0, self.y1);
130                                 context->line_to (self.x1, self.y1);
131                         }
132                         
133                         if (_outline_what & RIGHT) {
134                                 context->move_to (self.x1, self.y0);
135                                 context->line_to (self.x1, self.y1);
136                         }
137                 }
138                 
139                 context->stroke ();
140         }
141 }
142
143 void
144 Rectangle::compute_bounding_box () const
145 {
146         if (!_rect.empty()) {
147                 Rect r = _rect.fix ();
148
149                 /* take into acount the 0.5 addition to the bounding
150                    box for the right and bottom edges, see ::render() above
151                 */
152
153                 r = r.expand (1.0);
154
155                 _bounding_box = r;
156         }
157
158         _bounding_box_dirty = false;
159 }
160
161 void
162 Rectangle::set (Rect const & r)
163 {
164         /* We don't update the bounding box here; it's just
165            as cheap to do it when asked.
166         */
167
168         if (r != _rect) {
169                 
170                 begin_change ();
171                 
172                 _rect = r;
173                 
174                 _bounding_box_dirty = true;
175                 end_change ();
176         }
177 }
178
179 void
180 Rectangle::set_x0 (Coord x0)
181 {
182         if (x0 != _rect.x0) {
183                 begin_change ();
184                 
185                 _rect.x0 = x0;
186                 
187                 _bounding_box_dirty = true;
188                 end_change ();
189         }
190 }
191
192 void
193 Rectangle::set_y0 (Coord y0)
194 {
195         if (y0 != _rect.y0) {
196                 begin_change ();
197                 
198                 _rect.y0 = y0;
199                 
200                 _bounding_box_dirty = true;
201                 end_change();
202         }
203 }
204
205 void
206 Rectangle::set_x1 (Coord x1)
207 {
208         if (x1 != _rect.x1) {
209                 begin_change ();
210                 
211                 _rect.x1 = x1;
212                 
213                 _bounding_box_dirty = true;
214                 end_change ();
215         }
216 }
217
218 void
219 Rectangle::set_y1 (Coord y1)
220 {
221         if (y1 != _rect.y1) {
222                 begin_change ();
223                 
224                 _rect.y1 = y1;
225                 
226                 _bounding_box_dirty = true;
227                 end_change ();
228         }
229 }
230
231 void
232 Rectangle::set_outline_what (What what)
233 {
234         if (what != _outline_what) {
235                 begin_visual_change ();
236                 _outline_what = what;
237                 end_visual_change ();
238         }
239 }
240