fix dragging that involves locked regions; auto-rebinding patch for people to experim...
authorPaul Davis <paul@linuxaudiosystems.com>
Fri, 21 Mar 2008 20:22:00 +0000 (20:22 +0000)
committerPaul Davis <paul@linuxaudiosystems.com>
Fri, 21 Mar 2008 20:22:00 +0000 (20:22 +0000)
git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@3164 d708f5d6-7413-0410-9779-e7cbd77b26cf

26 files changed:
gtk2_ardour/ardour.menus
gtk2_ardour/ardour_ui_ed.cc
gtk2_ardour/audio_region_view.cc
gtk2_ardour/audio_region_view.h
gtk2_ardour/draginfo.h
gtk2_ardour/editor_imageframe.cc
gtk2_ardour/editor_mouse.cc
gtk2_ardour/gain_meter.h
gtk2_ardour/mixer_ui.cc
gtk2_ardour/mixer_ui.h
gtk2_ardour/panner_ui.cc
gtk2_ardour/panner_ui.h
gtk2_ardour/region_view.cc
gtk2_ardour/region_view.h
libs/ardour/ardour/region_factory.h
libs/ardour/ardour/session.h
libs/ardour/region_factory.cc
libs/ardour/session.cc
libs/gtkmm2ext/gtkmm2ext/barcontroller.h
libs/gtkmm2ext/gtkmm2ext/bindable_button.h
libs/gtkmm2ext/gtkmm2ext/binding_proxy.h
libs/pbd/controllable.cc
libs/pbd/pbd/controllable.h
libs/surfaces/generic_midi/generic_midi_control_protocol.cc
libs/surfaces/generic_midi/generic_midi_control_protocol.h
libs/surfaces/generic_midi/midicontrollable.h

index ca3628f87939d2bb537591e43ab59a0d1149df7c..3f24ffb96ba06fc52e422119bc283cf9f3b9944d 100644 (file)
                    <menuitem action='RemoteUserDefined'/>
                    <menuitem action='RemoteMixerDefined'/>
                    <menuitem action='RemoteEditorDefined'/>
+                  <separator/>
+                   <menuitem action='AutoRebinding'/>
                </menu>
                <menu action='Monitoring'>
                    <menuitem action='UseHardwareMonitoring'/>
index 1ef2e00b568434b3bca1494d466d0a7e77369474..8eec76956c24577b1d9e5643f0efc574b78127ce 100644 (file)
@@ -37,6 +37,7 @@
 #include "engine_dialog.h"
 #include "editor.h"
 #include "actions.h"
+#include "mixer_ui.h"
 
 #ifdef GTKOSX
 #include <gtkmm2ext/sync-menu.h>
@@ -573,6 +574,10 @@ ARDOUR_UI::install_actions ()
        act = ActionManager::register_radio_action (option_actions, remote_group, X_("RemoteEditorDefined"), _("Remote ID follows order of Editor"), hide_return (bind (mem_fun (*this, &ARDOUR_UI::set_remote_model), EditorOrdered)));
        ActionManager::session_sensitive_actions.push_back (act);
 
+       act = ActionManager::register_toggle_action (option_actions, X_("AutoRebinding"), _("Auto Rebind Controls"), mem_fun (*(this->mixer), &Mixer_UI::toggle_auto_rebinding));
+       ActionManager::session_sensitive_actions.push_back (act);
+
+
        ActionManager::add_action_group (shuttle_actions);
        ActionManager::add_action_group (option_actions);
        ActionManager::add_action_group (jack_actions);
index a5b767c8cde766d7703462478936da8697d2ef78..16c8e54bedb8d9822592971247bf1c08f258b3db 100644 (file)
@@ -116,6 +116,28 @@ AudioRegionView::AudioRegionView (const AudioRegionView& other)
        init (c, true);
 }
 
+AudioRegionView::AudioRegionView (const AudioRegionView& other, boost::shared_ptr<AudioRegion> other_region)
+       : RegionView (other, boost::shared_ptr<Region> (other_region))
+       , zero_line(0)
+       , fade_in_shape(0)
+       , fade_out_shape(0)
+       , fade_in_handle(0)
+       , fade_out_handle(0)
+       , gain_line(0)
+       , _amplitude_above_axis(1.0)
+       , _flags(0)
+       , fade_color(0)
+
+{
+       Gdk::Color c;
+       int r,g,b,a;
+
+       UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
+       c.set_rgb_p (r/255.0, g/255.0, b/255.0);
+       
+       init (c, true);
+}
+
 void
 AudioRegionView::init (Gdk::Color& basic_color, bool wfd)
 {
index 63e7647591d30dc2f1f26c81ed50c8e65c3afc01..99029d7afaadff6e9c71736f3f29681019b3bec2 100644 (file)
@@ -55,6 +55,7 @@ class AudioRegionView : public RegionView
                         Gdk::Color& basic_color);
 
         AudioRegionView (const AudioRegionView& other);
+        AudioRegionView (const AudioRegionView& other, boost::shared_ptr<ARDOUR::AudioRegion>);
 
        ~AudioRegionView ();
        
