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