Fix #2559; crash when doing end_grab on an already-deleted canvas item.
[ardour.git] / gtk2_ardour / editor.cc
index 6f7a5eb13294338b0161f37bea82cacbd35defc5..86efc2069c90fb4d84d5b66717b5d6c38290f530 100644 (file)
 
 #include <sigc++/bind.h>
 
-#include <pbd/convert.h>
-#include <pbd/error.h>
-#include <pbd/enumwriter.h>
-#include <pbd/memento_command.h>
+#include "pbd/convert.h"
+#include "pbd/error.h"
+#include "pbd/enumwriter.h"
+#include "pbd/memento_command.h"
 
 #include <glibmm/miscutils.h>
 #include <gtkmm/image.h>
 #include <gtkmm2ext/window_title.h>
 #include <gtkmm2ext/choice.h>
 
-#include <ardour/audio_track.h>
-#include <ardour/audio_diskstream.h>
-#include <ardour/plugin_manager.h>
-#include <ardour/location.h>
-#include <ardour/audioplaylist.h>
-#include <ardour/audioregion.h>
-#include <ardour/midi_region.h>
-#include <ardour/session_route.h>
-#include <ardour/session_directory.h>
-#include <ardour/session_state_utils.h>
-#include <ardour/tempo.h>
-#include <ardour/utils.h>
-#include <ardour/profile.h>
-
-#include <control_protocol/control_protocol.h>
+#include "ardour/audio_diskstream.h"
+#include "ardour/audio_track.h"
+#include "ardour/audioplaylist.h"
+#include "ardour/audioregion.h"
+#include "ardour/location.h"
+#include "ardour/midi_region.h"
+#include "ardour/plugin_manager.h"
+#include "ardour/profile.h"
+#include "ardour/route_group.h"
+#include "ardour/session_directory.h"
+#include "ardour/session_route.h"
+#include "ardour/session_state_utils.h"
+#include "ardour/tempo.h"
+#include "ardour/utils.h"
+
+#include "control_protocol/control_protocol.h"
 
 #include "ardour_ui.h"
 #include "editor.h"
@@ -87,6 +88,8 @@
 #include "actions.h"
 #include "tempo_lines.h"
 #include "analysis_window.h"
+#include "bundle_manager.h"
+#include "global_port_matrix.h"
 
 #include "i18n.h"
 
@@ -245,7 +248,8 @@ Editor::Editor ()
          /* nudge */
 
          nudge_clock (X_("nudge"), false, X_("NudgeClock"), true, true),
-         meters_running(false)
+         meters_running(false),
+         _pending_locate_request (false)
 
 {
        constructed = false;
@@ -347,6 +351,10 @@ Editor::Editor ()
        select_new_marker = false;
        zoomed_to_region = false;
        rhythm_ferret = 0;
+       _bundle_manager = 0;
+        for (ARDOUR::DataType::iterator i = ARDOUR::DataType::begin(); i != ARDOUR::DataType::end(); ++i) {
+               _global_port_matrix[*i] = 0;
+       }
        allow_vertical_scroll = false;
        no_save_visual = false;
        need_resize_line = false;
@@ -446,7 +454,7 @@ Editor::Editor ()
 
        selection->TimeChanged.connect (mem_fun(*this, &Editor::time_selection_changed));
        selection->TracksChanged.connect (mem_fun(*this, &Editor::track_selection_changed));
-       selection->RegionsChanged.connect (mem_fun(*this, &Editor::region_selection_changed));
+       editor_regions_selection_changed_connection = selection->RegionsChanged.connect (mem_fun(*this, &Editor::region_selection_changed));
        selection->PointsChanged.connect (mem_fun(*this, &Editor::point_selection_changed));
        selection->MarkersChanged.connect (mem_fun(*this, &Editor::marker_selection_changed));
 
@@ -667,7 +675,7 @@ Editor::Editor ()
        region_list_display.append_column (_("Used"), region_list_columns.used);
        region_list_display.append_column (_("Path"), region_list_columns.path);
        region_list_display.set_headers_visible (true);
-       region_list_display.set_grid_lines (TREE_VIEW_GRID_LINES_BOTH);
+       //region_list_display.set_grid_lines (TREE_VIEW_GRID_LINES_BOTH);
        
        CellRendererText* region_name_cell = dynamic_cast<CellRendererText*>(region_list_display.get_column_cell_renderer (0));
        region_name_cell->property_editable() = true;
@@ -701,7 +709,7 @@ Editor::Editor ()
        region_list_display.signal_key_release_event().connect (mem_fun(*this, &Editor::region_list_display_key_release));
        region_list_display.signal_button_press_event().connect (mem_fun(*this, &Editor::region_list_display_button_press), false);
        region_list_display.signal_button_release_event().connect (mem_fun(*this, &Editor::region_list_display_button_release));
-       region_list_display.get_selection()->signal_changed().connect (mem_fun(*this, &Editor::region_list_selection_changed));
+       region_list_change_connection = region_list_display.get_selection()->signal_changed().connect (mem_fun(*this, &Editor::region_list_selection_changed));
        // region_list_display.signal_popup_menu().connect (bind (mem_fun (*this, &Editor::show_region_list_display_context_menu), 1, 0));
        
        //ARDOUR_UI::instance()->secondary_clock.mode_changed.connect (mem_fun(*this, &Editor::redisplay_regions));
@@ -885,10 +893,8 @@ Editor::~Editor()
        }
 #endif
 