index d8e3eb74b455289f4e556dc6cc05d9425b12d7c7..bd6313d97035181ea75be38c7132782afe5414a5 100644 (file)
@@ -53,7 +53,8 @@ struct DragInfo {
     double last_pointer_y;
     void (Editor::*motion_callback)(ArdourCanvas::Item*, GdkEvent*);
     void (Editor::*finished_callback)(ArdourCanvas::Item*, GdkEvent*);
-    TimeAxisView* last_trackview;
+    TimeAxisView* source_trackview;
+    TimeAxisView* dest_trackview;
     bool x_constrained;
     bool y_constrained;
     bool copy;
index 7634617f01c1db94cafee85e7beeef78748c2b92..397af2d30551ac801d554535bad56e3622b02094 100644 (file)
@@ -430,7 +430,8 @@ Editor::start_imageframe_grab(ArdourCanvas::Item* item, GdkEvent* event)
        drag_info.finished_callback = &Editor::timeaxis_item_drag_finished_callback;
        drag_info.last_frame_position = ifv->get_position() ;
  
-       drag_info.last_trackview = &ifv->get_time_axis_view() ;
+       drag_info.source_trackview = &ifv->get_time_axis_view() ;
+       drag_info.dest_trackview = drag_info.source_trackview;
        
        /* this is subtle. raising the regionview itself won't help,
           because raise_to_top() just puts the item on the top of
@@ -442,7 +443,7 @@ Editor::start_imageframe_grab(ArdourCanvas::Item* item, GdkEvent* event)
        */
 
        drag_info.item->raise_to_top();
-       drag_info.last_trackview->canvas_display->raise_to_top();
+       drag_info.source_trackview->canvas_display->raise_to_top();
        //time_line_group->raise_to_top();
        cursor_group->raise_to_top ();
 
@@ -463,7 +464,8 @@ Editor::start_markerview_grab(ArdourCanvas::Item* item, GdkEvent* event)
        drag_info.finished_callback = &Editor::timeaxis_item_drag_finished_callback;
        drag_info.last_frame_position = mv->get_position() ;
 
-       drag_info.last_trackview = &mv->get_time_axis_view() ;
+       drag_info.source_trackview = &mv->get_time_axis_view() ;
+       drag_info.dest_trackview = drag_info.source_trackview;
 
        /* this is subtle. raising the regionview itself won't help,
           because raise_to_top() just puts the item on the top of
@@ -475,7 +477,7 @@ Editor::start_markerview_grab(ArdourCanvas::Item* item, GdkEvent* event)
        */
 
        drag_info.item->raise_to_top();
-       drag_info.last_trackview->canvas_display->raise_to_top();
+       drag_info.source_trackview->canvas_display->raise_to_top();
        //time_line_group->raise_to_top();
        cursor_group->raise_to_top ();
 
index 035dd9d7af2e04663a7a735f59f65678639afcfb..5fe65d16dc2b793c84b5e4c9369d1e8247a824f4 100644 (file)
@@ -1846,7 +1846,8 @@ Editor::end_grab (ArdourCanvas::Item* item, GdkEvent* event)
        drag_info.copy = false;
        drag_info.motion_callback = 0;
        drag_info.finished_callback = 0;
-       drag_info.last_trackview = 0;
+       drag_info.dest_trackview = 0;
+       drag_info.source_trackview = 0;
        drag_info.last_frame_position = 0;
        drag_info.grab_frame = 0;
        drag_info.last_pointer_frame = 0;
@@ -2944,7 +2945,8 @@ Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event)
        
        drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
        drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
-       drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
+       drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
+       drag_info.dest_trackview = drag_info.source_trackview;
        // we want a move threshold
        drag_info.want_move_threshold = true;
        
@@ -2974,7 +2976,8 @@ Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event)
                speed = atv->get_diskstream()->speed();
        }
        
-       drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
+       drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
+       drag_info.dest_trackview = drag_info.source_trackview;
        drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
        drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
        // we want a move threshold
@@ -3009,7 +3012,8 @@ Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event)
        
        drag_info.last_frame_position = (nframes_t) (clicked_regionview->region()->position() / speed);
        drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
-       drag_info.last_trackview = &clicked_regionview->get_time_axis_view();
+       drag_info.source_trackview = &clicked_regionview->get_time_axis_view();
+       drag_info.dest_trackview = drag_info.source_trackview;
        // we want a move threshold
        drag_info.want_move_threshold = true;
        drag_info.brushing = true;
@@ -3024,7 +3028,7 @@ Editor::possibly_copy_regions_during_grab (GdkEvent* event)
 
                drag_info.want_move_threshold = false; // don't copy again
 
-               /* duplicate the region(s) */
+               /* duplicate the regionview(s) and region(s) */
 
                vector<RegionView*> new_regionviews;
                
@@ -3032,7 +3036,7 @@ Editor::possibly_copy_regions_during_grab (GdkEvent* event)
                        RegionView* rv;
                        RegionView* nrv;
                        AudioRegionView* arv;
-
+                       
                        rv = (*i);
 
                        
@@ -3040,8 +3044,12 @@ 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);
 
-                       nrv = new AudioRegionView (*arv);
+                       nrv = new AudioRegionView (*arv, ar);
                        nrv->get_canvas_group()->show ();
 
                        new_regionviews.push_back (nrv);
@@ -3178,7 +3186,7 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                return;
        }
 
