Unify the canvases by moving groups around rather than using set_scrolling_region...
[ardour.git] / gtk2_ardour / editor_mouse.cc
index 36029a421358515dafca75c996e9445f63b6bcd2..9a862786b27b32d47b63dd8d016e8cce8ef5a125 100644 (file)
@@ -1,3 +1,4 @@
+
 /*
     Copyright (C) 2000-2001 Paul Davis 
 
@@ -85,23 +86,15 @@ Editor::mouse_frame (nframes64_t& where, bool& in_track_canvas) const
        pointer_window = canvas_window->get_pointer (x, y, mask);
 
        if (pointer_window == track_canvas->get_bin_window()) {
-
-               track_canvas->window_to_world (x, y, wx, wy);
+               wx = x;
+               wy = y;
                in_track_canvas = true;
 
        } else {
                in_track_canvas = false;
-
-               if (pointer_window == time_canvas->get_bin_window()) {
-                       time_canvas->window_to_world (x, y, wx, wy);
-               } else {
                        return false;
-               }
        }
 
-       wx += horizontal_adjustment.get_value();
-       wy += vertical_adjustment.get_value();
-
        GdkEvent event;
        event.type = GDK_BUTTON_RELEASE;
        event.button.x = wx;
@@ -131,10 +124,16 @@ Editor::event_frame (GdkEvent* event, double* pcx, double* pcy) const
        case GDK_BUTTON_PRESS:
        case GDK_2BUTTON_PRESS:
        case GDK_3BUTTON_PRESS:
-               track_canvas->w2c(event->button.x, event->button.y, *pcx, *pcy);
+
+               *pcx = event->button.x;
+               *pcy = event->button.y;
+               _trackview_group->w2i(*pcx, *pcy);
                break;
        case GDK_MOTION_NOTIFY:
-               track_canvas->w2c(event->motion.x, event->motion.y, *pcx, *pcy);
+       
+               *pcx = event->motion.x;
+               *pcy = event->motion.y;
+               _trackview_group->w2i(*pcx, *pcy);
                break;
        case GDK_ENTER_NOTIFY:
        case GDK_LEAVE_NOTIFY:
@@ -214,9 +213,10 @@ Editor::which_grabber_cursor ()
                return grabber_edit_point_cursor;
                break;
        default:
-               return grabber_cursor;
+         //return grabber_cursor;
                break;
        }
+               return grabber_cursor;
 }
 
 void
@@ -478,7 +478,6 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp
                pointer_window = canvas_window->get_pointer (x, y, mask);
                
                if (pointer_window == track_canvas->get_bin_window()) {
-                       
                        track_canvas->window_to_world (x, y, wx, wy);
                        allow_vertical_scroll = true;
                } else {
@@ -1360,7 +1359,7 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
        case MeterBarItem:
        case TempoBarItem:
                if (is_drawable()) {
-                       time_canvas->get_window()->set_cursor (*timebar_cursor);
+                       track_canvas->get_window()->set_cursor (*timebar_cursor);
                }
                break;
 
@@ -1374,7 +1373,7 @@ Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
        case MeterMarkerItem:
        case TempoMarkerItem:
                if (is_drawable()) {
-                       time_canvas->get_window()->set_cursor (*timebar_cursor);
+                       track_canvas->get_window()->set_cursor (*timebar_cursor);
                }
                break;
        case FadeInHandleItem:
@@ -1495,7 +1494,7 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
        case TempoBarItem:
        case MarkerBarItem:
                if (is_drawable()) {
-                       time_canvas->get_window()->set_cursor (*timebar_cursor);
+                       track_canvas->get_window()->set_cursor (*timebar_cursor);
                }
                break;
                
@@ -1512,7 +1511,7 @@ Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_
        case TempoMarkerItem:
                
                if (is_drawable()) {
-                       time_canvas->get_window()->set_cursor (*timebar_cursor);
+                       track_canvas->get_window()->set_cursor (*timebar_cursor);
                }
 
                break;
@@ -1647,7 +1646,7 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item
                   where DISPLAY = :0.0, and given the cost of what the motion
                   event might do, its a good tradeoff.  
                */
