use a different approach for indicating expose rects (from robin gareus). #ifdef...
[ardour.git] / gtk2_ardour / editor_canvas.cc
index 0c08af5c1143165eac86bea27cfddddfe51069aa..8ad2abdfad3ca77f82c10887b189d96d4fb28472 100644 (file)
@@ -66,12 +66,24 @@ Editor::initialize_canvas ()
 {
        _track_canvas_viewport = new ArdourCanvas::GtkCanvasViewport (horizontal_adjustment, vertical_adjustment);
        _track_canvas = _track_canvas_viewport->canvas ();
-       _track_canvas->set_global_scroll (false);
 
-       hv_scroll_group = new ArdourCanvas::ScrollGroup (_track_canvas->root(), 
-                                                        ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically|
-                                                                                                      ArdourCanvas::ScrollGroup::ScrollsHorizontally));
+       ArdourCanvas::ScrollGroup* hsg; 
+       ArdourCanvas::ScrollGroup* hg;
+       ArdourCanvas::ScrollGroup* vg;
+
+       hv_scroll_group = hsg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), 
+                                                              ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically|
+                                                                                                            ArdourCanvas::ScrollGroup::ScrollsHorizontally));
        CANVAS_DEBUG_NAME (hv_scroll_group, "canvas hv scroll");
+       _track_canvas->add_scroller (*hsg);
+
+       v_scroll_group = vg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsVertically);
+       CANVAS_DEBUG_NAME (v_scroll_group, "canvas v scroll");
+       _track_canvas->add_scroller (*vg);
+
+       h_scroll_group = hg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
+       CANVAS_DEBUG_NAME (h_scroll_group, "canvas h scroll");
+       _track_canvas->add_scroller (*hg);
 
        _verbose_cursor = new VerboseCursor (this);
 
@@ -104,48 +116,27 @@ Editor::initialize_canvas ()
        CANVAS_DEBUG_NAME (time_line_group, "time line group");
 
        _trackview_group = new ArdourCanvas::Group (hv_scroll_group);
-       //_trackview_group->set_scroll_sensitivity (ArdourCanvas::Group::ScrollSensitivity (ArdourCanvas::Group::ScrollsVertically|ArdourCanvas::Group::ScrollsHorizontally));  
        CANVAS_DEBUG_NAME (_trackview_group, "Canvas TrackViews");
        
-       _region_motion_group = new ArdourCanvas::Group (_trackview_group);
-       CANVAS_DEBUG_NAME (_region_motion_group, "Canvas Region Motion");
-
-       /* TIME BAR CANVAS */
-       
-       _time_bars_canvas_viewport = new ArdourCanvas::GtkCanvasViewport (horizontal_adjustment, unused_adjustment);
-       _time_bars_canvas = _time_bars_canvas_viewport->canvas ();
-
-       meter_bar_group = new ArdourCanvas::Group (_time_bars_canvas->root ());
-       meter_bar = new ArdourCanvas::Rectangle (meter_bar_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
-       CANVAS_DEBUG_NAME (meter_bar, "meter Bar");
-       meter_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
-
-       tempo_bar_group = new ArdourCanvas::Group (_time_bars_canvas->root ());
-       tempo_bar = new ArdourCanvas::Rectangle (tempo_bar_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
-       CANVAS_DEBUG_NAME (tempo_bar, "Tempo  Bar");
-       tempo_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
-
-       range_marker_bar_group = new ArdourCanvas::Group (_time_bars_canvas->root ());
-       range_marker_bar = new ArdourCanvas::Rectangle (range_marker_bar_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
-       CANVAS_DEBUG_NAME (range_marker_bar, "Range Marker Bar");
-       range_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
+       // used to show zoom mode active zooming
+       zoom_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
+       zoom_rect->hide();
+       zoom_rect->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_zoom_rect_event), (ArdourCanvas::Item*) 0));
 