-       original_pointer_order = drag_info.last_trackview->order;
+       original_pointer_order = drag_info.dest_trackview->order;
                
        /************************************************************
                  Y-Delta Computation
@@ -3190,7 +3198,7 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                goto y_axis_done;
        }
 
-       if ((pointer_y_span = (drag_info.last_trackview->order - tv->order)) != 0) {
+       if ((pointer_y_span = (drag_info.dest_trackview->order - tv->order)) != 0) {
 
                int32_t children = 0, numtracks = 0;
                // XXX hard coding track limit, oh my, so very very bad
@@ -3234,16 +3242,16 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                /* find the actual span according to the canvas */
 
                canvas_pointer_y_span = pointer_y_span;
-               if (drag_info.last_trackview->order >= tv->order) {
+               if (drag_info.dest_trackview->order >= tv->order) {
                        int32_t y;
-                       for (y = tv->order; y < drag_info.last_trackview->order; y++) {
+                       for (y = tv->order; y < drag_info.dest_trackview->order; y++) {
                                if (height_list[y] == 0 ) {
                                        canvas_pointer_y_span--;
                                }
                        }
                } else {
                        int32_t y;
-                       for (y = drag_info.last_trackview->order;y <= tv->order; y++) {
+                       for (y = drag_info.dest_trackview->order;y <= tv->order; y++) {
                                if (    height_list[y] == 0 ) {
                                        canvas_pointer_y_span++;
                                }
@@ -3255,6 +3263,10 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                        double ix1, ix2, iy1, iy2;
                        int32_t n = 0;
 
+                       if (rv2->region()->locked()) {
+                               continue;
+                       }
+
                        rv2->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
                        rv2->get_canvas_group()->i2w (ix1, iy1);
                        TimeAxisView* tvp2 = trackview_by_y_position (iy1);
@@ -3326,13 +3338,13 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                        }
                }
 
-       } else  if (drag_info.last_trackview == tv) {
+       } else  if (drag_info.dest_trackview == tv) {
                clamp_y_axis = true;
        }         
 
   y_axis_done:
        if (!clamp_y_axis) {
-               drag_info.last_trackview = tv;        
+               drag_info.dest_trackview = tv;        
        }
          
        /************************************************************
@@ -3465,6 +3477,10 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                        double ix1, ix2, iy1, iy2;
                        int32_t temp_pointer_y_span = pointer_y_span;
 
+                       if (rv->region()->locked()) {
+                               continue;
+                       }
+
                        /* get item BBox, which will be relative to parent. so we have
                           to query on a child, then convert to world coordinates using
                           the parent.
@@ -3502,6 +3518,7 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                                                        y_delta -= (*j);        
                                                        temp_pointer_y_span--;  
                                                }
+
                                                while (temp_pointer_y_span < 0) {                 
                                                        y_delta += (*j);
                                                        if (x != original_pointer_order) { 
@@ -3532,7 +3549,8 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
                                        x++;
                                }
                        }
-         
+
+
                        /* prevent the regionview from being moved to before 
                           the zero position on the canvas.
                        */
@@ -3597,6 +3615,10 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event
        bool regionview_y_movement;
        bool regionview_x_movement;
        vector<RegionView*> copies;
+       RouteTimeAxisView* tvp1;
+       boost::shared_ptr<Diskstream> ds;
+       boost::shared_ptr<Playlist> from_playlist;
+       bool axis_motion;
 
        /* first_move is set to false if the regionview has been moved in the 
           motion handler. 
@@ -3632,19 +3654,40 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event
                goto out;
        }
 
+       /* get the playlist where this drag started. we can't use rv->region()->playlist()
+          because we may have copied the region and it has not been attached to a playlist.
+       */
+
+       assert ((tvp1 = dynamic_cast<RouteTimeAxisView*> (drag_info.source_trackview)));
+       assert ((ds = tvp1->get_diskstream()));
+       assert ((from_playlist = ds->playlist()));
+
        /* adjust for track speed */
        speed = 1.0;
 
-       atv = dynamic_cast<AudioTimeAxisView*> (drag_info.last_trackview);
+       atv = dynamic_cast<AudioTimeAxisView*> (drag_info.dest_trackview);
+
        if (atv && atv->get_diskstream()) {
                speed = atv->get_diskstream()->speed();
        }
+
+       /* check all regions for motion because some might have been locked */
        
-       regionview_x_movement = (drag_info.last_frame_position != (nframes_t) (rv->region()->position()/speed));
-       regionview_y_movement = (drag_info.last_trackview != &rv->get_time_axis_view());
+       regionview_x_movement = false;
+       regionview_y_movement = false;
+
+       for (list<RegionView*>::const_iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
+               if (drag_info.last_frame_position != (nframes_t) ((*i)->region()->position()/speed)) {
+                       regionview_x_movement = true;
+               }
+               
+               if (drag_info.dest_trackview != &(*i)->get_time_axis_view()) {
+                       regionview_y_movement = true;
+               }
+       }
 
        //printf ("last_frame: %s position is %lu  %g\n", rv->get_time_axis_view().name().c_str(), drag_info.last_frame_position, speed); 
-       //printf ("last_rackview: %s \n", drag_info.last_trackview->name().c_str()); 
+       //printf ("last_rackview: %s \n", drag_info.dest_trackview->name().c_str()); 
        
        char* op_string;
 
@@ -3673,19 +3716,29 @@ 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);              
+                       
+                       if (rv->region()->locked()) {
+                               ++i;
+                               continue;
+                       }
 
                        double ix1, ix2, iy1, iy2;
-                       
                        rv->get_canvas_frame()->get_bounds (ix1, iy1, ix2, iy2);
                        rv->get_canvas_group()->i2w (ix1, iy1);
                        TimeAxisView* tvp2 = trackview_by_y_position (iy1);
                        AudioTimeAxisView* atv2 = dynamic_cast<AudioTimeAxisView*>(tvp2);
 
-                       boost::shared_ptr<Playlist> from_playlist = rv->region()->playlist();
                        boost::shared_ptr<Playlist> to_playlist = atv2->playlist();
 
                        where = (nframes_t) (unit_to_frame (ix1) * speed);
-                       boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region()));
+                       boost::shared_ptr<Region> new_region;
+
+                       if (drag_info.copy) {
+                               /* we already made a copy */
+                               new_region = rv->region();
+                       } else {
+                               new_region = RegionFactory::create (rv->region());
+                       }
 
                        /* undo the previous hide_dependent_views so that xfades don't
                           disappear on copying regions 
@@ -3797,31 +3850,21 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event
                                where = rv->region()->position();
                        }
 
-                       boost::shared_ptr<Playlist> to_playlist = rv->region()->playlist();
-
-                       assert (to_playlist);
-
                        /* add the undo */
 