-               
+
                track_canvas->get_pointer (x, y);
        } 
 
@@ -1705,6 +1704,9 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item
        switch (item_type) {
        case PlayheadCursorItem:
        case MarkerItem:
+       case RangeMarkerBarItem:
+       case TransportMarkerBarItem:
+       case CdMarkerBarItem:
        case GainControlPointItem:
        case RedirectAutomationControlPointItem:
        case GainAutomationControlPointItem:
@@ -1730,13 +1732,13 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item
          if (drag_info.item && (event->motion.state & Gdk::BUTTON1_MASK ||
                                 (event->motion.state & Gdk::BUTTON2_MASK))) {
                  if (!from_autoscroll) {
-                         maybe_autoscroll (&event->motion);
+                         maybe_autoscroll_horizontally (&event->motion);
                  }
                  (this->*(drag_info.motion_callback)) (item, event);
                  goto handled;
          }
          goto not_handled;
-         
+         break;
        default:
                break;
        }
@@ -2278,7 +2280,7 @@ Editor::start_marker_grab (ArdourCanvas::Item* item, GdkEvent* event)
                // marker_drag_line->raise_to_top();
        } else {
                range_marker_drag_rect->show();
-               range_marker_drag_rect->raise_to_top();
+               //range_marker_drag_rect->raise_to_top();
        }
 
        if (is_start) {
@@ -2978,6 +2980,17 @@ Editor::line_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event)
 void
 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
 {
+       /* 
+          the group containing moved regions may have been 
+          offset during autoscroll. reset its y offset
+          (we should really handle this in the same way 
+          we do with the x axis, but a simple way of achieving that 
+          eludes me right now). 
+       */
+
+       _region_motion_group->property_y() = 0;
+       grabbed_vadjustment = vertical_adjustment.get_value ();
+
        if (selection->regions.empty() || clicked_regionview == 0) {
                return;
        }
@@ -3019,6 +3032,8 @@ Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
 void
 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
 {
+       _region_motion_group->property_y() = 0;
+
        if (selection->regions.empty() || clicked_regionview == 0) {
                return;
        }
@@ -3105,7 +3120,7 @@ Editor::possibly_copy_regions_during_grab (GdkEvent* event)
                                /* XXX handle MIDI here */
                                continue;
                        }
-                       
+
                        const boost::shared_ptr<const Region> original = arv->region();
                        boost::shared_ptr<Region> region_copy = RegionFactory::create (original);
                        boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (region_copy);
@@ -3336,6 +3351,8 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
 
                        rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
                        rv2->get_canvas_group()->i2w (ix1, iy1);
+                       iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
+
                        TimeAxisView* tvp2 = trackview_by_y_position (iy1);
                        RouteTimeAxisView* atv2 = dynamic_cast<RouteTimeAxisView*>(tvp2);
 
@@ -3421,10 +3438,9 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
        /* compute the amount of pointer motion in frames, and where
           the region would be if we moved it by that much.
        */
-
        if ( drag_info.move_threshold_passed ) {
 
-               if (drag_info.current_pointer_frame > drag_info.pointer_frame_offset) {
+               if (drag_info.current_pointer_frame >= drag_info.pointer_frame_offset) {
 
                        nframes64_t sync_frame;
                        nframes64_t sync_offset;
@@ -3459,7 +3475,7 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                if (pending_region_position > max_frames - rv->region()->length()) {
                        pending_region_position = drag_info.last_frame_position;
                }
-         
+
                // printf ("3: pending_region_position= %lu    %lu\n", pending_region_position, drag_info.last_frame_position );
 
                bool x_move_allowed;
@@ -3475,7 +3491,7 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                        x_move_allowed = !drag_info.x_constrained;
                }
 
-               if ( pending_region_position != drag_info.last_frame_position && x_move_allowed ) {
+               if (( pending_region_position != drag_info.last_frame_position) && x_move_allowed ) {
 
                        /* now compute the canvas unit distance we need to move the regionview
                           to make it appear at the new location.
@@ -3485,10 +3501,29 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                                x_delta = ((double) (pending_region_position - drag_info.last_frame_position) / frames_per_unit);
                        } else {
                                x_delta = -((double) (drag_info.last_frame_position - pending_region_position) / frames_per_unit);
-                       }
+                               for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
 
+                                       RegionView* rv2 = (*i);
+
+                                       // If any regionview is at zero, we need to know so we can stop further leftward motion.
+       
+                                       double ix1, ix2, iy1, iy2;
+                                       rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
+                                       rv2->get_canvas_group()->i2w (ix1, iy1);
+                       
+                                       if (-x_delta > ix1 + horizontal_adjustment.get_value()) {
+                                               //      do_move = false;
+                                               cerr << "illegal move" << endl;
+                                               x_delta = 0;
+                                               pending_region_position = drag_info.last_frame_position;
+                                               break;
+                                       }
+                               }
+
+                       }
+               
                        drag_info.last_frame_position = pending_region_position;
-           
+
                } else {
                        x_delta = 0;
                }
@@ -3508,41 +3543,16 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                   trackviews. nothing to do.
                */
                return;
-       } 
-
-
-       if (x_delta < 0) {
-               for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i) {
-
-                       RegionView* rv2 = (*i);
-
-                       // If any regionview is at zero, we need to know so we can stop further leftward motion.
-                       
-                       double ix1, ix2, iy1, iy2;
-                       rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
-                       rv2->get_canvas_group()->i2w (ix1, iy1);
-
-                       if (ix1 <= 1) {
-                               x_delta = 0;
-                               break;
-                       }
-               }
        }
 
        /*************************************************************
                         MOTION                                                               
        ************************************************************/
-
-       bool do_move;
-
+       bool do_move = true;
        if (drag_info.first_move) {
-               if (drag_info.move_threshold_passed) {
-                       do_move = true;
-               } else {
+               if (!drag_info.move_threshold_passed) {
                        do_move = false;
                }
-       } else {
-               do_move = true;
        }
 
        if (do_move) {
@@ -3567,6 +3577,30 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
 
                        rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
                        rv->get_canvas_group()->i2w (ix1, iy1);
+
+                       if (drag_info.first_move) {
+
+                               // hide any dependent views 
+                       
+                               rv->get_time_axis_view().hide_dependent_views (*rv);
+
+                               /* 
+                                  reparent to a non scrolling group so that we can keep the 
+                                  region selection above all time axis views.
+                                  reparenting means we have to move the rv as the two 
+                                  parent groups have different coordinates.
+                               */
+
+                               rv->get_canvas_group()->reparent(*_region_motion_group);
+
+                               rv->get_canvas_group()->move ( 0, iy1 - 1);
+                               rv->fake_set_opaque (true);
+                       }
+                       /* for evaluation of the track position of iy1, we have to adjust 
+                          to allow for the vertical scrolling adjustment and the height of the timebars.
+                       */
+                       iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
+
                        TimeAxisView* tvp2 = trackview_by_y_position (iy1);
                        AudioTimeAxisView* canvas_atv = dynamic_cast<AudioTimeAxisView*>(tvp2);
                        AudioTimeAxisView* temp_atv;
@@ -3629,43 +3663,12 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                                }
                        }
 
-
-                       /* prevent the regionview from being moved to before 
-                          the zero position on the canvas.
-                       */
-                       /* clamp */
-               
-                       if (x_delta < 0) {
-                               if (-x_delta > ix1) {
-                                       x_delta = -ix1;
-                               }
-                       } else if ((x_delta > 0) && (rv->region()->last_frame() > max_frames - x_delta)) {
-                               x_delta = max_frames - rv->region()->last_frame();
-                       }
-
-
-                       if (drag_info.first_move) {
-
-                               /* hide any dependent views */
-                       
-                               rv->get_time_axis_view().hide_dependent_views (*rv);
-                       
-                               /* this is subtle. raising the regionview itself won't help,
-                                  because raise_to_top() just puts the item on the top of
-                                  its parent's stack. so, we need to put the trackview canvas_display group
-                                  on the top, since its parent is the whole canvas.
-                               */
-                       
-                               rv->get_canvas_group()->raise_to_top();
-                               rv->get_time_axis_view().canvas_display->raise_to_top();
-                               cursor_group->raise_to_top();
-                               rv->fake_set_opaque (true);
-                       }
-
                        if (drag_info.brushing) {
                                mouse_brush_insert_region (rv, pending_region_position);
                        } else {
-                               rv->move (x_delta, y_delta);                    
+
+                         rv->move (x_delta, y_delta);
+
                        }
 
                } /* foreach region */
@@ -3693,7 +3696,9 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event
        vector<RegionView*> new_selection;
        typedef set<boost::shared_ptr<Playlist> > PlaylistSet;
        PlaylistSet modified_playlists;
-       pair<PlaylistSet::iterator,bool> insert_result;
+       PlaylistSet frozen_playlists;
+       list <sigc::connection> modified_playlist_connections;
+       pair<PlaylistSet::iterator,bool> insert_result, frozen_insert_result;
 
        /* first_move is set to false if the regionview has been moved in the 
           motion handler. 
@@ -3706,11 +3711,12 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event
 
        nocommit = false;
 
-       /* The regionview has been moved at some stage during the grab so we need
+       /* XXX is this true??? i can''t tell the difference.
+          The regionview has been moved at some stage during the grab so we need
           to account for any mouse movement between this event and the last one. 
        */      
 
-       region_drag_motion_callback (item, event);
+       //region_drag_motion_callback (item, event);
 
        if (Config->get_edit_mode() == Splice && !pre_drag_region_selection.empty()) {
                selection->set (pre_drag_region_selection);
@@ -3758,14 +3764,17 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event
        for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ) {
                        
                RegionView* rv = (*i);              
-               double ix1, ix2, iy1, iy2;
+               double ix1, ix2, iy1, iy2, y_pos;
                rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
                rv->get_canvas_group()->i2w (ix1, iy1);
+
+               y_pos = iy1;
+               iy1 += vertical_adjustment.get_value() - canvas_timebars_vsize;
+
                TimeAxisView* dest_tv = trackview_by_y_position (iy1);
                AudioTimeAxisView* dest_atv = dynamic_cast<AudioTimeAxisView*>(dest_tv);
                double speed;
-               bool changed_tracks;
-               bool changed_position;
+               bool changed_tracks, changed_position;
                nframes64_t where;
 
                if (rv->region()->locked()) {
@@ -3785,23 +3794,25 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event
                changed_tracks = (dest_tv != &rv->get_time_axis_view());
 
                if (changed_position && !drag_info.x_constrained) {
+                       _master_group->w2i(ix1, iy1);
                        where = (nframes64_t) (unit_to_frame (ix1) * speed);
                } else {
                        where = rv->region()->position();
                }
                        
-               /* undo the previous hide_dependent_views so that xfades don't
-                  disappear on copying regions 
-               */
-               
-               rv->get_time_axis_view().reveal_dependent_views (*rv);
-               
                boost::shared_ptr<Region> new_region;
 
                if (drag_info.copy) {
                        /* we already made a copy */
                        new_region = rv->region();
-               } else {
+
+                       /* undo the previous hide_dependent_views so that xfades don't
+                          disappear on copying regions 
+                       */
+               
+                       //rv->get_time_axis_view().reveal_dependent_views (*rv);
+               
+               } else if (changed_tracks) {
                        new_region = RegionFactory::create (rv->region());
                }
 
@@ -3815,7 +3826,7 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event
                        
                        insert_result = modified_playlists.insert (to_playlist);
                        if (insert_result.second) {
-                               session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));        
+                               session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));
                        }
 
                        to_playlist->add_region (new_region, where);
