merge with master.
[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         , _need_marks (true)
40 {
41 }
42
43 Ruler::Ruler (Canvas* c, const Metric& m, Rect const& r)
44         : Rectangle (c, r)
45         , _metric (m)
46         , _lower (0)
47         , _upper (0)
48         , _need_marks (true)
49 {
50 }
51
52 Ruler::Ruler (Item* parent, const Metric& m)
53         : Rectangle (parent)
54         , _metric (m)
55         , _lower (0)
56         , _upper (0)
57         , _need_marks (true)
58 {
59 }
60
61 Ruler::Ruler (Item* parent, const Metric& m, Rect const& r)
62         : Rectangle (parent, r)
63         , _metric (m)
64         , _lower (0)
65         , _upper (0)
66         , _need_marks (true)
67 {
68 }
69
70 void
71 Ruler::set_range (double l, double u)
72 {
73         begin_visual_change ();
74         _lower = l;
75         _upper = u;
76         _need_marks = true;
77         end_visual_change ();
78 }
79
80 void
81 Ruler::set_font_description (Pango::FontDescription fd)
82 {
83         begin_visual_change ();
84         _font_description = new Pango::FontDescription (fd);
85         end_visual_change ();
86 }
87
88 void
89 Ruler::render (Rect const & area, Cairo::RefPtr<Cairo::Context> cr) const
90 {
91         if (_lower == _upper) {
92                 /* nothing to draw */
93                 return;
94         }
95
96         Rect self (item_to_window (get()));
97         boost::optional<Rect> i = self.intersection (area);
98
99         if (!i) {
100                 return;
101         }
102
103         Rect intersection (i.get());
104
105         Distance height = self.height();
106
107         if (_need_marks) {
108                 marks.clear ();
109                 _metric.get_marks (marks, _lower, _upper, 50);
110                 _need_marks = false;
111         }
112
113         /* draw background */
114
115         setup_fill_context (cr);
116         cr->rectangle (intersection.x0, intersection.y0, intersection.width(), intersection.height());
117         cr->fill ();
118
119         /* switch to outline context */
120
121         setup_outline_context (cr);
122
123         /* draw line on lower edge as a separator */
124
125         if (_outline_width == 1.0) {
126                 /* Cairo single pixel line correction */
127                 cr->move_to (self.x0, self.y1-0.5);
128                 cr->line_to (self.x1, self.y1-0.5);
129         } else {
130                 cr->move_to (self.x0, self.y1);
131                 cr->line_to (self.x1, self.y1);
132         }
133         cr->stroke ();
134
135         /* draw ticks + text */
136         
137         Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create (cr);
138         if (_font_description) {
139                 layout->set_font_description (*_font_description);
140         }
141
142         for (vector<Mark>::const_iterator m = marks.begin(); m != marks.end(); ++m) {
143                 Duple pos;
144
145                 pos.x = floor ((m->position - _lower) / _metric.units_per_pixel);
146                 pos.y = self.y1; /* bottom edge */
147
148                 if (_outline_width == 1.0) {
149                         /* Cairo single pixel line correction */
150                         cr->move_to (pos.x + 0.5, pos.y);
151                 } else {
152                         cr->move_to (pos.x, pos.y);
153                 }
154                 
155                 switch (m->style) {
156                 case Mark::Major:
157                         cr->rel_line_to (0, -height);
158                         break;
159                 case Mark::Minor:
160                         cr->rel_line_to (0, -height/2.0);
161                         break;
162                 case Mark::Micro:
163                         cr->rel_line_to (0, -height/4.0);
164                         break;
165                 }
166                 cr->stroke ();
167
168                 /* and the text */
169
170                 if (!m->label.empty()) {
171                         Pango::Rectangle logical;
172
173                         layout->set_text (m->label);
174                         logical = layout->get_pixel_logical_extents ();
175                         
176                         cr->move_to (pos.x + 2.0, self.y0 + logical.get_y());
177                         layout->show_in_cairo_context (cr);
178                 }
179         }
180
181         /* done! */
182 }