-                       session->add_command (new MementoCommand<Playlist>(*to_playlist, &to_playlist->get_state(), 0));        
-
+                       session->add_command (new MementoCommand<Playlist>(*from_playlist, &from_playlist->get_state(), 0));    
+                       
                        if (drag_info.copy) {
 
-                               boost::shared_ptr<Region> newregion;
-                               boost::shared_ptr<Region> ar;
+                               /* we already made a copy of the region */
 
-                               if ((ar = boost::dynamic_pointer_cast<AudioRegion>(rv->region())) != 0) {
-                                       newregion = RegionFactory::create (ar);
-                               } else {
-                                       /* XXX MIDI HERE drobilla */
-                                       continue;
-                               }
+                               boost::shared_ptr<Region> newregion = rv->region();
 
                                /* add it */
 
                                latest_regionviews.clear ();
                                sigc::connection c = atv->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
-                               to_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed()));
+                               from_playlist->add_region (newregion, (nframes_t) (where * atv->get_diskstream()->speed()));
                                c.disconnect ();
 
                                if (!latest_regionviews.empty()) {
@@ -3844,7 +3887,7 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event
 
                        /* add the redo */
 
-                       session->add_command (new MementoCommand<Playlist>(*to_playlist, 0, &to_playlist->get_state()));
+                       session->add_command (new MementoCommand<Playlist>(*from_playlist, 0, &from_playlist->get_state()));
 
                        if (drag_info.copy) {
                                copies.push_back (rv);
@@ -4109,7 +4152,8 @@ Editor::start_selection_grab (ArdourCanvas::Item* item, GdkEvent* event)
 
        start_grab (event);
        
-       drag_info.last_trackview = clicked_trackview;
+       drag_info.source_trackview = clicked_trackview;
+       drag_info.dest_trackview = drag_info.source_trackview;
        drag_info.last_frame_position = latest_regionviews.front()->region()->position();
        drag_info.pointer_frame_offset = drag_info.grab_frame - drag_info.last_frame_position;
        
index ba6984088d56f9d73e2dfc40ab3bda7f48a2c7eb..1673e8ca073489a0be6858f27e74df8f65c003e4 100644 (file)
@@ -73,6 +73,7 @@ class GainMeter : public Gtk::VBox
 
        void set_meter_strip_name (const char * name);
        void set_fader_name (const char * name);
+       PBD::Controllable& get_controllable() { return _io->gain_control(); }
 
        void clear_meters ();
 
index 3484fddd38160180b27401f6b95959f69b5c7af0..17871de7d5d9bda3833ee9a6f291285afd560196 100644 (file)
@@ -223,6 +223,8 @@ Mixer_UI::Mixer_UI ()
        signal_configure_event().connect (mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
 
        _selection.RoutesChanged.connect (mem_fun(*this, &Mixer_UI::follow_strip_selection));
+
+       auto_rebinding = FALSE;
 }
 
 Mixer_UI::~Mixer_UI ()
@@ -712,8 +714,97 @@ Mixer_UI::redisplay_track_list ()
                Route::SyncOrderKeys (); // EMIT SIGNAL
                ignore_sync = false;
        }
+
+       // Rebind all of the midi controls automatically
+       
+       if (auto_rebinding)
+               auto_rebind_midi_controls ();
+
+}
+
+void
+Mixer_UI::set_auto_rebinding( bool val )
+{
+       if( val == TRUE )
+       {
+               auto_rebinding = TRUE;
+               Session::AutoBindingOff();
+       }
+       else
+       {
+               auto_rebinding = FALSE;
+               Session::AutoBindingOn();
+       }
 }
 