@@ -3824,19 +3835,36 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event
                                                              
                        if (!latest_regionviews.empty()) {
                                // XXX why just the first one ? we only expect one
-                               dest_atv->reveal_dependent_views (*latest_regionviews.front());
+                               //dest_atv->reveal_dependent_views (*latest_regionviews.front());
                                new_selection.push_back (latest_regionviews.front());
                        }
 
                } else {
-                       
+                       /* 
+                          motion on the same track. plonk the previously reparented region 
+                          back to its original canvas group (its streamview).
+                          No need to do anything for copies as they are fake regions which will be deleted.
+                       */
+                       /* account for any vertical autoscrolling that might have happened during the move */
+                       double vadjustment_delta = vertical_adjustment.get_value () - grabbed_vadjustment;
+                       y_pos += vadjustment_delta;
+
+                       RouteTimeAxisView* dest_rtv = dynamic_cast<RouteTimeAxisView*> (dest_atv);
+                       rv->get_canvas_group()->reparent (*dest_rtv->view()->canvas_item());
+                       rv->get_canvas_group()->move ( 0, -y_pos + 1);
+                 
                        /* just change the model */
                        
                        boost::shared_ptr<Playlist> playlist = dest_atv->playlist();
 
                        insert_result = modified_playlists.insert (playlist);
                        if (insert_result.second) {
-                               session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));      
+                               session->add_command (new MementoCommand<Playlist>(*playlist, &playlist->get_state(), 0));
+                       }
+                       /* freeze to avoid lots of relayering in the case of a multi-region drag */
+                       frozen_insert_result = frozen_playlists.insert(playlist);
+                       if (frozen_insert_result.second) {
+                               playlist->freeze();
                        }
 
                        rv->region()->set_position (where, (void*) this);
