fix crash when copy'ing latent plugins
[ardour.git] / libs / canvas / ruler.cc
1 /*
2     Copyright (C) 2014 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 <algorithm>
21 #include <cairomm/context.h>
22
23 #include <pangomm/layout.h>
24
25 #include "canvas/ruler.h"
26 #include "canvas/types.h"
27 #include "canvas/debug.h"
28 #include "canvas/utils.h"
29 #include "canvas/canvas.h"
30
31 using namespace std;
32 using namespace ArdourCanvas;
33
34 Ruler::Ruler (Canvas* c, const Metric& m)
35         : Rectangle (c)
36         , _metric (&m)
37         , _lower (0)
38         , _upper (0)
39         , _divide_height (-1.0)
40         , _font_description (0)
41         , _need_marks (true)
42 {
43 }
44
45 Ruler::Ruler (Canvas* c, const Metric& m, Rect const& r)
46         : Rectangle (c, r)
47         , _metric (&m)
48         , _lower (0)
49         , _upper (0)
50         , _divide_height (-1.0)
51         , _font_description (0)
52         , _need_marks (true)
53 {
54 }
55
56 Ruler::Ruler (Item* parent, const Metric& m)
57         : Rectangle (parent)
58         , _metric (&m)
59         , _lower (0)
60         , _upper (0)
61         , _divide_height (-1.0)
62         , _font_description (0)
63         , _need_marks (true)
64 {
65 }
66
67 Ruler::Ruler (Item* parent, const Metric& m, Rect const& r)
68         : Rectangle (parent, r)
69         , _metric (&m)
70         , _lower (0)
71         , _upper (0)
72         , _divide_height (-1.0)
73         , _font_description (0)
74         , _need_marks (true)
75 {
76 }
77
78 void
79 Ruler::set_range (double l, double u)
80 {
81         begin_visual_change ();
82         _lower = l;
83         _upper = u;
84         _need_marks = true;
85         end_visual_change ();
86 }
87
88 void
89 Ruler::set_font_description (Pango::FontDescription fd)
90 {
91         begin_visual_change ();
92         delete _font_description;
93         _font_description = new Pango::FontDescription (fd);
94         end_visual_change ();
95 }
96
97 void
98 Ruler::render (Rect const & area, Cairo::RefPtr<Cairo::Context> cr) const
99 {
100         if (_lower == _upper) {
101                 /* nothing to draw */
102                 return;
103         }
104
105         Rect self (item_to_window (get()));
106         boost::optional<Rect> i = self.intersection (area);
107
108         if (!i) {
109                 return;
110         }
111
112         Rect intersection (i.get());
113
114         Distance height = self.height();
115
116         if (_need_marks) {
117                 marks.clear ();
118                 _metric->get_marks (marks, _lower, _upper, 50);
119                 _need_marks = false;
120         }
121
122         /* draw background */
123
124         setup_fill_context (cr);
125         cr->rectangle (intersection.x0, intersection.y0, intersection.width(), intersection.height());
126         cr->fill ();
127
128         /* switch to outline context */
129
130         setup_outline_context (cr);
131
132         /* draw line on lower edge as a separator */
133
134         if (_outline_width == 1.0) {
135                 /* Cairo single pixel line correction */
136                 cr->move_to (self.x0, self.y1-0.5);
137                 cr->line_to (self.x1, self.y1-0.5);
138         } else {
139                 cr->move_to (self.x0, self.y1);
140                 cr->line_to (self.x1, self.y1);
141         }
142         cr->stroke ();
143
144         /* draw ticks + text */
145
146         Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create (cr);
147         if (_font_description) {
148                 layout->set_font_description (*_font_description);
149         }
150
151         for (vector<Mark>::const_iterator m = marks.begin(); m != marks.end(); ++m) {
152                 Duple pos;
153
154                 pos.x = floor ((m->position - _lower) / _metric->units_per_pixel);
155                 pos.y = self.y1; /* bottom edge */
156
157                 if (_outline_width == 1.0) {
158                         /* Cairo single pixel line correction */
159                         cr->move_to (pos.x + 0.5, pos.y);
160                 } else {
161                         cr->move_to (pos.x, pos.y);
162                 }
163
164                 switch (m->style) {
165                 case Mark::Major:
166                         if (_divide_height >= 0) {
167                                 cr->rel_line_to (0, -_divide_height);
168                         } else {
169                                 cr->rel_line_to (0, -height);
170                         }
171                         break;
172                 case Mark::Minor:
173                         cr->rel_line_to (0, -height/3.0);
174                         break;
175                 case Mark::Micro:
176                         cr->rel_line_to (0, -height/5.0);
177                         break;
178                 }
179                 cr->stroke ();
180
181                 /* and the text */
182
183                 if (!m->label.empty()) {
184                         Pango::Rectangle logical;
185
186                         layout->set_text (m->label);
187                         logical = layout->get_pixel_logical_extents ();
188
189                         if (_divide_height >= 0) {
190                                 cr->move_to (pos.x + 2.0, self.y0 + _divide_height + logical.get_y() + 2.0); /* 2 pixel padding below divider */
191                         } else {
192                                 cr->move_to (pos.x + 2.0, self.y0 + logical.get_y());
193                         }
194                         layout->show_in_cairo_context (cr);
195                 }
196         }
197
198         if (_divide_height >= 0.0) {
199
200                 cr->set_line_width (1.0);
201
202                 set_source_rgba (cr, _divider_color_top);
203                 cr->move_to (self.x0, self.y0 + _divide_height-1.0+0.5);
204                 cr->line_to (self.x1, self.y0 + _divide_height-1.0+0.5);
205                 cr->stroke ();
206
207                 set_source_rgba (cr, _divider_color_bottom);
208                 cr->move_to (self.x0, self.y0 + _divide_height+0.5);
209                 cr->line_to (self.x1, self.y0 + _divide_height+0.5);
210                 cr->stroke ();
211
212
213         }
214
215         /* done! */
216 }
217
218 void
219 Ruler::set_divide_height (double h)
220 {
221         _divide_height = h;
222 }
223
224 void
225 Ruler::set_divide_colors (Color t, Color b)
226 {
227         _divider_color_bottom = b;
228         _divider_color_top = t;
229 }
230
231 void
232 Ruler::set_metric (const Metric& m)
233 {
234         _metric = &m;
235         _need_marks = true;
236         redraw ();
237 }