+void 
+Mixer_UI::toggle_auto_rebinding() 
+{
+       if (auto_rebinding)
+       {
+               set_auto_rebinding( FALSE );
+       }
+       
+       else
+       {
+               set_auto_rebinding( TRUE );
+       }
+
+       auto_rebind_midi_controls();
+}
+
+void 
+Mixer_UI::auto_rebind_midi_controls () 
+{
+       TreeModel::Children rows = track_model->children();
+       TreeModel::Children::iterator i;
+       int pos;
+
+       // Create bindings for all visible strips and remove those that are not visible
+       pos = 1;  // 0 is reserved for the master strip
+       for (i = rows.begin(); i != rows.end(); ++i) {
+               MixerStrip* strip = (*i)[track_columns.strip];
+    
+               if ( (*i)[track_columns.visible] == true ) {  // add bindings for
+                       // make the actual binding
+                       //cout<<"Auto Binding:  Visible Strip Found: "<<strip->name()<<endl;
+
+                       int controlValue = pos;
+                       if( strip->route()->master() ) {
+                               controlValue = 0;
+                       }
+                       else {
+                               pos++;
+                       }
+
+                       PBD::Controllable::CreateBinding ( strip->solo_button->get_controllable(), controlValue, 0);
+                       PBD::Controllable::CreateBinding ( strip->mute_button->get_controllable(), controlValue, 1);
+
+                       if( strip->is_audio_track() ) {
+                               PBD::Controllable::CreateBinding ( strip->rec_enable_button->get_controllable(), controlValue, 2);
+                       }
+
+                       PBD::Controllable::CreateBinding ( &(strip->gpm.get_controllable()), controlValue, 3);
+                       PBD::Controllable::CreateBinding ( strip->panners.get_controllable(), controlValue, 4);
+
+               }
+               else {  // Remove any existing binding
+                       PBD::Controllable::DeleteBinding ( strip->solo_button->get_controllable() );
+                       PBD::Controllable::DeleteBinding ( strip->mute_button->get_controllable() );
+
+                       if( strip->is_audio_track() ) {
+                               PBD::Controllable::DeleteBinding ( strip->rec_enable_button->get_controllable() );
+                       }
+
+                       PBD::Controllable::DeleteBinding ( &(strip->gpm.get_controllable()) );
+                       PBD::Controllable::DeleteBinding ( strip->panners.get_controllable() ); // This only takes the first panner if there are multiples...
+               }
+
+       } // for
+  
+}
+
+
 struct SignalOrderRouteSorter {
     bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
            /* use of ">" forces the correct sort order */
@@ -1069,6 +1160,7 @@ Mixer_UI::mix_group_row_change (const Gtk::TreeModel::Path& path,const Gtk::Tree
        if (name != group->name()) {
                group->set_name (name);
        }
+
 }
 
 void
index 14884bfeedb6cd1be03bb59b413fbc29367b8133..06a16519ccb67d23229fa64920cd8536a1aaf70d 100644 (file)
@@ -75,6 +75,8 @@ class Mixer_UI : public Gtk::Window
        void hide_strip (MixerStrip *);
 
        void ensure_float (Gtk::Window&);
+       void toggle_auto_rebinding ();
+       void set_auto_rebinding(bool);
 
        RouteRedirectSelection& selection() { return _selection; }
        
@@ -129,6 +131,9 @@ class Mixer_UI : public Gtk::Window
        void unselect_all_audiobus_strips ();
        void select_all_audiobus_strips ();
 
+       void auto_rebind_midi_controls ();
+       bool auto_rebinding;
+
        void strip_select_op (bool audiotrack, bool select);
        void select_strip_op (MixerStrip*, bool select);
 
index 4ad59fb1ea5af10438850a1a1392428eeb47650f..fcbaf8d8bb82dce16435be8a31df191b221a73af 100644 (file)
@@ -152,6 +152,12 @@ PannerUI::PannerUI (boost::shared_ptr<IO> io, Session& s)
        pan_automation_state_changed ();
 }
 
+PBD::Controllable* 
+PannerUI::get_controllable() 
+{ 
+       return pan_bars[0]->get_controllable();
+}
+
 bool
 PannerUI::panning_link_button_press (GdkEventButton* ev)
 {
index d587359ecdf62ba74e80d70df88ed217290c9472..093800387dfb1a447253ea268a16d1f1d621969d 100644 (file)
@@ -68,9 +68,11 @@ class PannerUI : public Gtk::HBox
        void effective_pan_display ();
 
        void set_meter_strip_name (string name);
+       PBD::Controllable* get_controllable();
 
   private:
        friend class MixerStrip;
+
        boost::shared_ptr<ARDOUR::IO> _io;
        ARDOUR::Session& _session;
 
index 89e78b44b917e75362e0145ad7a838190026bb7e..bb16637d6ad9a1e9f2d2c5b623c2795ada9f7142 100644 (file)
@@ -93,6 +93,23 @@ RegionView::RegionView (const RegionView& other)
        _height = other._height;
 }
 
+RegionView::RegionView (const RegionView& other, boost::shared_ptr<Region> other_region)
+       : TimeAxisViewItem (other)
+{
+       /* this is a pseudo-copy constructor used when dragging regions 
+          around on the canvas.
+       */
+
+       /* derived concrete type will call init () */
+
+       _region = other_region;
+       editor = other.editor;
+       current_visible_sync_position = other.current_visible_sync_position;
+       valid = false;
+       _pixel_width = other._pixel_width;
+       _height = other._height;
+}
+
 RegionView::RegionView (ArdourCanvas::Group*         parent, 
                         TimeAxisView&                tv,
                         boost::shared_ptr<ARDOUR::Region> r,
index 4f160c5a58d3f14e7c21f039ddab5440b0393d99..bc5771e848c5898630ef24d190471e8bfa26b4ec 100644 (file)
@@ -47,6 +47,7 @@ class RegionView : public TimeAxisViewItem
                    Gdk::Color&          basic_color);
 
        RegionView (const RegionView& other);