-       if (track_canvas) {
-               delete track_canvas;
-               track_canvas = 0;
-       }
+       delete track_canvas;
+       track_canvas = 0;
 }
 
 void
@@ -905,6 +911,10 @@ Editor::catch_vanishing_regionview (RegionView *rv)
           audioregionview by itself.
        */
 
+       if (rv->get_canvas_group() == drag_info.item) {
+               end_grab (drag_info.item, 0);
+       }
+
        if (clicked_regionview == rv) {
                clicked_regionview = 0;
        }
@@ -1275,6 +1285,8 @@ Editor::connect_to_session (Session *t)
 
        session_connections.push_back (session->tempo_map().StateChanged.connect (mem_fun(*this, &Editor::tempo_map_changed)));
 
+       session_connections.push_back (session->Located.connect (mem_fun (*this, &Editor::located)));
+
        edit_groups_changed ();
 
        edit_point_clock.set_mode(AudioClock::BBT);
@@ -2131,42 +2143,39 @@ void
 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
 {
        using namespace Menu_Helpers;
-       Menu     *selection_menu = manage (new Menu);
-       MenuList& items       = selection_menu->items();
-       selection_menu->set_name ("ArdourContextMenu");
 
-       items.push_back (MenuElem (_("Play range"), mem_fun(*this, &Editor::play_selection)));
-       items.push_back (MenuElem (_("Loop range"), bind (mem_fun(*this, &Editor::set_loop_from_selection), true)));
+       edit_items.push_back (MenuElem (_("Play range"), mem_fun(*this, &Editor::play_selection)));
+       edit_items.push_back (MenuElem (_("Loop range"), bind (mem_fun(*this, &Editor::set_loop_from_selection), true)));
 
-       items.push_back (SeparatorElem());
-       items.push_back (MenuElem (_("Spectral Analysis"), mem_fun(*this, &Editor::analyze_range_selection)));
+       edit_items.push_back (SeparatorElem());
+       edit_items.push_back (MenuElem (_("Spectral Analysis"), mem_fun(*this, &Editor::analyze_range_selection)));
        
-       items.push_back (SeparatorElem());
-       items.push_back (MenuElem (_("Extend Range to End of Region"), bind (mem_fun(*this, &Editor::extend_selection_to_end_of_region), false)));
-       items.push_back (MenuElem (_("Extend Range to Start of Region"), bind (mem_fun(*this, &Editor::extend_selection_to_start_of_region), false)));
+       edit_items.push_back (SeparatorElem());
+       edit_items.push_back (MenuElem (_("Extend Range to End of Region"), bind (mem_fun(*this, &Editor::extend_selection_to_end_of_region), false)));
+       edit_items.push_back (MenuElem (_("Extend Range to Start of Region"), bind (mem_fun(*this, &Editor::extend_selection_to_start_of_region), false)));
 
-       items.push_back (SeparatorElem());
-       items.push_back (MenuElem (_("Convert to region in-place"), mem_fun(*this, &Editor::separate_region_from_selection)));
-       items.push_back (MenuElem (_("Convert to region in region list"), mem_fun(*this, &Editor::new_region_from_selection)));
+       edit_items.push_back (SeparatorElem());
+       edit_items.push_back (MenuElem (_("Convert to region in-place"), mem_fun(*this, &Editor::separate_region_from_selection)));
+       edit_items.push_back (MenuElem (_("Convert to region in region list"), mem_fun(*this, &Editor::new_region_from_selection)));
        
-       items.push_back (SeparatorElem());
-       items.push_back (MenuElem (_("Select all in range"), mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
+       edit_items.push_back (SeparatorElem());
+       edit_items.push_back (MenuElem (_("Select all in range"), mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
 
-       items.push_back (SeparatorElem());
-       items.push_back (MenuElem (_("Set loop from selection"), bind (mem_fun(*this, &Editor::set_loop_from_selection), false)));
-       items.push_back (MenuElem (_("Set punch from selection"), mem_fun(*this, &Editor::set_punch_from_selection)));
+       edit_items.push_back (SeparatorElem());
+       edit_items.push_back (MenuElem (_("Set loop from selection"), bind (mem_fun(*this, &Editor::set_loop_from_selection), false)));
+       edit_items.push_back (MenuElem (_("Set punch from selection"), mem_fun(*this, &Editor::set_punch_from_selection)));
        
-       items.push_back (SeparatorElem());
-       items.push_back (MenuElem (_("Add Range Markers"), mem_fun (*this, &Editor::add_location_from_selection)));
-       items.push_back (SeparatorElem());
-       items.push_back (MenuElem (_("Crop region to range"), mem_fun(*this, &Editor::crop_region_to_selection)));
-       items.push_back (MenuElem (_("Fill range with region"), mem_fun(*this, &Editor::region_fill_selection)));
-       items.push_back (MenuElem (_("Duplicate range"), bind (mem_fun(*this, &Editor::duplicate_dialog), false)));
-       items.push_back (MenuElem (_("Create chunk from range"), mem_fun(*this, &Editor::create_named_selection)));
-       items.push_back (SeparatorElem());
-       items.push_back (MenuElem (_("Consolidate range"), bind (mem_fun(*this, &Editor::bounce_range_selection), true)));
-       items.push_back (MenuElem (_("Bounce range to region list"), bind (mem_fun(*this, &Editor::bounce_range_selection), false)));
-       items.push_back (MenuElem (_("Export range"), mem_fun(*this, &Editor::export_range)));
+       edit_items.push_back (SeparatorElem());
+       edit_items.push_back (MenuElem (_("Add Range Markers"), mem_fun (*this, &Editor::add_location_from_selection)));
+       edit_items.push_back (SeparatorElem());
+       edit_items.push_back (MenuElem (_("Crop region to range"), mem_fun(*this, &Editor::crop_region_to_selection)));
+       edit_items.push_back (MenuElem (_("Fill range with region"), mem_fun(*this, &Editor::region_fill_selection)));
+       edit_items.push_back (MenuElem (_("Duplicate range"), bind (mem_fun(*this, &Editor::duplicate_dialog), false)));
+       edit_items.push_back (MenuElem (_("Create chunk from range"), mem_fun(*this, &Editor::create_named_selection)));
+       edit_items.push_back (SeparatorElem());
+       edit_items.push_back (MenuElem (_("Consolidate range"), bind (mem_fun(*this, &Editor::bounce_range_selection), true)));
+       edit_items.push_back (MenuElem (_("Bounce range to region list"), bind (mem_fun(*this, &Editor::bounce_range_selection), false)));
+       edit_items.push_back (MenuElem (_("Export range"), mem_fun(*this, &Editor::export_range)));
 }
 
        
@@ -2724,19 +2733,23 @@ Editor::get_state ()
 
 
 
-TimeAxisView *
+/** @param y y offset from the top of all trackviews.
+ *  @return pair: TimeAxisView that y is over, layer index.
+ *  TimeAxisView may be 0.  Layer index is the layer number if the TimeAxisView is valid and is
+ *  in stacked region display mode, otherwise 0.
+ */
+std::pair<TimeAxisView *, layer_t>
 Editor::trackview_by_y_position (double y)
 {
        for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
 
-               TimeAxisView *tv;
-
-               if ((tv = (*iter)->covers_y_position (y)) != 0) {
-                       return tv;
+               std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
+               if (r.first) {
+                       return r;
                }
        }
 
-       return 0;
+       return std::make_pair ( (TimeAxisView *) 0, 0);
 }
 
 void
@@ -3243,12 +3256,10 @@ Editor::setup_toolbar ()
 }
 
 void
-Editor::midi_panic_toggle ()
+Editor::midi_panic_button_pressed ()
 {
        if (session) {
                session->midi_panic();
-               midi_panic_button.set_active (false);
-               midi_panic_button.set_state (STATE_NORMAL);
        }
 }
 
@@ -3283,18 +3294,18 @@ Editor::setup_midi_toolbar ()
        midi_tool_button_box.pack_start(midi_tool_pencil_button, true, true);
        midi_tool_button_box.pack_start(midi_tool_select_button, true, true);
        midi_tool_button_box.pack_start(midi_tool_resize_button, true, true);
-       midi_tool_button_box.pack_start(midi_tool_erase_button, true, true);
+       midi_tool_button_box.pack_start(midi_tool_erase_button , true, true);
        midi_tool_button_box.set_homogeneous(true);
 
        midi_tool_pencil_button.set_name ("MouseModeButton");
        midi_tool_select_button.set_name ("MouseModeButton");
        midi_tool_resize_button.set_name ("MouseModeButton");
-       midi_tool_erase_button.set_name ("MouseModeButton");
+       midi_tool_erase_button .set_name ("MouseModeButton");
 
        ARDOUR_UI::instance()->tooltips().set_tip (midi_tool_pencil_button, _("Add/Move/Stretch Notes"));
        ARDOUR_UI::instance()->tooltips().set_tip (midi_tool_select_button, _("Select/Move Notes"));
        ARDOUR_UI::instance()->tooltips().set_tip (midi_tool_resize_button, _("Resize Notes"));
-       ARDOUR_UI::instance()->tooltips().set_tip (midi_tool_erase_button, _("Erase Notes"));
+       ARDOUR_UI::instance()->tooltips().set_tip (midi_tool_erase_button,  _("Erase Notes"));
 
        midi_tool_pencil_button.unset_flags (CAN_FOCUS);
        midi_tool_select_button.unset_flags (CAN_FOCUS);
@@ -3310,12 +3321,20 @@ Editor::setup_midi_toolbar ()
        midi_tool_erase_button.signal_toggled().connect (bind (mem_fun(*this,
                                &Editor::midi_edit_mode_toggled), Editing::MidiEditErase));
 
+       
+       /* Midi sound notes */
+       midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
+       midi_sound_notes.set_relief(Gtk::RELIEF_NONE);
+       ARDOUR_UI::instance()->tooltips().set_tip (midi_sound_notes, _("Sound Notes"));
+       midi_sound_notes.unset_flags (CAN_FOCUS);
+       
        /* Panic */
        
-       VBox* panic_box = manage (new VBox);
+       HBox* panic_box = manage (new HBox);
        midi_panic_button.set_name("MidiPanicButton");
        midi_panic_button.signal_pressed().connect (
-                       mem_fun(this, &Editor::midi_panic_toggle));
+                       mem_fun(this, &Editor::midi_panic_button_pressed));
+       panic_box->pack_start (midi_sound_notes , true, true);
        panic_box->pack_start (midi_panic_button, true, true);
        
        /* Pack everything in... */
@@ -3494,15 +3513,6 @@ Editor::State::~State ()
        delete selection;
 }
 
-UndoAction
-Editor::get_memento () const
-{
-       State *state = new State (this);
-
-       store_state (*state);
-       return bind (mem_fun (*(const_cast<Editor*>(this)), &Editor::restore_state), state);
-}
-
 void
 Editor::store_state (State& state) const
 {
@@ -3518,7 +3528,7 @@ Editor::restore_state (State *state)
 
        *selection = *state->selection;
        time_selection_changed ();
-       region_selection_changed ();   
+       region_selection_changed ();
 
        /* XXX other selection change handlers? */
 }
@@ -4820,7 +4830,7 @@ Editor::idle_visual_changer ()
 
 struct EditorOrderTimeAxisSorter {
     bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
-           return a->order < b->order;
+           return a->order () < b->order ();
     }
 };
        
