add API to Ruler to optionally draw a divider (2 single pixel horizontal lines) at...
[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                         cr->rel_line_to (0, -height);
162                         break;
163                 case Mark::Minor:
164                         cr->rel_line_to (0, -height/2.0);
165                         break;
166                 case Mark::Micro:
167                         cr->rel_line_to (0, -height/4.0);
168                         break;
169                 }
170                 cr->stroke ();
171
172                 /* and the text */
173
174                 if (!m->label.empty()) {
175                         Pango::Rectangle logical;
176
177                         layout->set_text (m->label);
178                         logical = layout->get_pixel_logical_extents ();
179                         
180                         cr->move_to (pos.x + 2.0, self.y0 + logical.get_y());
181                         layout->show_in_cairo_context (cr);
182                 }
183         }
184
185         if (_divide_height >= 0.0) {
186                 
187                 cr->set_line_width (1.0);
188
189                 set_source_rgba (cr, _divider_color_top);
190                 cr->move_to (self.x0, self.y0 + _divide_height+0.5);
191                 cr->line_to (self.x1, self.y0 + _divide_height+0.5);
192                 cr->stroke ();
193
194                 set_source_rgba (cr, _divider_color_bottom);
195                 cr->move_to (self.x0, self.y0 + _divide_height+1.5);
196                 cr->line_to (self.x1, self.y0 + _divide_height+1.5);
197                 cr->stroke ();
198
199
200         }
201
202         /* done! */
203 }
204
205 void
206 Ruler::set_divide_height (double h)
207 {
208         _divide_height = h;
209 }
210
211 void
212 Ruler::set_metric (const Metric& m)
213 {
214         _metric = &m;
215         _need_marks = true;
216         redraw ();
217 }