From 9879aef3ddb3dc903a3de272eda182a4e5c1a9cc Mon Sep 17 00:00:00 2001 From: Joel Holdsworth Date: Sat, 24 Jan 2009 10:58:54 +0000 Subject: [PATCH] Tidied and documented TimelineLayoutHelper --- .../timeline/timeline-layout-helper.cpp | 357 +++++++++--------- .../timeline/timeline-layout-helper.hpp | 134 ++++++- 2 files changed, 304 insertions(+), 187 deletions(-) diff --git a/src/gui/widgets/timeline/timeline-layout-helper.cpp b/src/gui/widgets/timeline/timeline-layout-helper.cpp index 4df9b0c98..105e6fcd1 100644 --- a/src/gui/widgets/timeline/timeline-layout-helper.cpp +++ b/src/gui/widgets/timeline/timeline-layout-helper.cpp @@ -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(*target); - REQUIRE(model_track); - const weak_ptr 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 &dragging_track = *draggingTrackIter; - REQUIRE(dragging_track); - REQUIRE(dragging_track != timelineWidget.sequence); - - shared_ptr &target_track = *drop.target; - REQUIRE(target_track); - REQUIRE(target_track != timelineWidget.sequence); - - // Detach the track from the old parent - shared_ptr old_parent = - dynamic_pointer_cast( - 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 new_parent = - dynamic_pointer_cast( - 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 > &dest = - new_parent->get_child_track_list(); - list< shared_ptr >::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 new_parent = - dynamic_pointer_cast( - 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(*target); + REQUIRE(model_track); + const weak_ptr 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 &dragging_track = *draggingTrackIter; + REQUIRE(dragging_track); + REQUIRE(dragging_track != timelineWidget.sequence); + + shared_ptr &target_track = *drop.target; + REQUIRE(target_track); + REQUIRE(target_track != timelineWidget.sequence); + + // Detach the track from the old parent + shared_ptr old_parent = + dynamic_pointer_cast( + 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 new_parent = + dynamic_pointer_cast( + 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 > &dest = + new_parent->get_child_track_list(); + list< shared_ptr >::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 new_parent = + dynamic_pointer_cast( + 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 diff --git a/src/gui/widgets/timeline/timeline-layout-helper.hpp b/src/gui/widgets/timeline/timeline-layout-helper.hpp index 18463af49..22da655d9 100644 --- a/src/gui/widgets/timeline/timeline-layout-helper.hpp +++ b/src/gui/widgets/timeline/timeline-layout-helper.hpp @@ -127,16 +127,40 @@ public: **/ boost::shared_ptr 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 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); - + + /** + * 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.