-       transport_marker_bar_group = new ArdourCanvas::Group (_time_bars_canvas->root ());
-       transport_marker_bar = new ArdourCanvas::Rectangle (transport_marker_bar_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
-       CANVAS_DEBUG_NAME (transport_marker_bar, "transport Marker Bar");
-       transport_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
+       // used as rubberband rect
+       rubberband_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
+       rubberband_rect->hide();
 
-       marker_bar_group = new ArdourCanvas::Group (_time_bars_canvas->root ());
-       marker_bar = new ArdourCanvas::Rectangle (marker_bar_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
-       CANVAS_DEBUG_NAME (marker_bar, "Marker Bar");
-       marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
+       /* a group to hold stuff while it gets dragged around. Must be the
+        * uppermost (last) group with hv_scroll_group as a parent
+        */
+       _drag_motion_group = new ArdourCanvas::Group (hv_scroll_group);                                                                                                                                     
+        CANVAS_DEBUG_NAME (_drag_motion_group, "Canvas Drag Motion");
 
-       cd_marker_bar_group = new ArdourCanvas::Group (_time_bars_canvas->root ());
-       cd_marker_bar = new ArdourCanvas::Rectangle (cd_marker_bar_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
-       CANVAS_DEBUG_NAME (cd_marker_bar, "CD Marker Bar");
-       cd_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
+       /* TIME BAR CANVAS */
        
-       _time_markers_group = new ArdourCanvas::Group (_time_bars_canvas->root());
+       _time_markers_group = new ArdourCanvas::Group (h_scroll_group);
+       CANVAS_DEBUG_NAME (_time_markers_group, "time bars");
 
        cd_marker_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, 0.0));
        CANVAS_DEBUG_NAME (cd_marker_group, "cd marker group");
@@ -165,8 +156,32 @@ Editor::initialize_canvas ()
        meter_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 5.0) + 1.0));
        CANVAS_DEBUG_NAME (meter_group, "meter group");
 
-       ARDOUR_UI::instance()->video_timeline = new VideoTimeLine(this, videotl_group, (timebar_height * videotl_bar_height));
+       meter_bar = new ArdourCanvas::Rectangle (meter_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
+       CANVAS_DEBUG_NAME (meter_bar, "meter Bar");
+       meter_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
+
+       tempo_bar = new ArdourCanvas::Rectangle (tempo_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
+       CANVAS_DEBUG_NAME (tempo_bar, "Tempo  Bar");
+       tempo_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
+
+       range_marker_bar = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
+       CANVAS_DEBUG_NAME (range_marker_bar, "Range Marker Bar");
+       range_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
+
+       transport_marker_bar = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
+       CANVAS_DEBUG_NAME (transport_marker_bar, "transport Marker Bar");
+       transport_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
 
+       marker_bar = new ArdourCanvas::Rectangle (marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
+       CANVAS_DEBUG_NAME (marker_bar, "Marker Bar");
+       marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
+
+       cd_marker_bar = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
+       CANVAS_DEBUG_NAME (cd_marker_bar, "CD Marker Bar");
+       cd_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
+
+       ARDOUR_UI::instance()->video_timeline = new VideoTimeLine(this, videotl_group, (timebar_height * videotl_bar_height));
+       
        cd_marker_bar_drag_rect = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
        CANVAS_DEBUG_NAME (cd_marker_bar_drag_rect, "cd marker drag");
        cd_marker_bar_drag_rect->set_outline (false);
@@ -196,15 +211,6 @@ Editor::initialize_canvas ()
        transport_punchout_line->set_y1 (ArdourCanvas::COORD_MAX);
        transport_punchout_line->hide();
 
-       // used to show zoom mode active zooming
-       zoom_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
-       zoom_rect->hide();
-       zoom_rect->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_zoom_rect_event), (ArdourCanvas::Item*) 0));
-
-       // used as rubberband rect
-       rubberband_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
-       rubberband_rect->hide();
-
        tempo_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_tempo_bar_event), tempo_bar));
        meter_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_meter_bar_event), meter_bar));
        marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_marker_bar_event), marker_bar));
@@ -220,17 +226,17 @@ Editor::initialize_canvas ()
        }
 
 
-       _canvas_bottom_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, 20));
+       _canvas_drop_zone = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, 0.0));
        /* this thing is transparent */
-       _canvas_bottom_rect->set_fill (false);
-       _canvas_bottom_rect->set_outline (false);
-       _canvas_bottom_rect->Event.connect (sigc::mem_fun (*this, &Editor::canvas_bottom_rect_event));
+       _canvas_drop_zone->set_fill (false);
+       _canvas_drop_zone->set_outline (false);
+       _canvas_drop_zone->Event.connect (sigc::mem_fun (*this, &Editor::canvas_drop_zone_event));
 
        /* these signals will initially be delivered to the canvas itself, but if they end up remaining unhandled, they are passed to Editor-level
           handlers.
        */
 
-       _track_canvas->signal_scroll_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_scroll_event));
+       _track_canvas->signal_scroll_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_scroll_event), true));
        _track_canvas->signal_motion_notify_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_motion_notify_event));
        _track_canvas->signal_button_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_press_event));
        _track_canvas->signal_button_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_release_event));
@@ -260,6 +266,8 @@ Editor::initialize_canvas ()
 
        _track_canvas_viewport->signal_size_allocate().connect (sigc::mem_fun(*this, &Editor::track_canvas_viewport_allocate));
 
+       initialize_rulers ();
+
        ColorsChanged.connect (sigc::mem_fun (*this, &Editor::color_handler));
        color_handler();
 
@@ -280,6 +288,8 @@ Editor::track_canvas_viewport_size_allocated ()
        _visible_canvas_width  = _canvas_viewport_allocation.get_width ();
        _visible_canvas_height = _canvas_viewport_allocation.get_height ();
 
