lumiera_documentation/src/gui/widget/timeline-widget.cpp
Ichthyostega 9c21164ae6 Doxygen Fixes (#1062)
This changeset fixes a huge pile of problems, as indicated in the
error log of the Doxygen run after merging all the recent Doxygen improvements

unfortunately, auto-linking does still not work at various places.
There is no clear indication what might be the problem.
Possibly the rather unstable Sqlite support in this Doxygen version
is the cause. Anyway, needs to be investigated further.
2017-04-02 04:22:51 +02:00

559 lines
15 KiB
C++

/*
TimelineWidget - custom widget for timeline display of the project
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.
* *****************************************************/
/** @file widget/timeline-widget.cpp
**
** @deprecated as of 11/2016, a complete rework of the timeline display is underway
** @see gui::timeline::TimelineWidget new timeline display
**
*/
#include "gui/widget/timeline-widget.hpp"
#include <boost/foreach.hpp>
#include <typeinfo>
#include <memory>
using lib::time::Time;
using lib::time::TimeValue;
using std::dynamic_pointer_cast;
using namespace Gtk; ////////////////////////////////////////////////////////////////////////////////TICKET #1071 no wildcard includes please!
using namespace util; ////////////////////////////////////////////////////////////////////////////////TICKET #1071 no wildcard includes please!
using namespace gui::widget::timeline; ////////////TODO OK this one looks more sensible, but why do we need to refer to such a nested space so much?
namespace gui {
namespace widget {
const int TimelineWidget::TrackPadding = 1;
const int TimelineWidget::HeaderWidth = 150;
const int TimelineWidget::HeaderIndentWidth = 10;
TimelineWidget::TimelineWidget (shared_ptr<timeline::TimelineState> source_state)
: Table(2, 2)
, layoutHelper(*this)
, headerContainer(NULL)
, body(NULL)
, ruler(NULL)
, horizontalAdjustment(Gtk::Adjustment::create(0, 0, 0))
, verticalAdjustment(Gtk::Adjustment::create(0, 0, 0))
, horizontalScroll(horizontalAdjustment)
, verticalScroll(verticalAdjustment)
, update_tracks_frozen(false)
{
body = manage(new TimelineBody(*this));
ENSURE(body != NULL);
headerContainer = manage(new TimelineHeaderContainer(*this));
ENSURE(headerContainer != NULL);
ruler = manage(new TimelineRuler(*this));
ENSURE(ruler != NULL);
horizontalAdjustment->signal_value_changed().connect( sigc::mem_fun(
this, &TimelineWidget::on_scroll) );
verticalAdjustment->signal_value_changed().connect( sigc::mem_fun(
this, &TimelineWidget::on_scroll) );
body->signal_motion_notify_event().connect( sigc::mem_fun(
this, &TimelineWidget::on_motion_in_body_notify_event) );
update_tracks();
attach(*body, 1, 2, 1, 2, FILL|EXPAND, FILL|EXPAND);
attach(*ruler, 1, 2, 0, 1, FILL|EXPAND, SHRINK);
attach(*headerContainer, 0, 1, 1, 2, SHRINK, FILL|EXPAND);
attach(horizontalScroll, 1, 2, 2, 3, FILL|EXPAND, SHRINK);
attach(verticalScroll, 2, 3, 1, 2, SHRINK, FILL|EXPAND);
set_state(source_state);
set_tool(timeline::Arrow);
}
TimelineWidget::~TimelineWidget()
{
REQUIRE(headerContainer);
headerContainer->clear_headers();
trackMap.clear();
}
/* ===== Data Access ===== */
/** @deprecated for #955 */
shared_ptr<timeline::TimelineState>
TimelineWidget::get_state()
{
return state;
}
/** @deprecated for #955 */
void
TimelineWidget::set_state (shared_ptr<timeline::TimelineState> new_state)
{
state = new_state;
// Clear the track tree
trackMap.clear();
if (state)
{
// Hook up event handlers
state->getViewWindow().changed_signal().connect( sigc::mem_fun(
this, &TimelineWidget::on_view_window_changed) );
state->getSequence()->get_child_track_list().signal_changed().
connect(sigc::mem_fun(
this, &TimelineWidget::on_track_list_changed ) );
state->selectionChangedSignal().connect(mem_fun(*this,
&TimelineWidget::on_body_changed));
state->playbackChangedSignal().connect(mem_fun(*this,
&TimelineWidget::on_body_changed));
}
update_tracks();
// Send the state changed signal
stateChangedSignal.emit (state);
}
void
TimelineWidget::zoom_view (double timescale_ratio)
{
if(state)
{
const int view_width = body->get_allocation().get_width();
state->getViewWindow().zoom_view(view_width / 2, timescale_ratio);
}
}
ToolType
TimelineWidget::get_tool() const
{
REQUIRE(body != NULL);
return body->get_tool();
}
void
TimelineWidget::set_tool(ToolType tool_type)
{
REQUIRE(body != NULL);
body->set_tool(tool_type);
}
shared_ptr<timeline::Track>
TimelineWidget::get_hovering_track() const
{
return hoveringTrack;
}
/* ===== Signals ===== */
sigc::signal<void, Time>
TimelineWidget::mouse_hover_signal() const
{
return mouseHoverSignal;
}
sigc::signal<void>
TimelineWidget::playback_period_drag_released_signal() const
{
return playbackPeriodDragReleasedSignal;
}
sigc::signal<void, shared_ptr<timeline::Track>>
TimelineWidget::hovering_track_changed_signal() const
{
return hoveringTrackChangedSignal;
}
TimelineWidget::TimelineStateChangeSignal
TimelineWidget::state_changed_signal() const
{
return stateChangedSignal;
}
/* ===== Events ===== */
void
TimelineWidget::on_scroll()
{
if(state)
{
TimeValue newStartOffset ((gavl_time_t)horizontalAdjustment->get_value());
state->getViewWindow().set_time_offset(Time(newStartOffset));
}
}
void
TimelineWidget::on_size_allocate(Allocation& allocation)
{
Widget::on_size_allocate(allocation);
update_scroll();
}
void
TimelineWidget::on_view_window_changed()
{
REQUIRE(ruler != NULL);
if(state)
{
timeline::TimelineViewWindow &window = state->getViewWindow();
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()));
}
}
void
TimelineWidget::on_body_changed()
{
REQUIRE(ruler != NULL);
REQUIRE(body != NULL);
ruler->queue_draw();
body->queue_draw();
}
void
TimelineWidget::on_add_track_command()
{
// # TEST CODE
if(sequence())
sequence()->get_child_track_list().push_back(
shared_ptr<model::Track>(new model::ClipTrack()));
}
/* ===== Internals ===== */
void
TimelineWidget::update_tracks()
{
if(update_tracks_frozen)
return;
if(state)
{
// Remove any tracks which are no longer present in the model
remove_orphaned_tracks();
// Create timeline tracks from all the model tracks
create_timeline_tracks();
// Update the layout helper
layoutHelper.clone_tree_from_sequence();
layoutHelper.update_layout();
}
else
trackMap.clear();
}
void
TimelineWidget::freeze_update_tracks()
{
update_tracks_frozen = true;
}
void
TimelineWidget::thaw_update_tracks()
{
update_tracks_frozen = false;
}
void
TimelineWidget::create_timeline_tracks()
{
REQUIRE(state);
BOOST_FOREACH(shared_ptr<model::Track> child,
sequence()->get_child_tracks())
create_timeline_tracks_from_branch(child);
// Update the header container
REQUIRE(headerContainer != NULL);
headerContainer->update_headers();
}
/** @deprecated for #955 */
void
TimelineWidget::create_timeline_tracks_from_branch(
shared_ptr<model::Track> modelTrack)
{
REQUIRE(modelTrack);
// Is a timeline UI track present in the map already?
if (!contains(trackMap, modelTrack))
{
// The timeline UI track is not present
// We will need to create one
trackMap[modelTrack] =
create_timeline_track_from_modelTrack(modelTrack);
}
// Recurse to child tracks
BOOST_FOREACH(shared_ptr<model::Track> child,
modelTrack->get_child_tracks())
create_timeline_tracks_from_branch(child);
}
/** @deprecated for #955 */
shared_ptr<timeline::Track>
TimelineWidget::create_timeline_track_from_modelTrack(
shared_ptr<model::Track> modelTrack)
{
REQUIRE(modelTrack);
// Choose a corresponding timeline track class from the model track's
// class
if(typeid(*modelTrack) == typeid(model::ClipTrack))
return shared_ptr<timeline::Track>(new timeline::ClipTrack(
*this, dynamic_pointer_cast<model::ClipTrack>(modelTrack)));
else if(typeid(*modelTrack) == typeid(model::GroupTrack))
return shared_ptr<timeline::Track>(new timeline::GroupTrack(
*this, dynamic_pointer_cast<model::GroupTrack>(modelTrack)));
ASSERT(NULL); // Unknown track type;
return shared_ptr<timeline::Track>();
}
/** @deprecated for #955 */
void
TimelineWidget::remove_orphaned_tracks()
{
std::map<shared_ptr<model::Track>,
shared_ptr<timeline::Track>>
orphan_track_map(trackMap);
// Remove all tracks which are still present in the sequence
BOOST_FOREACH(shared_ptr<model::Track> child,
sequence()->get_child_tracks())
search_orphaned_tracks_in_branch(child, orphan_track_map);
// orphan_track_map now contains all the orphaned tracks
// Remove them
std::pair<shared_ptr<model::Track>, shared_ptr<timeline::Track>>
pair;
BOOST_FOREACH( pair, orphan_track_map )
{
ENSURE(pair.first);
trackMap.erase(pair.first);
}
}
/** @deprecated for #955 */
void
TimelineWidget::search_orphaned_tracks_in_branch(
shared_ptr<model::Track> modelTrack,
std::map<shared_ptr<model::Track>,
shared_ptr<timeline::Track>>& orphan_track_map)
{
REQUIRE(modelTrack);
// Is the timeline UI still present?
if(contains(orphan_track_map, modelTrack))
orphan_track_map.erase(modelTrack);
// Recurse to child tracks
BOOST_FOREACH(shared_ptr<model::Track> child,
modelTrack->get_child_tracks())
search_orphaned_tracks_in_branch(child, orphan_track_map);
}
/** @deprecated for #955 */
shared_ptr<timeline::Track>
TimelineWidget::lookup_timeline_track(
shared_ptr<model::Track> modelTrack) const
{
REQUIRE(modelTrack);
REQUIRE(modelTrack != sequence()); // The sequence isn't
// really a track
std::map<shared_ptr<model::Track>, shared_ptr<timeline::Track>>::
const_iterator iterator = trackMap.find(modelTrack);
if (iterator == trackMap.end())
{
// The track is not present in the map
// We are in an error condition if the timeline track is not found
// - the timeline tracks must always be synchronous with the model
// tracks.
ENSURE(0);
return shared_ptr<timeline::Track>();
}
ENSURE(iterator->second != NULL);
return iterator->second;
}
void
TimelineWidget::on_layout_changed()
{
REQUIRE(headerContainer != NULL);
REQUIRE(body != NULL);
headerContainer->on_layout_changed();
body->queue_draw();
update_scroll();
}
void
TimelineWidget::update_scroll()
{
REQUIRE(body != NULL);
const Allocation body_allocation = body->get_allocation();
if(state)
{
///////////////////////////////////////////////TICKET #861 shoudln't that be performed by TimelineViewWindow, instead of manipulating values from the outside?
timeline::TimelineViewWindow &window = state->getViewWindow();
//----- Horizontal Scroll ------//
// TEST CODE
horizontalAdjustment->set_upper( 1000 * GAVL_TIME_SCALE / 200);
horizontalAdjustment->set_lower(-1000 * GAVL_TIME_SCALE / 200);
// Set the page size
horizontalAdjustment->set_page_size(
window.get_time_scale() * body_allocation.get_width());
//----- Vertical Scroll -----//
// Calculate the vertical length that can be scrolled:
// the total height of all the tracks minus one screenful
int y_scroll_length = layoutHelper.get_total_height() -
body_allocation.get_height();
if(y_scroll_length < 0) y_scroll_length = 0;
// If by resizing we're now over-scrolled, scroll back to
// maximum distance
if((int)verticalAdjustment->get_value() > y_scroll_length)
verticalAdjustment->set_value(y_scroll_length);
verticalAdjustment->set_upper(y_scroll_length);
// Hide the scrollbar if no scrolling is possible
// Having this code included seems to cause a layout loop as the
// window is shrunk
if(y_scroll_length <= 0 && verticalScroll.get_visible())
verticalScroll.hide();
else if(y_scroll_length > 0 && !verticalScroll.get_visible())
verticalScroll.show();
}
}
int
TimelineWidget::get_y_scroll_offset() const
{
return (int)verticalAdjustment->get_value();
}
void
TimelineWidget::set_y_scroll_offset(const int offset)
{
verticalAdjustment->set_value(offset);
}
bool
TimelineWidget::on_motion_in_body_notify_event(GdkEventMotion *event)
{
REQUIRE(event != NULL);
ruler->set_mouse_chevron_offset(event->x);
if(state)
{
timeline::TimelineViewWindow &window = state->getViewWindow();
mouseHoverSignal.emit(window.x_to_time(event->x));
}
return true;
}
/** @deprecated for #955 */
shared_ptr<model::Sequence>
TimelineWidget::sequence() const
{
if(!state)
return shared_ptr<model::Sequence>();
shared_ptr<model::Sequence> sequence = state->getSequence();
ENSURE(sequence);
return sequence;
}
void
TimelineWidget::on_track_list_changed()
{
update_tracks();
}
void
TimelineWidget::on_playback_period_drag_released()
{
playbackPeriodDragReleasedSignal.emit();
}
void
TimelineWidget::set_hovering_track(shared_ptr<timeline::Track> hovering_track)
{
hoveringTrack = hovering_track;
hoveringTrackChangedSignal.emit(hovering_track);
}
}}// gui::widget