/* === S Y N F I G ========================================================= */
/*! \file widget_ruler.cpp
** \brief Template File
**
** $Id$
**
** \legal
** ......... ... 2014 Ivan Mahonin
**
** This package is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License as
** published by the Free Software Foundation; either version 2 of
** the License, or (at your option) any later version.
**
** This package is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** General Public License for more details.
** \endlegal
*/
/* ========================================================================= */
/* === H E A D E R S ======================================================= */
#ifdef USING_PCH
# include "pch.h"
#else
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <synfig/general.h>
#include <ETL/stringf>
#include "widgets/widget_ruler.h"
#include <gui/localization.h>
#endif
/* === U S I N G =========================================================== */
using namespace std;
using namespace synfig;
using namespace studio;
/* === M A C R O S ========================================================= */
/* === G L O B A L S ======================================================= */
/* === P R O C E D U R E S ================================================= */
/* === M E T H O D S ======================================================= */
Widget_Ruler::Widget_Ruler(bool is_vertical):
is_vertical(is_vertical),
layout(Pango::Layout::create(get_pango_context())),
min(0.0),
max(0.0),
position(0.0)
{ }
Widget_Ruler::~Widget_Ruler() { }
void
Widget_Ruler::set_min(synfig::Real value)
{ if (min != value) { min = value; this->queue_draw(); } }
void
Widget_Ruler::set_max(synfig::Real value)
{ if (max != value) { max = value; this->queue_draw(); } }
void
Widget_Ruler::set_position(synfig::Real value)
{ if (position != value) { position = value; this->queue_draw(); } }
void
Widget_Ruler::draw_line(
const ::Cairo::RefPtr< ::Cairo::Context>& cr,
synfig::Real position,
synfig::Real size,
const Gdk::RGBA &color,
synfig::Real width,
synfig::Real height )
{
position = round(position) - 0.5;
cr->set_line_width(0.5);
cr->set_source_rgba(color.get_red(), color.get_green(), color.get_blue(), color.get_alpha());
if (is_vertical) {
cr->move_to(width - size, position);
cr->line_to(width, position);
}
else
{
cr->move_to(position, height - size);
cr->line_to(position, height);
}
cr->stroke();
}
void
Widget_Ruler::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 )
{
layout->set_text(text);
int w = 0, h = 0;
Pango::AttrList attr_list;
Pango::AttrInt pango_size(Pango::Attribute::create_attr_size(Pango::SCALE*size));
pango_size.set_start_index(0);
pango_size.set_end_index(64);
attr_list.change(pango_size);
layout->set_attributes(attr_list);
layout->get_pixel_size(w, h);
if (is_vertical) {
cr->save();
cr->set_source_rgba(color.get_red(), color.get_green(), color.get_blue(), color.get_alpha());
cr->rotate_degrees(-90.f);
cr->move_to(-position + 3, width - offset - h);
layout->show_in_cairo_context(cr);
cr->restore();
} else {
cr->set_source_rgba(color.get_red(), color.get_green(), color.get_blue(), color.get_alpha());
cr->move_to(position + 3, height - offset - h);
layout->show_in_cairo_context(cr);
}
}
bool
Widget_Ruler::on_draw(const ::Cairo::RefPtr< ::Cairo::Context>& cr)
{
const Real min_screen_text_mark_distance = 50.0;
const Real mark_1_size = 18.0;
const Real mark_2_size = 18.0;
const Real mark_3_size = 5.0;
const Real mark_4_size = 3.0;
const int text_size = 8;
const Real text_offset = 5.0;
Real screen_min = get_screen_min();
Real screen_max = get_screen_max();
Gdk::RGBA color = get_style_context()->get_color(Gtk::STATE_FLAG_NORMAL);
// Make ruler less visually noisy
// TODO: this should probably be done via Gtk styling instead
color.set_alpha(0.6);
Real min_text_mark_distance = fabs(distance_from_screen(min_screen_text_mark_distance));
int text_degree = (int)round(ceil(log10(min_text_mark_distance)));
Real text_mark_distance = exp(log(10)*(Real)text_degree);
Real screen_text_mark_distance = fabs(distance_to_screen(text_mark_distance));
int mode = 0.2*screen_text_mark_distance >= min_screen_text_mark_distance ? 2
: 0.5*screen_text_mark_distance >= min_screen_text_mark_distance ? 5
: 10;
int sub_divisions_count = 100/mode;
Real mark_distance = text_mark_distance/(Real)sub_divisions_count;
Real screen_mark_distance = fabs(distance_to_screen(mark_distance));
Real begin, end;
if (min < max)
{
begin = floor(min/(10.0*text_mark_distance))*(10.0*text_mark_distance);
end = ceil(max/(10.0*text_mark_distance))*(10.0*text_mark_distance);
}
else
{
begin = ceil(min/(10.0*text_mark_distance))*(10.0*text_mark_distance);
end = floor(max/(10.0*text_mark_distance))*(10.0*text_mark_distance);
}
Real screen_begin = position_to_screen(begin);
Real screen_position = get_screen_position();
int total_marks_count = sub_divisions_count*(int)round(fabs(end - begin)/text_mark_distance) + 1;
if (total_marks_count > 16384) return true;
Real width = (Real)get_width();
Real height = (Real)get_height();
// draw background
//if (is_vertical)
// cr->rectangle(0.0, screen_min, width, screen_max - screen_min);
//else
// cr->rectangle(screen_min, 0.0, screen_max - screen_min, height);
//cr->set_source_rgb(1, 1, 1);
//cr->fill();
// draw bounds
//draw_line(cr, screen_min, is_vertical ? width : height, width, height);
//draw_line(cr, screen_max, is_vertical ? width : height, width, height);
// draw marks
for(int i = 0; i < total_marks_count; ++i)
{
Real screen_pos = screen_begin + (Real)i*screen_mark_distance;
if (screen_pos <= screen_min || screen_pos >= screen_max)
continue;
Real pos = min < max
? begin + (Real)i*mark_distance
: begin - (Real)i*mark_distance;
if ((int)round(pos/mark_distance) == 0)
pos = 0.0;
if (i%sub_divisions_count == 0)
{
draw_line(cr, screen_pos, mark_1_size, color, width, height);
String format = etl::strprintf("%%.%df", text_degree < 0 ? -text_degree : 0);
String text = etl::strprintf(format.c_str(), pos);
draw_text(cr, screen_pos, text, text_size, color, text_offset, width, height);
}
else
if ( (mode == 5 && i%10 == 0)
|| (mode == 2 && i%10 == 0) )
{
draw_line(cr, screen_pos, mark_2_size, color, width, height);
String format = etl::strprintf("%%.%df", text_degree < 1 ? 1-text_degree : 0);
String text = etl::strprintf(format.c_str(), pos);
draw_text(cr, screen_pos, text, text_size, color, text_offset, width, height);
}
else
if ( (mode == 10 && i%2 == 0)
|| (mode == 5 && i%2 == 0)
|| (mode == 2 && i%5 == 0) )
{
draw_line(cr, screen_pos, mark_3_size, color, width, height);
}
else
{
draw_line(cr, screen_pos, mark_4_size, color, width, height);
}
}
// draw current position
if (screen_position > screen_min && screen_position < screen_max)
draw_line(cr, screen_position, is_vertical ? width : height, color, width, height);
return true;
}