@@ -3867,7 +3895,7 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event
 
                        insert_result = modified_playlists.insert (from_playlist);
                        if (insert_result.second) {
-                               session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));    
+                               session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));
                        }
 
                        from_playlist->remove_region ((rv->region()));
@@ -3903,7 +3931,6 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event
                        copies.push_back (rv);
                }
        }
-
        
        if (new_selection.empty()) {
                if (drag_info.copy) {
@@ -3919,6 +3946,10 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event
                */
                selection->set (new_selection);
        }
+
+       for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
+               (*p)->thaw();
+       }
                        
   out:
        if (!nocommit) {
@@ -3931,6 +3962,7 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event
        for (vector<RegionView*>::iterator x = copies.begin(); x != copies.end(); ++x) {
                delete *x;
        }
+
 }
 
 void
@@ -4021,7 +4053,7 @@ Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos,
                set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
        }
        else {
-               set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset, drag_info.current_pointer_y + offset);
+               set_verbose_canvas_cursor (buf, drag_info.current_pointer_x + offset - horizontal_adjustment.get_value(), drag_info.current_pointer_y + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
        }
        show_verbose_canvas_cursor ();
 }
@@ -4934,7 +4966,7 @@ Editor::drag_range_markerbar_op (ArdourCanvas::Item* item, GdkEvent* event)
 
                        update_marker_drag_item (temp_location);
                        range_marker_drag_rect->show();
-                       range_marker_drag_rect->raise_to_top();
+                       //range_marker_drag_rect->raise_to_top();
                        
                } 
                break;          
@@ -5215,8 +5247,7 @@ Editor::end_rubberband_select (ArdourCanvas::Item* item, GdkEvent* event)
                if (drag_info.current_pointer_y < drag_info.grab_y) {
                        y1 = drag_info.current_pointer_y;
                        y2 = drag_info.grab_y;
-               }
-               else {
+               } else {
                        y2 = drag_info.current_pointer_y;
                        y1 = drag_info.grab_y;
                }