X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Feditor.cc;h=bde42378e9ddfb56bb8f32aaf589f11f00a0e02d;hb=79ba9960964c8ea701abfe00dbef128ca7da9fbd;hp=ed059efbb7e2d030331b2dcdeb5b5919a029e9bc;hpb=4566992139ba5242cd4a0670f85c51367340d3ff;p=ardour.git diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index ed059efbb7..bde42378e9 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2000-2006 Paul Davis + Copyright (C) 2000-2007 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -15,28 +15,35 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id$ */ +#include #include #include #include #include #include +#include + #include #include #include +#include #include +#include #include #include #include +#include #include #include #include +#include +#include #include #include @@ -48,12 +55,12 @@ #include #include #include +#include #include #include "ardour_ui.h" #include "editor.h" -#include "grouped_buttons.h" #include "keyboard.h" #include "marker.h" #include "playlist_selector.h" @@ -68,10 +75,10 @@ #include "editing.h" #include "public_editor.h" #include "crossfade_edit.h" -#include "audio_time_axis.h" #include "canvas_impl.h" #include "actions.h" #include "gui_thread.h" +#include "sfdb_ui.h" #ifdef FFT_ANALYSIS #include "analysis_window.h" @@ -114,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"), @@ -128,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 }; @@ -156,25 +171,9 @@ 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 (AudioEngine& eng) - : engine (eng), - +Editor::Editor () + : /* time display buttons */ - minsec_label (_("Mins:Secs")), bbt_label (_("Bars:Beats")), smpte_label (_("Timecode")), @@ -196,8 +195,8 @@ Editor::Editor (AudioEngine& eng) /* tool bar related */ - edit_cursor_clock (X_("EditCursorClock"), true), - zoom_range_clock (X_("ZoomRangeClock"), true, true), + edit_cursor_clock (X_("editcursor"), false, X_("EditCursorClock"), true), + zoom_range_clock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, true), toolbar_selection_clock_table (2,3), @@ -210,7 +209,7 @@ Editor::Editor (AudioEngine& eng) /* nudge */ - nudge_clock (X_("NudgeClock"), true, true) + nudge_clock (X_("nudge"), false, X_("NudgeClock"), true, true) { constructed = false; @@ -228,6 +227,7 @@ Editor::Editor (AudioEngine& eng) 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; @@ -239,15 +239,21 @@ Editor::Editor (AudioEngine& eng) 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; @@ -255,6 +261,7 @@ Editor::Editor (AudioEngine& eng) autoscroll_active = false; autoscroll_timeout_tag = -1; interthread_progress_window = 0; + logo_item = 0; #ifdef FFT_ANALYSIS analysis_window = 0; @@ -266,12 +273,12 @@ Editor::Editor (AudioEngine& eng) _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; verbose_cursor_on = true; route_removal = false; - track_spacing = 0; show_automatic_regions_in_region_list = true; region_list_sort_type = (Editing::RegionListSortType) 0; have_pending_keyboard_selection = false; @@ -283,6 +290,7 @@ Editor::Editor (AudioEngine& eng) route_list_menu = 0; region_list_menu = 0; marker_menu = 0; + start_end_marker_menu = 0; range_marker_menu = 0; marker_menu_item = 0; tm_marker_menu = 0; @@ -290,7 +298,6 @@ Editor::Editor (AudioEngine& eng) new_transport_marker_menu = 0; editor_mixer_strip_width = Wide; show_editor_mixer_when_tracks_arrive = false; - repos_zoom_queued = false; region_edit_menu_split_item = 0; temp_location = 0; region_edit_menu_split_multichannel_item = 0; @@ -299,29 +306,35 @@ Editor::Editor (AudioEngine& eng) 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_direction = 0; + + sfbrowser = 0; + ignore_route_order_sync = false; - location_marker_color = color_map[cLocationMarker]; - location_range_color = color_map[cLocationRange]; - location_cd_marker_color = color_map[cLocationCDMarker]; - location_loop_color = color_map[cLocationLoop]; - location_punch_color = color_map[cLocationPunch]; + location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get(); + location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get(); + location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get(); + location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get(); + location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get(); range_marker_drag_rect = 0; marker_drag_line = 0; set_mouse_mode (MouseObject, true); - frames_per_unit = 2048; /* too early to use set_frames_per_unit */ + frames_per_unit = 2048; /* too early to use reset_zoom () */ reset_hscrollbar_stepping (); zoom_focus = ZoomFocusLeft; @@ -333,7 +346,7 @@ Editor::Editor (AudioEngine& eng) 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); @@ -354,21 +367,24 @@ Editor::Editor (AudioEngine& eng) edit_vscrollbar.set_adjustment (vertical_adjustment); edit_hscrollbar.set_adjustment (horizontal_adjustment); - edit_hscrollbar.signal_button_press_event().connect (mem_fun(*this, &Editor::hscrollbar_button_press)); - edit_hscrollbar.signal_button_release_event().connect (mem_fun(*this, &Editor::hscrollbar_button_release)); + edit_hscrollbar.signal_button_press_event().connect (mem_fun(*this, &Editor::hscrollbar_button_press), false); + edit_hscrollbar.signal_button_release_event().connect (mem_fun(*this, &Editor::hscrollbar_button_release), false); edit_hscrollbar.signal_size_allocate().connect (mem_fun(*this, &Editor::hscrollbar_allocate)); + edit_hscrollbar.set_name ("EditorHScrollbar"); + build_cursors (); setup_toolbar (); edit_cursor_clock.ValueChanged.connect (mem_fun(*this, &Editor::edit_cursor_clock_changed)); + time_canvas_vbox.pack_start (*_ruler_separator, false, false); time_canvas_vbox.pack_start (*minsec_ruler, false, false); time_canvas_vbox.pack_start (*smpte_ruler, false, false); time_canvas_vbox.pack_start (*frames_ruler, false, false); time_canvas_vbox.pack_start (*bbt_ruler, false, false); time_canvas_vbox.pack_start (time_canvas, true, true); - time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars)); + time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2); bbt_label.set_name ("EditorTimeButton"); bbt_label.set_size_request (-1, (int)timebar_height); @@ -421,6 +437,9 @@ Editor::Editor (AudioEngine& eng) time_button_event_box.set_name ("TimebarLabelBase"); time_button_event_box.signal_button_release_event().connect (mem_fun(*this, &Editor::ruler_label_button_release)); + time_button_frame.add(time_button_event_box); + time_button_frame.property_shadow_type() = Gtk::SHADOW_OUT; + /* these enable us to have a dedicated window (for cursor setting, etc.) for the canvas areas. */ @@ -438,12 +457,13 @@ Editor::Editor (AudioEngine& eng) edit_packer.attach (edit_vscrollbar, 0, 1, 1, 3, FILL, FILL|EXPAND, 0, 0); - edit_packer.attach (time_button_event_box, 1, 2, 0, 1, FILL, FILL, 0, 0); + edit_packer.attach (time_button_frame, 0, 2, 0, 1, FILL, FILL, 0, 0); edit_packer.attach (time_canvas_event_box, 2, 3, 0, 1, FILL|EXPAND, FILL, 0, 0); edit_packer.attach (controls_layout, 1, 2, 1, 2, FILL, FILL|EXPAND, 0, 0); edit_packer.attach (track_canvas_event_box, 2, 3, 1, 2, FILL|EXPAND, FILL|EXPAND, 0, 0); + edit_packer.attach (zoom_box, 1, 2, 2, 3, FILL, FILL, 0, 0); edit_packer.attach (edit_hscrollbar, 2, 3, 2, 3, FILL|EXPAND, FILL, 0, 0); bottom_hbox.set_border_width (2); @@ -467,6 +487,7 @@ Editor::Editor (AudioEngine& eng) 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); @@ -502,12 +523,11 @@ Editor::Editor (AudioEngine& eng) active_cell->property_activatable() = true; active_cell->property_radio() = false; - edit_group_display.set_name ("EditGroupList"); - group_model->signal_row_changed().connect (mem_fun (*this, &Editor::edit_group_row_change)); edit_group_display.set_name ("EditGroupList"); edit_group_display.get_selection()->set_mode (SELECTION_SINGLE); + edit_group_display.set_headers_visible (true); edit_group_display.set_reorderable (false); edit_group_display.set_rules_hint (true); edit_group_display.set_size_request (75, -1); @@ -593,11 +613,12 @@ Editor::Editor (AudioEngine& eng) named_selection_display.append_column (_("Chunks"), named_selection_columns.text); named_selection_display.set_headers_visible (false); named_selection_display.set_size_request (100, -1); - named_selection_display.set_name ("RegionListDisplay"); + named_selection_display.set_name ("NamedSelectionDisplay"); named_selection_display.get_selection()->set_mode (SELECTION_SINGLE); named_selection_display.set_size_request (100, -1); - named_selection_display.signal_button_press_event().connect (mem_fun(*this, &Editor::named_selection_display_button_press), false); + named_selection_display.signal_button_release_event().connect (mem_fun(*this, &Editor::named_selection_display_button_release), false); + named_selection_display.signal_key_release_event().connect (mem_fun(*this, &Editor::named_selection_display_key_release), false); named_selection_display.get_selection()->signal_changed().connect (mem_fun (*this, &Editor::named_selection_display_selection_changed)); /* SNAPSHOTS */ @@ -605,7 +626,7 @@ Editor::Editor (AudioEngine& eng) snapshot_display_model = ListStore::create (snapshot_display_columns); snapshot_display.set_model (snapshot_display_model); snapshot_display.append_column (X_("snapshot"), snapshot_display_columns.visible_name); - snapshot_display.set_name ("SnapshotDisplayList"); + snapshot_display.set_name ("SnapshotDisplay"); snapshot_display.set_size_request (75, -1); snapshot_display.set_headers_visible (false); snapshot_display.set_reorderable (false); @@ -658,6 +679,9 @@ Editor::Editor (AudioEngine& eng) 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 */ @@ -706,8 +730,11 @@ Editor::Editor (AudioEngine& eng) set_icon_list (window_icons); set_default_icon_list (window_icons); } - set_title (_("ardour: editor")); - set_wmclass (_("ardour_editor"), "Ardour"); + + WindowTitle title(Glib::get_application_name()); + title += _("Editor"); + set_title (title.get_string()); + set_wmclass (X_("ardour_editor"), "Ardour"); add (vpacker); add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK); @@ -723,6 +750,7 @@ Editor::Editor (AudioEngine& eng) 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 (); @@ -817,68 +845,26 @@ void Editor::tie_vertical_scrolling () { double y1 = vertical_adjustment.get_value(); - controls_layout.get_vadjustment()->set_value (y1); - playhead_cursor->set_y_axis(y1); - edit_cursor->set_y_axis(y1); -} - -void -Editor::set_frames_per_unit (double fpu) -{ - nframes_t frames; - - if (fpu == frames_per_unit) { - return; - } - if (fpu < 2.0) { - fpu = 2.0; + playhead_cursor->set_y_axis (y1); + if (logo_item) { + logo_item->property_y() = y1; } - // convert fpu to frame count + controls_layout.get_vadjustment()->set_value (y1); - frames = (nframes_t) floor (fpu * canvas_width); - - /* don't allow zooms that fit more than the maximum number - of frames into an 800 pixel wide space. +#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. */ - - if (max_frames / fpu < 800.0) { - return; - } - - if (fpu == frames_per_unit) { - return; - } - - frames_per_unit = fpu; - - if (frames != zoom_range_clock.current_duration()) { - zoom_range_clock.set (frames); - } - - if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) { - if (!selection->tracks.empty()) { - for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) { - (*i)->reshow_selection (selection->time); - } - } else { - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - (*i)->reshow_selection (selection->time); - } - } - } - - ZoomChanged (); /* EMIT_SIGNAL */ - - 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 (); - + track_canvas.update_now (); + gdk_window_process_updates (GTK_LAYOUT(track_canvas.gobj())->bin_window, true); +#endif } void @@ -895,35 +881,11 @@ Editor::instant_save () } } -void -Editor::reposition_x_origin (nframes_t frame) -{ - if (frame != leftmost_frame) { - leftmost_frame = frame; - - nframes_t rightmost_frame = leftmost_frame + current_page_frames (); - - if (rightmost_frame > last_canvas_frame) { - last_canvas_frame = rightmost_frame; - reset_scrolling_region (); - } - - horizontal_adjustment.set_value (frame/frames_per_unit); - } else { - update_fixed_rulers(); - tempo_map_changed (Change (0)); - } -} - 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 () { @@ -954,73 +916,68 @@ Editor::control_scroll (float fraction) } double step = fraction * current_page_frames(); - nframes_t target; - if ((fraction < 0.0f) && (session->transport_frame() < (nframes_t) fabs(step))) { - target = 0; - } else if ((fraction > 0.0f) && (max_frames - session->transport_frame() < step)) { - target = (max_frames - (current_page_frames()*2)); // allow room for slop in where the PH is on the screen + /* + _control_scroll_target is an optional + + it acts like a pointer to an nframes_t, with + a operator conversion to boolean to check + that it has a value could possibly use + playhead_cursor->current_frame to store the + value and a boolean in the class to know + when it's out of date + */ + + if (!_control_scroll_target) { + _control_scroll_target = session->transport_frame(); + _dragging_playhead = true; + } + + if ((fraction < 0.0f) && (*_control_scroll_target < (nframes_t) fabs(step))) { + *_control_scroll_target = 0; + } else if ((fraction > 0.0f) && (max_frames - *_control_scroll_target < step)) { + *_control_scroll_target = max_frames - (current_page_frames()*2); // allow room for slop in where the PH is on the screen } else { - target = (session->transport_frame() + (nframes_t) floor ((fraction * current_page_frames()))); + *_control_scroll_target += (nframes_t) floor (step); } /* move visuals, we'll catch up with it later */ - playhead_cursor->set_position (target); - - if (target > (current_page_frames() / 2)) { + playhead_cursor->set_position (*_control_scroll_target); + UpdateAllTransportClocks (*_control_scroll_target); + + if (*_control_scroll_target > (current_page_frames() / 2)) { /* try to center PH in window */ - reposition_x_origin (target - (current_page_frames()/2)); + reset_x_origin (*_control_scroll_target - (current_page_frames()/2)); } else { - reposition_x_origin (0); + reset_x_origin (0); } - /* cancel the existing */ + /* + Now we do a timeout to actually bring the session to the right place + according to the playhead. This is to avoid reading disk buffers on every + call to control_scroll, which is driven by ScrollTimeline and therefore + probably by a control surface wheel which can generate lots of events. + */ + /* cancel the existing timeout */ control_scroll_connection.disconnect (); - /* add the next one */ + /* add the next timeout */ - control_scroll_connection = Glib::signal_timeout().connect (bind (mem_fun (*this, &Editor::deferred_control_scroll), target), 50); + control_scroll_connection = Glib::signal_timeout().connect (bind (mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250); } bool Editor::deferred_control_scroll (nframes_t target) { - session->request_locate (target); + session->request_locate (*_control_scroll_target, session->transport_rolling()); + // reset for next stream + _control_scroll_target = boost::none; + _dragging_playhead = false; return false; } -void -Editor::canvas_horizontally_scrolled () -{ - - leftmost_frame = (nframes_t) floor (horizontal_adjustment.get_value() * frames_per_unit); - update_fixed_rulers (); - tempo_map_changed (Change (0)); - -} - -void -Editor::reposition_and_zoom (nframes_t frame, double nfpu) -{ - if (!repos_zoom_queued) { - repos_zoom_queued = true; - Glib::signal_idle().connect (bind (mem_fun(*this, &Editor::deferred_reposition_and_zoom), frame, nfpu)); - } -} - -gint -Editor::deferred_reposition_and_zoom (nframes_t frame, double nfpu) -{ - - set_frames_per_unit (nfpu); - reposition_x_origin (frame); - repos_zoom_queued = false; - - return FALSE; -} - void Editor::on_realize () { @@ -1078,7 +1035,7 @@ Editor::center_screen_internal (nframes_t frame, float page) frame = 0; } - reposition_x_origin (frame); + reset_x_origin (frame); } void @@ -1090,6 +1047,7 @@ Editor::handle_new_duration () if (new_end > last_canvas_frame) { last_canvas_frame = new_end; + horizontal_adjustment.set_upper (last_canvas_frame / frames_per_unit); reset_scrolling_region (); } @@ -1112,24 +1070,21 @@ Editor::update_title () if (session) { bool dirty = session->dirty(); - string wintitle = _("ardour: editor: "); - - if (dirty) { - wintitle += '['; - } - - wintitle += session->name(); + string session_name; if (session->snap_name() != session->name()) { - wintitle += ':'; - wintitle += session->snap_name(); + session_name = session->snap_name(); + } else { + session_name = session->name(); } if (dirty) { - wintitle += ']'; + session_name = "*" + session_name; } - set_title (wintitle); + WindowTitle title(session_name); + title += Glib::get_application_name(); + set_title (title.get_string()); } } @@ -1138,6 +1093,13 @@ Editor::connect_to_session (Session *t) { session = t; + XMLNode* node = ARDOUR_UI::instance()->editor_settings(); + set_state (*node); + + /* catch up with the playhead */ + + session->request_locate (playhead_cursor->current_frame); + if (first_action_message) { first_action_message->hide(); } @@ -1191,8 +1153,7 @@ Editor::connect_to_session (Session *t) } session->locations()->add (loc, false); session->set_auto_loop_location (loc); - } - else { + } else { // force name loc->set_name (_("Loop")); } @@ -1205,8 +1166,7 @@ Editor::connect_to_session (Session *t) } session->locations()->add (loc, false); session->set_auto_punch_location (loc); - } - else { + } else { // force name loc->set_name (_("Punch")); } @@ -1222,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 (); @@ -1234,27 +1198,12 @@ Editor::connect_to_session (Session *t) (static_cast(*i))->set_samples_per_unit (frames_per_unit); } - /* ::reposition_x_origin() doesn't work right here, since the old - position may be zero already, and it does nothing in such - circumstances. - */ - - leftmost_frame = 0; - - horizontal_adjustment.set_value (0); - restore_ruler_visibility (); //tempo_map_changed (Change (0)); session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); - edit_cursor->set_position (0); - playhead_cursor->set_position (0); - start_scrolling (); - XMLNode* node = ARDOUR_UI::instance()->editor_settings(); - set_state (*node); - /* don't show master bus in a new session */ if (ARDOUR_UI::instance()->session_is_new ()) { @@ -1345,36 +1294,45 @@ Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* i case FadeInItem: case FadeInHandleItem: if (arv->audio_region()->fade_in_active()) { - items.push_back (MenuElem (_("Deactivate"), bind (mem_fun (*arv, &AudioRegionView::set_fade_in_active), false))); + items.push_back (MenuElem (_("Deactivate"), bind (mem_fun (*this, &Editor::set_fade_in_active), false))); } else { - items.push_back (MenuElem (_("Activate"), bind (mem_fun (*arv, &AudioRegionView::set_fade_in_active), true))); + items.push_back (MenuElem (_("Activate"), bind (mem_fun (*this, &Editor::set_fade_in_active), true))); } items.push_back (SeparatorElem()); - - items.push_back (MenuElem (_("Linear"), bind (mem_fun (*arv, &AudioRegionView::set_fade_in_shape), AudioRegion::Linear))); - items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*arv, &AudioRegionView::set_fade_in_shape), AudioRegion::LogB))); - items.push_back (MenuElem (_("Slow"), bind (mem_fun (*arv, &AudioRegionView::set_fade_in_shape), AudioRegion::Fast))); - items.push_back (MenuElem (_("Fast"), bind (mem_fun (*arv, &AudioRegionView::set_fade_in_shape), AudioRegion::LogA))); - items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*arv, &AudioRegionView::set_fade_in_shape), AudioRegion::Slow))); + + if (Profile->get_sae()) { + items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear))); + items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast))); + } else { + items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear))); + items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast))); + items.push_back (MenuElem (_("Slow"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogB))); + items.push_back (MenuElem (_("Fast"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogA))); + items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Slow))); + } break; case FadeOutItem: case FadeOutHandleItem: if (arv->audio_region()->fade_out_active()) { - items.push_back (MenuElem (_("Deactivate"), bind (mem_fun (*arv, &AudioRegionView::set_fade_out_active), false))); + items.push_back (MenuElem (_("Deactivate"), bind (mem_fun (*this, &Editor::set_fade_out_active), false))); } else { - items.push_back (MenuElem (_("Activate"), bind (mem_fun (*arv, &AudioRegionView::set_fade_out_active), true))); + items.push_back (MenuElem (_("Activate"), bind (mem_fun (*this, &Editor::set_fade_out_active), true))); } items.push_back (SeparatorElem()); - - items.push_back (MenuElem (_("Linear"), bind (mem_fun (*arv, &AudioRegionView::set_fade_out_shape), AudioRegion::Linear))); - items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*arv, &AudioRegionView::set_fade_out_shape), AudioRegion::Fast))); - items.push_back (MenuElem (_("Slow"), bind (mem_fun (*arv, &AudioRegionView::set_fade_out_shape), AudioRegion::LogB))); - items.push_back (MenuElem (_("Fast"), bind (mem_fun (*arv, &AudioRegionView::set_fade_out_shape), AudioRegion::LogA))); - items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*arv, &AudioRegionView::set_fade_out_shape), AudioRegion::Slow))); + if (Profile->get_sae()) { + items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear))); + items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow))); + } else { + items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear))); + items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow))); + items.push_back (MenuElem (_("Slow"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogA))); + items.push_back (MenuElem (_("Fast"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogB))); + items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Fast))); + } break; default: @@ -1441,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); } } /* @@ -1474,7 +1432,7 @@ Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, return; } - if (clicked_audio_trackview && clicked_audio_trackview->audio_track()) { + if (item_type != SelectionItem && clicked_audio_trackview && clicked_audio_trackview->audio_track()) { /* Bounce to disk */ @@ -1537,7 +1495,7 @@ Editor::build_track_region_context_menu (nframes_t frame) if (atv) { boost::shared_ptr ds; - Playlist* pl; + boost::shared_ptr pl; if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()))) { Playlist::RegionList* regions = pl->regions_at ((nframes_t) floor ( (double)frame * ds->speed())); @@ -1564,10 +1522,10 @@ Editor::build_track_crossfade_context_menu (nframes_t frame) if (atv) { boost::shared_ptr ds; - Playlist* pl; - AudioPlaylist* apl; + boost::shared_ptr pl; + boost::shared_ptr apl; - if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()) != 0) && ((apl = dynamic_cast (pl)) != 0)) { + if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast (pl)) != 0)) { Playlist::RegionList* regions = pl->regions_at (frame); AudioPlaylist::Crossfades xfades; @@ -1641,13 +1599,14 @@ Editor::build_track_selection_context_menu (nframes_t ignored) edit_items.clear (); add_selection_context_items (edit_items); - add_dstream_context_items (edit_items); + // edit_items.push_back (SeparatorElem()); + // add_dstream_context_items (edit_items); return &track_selection_context_menu; } void -Editor::add_crossfade_context_items (AudioStreamView* view, Crossfade* xfade, Menu_Helpers::MenuList& edit_items, bool many) +Editor::add_crossfade_context_items (AudioStreamView* view, boost::shared_ptr xfade, Menu_Helpers::MenuList& edit_items, bool many) { using namespace Menu_Helpers; Menu *xfade_menu = manage (new Menu); @@ -1661,8 +1620,8 @@ Editor::add_crossfade_context_items (AudioStreamView* view, Crossfade* xfade, Me str = _("Unmute"); } - items.push_back (MenuElem (str, bind (mem_fun(*this, &Editor::toggle_xfade_active), xfade))); - items.push_back (MenuElem (_("Edit"), bind (mem_fun(*this, &Editor::edit_xfade), xfade))); + items.push_back (MenuElem (str, bind (mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr (xfade)))); + items.push_back (MenuElem (_("Edit"), bind (mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr (xfade)))); if (xfade->can_follow_overlap()) { @@ -1721,13 +1680,13 @@ Editor::add_region_context_items (AudioStreamView* sv, boost::shared_ptr become selected. */ - // region_menu->signal_map_event().connect (bind (mem_fun(*this, &Editor::set_selected_regionview_from_map_event), sv, boost::weak_ptr(region))); + region_menu->signal_map_event().connect (bind (mem_fun(*this, &Editor::set_selected_regionview_from_map_event), sv, boost::weak_ptr(region))); items.push_back (MenuElem (_("Popup region editor"), mem_fun(*this, &Editor::edit_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()); @@ -1741,62 +1700,83 @@ Editor::add_region_context_items (AudioStreamView* sv, boost::shared_ptr items.push_back (SeparatorElem()); - items.push_back (CheckMenuElem (_("Lock"), mem_fun(*this, &Editor::toggle_region_lock))); + sigc::connection fooc; + + items.push_back (CheckMenuElem (_("Lock"))); region_lock_item = static_cast(&items.back()); + fooc = region_lock_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_lock)); if (region->locked()) { + fooc.block (true); region_lock_item->set_active(); + fooc.block (false); } - items.push_back (CheckMenuElem (_("Mute"), mem_fun(*this, &Editor::toggle_region_mute))); + items.push_back (CheckMenuElem (_("Mute"))); region_mute_item = static_cast(&items.back()); + fooc = region_mute_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_mute)); if (region->muted()) { + fooc.block (true); region_mute_item->set_active(); + fooc.block (false); } - items.push_back (CheckMenuElem (_("Opaque"), mem_fun(*this, &Editor::toggle_region_opaque))); - region_opaque_item = static_cast(&items.back()); - if (region->opaque()) { - region_opaque_item->set_active(); + + if (!Profile->get_sae()) { + items.push_back (CheckMenuElem (_("Opaque"))); + region_opaque_item = static_cast(&items.back()); + fooc = region_opaque_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_opaque)); + if (region->opaque()) { + fooc.block (true); + region_opaque_item->set_active(); + fooc.block (false); + } } items.push_back (CheckMenuElem (_("Original position"), mem_fun(*this, &Editor::naturalize))); if (region->at_natural_position()) { items.back().set_sensitive (false); } - + items.push_back (SeparatorElem()); - + if (ar) { RegionView* rv = sv->find_view (ar); AudioRegionView* arv = dynamic_cast(rv); - - items.push_back (MenuElem (_("Reset Envelope"), mem_fun(*this, &Editor::reset_region_gain_envelopes))); - items.push_back (CheckMenuElem (_("Envelope Visible"), mem_fun(*this, &Editor::toggle_gain_envelope_visibility))); - region_envelope_visible_item = static_cast (&items.back()); - - if (arv->envelope_visible()) { - region_envelope_visible_item->set_active (true); - } - - items.push_back (CheckMenuElem (_("Envelope Active"), mem_fun(*this, &Editor::toggle_gain_envelope_active))); - region_envelope_active_item = static_cast (&items.back()); + if (!Profile->get_sae()) { + items.push_back (MenuElem (_("Reset Envelope"), mem_fun(*this, &Editor::reset_region_gain_envelopes))); + + items.push_back (CheckMenuElem (_("Envelope Visible"))); + region_envelope_visible_item = static_cast (&items.back()); + fooc = region_envelope_visible_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_gain_envelope_visibility)); + if (arv->envelope_visible()) { + fooc.block (true); + region_envelope_visible_item->set_active (true); + fooc.block (false); + } + + items.push_back (CheckMenuElem (_("Envelope Active"))); + region_envelope_active_item = static_cast (&items.back()); + fooc = region_envelope_active_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_gain_envelope_active)); + + if (ar->envelope_active()) { + fooc.block (true); + region_envelope_active_item->set_active (true); + fooc.block (false); + } - if (ar->envelope_active()) { - region_envelope_active_item->set_active (true); + items.push_back (SeparatorElem()); } - items.push_back (SeparatorElem()); - if (ar->scale_amplitude() != 1.0f) { items.push_back (MenuElem (_("DeNormalize"), mem_fun(*this, &Editor::denormalize_region))); } else { items.push_back (MenuElem (_("Normalize"), mem_fun(*this, &Editor::normalize_region))); } } + items.push_back (MenuElem (_("Reverse"), mem_fun(*this, &Editor::reverse_region))); items.push_back (SeparatorElem()); - /* range related stuff */ items.push_back (MenuElem (_("Add Range Markers"), mem_fun (*this, &Editor::add_location_from_audio_region))); @@ -1821,8 +1801,8 @@ Editor::add_region_context_items (AudioStreamView* sv, boost::shared_ptr 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()); @@ -1859,15 +1839,12 @@ Editor::add_region_context_items (AudioStreamView* sv, boost::shared_ptr } void -Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items) +Editor::add_selection_context_items (Menu_Helpers::MenuList& 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"), mem_fun(*this, &Editor::set_route_loop_selection))); + items.push_back (MenuElem (_("Loop range"), bind (mem_fun(*this, &Editor::set_loop_from_selection), true))); #ifdef FFT_ANALYSIS items.push_back (SeparatorElem()); @@ -1875,26 +1852,30 @@ Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items) #endif items.push_back (SeparatorElem()); - items.push_back (MenuElem (_("Separate range to track"), mem_fun(*this, &Editor::separate_region_from_selection))); - items.push_back (MenuElem (_("Separate range to region list"), mem_fun(*this, &Editor::new_region_from_selection))); + 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))); + + 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))); items.push_back (SeparatorElem()); 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))); + items.push_back (SeparatorElem()); items.push_back (MenuElem (_("Add Range Markers"), mem_fun (*this, &Editor::add_location_from_selection))); - items.push_back (MenuElem (_("Set range to loop range"), mem_fun(*this, &Editor::set_selection_from_loop))); - items.push_back (MenuElem (_("Set range to punch range"), mem_fun(*this, &Editor::set_selection_from_punch))); 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::name_selection))); + items.push_back (MenuElem (_("Create chunk from range"), mem_fun(*this, &Editor::create_named_selection))); items.push_back (SeparatorElem()); items.push_back (MenuElem (_("Bounce range"), mem_fun(*this, &Editor::bounce_range_selection))); items.push_back (MenuElem (_("Export range"), mem_fun(*this, &Editor::export_selection))); - - edit_items.push_back (MenuElem (_("Range"), *selection_menu)); - edit_items.push_back (SeparatorElem()); } void @@ -1908,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()); @@ -1930,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)); @@ -1947,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()); @@ -1975,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)); } @@ -1993,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)); @@ -2008,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))); @@ -2031,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)); } @@ -2077,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) @@ -2112,12 +2108,31 @@ Editor::set_state (const XMLNode& node) set_default_size (g.base_width, g.base_height); move (x, y); + if (session && (prop = node.property ("playhead"))) { + nframes_t pos = atol (prop->value().c_str()); + playhead_cursor->set_position (pos); + } else { + playhead_cursor->set_position (0); + + /* reset_x_origin() doesn't work right here, since the old + position may be zero already, and it does nothing in such + circumstances. + */ + + leftmost_frame = 0; + horizontal_adjustment.set_value (0); + } + + if ((prop = node.property ("mixer-width"))) { + editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width)); + } + if ((prop = node.property ("zoom-focus"))) { set_zoom_focus ((ZoomFocus) atoi (prop->value())); } if ((prop = node.property ("zoom"))) { - set_frames_per_unit (PBD::atof (prop->value())); + reset_zoom (PBD::atof (prop->value())); } if ((prop = node.property ("snap-to"))) { @@ -2128,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 */ @@ -2175,16 +2194,16 @@ Editor::set_state (const XMLNode& node) if ((prop = node.property ("follow-playhead"))) { bool yn = (prop->value() == "yes"); + set_follow_playhead (yn); RefPtr act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead")); if (act) { RefPtr tact = RefPtr::cast_dynamic(act); - /* do it twice to force the change */ - tact->set_active (!yn); - tact->set_active (yn); + if (tact->get_active() != yn) { + tact->set_active (yn); + } } } - if ((prop = node.property ("region-list-sort-type"))) { region_list_sort_type = (Editing::RegionListSortType) -1; // force change reset_region_list_sort_type(str2regionlistsorttype(prop->value())); @@ -2252,6 +2271,8 @@ Editor::get_state () node->add_child_nocopy (*geometry); } + maybe_add_mixer_strip_width (*node); + snprintf (buf, sizeof(buf), "%d", (int) zoom_focus); node->add_property ("zoom-focus", buf); snprintf (buf, sizeof(buf), "%f", frames_per_unit); @@ -2261,6 +2282,11 @@ 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); + node->add_property ("show-waveforms", _show_waveforms ? "yes" : "no"); node->add_property ("show-waveforms-recording", _show_waveforms_recording ? "yes" : "no"); node->add_property ("show-measures", _show_measures ? "yes" : "no"); @@ -2283,18 +2309,11 @@ Editor::get_state () TimeAxisView * Editor::trackview_by_y_position (double y) { - TrackViewList::iterator iter; - TimeAxisView *tv; - - for (iter = track_views.begin(); iter != track_views.end(); ++iter) { + for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) { - tv = *iter; - - if (tv->hidden()) { - continue; - } + TimeAxisView *tv; - if (tv->y_position <= y && y < ((tv->y_position + tv->height + track_spacing))) { + if ((tv = (*iter)->covers_y_position (y)) != 0) { return tv; } } @@ -2303,7 +2322,7 @@ Editor::trackview_by_y_position (double y) } void -Editor::snap_to (nframes_t& start, int32_t direction, bool for_mark) +Editor::snap_to (nframes64_t& start, int32_t direction, bool for_mark) { Location* before = 0; Location* after = 0; @@ -2312,10 +2331,11 @@ Editor::snap_to (nframes_t& start, int32_t direction, bool for_mark) return; } - const nframes_t one_second = session->frame_rate(); - const nframes_t one_minute = session->frame_rate() * 60; - - nframes_t presnap = start; + const nframes64_t one_second = session->frame_rate(); + const nframes64_t one_minute = session->frame_rate() * 60; + const nframes64_t one_smpte_second = (nframes64_t)(rint(session->smpte_frames_per_second()) * session->frames_per_smpte_frame()); + nframes64_t one_smpte_minute = (nframes64_t)(rint(session->smpte_frames_per_second()) * session->frames_per_smpte_frame() * 60); + nframes64_t presnap = start; switch (snap_type) { case SnapToFrame: @@ -2328,8 +2348,9 @@ Editor::snap_to (nframes_t& start, int32_t direction, bool for_mark) start = (nframes_t) floor ((double) start / (one_second / 75)) * (one_second / 75); } break; + case SnapToSMPTEFrame: - if (direction) { + if (fmod((double)start, (double)session->frames_per_smpte_frame()) > (session->frames_per_smpte_frame() / 2)) { start = (nframes_t) (ceil ((double) start / session->frames_per_smpte_frame()) * session->frames_per_smpte_frame()); } else { start = (nframes_t) (floor ((double) start / session->frames_per_smpte_frame()) * session->frames_per_smpte_frame()); @@ -2343,10 +2364,10 @@ Editor::snap_to (nframes_t& start, int32_t direction, bool for_mark) } else { start -= session->smpte_offset (); } - if (direction > 0) { - start = (nframes_t) ceil ((double) start / one_second) * one_second; + if (start % one_smpte_second > one_smpte_second / 2) { + start = (nframes_t) ceil ((double) start / one_smpte_second) * one_smpte_second; } else { - start = (nframes_t) floor ((double) start / one_second) * one_second; + start = (nframes_t) floor ((double) start / one_smpte_second) * one_smpte_second; } if (session->smpte_offset_negative()) @@ -2364,10 +2385,10 @@ Editor::snap_to (nframes_t& start, int32_t direction, bool for_mark) } else { start -= session->smpte_offset (); } - if (direction) { - start = (nframes_t) ceil ((double) start / one_minute) * one_minute; + if (start % one_smpte_minute > one_smpte_minute / 2) { + start = (nframes_t) ceil ((double) start / one_smpte_minute) * one_smpte_minute; } else { - start = (nframes_t) floor ((double) start / one_minute) * one_minute; + start = (nframes_t) floor ((double) start / one_smpte_minute) * one_smpte_minute; } if (session->smpte_offset_negative()) { @@ -2378,7 +2399,7 @@ Editor::snap_to (nframes_t& start, int32_t direction, bool for_mark) break; case SnapToSeconds: - if (direction) { + if (start % one_second > one_second / 2) { start = (nframes_t) ceil ((double) start / one_second) * one_second; } else { start = (nframes_t) floor ((double) start / one_second) * one_second; @@ -2386,7 +2407,7 @@ Editor::snap_to (nframes_t& start, int32_t direction, bool for_mark) break; case SnapToMinutes: - if (direction) { + if (start % one_minute > one_minute / 2) { start = (nframes_t) ceil ((double) start / one_minute) * one_minute; } else { start = (nframes_t) floor ((double) start / one_minute) * one_minute; @@ -2421,8 +2442,8 @@ Editor::snap_to (nframes_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: @@ -2618,37 +2639,33 @@ Editor::setup_toolbar () zoom_box.set_border_width (2); zoom_in_button.set_name ("EditorTimeButton"); + zoom_in_button.set_size_request(-1,16); zoom_in_button.add (*(manage (new Image (::get_icon("zoom_in"))))); zoom_in_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::temporal_zoom_step), false)); ARDOUR_UI::instance()->tooltips().set_tip (zoom_in_button, _("Zoom In")); zoom_out_button.set_name ("EditorTimeButton"); + zoom_out_button.set_size_request(-1,16); zoom_out_button.add (*(manage (new Image (::get_icon("zoom_out"))))); zoom_out_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::temporal_zoom_step), true)); ARDOUR_UI::instance()->tooltips().set_tip (zoom_out_button, _("Zoom Out")); zoom_out_full_button.set_name ("EditorTimeButton"); + zoom_out_full_button.set_size_request(-1,16); zoom_out_full_button.add (*(manage (new Image (::get_icon("zoom_full"))))); zoom_out_full_button.signal_clicked().connect (mem_fun(*this, &Editor::temporal_zoom_session)); ARDOUR_UI::instance()->tooltips().set_tip (zoom_out_full_button, _("Zoom to Session")); - - zoom_box.pack_start (zoom_out_button, false, false); - zoom_box.pack_start (zoom_in_button, false, false); - zoom_box.pack_start (zoom_range_clock, false, false); - zoom_box.pack_start (zoom_out_full_button, false, false); - - ARDOUR_UI::instance()->tooltips().set_tip (zoom_range_clock, _("Current Zoom Range\n(Width of visible area)")); zoom_focus_selector.set_name ("ZoomFocusSelector"); - Gtkmm2ext::set_size_request_to_display_given_text (zoom_focus_selector, "Focus Center", 2+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")); - zoom_box.pack_start (zoom_focus_selector, false, false); - - - /* Edit Cursor / Snap */ + zoom_box.pack_start (zoom_focus_selector, true, true); + zoom_box.pack_start (zoom_out_button, false, false); + zoom_box.pack_start (zoom_in_button, false, false); + zoom_box.pack_start (zoom_out_full_button, false, false); snap_box.set_spacing (1); snap_box.set_border_width (2); @@ -2657,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 */ @@ -2708,7 +2732,7 @@ Editor::setup_toolbar () hbox->pack_start (snap_box, false, false); - hbox->pack_start (zoom_box, false, false); + // hbox->pack_start (zoom_box, false, false); hbox->pack_start (*nudge_box, false, false); hbox->show_all (); @@ -2738,7 +2762,7 @@ Editor::convert_drop_to_paths (vector& paths, vector uris = data.get_uris(); if (uris.empty()) { - + /* This is seriously fucked up. Nautilus doesn't say that its URI lists are actually URI lists. So do it by hand. */ @@ -2788,10 +2812,35 @@ Editor::convert_drop_to_paths (vector& paths, } for (vector::iterator i = uris.begin(); i != uris.end(); ++i) { + if ((*i).substr (0,7) == "file://") { - string p = *i; + + + ustring p = *i; PBD::url_decode (p); - paths.push_back (p.substr (7)); + + // scan forward past three slashes + + ustring::size_type slashcnt = 0; + ustring::size_type n = 0; + ustring::iterator x = p.begin(); + + while (slashcnt < 3 && x != p.end()) { + if ((*x) == '/') { + slashcnt++; + } else if (slashcnt == 3) { + break; + } + ++n; + ++x; + } + + if (slashcnt != 3 || x == p.end()) { + error << _("malformed URL passed to drag-n-drop code") << endmsg; + continue; + } + + paths.push_back (p.substr (n - 1)); } } @@ -2812,6 +2861,8 @@ Editor::map_transport_state () if (session->transport_stopped()) { have_pending_keyboard_selection = false; } + + update_loop_range_view (true); } /* UNDO/REDO */ @@ -2859,7 +2910,7 @@ void Editor::begin_reversible_command (string name) { if (session) { - before = &get_state(); + // before = &get_state(); session->begin_reversible_command (name); } } @@ -2868,435 +2919,13 @@ void Editor::commit_reversible_command () { if (session) { - session->commit_reversible_command (new MementoCommand(*this, before, &get_state())); + // session->commit_reversible_command (new MementoCommand(*this, before, &get_state())); + session->commit_reversible_command (); } } -bool -Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove) -{ - bool commit = false; - - switch (op) { - case Selection::Toggle: - if (selection->selected (&view)) { - if (!no_remove) { - selection->remove (&view); - commit = true; - } - } else { - selection->add (&view); - commit = false; - } - break; - - case Selection::Add: - if (!selection->selected (&view)) { - selection->add (&view); - commit = true; - } - break; - - case Selection::Set: - if (selection->selected (&view) && selection->tracks.size() == 1) { - /* no commit necessary */ - } - - selection->set (&view); - break; - - case Selection::Extend: - /* not defined yet */ - break; - } - - return commit; -} - -bool -Editor::set_selected_track_from_click (Selection::Operation op, bool no_remove) -{ - if (!clicked_trackview) { - return false; - } - - return set_selected_track (*clicked_trackview, op, no_remove); -} - -bool -Editor::set_selected_control_point_from_click (Selection::Operation op, bool no_remove) -{ - if (!clicked_control_point) { - return false; - } - - /* select this point and any others that it represents */ - - double y1, y2; - nframes_t x1, x2; - - x1 = pixel_to_frame (clicked_control_point->get_x() - 10); - x2 = pixel_to_frame (clicked_control_point->get_x() + 10); - y1 = clicked_control_point->get_x() - 10; - y2 = clicked_control_point->get_y() + 10; - - return select_all_within (x1, x2, y1, y2, op); -} - -void -Editor::get_relevant_audio_tracks (set& relevant_tracks) -{ - /* step one: get all selected tracks and all tracks in the relevant edit groups */ - - for (TrackSelection::iterator ti = selection->tracks.begin(); ti != selection->tracks.end(); ++ti) { - - AudioTimeAxisView* atv = dynamic_cast(*ti); - - if (!atv) { - continue; - } - - RouteGroup* group = atv->route()->edit_group(); - - if (group && group->is_active()) { - - /* active group for this track, loop over all tracks and get every member of the group */ - - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - - AudioTimeAxisView* tatv; - - if ((tatv = dynamic_cast (*i)) != 0) { - - if (tatv->route()->edit_group() == group) { - relevant_tracks.insert (tatv); - } - } - } - } else { - relevant_tracks.insert (atv); - } - } -} - -void -Editor::mapover_audio_tracks (slot sl) -{ - set relevant_tracks; - - get_relevant_audio_tracks (relevant_tracks); - - uint32_t sz = relevant_tracks.size(); - - for (set::iterator ati = relevant_tracks.begin(); ati != relevant_tracks.end(); ++ati) { - sl (**ati, sz); - } -} - -void -Editor::mapped_set_selected_regionview_from_click (RouteTimeAxisView& tv, uint32_t ignored, - RegionView* basis, vector* all_equivs) -{ - Playlist* pl; - vector > results; - RegionView* marv; - boost::shared_ptr ds; - - if ((ds = tv.get_diskstream()) == 0) { - /* bus */ - return; - } - - if (&tv == &basis->get_time_axis_view()) { - /* looking in same track as the original */ - return; - } - - - if ((pl = dynamic_cast(ds->playlist())) != 0) { - pl->get_equivalent_regions (basis->region(), results); - } - - for (vector >::iterator ir = results.begin(); ir != results.end(); ++ir) { - if ((marv = tv.view()->find_view (*ir)) != 0) { - all_equivs->push_back (marv); - } - } -} - -bool -Editor::set_selected_regionview_from_click (bool press, Selection::Operation op, bool no_track_remove) -{ - vector all_equivalent_regions; - bool commit = false; - - if (!clicked_regionview || !clicked_audio_trackview) { - return false; - } - - if (op == Selection::Toggle || op == Selection::Set) { - - mapover_audio_tracks (bind (mem_fun (*this, &Editor::mapped_set_selected_regionview_from_click), - clicked_regionview, &all_equivalent_regions)); - - - /* add clicked regionview since we skipped all other regions in the same track as the one it was in */ - - all_equivalent_regions.push_back (clicked_regionview); - - switch (op) { - case Selection::Toggle: - - if (clicked_regionview->get_selected()) { - if (press) { - - /* whatever was clicked was selected already; do nothing here but allow - the button release to deselect it - */ - - button_release_can_deselect = true; - - } else { - - if (button_release_can_deselect) { - - /* just remove this one region, but only on a permitted button release */ - - selection->remove (clicked_regionview); - commit = true; - - /* no more deselect action on button release till a new press - finds an already selected object. - */ - - button_release_can_deselect = false; - } - } - - } else { - - if (press) { - /* add all the equivalent regions, but only on button press */ - - if (!all_equivalent_regions.empty()) { - commit = true; - } - - for (vector::iterator i = all_equivalent_regions.begin(); i != all_equivalent_regions.end(); ++i) { - selection->add (*i); - } - } - } - break; - - case Selection::Set: - if (!clicked_regionview->get_selected()) { - selection->set (all_equivalent_regions); - commit = true; - } else { - /* no commit necessary: clicked on an already selected region */ - goto out; - } - break; - - default: - /* silly compiler */ - break; - } - - } else if (op == Selection::Extend) { - - list results; - nframes_t last_frame; - nframes_t first_frame; - - /* 1. find the last selected regionview in the track that was clicked in */ - - last_frame = 0; - first_frame = max_frames; - - for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) { - if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) { - - if ((*x)->region()->last_frame() > last_frame) { - last_frame = (*x)->region()->last_frame(); - } - - if ((*x)->region()->first_frame() < first_frame) { - first_frame = (*x)->region()->first_frame(); - } - } - } - - /* 2. figure out the boundaries for our search for new objects */ - - switch (clicked_regionview->region()->coverage (first_frame, last_frame)) { - case OverlapNone: - if (last_frame < clicked_regionview->region()->first_frame()) { - first_frame = last_frame; - last_frame = clicked_regionview->region()->last_frame(); - } else { - last_frame = first_frame; - first_frame = clicked_regionview->region()->first_frame(); - } - break; - - case OverlapExternal: - if (last_frame < clicked_regionview->region()->first_frame()) { - first_frame = last_frame; - last_frame = clicked_regionview->region()->last_frame(); - } else { - last_frame = first_frame; - first_frame = clicked_regionview->region()->first_frame(); - } - break; - - case OverlapInternal: - if (last_frame < clicked_regionview->region()->first_frame()) { - first_frame = last_frame; - last_frame = clicked_regionview->region()->last_frame(); - } else { - last_frame = first_frame; - first_frame = clicked_regionview->region()->first_frame(); - } - break; - - case OverlapStart: - case OverlapEnd: - /* nothing to do except add clicked region to selection, since it - overlaps with the existing selection in this track. - */ - break; - } - - /* 2. find all selectable objects (regionviews in this case) between that one and the end of the - one that was clicked. - */ - - set relevant_tracks; - - get_relevant_audio_tracks (relevant_tracks); - - for (set::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) { - (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results); - } - - /* 3. convert to a vector of audio regions */ - - vector regions; - - for (list::iterator x = results.begin(); x != results.end(); ++x) { - RegionView* arv; - - if ((arv = dynamic_cast(*x)) != 0) { - regions.push_back (arv); - } - } - - if (!regions.empty()) { - selection->add (regions); - commit = true; - } - } - - out: - return commit; -} - -void -Editor::set_selected_regionview_from_region_list (boost::shared_ptr region, Selection::Operation op) -{ - vector all_equivalent_regions; - - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - - RouteTimeAxisView* tatv; - - if ((tatv = dynamic_cast (*i)) != 0) { - - Playlist* pl; - vector > results; - RegionView* marv; - boost::shared_ptr ds; - - if ((ds = tatv->get_diskstream()) == 0) { - /* bus */ - continue; - } - - if ((pl = dynamic_cast(ds->playlist())) != 0) { - pl->get_region_list_equivalent_regions (region, results); - } - - for (vector >::iterator ir = results.begin(); ir != results.end(); ++ir) { - if ((marv = tatv->view()->find_view (*ir)) != 0) { - all_equivalent_regions.push_back (marv); - } - } - - } - } - - begin_reversible_command (_("set selected regions")); - - switch (op) { - case Selection::Toggle: - /* XXX this is not correct */ - selection->toggle (all_equivalent_regions); - break; - case Selection::Set: - selection->set (all_equivalent_regions); - break; - case Selection::Extend: - selection->add (all_equivalent_regions); - break; - case Selection::Add: - selection->add (all_equivalent_regions); - break; - } - - commit_reversible_command () ; -} - -bool -Editor::set_selected_regionview_from_map_event (GdkEventAny* ev, StreamView* sv, boost::weak_ptr weak_r) -{ - RegionView* rv; - boost::shared_ptr r (weak_r.lock()); - - if (!r) { - return true; - } - - boost::shared_ptr ar; - - if ((ar = boost::dynamic_pointer_cast (r)) == 0) { - return true; - } - - if ((rv = sv->find_view (ar)) == 0) { - return true; - } - - /* don't reset the selection if its something other than - a single other region. - */ - - if (selection->regions.size() > 1) { - return true; - } - - begin_reversible_command (_("set selected regions")); - - selection->set (rv); - - commit_reversible_command () ; - - return true; -} - -void -Editor::set_edit_group_solo (Route& route, bool yn) +void +Editor::set_edit_group_solo (Route& route, bool yn) { RouteGroup *edit_group; @@ -3346,32 +2975,34 @@ Editor::history_changed () void Editor::duplicate_dialog (bool dup_region) { - if (dup_region) { - if (clicked_regionview == 0) { - return; - } - } else { - if (selection->time.length() == 0) { - return; - } + if (selection->regions.empty() && (selection->time.length() == 0)) { + return; } ArdourDialog win ("duplicate dialog"); - Entry entry; Label label (_("Duplicate how many times?")); + Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0); + SpinButton spinner (adjustment); + win.get_vbox()->set_spacing (12); win.get_vbox()->pack_start (label); - win.add_action_widget (entry, RESPONSE_ACCEPT); + + /* dialogs have ::add_action_widget() but that puts the spinner in the wrong + place, visually. so do this by hand. + */ + + win.get_vbox()->pack_start (spinner); + spinner.signal_activate().connect (sigc::bind (mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT)); + + label.show (); + spinner.show (); + win.add_button (Stock::OK, RESPONSE_ACCEPT); win.add_button (Stock::CANCEL, RESPONSE_CANCEL); win.set_position (WIN_POS_MOUSE); - entry.set_text ("1"); - set_size_request_to_display_given_text (entry, X_("12345678"), 20, 15); - entry.select_region (0, -1); - entry.grab_focus (); - + spinner.grab_focus (); switch (win.run ()) { case RESPONSE_ACCEPT: @@ -3380,17 +3011,12 @@ Editor::duplicate_dialog (bool dup_region) return; } - string text = entry.get_text(); - float times; + float times = adjustment.get_value(); - if (sscanf (text.c_str(), "%f", ×) == 1) { - if (dup_region) { - RegionSelection regions; - regions.add (clicked_regionview); - duplicate_some_regions (regions, times); - } else { - duplicate_selection (times); - } + if (!selection->regions.empty()) { + duplicate_some_regions (selection->regions, times); + } else { + duplicate_selection (times); } } @@ -3467,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")) { @@ -3519,103 +3145,60 @@ Editor::snap_mode_selection_done () } void -Editor::zoom_focus_selection_done () +Editor::edit_point_selection_done () { - string choice = zoom_focus_selector.get_active_text(); - ZoomFocus focus_type = ZoomFocusLeft; + string choice = edit_point_selector.get_active_text(); + EditPoint ep = EditAtSelectedMarker; - if (choice == _("Left")) { - focus_type = ZoomFocusLeft; - } else if (choice == _("Right")) { - focus_type = ZoomFocusRight; - } else if (choice == _("Center")) { - focus_type = ZoomFocusCenter; + if (choice == _("Marker")) { + _edit_point = EditAtSelectedMarker; } else if (choice == _("Playhead")) { - focus_type = ZoomFocusPlayhead; - } else if (choice == _("Edit Cursor")) { - focus_type = ZoomFocusEdit; - } - - RefPtr ract = zoom_focus_action (focus_type); - - if (ract) { - ract->set_active (); - } -} - -gint -Editor::edit_controls_button_release (GdkEventButton* ev) -{ - if (Keyboard::is_context_menu_event (ev)) { - ARDOUR_UI::instance()->add_route (); - } - return TRUE; -} - -void -Editor::track_selection_changed () -{ - switch (selection->tracks.size()){ - case 0: - break; - default: - set_selected_mixer_strip (*(selection->tracks.front())); - break; + _edit_point = EditAtPlayhead; + } else { + _edit_point = EditAtMouse; } - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - (*i)->set_selected (false); - if (mouse_mode == MouseRange) { - (*i)->hide_selection (); - } - } + RefPtr ract = edit_point_action (ep); - for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) { - (*i)->set_selected (true); - if (mouse_mode == MouseRange) { - (*i)->show_selection (selection->time); - } + if (ract) { + ract->set_active (true); } } void -Editor::time_selection_changed () +Editor::zoom_focus_selection_done () { - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - (*i)->hide_selection (); - } - - if (selection->tracks.empty()) { - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - (*i)->show_selection (selection->time); - } - } else { - for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) { - (*i)->show_selection (selection->time); - } - } + string choice = zoom_focus_selector.get_active_text(); + ZoomFocus focus_type = ZoomFocusLeft; - if (selection->time.empty()) { - ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false); - } else { - ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true); - } -} + if (choice == _("Left")) { + focus_type = ZoomFocusLeft; + } else if (choice == _("Right")) { + focus_type = ZoomFocusRight; + } else if (choice == _("Center")) { + focus_type = ZoomFocusCenter; + } else if (choice == _("Playhead")) { + focus_type = ZoomFocusPlayhead; + } else if (choice == _("Mouse")) { + focus_type = ZoomFocusMouse; + } else if (choice == _("Edit Point")) { + focus_type = ZoomFocusEdit; + } + + RefPtr ract = zoom_focus_action (focus_type); -void -Editor::region_selection_changed () -{ - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - (*i)->set_selected_regionviews (selection->regions); + if (ract) { + ract->set_active (); } -} +} -void -Editor::point_selection_changed () +gint +Editor::edit_controls_button_release (GdkEventButton* ev) { - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - (*i)->set_selected_points (selection->points); + if (Keyboard::is_context_menu_event (ev)) { + ARDOUR_UI::instance()->add_route (this); } + return TRUE; } gint @@ -3624,10 +3207,10 @@ Editor::mouse_select_button_release (GdkEventButton* ev) /* this handles just right-clicks */ if (ev->button != 3) { - return FALSE; + return false; } - return TRUE; + return true; } Editor::TrackViewList * @@ -3762,7 +3345,6 @@ Editor::set_show_measures (bool yn) if ((_show_measures = yn) == true) { draw_measures (); } - DisplayControlChanged (ShowMeasures); instant_save (); } } @@ -3785,27 +3367,38 @@ Editor::set_follow_playhead (bool yn) /* catch up */ update_current_screen (); } - DisplayControlChanged (FollowPlayhead); instant_save (); } } void -Editor::toggle_xfade_active (Crossfade* xfade) +Editor::toggle_xfade_active (boost::weak_ptr wxfade) { - xfade->set_active (!xfade->active()); + boost::shared_ptr xfade (wxfade.lock()); + if (xfade) { + xfade->set_active (!xfade->active()); + } } void -Editor::toggle_xfade_length (Crossfade* xfade) +Editor::toggle_xfade_length (boost::weak_ptr wxfade) { - xfade->set_follow_overlap (!xfade->following_overlap()); + boost::shared_ptr xfade (wxfade.lock()); + if (xfade) { + xfade->set_follow_overlap (!xfade->following_overlap()); + } } void -Editor::edit_xfade (Crossfade* xfade) +Editor::edit_xfade (boost::weak_ptr wxfade) { - CrossfadeEditor cew (*session, *xfade, xfade->fade_in().get_min_y(), 1.0); + boost::shared_ptr xfade (wxfade.lock()); + + if (!xfade) { + return; + } + + CrossfadeEditor cew (*session, xfade, xfade->fade_in().get_min_y(), 1.0); ensure_float (cew); @@ -3845,19 +3438,21 @@ Editor::end_location_changed (Location* location) } int -Editor::playlist_deletion_dialog (Playlist* pl) +Editor::playlist_deletion_dialog (boost::shared_ptr pl) { ArdourDialog dialog ("playlist deletion dialog"); Label label (string_compose (_("Playlist %1 is currently unused.\n" - "If left alone, no audio files used by it will be cleaned.\n" - "If deleted, audio files used by it alone by will cleaned."), - pl->name())); - + "If left alone, no audio files used by it will be cleaned.\n" + "If deleted, audio files used by it alone by will cleaned."), + pl->name())); + dialog.set_position (WIN_POS_CENTER); dialog.get_vbox()->pack_start (label); + label.show (); + dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT); - dialog.add_button (_("Keep playlist"), RESPONSE_CANCEL); + dialog.add_button (_("Keep playlist"), RESPONSE_REJECT); dialog.add_button (_("Cancel"), RESPONSE_CANCEL); switch (dialog.run ()) { @@ -3941,6 +3536,9 @@ Editor::control_layout_scroll (GdkEventScroll* ev) return false; } + +/** A new snapshot has been selected. + */ void Editor::snapshot_display_selection_changed () { @@ -3965,7 +3563,93 @@ Editor::snapshot_display_selection_changed () bool Editor::snapshot_display_button_press (GdkEventButton* ev) { - return false; + if (ev->button == 3) { + /* Right-click on the snapshot list. Work out which snapshot it + was over. */ + Gtk::TreeModel::Path path; + Gtk::TreeViewColumn* col; + int cx; + int cy; + snapshot_display.get_path_at_pos ((int) ev->x, (int) ev->y, path, col, cx, cy); + Gtk::TreeModel::iterator iter = snapshot_display_model->get_iter (path); + if (iter) { + Gtk::TreeModel::Row row = *iter; + popup_snapshot_context_menu (ev->button, ev->time, row[snapshot_display_columns.real_name]); + } + return true; + } + + return false; +} + + +/** Pop up the snapshot display context menu. + * @param button Button used to open the menu. + * @param time Menu open time. + * @snapshot_name Name of the snapshot that the menu click was over. + */ + +void +Editor::popup_snapshot_context_menu (int button, int32_t time, Glib::ustring snapshot_name) +{ + using namespace Menu_Helpers; + + MenuList& items (snapshot_context_menu.items()); + items.clear (); + + const bool modification_allowed = (session->snap_name() != snapshot_name && session->name() != snapshot_name); + + items.push_back (MenuElem (_("Remove"), bind (mem_fun (*this, &Editor::remove_snapshot), snapshot_name))); + if (!modification_allowed) { + items.back().set_sensitive (false); + } + + items.push_back (MenuElem (_("Rename"), bind (mem_fun (*this, &Editor::rename_snapshot), snapshot_name))); + if (!modification_allowed) { + items.back().set_sensitive (false); + } + + snapshot_context_menu.popup (button, time); +} + +void +Editor::rename_snapshot (Glib::ustring old_name) +{ + ArdourPrompter prompter(true); + + string new_name; + + prompter.set_name ("Prompter"); + prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT); + prompter.set_prompt (_("New name of snapshot")); + prompter.set_initial_text (old_name); + + if (prompter.run() == RESPONSE_ACCEPT) { + prompter.get_result (new_name); + if (new_name.length()) { + session->rename_state (old_name, new_name); + redisplay_snapshots (); + } + } +} + + +void +Editor::remove_snapshot (Glib::ustring name) +{ + vector choices; + + std::string prompt = string_compose (_("Do you really want to remove snapshot \"%1\" ?\n(cannot be undone)"), name); + + choices.push_back (_("No, do nothing.")); + choices.push_back (_("Yes, remove it.")); + + Gtkmm2ext::Choice prompter (prompt, choices); + + if (prompter.run () == 1) { + session->remove_state (name); + redisplay_snapshots (); + } } void @@ -3987,15 +3671,16 @@ Editor::redisplay_snapshots () string statename = *(*i); TreeModel::Row row = *(snapshot_display_model->append()); - // we don't have a way of changing the rendering in just one TreeView - // cell so just put an asterisk on each side of the name for now. + /* this lingers on in case we ever want to change the visible + name of the snapshot. + */ + string display_name; + display_name = statename; + if (statename == session->snap_name()) { - display_name = "*"+statename+"*"; snapshot_display.get_selection()->select(row); - } else { - display_name = statename; - } + } row[snapshot_display_columns.visible_name] = display_name; row[snapshot_display_columns.real_name] = statename; @@ -4058,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 (); } @@ -4106,3 +3811,277 @@ Editor::on_key_press_event (GdkEventKey* ev) return key_press_focus_accelerator_handler (*this, ev); } +void +Editor::reset_x_origin (nframes_t frame) +{ + queue_visual_change (frame); +} + +void +Editor::reset_zoom (double fpu) +{ + queue_visual_change (fpu); +} + +void +Editor::reposition_and_zoom (nframes_t frame, double fpu) +{ + reset_x_origin (frame); + reset_zoom (fpu); +} + +void +Editor::set_frames_per_unit (double fpu) +{ + nframes_t frames; + + /* this is the core function that controls the zoom level of the canvas. it is called + whenever one or more calls are made to reset_zoom(). it executes in an idle handler. + */ + + if (fpu == frames_per_unit) { + return; + } + + if (fpu < 2.0) { + fpu = 2.0; + } + + // convert fpu to frame count + + frames = (nframes_t) floor (fpu * canvas_width); + + /* don't allow zooms that fit more than the maximum number + of frames into an 800 pixel wide space. + */ + + if (max_frames / fpu < 800.0) { + return; + } + + if (fpu == frames_per_unit) { + return; + } + + frames_per_unit = fpu; + + if (frames != zoom_range_clock.current_duration()) { + zoom_range_clock.set (frames); + } + + if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) { + if (!selection->tracks.empty()) { + for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) { + (*i)->reshow_selection (selection->time); + } + } else { + for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { + (*i)->reshow_selection (selection->time); + } + } + } + + ZoomChanged (); /* EMIT_SIGNAL */ + + reset_hscrollbar_stepping (); + reset_scrolling_region (); + + if (playhead_cursor) playhead_cursor->set_position (playhead_cursor->current_frame); + + instant_save (); +} + +void +Editor::queue_visual_change (nframes_t where) +{ + pending_visual_change.pending = VisualChange::Type (pending_visual_change.pending | VisualChange::TimeOrigin); + pending_visual_change.time_origin = where; + + if (pending_visual_change.idle_handler_id < 0) { + pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this); + } +} + +void +Editor::queue_visual_change (double fpu) +{ + pending_visual_change.pending = VisualChange::Type (pending_visual_change.pending | VisualChange::ZoomLevel); + pending_visual_change.frames_per_unit = fpu; + + if (pending_visual_change.idle_handler_id < 0) { + pending_visual_change.idle_handler_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE, _idle_visual_changer, this, 0); + } +} + +int +Editor::_idle_visual_changer (void* arg) +{ + return static_cast(arg)->idle_visual_changer (); +} + +int +Editor::idle_visual_changer () +{ + VisualChange::Type p = pending_visual_change.pending; + + pending_visual_change.pending = (VisualChange::Type) 0; + pending_visual_change.idle_handler_id = -1; + + if (p & VisualChange::ZoomLevel) { + set_frames_per_unit (pending_visual_change.frames_per_unit); + } + + if (p & VisualChange::TimeOrigin) { + if (pending_visual_change.time_origin != leftmost_frame) { + horizontal_adjustment.set_value (pending_visual_change.time_origin/frames_per_unit); + /* the signal handler will do the rest */ + } else { + update_fixed_rulers(); + redisplay_tempo (true); + } + } + + return 0; /* this is always a one-shot call */ +} + +struct EditorOrderTimeAxisSorter { + bool operator() (const TimeAxisView* a, const TimeAxisView* b) const { + return a->order < b->order; + } +}; + +void +Editor::sort_track_selection () +{ + EditorOrderTimeAxisSorter cmp; + selection->tracks.sort (cmp); +} + +nframes64_t +Editor::get_preferred_edit_position() const +{ + 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 -1; +} + +void +Editor::set_loop_range (nframes_t start, nframes_t end, string cmd) +{ + if (!session) return; + + begin_reversible_command (cmd); + + Location* tll; + + if ((tll = transport_loop_location()) == 0) { + Location* loc = new Location (start, end, _("Loop"), Location::IsAutoLoop); + XMLNode &before = session->locations()->get_state(); + session->locations()->add (loc, true); + session->set_auto_loop_location (loc); + XMLNode &after = session->locations()->get_state(); + session->add_command (new MementoCommand(*(session->locations()), &before, &after)); + } + else { + XMLNode &before = tll->get_state(); + tll->set_hidden (false, this); + tll->set (start, end); + XMLNode &after = tll->get_state(); + session->add_command (new MementoCommand(*tll, &before, &after)); + } + + commit_reversible_command (); +} + +void +Editor::set_punch_range (nframes_t start, nframes_t end, string cmd) +{ + if (!session) return; + + begin_reversible_command (cmd); + + Location* tpl; + + if ((tpl = transport_punch_location()) == 0) { + Location* loc = new Location (start, end, _("Loop"), Location::IsAutoPunch); + XMLNode &before = session->locations()->get_state(); + session->locations()->add (loc, true); + session->set_auto_loop_location (loc); + XMLNode &after = session->locations()->get_state(); + session->add_command (new MementoCommand(*(session->locations()), &before, &after)); + } + else { + XMLNode &before = tpl->get_state(); + tpl->set_hidden (false, this); + tpl->set (start, end); + XMLNode &after = tpl->get_state(); + session->add_command (new MementoCommand(*tpl, &before, &after)); + } + + 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(*t); + + if (atv) { + boost::shared_ptr ds; + boost::shared_ptr 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; +}