Tidied and documented TimelineLayoutHelper

This commit is contained in:
Joel Holdsworth 2009-01-24 10:58:54 +00:00
parent 1908ff08a0
commit 9879aef3dd
2 changed files with 304 additions and 187 deletions

View file

@ -154,7 +154,7 @@ TimelineLayoutHelper::begin_dragging_track(
draggingTrackIter = iterator_from_track(model_track);
dragBranchHeight = measure_branch_height(draggingTrackIter);
draggingDrop.relation = None;
dropPoint.relation = None;
return dragging_track;
}
@ -163,7 +163,7 @@ void
TimelineLayoutHelper::end_dragging_track(bool apply)
{
if(apply)
apply_drop_to_model_tree(draggingDrop);
apply_drop_to_model_tree(dropPoint);
draggingTrackIter.node = NULL;
clone_tree_from_sequence();
@ -183,14 +183,17 @@ TimelineLayoutHelper::get_dragging_track_iter() const
}
void
TimelineLayoutHelper::drag_to_point(const Gdk::Point &point)
TimelineLayoutHelper::drag_to_point(const Gdk::Point &mouse_point)
{
Drop drop;
DropPoint drop;
// begin_dragging_track must have been called before
REQUIRE(is_dragging_track());
// Apply the scroll offset
const Gdk::Point last_point(dragPoint);
dragPoint = Gdk::Point(point.get_x(),
point.get_y() + timelineWidget.get_y_scroll_offset());
dragPoint = Gdk::Point(mouse_point.get_x(),
mouse_point.get_y() + timelineWidget.get_y_scroll_offset());
// Get a test-point
// We probe on the bottom edge of the dragging branch if the track is
@ -226,182 +229,12 @@ TimelineLayoutHelper::drag_to_point(const Gdk::Point &point)
if(drop.relation != None)
{
apply_drop_to_layout_tree(drop);
draggingDrop = drop;
dropPoint = drop;
}
update_layout();
}
TimelineLayoutHelper::Drop
TimelineLayoutHelper::attempt_drop(TrackTree::pre_order_iterator target,
const Gdk::Point &point)
{
// Lookup the tracks
const shared_ptr<model::Track> model_track(*target);
REQUIRE(model_track);
const weak_ptr<timeline::Track> timeline_track =
lookup_timeline_track(model_track);
// Calculate coordinates
const Gdk::Rectangle &rect = headerBoxes[timeline_track];
const int half_height = rect.get_height() / 2;
const int y = rect.get_y();
const int y_mid = y + half_height;
const int full_width = rect.get_x() + rect.get_width();
const int x_mid = rect.get_x() + rect.get_width() / 2;
// Initialize the drop
// By specifying relation = None, the default return value will signal
// no drop-point was foind at point
Drop drop = {target, None};
if(pt_in_rect(point, Gdk::Rectangle(0, y, full_width, half_height)))
{
// We're hovering over the upper half of the header
drop.relation = Before;
}
else if(pt_in_rect(point, Gdk::Rectangle(0, y_mid,
full_width, half_height)))
{
// We're hovering over the lower half of the header
if(model_track->can_host_children())
{
if(model_track->get_child_tracks().empty())
{
// Is our track being dragged after this header?
if(dragPoint.get_x() < x_mid)
drop.relation = After;
else
drop.relation = FirstChild;
}
else
drop.relation = LastChild;
}
else
{
// When this track cannot be a parent, the dragging track is
// simply dropped after
drop.relation = After;
}
}
return drop;
}
void
TimelineLayoutHelper::apply_drop_to_layout_tree(
const TimelineLayoutHelper::Drop &drop)
{
switch(drop.relation)
{
case None:
break;
case Before:
draggingTrackIter = layoutTree.move_before(
drop.target, draggingTrackIter);
break;
case After:
draggingTrackIter = layoutTree.move_after(
drop.target, draggingTrackIter);
break;
case FirstChild:
if(draggingTrackIter.node->parent != drop.target.node)
{
draggingTrackIter = layoutTree.move_ontop(
layoutTree.prepend_child(drop.target), draggingTrackIter);
}
break;
case LastChild:
if(draggingTrackIter.node->parent != drop.target.node)
{
draggingTrackIter = layoutTree.move_ontop(
layoutTree.append_child(drop.target), draggingTrackIter);
}
break;
default:
ASSERT(0); // Unexpected value of relation
break;
}
}
void
TimelineLayoutHelper::apply_drop_to_model_tree(
const TimelineLayoutHelper::Drop &drop)
{
if(drop.relation == None)
return;
// Freeze the timeline widget - it must be done manually later
timelineWidget.freeze_update_tracks();
// Get the tracks
shared_ptr<model::Track> &dragging_track = *draggingTrackIter;
REQUIRE(dragging_track);
REQUIRE(dragging_track != timelineWidget.sequence);
shared_ptr<model::Track> &target_track = *drop.target;
REQUIRE(target_track);
REQUIRE(target_track != timelineWidget.sequence);
// Detach the track from the old parent
shared_ptr<model::ParentTrack> old_parent =
dynamic_pointer_cast<model::ParentTrack, model::Track>(
model::Track::find_parent(
timelineWidget.sequence, dragging_track));
REQUIRE(old_parent); // The track must have a parent
old_parent->get_child_track_list().remove(dragging_track);
if(drop.relation == Before || drop.relation == After)
{
// Find the new parent track
shared_ptr<model::ParentTrack> new_parent =
dynamic_pointer_cast<model::ParentTrack, model::Track>(
model::Track::find_parent(
timelineWidget.sequence, target_track));
REQUIRE(new_parent); // The track must have a parent
// Find the destination point
observable_list< shared_ptr<model::Track> > &dest =
new_parent->get_child_track_list();
list< shared_ptr<model::Track> >::iterator iter;
for(iter = dest.begin(); iter != dest.end(); iter++)
{
if(*iter == target_track)
break;
}
REQUIRE(iter != dest.end()); // The target must be
// in the destination
// We have to jump on 1 if we want to insert after
if(drop.relation == After)
iter++;
// Insert at this point
dest.insert(iter, dragging_track);
}
else if(drop.relation == FirstChild || drop.relation == LastChild)
{
shared_ptr<model::ParentTrack> new_parent =
dynamic_pointer_cast<model::ParentTrack, model::Track>(
target_track);
REQUIRE(new_parent); // The track must have a parent
if(drop.relation == FirstChild)
new_parent->get_child_track_list().push_front(dragging_track);
else if(drop.relation == LastChild)
new_parent->get_child_track_list().push_back(dragging_track);
}
else ASSERT(0); // Unexpected value of relation
// Freeze the timeline widget - we will do it manually
timelineWidget.freeze_update_tracks();
}
int
TimelineLayoutHelper::get_total_height() const
{
@ -612,6 +445,176 @@ TimelineLayoutHelper::on_animation_tick()
return animating;
}
TimelineLayoutHelper::DropPoint
TimelineLayoutHelper::attempt_drop(TrackTree::pre_order_iterator target,
const Gdk::Point &point)
{
// Lookup the tracks
const shared_ptr<model::Track> model_track(*target);
REQUIRE(model_track);
const weak_ptr<timeline::Track> timeline_track =
lookup_timeline_track(model_track);
// Calculate coordinates
const Gdk::Rectangle &rect = headerBoxes[timeline_track];
const int half_height = rect.get_height() / 2;
const int y = rect.get_y();
const int y_mid = y + half_height;
const int full_width = rect.get_x() + rect.get_width();
const int x_mid = rect.get_x() + rect.get_width() / 2;
// Initialize the drop
// By specifying relation = None, the default return value will signal
// no drop-point was foind at point
DropPoint drop = {target, None};
if(pt_in_rect(point, Gdk::Rectangle(0, y, full_width, half_height)))
{
// We're hovering over the upper half of the header
drop.relation = Before;
}
else if(pt_in_rect(point, Gdk::Rectangle(0, y_mid,
full_width, half_height)))
{
// We're hovering over the lower half of the header
if(model_track->can_host_children())
{
if(model_track->get_child_tracks().empty())
{
// Is our track being dragged after this header?
if(dragPoint.get_x() < x_mid)
drop.relation = After;
else
drop.relation = FirstChild;
}
else
drop.relation = LastChild;
}
else
{
// When this track cannot be a parent, the dragging track is
// simply dropped after
drop.relation = After;
}
}
return drop;
}
void
TimelineLayoutHelper::apply_drop_to_layout_tree(
const TimelineLayoutHelper::DropPoint &drop)
{
switch(drop.relation)
{
case None:
break;
case Before:
draggingTrackIter = layoutTree.move_before(
drop.target, draggingTrackIter);
break;
case After:
draggingTrackIter = layoutTree.move_after(
drop.target, draggingTrackIter);
break;
case FirstChild:
if(draggingTrackIter.node->parent != drop.target.node)
{
draggingTrackIter = layoutTree.move_ontop(
layoutTree.prepend_child(drop.target), draggingTrackIter);
}
break;
case LastChild:
if(draggingTrackIter.node->parent != drop.target.node)
{
draggingTrackIter = layoutTree.move_ontop(
layoutTree.append_child(drop.target), draggingTrackIter);
}
break;
default:
ASSERT(0); // Unexpected value of relation
break;
}
}
void
TimelineLayoutHelper::apply_drop_to_model_tree(
const TimelineLayoutHelper::DropPoint &drop)
{
if(drop.relation == None)
return;
// Freeze the timeline widget - it must be done manually later
timelineWidget.freeze_update_tracks();
// Get the tracks
shared_ptr<model::Track> &dragging_track = *draggingTrackIter;
REQUIRE(dragging_track);
REQUIRE(dragging_track != timelineWidget.sequence);
shared_ptr<model::Track> &target_track = *drop.target;
REQUIRE(target_track);
REQUIRE(target_track != timelineWidget.sequence);
// Detach the track from the old parent
shared_ptr<model::ParentTrack> old_parent =
dynamic_pointer_cast<model::ParentTrack, model::Track>(
model::Track::find_parent(
timelineWidget.sequence, dragging_track));
REQUIRE(old_parent); // The track must have a parent
old_parent->get_child_track_list().remove(dragging_track);
if(drop.relation == Before || drop.relation == After)
{
// Find the new parent track
shared_ptr<model::ParentTrack> new_parent =
dynamic_pointer_cast<model::ParentTrack, model::Track>(
model::Track::find_parent(
timelineWidget.sequence, target_track));
REQUIRE(new_parent); // The track must have a parent
// Find the destination point
observable_list< shared_ptr<model::Track> > &dest =
new_parent->get_child_track_list();
list< shared_ptr<model::Track> >::iterator iter;
for(iter = dest.begin(); iter != dest.end(); iter++)
{
if(*iter == target_track)
break;
}
REQUIRE(iter != dest.end()); // The target must be
// in the destination
// We have to jump on 1 if we want to insert after
if(drop.relation == After)
iter++;
// Insert at this point
dest.insert(iter, dragging_track);
}
else if(drop.relation == FirstChild || drop.relation == LastChild)
{
shared_ptr<model::ParentTrack> new_parent =
dynamic_pointer_cast<model::ParentTrack, model::Track>(
target_track);
REQUIRE(new_parent); // The track must have a parent
if(drop.relation == FirstChild)
new_parent->get_child_track_list().push_front(dragging_track);
else if(drop.relation == LastChild)
new_parent->get_child_track_list().push_back(dragging_track);
}
else ASSERT(0); // Unexpected value of relation
// Freeze the timeline widget - we will do it manually
timelineWidget.freeze_update_tracks();
}
} // namespace timeline
} // namespace widgets
} // namespace gui

