catch markers as they go away, to avoid selection corruption; add select-range-betwee...
[ardour.git] / gtk2_ardour / editor.cc
index 647d62ca17d997d456eaa0a7a45effa2b01fa486..bde42378e9ddfb56bb8f32aaf589f11f00a0e02d 100644 (file)
@@ -17,6 +17,7 @@
 
 */
 
+#include <sys/time.h>
 #include <unistd.h>
 #include <cstdlib>
 #include <cmath>
@@ -77,6 +78,7 @@
 #include "canvas_impl.h"
 #include "actions.h"
 #include "gui_thread.h"
+#include "sfdb_ui.h"
 
 #ifdef FFT_ANALYSIS
 #include "analysis_window.h"
@@ -119,7 +121,7 @@ static const gchar *_snap_type_strings[] = {
        N_("Beats"),
        N_("Bars"),
        N_("Marks"),
-       N_("Edit Cursor"),
+       N_("Edit Point"),
        N_("Region starts"),
        N_("Region ends"),
        N_("Region syncs"),
@@ -133,12 +135,20 @@ static const gchar *_snap_mode_strings[] = {
        0
 };
 
+static const gchar *_edit_point_strings[] = {
+       N_("Playhead"),
+       N_("Marker"),
+       N_("Mouse"),
+       0
+};
+
 static const gchar *_zoom_focus_strings[] = {
        N_("Left"),
        N_("Right"),
        N_("Center"),
        N_("Playhead"),
-       N_("Edit Cursor"),
+       N_("Mouse"),
+       N_("Marker"),
        0
 };
 
@@ -161,20 +171,6 @@ show_me_the_size (Requisition* r, const char* what)
        cerr << "size of " << what << " = " << r->width << " x " << r->height << endl;
 }
 
-void 
-check_adjustment (Gtk::Adjustment* adj)
-{
-       cerr << "CHANGE adj  = " 
-            << adj->get_lower () <<  ' '
-            << adj->get_upper () <<  ' '
-            << adj->get_value () <<  ' '
-            << adj->get_step_increment () <<  ' '
-            << adj->get_page_increment () <<  ' '
-            << adj->get_page_size () <<  ' '
-            << endl;
-
-}
-
 Editor::Editor ()
        : 
          /* time display buttons */
@@ -231,6 +227,7 @@ Editor::Editor ()
        selection->TracksChanged.connect (mem_fun(*this, &Editor::track_selection_changed));
        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));
 
        clicked_regionview = 0;
        clicked_trackview = 0;
@@ -242,15 +239,21 @@ Editor::Editor ()
        drag_info.item = 0;
        current_mixer_strip = 0;
        current_bbt_points = 0;
-
-       snap_type_strings = I18N (_snap_type_strings);
-       snap_mode_strings = I18N (_snap_mode_strings);
-       zoom_focus_strings = I18N(_zoom_focus_strings);
+       
+       snap_type_strings =  I18N (_snap_type_strings);
+       snap_mode_strings =  I18N (_snap_mode_strings);
+       zoom_focus_strings = I18N (_zoom_focus_strings);
+       edit_point_strings = I18N (_edit_point_strings);
 
        snap_type = SnapToFrame;
        set_snap_to (snap_type);
+
        snap_mode = SnapNormal;
        set_snap_mode (snap_mode);
+
+       _edit_point = EditAtMouse;
+       set_edit_point_preference (_edit_point);
+
        snap_threshold = 5.0;
        bbt_beat_subdivision = 4;
        canvas_width = 0;
@@ -258,6 +261,7 @@ Editor::Editor ()
        autoscroll_active = false;
        autoscroll_timeout_tag = -1;
        interthread_progress_window = 0;
+       logo_item = 0;
 
 #ifdef FFT_ANALYSIS
        analysis_window = 0;
@@ -269,6 +273,7 @@ Editor::Editor ()
        _show_waveforms_recording = true;
        first_action_message = 0;
        export_dialog = 0;
+       export_range_markers_dialog = 0;
        show_gain_after_trim = false;
        ignore_route_list_reorder = false;
        no_route_list_redisplay = false;
@@ -301,18 +306,22 @@ Editor::Editor ()
        current_stepping_trackview = 0;
        entered_track = 0;
        entered_regionview = 0;