+       RegionView (const RegionView& other, boost::shared_ptr<ARDOUR::Region> other_region);
 
        ~RegionView ();
        
index b944202c36bf5bb438586cf77ccc4030d4f0f2c8..ada5afcd9c102d01c1acf8353bc0d5e1e3b0db64 100644 (file)
@@ -42,6 +42,12 @@ class RegionFactory {
 
        static sigc::signal<void,boost::shared_ptr<Region> > CheckNewRegion;
 
+       static boost::shared_ptr<Region> create (boost::shared_ptr<const Region>);
+
+       /* note: both of the first two should use const shared_ptr as well, but
+          gcc 4.1 doesn't seem to be able to disambiguate them if they do.
+       */
+
        static boost::shared_ptr<Region> create (boost::shared_ptr<Region>, nframes_t start, 
                                                 nframes_t length, std::string name, 
                                                 layer_t = 0, Region::Flag flags = Region::DefaultFlags, bool announce = true);
@@ -50,7 +56,6 @@ class RegionFactory {
                                                 layer_t = 0, Region::Flag flags = Region::DefaultFlags, bool announce = true);
        static boost::shared_ptr<Region> create (boost::shared_ptr<Source>, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags, bool announce = true);
        static boost::shared_ptr<Region> create (const SourceList &, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags, bool announce = true);
-       static boost::shared_ptr<Region> create (boost::shared_ptr<Region>);
        static boost::shared_ptr<Region> create (Session&, XMLNode&, bool);
        static boost::shared_ptr<Region> create (SourceList &, const XMLNode&);
 };
index 76590368f8fbf2e153e6cf670925fae94505d162..fa50212f3a6dfc2125bb989f4ff0fa5bf21dd968 100644 (file)
@@ -249,6 +249,9 @@ class Session : public PBD::StatefulDestructible
        bool deletion_in_progress() const { return _state_of_the_state & Deletion; }
        sigc::signal<void> DirtyChanged;
 
+       static sigc::signal<void> AutoBindingOn;
+       static sigc::signal<void> AutoBindingOff;
+
        std::string sound_dir (bool with_path = true) const;
        std::string peak_dir () const;
        std::string dead_sound_dir () const;
index 5cab80280142a3631607419ea309b71d966f93db..163cb07c6c5a2b769e6c9bb954738ccf4fba0acd 100644 (file)
@@ -35,8 +35,8 @@ sigc::signal<void,boost::shared_ptr<Region> > RegionFactory::CheckNewRegion;
 
 boost::shared_ptr<Region>
 RegionFactory::create (boost::shared_ptr<Region> region, nframes_t start, 
-                            nframes_t length, std::string name, 
-                            layer_t layer, Region::Flag flags, bool announce)
+                      nframes_t length, std::string name, 
+                      layer_t layer, Region::Flag flags, bool announce)
 {
        boost::shared_ptr<const AudioRegion> other;
 
@@ -57,11 +57,11 @@ RegionFactory::create (boost::shared_ptr<Region> region, nframes_t start,
 }
 
 boost::shared_ptr<Region>
-RegionFactory::create (boost::shared_ptr<Region> region)
+RegionFactory::create (boost::shared_ptr<const Region> region)
 {
-       boost::shared_ptr<AudioRegion> other;
+       boost::shared_ptr<const AudioRegion> other;
        
-       if ((other = boost::dynamic_pointer_cast<AudioRegion>(region)) != 0) {
+       if ((other = boost::dynamic_pointer_cast<const AudioRegion>(region)) != 0) {
                boost::shared_ptr<Region> ret (new AudioRegion (other));
                /* pure copy constructor - no CheckNewRegion emitted */
                return ret;
@@ -75,8 +75,8 @@ RegionFactory::create (boost::shared_ptr<Region> region)
 
 boost::shared_ptr<Region>
 RegionFactory::create (boost::shared_ptr<AudioRegion> region, nframes_t start, 
-                            nframes_t length, std::string name, 
-                            layer_t layer, Region::Flag flags, bool announce)
+                      nframes_t length, std::string name, 
+                      layer_t layer, Region::Flag flags, bool announce)
 {
        return create (boost::static_pointer_cast<Region> (region), start, length, name, layer, flags, announce);
 }
index d53291b1780ef679560d607f718c33db331e8119..d7c3c95d9d32c552b4feb98619471973932e024e 100644 (file)
@@ -113,6 +113,10 @@ sigc::signal<void> Session::SMPTEOffsetChanged;
 sigc::signal<void> Session::StartTimeChanged;
 sigc::signal<void> Session::EndTimeChanged;
 
+sigc::signal<void> Session::AutoBindingOn;
+sigc::signal<void> Session::AutoBindingOff;
+
+
 int
 Session::find_session (string str, string& path, string& snapshot, bool& isnew)
 {
index c91f4c8a06a495973f499b33a760ac08f95a4a90..d149ded527691be1566215088674e50263c08953 100644 (file)
@@ -59,6 +59,7 @@ class BarController : public Gtk::Frame
        /* export this to allow direct connection to button events */
 
        Gtk::Widget& event_widget() { return darea; }
+       PBD::Controllable* get_controllable() { return binding_proxy.get_controllable(); }
 
   protected:
        Gtk::Adjustment&    adjustment;
index 2ddd3628fc30177933920542af3e02d0b4a42642..1cde32c5bab3da9f613bb4fe73f345a5fb0249ce 100644 (file)
@@ -47,7 +47,8 @@ class BindableToggleButton : public Gtkmm2ext::StatefulToggleButton
                        return true;
                }
        }
-
+       
+       PBD::Controllable* get_controllable() { return binding_proxy.get_controllable(); }
   private:
        BindingProxy binding_proxy;
 };
@@ -71,6 +72,8 @@ class BindableButton : public Gtkmm2ext::StatefulButton
                }
        }
 