+       _canvas_drop_zone->set_y1 (_canvas_drop_zone->y0() + (_visible_canvas_height - 20.0));
+
        // SHOWTRACKS
 
        if (height_changed) {
@@ -334,13 +344,13 @@ Editor::reset_controls_layout_height (int32_t h)
         * (the drag-n-drop zone) is, in fact, at the bottom.
         */
 
-       _canvas_bottom_rect->set_position (ArdourCanvas::Duple (0, h));
+       _canvas_drop_zone->set_position (ArdourCanvas::Duple (0, h));
 
        /* track controls layout must span the full height of "h" (all tracks)
         * plus the bottom rect.
         */
 
-       h += _canvas_bottom_rect->height ();
+       h += _canvas_drop_zone->height ();
 
         /* set the height of the scrollable area (i.e. the sum of all contained widgets)
         * for the controls layout. The size request is set elsewhere.
@@ -449,7 +459,7 @@ Editor::drop_paths (const RefPtr<Gdk::DragContext>& context,
 
        if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
 
-               /* D-n-D coordinates are window-relative, so convert to "world" coordinates
+               /* D-n-D coordinates are window-relative, so convert to canvas coordinates
                 */
 
                ev.type = GDK_BUTTON_RELEASE;
@@ -483,18 +493,46 @@ Editor::drop_paths (const RefPtr<Gdk::DragContext>& context,
 void
 Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers)
 {
-       if (!Config->get_autoscroll_editor ()) {
+       if (!Config->get_autoscroll_editor () || autoscroll_active ()) {
                return;
        }
 
+       /* define a rectangular boundary for scrolling. If the mouse moves
+        * outside of this area and/or continue to be outside of this area,
+        * then we will continuously auto-scroll the canvas in the appropriate
+        * direction(s)
+        *
+        * the boundary is defined in coordinates relative to the toplevel
+        * window since that is what we're going to call ::get_pointer() on
+        * during autoscrolling to determine if we're still outside the
+        * boundary or not.
+        */
+
        ArdourCanvas::Rect scrolling_boundary;
        Gtk::Allocation alloc;
-       
+       int cx, cy;
+
        if (from_headers) {
                alloc = controls_layout.get_allocation ();
-       } else {
+       } else {        
                alloc = _track_canvas_viewport->get_allocation ();
+               cx = alloc.get_x();
+               cy = alloc.get_y();
+
+               /* reduce height by the height of the timebars, which happens
+                  to correspond to the position of the hv_scroll_group.
+               */
                
+               alloc.set_height (alloc.get_height() - hv_scroll_group->position().y);
+               alloc.set_y (alloc.get_y() + hv_scroll_group->position().y);
+
+               /* now reduce it again so that we start autoscrolling before we
+                * move off the top or bottom of the canvas
+                */
+
+               alloc.set_height (alloc.get_height() - 20);
+               alloc.set_y (alloc.get_y() + 10);
+
                /* the effective width of the autoscroll boundary so
                   that we start scrolling before we hit the edge.
                   
@@ -507,19 +545,18 @@ Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers)
                        alloc.set_width (alloc.get_width() - 20);
                        alloc.set_x (alloc.get_x() + 10);
                } 
+
        }
        
-       scrolling_boundary = ArdourCanvas::Rect (alloc.get_x(), alloc.get_y(), 
-                                                alloc.get_x() + alloc.get_width(), 
-                                                alloc.get_y() + alloc.get_height());
+       scrolling_boundary = ArdourCanvas::Rect (alloc.get_x(), alloc.get_y(), alloc.get_x() + alloc.get_width(), alloc.get_y() + alloc.get_height());
        
        int x, y;
        Gdk::ModifierType mask;
 
        get_window()->get_pointer (x, y, mask);
 
-       if ((allow_horiz && (x < scrolling_boundary.x0 || x >= scrolling_boundary.x1)) ||
-           (allow_vert && (y < scrolling_boundary.y0 || y >= scrolling_boundary.y1))) {
+       if ((allow_horiz && ((x < scrolling_boundary.x0 && leftmost_frame > 0) || x >= scrolling_boundary.x1)) ||
+           (allow_vert && ((y < scrolling_boundary.y0 && vertical_adjustment.get_value() > 0)|| y >= scrolling_boundary.y1))) {
                start_canvas_autoscroll (allow_horiz, allow_vert, scrolling_boundary);
        }
 }
@@ -542,6 +579,7 @@ Editor::autoscroll_canvas ()
        get_window()->get_pointer (x, y, mask);
 
        VisualChange vc;
+       bool vertical_motion = false;
 
        if (autoscroll_horizontal_allowed) {
 
@@ -589,9 +627,8 @@ Editor::autoscroll_canvas ()
 
        if (autoscroll_vertical_allowed) {
                
-               const double vertical_pos = vertical_adjustment.get_value();
-               double new_pixel = vertical_pos;
-               const int speed_factor = 20;
+               // const double vertical_pos = vertical_adjustment.get_value();
+               const int speed_factor = 10;
 
                /* vertical */ 
                
@@ -601,20 +638,21 @@ Editor::autoscroll_canvas ()
 
                        if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
                                y_motion = scroll_up_one_track ();
+                               vertical_motion = true;
                        }
 
                } else if (y > autoscroll_boundary.y1) {
 
                        if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
                                y_motion = scroll_down_one_track ();
-                               
+                               vertical_motion = true;
                        }
                }
 
                no_stop = true;
        }
 
