2008-06-19 00:57:47 +02:00
|
|
|
/*
|
|
|
|
|
timeline-ruler.cpp - Implementation of the time ruler widget
|
|
|
|
|
|
|
|
|
|
Copyright (C) Lumiera.org
|
|
|
|
|
2008, Joel Holdsworth <joel@airwebreathe.org.uk>
|
|
|
|
|
|
|
|
|
|
This program 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 program 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.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
|
|
|
|
|
|
* *****************************************************/
|
|
|
|
|
|
|
|
|
|
#include <cairomm-1.0/cairomm/cairomm.h>
|
|
|
|
|
|
|
|
|
|
#include "timeline-ruler.hpp"
|
|
|
|
|
#include "../../window-manager.hpp"
|
|
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
|
#include "../../../lib/time.h"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
using namespace Gtk;
|
|
|
|
|
using namespace std;
|
|
|
|
|
using namespace lumiera::gui;
|
|
|
|
|
using namespace lumiera::gui::widgets;
|
|
|
|
|
using namespace lumiera::gui::widgets::timeline;
|
|
|
|
|
|
|
|
|
|
namespace lumiera {
|
|
|
|
|
namespace gui {
|
|
|
|
|
namespace widgets {
|
|
|
|
|
namespace timeline {
|
|
|
|
|
|
|
|
|
|
TimelineRuler::TimelineRuler() :
|
2008-06-23 11:54:37 +02:00
|
|
|
Glib::ObjectBase("TimelineRuler"),
|
|
|
|
|
timeOffset(0),
|
2008-06-23 18:07:57 +02:00
|
|
|
timeScale(1),
|
|
|
|
|
annotationHorzMargin(0),
|
2008-06-23 22:01:29 +02:00
|
|
|
annotationVertMargin(0),
|
|
|
|
|
majorTickHeight(0),
|
|
|
|
|
minorLongTickHeight(0),
|
2008-06-23 22:18:20 +02:00
|
|
|
minorShortTickHeight(0),
|
|
|
|
|
minDivisionWidth(100)
|
2008-06-19 22:37:36 +02:00
|
|
|
{
|
|
|
|
|
set_flags(Gtk::NO_WINDOW); // This widget will not have a window
|
2008-06-23 18:07:57 +02:00
|
|
|
|
|
|
|
|
// Install style properties
|
2008-06-23 22:01:29 +02:00
|
|
|
register_styles();
|
2008-06-19 22:37:36 +02:00
|
|
|
}
|
2008-06-19 00:57:47 +02:00
|
|
|
|
2008-06-20 00:35:53 +02:00
|
|
|
void
|
|
|
|
|
TimelineRuler::set_time_offset(gavl_time_t time_offset)
|
|
|
|
|
{
|
|
|
|
|
timeOffset = time_offset;
|
|
|
|
|
queue_draw();
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-23 11:54:37 +02:00
|
|
|
void
|
|
|
|
|
TimelineRuler::set_time_scale(int64_t time_scale)
|
|
|
|
|
{
|
|
|
|
|
REQUIRE(time_scale > 0);
|
|
|
|
|
timeScale = time_scale;
|
|
|
|
|
queue_draw();
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-23 22:01:29 +02:00
|
|
|
void
|
|
|
|
|
TimelineRuler::on_realize()
|
|
|
|
|
{
|
|
|
|
|
Widget::on_realize();
|
|
|
|
|
|
|
|
|
|
// Load styles
|
|
|
|
|
read_styles();
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-19 00:57:47 +02:00
|
|
|
bool
|
|
|
|
|
TimelineRuler::on_expose_event(GdkEventExpose* event)
|
2008-06-19 22:37:36 +02:00
|
|
|
{
|
|
|
|
|
// This is where we draw on the window
|
|
|
|
|
Glib::RefPtr<Gdk::Window> window = get_window();
|
|
|
|
|
if(!window)
|
|
|
|
|
return false;
|
2008-06-23 18:07:57 +02:00
|
|
|
|
2008-06-19 22:37:36 +02:00
|
|
|
// Prepare to render via cairo
|
|
|
|
|
Allocation allocation = get_allocation();
|
2008-06-23 22:01:29 +02:00
|
|
|
const int height = allocation.get_height();
|
2008-06-19 22:37:36 +02:00
|
|
|
Glib::RefPtr<Style> style = get_style();
|
|
|
|
|
Cairo::RefPtr<Cairo::Context> cairo = window->create_cairo_context();
|
|
|
|
|
Glib::RefPtr<Pango::Layout> pango_layout = create_pango_layout("");
|
2008-06-19 00:57:47 +02:00
|
|
|
|
2008-06-19 22:37:36 +02:00
|
|
|
cairo->translate(allocation.get_x(), allocation.get_y());
|
|
|
|
|
|
2008-06-20 00:35:53 +02:00
|
|
|
// Render the background, and clip inside the area
|
2008-06-19 22:37:36 +02:00
|
|
|
Gdk::Cairo::set_source_color(cairo, style->get_bg(STATE_NORMAL));
|
2008-06-23 16:42:14 +02:00
|
|
|
cairo->rectangle(0, 0,
|
|
|
|
|
allocation.get_width(), allocation.get_height());
|
2008-06-20 00:35:53 +02:00
|
|
|
cairo->fill_preserve();
|
|
|
|
|
cairo->clip();
|
2008-06-19 22:37:36 +02:00
|
|
|
|
2008-06-23 11:54:37 +02:00
|
|
|
// Make sure we don't have impossible zoom
|
|
|
|
|
if(timeScale <= 0)
|
|
|
|
|
return true;
|
|
|
|
|
|
2008-06-19 22:37:36 +02:00
|
|
|
// Render ruler annotations
|
2008-06-20 00:35:53 +02:00
|
|
|
Gdk::Cairo::set_source_color(cairo, style->get_fg(STATE_NORMAL));
|
|
|
|
|
|
2008-06-23 16:42:14 +02:00
|
|
|
const gavl_time_t major_spacing = calculate_major_spacing();
|
2008-06-23 18:48:48 +02:00
|
|
|
const gavl_time_t minor_spacing = major_spacing / 10;
|
2008-06-19 22:37:36 +02:00
|
|
|
|
2008-06-23 22:01:29 +02:00
|
|
|
|
2008-06-23 18:48:48 +02:00
|
|
|
int64_t time_offset = timeOffset - timeOffset % minor_spacing;
|
2008-06-20 00:35:53 +02:00
|
|
|
int x = 0;
|
|
|
|
|
const int64_t x_offset = timeOffset / timeScale;
|
|
|
|
|
|
|
|
|
|
do
|
2008-06-19 00:57:47 +02:00
|
|
|
{
|
2008-06-20 00:35:53 +02:00
|
|
|
x = (int)(time_offset / timeScale - x_offset);
|
2008-06-19 00:57:47 +02:00
|
|
|
|
2008-06-20 00:35:53 +02:00
|
|
|
cairo->set_line_width(1);
|
2008-06-19 00:57:47 +02:00
|
|
|
|
2008-06-23 18:48:48 +02:00
|
|
|
if(time_offset % major_spacing == 0)
|
|
|
|
|
{
|
|
|
|
|
// Draw the major grid-line
|
2008-06-23 22:01:29 +02:00
|
|
|
cairo->move_to(x + 0.5, height - majorTickHeight);
|
|
|
|
|
cairo->line_to(x + 0.5, height);
|
2008-06-23 18:48:48 +02:00
|
|
|
cairo->stroke();
|
|
|
|
|
|
|
|
|
|
// Draw the text
|
|
|
|
|
pango_layout->set_text(lumiera_tmpbuf_print_time(time_offset));
|
|
|
|
|
cairo->move_to(annotationHorzMargin + x, annotationVertMargin);
|
|
|
|
|
pango_layout->add_to_cairo_context(cairo);
|
|
|
|
|
cairo->fill();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2008-06-23 22:01:29 +02:00
|
|
|
// Draw the long or short minor grid-line
|
2008-06-23 18:48:48 +02:00
|
|
|
if(time_offset % (minor_spacing * 2) == 0)
|
2008-06-23 22:01:29 +02:00
|
|
|
cairo->move_to(x + 0.5, height - minorLongTickHeight);
|
2008-06-23 18:48:48 +02:00
|
|
|
else
|
2008-06-23 22:01:29 +02:00
|
|
|
cairo->move_to(x + 0.5, height - minorShortTickHeight);
|
2008-06-23 18:48:48 +02:00
|
|
|
|
2008-06-23 22:01:29 +02:00
|
|
|
cairo->line_to(x + 0.5, height);
|
2008-06-23 18:48:48 +02:00
|
|
|
cairo->stroke();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
time_offset += minor_spacing;
|
2008-06-19 00:57:47 +02:00
|
|
|
}
|
2008-06-20 00:35:53 +02:00
|
|
|
while(x < allocation.get_width());
|
2008-06-19 00:57:47 +02:00
|
|
|
|
2008-06-19 22:37:36 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
2008-06-23 16:42:14 +02:00
|
|
|
|
2008-06-23 22:01:29 +02:00
|
|
|
void
|
|
|
|
|
TimelineRuler::on_size_request (Gtk::Requisition *requisition)
|
|
|
|
|
{
|
|
|
|
|
// Initialize the output parameter
|
|
|
|
|
*requisition = Gtk::Requisition();
|
|
|
|
|
|
|
|
|
|
requisition->width = 0;
|
|
|
|
|
get_style_property("height", requisition->height);
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-23 16:42:14 +02:00
|
|
|
gavl_time_t
|
|
|
|
|
TimelineRuler::calculate_major_spacing() const
|
|
|
|
|
{
|
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
|
|
const gavl_time_t major_spacings[] = {
|
|
|
|
|
GAVL_TIME_SCALE / 1000,
|
|
|
|
|
GAVL_TIME_SCALE / 400,
|
|
|
|
|
GAVL_TIME_SCALE / 200,
|
|
|
|
|
GAVL_TIME_SCALE / 100,
|
|
|
|
|
GAVL_TIME_SCALE / 40,
|
|
|
|
|
GAVL_TIME_SCALE / 20,
|
|
|
|
|
GAVL_TIME_SCALE / 10,
|
|
|
|
|
GAVL_TIME_SCALE / 4,
|
|
|
|
|
GAVL_TIME_SCALE / 2,
|
|
|
|
|
GAVL_TIME_SCALE,
|
|
|
|
|
2l * GAVL_TIME_SCALE,
|
|
|
|
|
5l * GAVL_TIME_SCALE,
|
|
|
|
|
10l * GAVL_TIME_SCALE,
|
|
|
|
|
15l * GAVL_TIME_SCALE,
|
|
|
|
|
30l * GAVL_TIME_SCALE,
|
|
|
|
|
60l * GAVL_TIME_SCALE,
|
|
|
|
|
2l * 60l * GAVL_TIME_SCALE,
|
|
|
|
|
5l * 60l * GAVL_TIME_SCALE,
|
|
|
|
|
10l * 60l * GAVL_TIME_SCALE,
|
|
|
|
|
15l * 60l * GAVL_TIME_SCALE,
|
|
|
|
|
30l * 60l * GAVL_TIME_SCALE,
|
|
|
|
|
60l * 60l * GAVL_TIME_SCALE
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for(i = 0; i < sizeof(major_spacings) / sizeof(gavl_time_t); i++)
|
|
|
|
|
{
|
|
|
|
|
const int64_t division_width = major_spacings[i] / timeScale;
|
|
|
|
|
|
2008-06-23 22:18:20 +02:00
|
|
|
if(division_width > minDivisionWidth)
|
2008-06-23 16:42:14 +02:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return major_spacings[i];
|
|
|
|
|
}
|
2008-06-23 18:07:57 +02:00
|
|
|
|
2008-06-23 22:01:29 +02:00
|
|
|
void
|
|
|
|
|
TimelineRuler::register_styles() const
|
|
|
|
|
{
|
|
|
|
|
GtkWidgetClass *klass = GTK_WIDGET_CLASS(G_OBJECT_GET_CLASS(gobj()));
|
|
|
|
|
|
|
|
|
|
gtk_widget_class_install_style_property(klass,
|
|
|
|
|
g_param_spec_int("height",
|
2008-06-23 22:18:20 +02:00
|
|
|
"Height of the Ruler Widget",
|
|
|
|
|
"The height of the ruler widget in pixels.",
|
|
|
|
|
0, G_MAXINT, 18, G_PARAM_READABLE));
|
|
|
|
|
|
2008-06-23 22:01:29 +02:00
|
|
|
gtk_widget_class_install_style_property(klass,
|
2008-06-23 22:18:20 +02:00
|
|
|
g_param_spec_int("major_tick_height",
|
|
|
|
|
"Height of Major Ticks",
|
|
|
|
|
"The length of major ticks in pixels.",
|
|
|
|
|
0, G_MAXINT, 18, G_PARAM_READABLE));
|
2008-06-23 22:01:29 +02:00
|
|
|
|
|
|
|
|
gtk_widget_class_install_style_property(klass,
|
2008-06-23 22:18:20 +02:00
|
|
|
g_param_spec_int("minor_long_tick_height",
|
|
|
|
|
"Height of Long Minor Ticks",
|
|
|
|
|
"The length of long minor ticks in pixels.",
|
|
|
|
|
0, G_MAXINT, 6, G_PARAM_READABLE));
|
2008-06-23 22:01:29 +02:00
|
|
|
|
|
|
|
|
gtk_widget_class_install_style_property(klass,
|
2008-06-23 22:18:20 +02:00
|
|
|
g_param_spec_int("minor_short_tick_height",
|
|
|
|
|
"Height of Short Minor Ticks",
|
|
|
|
|
"The length of short minor ticks in pixels.",
|
|
|
|
|
0, G_MAXINT, 3, G_PARAM_READABLE));
|
|
|
|
|
|
|
|
|
|
gtk_widget_class_install_style_property(klass,
|
|
|
|
|
g_param_spec_int("annotation_horz_margin",
|
|
|
|
|
"Horizontal margin around annotation text",
|
|
|
|
|
"The horizontal margin around the annotation text in pixels.",
|
|
|
|
|
0, G_MAXINT, 3,
|
|
|
|
|
G_PARAM_READABLE));
|
2008-06-23 22:01:29 +02:00
|
|
|
|
|
|
|
|
gtk_widget_class_install_style_property(klass,
|
2008-06-23 22:18:20 +02:00
|
|
|
g_param_spec_int("annotation_vert_margin",
|
|
|
|
|
"Vertical margin around annotation text",
|
|
|
|
|
"The vertical margin around the annotation text in pixels.",
|
|
|
|
|
0, G_MAXINT, 0, G_PARAM_READABLE));
|
2008-06-23 22:01:29 +02:00
|
|
|
|
|
|
|
|
gtk_widget_class_install_style_property(klass,
|
2008-06-23 22:18:20 +02:00
|
|
|
g_param_spec_int("min_division_width",
|
|
|
|
|
"Minimum Division Width",
|
|
|
|
|
"The minimum distance in pixels that two major division may approach.",
|
|
|
|
|
0, G_MAXINT, 100, G_PARAM_READABLE));
|
2008-06-23 22:01:29 +02:00
|
|
|
}
|
|
|
|
|
|
2008-06-23 18:07:57 +02:00
|
|
|
void
|
|
|
|
|
TimelineRuler::read_styles()
|
|
|
|
|
{
|
|
|
|
|
get_style_property("annotation_horz_margin", annotationHorzMargin);
|
2008-06-23 22:01:29 +02:00
|
|
|
get_style_property("annotation_vert_margin", annotationVertMargin);
|
|
|
|
|
get_style_property("major_tick_height", majorTickHeight);
|
|
|
|
|
get_style_property("minor_long_tick_height", minorLongTickHeight);
|
|
|
|
|
get_style_property("minor_short_tick_height", minorShortTickHeight);
|
2008-06-23 22:18:20 +02:00
|
|
|
get_style_property("min_division_width", minDivisionWidth);
|
2008-06-23 18:07:57 +02:00
|
|
|
}
|
2008-06-19 00:57:47 +02:00
|
|
|
|
|
|
|
|
} // namespace timeline
|
|
|
|
|
} // namespace widgets
|
|
|
|
|
} // namespace gui
|
|
|
|
|
} // namespace lumiera
|