+       PBD::Controllable* get_controllable() { return binding_proxy.get_controllable(); }
+
   private:
        BindingProxy binding_proxy;
 };
index d8f37c7649eeb0727b34a7b6b11bcc2b83d76cd1..dd9b94319df27df59d3257a1051d172d158e3c1d 100644 (file)
@@ -40,6 +40,7 @@ class BindingProxy : public sigc::trackable
 
        bool button_press_handler (GdkEventButton *);
 
+       PBD::Controllable* get_controllable() { return &controllable; }
   protected:
 
        Gtkmm2ext::PopUp*  prompter;
index bf0f7f5c6db954db83c367c8fecd1719650e1868..6a0028668b67fffce6db2355eb6bfceca69192e9 100644 (file)
@@ -28,6 +28,8 @@ using namespace PBD;
 sigc::signal<void,Controllable*> Controllable::Destroyed;
 sigc::signal<bool,Controllable*> Controllable::StartLearning;
 sigc::signal<void,Controllable*> Controllable::StopLearning;
+sigc::signal<void,Controllable*,int,int> Controllable::CreateBinding;
+sigc::signal<void,Controllable*> Controllable::DeleteBinding;
 
 Glib::Mutex* Controllable::registry_lock = 0;
 Controllable::Controllables Controllable::registry;
index 85f09726caba27f81650448afda7d1d326698069..17ee8f1a6f4e594498a7764122c54278331fd11b 100644 (file)
@@ -44,6 +44,8 @@ class Controllable : public PBD::StatefulDestructible {
        virtual bool can_send_feedback() const { return true; }
 
        sigc::signal<void> LearningFinished;
+       static sigc::signal<void,PBD::Controllable*,int,int> CreateBinding;
+       static sigc::signal<void,PBD::Controllable*> DeleteBinding;
 
        static sigc::signal<bool,PBD::Controllable*> StartLearning;
        static sigc::signal<void,PBD::Controllable*> StopLearning;
index 720711d94b1908239c56a16b3c93a4dd21cab3da..90b6b4bda95f4d5f2c9dd8d6b2235da7235f33c2 100644 (file)
@@ -56,9 +56,17 @@ GenericMidiControlProtocol::GenericMidiControlProtocol (Session& s)
        _feedback_interval = 10000; // microseconds
        last_feedback_time = 0;
 
+       auto_binding = FALSE;
+
        Controllable::StartLearning.connect (mem_fun (*this, &GenericMidiControlProtocol::start_learning));
        Controllable::StopLearning.connect (mem_fun (*this, &GenericMidiControlProtocol::stop_learning));
        Session::SendFeedback.connect (mem_fun (*this, &GenericMidiControlProtocol::send_feedback));
+       
+       Controllable::CreateBinding.connect (mem_fun (*this, &GenericMidiControlProtocol::create_binding));
+       Controllable::DeleteBinding.connect (mem_fun (*this, &GenericMidiControlProtocol::delete_binding));
+
+       Session::AutoBindingOn.connect (mem_fun (*this, &GenericMidiControlProtocol::auto_binding_on));
+       Session::AutoBindingOff.connect (mem_fun (*this, &GenericMidiControlProtocol::auto_binding_off));
 }
 
 GenericMidiControlProtocol::~GenericMidiControlProtocol ()
@@ -225,6 +233,71 @@ GenericMidiControlProtocol::stop_learning (Controllable* c)
        }
 }
 
+void
+GenericMidiControlProtocol::delete_binding ( PBD::Controllable* control )
+{
+       if( control != 0 ) {
+               Glib::Mutex::Lock lm2 (controllables_lock);
+               
+               for( MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ++iter) {
+                       MIDIControllable* existingBinding = (*iter);
+                       
+                       if( control == &(existingBinding->get_controllable()) ) {
+                               delete existingBinding;
+                               controllables.erase (iter);
+                       }
+                       
+               } // end for midi controllables
+       } // end null check
+}
+void
+GenericMidiControlProtocol::create_binding (PBD::Controllable* control, int pos, int control_number)
+{
+       if( control != NULL ) {
+               Glib::Mutex::Lock lm2 (controllables_lock);
+               
+               MIDI::channel_t channel = (pos & 0xf);
+               MIDI::byte value = control_number;
+               
+               // Create a MIDIControllable::
+               MIDIControllable* mc = new MIDIControllable (*_port, *control);
+               
+               // Remove any old binding for this midi channel/type/value pair
+               // Note:  can't use delete_binding() here because we don't know the specific controllable we want to remove, only the midi information
+               for( MIDIControllables::iterator iter = controllables.begin(); iter != controllables.end(); ++iter) {
+                       MIDIControllable* existingBinding = (*iter);
+                       
+                       if( (existingBinding->get_control_channel() & 0xf ) == channel &&
+                           existingBinding->get_control_additional() == value &&
+                           (existingBinding->get_control_type() & 0xf0 ) == MIDI::controller ) {
+                               
+                               delete existingBinding;
+                               controllables.erase (iter);
+                       }
+                       
+               } // end for midi controllables
+               
+               
+               // Update the MIDI Controllable based on the the pos param
+               // Here is where a table lookup for user mappings could go; for now we'll just wing it...
+               mc->bind_midi( channel, MIDI::controller, value );
+               
+               controllables.insert (mc);
+       } // end null test
+}
+
+void
+GenericMidiControlProtocol::auto_binding_on()
+{
+       auto_binding = TRUE;
+}
+
+void
+GenericMidiControlProtocol::auto_binding_off()
+{
+       auto_binding = FALSE;
+}
+
 XMLNode&
 GenericMidiControlProtocol::get_state () 
 {
@@ -269,46 +342,47 @@ GenericMidiControlProtocol::set_state (const XMLNode& node)
                _feedback_interval = 10000;
        }
 
