diff --git a/src/gui/gtk-lumiera.hpp b/src/gui/gtk-lumiera.hpp index 765a628b7..e0bb55310 100644 --- a/src/gui/gtk-lumiera.hpp +++ b/src/gui/gtk-lumiera.hpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include "lib/util.hpp" diff --git a/src/gui/widgets/timeline-widget.cpp b/src/gui/widgets/timeline-widget.cpp index 155b021df..64421bf16 100644 --- a/src/gui/widgets/timeline-widget.cpp +++ b/src/gui/widgets/timeline-widget.cpp @@ -50,12 +50,12 @@ TimelineWidget::TimelineWidget( playbackPeriodStart(0), playbackPeriodEnd(0), playbackPoint(GAVL_TIME_UNDEFINED), - totalHeight(0), horizontalAdjustment(0, 0, 0), verticalAdjustment(0, 0, 0), horizontalScroll(horizontalAdjustment), verticalScroll(verticalAdjustment) { + g_message("TimelineWidget::TimelineWidget"); REQUIRE(sequence); body = new TimelineBody(this); @@ -64,6 +64,8 @@ TimelineWidget::TimelineWidget( ENSURE(headerContainer != NULL); ruler = new TimelineRuler(this); ENSURE(ruler != NULL); + + g_message("Widgets Constructed"); horizontalAdjustment.signal_value_changed().connect( sigc::mem_fun( this, &TimelineWidget::on_scroll) ); @@ -77,6 +79,7 @@ TimelineWidget::TimelineWidget( viewWindow.set_time_scale(GAVL_TIME_SCALE / 200); set_selection(2000000, 4000000); + g_message("update_tracks"); update_tracks(); attach(*body, 1, 2, 1, 2, FILL|EXPAND, FILL|EXPAND); @@ -90,6 +93,7 @@ TimelineWidget::TimelineWidget( // Receive notifications of changes to the tracks sequence->get_child_track_list().signal_changed().connect( sigc::mem_fun( this, &TimelineWidget::on_track_list_changed ) ); + g_message("Constructor }"); } TimelineWidget::~TimelineWidget() @@ -296,6 +300,10 @@ TimelineWidget::update_tracks() // Create timeline tracks from all the model tracks create_timeline_tracks(); + // Update the layout helper + layoutHelper.clone_tree_from_sequence(); + layoutHelper.update_layout(); + // Update the header container REQUIRE(headerContainer != NULL); headerContainer->show_all_children(); @@ -303,15 +311,6 @@ TimelineWidget::update_tracks() // Update the body body->queue_draw(); - - // Recalculate the total height of the timeline scrolled area - totalHeight = 0; - BOOST_FOREACH(shared_ptr track, - sequence->get_child_tracks()) - { - REQUIRE(track); - totalHeight += measure_branch_height(track); - } } void @@ -339,9 +338,6 @@ TimelineWidget::create_timeline_tracks_from_branch( // We will need to create one trackMap[model_track] = create_timeline_track_from_model_track(model_track); - - // Hook up - } // Recurse to child tracks @@ -419,6 +415,9 @@ TimelineWidget::lookup_timeline_track( shared_ptr model_track) const { REQUIRE(sequence); + REQUIRE(model_track); + REQUIRE(model_track != sequence); // The sequence isn't really a track + std::map, shared_ptr >:: const_iterator iterator = trackMap.find(model_track); if(iterator == trackMap.end()) @@ -434,31 +433,6 @@ TimelineWidget::lookup_timeline_track( return iterator->second; } -boost::shared_ptr -TimelineWidget::lookup_model_track( - const timeline::Track *timeline_track) const -{ - REQUIRE(sequence); - - std::pair, shared_ptr > - pair; - BOOST_FOREACH( pair, trackMap ) - { - if(pair.second.get() == timeline_track) - { - ENSURE(pair.first); - return pair.first; - } - } - - // 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(); -} - void TimelineWidget::update_scroll() { @@ -479,7 +453,8 @@ TimelineWidget::update_scroll() // Calculate the vertical length that can be scrolled: // the total height of all the tracks minus one screenful - int y_scroll_length = totalHeight - body_allocation.get_height(); + 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 @@ -501,29 +476,6 @@ TimelineWidget::update_scroll() } -int -TimelineWidget::measure_branch_height( - shared_ptr model_track) -{ - REQUIRE(model_track); - - const shared_ptr timeline_track = - lookup_timeline_track(model_track); - ENSURE(timeline_track); - - int height = timeline_track->get_height() + TrackPadding; - - // Recurse through all the children - BOOST_FOREACH( shared_ptr child, - model_track->get_child_tracks() ) - { - REQUIRE(child); - height += measure_branch_height(child); - } - - return height; -} - int TimelineWidget::get_y_scroll_offset() const { diff --git a/src/gui/widgets/timeline-widget.hpp b/src/gui/widgets/timeline-widget.hpp index 9f368e147..b6ea075a4 100644 --- a/src/gui/widgets/timeline-widget.hpp +++ b/src/gui/widgets/timeline-widget.hpp @@ -217,25 +217,11 @@ private: **/ boost::shared_ptr lookup_timeline_track( boost::shared_ptr model_track) const; - - /** - * Looks up a model track in trackMap that corresponds to a - * given timeline track. - * @param timeline_track The timeline UI track to look up. - * @returns The model track found, or an empty shared_ptr if - * timeline_track has no corresponding timeline UI track (this is an - * error condition). - **/ - boost::shared_ptr lookup_model_track( - const timeline::Track *timeline_track) const; // ----- Layout Functions ----- // void update_scroll(); - int measure_branch_height( - boost::shared_ptr model_track); - int get_y_scroll_offset() const; // ----- Event Handlers -----// @@ -293,8 +279,6 @@ protected: boost::shared_ptr hoveringTrack; - int totalHeight; - // Child Widgets timeline::TimelineHeaderContainer *headerContainer; timeline::TimelineBody *body; diff --git a/src/gui/widgets/timeline/timeline-header-container.cpp b/src/gui/widgets/timeline/timeline-header-container.cpp index be1307c1c..a1bd1b075 100644 --- a/src/gui/widgets/timeline/timeline-header-container.cpp +++ b/src/gui/widgets/timeline/timeline-header-container.cpp @@ -135,6 +135,7 @@ TimelineHeaderContainer::on_unrealize() bool TimelineHeaderContainer::on_button_press_event ( GdkEventButton* event) { + REQUIRE(timelineWidget != NULL); REQUIRE(event != NULL); switch(event->button) @@ -152,8 +153,9 @@ bool TimelineHeaderContainer::on_button_press_event ( case 3: // Right Click { // Popup the context menu - shared_ptr header = header_from_point( - Gdk::Point(event->x, event->y)); + shared_ptr header( + timelineWidget->layoutHelper.header_from_point( + Gdk::Point(event->x, event->y))); // Are we hovering on a header? if(header) @@ -183,6 +185,8 @@ bool TimelineHeaderContainer::on_button_release_event ( // Yes? The toggle the expanding clickedExpander->set_expanded(!clickedExpander->get_expanded()); clickedExpander.reset(); + + timelineWidget->layoutHelper.update_layout(); layout_headers(); } @@ -304,88 +308,58 @@ TimelineHeaderContainer::on_hovering_track_changed( void TimelineHeaderContainer::layout_headers() -{ +{ + REQUIRE(timelineWidget != NULL); + // We can't layout before the widget has been set up if(!gdkWindow) return; - - // Make sure the style are loaded - read_styles(); - - // Clear previously cached layout - headerBoxes.clear(); - - // Start at minus-the-scroll offset - int offset = -timelineWidget->get_y_scroll_offset(); - - const Allocation container_allocation = get_allocation(); - const int header_width = container_allocation.get_width(); - BOOST_FOREACH( shared_ptr model_track, get_tracks() ) - layout_headers_recursive( - model_track, offset, header_width, 0, true); + TimelineLayoutHelper &layoutHelper = + timelineWidget->layoutHelper; + const TimelineLayoutHelper::TrackTree &layoutTree = + layoutHelper.get_layout_tree(); + + TimelineLayoutHelper::TrackTree::pre_order_iterator iterator; + for(iterator = ++layoutTree.begin(); // ++ so that we skip the sequence root + iterator != layoutTree.end(); + iterator++) + { + const shared_ptr timeline_track = + lookup_timeline_track(*iterator); + + Widget &widget = timeline_track->get_header_widget(); + + optional header_rect = + layoutHelper.get_track_header_rect(timeline_track); + + if(header_rect) + { + REQUIRE(header_rect->get_width() >= 0); + REQUIRE(header_rect->get_height() >= 0); + + // Calculate the allocation of the header widget + Allocation header_allocation( + header_rect->get_x() + margin + expand_button_size, // x + header_rect->get_y() + margin, // y + max( header_rect->get_width() - expand_button_size - + margin * 2, 0 ), // width + header_rect->get_height() - margin * 2); // height + + // Apply the allocation to the header + widget.size_allocate (header_allocation); + if(!widget.is_visible()) + widget.show(); + } + else // No header rect, so the track must be hidden + if(widget.is_visible()) + widget.hide(); + } // Repaint the background of our parenting queue_draw (); } -void -TimelineHeaderContainer::layout_headers_recursive( - shared_ptr model_track, int &offset, - const int header_width, const int depth, bool parent_expanded) -{ - REQUIRE(depth >= 0); - REQUIRE(model_track != NULL); - - shared_ptr timeline_track = - lookup_timeline_track(model_track); - - const int indent = depth * 10; - Widget &widget = timeline_track->get_header_widget(); - - if(parent_expanded) - { - const int track_height = timeline_track->get_height(); - - // Calculate the box of the header - Gdk::Rectangle header_box( - indent, // x - offset, // y - max( header_width - indent, 0 ), // width - track_height); // height - REQUIRE(header_box.get_height() >= 0); - - // Cache the bounding box - headerBoxes[timeline_track] = header_box; - - // Calculate the allocation of the header widget - Allocation header_allocation( - header_box.get_x() + margin + expand_button_size, // x - header_box.get_y() + margin, // y - max( header_box.get_width() - expand_button_size - - margin * 2, 0 ), // width - header_box.get_height() - margin * 2); // height - - // Apply the allocation to the header - widget.size_allocate (header_allocation); - if(!widget.is_visible()) - widget.show(); - - // Offset for the next header - offset += track_height + TimelineWidget::TrackPadding; - } - else - if(widget.is_visible()) - widget.hide(); - - // Recurse through all the children - BOOST_FOREACH( boost::shared_ptr child, - model_track->get_child_tracks() ) - layout_headers_recursive( - child, offset, header_width, depth + 1, - timeline_track->get_expanded() && parent_expanded); -} - void TimelineHeaderContainer::set_parent_recursive( boost::shared_ptr model_track) @@ -442,6 +416,7 @@ TimelineHeaderContainer::draw_header_decoration( shared_ptr model_track, const Gdk::Rectangle &clip_rect) { + REQUIRE(timelineWidget != NULL); REQUIRE(model_track != NULL); REQUIRE(clip_rect.get_width() > 0); REQUIRE(clip_rect.get_height() > 0); @@ -452,10 +427,11 @@ TimelineHeaderContainer::draw_header_decoration( shared_ptr timeline_track = lookup_timeline_track(model_track); - // Get the cached header box - weak_ptr ptr(timeline_track); - REQUIRE(contains(headerBoxes, ptr)); - const Gdk::Rectangle &box = headerBoxes[timeline_track]; + // Get the header box + const optional &optional_box = + timelineWidget->layoutHelper.get_track_header_rect(timeline_track); + REQUIRE(optional_box); + const Gdk::Rectangle box = *optional_box; // Paint the box, if it will be visible if(box.get_x() < clip_rect.get_width() && @@ -498,58 +474,55 @@ TimelineHeaderContainer::draw_header_decoration( } } -boost::shared_ptr -TimelineHeaderContainer::header_from_point(const Gdk::Point &point) -{ - std::pair, Gdk::Rectangle> pair; - BOOST_FOREACH( pair, headerBoxes ) - { - // Hit test the rectangle - const Gdk::Rectangle &rect = pair.second; - - if(point.get_x() >= rect.get_x() && - point.get_x() < rect.get_x() + rect.get_width() && - point.get_y() >= rect.get_y() && - point.get_y() < rect.get_y() + rect.get_height()) - return pair.first; - } - - return shared_ptr(); -} - shared_ptr TimelineHeaderContainer::expander_button_from_point( const Gdk::Point &point) -{ - std::pair, Gdk::Rectangle> pair; - BOOST_FOREACH( pair, headerBoxes ) +{ + const TimelineLayoutHelper::TrackTree &layoutTree = + timelineWidget->layoutHelper.get_layout_tree(); + + TimelineLayoutHelper::TrackTree::pre_order_iterator iterator; + for(iterator = ++layoutTree.begin(); // ++ so we skip the sequence root + iterator != layoutTree.end(); + iterator++) { - // Hit test the rectangle - const Gdk::Rectangle rect = - get_expander_button_rectangle(pair.first); + const shared_ptr timeline_track = + lookup_timeline_track(*iterator); - if(point.get_x() >= rect.get_x() && - point.get_x() < rect.get_x() + rect.get_width() && - point.get_y() >= rect.get_y() && - point.get_y() < rect.get_y() + rect.get_height()) - return pair.first; + // Hit test the rectangle + const optional rect = + get_expander_button_rectangle(timeline_track); + + if(rect) + { + if(point.get_x() >= rect->get_x() && + point.get_x() < rect->get_x() + rect->get_width() && + point.get_y() >= rect->get_y() && + point.get_y() < rect->get_y() + rect->get_height()) + return timeline_track; + } } return shared_ptr(); } -const Gdk::Rectangle +const optional TimelineHeaderContainer::get_expander_button_rectangle( shared_ptr track) { + REQUIRE(timelineWidget != NULL); REQUIRE(track != NULL); - weak_ptr ptr(track); - REQUIRE(contains(headerBoxes, ptr)); - const Gdk::Rectangle &box = headerBoxes[track]; - return Gdk::Rectangle( - margin + box.get_x(), margin + box.get_y(), - expand_button_size, box.get_height() - margin * 2); + optional box = + timelineWidget->layoutHelper.get_track_header_rect(track); + if(box) + { + return optional(Gdk::Rectangle( + margin + box->get_x(), margin + box->get_y(), + expand_button_size, box->get_height() - margin * 2)); + } + + return optional(); } shared_ptr diff --git a/src/gui/widgets/timeline/timeline-header-container.hpp b/src/gui/widgets/timeline/timeline-header-container.hpp index 86d61182e..d8e29d2a2 100644 --- a/src/gui/widgets/timeline/timeline-header-container.hpp +++ b/src/gui/widgets/timeline/timeline-header-container.hpp @@ -149,19 +149,7 @@ private: * stacking etc. */ void layout_headers(); - - /** - * Recursively lays out all the controls in the header widget. - * @param track The parent track object which will be recursed into. - * @param offset A shared value used to accumulate the y-offset of - * header widgets. - * @param header_width The width of this widget in pixels. - * @param depth The depth within the tree of track. - **/ - void layout_headers_recursive(boost::shared_ptr track, - int &offset, const int header_width, const int depth, - bool parent_expanded); - + /** * Recursively sets all the track header widgets to be child widgets * of this widget. @@ -199,9 +187,6 @@ private: void draw_header_decoration( boost::shared_ptr model_track, const Gdk::Rectangle &clip_rect); - - boost::shared_ptr header_from_point( - const Gdk::Point &point); /** * Given a point, expander_button_from_point finds the track of the @@ -219,7 +204,7 @@ private: * @param track The track to get the expander button rectangle of. * @return Returns the rectangle of the expander button of track. **/ - const Gdk::Rectangle get_expander_button_rectangle( + const boost::optional get_expander_button_rectangle( boost::shared_ptr track); /** @@ -278,16 +263,7 @@ private: * click is not processed by track headers. **/ Gtk::Menu contextMenu; - - /** - * A map of tracks to the rectangles of their headers. - * @remarks This map is used as a cache, so that the rectangles don't - * need to be perpetually recalculated. This cache is regenerated by - * the layout_headers method. - **/ - std::map, Gdk::Rectangle> - headerBoxes; - + //----- User Interaction State -----// boost::shared_ptr hoveringExpander; diff --git a/src/gui/widgets/timeline/timeline-layout-helper.cpp b/src/gui/widgets/timeline/timeline-layout-helper.cpp index 11d97d7ce..f7c46dafd 100644 --- a/src/gui/widgets/timeline/timeline-layout-helper.cpp +++ b/src/gui/widgets/timeline/timeline-layout-helper.cpp @@ -31,13 +31,15 @@ using namespace Gtk; using namespace std; using namespace boost; using namespace lumiera; +using namespace util; namespace gui { namespace widgets { namespace timeline { -TimelineLayoutHelper::TimelineLayoutHelper(TimelineWidget &owner) - : timelineWidget(owner) +TimelineLayoutHelper::TimelineLayoutHelper(TimelineWidget &owner) : + timelineWidget(owner), + totalHeight(0) { } @@ -48,12 +50,16 @@ TimelineLayoutHelper::clone_tree_from_sequence() REQUIRE(sequence); layoutTree.clear(); - TrackTree::iterator_base iterator = - layoutTree.set_head(sequence); - + TrackTree::iterator_base iterator = layoutTree.set_head(sequence); add_branch(iterator, sequence); } +TimelineLayoutHelper::TrackTree& +TimelineLayoutHelper::get_layout_tree() +{ + return layoutTree; +} + void TimelineLayoutHelper::add_branch( TrackTree::iterator_base parent_iterator, @@ -68,6 +74,45 @@ TimelineLayoutHelper::add_branch( } } +optional +TimelineLayoutHelper::get_track_header_rect( + boost::weak_ptr track) +{ + if(contains(headerBoxes, track)) + { + Gdk::Rectangle rect(headerBoxes[track]); + rect.set_y(rect.get_y() - timelineWidget.get_y_scroll_offset()); + return optional(rect); + } + return optional(); +} + +weak_ptr +TimelineLayoutHelper::header_from_point(const Gdk::Point &point) +{ + std::pair, Gdk::Rectangle> pair; + BOOST_FOREACH( pair, headerBoxes ) + { + // Hit test the rectangle + const Gdk::Rectangle &rect = pair.second; + + if(point.get_x() >= rect.get_x() && + point.get_x() < rect.get_x() + rect.get_width() && + point.get_y() >= rect.get_y() && + point.get_y() < rect.get_y() + rect.get_height()) + return pair.first; + } + + return shared_ptr(); +} + +int +TimelineLayoutHelper::get_total_height() const +{ + ENSURE(totalHeight >= 0); + return totalHeight; +} + void TimelineLayoutHelper::update_layout() { @@ -81,11 +126,13 @@ TimelineLayoutHelper::update_layout() int offset = 0;//-timelineWidget->get_y_scroll_offset(); //const Allocation container_allocation = get_allocation(); - const int header_width = 100;//container_allocation.get_width(); + const int header_width = 150;//container_allocation.get_width(); const int indent_width = 10; - + layout_headers_recursive(layoutTree.begin(), offset, header_width, indent_width, 0, true); + + totalHeight = offset; } void diff --git a/src/gui/widgets/timeline/timeline-layout-helper.hpp b/src/gui/widgets/timeline/timeline-layout-helper.hpp index d9446d707..61ce80e52 100644 --- a/src/gui/widgets/timeline/timeline-layout-helper.hpp +++ b/src/gui/widgets/timeline/timeline-layout-helper.hpp @@ -56,17 +56,30 @@ public: public: TimelineLayoutHelper(TimelineWidget &owner); -protected: - + void clone_tree_from_sequence(); + TrackTree& get_layout_tree(); + + /** + * Recalculates the track layout from layoutTree. + **/ + void update_layout(); + + boost::optional get_track_header_rect( + boost::weak_ptr track); + + boost::weak_ptr header_from_point( + const Gdk::Point &point); + + int get_total_height() const; + +protected: + void add_branch(TrackTree::iterator_base parent_iterator, boost::shared_ptr parent); - /** - * Recaculates the track layout from layoutTree. - **/ - void update_layout(); + /** * Recursively lays out all the controls in the header widget. @@ -113,6 +126,8 @@ protected: **/ std::map, Gdk::Rectangle> headerBoxes; + + int totalHeight; }; } // namespace timeline diff --git a/src/gui/widgets/timeline/timeline-track.cpp b/src/gui/widgets/timeline/timeline-track.cpp index e7d39e22a..d6605941c 100644 --- a/src/gui/widgets/timeline/timeline-track.cpp +++ b/src/gui/widgets/timeline/timeline-track.cpp @@ -90,6 +90,12 @@ Track::get_header_widget() return headerWidget; } +shared_ptr +Track::get_model_track() const +{ + return model_track; +} + int Track::get_height() const { diff --git a/src/gui/widgets/timeline/timeline-track.hpp b/src/gui/widgets/timeline/timeline-track.hpp index 1840b207d..652543afc 100644 --- a/src/gui/widgets/timeline/timeline-track.hpp +++ b/src/gui/widgets/timeline/timeline-track.hpp @@ -45,6 +45,8 @@ public: Gtk::Widget& get_header_widget(); + boost::shared_ptr get_model_track() const; + int get_height() const; bool get_expanded() const;