Finished TimelineState work

This commit is contained in:
Joel Holdsworth 2009-03-27 14:26:08 +00:00
parent 2933deaa20
commit 7766a223eb
7 changed files with 254 additions and 120 deletions

View file

@ -37,6 +37,7 @@ using namespace Gtk;
using namespace sigc;
using namespace std;
using namespace boost;
using namespace util;
using namespace gui::widgets;
using namespace gui::model;
@ -62,24 +63,23 @@ TimelinePanel::TimelinePanel(workspace::WorkspaceWindow
zoomOut(Stock::ZOOM_OUT),
updatingToolbar(false),
currentTool(timeline::IBeam)
{
//timelineWidget.mouse_hover_signal().connect(
// mem_fun(this, &TimelinePanel::on_mouse_hover));
//timelineWidget.playback_period_drag_released_signal().connect(
// mem_fun(this, &TimelinePanel::on_playback_period_drag_released));
{
// Hook up notifications
workspace.get_project().get_sequences().signal_changed().connect(
mem_fun(this, &TimelinePanel::on_sequence_list_changed));
// Setup the notebook
notebook.signal_switch_page().connect(
mem_fun(this, &TimelinePanel::on_page_switched));
notebook.popup_enable();
// Setup the sequence chooser
sequenceChooserModel = Gtk::ListStore::create(sequenceChooserColumns);
ENSURE(sequenceChooserModel);
// Setup the sequence chooser;
sequenceChooser.set_model(sequenceChooserModel);
sequenceChooser.pack_start(sequenceChooserColumns.nameColumn);
sequenceChooser.show_all();
panelBar.pack_start(sequenceChooser, PACK_EXPAND_WIDGET);
sequenceChooserChangedConnection = sequenceChooser.signal_changed().
connect( sigc::mem_fun(*this, &TimelinePanel::on_sequence_chosen) );
panelBar.pack_start(sequenceChooser, PACK_SHRINK);
// Setup the toolbar
timeIndicatorButton.add(timeIndicator);
@ -112,11 +112,13 @@ TimelinePanel::TimelinePanel(workspace::WorkspaceWindow
toolbar.show_all();
panelBar.pack_start(toolbar, PACK_SHRINK);
// Add the notebook
pack_start(notebook, PACK_EXPAND_WIDGET);
// Setup the timeline widget
shared_ptr<Sequence> sequence
= *workspace.get_project().get_sequences().begin();
timelineWidget.reset(new TimelineWidget(load_state(sequence)));
pack_start(*timelineWidget, PACK_EXPAND_WIDGET);
// Set the initial UI state
update_notebook();
update_sequence_chooser();
update_tool_buttons();
update_zoom_buttons();
@ -165,32 +167,16 @@ TimelinePanel::on_ibeam_tool()
void
TimelinePanel::on_zoom_in()
{
TimelineWidget *const widget = get_current_page();
REQUIRE(widget != NULL);
widget->zoom_view(ZoomToolSteps);
REQUIRE(timelineWidget);
timelineWidget->zoom_view(ZoomToolSteps);
update_zoom_buttons();
}
void
TimelinePanel::on_zoom_out()
{
TimelineWidget *const widget = get_current_page();
REQUIRE(widget != NULL);
widget->zoom_view(-ZoomToolSteps);
update_zoom_buttons();
}
void
TimelinePanel::on_page_switched(GtkNotebookPage*, guint)
{
// The page has changed. Update the UI for this new page
// Set the tool in the new page to be the same as the tool in the last
// page
set_tool(currentTool);
REQUIRE(timelineWidget);
timelineWidget->zoom_view(-ZoomToolSteps);
update_zoom_buttons();
}
@ -206,11 +192,10 @@ TimelinePanel::on_playback_period_drag_released()
//----- TEST CODE - this needs to set the playback point via the
// real backend
TimelineWidget *const widget = get_current_page();
REQUIRE(widget != NULL);
REQUIRE(timelineWidget);
widget->get_state()->set_playback_point(
widget->get_state()->get_playback_period_start());
timelineWidget->get_state()->set_playback_point(
timelineWidget->get_state()->get_playback_period_start());
//----- END TEST CODE
play();
@ -219,55 +204,63 @@ TimelinePanel::on_playback_period_drag_released()
void
TimelinePanel::on_sequence_list_changed()
{
update_notebook();
update_sequence_chooser();
}
void
TimelinePanel::on_sequence_chosen()
{
REQUIRE(timelineWidget);
Gtk::TreeIter iter = sequenceChooser.get_active();
if(iter)
{
weak_ptr<Sequence> sequence_ptr =
(*iter)[sequenceChooserColumns.sequenceColumn];
shared_ptr<Sequence> sequence(sequence_ptr.lock());
if(sequence)
{
shared_ptr<timeline::TimelineState> old_state(
timelineWidget->get_state());
REQUIRE(old_state);
if(sequence != old_state->get_sequence())
timelineWidget->set_state(load_state(sequence));
}
}
update_zoom_buttons();
}
void
TimelinePanel::update_sequence_chooser()
{
sequenceChooser.clear_items();
REQUIRE(sequenceChooserModel);
// Block the event handler
sequenceChooserChangedConnection.block();
// Repopulate the sequence chooser
sequenceChooserModel->clear();
shared_ptr<timeline::TimelineState> state =
timelineWidget->get_state();
REQUIRE(state);
BOOST_FOREACH( shared_ptr< model::Sequence > sequence,
workspace.get_project().get_sequences() )
{
sequenceChooser.append_text(sequence->get_name());
}
}
void
TimelinePanel::update_notebook()
{
std::map<const model::Sequence*, shared_ptr<TimelineWidget> >
old_pages;
old_pages.swap(notebook_pages);
BOOST_FOREACH( shared_ptr< model::Sequence > sequence,
workspace.get_project().get_sequences() )
{
std::map<const model::Sequence*, shared_ptr<TimelineWidget> >::
iterator iterator = old_pages.find(sequence.get());
if(iterator != old_pages.end())
{
// This sequence has not been changed
// leave it in the new list
notebook_pages[iterator->first] = iterator->second;
old_pages.erase(iterator->first);
}
else
{
// This is a new sequence, add it in
shared_ptr< timeline::TimelineState > state(
new timeline::TimelineState(sequence));
shared_ptr< TimelineWidget > widget(
new TimelineWidget(state));
notebook_pages[sequence.get()] = widget;
notebook.append_page(*widget.get(), sequence->get_name());
notebook.set_tab_reorderable(*widget.get());
}
Gtk::TreeIter iter = sequenceChooserModel->append();
Gtk::TreeModel::Row row = *iter;
row[sequenceChooserColumns.sequenceColumn] = sequence;
row[sequenceChooserColumns.nameColumn] = sequence->get_name();
if(state->get_sequence() == sequence)
sequenceChooser.set_active(iter);
}
notebook.show_all_children();
// Unblock the event handler
sequenceChooserChangedConnection.unblock();
}
void
@ -292,17 +285,14 @@ TimelinePanel::update_tool_buttons()
void
TimelinePanel::update_zoom_buttons()
{
TimelineWidget *const widget = get_current_page();
REQUIRE(timelineWidget);
timeline::TimelineViewWindow &viewWindow =
timelineWidget->get_state()->get_view_window();
if(widget != NULL)
{
timeline::TimelineViewWindow &viewWindow =
widget->get_state()->get_view_window();
zoomIn.set_sensitive(viewWindow.get_time_scale() != 1);
zoomOut.set_sensitive(viewWindow.get_time_scale()
!= TimelineWidget::MaxScale);
}
zoomIn.set_sensitive(viewWindow.get_time_scale() != 1);
zoomOut.set_sensitive(viewWindow.get_time_scale()
!= TimelineWidget::MaxScale);
}
void
@ -327,15 +317,13 @@ TimelinePanel::is_playing() const
void
TimelinePanel::set_tool(timeline::ToolType tool)
{
REQUIRE(timelineWidget);
if(updatingToolbar) return;
TimelineWidget *const widget = get_current_page();
if(widget != NULL)
{
currentTool = tool;
widget->set_tool(tool);
update_tool_buttons();
}
currentTool = tool;
timelineWidget->set_tool(tool);
update_tool_buttons();
}
void
@ -344,17 +332,6 @@ TimelinePanel::show_time(gavl_time_t time)
timeIndicator.set_text(lumiera_tmpbuf_print_time(time));
}
TimelineWidget*
TimelinePanel::get_current_page()
{
Notebook::PageList::iterator page_iterator = notebook.get_current();
if(!page_iterator) return NULL;
Widget* const widget = (*page_iterator).get_child();
REQUIRE(widget != NULL);
return (TimelineWidget*)widget;
}
bool
TimelinePanel::on_frame()
{
@ -374,5 +351,18 @@ TimelinePanel::on_frame()
return true;
}
shared_ptr<timeline::TimelineState>
TimelinePanel::load_state(weak_ptr<Sequence> sequence)
{
if(contains(timelineStates, sequence))
return timelineStates[sequence];
shared_ptr<Sequence> shared_sequence = sequence.lock();
if(shared_sequence)
return shared_ptr< timeline::TimelineState >(
new timeline::TimelineState(shared_sequence));
return shared_ptr< timeline::TimelineState >();
}
} // namespace panels
} // namespace gui

View file

@ -32,7 +32,7 @@
using namespace gui::widgets;
namespace gui {
namespace model {
class Sequence;
}
@ -70,20 +70,25 @@ private:
void on_zoom_out();
void on_time_pressed();
void on_page_switched(GtkNotebookPage*, guint);
void on_mouse_hover(gavl_time_t time);
void on_playback_period_drag_released();
/**
* An event handler for when the list of sequences changes.
**/
void on_sequence_list_changed();
/**
* An event handler for when a new sequence is chosen in the
* sequenceChooser.
**/
void on_sequence_chosen();
private:
void update_sequence_chooser();
void update_notebook();
void update_playback_buttons();
void update_tool_buttons();
void update_zoom_buttons();
@ -97,8 +102,36 @@ private:
void set_tool(gui::widgets::timeline::ToolType tool);
void show_time(gavl_time_t time);
boost::shared_ptr<widgets::timeline::TimelineState> load_state(
boost::weak_ptr<model::Sequence> sequence);
TimelineWidget* get_current_page();
private:
/**
* The definition of the sequence chooser combo box columns
**/
class SequenceChooserColumns : public Gtk::TreeModel::ColumnRecord
{
public:
/**
* Constructor
**/
SequenceChooserColumns()
{ add(nameColumn); add(sequenceColumn); }
/**
* An invisible column which will be used to identify the sequence
* of a row.
**/
Gtk::TreeModelColumn< boost::weak_ptr<model::Sequence> >
sequenceColumn;
/**
* The column to use as the label for the combo box widget items.
**/
Gtk::TreeModelColumn< Glib::ustring > nameColumn;
};
private:
@ -106,12 +139,19 @@ private:
// Grip Widgets
ButtonBar toolbar;
Gtk::ComboBoxText sequenceChooser;
// Sequence Chooser
SequenceChooserColumns sequenceChooserColumns;
Glib::RefPtr<Gtk::ListStore> sequenceChooserModel;
Gtk::ComboBox sequenceChooser;
sigc::connection sequenceChooserChangedConnection;
// Body Widgets
Gtk::Notebook notebook;
std::map< const model::Sequence*, boost::shared_ptr<TimelineWidget> >
notebook_pages;
boost::scoped_ptr<TimelineWidget> timelineWidget;
std::map< boost::weak_ptr<model::Sequence>,
boost::shared_ptr<widgets::timeline::TimelineState> >
timelineStates;
// Toolbar Widgets
Gtk::Label timeIndicator;

View file

@ -109,8 +109,14 @@ TimelineWidget::get_state()
void
TimelineWidget::set_state(shared_ptr<timeline::TimelineState> new_state)
{
if(!new_state)
return;
state = new_state;
REQUIRE(state);
// Clear the track tree
trackMap.clear();
// Hook up event handlers
state->get_view_window().changed_signal().connect( sigc::mem_fun(
@ -125,6 +131,9 @@ TimelineWidget::set_state(shared_ptr<timeline::TimelineState> new_state)
&TimelineWidget::on_body_changed));
update_tracks();
// Send the state changed signal
stateChangedSignal.emit();
}
void
@ -175,6 +184,11 @@ TimelineWidget::hovering_track_changed_signal() const
{
return hoveringTrackChangedSignal;
}
sigc::signal<void>
TimelineWidget::state_changed_signal() const
{
return stateChangedSignal;
}
/* ===== Events ===== */

View file

@ -57,6 +57,8 @@ class TimelineWidget : public Gtk::Table
public:
/**
* Constructor
* @param source_state The state that will be used as the data source
* for this timeline widget.
*/
TimelineWidget(
boost::shared_ptr<timeline::TimelineState> source_state);
@ -69,8 +71,17 @@ public:
/* ===== Data Access ===== */
public:
/**
* Gets a pointer to the current state object.
* @return The state object that the timeline widget is currently
* working with.
**/
boost::shared_ptr<timeline::TimelineState> get_state();
/**
* Replaces the current TimelineState object with another.
* @param new_state The new state to swap in.
**/
void set_state(boost::shared_ptr<timeline::TimelineState> new_state);
/**
@ -101,6 +112,8 @@ public:
sigc::signal<void, boost::shared_ptr<timeline::Track> >
hovering_track_changed_signal() const;
sigc::signal<void> state_changed_signal() const;
/* ===== Events ===== */
protected:
@ -217,6 +230,10 @@ private:
protected:
/**
* The state that will be used as the data source for this timeline
* widget.
**/
boost::shared_ptr<timeline::TimelineState> state;
// Model Data
@ -251,6 +268,7 @@ protected:
sigc::signal<void> playbackPeriodDragReleasedSignal;
sigc::signal<void, boost::shared_ptr<timeline::Track> >
hoveringTrackChangedSignal;
sigc::signal<void> stateChangedSignal;
bool update_tracks_frozen;

View file

@ -50,11 +50,14 @@ TimelineBody::TimelineBody(TimelineWidget &timeline_widget) :
timelineWidget(timeline_widget)
{
// Connect up some events
view_window().changed_signal().connect(
sigc::mem_fun(this, &TimelineBody::on_update_view) );
timeline_widget.state_changed_signal().connect(
sigc::mem_fun(this, &TimelineBody::on_state_changed) );
// Install style properties
register_styles();
// Reset the state
on_state_changed();
}
TimelineBody::~TimelineBody()
@ -270,6 +273,17 @@ TimelineBody::on_motion_notify_event(GdkEventMotion *event)
return false;
}
void
TimelineBody::on_state_changed()
{
// Connect up some events
view_window().changed_signal().connect(
sigc::mem_fun(this, &TimelineBody::on_update_view) );
// Redraw
queue_draw();
}
void
TimelineBody::draw_tracks(Cairo::RefPtr<Cairo::Context> cr)
{

View file

@ -75,7 +75,7 @@ public:
* @param tool_type The type of tool to set.
*/
void set_tool(ToolType tool_type);
/* ===== Events ===== */
protected:
@ -111,6 +111,12 @@ protected:
*/
bool on_motion_notify_event(GdkEventMotion *event);
/**
* The event handler for when the TimelineWidget's state object is
* replaced.
**/
void on_state_changed();
/* ===== Internals ===== */
private:

View file

@ -37,14 +37,28 @@ class Sequence;
namespace widgets {
namespace timeline {
/**
* TimelineState is a container for the state data for TimelineWidget.
* @remarks TimelineState s can be swapped out so that TimelineWidget
* can flip between different views.
**/
class TimelineState
{
public:
/**
* Constructor
* @param source_sequence The sequence on which the TimelineWidget
* will operate when this TimelineState is attached.
**/
TimelineState(boost::shared_ptr<model::Sequence> source_sequence);
public:
/**
* Gets the sequence that is attached to this timeline state object.
* @return Returns a shared_ptr to the sequence object.
**/
boost::shared_ptr<model::Sequence> get_sequence() const;
/**
@ -104,9 +118,15 @@ public:
*/
gavl_time_t get_playback_point() const;
/**
* A signal to notify when the selected period has changed.
**/
sigc::signal<void> selection_changed_signal() const;
/**
* A signal to notify when the playback point or playback periods have
* changed.
**/
sigc::signal<void> playback_changed_signal() const;
private:
@ -120,17 +140,49 @@ private:
boost::shared_ptr<model::Sequence> sequence;
// View State
/**
* The ViewWindow for the TimelineWidget display with.
**/
timeline::TimelineViewWindow viewWindow;
// Selection State
/**
* The start time of the selection period.
**/
gavl_time_t selectionStart;
/**
* The end time of the selection period.
**/
gavl_time_t selectionEnd;
/**
* The start time of the playback period.
**/
gavl_time_t playbackPeriodStart;
/**
* The end time of the playback period.
**/
gavl_time_t playbackPeriodEnd;
/**
* The time of the playback point.
**/
gavl_time_t playbackPoint;
// Signals
/**
* A signal to notify when the selected period has changed.
**/
sigc::signal<void> selectionChangedSignal;
/**
* A signal to notify when the playback point or playback periods have
* changed.
**/
sigc::signal<void> playbackChangedSignal;
};