-       Controllable* c;
-
-       {
-               Glib::Mutex::Lock lm (pending_lock);
-               pending_controllables.clear ();
-       }
-
-       Glib::Mutex::Lock lm2 (controllables_lock);
-
-       controllables.clear ();
-
-       nlist = node.children(); // "controls"
-
-       if (nlist.empty()) {
-               return 0;
-       }
-
-       nlist = nlist.front()->children ();
-
-       for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
-
-               if ((prop = (*niter)->property ("id")) != 0) {
-                       
-                       ID id = prop->value ();
-                       
-                       c = Controllable::by_id (id);
+       // Are we using the autobinding feature?  If so skip this part
+       if ( !auto_binding ) {
+               
+               Controllable* c;
+               
+               {
+                       Glib::Mutex::Lock lm (pending_lock);
+                       pending_controllables.clear ();
+               }
+               
+               Glib::Mutex::Lock lm2 (controllables_lock);
+               controllables.clear ();
+               nlist = node.children(); // "controls"
+               
+               if (nlist.empty()) {
+                       return 0;
+               }
+               
+               nlist = nlist.front()->children ();
+               
+               for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
                        
-                       if (c) {
-                               MIDIControllable* mc = new MIDIControllable (*_port, *c);
-                               if (mc->set_state (**niter) == 0) {
-                                       controllables.insert (mc);
-                               }
+                       if ((prop = (*niter)->property ("id")) != 0) {
+
+                               ID id = prop->value ();
+                               c = session->controllable_by_id (id);
                                
-                       } else {
-                               warning << string_compose (_("Generic MIDI control: controllable %1 not found (ignored)"), id)
-                                       << endmsg;
+                               if (c) {
+                                       MIDIControllable* mc = new MIDIControllable (*_port, *c);
+                                       if (mc->set_state (**niter) == 0) {
+                                               controllables.insert (mc);
+                                       }
+                                       
+                               } else {
+                                       warning << string_compose (_("Generic MIDI control: controllable %1 not found in session (ignored)"),
+                                                                  id)
+                                               << endmsg;
+                               }
                        }
                }
-       }
-
+       } // end autobinding check
        return 0;
 }
 
index 95aeb77cdbe81b71e17e97bcfb98076d6ebee690..dfff8810d1449e54dd113c2eed2957b88eff826e 100644 (file)
@@ -44,6 +44,7 @@ class GenericMidiControlProtocol : public ARDOUR::ControlProtocol {
        ARDOUR::microseconds_t last_feedback_time;
 
        bool  do_feedback;
+       bool  auto_binding;
        void _send_feedback ();
        void  send_feedback ();
 
@@ -59,6 +60,13 @@ class GenericMidiControlProtocol : public ARDOUR::ControlProtocol {
        void stop_learning (PBD::Controllable*);
 
        void learning_stopped (MIDIControllable*);
+
+       void create_binding (PBD::Controllable*, int, int);
+       void delete_binding (PBD::Controllable*);
+
+       void auto_binding_on();
+       void auto_binding_off();
+
 };
 
 #endif /* ardour_generic_midi_control_protocol_h */
index 4bac325feb6f34c460325ec83edfbc20778e3e49..df6eea396c4862d544ac56e55f692987329f8f7c 100644 (file)
@@ -63,6 +63,10 @@ class MIDIControllable : public Stateful
        XMLNode& get_state (void);
        int set_state (const XMLNode&);
 
+       void bind_midi (MIDI::channel_t, MIDI::eventType, MIDI::byte);
+       MIDI::channel_t get_control_channel () { return control_channel; }
+       MIDI::eventType get_control_type () { return control_type; }
+       MIDI::byte get_control_additional () { return control_additional; }
   private:
        PBD::Controllable& controllable;
        MIDI::Port&     _port;
@@ -86,8 +90,6 @@ class MIDIControllable : public Stateful
        void midi_sense_controller (MIDI::Parser &, MIDI::EventTwoBytes *);
        void midi_sense_program_change (MIDI::Parser &, MIDI::byte);
        void midi_sense_pitchbend (MIDI::Parser &, MIDI::pitchbend_t);
-
-       void bind_midi (MIDI::channel_t, MIDI::eventType, MIDI::byte);
 };
 
 #endif // __gm_midicontrollable_h__