-       if (vc.pending) {
+       if (vc.pending || vertical_motion) {
 
                /* change horizontal first */
 
@@ -633,26 +671,33 @@ Editor::autoscroll_canvas ()
                
                /* the motion handler expects events in canvas coordinate space */
 
-               /* first convert from Editor window coordinates to canvas
-                * window coordinates
+               /* we asked for the mouse position above (::get_pointer()) via
+                * our own top level window (we being the Editor). Convert into 
+                * coordinates within the canvas window.
                 */
 
                int cx;
                int cy;
 
-               /* clamp x and y to remain within the visible area */
+               translate_coordinates (*_track_canvas, x, y, cx, cy);
 
-               x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1);
-               y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1);
+               /* clamp x and y to remain within the autoscroll boundary,
+                * which is defined in window coordinates
+                */
 
-               translate_coordinates (*_track_canvas_viewport, x, y, cx, cy);
+               x = min (max ((ArdourCanvas::Coord) cx, autoscroll_boundary.x0), autoscroll_boundary.x1);
+               y = min (max ((ArdourCanvas::Coord) cy, autoscroll_boundary.y0), autoscroll_boundary.y1);
+
+               /* now convert from Editor window coordinates to canvas
+                * window coordinates
+                */
 
                ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
                ev.x = d.x;
                ev.y = d.y;
 
                motion_handler (0, (GdkEvent*) &ev, true);
-
+               
        } else if (no_stop) {
 
                /* not changing visual state but pointer is outside the scrolling boundary
@@ -692,7 +737,7 @@ Editor::autoscroll_canvas ()
                ev.y = d.y;
 
                motion_handler (0, (GdkEvent*) &ev, true);
-
+               
        } else {
                stop_canvas_autoscroll ();
                return false;
@@ -804,6 +849,17 @@ Editor::set_horizontal_position (double p)
 void
 Editor::color_handler()
 {
+       ArdourCanvas::Color base = ARDOUR_UI::config()->get_canvasvar_RulerBase();
+       ArdourCanvas::Color text = ARDOUR_UI::config()->get_canvasvar_RulerText();
+       timecode_ruler->set_fill_color (base);
+       timecode_ruler->set_outline_color (text);
+       minsec_ruler->set_fill_color (base);
+       minsec_ruler->set_outline_color (text);
+       samples_ruler->set_fill_color (base);
+       samples_ruler->set_outline_color (text);
+       bbt_ruler->set_fill_color (base);
+       bbt_ruler->set_outline_color (text);
+       
        playhead_cursor->set_color (ARDOUR_UI::config()->get_canvasvar_PlayHead());
        _verbose_cursor->set_color (ARDOUR_UI::config()->get_canvasvar_VerboseCanvasCursor());
 
@@ -884,6 +940,25 @@ Editor::set_canvas_cursor (Gdk::Cursor* cursor, bool save)
        }
 }
 
+void
+Editor::push_canvas_cursor (Gdk::Cursor* cursor)
+{
+       if (cursor) {
+               _cursor_stack.push (cursor);
+               set_canvas_cursor (cursor, false);
+       }
+}
+
+void
+Editor::pop_canvas_cursor ()
+{
+       if (!_cursor_stack.empty()) {
+               Gdk::Cursor* cursor = _cursor_stack.top ();
+               _cursor_stack.pop ();
+               set_canvas_cursor (cursor, false);
+       }
+}
+
 bool
 Editor::track_canvas_key_press (GdkEventKey*)
 {
@@ -924,24 +999,6 @@ Editor::clamp_verbose_cursor_y (double y)
        return y;
 }
 
-ArdourCanvas::Group*
-Editor::get_time_bars_group () const
-{
-       return _time_bars_canvas->root();
-}
-
-ArdourCanvas::Group*
-Editor::get_track_canvas_group() const
-{
-       return hv_scroll_group;
-}
-
-ArdourCanvas::GtkCanvasViewport*
-Editor::get_time_bars_canvas() const
-{
-       return _time_bars_canvas_viewport;
-}
-
 ArdourCanvas::GtkCanvasViewport*
 Editor::get_track_canvas() const
 {