Initial drop code added

This commit is contained in:
Joel Holdsworth 2009-01-21 23:48:56 +00:00
parent f18c039074
commit 69253909e3
8 changed files with 228 additions and 59 deletions

View file

@ -72,6 +72,25 @@ Track::print_branch()
return print_branch_recursive(0);
}
shared_ptr<Track>
Track::find_parent(shared_ptr<Track> root, shared_ptr<Track> child)
{
REQUIRE(root != NULL);
REQUIRE(child != NULL);
const list< shared_ptr<Track> > children = root->get_child_tracks();
BOOST_FOREACH(shared_ptr<Track> track, children)
{
if(track == child)
return root;
shared_ptr<Track> result = find_parent(track, child);
if(result)
return result;
}
return shared_ptr<Track>();
}
string
Track::print_branch_recursive(const unsigned int indentation)
{

View file

@ -53,7 +53,9 @@ public:
virtual std::string print_track() = 0;
static boost::shared_ptr<Track>
find_parent(boost::shared_ptr<Track> root,
boost::shared_ptr<Track> child);
protected:
std::string print_branch_recursive(const unsigned int indentation);

View file

@ -54,7 +54,8 @@ TimelineWidget::TimelineWidget(
horizontalAdjustment(0, 0, 0),
verticalAdjustment(0, 0, 0),
horizontalScroll(horizontalAdjustment),
verticalScroll(verticalAdjustment)
verticalScroll(verticalAdjustment),
update_tracks_frozen(false)
{
REQUIRE(sequence);
@ -288,6 +289,9 @@ TimelineWidget::on_add_track_command()
void
TimelineWidget::update_tracks()
{
if(update_tracks_frozen)
return;
REQUIRE(sequence);
// Remove any tracks which are no longer present in the model
@ -301,6 +305,18 @@ TimelineWidget::update_tracks()
layoutHelper.update_layout();
}
void
TimelineWidget::freeze_update_tracks()
{
update_tracks_frozen = true;
}
void
TimelineWidget::thaw_update_tracks()
{
update_tracks_frozen = false;
}
void
TimelineWidget::create_timeline_tracks()
{

View file

@ -171,6 +171,10 @@ private:
**/
void update_tracks();
void freeze_update_tracks();
void thaw_update_tracks();
/**
* Ensures timeline UI tracks have been created for every model track
* present in sequence.
@ -297,6 +301,8 @@ protected:
sigc::signal<void> playbackPeriodDragReleasedSignal;
sigc::signal<void, boost::shared_ptr<timeline::Track> >
hoveringTrackChangedSignal;
bool update_tracks_frozen;
/* ===== Constants ===== */
public:

View file

@ -411,13 +411,13 @@ TimelineHeaderContainer::begin_drag()
}
void
TimelineHeaderContainer::end_drag()
TimelineHeaderContainer::end_drag(bool apply)
{
TimelineLayoutHelper &layout = timelineWidget.layoutHelper;
// Has the user been dragging?
if(layout.get_dragging_track())
layout.end_dragging_track();
layout.end_dragging_track(apply);
// Reset the arrow as a cursor
REQUIRE(gdkWindow);

View file

@ -185,7 +185,7 @@ private:
void begin_drag();
void end_drag();
void end_drag(bool apply = true);
/**
* Recusively raises all the header widget windows in a branch to the

View file

@ -150,13 +150,18 @@ TimelineLayoutHelper::begin_dragging_track(
draggingTrack->get_model_track();
draggingTrackIter = iterator_from_track(model_track);
dragBranchHeight = measure_branch_height(draggingTrackIter);
draggingDrop.relation = None;
return draggingTrack;
}
void
TimelineLayoutHelper::end_dragging_track()
TimelineLayoutHelper::end_dragging_track(bool apply)
{
if(apply)
apply_drop_to_model_tree(draggingDrop);
draggingTrack.reset();
clone_tree_from_sequence();
update_layout();
@ -177,6 +182,8 @@ TimelineLayoutHelper::get_dragging_track_iter() const
void
TimelineLayoutHelper::drag_to_point(const Gdk::Point &point)
{
optional<Drop> drop;
// Apply the scroll offset
const Gdk::Point last_point(dragPoint);
dragPoint = Gdk::Point(point.get_x(),
@ -221,34 +228,42 @@ TimelineLayoutHelper::drag_to_point(const Gdk::Point &point)
const int full_width = rect.get_x() + rect.get_width();
const int x_mid = rect.get_x() + rect.get_width() / 2;
// Do hit test
if(attempt_drop_upper(iterator, test_point, y,
full_width, half_height))
break;
// Do hit test
drop = attempt_drop_upper(iterator, test_point, y,
full_width, half_height);
if(drop) break;
if(attempt_drop_lower(iterator, test_point,
x_mid, full_width, y_mid, half_height))
break;
drop = attempt_drop_lower(iterator, test_point,
x_mid, full_width, y_mid, half_height);
if(drop) break;
}
// Did we get a drop point?
if(drop)
{
apply_drop_to_layout_tree(*drop);
draggingDrop = *drop;
}
update_layout();
}
bool
optional<TimelineLayoutHelper::Drop>
TimelineLayoutHelper::attempt_drop_upper(
TrackTree::pre_order_iterator target, const Gdk::Point &point,
const int y, const int full_width, const int half_height)
{
if(pt_in_rect(point, Gdk::Rectangle(0, y, full_width, half_height)))
{
draggingTrackIter = layoutTree.move_before(
target, draggingTrackIter);
return true;
Drop drop;
drop.target = target;
drop.relation = Before;
return drop;
}
return false;
return optional<Drop>();
}
bool
optional<TimelineLayoutHelper::Drop>
TimelineLayoutHelper::attempt_drop_lower(
TrackTree::pre_order_iterator target, const Gdk::Point &point,
const int x_mid, const int full_width, const int y_mid,
@ -259,7 +274,9 @@ TimelineLayoutHelper::attempt_drop_lower(
if(!pt_in_rect(point, Gdk::Rectangle(0, y_mid,
full_width, half_height)))
return false;
return optional<Drop>();
Drop drop = {target, None};
if(model_track->can_host_children())
{
@ -267,47 +284,135 @@ TimelineLayoutHelper::attempt_drop_lower(
{
// Is our track being dragged after this header?
if(dragPoint.get_x() < x_mid)
{
draggingTrackIter = layoutTree.move_after(
target, draggingTrackIter);
}
drop.relation = After;
else
{
if(draggingTrackIter.node->parent != target.node)
{
// Insert a place-holder in the tree
const TrackTree::pre_order_iterator placeholder =
layoutTree.prepend_child(target);
// Replace it with the relocated branch
draggingTrackIter = layoutTree.move_ontop(
placeholder, draggingTrackIter);
}
}
drop.relation = FirstChild;
}
else
{
if(draggingTrackIter.node->parent != target.node)
{
// Insert a place-holder in the tree
const TrackTree::pre_order_iterator placeholder =
layoutTree.append_child(target);
// Replace it with the relocated branch
draggingTrackIter = layoutTree.move_ontop(
placeholder, draggingTrackIter);
}
}
drop.relation = LastChild;
}
else
{
// When this track cannot be a parent, the dragging track is
// simply dropped after
draggingTrackIter = layoutTree.move_after(
target, draggingTrackIter);
// simply dropped after
drop.relation = After;
}
return true;
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

View file

@ -130,7 +130,7 @@ public:
boost::shared_ptr<timeline::Track>
begin_dragging_track(const Gdk::Point &mouse_point);
void end_dragging_track();
void end_dragging_track(bool apply);
boost::shared_ptr<timeline::Track> get_dragging_track() const;
@ -159,7 +159,23 @@ public:
boost::shared_ptr<model::Track> model_track);
int measure_branch_height(TrackTree::iterator_base parent_iterator);
protected:
enum TreeRelation
{
None,
Before,
After,
FirstChild,
LastChild
};
struct Drop
{
TrackTree::pre_order_iterator target;
TreeRelation relation;
};
protected:
/**
@ -221,16 +237,19 @@ protected:
* The animation timer tick callback.
**/
bool on_animation_tick();
bool
boost::optional<TimelineLayoutHelper::Drop>
attempt_drop_upper(TrackTree::pre_order_iterator target, const Gdk::Point &point, const int y, const int full_width, const int half_height);
bool
boost::optional<TimelineLayoutHelper::Drop>
attempt_drop_lower(TrackTree::pre_order_iterator target, const Gdk::Point &point, const int x_mid, const int full_width, const int y_mid, const int half_height);
protected:
void apply_drop_to_layout_tree(const Drop &drop);
void apply_drop_to_model_tree(const Drop &drop);
protected:
/**
* The owner timeline widget as provided to the constructor.
**/
@ -268,6 +287,8 @@ protected:
int dragBranchHeight;
Drop draggingDrop;
/**
* The connection to the animation timer.
* @see begin_animation()