View file

@ -127,16 +127,40 @@ public:
**/
boost::shared_ptr<timeline::Track> track_from_y(int y);
/**
* Begins to drag the track under mouse_point, if there is one.
* @param mouse_point The mouse point to begin dragging from, measured
* in pixels from the top left of the header container widget.
**/
boost::shared_ptr<timeline::Track>
begin_dragging_track(const Gdk::Point &mouse_point);
/**
* Drops the dragging track.
* @param apply true if the model tree should be modified.
**/
void end_dragging_track(bool apply);
/**
* Returns true if a track is being dragged.
**/
bool is_dragging_track() const;
/**
* Gets the iterator of the layout tree node that is being dragged.
**/
TrackTree::pre_order_iterator get_dragging_track_iter() const;
void drag_to_point(const Gdk::Point &point);
/**
* Drags the dragging branch to a new mouse point.
* @param mouse_point The mouse point to drag the dragging track to.
* This point is in pixels relative to the top left of the header
* container.
* @remarks drag_to_point may only be called after
* begin_dragging_track and before end_dragging_point have been
* called.
**/
void drag_to_point(const Gdk::Point &mouse_point);
/**
* Returns the total height in pixels of the layout tree.
@ -146,6 +170,9 @@ public:
**/
int get_total_height() const;
/**
* Returns true if the layout is currently animating.
**/
bool is_animating() const;
/**
@ -157,22 +184,65 @@ public:
**/
TrackTree::pre_order_iterator iterator_from_track(
boost::shared_ptr<model::Track> model_track);
/**
* A function that recursively calculates the visible height of a
* branch taking into account branches that are expanded or collapsed.
* @param parent_iterator The parent of the branch to measure. This
* node and all the child nodes will be included in the measurement.
* @return Returns the height of the branch in pixels.
**/
int measure_branch_height(TrackTree::iterator_base parent_iterator);
protected:
/**
* An enum to specify the relationship between a tree node, and
* another node which is going to be inserted adjacent.
**/
enum TreeRelation
{
/**
* No relation
**/
None,
/**
* The node will be inserted immediately before this one.
**/
Before,
/**
* The node will be inserted immediately after this one.
**/
After,
/**
* The node will be inserted as the first child of this one.
**/
FirstChild,
/**
* The node will be inserted as the last child of this one.
**/
LastChild
};
struct Drop
/**
* A structure used to specify where a track will be dropped when
* dragging ends.
**/
struct DropPoint
{
/**
* Specifies the target node onto which the dragging track will be
* dropped.
**/
TrackTree::pre_order_iterator target;
/**
* The where to drop the dragging track in relation to target.
**/
TreeRelation relation;
};
@ -237,14 +307,34 @@ protected:
* The animation timer tick callback.
**/
bool on_animation_tick();
TimelineLayoutHelper::Drop
/**
* Attempts to find a drop point on the target node at point.
* @param[in] target The iterator of the target node on which to make
* the attempt.
* @param[in] point The point on which do to the test.
* @remarks This function hit-tests a header looking to see if the
* point is hovering over it, and if it is, it works out what part of
* the header, and therefore what drop location the user us gesturally
* pointing to.
**/
TimelineLayoutHelper::DropPoint
attempt_drop(TrackTree::pre_order_iterator target,
const Gdk::Point &point);
void apply_drop_to_layout_tree(const Drop &drop);
/**
* Drops the dragging track to a new location in the layout tree as
* specified by drop.
* @param[in] drop The point in the tree to drop onto.
**/
void apply_drop_to_layout_tree(const DropPoint &drop);
void apply_drop_to_model_tree(const Drop &drop);
/**
* Drops the dragging track to a new location in the model tree as
* specified by drop.
* @param[in] drop The point in the tree to drop onto.
**/
void apply_drop_to_model_tree(const DropPoint &drop);
protected:
/**
@ -274,15 +364,39 @@ protected:
**/
int totalHeight;
/**
* The iterator of the layoutTree node that is presently being
* dragged.
* @remarks draggingTrackIter.node is set to NULL when no drag is
* taking place.
**/
TrackTree::pre_order_iterator draggingTrackIter;
/**
* The offset of the mouse relative to the top-left corner of the
* dragging track.
**/
Gdk::Point dragStartOffset;
/**
* The coordinates of the dragging mouse in pixels, measured from the
* top left of the whole layout.
* @remarks This value is changed by begin_dragging_track and
* drag_to_point
**/
Gdk::Point dragPoint;
/**
* The total height of the dragging branch in pixels.
* @remarks This value is updated by begin_dragging_track
**/
int dragBranchHeight;
Drop draggingDrop;
/**
* The tree point the the user is currently hovering on.
* @remarks This value is updated by drag_to_point.
**/
DropPoint dropPoint;
/**
* The connection to the animation timer.