/* timeline-ruler.cpp - Implementation of the time ruler widget Copyright (C) Lumiera.org 2008, Joel Holdsworth 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 #include "timeline-ruler.hpp" #include "../timeline-widget.hpp" #include "../../window-manager.hpp" extern "C" { #include "../../../lib/time.h" } using namespace Gtk; using namespace Cairo; 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( lumiera::gui::widgets::TimelineWidget *timeline_widget) : Glib::ObjectBase("TimelineRuler"), isDragging(false), pinnedDragTime(0), mouseChevronOffset(0), annotationHorzMargin(0), annotationVertMargin(0), majorTickHeight(0), minorLongTickHeight(0), minorShortTickHeight(0), minDivisionWidth(100), mouseChevronSize(5), selectionChevronSize(5), playbackArrowAlpha(0.5f), playbackArrowSize(10), playbackArrowStemSize(3), timelineWidget(timeline_widget) { REQUIRE(timelineWidget != NULL); // Install style properties register_styles(); } void TimelineRuler::set_mouse_chevron_offset(int offset) { mouseChevronOffset = offset; queue_draw(); } void TimelineRuler::update_view() { rulerImage.clear(); queue_draw(); } void TimelineRuler::on_realize() { Widget::on_realize(); // Set event notifications add_events(Gdk::POINTER_MOTION_MASK | Gdk::SCROLL_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK); // Load styles read_styles(); } bool TimelineRuler::on_expose_event(GdkEventExpose* event) { REQUIRE(event != NULL); // This is where we draw on the window Glib::RefPtr window = get_window(); if(!window) return false; // Prepare to render via cairo const Allocation allocation = get_allocation(); Cairo::RefPtr cr = window->create_cairo_context(); REQUIRE(cr); // Draw the ruler if(!rulerImage) { // We have no cached rendering - it must be redrawn // but do we need ro allocate a new image? if(!rulerImage || rulerImage->get_width() != allocation.get_width() || rulerImage->get_height() != allocation.get_height()) rulerImage = ImageSurface::create(FORMAT_RGB24, allocation.get_width(), allocation.get_height()); ENSURE(rulerImage); Cairo::RefPtr image_cairo = Context::create(rulerImage); ENSURE(image_cairo); draw_ruler(image_cairo, allocation); } // Draw the cached ruler image cr->set_source(rulerImage, 0, 0); cr->paint(); // Draw the overlays draw_mouse_chevron(cr, allocation); draw_selection(cr, allocation); draw_playback_period(cr, allocation); return true; } bool TimelineRuler::on_button_press_event(GdkEventButton* event) { REQUIRE(event != NULL); if(event->button == 1) { pinnedDragTime = timelineWidget->x_to_time(event->x); isDragging = true; } return true; } bool TimelineRuler::on_button_release_event(GdkEventButton* event) { REQUIRE(event != NULL); if(event->button == 1) isDragging = false; return true; } bool TimelineRuler::on_motion_notify_event(GdkEventMotion *event) { REQUIRE(event != NULL); set_mouse_chevron_offset(event->x); if(isDragging) set_leading_x(event->x); return true; } void TimelineRuler::on_size_request (Gtk::Requisition *requisition) { REQUIRE(requisition != NULL); // Initialize the output parameter *requisition = Gtk::Requisition(); requisition->width = 0; get_style_property("height", requisition->height); } void TimelineRuler::on_size_allocate(Gtk::Allocation& allocation) { Widget::on_size_allocate(allocation); rulerImage.clear(); // The widget has changed size - redraw } void TimelineRuler::set_leading_x(const int x) { REQUIRE(timelineWidget != NULL); const gavl_time_t time = timelineWidget->x_to_time(x); if(time > pinnedDragTime) timelineWidget->set_playback_period(pinnedDragTime, time); else timelineWidget->set_playback_period(time, pinnedDragTime); } void TimelineRuler::draw_ruler(Cairo::RefPtr cr, const Gdk::Rectangle ruler_rect) { REQUIRE(cr); REQUIRE(ruler_rect.get_width() > 0); REQUIRE(ruler_rect.get_height() > 0); REQUIRE(timelineWidget != NULL); const gavl_time_t left_offset = timelineWidget->timeOffset; const int64_t time_scale = timelineWidget->timeScale; // Preparation steps const int height = ruler_rect.get_height(); Glib::RefPtr pango_layout = create_pango_layout(""); Glib::RefPtr