1 /* === S Y N F I G ========================================================= */
2 /*! \file widget_ruler.cpp
3 ** \brief Template File
4 **
5 ** $Id$
6 **
7 ** \legal
8 ** ......... ... 2014 Ivan Mahonin
9 **
10 ** This package is free software; you can redistribute it and/or
11 ** modify it under the terms of the GNU General Public License as
12 ** published by the Free Software Foundation; either version 2 of
13 ** the License, or (at your option) any later version.
14 **
15 ** This package is distributed in the hope that it will be useful,
16 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 ** General Public License for more details.
19 ** \endlegal
20 */
21 /* ========================================================================= */
22
23 /* === H E A D E R S ======================================================= */
24
25 #ifdef USING_PCH
26 # include "pch.h"
27 #else
28 #ifdef HAVE_CONFIG_H
29 # include <config.h>
30 #endif
31
32
33 #include <synfig/general.h>
34
35 #include <ETL/stringf>
36
37 #include "widgets/widget_ruler.h"
38
39 #include <gui/localization.h>
40
41 #endif
42
43 /* === U S I N G =========================================================== */
44
45 using namespace std;
46 using namespace synfig;
47 using namespace studio;
48
49 /* === M A C R O S ========================================================= */
50
51 /* === G L O B A L S ======================================================= */
52
53 /* === P R O C E D U R E S ================================================= */
54
55 /* === M E T H O D S ======================================================= */
56
Widget_Ruler(bool is_vertical)57 Widget_Ruler::Widget_Ruler(bool is_vertical):
58 is_vertical(is_vertical),
59 layout(Pango::Layout::create(get_pango_context())),
60 min(0.0),
61 max(0.0),
62 position(0.0)
63 { }
64
~Widget_Ruler()65 Widget_Ruler::~Widget_Ruler() { }
66
67 void
set_min(synfig::Real value)68 Widget_Ruler::set_min(synfig::Real value)
69 { if (min != value) { min = value; this->queue_draw(); } }
70
71 void
set_max(synfig::Real value)72 Widget_Ruler::set_max(synfig::Real value)
73 { if (max != value) { max = value; this->queue_draw(); } }
74
75 void
set_position(synfig::Real value)76 Widget_Ruler::set_position(synfig::Real value)
77 { if (position != value) { position = value; this->queue_draw(); } }
78
79 void
draw_line(const::Cairo::RefPtr<::Cairo::Context> & cr,synfig::Real position,synfig::Real size,const Gdk::RGBA & color,synfig::Real width,synfig::Real height)80 Widget_Ruler::draw_line(
81 const ::Cairo::RefPtr< ::Cairo::Context>& cr,
82 synfig::Real position,
83 synfig::Real size,
84 const Gdk::RGBA &color,
85 synfig::Real width,
86 synfig::Real height )
87 {
88 position = round(position) - 0.5;
89
90 cr->set_line_width(0.5);
91 cr->set_source_rgba(color.get_red(), color.get_green(), color.get_blue(), color.get_alpha());
92 if (is_vertical) {
93 cr->move_to(width - size, position);
94 cr->line_to(width, position);
95 }
96 else
97 {
98 cr->move_to(position, height - size);
99 cr->line_to(position, height);
100 }
101 cr->stroke();
102 }
103
104 void
draw_text(const::Cairo::RefPtr<::Cairo::Context> & cr,synfig::Real position,const synfig::String & text,int size,const Gdk::RGBA & color,synfig::Real offset,synfig::Real width,synfig::Real height)105 Widget_Ruler::draw_text(
106 const ::Cairo::RefPtr< ::Cairo::Context>& cr,
107 synfig::Real position,
108 const synfig::String &text,
109 int size,
110 const Gdk::RGBA &color,
111 synfig::Real offset,
112 synfig::Real width,
113 synfig::Real height )
114 {
115 layout->set_text(text);
116
117 int w = 0, h = 0;
118 Pango::AttrList attr_list;
119 Pango::AttrInt pango_size(Pango::Attribute::create_attr_size(Pango::SCALE*size));
120 pango_size.set_start_index(0);
121 pango_size.set_end_index(64);
122 attr_list.change(pango_size);
123 layout->set_attributes(attr_list);
124 layout->get_pixel_size(w, h);
125
126 if (is_vertical) {
127 cr->save();
128 cr->set_source_rgba(color.get_red(), color.get_green(), color.get_blue(), color.get_alpha());
129 cr->rotate_degrees(-90.f);
130 cr->move_to(-position + 3, width - offset - h);
131 layout->show_in_cairo_context(cr);
132 cr->restore();
133 } else {
134 cr->set_source_rgba(color.get_red(), color.get_green(), color.get_blue(), color.get_alpha());
135 cr->move_to(position + 3, height - offset - h);
136 layout->show_in_cairo_context(cr);
137 }
138 }
139
140 bool
on_draw(const::Cairo::RefPtr<::Cairo::Context> & cr)141 Widget_Ruler::on_draw(const ::Cairo::RefPtr< ::Cairo::Context>& cr)
142 {
143 const Real min_screen_text_mark_distance = 50.0;
144 const Real mark_1_size = 18.0;
145 const Real mark_2_size = 18.0;
146 const Real mark_3_size = 5.0;
147 const Real mark_4_size = 3.0;
148 const int text_size = 8;
149 const Real text_offset = 5.0;
150
151 Real screen_min = get_screen_min();
152 Real screen_max = get_screen_max();
153
154 Gdk::RGBA color = get_style_context()->get_color(Gtk::STATE_FLAG_NORMAL);
155
156 // Make ruler less visually noisy
157 // TODO: this should probably be done via Gtk styling instead
158 color.set_alpha(0.6);
159
160 Real min_text_mark_distance = fabs(distance_from_screen(min_screen_text_mark_distance));
161 int text_degree = (int)round(ceil(log10(min_text_mark_distance)));
162 Real text_mark_distance = exp(log(10)*(Real)text_degree);
163 Real screen_text_mark_distance = fabs(distance_to_screen(text_mark_distance));
164
165 int mode = 0.2*screen_text_mark_distance >= min_screen_text_mark_distance ? 2
166 : 0.5*screen_text_mark_distance >= min_screen_text_mark_distance ? 5
167 : 10;
168 int sub_divisions_count = 100/mode;
169
170 Real mark_distance = text_mark_distance/(Real)sub_divisions_count;
171 Real screen_mark_distance = fabs(distance_to_screen(mark_distance));
172
173 Real begin, end;
174 if (min < max)
175 {
176 begin = floor(min/(10.0*text_mark_distance))*(10.0*text_mark_distance);
177 end = ceil(max/(10.0*text_mark_distance))*(10.0*text_mark_distance);
178 }
179 else
180 {
181 begin = ceil(min/(10.0*text_mark_distance))*(10.0*text_mark_distance);
182 end = floor(max/(10.0*text_mark_distance))*(10.0*text_mark_distance);
183 }
184
185 Real screen_begin = position_to_screen(begin);
186 Real screen_position = get_screen_position();
187
188 int total_marks_count = sub_divisions_count*(int)round(fabs(end - begin)/text_mark_distance) + 1;
189 if (total_marks_count > 16384) return true;
190
191 Real width = (Real)get_width();
192 Real height = (Real)get_height();
193
194 // draw background
195 //if (is_vertical)
196 // cr->rectangle(0.0, screen_min, width, screen_max - screen_min);
197 //else
198 // cr->rectangle(screen_min, 0.0, screen_max - screen_min, height);
199 //cr->set_source_rgb(1, 1, 1);
200 //cr->fill();
201
202 // draw bounds
203 //draw_line(cr, screen_min, is_vertical ? width : height, width, height);
204 //draw_line(cr, screen_max, is_vertical ? width : height, width, height);
205
206 // draw marks
207 for(int i = 0; i < total_marks_count; ++i)
208 {
209 Real screen_pos = screen_begin + (Real)i*screen_mark_distance;
210 if (screen_pos <= screen_min || screen_pos >= screen_max)
211 continue;
212
213 Real pos = min < max
214 ? begin + (Real)i*mark_distance
215 : begin - (Real)i*mark_distance;
216 if ((int)round(pos/mark_distance) == 0)
217 pos = 0.0;
218
219 if (i%sub_divisions_count == 0)
220 {
221 draw_line(cr, screen_pos, mark_1_size, color, width, height);
222 String format = etl::strprintf("%%.%df", text_degree < 0 ? -text_degree : 0);
223 String text = etl::strprintf(format.c_str(), pos);
224 draw_text(cr, screen_pos, text, text_size, color, text_offset, width, height);
225 }
226 else
227 if ( (mode == 5 && i%10 == 0)
228 || (mode == 2 && i%10 == 0) )
229 {
230 draw_line(cr, screen_pos, mark_2_size, color, width, height);
231 String format = etl::strprintf("%%.%df", text_degree < 1 ? 1-text_degree : 0);
232 String text = etl::strprintf(format.c_str(), pos);
233 draw_text(cr, screen_pos, text, text_size, color, text_offset, width, height);
234 }
235 else
236 if ( (mode == 10 && i%2 == 0)
237 || (mode == 5 && i%2 == 0)
238 || (mode == 2 && i%5 == 0) )
239 {
240 draw_line(cr, screen_pos, mark_3_size, color, width, height);
241 }
242 else
243 {
244 draw_line(cr, screen_pos, mark_4_size, color, width, height);
245 }
246 }
247
248 // draw current position
249 if (screen_position > screen_min && screen_position < screen_max)
250 draw_line(cr, screen_position, is_vertical ? width : height, color, width, height);
251
252 return true;
253 }
254
255