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