+       entered_marker = 0;
        clear_entered_track = false;
        _new_regionviews_show_envelope = false;
        current_timestretch = 0;
        in_edit_group_row_change = false;
        last_canvas_frame = 0;
-       edit_cursor = 0;
        playhead_cursor = 0;
        button_release_can_deselect = true;
        canvas_idle_queued = false;
        _dragging_playhead = false;
        _dragging_hscrollbar = false;
-       _scrubbing = false;
+
+       scrubbing_direction = 0;
+
+       sfbrowser = 0;
+       ignore_route_order_sync = false;
 
        location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get();
        location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get();
@@ -337,7 +346,7 @@ Editor::Editor ()
 
        edit_controls_vbox.set_spacing (0);
        horizontal_adjustment.signal_value_changed().connect (mem_fun(*this, &Editor::canvas_horizontally_scrolled));
-       vertical_adjustment.signal_value_changed().connect (mem_fun(*this, &Editor::tie_vertical_scrolling));
+       vertical_adjustment.signal_value_changed().connect (mem_fun(*this, &Editor::tie_vertical_scrolling), true);
        
        track_canvas.set_hadjustment (horizontal_adjustment);
        track_canvas.set_vadjustment (vertical_adjustment);
@@ -478,6 +487,7 @@ Editor::Editor ()
        
        route_display_model->signal_row_deleted().connect (mem_fun (*this, &Editor::route_list_delete));
        route_display_model->signal_row_changed().connect (mem_fun (*this, &Editor::route_list_change));
+       route_display_model->signal_rows_reordered().connect (mem_fun (*this, &Editor::track_list_reorder));
 
        route_list_display.signal_button_press_event().connect (mem_fun (*this, &Editor::route_list_display_button_press), false);
 
@@ -669,6 +679,9 @@ Editor::Editor ()
        set_name ("EditorWindow");
        add_accel_group (ActionManager::ui_manager->get_accel_group());
 
+       status_bar_hpacker.show ();
+
+       vpacker.pack_end (status_bar_hpacker, false, false);
        vpacker.pack_end (global_hpacker, true, true);
 
        /* register actions now so that set_state() can find them and set toggles/checks etc */
@@ -737,6 +750,7 @@ Editor::Editor ()
        ControlProtocol::ScrollTimeline.connect (mem_fun (*this, &Editor::control_scroll));
 
        Config->ParameterChanged.connect (mem_fun (*this, &Editor::parameter_changed));
+       Route::SyncOrderKeys.connect (mem_fun (*this, &Editor::sync_order_keys));
 
        constructed = true;
        instant_save ();
@@ -831,9 +845,26 @@ void
 Editor::tie_vertical_scrolling ()
 {
        double y1 = vertical_adjustment.get_value();
+
+       playhead_cursor->set_y_axis (y1);
+       if (logo_item) {
+               logo_item->property_y() = y1;
+       }
+
        controls_layout.get_vadjustment()->set_value (y1);
-       playhead_cursor->set_y_axis(y1);
-       edit_cursor->set_y_axis(y1);
+
+#ifdef GTKOSX
+       /* the way idle updates and immediate window flushing work on GTK-Quartz
+          requires that we force an immediate redraw right here. The controls
+          layout will do the same all by itself, as does the canvas widget, but
+          most of the time, the canvas itself hasn't updated itself because its
+          idle handler hasn't run. consequently, the call that its layout makes
+          to gdk_window_process_updates() finds nothing to do. here, we force
+          the update to happen, then request a flush of the new window state.
+       */
+       track_canvas.update_now ();
+       gdk_window_process_updates (GTK_LAYOUT(track_canvas.gobj())->bin_window, true);
+#endif
 }
 
 void
@@ -853,12 +884,8 @@ Editor::instant_save ()
 void
 Editor::edit_cursor_clock_changed()
 {
-       if (edit_cursor->current_frame != edit_cursor_clock.current_time()) {
-               edit_cursor->set_position (edit_cursor_clock.current_time());
-       }
 }
 
