diff --git a/src/gui/panels/timeline-panel.cpp b/src/gui/panels/timeline-panel.cpp index 07dce7b1d..6f755f25c 100644 --- a/src/gui/panels/timeline-panel.cpp +++ b/src/gui/panels/timeline-panel.cpp @@ -23,10 +23,12 @@ #include "gui/gtk-lumiera.hpp" #include "gui/panels/timeline-panel.hpp" +#include "gui/widgets/timeline/timeline-zoom-scale.hpp" #include "gui/workspace/workspace-window.hpp" #include "gui/model/project.hpp" #include "gui/controller/controller.hpp" + #include "lib/util.hpp" #include @@ -34,6 +36,7 @@ using namespace Gtk; using namespace sigc; using namespace gui::widgets; +using namespace gui::widgets::timeline; using namespace gui::model; using boost::shared_ptr; ///////////////////////////////TICKET #796 @@ -60,6 +63,7 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager, , iBeamTool(Gtk::StockID("tool_i_beam")) , zoomIn(Stock::ZOOM_IN) , zoomOut(Stock::ZOOM_OUT) + , zoomScale() , updatingToolbar(false) , currentTool(timeline::Arrow) { @@ -101,14 +105,15 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager, toolbar.append(separator2); - toolbar.append(zoomIn, mem_fun(this, &TimelinePanel::on_zoom_in)); - toolbar.append(zoomOut, mem_fun(this, &TimelinePanel::on_zoom_out)); - + toolbar.append(zoomScale); + zoomScale.signal_zoom(). + connect(mem_fun(this,&TimelinePanel::on_zoom)); + toolbar.show_all(); panelBar.pack_start(toolbar, PACK_SHRINK); // Setup tooltips - sequenceChooser .set_tooltip_text(_("Change sequence")); + sequenceChooser .set_tooltip_text(_("Change sequence")); previousButton .set_tooltip_text(_("To beginning")); rewindButton .set_tooltip_text(_("Rewind")); @@ -122,12 +127,18 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager, zoomIn .set_tooltip_text(_("Zoom in")); zoomOut .set_tooltip_text(_("Zoom out")); + zoomScale .set_tooltip_text(_("Adjust timeline zoom scale")); // Setup the timeline widget - shared_ptr sequence ///////////////////////////////TICKET #796 + shared_ptr sequence ///////////////////////////////TICKET #796 : should use std::tr1::shared_ptr instead of boost = *get_project().get_sequences().begin(); timelineWidget.reset(new TimelineWidget(load_state(sequence))); pack_start(*timelineWidget, PACK_EXPAND_WIDGET); + + // since TimelineWidget is now initialised, + // wire the zoom slider to react on timeline state changes + zoomScale.wireTimelineState (timelineWidget->get_state(), + timelineWidget->state_changed_signal()); // Set the initial UI state update_sequence_chooser(); @@ -136,7 +147,6 @@ TimelinePanel::TimelinePanel (workspace::PanelManager &panel_manager, show_time (Time::ZERO); } - const char* TimelinePanel::get_title() { @@ -183,6 +193,13 @@ TimelinePanel::on_ibeam_tool() set_tool(timeline::IBeam); } +void +TimelinePanel::on_zoom(double time_scale_ratio) +{ + REQUIRE(timelineWidget); + timelineWidget->zoom_view(time_scale_ratio); +} + void TimelinePanel::on_zoom_in() { @@ -236,18 +253,19 @@ TimelinePanel::on_sequence_chosen() { weak_ptr sequence_ptr = (*iter)[sequenceChooserColumns.sequenceColumn]; + shared_ptr sequence(sequence_ptr.lock()); + if(sequence) { shared_ptr old_state( timelineWidget->get_state()); REQUIRE(old_state); - if(sequence != old_state->get_sequence()) timelineWidget->set_state(load_state(sequence)); } } - + update_zoom_buttons(); } @@ -379,9 +397,10 @@ TimelinePanel::on_frame() shared_ptr TimelinePanel::load_state(weak_ptr sequence) { + /* state exists */ if(contains(timelineStates, sequence)) - return timelineStates[sequence]; - + return timelineStates[sequence]; + shared_ptr shared_sequence = sequence.lock(); if(shared_sequence) { diff --git a/src/gui/panels/timeline-panel.hpp b/src/gui/panels/timeline-panel.hpp index b65469bc9..2215c8657 100644 --- a/src/gui/panels/timeline-panel.hpp +++ b/src/gui/panels/timeline-panel.hpp @@ -32,6 +32,8 @@ #include "gui/panels/panel.hpp" #include "gui/widgets/timecode-widget.hpp" #include "gui/widgets/timeline-widget.hpp" +#include "gui/widgets/timeline/timeline-zoom-scale.hpp" + #include "lib/time/timevalue.hpp" #include @@ -87,6 +89,7 @@ private: void on_arrow_tool(); void on_ibeam_tool(); + void on_zoom(double time_scale_ratio); void on_zoom_in(); void on_zoom_out(); @@ -171,7 +174,7 @@ private: boost::scoped_ptr timelineWidget; std::map< boost::weak_ptr, - boost::shared_ptr > ///////////////////////////////TICKET #796 + boost::shared_ptr > ///////////////////////////////TICKET #796 : should use std::tr1::shared_ptr timelineStates; // Toolbar Widgets @@ -191,6 +194,7 @@ private: MiniButton zoomIn; MiniButton zoomOut; + gui::widgets::timeline::TimelineZoomScale zoomScale; Gtk::SeparatorToolItem separator2; diff --git a/src/gui/widgets/timeline-widget.cpp b/src/gui/widgets/timeline-widget.cpp index a00882b7e..eb4decffa 100644 --- a/src/gui/widgets/timeline-widget.cpp +++ b/src/gui/widgets/timeline-widget.cpp @@ -41,7 +41,7 @@ const int TimelineWidget::TrackPadding = 1; const int TimelineWidget::HeaderWidth = 150; const int TimelineWidget::HeaderIndentWidth = 10; const double TimelineWidget::ZoomIncrement = 1.25; -const int64_t TimelineWidget::MaxScale = 30000000; +const int64_t TimelineWidget::MaxScale = 30000000; // 30 Million TimelineWidget::TimelineWidget( boost::shared_ptr source_state) : @@ -102,6 +102,7 @@ TimelineWidget::get_state() void TimelineWidget::set_state(shared_ptr new_state) { + state = new_state; // Clear the track tree @@ -125,17 +126,17 @@ TimelineWidget::set_state(shared_ptr new_state) update_tracks(); // Send the state changed signal - stateChangedSignal.emit(); + stateChangedSignal.emit (state); } void -TimelineWidget::zoom_view(int zoom_size) +TimelineWidget::zoom_view(double timescale_ratio) { if(state) - { - const int view_width = body->get_allocation().get_width(); - state->get_view_window().zoom_view(view_width / 2, zoom_size); - } + { + const int view_width = body->get_allocation().get_width(); + state->get_view_window().zoom_view(view_width / 2, timescale_ratio); + } } ToolType @@ -178,7 +179,7 @@ TimelineWidget::hovering_track_changed_signal() const return hoveringTrackChangedSignal; } -sigc::signal +TimelineWidget::TimelineStateChangeSignal TimelineWidget::state_changed_signal() const { return stateChangedSignal; @@ -212,10 +213,12 @@ TimelineWidget::on_view_window_changed() if(state) { timeline::TimelineViewWindow &window = state->get_view_window(); + const int view_width = body->get_allocation().get_width(); - + horizontalAdjustment.set_page_size( window.get_time_scale() * view_width); + horizontalAdjustment.set_value(_raw(window.get_time_offset())); } } diff --git a/src/gui/widgets/timeline-widget.hpp b/src/gui/widgets/timeline-widget.hpp index ca213c15b..884598067 100644 --- a/src/gui/widgets/timeline-widget.hpp +++ b/src/gui/widgets/timeline-widget.hpp @@ -98,7 +98,7 @@ public: * @param zoom_size The number of steps to zoom by. The scale factor * is 1.25^(-zoom_size). */ - void zoom_view(int zoom_size); + void zoom_view(double timescale_ratio); /** * Gets the type of the tool currently active. @@ -115,14 +115,16 @@ public: public: /* ===== Signals ===== */ + typedef sigc::signal > TimelineStateChangeSignal; + typedef sigc::signal > HoveringTrackChangedSignal; + sigc::signal mouse_hover_signal() const; sigc::signal playback_period_drag_released_signal() const; - sigc::signal > - hovering_track_changed_signal() const; + HoveringTrackChangedSignal hovering_track_changed_signal() const; - sigc::signal state_changed_signal() const; + TimelineStateChangeSignal state_changed_signal() const; /* ===== Events ===== */ protected: @@ -275,9 +277,8 @@ protected: // Signals sigc::signal mouseHoverSignal; sigc::signal playbackPeriodDragReleasedSignal; - sigc::signal > - hoveringTrackChangedSignal; - sigc::signal stateChangedSignal; + HoveringTrackChangedSignal hoveringTrackChangedSignal; + TimelineStateChangeSignal stateChangedSignal; bool update_tracks_frozen; diff --git a/src/gui/widgets/timeline/timeline-body.cpp b/src/gui/widgets/timeline/timeline-body.cpp index 7f78d0929..b64757477 100644 --- a/src/gui/widgets/timeline/timeline-body.cpp +++ b/src/gui/widgets/timeline/timeline-body.cpp @@ -39,6 +39,8 @@ using namespace lumiera; using gui::util::CairoUtil; +using boost::shared_ptr; ////////////////////TICKET #796 + namespace gui { namespace widgets { namespace timeline { @@ -61,7 +63,7 @@ TimelineBody::TimelineBody (TimelineWidget &timelineWidget) register_styles(); // Reset the state - on_state_changed(); + propagateStateChange(); } TimelineBody::~TimelineBody() @@ -69,6 +71,13 @@ TimelineBody::~TimelineBody() WARN_IF(!tool, gui, "An invalid tool pointer is unexpected here"); } +TimelineViewWindow& +TimelineBody::viewWindow() const +{ + REQUIRE(timelineState); + return timelineState->get_view_window(); +} + TimelineWidget& TimelineBody::getTimelineWidget () const { @@ -147,7 +156,7 @@ TimelineBody::on_expose_event(GdkEventExpose* event) // Makes sure the widget styles have been loaded read_styles(); - if(timelineWidget.get_state()) + if (timelineState) { // Prepare to render via cairo const Allocation allocation = get_allocation(); @@ -169,9 +178,9 @@ TimelineBody::on_scroll_event (GdkEventScroll* event) { REQUIRE(event != NULL); - if(timelineWidget.get_state()) + if (timelineState) { - TimelineViewWindow &window = view_window(); + TimelineViewWindow &window = viewWindow(); const Allocation allocation = get_allocation(); if(event->state & GDK_CONTROL_MASK) @@ -255,17 +264,16 @@ TimelineBody::on_motion_notify_event(GdkEventMotion *event) { REQUIRE(event != NULL); - if(timelineWidget.get_state()) + if (timelineState) { // Handle a middle-mouse drag if one is occuring switch(dragType) { case Shift: { - TimelineViewWindow &window = view_window(); - - /////////////////////////////TICKET# 795 : don't reach in from outside and manipulate internals of the timeline view! + /////////////////////////////TICKET #795 : don't reach in from outside and manipulate internals of the timeline view! ///////////////////////////// : either encapsulate this entirely here, or leave it to the timeline view! + TimelineViewWindow &window = viewWindow(); const int64_t scale = window.get_time_scale(); window.set_time_offset(beginShiftTimeOffset + TimeValue(scale * (mouseDownX - event->x))); @@ -294,12 +302,20 @@ TimelineBody::on_motion_notify_event(GdkEventMotion *event) } void -TimelineBody::on_state_changed() +TimelineBody::on_state_changed (shared_ptr newState) { - if(timelineWidget.get_state()) + REQUIRE (newState); + timelineState = newState; + propagateStateChange(); +} + +void +TimelineBody::propagateStateChange() +{ + if (timelineState) { // Connect up some events - view_window().changed_signal().connect( + viewWindow().changed_signal().connect( sigc::mem_fun(this, &TimelineBody::on_update_view) ); } @@ -373,7 +389,7 @@ TimelineBody::draw_track(Cairo::RefPtr cr, // Render the track cr->save(); - TimelineViewWindow &window = view_window(); + TimelineViewWindow &window = viewWindow(); timeline_track->draw_track(cr, &window); cr->restore(); } @@ -386,12 +402,11 @@ TimelineBody::draw_selection(Cairo::RefPtr cr) // Prepare const Allocation allocation = get_allocation(); - shared_ptr state = timelineWidget.get_state(); - REQUIRE(state); + REQUIRE(timelineState); - TimelineViewWindow const& window = state->get_view_window(); - const int start_x = window.time_to_x(state->getSelectionStart()); - const int end_x = window.time_to_x(state->getSelectionEnd()); + TimelineViewWindow const& window = timelineState->get_view_window(); + const int start_x = window.time_to_x(timelineState->getSelectionStart()); + const int end_x = window.time_to_x(timelineState->getSelectionEnd()); // Draw the cover if(end_x > 0 && start_x < allocation.get_width()) @@ -427,16 +442,13 @@ TimelineBody::draw_playback_point(Cairo::RefPtr cr) { REQUIRE(cr); - // Prepare - - shared_ptr state = timelineWidget.get_state(); - if(state) + if (timelineState) { - if (!state->isPlaying()) return; + if (!timelineState->isPlaying()) return; const Allocation allocation = get_allocation(); - Time point = state->getPlaybackPoint(); - const int x = view_window().time_to_x(point); + Time point = timelineState->getPlaybackPoint(); + const int x = viewWindow().time_to_x(point); // Set source cr->set_source(playbackPointColour); @@ -455,10 +467,10 @@ TimelineBody::draw_playback_point(Cairo::RefPtr cr) void TimelineBody::begin_shift_drag() { - if(timelineWidget.get_state()) + if (timelineState) { dragType = Shift; - beginShiftTimeOffset = view_window().get_time_offset(); + beginShiftTimeOffset = viewWindow().get_time_offset(); beginShiftVerticalOffset = get_vertical_offset(); } } @@ -475,14 +487,6 @@ TimelineBody::set_vertical_offset(int offset) timelineWidget.verticalAdjustment.set_value(offset); } -TimelineViewWindow& -TimelineBody::view_window() const -{ - shared_ptr state = timelineWidget.get_state(); - REQUIRE(state); - return state->get_view_window(); -} - void TimelineBody::register_styles() const { diff --git a/src/gui/widgets/timeline/timeline-body.hpp b/src/gui/widgets/timeline/timeline-body.hpp index 74b057738..aaf90cb2e 100644 --- a/src/gui/widgets/timeline/timeline-body.hpp +++ b/src/gui/widgets/timeline/timeline-body.hpp @@ -68,10 +68,7 @@ public: */ TimelineBody(gui::widgets::TimelineWidget &timeline_widget); - /** - * Destructor - */ - ~TimelineBody(); + virtual ~TimelineBody(); TimelineWidget& getTimelineWidget () const; @@ -125,13 +122,18 @@ protected: bool on_motion_notify_event(GdkEventMotion *event); /** - * The event handler for when the TimelineWidget's state object is - * replaced. + * The event handler for when the TimelineWidget's state is switched. */ - void on_state_changed(); + void on_state_changed (boost::shared_ptr newState); /* ===== Internals ===== */ private: + /** + * Access the current timeline view window + * @warning must not be called unless the TimlineWidget + * has a valid state. + */ + TimelineViewWindow& viewWindow() const; /** * Draws the timeline tracks. @@ -161,13 +163,9 @@ private: void set_vertical_offset(int offset); - /** - * A helper function to get the view window - * @remarks This function must not be called unless the TimlineWidget - * has a valid state. - */ - TimelineViewWindow& view_window() const; - + /** adjust to the new timeline state */ + void propagateStateChange(); + /** * Registers all the styles that this class will respond to. */ @@ -202,6 +200,8 @@ private: Cairo::RefPtr playbackPointColour; gui::widgets::TimelineWidget &timelineWidget; + boost::shared_ptr timelineState; ////////////////////TICKET #796 : should use std::tr1::shared_ptr + friend class Tool; friend class ArrowTool; diff --git a/src/gui/widgets/timeline/timeline-ruler.cpp b/src/gui/widgets/timeline/timeline-ruler.cpp index afc395710..03a866482 100644 --- a/src/gui/widgets/timeline/timeline-ruler.cpp +++ b/src/gui/widgets/timeline/timeline-ruler.cpp @@ -75,6 +75,14 @@ TimelineRuler::TimelineRuler (TimelineWidget &timeline_widget) register_styles(); } +TimelineViewWindow& +TimelineRuler::viewWindow() const +{ + REQUIRE(timelineState); + return timelineState->get_view_window(); +} + + void TimelineRuler::set_mouse_chevron_offset(int offset) { @@ -114,7 +122,7 @@ TimelineRuler::on_expose_event(GdkEventExpose* event) if(!window) return false; - if(timelineWidget.get_state()) + if (timelineState) { // Prepare to render via cairo const Allocation allocation = get_allocation(); @@ -160,11 +168,11 @@ TimelineRuler::on_button_press_event(GdkEventButton* event) { REQUIRE(event != NULL); - if(timelineWidget.get_state()) + if (timelineState) { if(event->button == 1) { - pinnedDragTime = view_window().x_to_time(event->x); + pinnedDragTime = viewWindow().x_to_time(event->x); isDragging = true; } } @@ -219,34 +227,40 @@ TimelineRuler::on_size_allocate(Gtk::Allocation& allocation) } void -TimelineRuler::on_state_changed() +TimelineRuler::on_state_changed (shared_ptr newState) { - if(timelineWidget.get_state()) - { - // Connect up some events - view_window().changed_signal().connect( - sigc::mem_fun(this, &TimelineRuler::on_update_view) ); - } - + REQUIRE (newState); + timelineState = newState; + + propagateStateChange(); +} + +void +TimelineRuler::propagateStateChange() +{ + // Connect up some events + viewWindow().changed_signal().connect( + sigc::mem_fun(this, &TimelineRuler::on_update_view) ); + // Redraw on_update_view(); } + + void TimelineRuler::set_leading_x(const int x) { - shared_ptr state = timelineWidget.get_state(); - - if(state) + if (timelineState) { - TimeVar newStartPoint (view_window().x_to_time(x)); + TimeVar newStartPoint (viewWindow().x_to_time(x)); Offset selectionLength (pinnedDragTime, newStartPoint); if (newStartPoint > pinnedDragTime) newStartPoint=pinnedDragTime; // use the smaller one as selection start - state->setPlaybackPeriod (Mutation::changeTime(newStartPoint) ); - state->setPlaybackPeriod (Mutation::changeDuration(selectionLength)); + timelineState->setPlaybackPeriod (Mutation::changeTime(newStartPoint) ); + timelineState->setPlaybackPeriod (Mutation::changeDuration(selectionLength)); //////////////////////////////////////////////////////TICKET #797 : this is cheesy. Should provide a single Mutation to change all at once ////////////////////TODO : code duplication with timeline-ibeam-tool 205 } @@ -260,7 +274,7 @@ TimelineRuler::draw_ruler(Cairo::RefPtr cr, REQUIRE(ruler_rect.get_width() > 0); REQUIRE(ruler_rect.get_height() > 0); - const TimelineViewWindow &window = view_window(); + const TimelineViewWindow &window = viewWindow(); const gavl_time_t left_offset = _raw(window.get_time_offset()); const int64_t time_scale = window.get_time_scale(); @@ -361,16 +375,15 @@ TimelineRuler::draw_selection(Cairo::RefPtr cr, REQUIRE(cr); REQUIRE(ruler_rect.get_width() > 0); REQUIRE(ruler_rect.get_height() > 0); + REQUIRE(timelineState); - shared_ptr state = timelineWidget.get_state(); - REQUIRE(state); - const TimelineViewWindow &window = state->get_view_window(); + const TimelineViewWindow &window = timelineState->get_view_window(); Glib::RefPtr