@@ -4951,21 +4961,18 @@ Editor::get_regions_at (RegionSelection& rs, nframes64_t where, const TrackSelec
        }
 
        for (TrackSelection::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
-       
-               AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*t);
-
-               if (atv) {
+               RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
+               if (rtv) {
                        boost::shared_ptr<Diskstream> ds;
                        boost::shared_ptr<Playlist> pl;
                        
-                       if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()))) {
+                       if ((ds = rtv->get_diskstream()) && ((pl = ds->playlist()))) {
 
-                               Playlist::RegionList* regions = pl->regions_at ((nframes64_t) floor ( (double)where * ds->speed()));
+                               Playlist::RegionList* regions = pl->regions_at (
+                                               (nframes64_t) floor ( (double)where * ds->speed()));
 
                                for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
-
-                                       RegionView* rv = atv->audio_view()->find_view (*i);
-
+                                       RegionView* rv = rtv->view()->find_view (*i);
                                        if (rv) {
                                                rs.add (rv);
                                        }
@@ -4989,20 +4996,19 @@ Editor::get_regions_after (RegionSelection& rs, nframes64_t where, const TrackSe
        }
 
        for (TrackSelection::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
-       
-               AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*t);
-
-               if (atv) {
+               RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
+               if (rtv) {
                        boost::shared_ptr<Diskstream> ds;
                        boost::shared_ptr<Playlist> pl;
                        
-                       if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()))) {
+                       if ((ds = rtv->get_diskstream()) && ((pl = ds->playlist()))) {
 
-                               Playlist::RegionList* regions = pl->regions_touched ((nframes64_t) floor ( (double)where * ds->speed()), max_frames);
+                               Playlist::RegionList* regions = pl->regions_touched (
+                                               (nframes64_t) floor ( (double)where * ds->speed()), max_frames);
 
                                for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
 
-                                       RegionView* rv = atv->audio_view()->find_view (*i);
+                                       RegionView* rv = rtv->view()->find_view (*i);
 
                                        if (rv) {
                                                rs.push_back (rv);
@@ -5119,6 +5125,26 @@ Editor::show_rhythm_ferret ()
        rhythm_ferret->present ();
 }
 
+void
+Editor::show_bundle_manager ()
+{
+       if (_bundle_manager == 0) {
+               _bundle_manager = new BundleManager (*session);
+       }
+
+       _bundle_manager->show ();
+}
+
+void
+Editor::show_global_port_matrix (ARDOUR::DataType t)
+{
+       if (_global_port_matrix[t] == 0) {
+               _global_port_matrix[t] = new GlobalPortMatrixWindow (*session, t);
+       }
+
+       _global_port_matrix[t]->show ();
+}
+
 void
 Editor::first_idle ()
 {
@@ -5141,9 +5167,7 @@ Editor::first_idle ()
        // first idle adds route children (automation tracks), so we need to redisplay here
        redisplay_route_list();
        
-       if (dialog) {
-               delete dialog;
-       }
+       delete dialog;
 
        _have_idled = true;
 }
@@ -5317,3 +5341,11 @@ Editor::idle_resize ()
        resize_idle_id = -1;
        return false;
 }
+
+void
+Editor::located ()
+{
+       ENSURE_GUI_THREAD (mem_fun (*this, &Editor::located));
+
+       _pending_locate_request = false;
+}