-
 void
 Editor::zoom_adjustment_changed ()
 {
@@ -1155,6 +1182,10 @@ Editor::connect_to_session (Session *t)
        session->locations()->StateChanged.connect (mem_fun(*this, &Editor::refresh_location_display_s));
        session->locations()->end_location()->changed.connect (mem_fun(*this, &Editor::end_location_changed));
 
+       if (sfbrowser) {
+               sfbrowser->set_session (session);
+       }
+
        handle_new_duration ();
 
        redisplay_regions ();
@@ -1368,10 +1399,10 @@ Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type,
        case RegionViewNameHighlight:
                if (!with_selection) {
                        if (region_edit_menu_split_item) {
-                               if (clicked_regionview && clicked_regionview->region()->covers (edit_cursor->current_frame)) {
-                                       ActionManager::set_sensitive (ActionManager::edit_cursor_in_region_sensitive_actions, true);
+                               if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
+                                       ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
                                } else {
-                                       ActionManager::set_sensitive (ActionManager::edit_cursor_in_region_sensitive_actions, false);
+                                       ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
                                }
                        }
                        /*
@@ -1655,7 +1686,7 @@ Editor::add_region_context_items (AudioStreamView* sv, boost::shared_ptr<Region>
        items.push_back (MenuElem (_("Raise to top layer"), mem_fun(*this, &Editor::raise_region_to_top)));
        items.push_back (MenuElem (_("Lower to bottom layer"), mem_fun  (*this, &Editor::lower_region_to_bottom)));
        items.push_back (SeparatorElem());
-       items.push_back (MenuElem (_("Define sync point"), mem_fun(*this, &Editor::set_region_sync_from_edit_cursor)));
+       items.push_back (MenuElem (_("Define sync point"), mem_fun(*this, &Editor::set_region_sync_from_edit_point)));
        items.push_back (MenuElem (_("Remove sync point"), mem_fun(*this, &Editor::remove_region_sync)));
        items.push_back (SeparatorElem());
 
@@ -1770,8 +1801,8 @@ Editor::add_region_context_items (AudioStreamView* sv, boost::shared_ptr<Region>
        MenuList& trim_items = trim_menu->items();
        trim_menu->set_name ("ArdourContextMenu");
        
-       trim_items.push_back (MenuElem (_("Start to edit cursor"), mem_fun(*this, &Editor::trim_region_from_edit_cursor)));
-       trim_items.push_back (MenuElem (_("Edit cursor to end"), mem_fun(*this, &Editor::trim_region_to_edit_cursor)));
+       trim_items.push_back (MenuElem (_("Start to edit point"), mem_fun(*this, &Editor::trim_region_from_edit_point)));
+       trim_items.push_back (MenuElem (_("Edit point to end"), mem_fun(*this, &Editor::trim_region_to_edit_point)));
                             
        items.push_back (MenuElem (_("Trim"), *trim_menu));
        items.push_back (SeparatorElem());
@@ -1858,7 +1889,7 @@ Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
        MenuList& play_items = play_menu->items();
        play_menu->set_name ("ArdourContextMenu");
        
-       play_items.push_back (MenuElem (_("Play from edit cursor"), mem_fun(*this, &Editor::play_from_edit_cursor)));
+       play_items.push_back (MenuElem (_("Play from edit point"), mem_fun(*this, &Editor::play_from_edit_point)));
        play_items.push_back (MenuElem (_("Play from start"), mem_fun(*this, &Editor::play_from_start)));
        play_items.push_back (MenuElem (_("Play region"), mem_fun(*this, &Editor::play_selected_region)));
        play_items.push_back (SeparatorElem());
@@ -1880,11 +1911,15 @@ Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
        select_items.push_back (MenuElem (_("Set range to loop range"), mem_fun(*this, &Editor::set_selection_from_loop)));
        select_items.push_back (MenuElem (_("Set range to punch range"), mem_fun(*this, &Editor::set_selection_from_punch)));
        select_items.push_back (SeparatorElem());
-       select_items.push_back (MenuElem (_("Select all after edit cursor"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), edit_cursor, true)));
-       select_items.push_back (MenuElem (_("Select all before edit cursor"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), edit_cursor, false)));
-       select_items.push_back (MenuElem (_("Select all after playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
-       select_items.push_back (MenuElem (_("Select all before playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
-       select_items.push_back (MenuElem (_("Select all between cursors"), bind (mem_fun(*this, &Editor::select_all_selectables_between_cursors), playhead_cursor, edit_cursor)));
+       select_items.push_back (MenuElem (_("Select All After Edit Point"), bind (mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
+       select_items.push_back (MenuElem (_("Select All Before Edit Point"), bind (mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
+       select_items.push_back (MenuElem (_("Select All After Playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
+       select_items.push_back (MenuElem (_("Select All Before Playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
+       select_items.push_back (MenuElem (_("Select All Between Playhead & Edit Point"), bind (mem_fun(*this, &Editor::select_all_selectables_between), false)));
+       select_items.push_back (MenuElem (_("Select All Within Playhead & Edit Point"), bind (mem_fun(*this, &Editor::select_all_selectables_between), true)));
+       select_items.push_back (MenuElem (_("Select Range Between Playhead & Edit Point"), mem_fun(*this, &Editor::select_range_between)));
+
+
        select_items.push_back (SeparatorElem());
 
        edit_items.push_back (MenuElem (_("Select"), *select_menu));
@@ -1897,8 +1932,7 @@ Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
        
        cutnpaste_items.push_back (MenuElem (_("Cut"), mem_fun(*this, &Editor::cut)));
        cutnpaste_items.push_back (MenuElem (_("Copy"), mem_fun(*this, &Editor::copy)));
-       cutnpaste_items.push_back (MenuElem (_("Paste at edit cursor"), bind (mem_fun(*this, &Editor::paste), 1.0f)));
-       cutnpaste_items.push_back (MenuElem (_("Paste at mouse"), mem_fun(*this, &Editor::mouse_paste)));
+       cutnpaste_items.push_back (MenuElem (_("Paste"), bind (mem_fun(*this, &Editor::paste), 1.0f)));
 
        cutnpaste_items.push_back (SeparatorElem());
 
@@ -1925,9 +1959,9 @@ Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
        
        edit_items.push_back (SeparatorElem());
        nudge_items.push_back (MenuElem (_("Nudge entire track fwd"), (bind (mem_fun(*this, &Editor::nudge_track), false, true))));
-       nudge_items.push_back (MenuElem (_("Nudge track after edit cursor fwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, true))));
+       nudge_items.push_back (MenuElem (_("Nudge track after edit point fwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, true))));
        nudge_items.push_back (MenuElem (_("Nudge entire track bwd"), (bind (mem_fun(*this, &Editor::nudge_track), false, false))));
-       nudge_items.push_back (MenuElem (_("Nudge track after edit cursor bwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, false))));
+       nudge_items.push_back (MenuElem (_("Nudge track after edit point bwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, false))));
 
        edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
 }
@@ -1943,7 +1977,7 @@ Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
        MenuList& play_items = play_menu->items();
        play_menu->set_name ("ArdourContextMenu");
        
-       play_items.push_back (MenuElem (_("Play from edit cursor"), mem_fun(*this, &Editor::play_from_edit_cursor)));
+       play_items.push_back (MenuElem (_("Play from edit point"), mem_fun(*this, &Editor::play_from_edit_point)));
        play_items.push_back (MenuElem (_("Play from start"), mem_fun(*this, &Editor::play_from_start)));
        edit_items.push_back (MenuElem (_("Play"), *play_menu));
 
@@ -1958,8 +1992,8 @@ Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
        select_items.push_back (MenuElem (_("Invert selection in track"), mem_fun(*this, &Editor::invert_selection_in_track)));
        select_items.push_back (MenuElem (_("Invert selection"), mem_fun(*this, &Editor::invert_selection)));
        select_items.push_back (SeparatorElem());
-       select_items.push_back (MenuElem (_("Select all after edit cursor"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), edit_cursor, true)));
-       select_items.push_back (MenuElem (_("Select all before edit cursor"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), edit_cursor, false)));
+       select_items.push_back (MenuElem (_("Select all after edit point"), bind (mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
+       select_items.push_back (MenuElem (_("Select all before edit point"), bind (mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
        select_items.push_back (MenuElem (_("Select all after playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
        select_items.push_back (MenuElem (_("Select all before playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
 
@@ -1981,9 +2015,9 @@ Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
        
        edit_items.push_back (SeparatorElem());
        nudge_items.push_back (MenuElem (_("Nudge entire track fwd"), (bind (mem_fun(*this, &Editor::nudge_track), false, true))));
-       nudge_items.push_back (MenuElem (_("Nudge track after edit cursor fwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, true))));
+       nudge_items.push_back (MenuElem (_("Nudge track after edit point fwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, true))));
        nudge_items.push_back (MenuElem (_("Nudge entire track bwd"), (bind (mem_fun(*this, &Editor::nudge_track), false, false))));
-       nudge_items.push_back (MenuElem (_("Nudge track after edit cursor bwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, false))));
+       nudge_items.push_back (MenuElem (_("Nudge track after edit point bwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, false))));
 
        edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
 }
@@ -2027,6 +2061,18 @@ Editor::set_snap_mode (SnapMode mode)
 
        instant_save ();
 }
+void
+Editor::set_edit_point_preference (EditPoint ep)
+{
+       _edit_point = ep;
+       string str = edit_point_strings[(int)ep];
+
+       if (str != edit_point_selector.get_active_text ()) {
+               edit_point_selector.set_active_text (str);
+       }
+
+       instant_save ();
+}
 
 int
 Editor::set_state (const XMLNode& node)
@@ -2077,13 +2123,6 @@ Editor::set_state (const XMLNode& node)
                horizontal_adjustment.set_value (0);
        }
 
-       if (session && (prop = node.property ("edit-cursor"))) {
-               nframes_t pos = atol (prop->value().c_str());
-               edit_cursor->set_position (pos);
-       } else {
-               edit_cursor->set_position (0);
-       }
-
        if ((prop = node.property ("mixer-width"))) {
                editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
        }
@@ -2104,6 +2143,10 @@ Editor::set_state (const XMLNode& node)
                set_snap_mode ((SnapMode) atoi (prop->value()));
        }
 
+       if ((prop = node.property ("edit-point"))) {
+               set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point));
+       }
+
        if ((prop = node.property ("mouse-mode"))) {
                MouseMode m = str2mousemode(prop->value());
                mouse_mode = MouseMode ((int) m + 1); /* lie, force mode switch */
@@ -2239,10 +2282,10 @@ Editor::get_state ()
        snprintf (buf, sizeof(buf), "%d", (int) snap_mode);
        node->add_property ("snap-mode", buf);
 
+       node->add_property ("edit-point", enum_2_string (_edit_point));
+
        snprintf (buf, sizeof (buf), "%" PRIu32, playhead_cursor->current_frame);
        node->add_property ("playhead", buf);
-       snprintf (buf, sizeof (buf), "%" PRIu32, edit_cursor->current_frame);
-       node->add_property ("edit-cursor", buf);
 
        node->add_property ("show-waveforms", _show_waveforms ? "yes" : "no");
        node->add_property ("show-waveforms-recording", _show_waveforms_recording ? "yes" : "no");
@@ -2399,8 +2442,8 @@ Editor::snap_to (nframes64_t& start, int32_t direction, bool for_mark)
                 start = session->tempo_map().round_to_beat_subdivision (start, 3);
                 break;
 
-       case SnapToEditCursor:
-               start = edit_cursor->current_frame;
+       case SnapToEditPoint:
+               start = get_preferred_edit_position ();
                break;
 
        case SnapToMark:
@@ -2614,7 +2657,7 @@ Editor::setup_toolbar ()
        ARDOUR_UI::instance()->tooltips().set_tip (zoom_out_full_button, _("Zoom to Session"));
 
        zoom_focus_selector.set_name ("ZoomFocusSelector");
-       Gtkmm2ext::set_size_request_to_display_given_text (zoom_focus_selector, "Edit Cursor", FUDGE, 0);
+       Gtkmm2ext::set_size_request_to_display_given_text (zoom_focus_selector, "Playhead", FUDGE, 0);
        set_popdown_strings (zoom_focus_selector, zoom_focus_strings);
        zoom_focus_selector.signal_changed().connect (mem_fun(*this, &Editor::zoom_focus_selection_done));
        ARDOUR_UI::instance()->tooltips().set_tip (zoom_focus_selector, _("Zoom focus"));
@@ -2624,8 +2667,6 @@ Editor::setup_toolbar ()
        zoom_box.pack_start (zoom_in_button, false, false);
        zoom_box.pack_start (zoom_out_full_button, false, false);
 
-       /* Edit Cursor / Snap */
-
        snap_box.set_spacing (1);
        snap_box.set_border_width (2);
 
@@ -2633,17 +2674,24 @@ Editor::setup_toolbar ()
        Gtkmm2ext::set_size_request_to_display_given_text (snap_type_selector, "SMPTE Seconds", 2+FUDGE, 10);
        set_popdown_strings (snap_type_selector, snap_type_strings);
        snap_type_selector.signal_changed().connect (mem_fun(*this, &Editor::snap_type_selection_done));
-       ARDOUR_UI::instance()->tooltips().set_tip (snap_type_selector, _("Unit to snap cursors and ranges to"));
+       ARDOUR_UI::instance()->tooltips().set_tip (snap_type_selector, _("Snap/Grid Units"));
 
        snap_mode_selector.set_name ("SnapModeSelector");
        Gtkmm2ext::set_size_request_to_display_given_text (snap_mode_selector, "Magnetic Snap", 2+FUDGE, 10);
        set_popdown_strings (snap_mode_selector, snap_mode_strings);
        snap_mode_selector.signal_changed().connect (mem_fun(*this, &Editor::snap_mode_selection_done));
+       ARDOUR_UI::instance()->tooltips().set_tip (snap_mode_selector, _("Snap/Grid Mode"));
+
+       edit_point_selector.set_name ("SnapModeSelector");
+       Gtkmm2ext::set_size_request_to_display_given_text (edit_point_selector, "Playhead", 2+FUDGE, 10);
+       set_popdown_strings (edit_point_selector, edit_point_strings);
+       edit_point_selector.signal_changed().connect (mem_fun(*this, &Editor::edit_point_selection_done));
+       ARDOUR_UI::instance()->tooltips().set_tip (edit_point_selector, _("Edit point"));
 
        snap_box.pack_start (edit_cursor_clock, false, false);
        snap_box.pack_start (snap_mode_selector, false, false);
        snap_box.pack_start (snap_type_selector, false, false);
-
+       snap_box.pack_start (edit_point_selector, false, false);
 
        /* Nudge */
 
@@ -2713,8 +2761,6 @@ Editor::convert_drop_to_paths (vector<ustring>& paths,
 
        vector<ustring> uris = data.get_uris();
 
-       cerr << "there were " << uris.size() << " in that drag data\n";
-
        if (uris.empty()) {
 
                /* This is seriously fucked up. Nautilus doesn't say that its URI lists
@@ -3047,8 +3093,8 @@ Editor::snap_type_selection_done ()
                snaptype = SnapToBar;
        } else if (choice == _("Marks")) {
                snaptype = SnapToMark;
-       } else if (choice == _("Edit Cursor")) {
-               snaptype = SnapToEditCursor;
+       } else if (choice == _("Edit Point")) {
+               snaptype = SnapToEditPoint;
        } else if (choice == _("Region starts")) {
                snaptype = SnapToRegionStart;
        } else if (choice == _("Region ends")) {
@@ -3098,6 +3144,27 @@ Editor::snap_mode_selection_done ()
        }
 }
 
+void
+Editor::edit_point_selection_done ()
+{
+       string choice = edit_point_selector.get_active_text();
+       EditPoint ep = EditAtSelectedMarker;
+
+       if (choice == _("Marker")) {
+               _edit_point = EditAtSelectedMarker;
+       } else if (choice == _("Playhead")) {
+               _edit_point = EditAtPlayhead;
+       } else {
+               _edit_point = EditAtMouse;
+       }
+
+       RefPtr<RadioAction> ract = edit_point_action (ep);
+
+       if (ract) {
+               ract->set_active (true);
+       }
+}
+
 void
 Editor::zoom_focus_selection_done ()
 {
@@ -3112,7 +3179,9 @@ Editor::zoom_focus_selection_done ()
                focus_type = ZoomFocusCenter;
        } else if (choice == _("Playhead")) {
                focus_type = ZoomFocusPlayhead;
-       } else if (choice == _("Edit Cursor")) {
+       } else if (choice == _("Mouse")) {
+               focus_type = ZoomFocusMouse;
+       } else if (choice == _("Edit Point")) {
                focus_type = ZoomFocusEdit;
        } 
        
@@ -3674,27 +3743,47 @@ Editor::restore_editing_space ()
        );
 }
 
+/**
+ *  Make new playlists for a given track and also any others that belong
+ *  to the same active edit group.
+ *  @param v Track.
+ */
+
 void 
-Editor::new_playlists ()
+Editor::new_playlists (TimeAxisView* v)
 {
        begin_reversible_command (_("new playlists"));
-       mapover_audio_tracks (mem_fun (*this, &Editor::mapped_use_new_playlist));
+       mapover_audio_tracks (mem_fun (*this, &Editor::mapped_use_new_playlist), v);
        commit_reversible_command ();
 }
 
+
+/**
+ *  Use a copy of the current playlist for a given track and also any others that belong
+ *  to the same active edit group.
+ *  @param v Track.
+ */
+
 void
-Editor::copy_playlists ()
+Editor::copy_playlists (TimeAxisView* v)
 {
        begin_reversible_command (_("copy playlists"));
-       mapover_audio_tracks (mem_fun (*this, &Editor::mapped_use_copy_playlist));
+       mapover_audio_tracks (mem_fun (*this, &Editor::mapped_use_copy_playlist), v);
        commit_reversible_command ();
 }
 
+
+/**
+ *  Clear the current playlist for a given track and also any others that belong
+ *  to the same active edit group.
+ *  @param v Track.
+ */
+
 void 
-Editor::clear_playlists ()
+Editor::clear_playlists (TimeAxisView* v)
 {
        begin_reversible_command (_("clear playlists"));
-       mapover_audio_tracks (mem_fun (*this, &Editor::mapped_clear_playlist));
+       mapover_audio_tracks (mem_fun (*this, &Editor::mapped_clear_playlist), v);
        commit_reversible_command ();
 }
 
@@ -3797,7 +3886,6 @@ Editor::set_frames_per_unit (double fpu)
        reset_hscrollbar_stepping ();
        reset_scrolling_region ();
        
-       if (edit_cursor) edit_cursor->set_position (edit_cursor->current_frame);
        if (playhead_cursor) playhead_cursor->set_position (playhead_cursor->current_frame);
 
        instant_save ();
@@ -3869,14 +3957,36 @@ Editor::sort_track_selection ()
        selection->tracks.sort (cmp);
 }
 
-nframes_t
-Editor::edit_cursor_position(bool sync)
+nframes64_t
+Editor::get_preferred_edit_position() const
 {
-       if (sync && edit_cursor->current_frame != edit_cursor_clock.current_time()) {
-               edit_cursor_clock.set(edit_cursor->current_frame, true);
+       bool ignored;
+       nframes64_t where;
+
+       // XXX EDIT CURSOR used to sync with edit cursor clock
+
+       switch (_edit_point) {
+       case EditAtPlayhead:
+               return playhead_cursor->current_frame;
+               
+       case EditAtSelectedMarker:
+               if (!selection->markers.empty()) {
+                       bool whocares;
+                       Location* loc = find_location_from_marker (selection->markers.front(), whocares);
+                       if (loc) {
+                               return loc->start();
+                       }
+               } 
+               /* fallthru */
+               
+       default:
+       case EditAtMouse:
+               if (mouse_frame (where, ignored)) {
+                       return where;
+               } 
        }
 
-       return edit_cursor->current_frame;
+       return -1;
 }
 
 void
@@ -3934,3 +4044,44 @@ Editor::set_punch_range (nframes_t start, nframes_t end, string cmd)
        
        commit_reversible_command ();
 }
+
+RegionSelection
+Editor::get_regions_at (nframes64_t where, const TrackSelection& ts) const
+{
+       RegionSelection rs;
+       const TrackSelection* tracks;
+
+       if (ts.empty()) {
+               tracks = &track_views;
+       } else {
+               tracks = &ts;
+       }
+
+       for (TrackSelection::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
+       
+               AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*t);
+
+               if (atv) {
+                       boost::shared_ptr<Diskstream> ds;
+                       boost::shared_ptr<Playlist> pl;
+                       
+                       if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()))) {
+
+                               Playlist::RegionList* regions = pl->regions_at ((nframes_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);
+
+                                       if (rv) {
+                                               rs.push_back (rv);
+                                       }
+                               }
+
+                               delete regions;
+                       }
+               }
+       }
+
+       return rs;
+}