X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Feditor.cc;h=ff9d480a3549f947d321992c3239867f6b70cb35;hb=dc50730059771b4b2899ffb4c3b5ef49d5ffae91;hp=cfbbf6de037a36e9cf918bf8e9152cea1d06f6cd;hpb=bb5cd8aa32b9b4e5ce472e44068f903e14fcecfd;p=ardour.git diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index cfbbf6de03..ff9d480a35 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -68,6 +68,7 @@ #include "ardour/audio_track.h" #include "ardour/audioengine.h" #include "ardour/audioregion.h" +#include "ardour/lmath.h" #include "ardour/location.h" #include "ardour/profile.h" #include "ardour/route_group.h" @@ -80,7 +81,6 @@ #include "control_protocol/control_protocol.h" -#include "actions.h" #include "actions.h" #include "analysis_window.h" #include "audio_clock.h" @@ -107,10 +107,12 @@ #include "gui_thread.h" #include "keyboard.h" #include "marker.h" +#include "midi_region_view.h" #include "midi_time_axis.h" #include "mixer_strip.h" #include "mixer_ui.h" #include "mouse_cursors.h" +#include "note_base.h" #include "playlist_selector.h" #include "public_editor.h" #include "region_layering_order_editor.h" @@ -120,6 +122,7 @@ #include "sfdb_ui.h" #include "tempo_lines.h" #include "time_axis_view.h" +#include "timers.h" #include "utils.h" #include "verbose_cursor.h" @@ -138,7 +141,7 @@ using PBD::internationalize; using PBD::atoi; using Gtkmm2ext::Keyboard; -const double Editor::timebar_height = 15.0; +double Editor::timebar_height = 15.0; static const gchar *_snap_type_strings[] = { N_("CD Frames"), @@ -248,6 +251,7 @@ pane_size_watcher (Paned* pane) Editor::Editor () : _join_object_range_state (JOIN_OBJECT_RANGE_NONE) + , _mouse_changed_selection (false) /* time display buttons */ , minsec_label (_("Mins:Secs")) , bbt_label (_("Bars:Beats")) @@ -281,6 +285,8 @@ Editor::Editor () , _tools_tearoff (0) , _toolbar_viewport (*manage (new Gtk::Adjustment (0, 0, 1e10)), *manage (new Gtk::Adjustment (0, 0, 1e10))) + , selection_op_cmd_depth (0) + , selection_op_history_it (0) /* nudge */ @@ -305,13 +311,17 @@ Editor::Editor () selection = new Selection (this); cut_buffer = new Selection (this); + _selection_memento = new SelectionMemento (); + selection_op_history.clear(); + before.clear(); clicked_regionview = 0; clicked_axisview = 0; clicked_routeview = 0; clicked_control_point = 0; last_update_frame = 0; - pre_press_cursor = 0; + last_paste_pos = 0; + paste_count = 0; _drags = new DragManager (this); lock_dialog = 0; ruler_dialog = 0; @@ -388,20 +398,22 @@ Editor::Editor () sfbrowser = 0; - location_marker_color = ARDOUR_UI::config()->get_canvasvar_LocationMarker(); - location_range_color = ARDOUR_UI::config()->get_canvasvar_LocationRange(); - location_cd_marker_color = ARDOUR_UI::config()->get_canvasvar_LocationCDMarker(); - location_loop_color = ARDOUR_UI::config()->get_canvasvar_LocationLoop(); - location_punch_color = ARDOUR_UI::config()->get_canvasvar_LocationPunch(); + location_marker_color = ARDOUR_UI::config()->color ("location marker"); + location_range_color = ARDOUR_UI::config()->color ("location range"); + location_cd_marker_color = ARDOUR_UI::config()->color ("location cd marker"); + location_loop_color = ARDOUR_UI::config()->color ("location loop"); + location_punch_color = ARDOUR_UI::config()->color ("location punch"); - zoom_focus = ZoomFocusLeft; + zoom_focus = ZoomFocusPlayhead; _edit_point = EditAtMouse; - _internal_editing = false; - current_canvas_cursor = 0; - _visible_track_count = 16; + _visible_track_count = -1; samples_per_pixel = 2048; /* too early to use reset_zoom () */ + timebar_height = std::max(12., ceil (15. * ARDOUR_UI::ui_scale)); + TimeAxisView::setup_sizes (); + Marker::setup_sizes (timebar_height); + _scroll_callbacks = 0; bbt_label.set_name ("EditorRulerLabel"); @@ -484,6 +496,8 @@ Editor::Editor () initialize_canvas (); + CairoWidget::set_focus_handler (sigc::mem_fun (*this, &Editor::reset_focus)); + _summary = new EditorSummary (this); selection->TimeChanged.connect (sigc::mem_fun(*this, &Editor::time_selection_changed)); @@ -515,6 +529,9 @@ Editor::Editor () _cursors->set_cursor_set (ARDOUR_UI::config()->get_icon_set()); cerr << "Set cursor set to " << ARDOUR_UI::config()->get_icon_set() << endl; + /* Push default cursor to ever-present bottom of cursor stack. */ + push_canvas_cursor(_cursors->grabber); + ArdourCanvas::GtkCanvas* time_pad = manage (new ArdourCanvas::GtkCanvas ()); ArdourCanvas::Line* pad_line_1 = new ArdourCanvas::Line (time_pad->root()); @@ -551,6 +568,12 @@ Editor::Editor () _snapshots = new EditorSnapshots (this); _locations = new EditorLocations (this); + /* these are static location signals */ + + Location::start_changed.connect (*this, invalidator (*this), boost::bind (&Editor::location_changed, this, _1), gui_context()); + Location::end_changed.connect (*this, invalidator (*this), boost::bind (&Editor::location_changed, this, _1), gui_context()); + Location::changed.connect (*this, invalidator (*this), boost::bind (&Editor::location_changed, this, _1), gui_context()); + add_notebook_page (_("Regions"), _regions->widget ()); add_notebook_page (_("Tracks & Busses"), _routes->widget ()); add_notebook_page (_("Snapshots"), _snapshots->widget ()); @@ -658,7 +681,6 @@ Editor::Editor () _snap_mode = SnapOff; set_snap_mode (_snap_mode); set_mouse_mode (MouseObject, true); - pre_internal_mouse_mode = MouseObject; pre_internal_snap_type = _snap_type; pre_internal_snap_mode = _snap_mode; internal_snap_type = _snap_type; @@ -865,7 +887,7 @@ Editor::set_entered_regionview (RegionView* rv) entered_regionview = rv; if (entered_regionview != 0) { - entered_regionview->entered (internal_editing ()); + entered_regionview->entered (); } if (!_all_region_actions_sensitized && _last_region_menu_was_main) { @@ -1055,12 +1077,12 @@ Editor::control_scroll (float fraction) _dragging_playhead = true; } - if ((fraction < 0.0f) && (*_control_scroll_target < (framepos_t) fabs(step))) { + if ((fraction < 0.0f) && (*_control_scroll_target <= (framepos_t) fabs(step))) { *_control_scroll_target = 0; } else if ((fraction > 0.0f) && (max_framepos - *_control_scroll_target < step)) { *_control_scroll_target = max_framepos - (current_page_samples()*2); // allow room for slop in where the PH is on the screen } else { - *_control_scroll_target += (framepos_t) floor (step); + *_control_scroll_target += (framepos_t) trunc (step); } /* move visuals, we'll catch up with it later */ @@ -1149,9 +1171,28 @@ Editor::generic_event_handler (GdkEvent* ev) case GDK_KEY_RELEASE: gettimeofday (&last_event_time, 0); break; + + case GDK_LEAVE_NOTIFY: + switch (ev->crossing.detail) { + case GDK_NOTIFY_UNKNOWN: + case GDK_NOTIFY_INFERIOR: + case GDK_NOTIFY_ANCESTOR: + break; + case GDK_NOTIFY_VIRTUAL: + case GDK_NOTIFY_NONLINEAR: + case GDK_NOTIFY_NONLINEAR_VIRTUAL: + /* leaving window, so reset focus, thus ending any and + all text entry operations. + */ + reset_focus(); + break; + } + break; + default: break; } + return false; } @@ -1329,7 +1370,6 @@ Editor::set_session (Session *t) _session->locations()->added.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_new_location, this, _1), gui_context()); _session->locations()->removed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::location_gone, this, _1), gui_context()); _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context()); - _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context()); _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context()); playhead_cursor->show (); @@ -1346,7 +1386,7 @@ Editor::set_session (Session *t) (static_cast(*i))->set_samples_per_pixel (samples_per_pixel); } - super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect ( + super_rapid_screen_update_connection = Timers::super_rapid_connect ( sigc::mem_fun (*this, &Editor::super_rapid_screen_update) ); @@ -1364,6 +1404,7 @@ Editor::set_session (Session *t) /* register for undo history */ _session->register_with_memento_command_factory(id(), this); + _session->register_with_memento_command_factory(_selection_memento->id(), _selection_memento); ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated)); @@ -1456,8 +1497,10 @@ void Editor::popup_xfade_in_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType /*item_type*/) { using namespace Menu_Helpers; - AudioRegionView* arv = static_cast (item->get_data ("regionview")); - assert(arv); + AudioRegionView* arv = dynamic_cast ((RegionView*)item->get_data ("regionview")); + if (!arv) { + return; + } MenuList& items (xfade_in_context_menu.items()); items.clear (); @@ -1479,8 +1522,10 @@ void Editor::popup_xfade_out_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType /*item_type*/) { using namespace Menu_Helpers; - AudioRegionView* arv = static_cast (item->get_data ("regionview")); - assert(arv); + AudioRegionView* arv = dynamic_cast ((RegionView*)item->get_data ("regionview")); + if (!arv) { + return; + } MenuList& items (xfade_out_context_menu.items()); items.clear (); @@ -1751,10 +1796,18 @@ Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::sha _popup_region_menu_item->set_label (menu_item_name); } - const framepos_t position = get_preferred_edit_position (false, true); + /* No latering allowed in later is higher layering model */ + RefPtr act = ActionManager::get_action (X_("EditorMenu"), X_("RegionMenuLayering")); + if (act && Config->get_layer_model() == LaterHigher) { + act->set_sensitive (false); + } else if (act) { + act->set_sensitive (true); + } + + const framepos_t position = get_preferred_edit_position (EDIT_IGNORE_NONE, true); edit_items.push_back (*_popup_region_menu_item); - if (track->playlist()->count_regions_at (position) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) { + if (Config->get_layer_model() == Manual && track->playlist()->count_regions_at (position) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) { edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region-context-menu")->create_menu_item ())); } edit_items.push_back (SeparatorElem()); @@ -1808,15 +1861,16 @@ Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items) ); edit_items.push_back (SeparatorElem()); - edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection))); + edit_items.push_back (MenuElem (_("Separate"), mem_fun(*this, &Editor::separate_region_from_selection))); edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection))); edit_items.push_back (SeparatorElem()); edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection))); edit_items.push_back (SeparatorElem()); - edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false))); - edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection))); + edit_items.push_back (MenuElem (_("Set Loop from Selection"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false))); + edit_items.push_back (MenuElem (_("Set Punch from Selection"), sigc::mem_fun(*this, &Editor::set_punch_from_selection))); + edit_items.push_back (MenuElem (_("Set Session Start/End from Selection"), sigc::mem_fun(*this, &Editor::set_session_extents_from_selection))); edit_items.push_back (SeparatorElem()); edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection))); @@ -1833,7 +1887,7 @@ Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items) edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true))); edit_items.push_back (MenuElem (_("Export Range..."), sigc::mem_fun(*this, &Editor::export_selection))); if (ARDOUR_UI::instance()->video_timeline->get_duration() > 0) { - edit_items.push_back (MenuElem (_("Export Video Range..."), sigc::bind (sigc::mem_fun(*this, &Editor::export_video), true))); + edit_items.push_back (MenuElem (_("Export Video Range..."), sigc::bind (sigc::mem_fun(*(ARDOUR_UI::instance()), &ARDOUR_UI::export_video), true))); } } @@ -1870,6 +1924,7 @@ Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items) select_items.push_back (SeparatorElem()); select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop))); select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch))); + select_items.push_back (MenuElem (_("Set Range to Selected Regions"), sigc::mem_fun(*this, &Editor::set_selection_from_region))); select_items.push_back (SeparatorElem()); select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true))); select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false))); @@ -1992,6 +2047,12 @@ Editor::set_snap_to (SnapType st) { unsigned int snap_ind = (unsigned int)st; + if (internal_editing()) { + internal_snap_type = st; + } else { + pre_internal_snap_type = st; + } + _snap_type = st; if (snap_ind > snap_type_strings.size() - 1) { @@ -2048,6 +2109,8 @@ Editor::set_snap_to (SnapType st) break; } + redisplay_tempo (false); + SnapChanged (); /* EMIT SIGNAL */ } @@ -2056,7 +2119,7 @@ Editor::set_snap_mode (SnapMode mode) { string str = snap_mode_strings[(int)mode]; - if (_internal_editing) { + if (internal_editing()) { internal_snap_mode = mode; } else { pre_internal_snap_mode = mode; @@ -2070,23 +2133,23 @@ Editor::set_snap_mode (SnapMode mode) instant_save (); } + void Editor::set_edit_point_preference (EditPoint ep, bool force) { bool changed = (_edit_point != ep); _edit_point = ep; - string str = edit_point_strings[(int)ep]; - if (Profile->get_mixbus()) if (ep == EditAtSelectedMarker) ep = EditAtPlayhead; - + + string str = edit_point_strings[(int)ep]; if (str != edit_point_selector.get_text ()) { edit_point_selector.set_text (str); } - reset_canvas_cursor (); + update_all_enter_cursors(); if (!force && !changed) { return; @@ -2176,7 +2239,12 @@ Editor::set_state (const XMLNode& node, int /*version*/) if (_session && (prop = node.property ("playhead"))) { framepos_t pos; sscanf (prop->value().c_str(), "%" PRIi64, &pos); - playhead_cursor->set_position (pos); + if (pos >= 0) { + playhead_cursor->set_position (pos); + } else { + warning << _("Playhead position stored with a negative value - ignored (use zero instead)") << endmsg; + playhead_cursor->set_position (0); + } } else { playhead_cursor->set_position (0); } @@ -2221,7 +2289,6 @@ Editor::set_state (const XMLNode& node, int /*version*/) pre_internal_snap_type = (SnapType) string_2_enum (prop->value(), pre_internal_snap_type); } - if ((prop = node.property ("pre-internal-snap-mode"))) { pre_internal_snap_mode = (SnapMode) string_2_enum (prop->value(), pre_internal_snap_mode); } @@ -2247,16 +2314,6 @@ Editor::set_state (const XMLNode& node, int /*version*/) reset_y_origin (atof (prop->value ())); } - if ((prop = node.property ("internal-edit"))) { - bool yn = string_is_affirmative (prop->value()); - RefPtr act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit")); - if (act) { - RefPtr tact = RefPtr::cast_dynamic(act); - tact->set_active (!yn); - tact->set_active (yn); - } - } - if ((prop = node.property ("join-object-range"))) { RefPtr act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object-range")); bool yn = string_is_affirmative (prop->value()); @@ -2275,37 +2332,16 @@ Editor::set_state (const XMLNode& node, int /*version*/) if ((prop = node.property ("show-measures"))) { bool yn = string_is_affirmative (prop->value()); _show_measures = yn; - RefPtr act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility")); - if (act) { - RefPtr tact = RefPtr::cast_dynamic(act); - /* do it twice to force the change */ - tact->set_active (!yn); - tact->set_active (yn); - } } if ((prop = node.property ("follow-playhead"))) { bool yn = string_is_affirmative (prop->value()); set_follow_playhead (yn); - RefPtr act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead")); - if (act) { - RefPtr tact = RefPtr::cast_dynamic(act); - if (tact->get_active() != yn) { - tact->set_active (yn); - } - } } if ((prop = node.property ("stationary-playhead"))) { bool yn = string_is_affirmative (prop->value()); set_stationary_playhead (yn); - RefPtr act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead")); - if (act) { - RefPtr tact = RefPtr::cast_dynamic(act); - if (tact->get_active() != yn) { - tact->set_active (yn); - } - } } if ((prop = node.property ("region-list-sort-type"))) { @@ -2381,6 +2417,42 @@ Editor::set_state (const XMLNode& node, int /*version*/) nudge_clock->set (_session->frame_rate() * 5, true); } + { + /* apply state + * Not all properties may have been in XML, but + * those that are linked to a private variable may need changing + */ + RefPtr act; + bool yn; + + act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility")); + if (act) { + yn = _show_measures; + RefPtr tact = RefPtr::cast_dynamic(act); + /* do it twice to force the change */ + tact->set_active (!yn); + tact->set_active (yn); + } + + act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead")); + yn = _follow_playhead; + if (act) { + RefPtr tact = RefPtr::cast_dynamic(act); + if (tact->get_active() != yn) { + tact->set_active (yn); + } + } + + act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead")); + yn = _stationary_playhead; + if (act) { + RefPtr tact = RefPtr::cast_dynamic(act); + if (tact->get_active() != yn) { + tact->set_active (yn); + } + } + } + return 0; } @@ -2448,7 +2520,6 @@ Editor::get_state () node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no"); node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ())); node->add_property ("mouse-mode", enum2str(mouse_mode)); - node->add_property ("internal-edit", _internal_editing ? "yes" : "no"); node->add_property ("join-object-range", smart_mode_action->get_active () ? "yes" : "no"); Glib::RefPtr act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer")); @@ -2520,7 +2591,7 @@ Editor::trackview_by_y_position (double y, bool trackview_relative_offset) const * @param event Event to get current key modifier information from, or 0. */ void -Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_t direction, bool for_mark) +Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, RoundMode direction, bool for_mark) { if (!_session || !event) { return; @@ -2538,7 +2609,7 @@ Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_ } void -Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark) +Editor::snap_to (framepos_t& start, RoundMode direction, bool for_mark) { if (!_session || _snap_mode == SnapOff) { return; @@ -2548,14 +2619,27 @@ Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark) } void -Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /*for_mark*/) +Editor::snap_to_no_magnets (framepos_t& start, RoundMode direction, bool for_mark) +{ + if (!_session || _snap_mode == SnapOff) { + return; + } + + snap_to_internal (start, direction, for_mark, true); +} + +void +Editor::timecode_snap_to_internal (framepos_t& start, RoundMode direction, bool /*for_mark*/) { const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame()); framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60); switch (_snap_type) { case SnapToTimecodeFrame: - if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) { + if ((direction == RoundUpMaybe || direction == RoundDownMaybe) && + fmod((double)start, (double)_session->frames_per_timecode_frame()) == 0) { + /* start is already on a whole timecode frame, do nothing */ + } else if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) { start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame()); } else { start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame()); @@ -2568,7 +2652,10 @@ Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /* } else { start -= _session->config.get_timecode_offset (); } - if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) { + if ((direction == RoundUpMaybe || direction == RoundDownMaybe) && + (start % one_timecode_second == 0)) { + /* start is already on a whole second, do nothing */ + } else if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) { start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second; } else { start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second; @@ -2587,7 +2674,10 @@ Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /* } else { start -= _session->config.get_timecode_offset (); } - if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) { + if ((direction == RoundUpMaybe || direction == RoundDownMaybe) && + (start % one_timecode_minute == 0)) { + /* start is already on a whole minute, do nothing */ + } else if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) { start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute; } else { start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute; @@ -2600,12 +2690,12 @@ Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /* break; default: fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg; - /*NOTREACHED*/ + abort(); /*NOTREACHED*/ } } void -Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark) +Editor::snap_to_internal (framepos_t& start, RoundMode direction, bool for_mark, bool no_magnets) { const framepos_t one_second = _session->frame_rate(); const framepos_t one_minute = _session->frame_rate() * 60; @@ -2620,7 +2710,10 @@ Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark) return timecode_snap_to_internal (start, direction, for_mark); case SnapToCDFrame: - if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) { + if ((direction == RoundUpMaybe || direction == RoundDownMaybe) && + start % (one_second/75) == 0) { + /* start is already on a whole CD frame, do nothing */ + } else if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) { start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75); } else { start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75); @@ -2628,7 +2721,10 @@ Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark) break; case SnapToSeconds: - if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) { + if ((direction == RoundUpMaybe || direction == RoundDownMaybe) && + start % one_second == 0) { + /* start is already on a whole second, do nothing */ + } else if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) { start = (framepos_t) ceil ((double) start / one_second) * one_second; } else { start = (framepos_t) floor ((double) start / one_second) * one_second; @@ -2636,7 +2732,10 @@ Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark) break; case SnapToMinutes: - if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) { + if ((direction == RoundUpMaybe || direction == RoundDownMaybe) && + start % one_minute == 0) { + /* start is already on a whole minute, do nothing */ + } else if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) { start = (framepos_t) ceil ((double) start / one_minute) * one_minute; } else { start = (framepos_t) floor ((double) start / one_minute) * one_minute; @@ -2766,6 +2865,10 @@ Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark) case SnapMagnetic: + if (no_magnets) { + return; + } + if (presnap > start) { if (presnap > (start + pixel_to_sample(snap_threshold))) { start = presnap; @@ -2802,12 +2905,10 @@ Editor::setup_toolbar () mouse_mode_size_group->add_widget (mouse_move_button); mouse_mode_size_group->add_widget (mouse_cut_button); mouse_mode_size_group->add_widget (mouse_select_button); - mouse_mode_size_group->add_widget (mouse_zoom_button); - mouse_mode_size_group->add_widget (mouse_gain_button); mouse_mode_size_group->add_widget (mouse_timefx_button); mouse_mode_size_group->add_widget (mouse_audition_button); mouse_mode_size_group->add_widget (mouse_draw_button); - mouse_mode_size_group->add_widget (internal_edit_button); + mouse_mode_size_group->add_widget (mouse_content_button); mouse_mode_size_group->add_widget (zoom_in_button); mouse_mode_size_group->add_widget (zoom_out_button); @@ -2840,15 +2941,13 @@ Editor::setup_toolbar () if (!ARDOUR::Profile->get_mixbus()) { mouse_mode_hbox->pack_start (mouse_cut_button, false, false); - mouse_mode_hbox->pack_start (mouse_zoom_button, false, false); } if (!ARDOUR::Profile->get_trx()) { - mouse_mode_hbox->pack_start (mouse_gain_button, false, false); mouse_mode_hbox->pack_start (mouse_timefx_button, false, false); mouse_mode_hbox->pack_start (mouse_audition_button, false, false); mouse_mode_hbox->pack_start (mouse_draw_button, false, false); - mouse_mode_hbox->pack_start (internal_edit_button, false, false, 0); + mouse_mode_hbox->pack_start (mouse_content_button, false, false); } mouse_mode_vbox->pack_start (*mouse_mode_hbox); @@ -3132,16 +3231,14 @@ Editor::build_snap_type_menu () void Editor::setup_tooltips () { - ARDOUR_UI::instance()->set_tip (smart_mode_button, _("Smart Mode (add Range functions to Object mode)")); - ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Object Mode (select/move Objects)")); - ARDOUR_UI::instance()->set_tip (mouse_cut_button, _("Cut Mode (split Regions)")); - ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Range Mode (select/move Ranges)")); - ARDOUR_UI::instance()->set_tip (mouse_draw_button, _("Draw/Edit MIDI Notes")); - ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain")); - ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range")); - ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes")); - ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions")); - ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Note Level Editing")); + ARDOUR_UI::instance()->set_tip (smart_mode_button, _("Smart Mode (add Range functions to Grab mode)")); + ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Grab Mode (select/move objects)")); + ARDOUR_UI::instance()->set_tip (mouse_cut_button, _("Cut Mode (split regions)")); + ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Range Mode (select time ranges)")); + ARDOUR_UI::instance()->set_tip (mouse_draw_button, _("Draw Mode (draw and edit gain/notes/automation)")); + ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch Mode (time-stretch audio and midi regions, preserving pitch)")); + ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Audition Mode (listen to regions)")); + ARDOUR_UI::instance()->set_tip (mouse_content_button, _("Internal Edit Mode (edit notes and gain curves inside regions)")); ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations")); ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Later")); ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Earlier")); @@ -3264,10 +3361,114 @@ Editor::map_transport_state () /* UNDO/REDO */ +void +Editor::begin_selection_op_history () +{ + selection_op_cmd_depth = 0; + selection_op_history_it = 0; + + while(!selection_op_history.empty()) { + delete selection_op_history.front(); + selection_op_history.pop_front(); + } + + selection_undo_action->set_sensitive (false); + selection_redo_action->set_sensitive (false); + selection_op_history.push_front (&_selection_memento->get_state ()); +} + +void +Editor::begin_reversible_selection_op (string name) +{ + if (_session) { + //cerr << name << endl; + /* begin/commit pairs can be nested */ + selection_op_cmd_depth++; + } +} + +void +Editor::commit_reversible_selection_op () +{ + if (_session) { + if (selection_op_cmd_depth == 1) { + + if (selection_op_history_it > 0 && selection_op_history_it < selection_op_history.size()) { + /** + The user has undone some selection ops and then made a new one, + making anything earlier in the list invalid. + */ + + list::iterator it = selection_op_history.begin(); + list::iterator e_it = it; + advance (e_it, selection_op_history_it); + + for ( ; it != e_it; ++it) { + delete *it; + } + selection_op_history.erase (selection_op_history.begin(), e_it); + } + + selection_op_history.push_front (&_selection_memento->get_state ()); + selection_op_history_it = 0; + + selection_undo_action->set_sensitive (true); + selection_redo_action->set_sensitive (false); + } + + if (selection_op_cmd_depth > 0) { + selection_op_cmd_depth--; + } + } +} + +void +Editor::undo_selection_op () +{ + if (_session) { + selection_op_history_it++; + uint32_t n = 0; + for (std::list::iterator i = selection_op_history.begin(); i != selection_op_history.end(); ++i) { + if (n == selection_op_history_it) { + _selection_memento->set_state (*(*i), Stateful::current_state_version); + selection_redo_action->set_sensitive (true); + } + ++n; + } + /* is there an earlier entry? */ + if ((selection_op_history_it + 1) >= selection_op_history.size()) { + selection_undo_action->set_sensitive (false); + } + } +} + +void +Editor::redo_selection_op () +{ + if (_session) { + if (selection_op_history_it > 0) { + selection_op_history_it--; + } + uint32_t n = 0; + for (std::list::iterator i = selection_op_history.begin(); i != selection_op_history.end(); ++i) { + if (n == selection_op_history_it) { + _selection_memento->set_state (*(*i), Stateful::current_state_version); + selection_undo_action->set_sensitive (true); + } + ++n; + } + + if (selection_op_history_it == 0) { + selection_redo_action->set_sensitive (false); + } + } +} + void Editor::begin_reversible_command (string name) { if (_session) { + before.push_back (&_selection_memento->get_state ()); _session->begin_reversible_command (name); } } @@ -3276,14 +3477,40 @@ void Editor::begin_reversible_command (GQuark q) { if (_session) { + before.push_back (&_selection_memento->get_state ()); _session->begin_reversible_command (q); } } +void +Editor::abort_reversible_command () +{ + if (_session) { + while(!before.empty()) { + delete before.front(); + before.pop_front(); + } + _session->abort_reversible_command (); + } +} + void Editor::commit_reversible_command () { if (_session) { + if (before.size() == 1) { + _session->add_command (new MementoCommand(*(_selection_memento), before.front(), &_selection_memento->get_state ())); + redo_action->set_sensitive(false); + undo_action->set_sensitive(true); + begin_selection_op_history (); + } + + if (before.empty()) { + cerr << "Please call begin_reversible_command() before commit_reversible_command()." << endl; + } else { + before.pop_back(); + } + _session->commit_reversible_command (); } } @@ -3500,7 +3727,7 @@ Editor::build_track_count_menu () visible_tracks_selector.AddMenuElem (MenuElem (X_("24"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 24))); visible_tracks_selector.AddMenuElem (MenuElem (X_("32"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 32))); visible_tracks_selector.AddMenuElem (MenuElem (X_("64"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 64))); - visible_tracks_selector.AddMenuElem (MenuElem (_("Selected"), sigc::mem_fun(*this, &Editor::fit_selected_tracks))); + visible_tracks_selector.AddMenuElem (MenuElem (_("Selection"), sigc::mem_fun(*this, &Editor::fit_selection))); visible_tracks_selector.AddMenuElem (MenuElem (_("All"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 0))); } else { visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 1 track"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 1))); @@ -3512,7 +3739,7 @@ Editor::build_track_count_menu () visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 32 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 32))); visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 48 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 48))); visible_tracks_selector.AddMenuElem (MenuElem (_("Fit All tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 0))); - visible_tracks_selector.AddMenuElem (MenuElem (_("Fit Selected tracks"), sigc::mem_fun(*this, &Editor::fit_selected_tracks))); + visible_tracks_selector.AddMenuElem (MenuElem (_("Fit Selection"), sigc::mem_fun(*this, &Editor::fit_selection))); zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 10 ms"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 10))); zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 100 ms"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 100))); @@ -3557,6 +3784,7 @@ Editor::set_visible_track_count (int32_t n) int h; string str; + DisplaySuspender ds; if (_visible_track_count > 0) { h = trackviews_height() / _visible_track_count; @@ -3581,7 +3809,7 @@ Editor::set_visible_track_count (int32_t n) } for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - (*i)->set_height (h); + (*i)->set_height (h, TimeAxisView::HeightPerLane); } if (str != visible_tracks_selector.get_text()) { @@ -3592,7 +3820,7 @@ Editor::set_visible_track_count (int32_t n) void Editor::override_visible_track_count () { - _visible_track_count = -_visible_track_count; + _visible_track_count = -1; visible_tracks_selector.set_text ( _("*") ); } @@ -3828,90 +4056,75 @@ Editor::playlist_selector () const return *_playlist_selector; } -Evoral::MusicalTime +framecnt_t +Editor::get_paste_offset (framepos_t pos, unsigned paste_count, framecnt_t duration) +{ + if (paste_count == 0) { + /* don't bother calculating an offset that will be zero anyway */ + return 0; + } + + /* calculate basic unsnapped multi-paste offset */ + framecnt_t offset = paste_count * duration; + + /* snap offset so pos + offset is aligned to the grid */ + framepos_t offset_pos = pos + offset; + snap_to(offset_pos, RoundUpMaybe); + offset = offset_pos - pos; + + return offset; +} + +unsigned +Editor::get_grid_beat_divisions(framepos_t position) +{ + switch (_snap_type) { + case SnapToBeatDiv128: return 128; + case SnapToBeatDiv64: return 64; + case SnapToBeatDiv32: return 32; + case SnapToBeatDiv28: return 28; + case SnapToBeatDiv24: return 24; + case SnapToBeatDiv20: return 20; + case SnapToBeatDiv16: return 16; + case SnapToBeatDiv14: return 14; + case SnapToBeatDiv12: return 12; + case SnapToBeatDiv10: return 10; + case SnapToBeatDiv8: return 8; + case SnapToBeatDiv7: return 7; + case SnapToBeatDiv6: return 6; + case SnapToBeatDiv5: return 5; + case SnapToBeatDiv4: return 4; + case SnapToBeatDiv3: return 3; + case SnapToBeatDiv2: return 2; + default: return 0; + } + return 0; +} + +Evoral::Beats Editor::get_grid_type_as_beats (bool& success, framepos_t position) { success = true; + const unsigned divisions = get_grid_beat_divisions(position); + if (divisions) { + return Evoral::Beats(1.0 / (double)get_grid_beat_divisions(position)); + } + switch (_snap_type) { case SnapToBeat: - return 1.0; - break; - - case SnapToBeatDiv128: - return 1.0/128.0; - break; - case SnapToBeatDiv64: - return 1.0/64.0; - break; - case SnapToBeatDiv32: - return 1.0/32.0; - break; - case SnapToBeatDiv28: - return 1.0/28.0; - break; - case SnapToBeatDiv24: - return 1.0/24.0; - break; - case SnapToBeatDiv20: - return 1.0/20.0; - break; - case SnapToBeatDiv16: - return 1.0/16.0; - break; - case SnapToBeatDiv14: - return 1.0/14.0; - break; - case SnapToBeatDiv12: - return 1.0/12.0; - break; - case SnapToBeatDiv10: - return 1.0/10.0; - break; - case SnapToBeatDiv8: - return 1.0/8.0; - break; - case SnapToBeatDiv7: - return 1.0/7.0; - break; - case SnapToBeatDiv6: - return 1.0/6.0; - break; - case SnapToBeatDiv5: - return 1.0/5.0; - break; - case SnapToBeatDiv4: - return 1.0/4.0; - break; - case SnapToBeatDiv3: - return 1.0/3.0; - break; - case SnapToBeatDiv2: - return 1.0/2.0; - break; - + return Evoral::Beats(1.0); case SnapToBar: if (_session) { - return _session->tempo_map().meter_at (position).divisions_per_bar(); + return Evoral::Beats(_session->tempo_map().meter_at (position).divisions_per_bar()); } break; - - case SnapToCDFrame: - case SnapToTimecodeFrame: - case SnapToTimecodeSeconds: - case SnapToTimecodeMinutes: - case SnapToSeconds: - case SnapToMinutes: - case SnapToRegionStart: - case SnapToRegionEnd: - case SnapToRegionSync: - case SnapToRegionBoundary: default: success = false; break; } - return 0.0; + return Evoral::Beats(); } framecnt_t @@ -4034,7 +4247,7 @@ Editor::session_state_saved (string) void Editor::update_tearoff_visibility() { - bool visible = Config->get_keep_tearoffs(); + bool visible = ARDOUR_UI::config()->get_keep_tearoffs(); _mouse_mode_tearoff->set_visible (visible); _tools_tearoff->set_visible (visible); if (_zoom_tearoff) { @@ -4042,6 +4255,14 @@ Editor::update_tearoff_visibility() } } +void +Editor::reattach_all_tearoffs () +{ + if (_mouse_mode_tearoff) _mouse_mode_tearoff->put_it_back (); + if (_tools_tearoff) _tools_tearoff->put_it_back (); + if (_zoom_tearoff) _zoom_tearoff->put_it_back (); +} + void Editor::maximise_editing_space () { @@ -4106,7 +4327,7 @@ Editor::copy_playlists (TimeAxisView* v) void Editor::clear_playlists (TimeAxisView* v) { - begin_reversible_command (_("clear playlists")); + begin_reversible_command (_("clear playlists")); vector > playlists; _session->playlists->get (playlists); mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::select.property_id); @@ -4144,6 +4365,12 @@ Editor::on_key_release_event (GdkEventKey* ev) // return key_press_focus_accelerator_handler (*this, ev); } +double +Editor::get_y_origin () const +{ + return vertical_adjustment.get_value (); +} + /** Queue up a change to the viewport x origin. * @param frame New x origin. */ @@ -4225,7 +4452,9 @@ Editor::undo_visual_state () redo_visual_stack.push_back (current_visual_state (vs ? vs->gui_state != 0 : false)); - use_visual_state (*vs); + if (vs) { + use_visual_state (*vs); + } } void @@ -4238,9 +4467,13 @@ Editor::redo_visual_state () VisualState* vs = redo_visual_stack.back(); redo_visual_stack.pop_back(); - undo_visual_stack.push_back (current_visual_state (vs ? vs->gui_state != 0 : false)); + // can 'vs' really be 0? Is there a place that puts NULL pointers onto the stack? + // why do we check here? + undo_visual_stack.push_back (current_visual_state (vs ? (vs->gui_state != 0) : false)); - use_visual_state (*vs); + if (vs) { + use_visual_state (*vs); + } } void @@ -4268,6 +4501,7 @@ Editor::use_visual_state (VisualState& vs) *ARDOUR_UI::instance()->gui_object_state = *vs.gui_state; for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { + (*i)->clear_property_cache(); (*i)->reset_visual_state (); } } @@ -4349,7 +4583,8 @@ void Editor::ensure_visual_change_idle_handler () { if (pending_visual_change.idle_handler_id < 0) { - pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this); + // see comment in add_to_idle_resize above. + pending_visual_change.idle_handler_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, _idle_visual_changer, this, NULL); pending_visual_change.being_handled = false; } } @@ -4444,17 +4679,19 @@ Editor::sort_track_selection (TrackViewList& sel) } framepos_t -Editor::get_preferred_edit_position (bool ignore_playhead, bool from_context_menu) +Editor::get_preferred_edit_position (EditIgnoreOption ignore, bool from_context_menu, bool from_outside_canvas) { bool ignored; framepos_t where = 0; EditPoint ep = _edit_point; - if(Profile->get_mixbus()) + if (Profile->get_mixbus()) if (ep == EditAtSelectedMarker) - ep=EditAtPlayhead; - - if (from_context_menu && (ep == EditAtMouse)) { + ep = EditAtPlayhead; + + if (from_outside_canvas && (ep == EditAtMouse)) { + ep = EditAtPlayhead; + } else if (from_context_menu && (ep == EditAtMouse)) { return canvas_event_sample (&context_click_event, 0, 0); } @@ -4463,13 +4700,24 @@ Editor::get_preferred_edit_position (bool ignore_playhead, bool from_context_men return entered_marker->position(); } - if (ignore_playhead && ep == EditAtPlayhead) { + if ( (ignore==EDIT_IGNORE_PHEAD) && ep == EditAtPlayhead) { ep = EditAtSelectedMarker; } + if ( (ignore==EDIT_IGNORE_MOUSE) && ep == EditAtMouse) { + ep = EditAtPlayhead; + } + switch (ep) { case EditAtPlayhead: - where = _session->audible_frame(); + if (_dragging_playhead) { + if (!mouse_frame (where, ignored)) { + /* XXX not right but what can we do ? */ + return 0; + } + } else { + where = _session->audible_frame(); + } DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where)); break; @@ -4546,8 +4794,7 @@ Editor::set_punch_range (framepos_t start, framepos_t end, string cmd) _session->set_auto_punch_location (loc); XMLNode &after = _session->locations()->get_state(); _session->add_command (new MementoCommand(*(_session->locations()), &before, &after)); - } - else { + } else { XMLNode &before = tpl->get_state(); tpl->set_hidden (false, this); tpl->set (start, end); @@ -4724,6 +4971,50 @@ Editor::get_regions_from_selection_and_entered () return regions; } +void +Editor::get_regionviews_by_id (PBD::ID const id, RegionSelection & regions) const +{ + for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) { + RouteTimeAxisView* rtav; + + if ((rtav = dynamic_cast (*i)) != 0) { + boost::shared_ptr pl; + std::vector > results; + boost::shared_ptr tr; + + if ((tr = rtav->track()) == 0) { + /* bus */ + continue; + } + + if ((pl = (tr->playlist())) != 0) { + boost::shared_ptr r = pl->region_by_id (id); + if (r) { + RegionView* rv = rtav->view()->find_view (r); + if (rv) { + regions.push_back (rv); + } + } + } + } + } +} + +void +Editor::get_per_region_note_selection (list > > > > &selection) const +{ + + for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) { + MidiTimeAxisView* mtav; + + if ((mtav = dynamic_cast (*i)) != 0) { + + mtav->get_per_region_note_selection (selection); + } + } + +} + void Editor::get_regions_corresponding_to (boost::shared_ptr region, vector& regions, bool src_comparison) { @@ -4796,6 +5087,13 @@ Editor::first_idle () _routes->redisplay (); delete dialog; + + if (_session->undo_depth() == 0) { + undo_action->set_sensitive(false); + } + redo_action->set_sensitive(false); + begin_selection_op_history (); + _have_idled = true; } @@ -4809,7 +5107,11 @@ void Editor::add_to_idle_resize (TimeAxisView* view, int32_t h) { if (resize_idle_id < 0) { - resize_idle_id = g_idle_add (_idle_resize, this); + /* https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#G-PRIORITY-HIGH-IDLE:CAPS + * GTK+ uses G_PRIORITY_HIGH_IDLE + 10 for resizing operations, and G_PRIORITY_HIGH_IDLE + 20 for redrawing operations. + * (This is done to ensure that any pending resizes are processed before any pending redraws, so that widgets are not redrawn twice unnecessarily.) + */ + resize_idle_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE + 10, _idle_resize, this, NULL); _pending_resize_amount = 0; } @@ -4879,8 +5181,28 @@ Editor::located () } void -Editor::region_view_added (RegionView *) +Editor::region_view_added (RegionView * rv) { + for (list::iterator pr = selection->regions.pending.begin (); pr != selection->regions.pending.end (); ++pr) { + if (rv->region ()->id () == (*pr)) { + selection->add (rv); + selection->regions.pending.erase (pr); + break; + } + } + + MidiRegionView* mrv = dynamic_cast (rv); + if (mrv) { + list > > > >::iterator rnote; + for (rnote = selection->pending_midi_note_selection.begin(); rnote != selection->pending_midi_note_selection.end(); ++rnote) { + if (rv->region()->id () == (*rnote).first) { + mrv->select_notes ((*rnote).second); + selection->pending_midi_note_selection.erase(rnote); + break; + } + } + } + _summary->set_background_dirty (); } @@ -4933,6 +5255,7 @@ void Editor::resume_route_redisplay () { if (_routes) { + _routes->redisplay(); // queue redisplay _routes->resume_redisplay(); } } @@ -4944,6 +5267,8 @@ Editor::add_routes (RouteList& routes) RouteTimeAxisView *rtv; list new_views; + TrackViewList new_selection; + bool from_scratch = (track_views.size() == 0); for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) { boost::shared_ptr route = (*x); @@ -4966,15 +5291,10 @@ Editor::add_routes (RouteList& routes) new_views.push_back (rtv); track_views.push_back (rtv); + new_selection.push_back (rtv); rtv->effective_gain_display (); - if (internal_editing()) { - rtv->enter_internal_edit_mode (); - } else { - rtv->leave_internal_edit_mode (); - } - rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added)); rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed)); } @@ -4984,6 +5304,12 @@ Editor::add_routes (RouteList& routes) _summary->routes_added (new_views); } + if (!from_scratch) { + selection->tracks.clear(); + selection->add (new_selection); + begin_selection_op_history(); + } + if (show_editor_mixer_when_tracks_arrive) { show_editor_mixer (true); } @@ -5476,7 +5802,7 @@ Editor::show_editor_list (bool yn) void Editor::change_region_layering_order (bool from_context_menu) { - const framepos_t position = get_preferred_edit_position (false, from_context_menu); + const framepos_t position = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context_menu); if (!clicked_routeview) { if (layering_order_editor) { @@ -5611,6 +5937,42 @@ Editor::popup_control_point_context_menu (ArdourCanvas::Item* item, GdkEvent* ev _control_point_context_menu.popup (event->button.button, event->button.time); } +void +Editor::popup_note_context_menu (ArdourCanvas::Item* item, GdkEvent* event) +{ + using namespace Menu_Helpers; + + NoteBase* note = reinterpret_cast(item->get_data("notebase")); + if (!note) { + return; + } + + /* We need to get the selection here and pass it to the operations, since + popping up the menu will cause a region leave event which clears + entered_regionview. */ + + MidiRegionView& mrv = note->region_view(); + const RegionSelection rs = get_regions_from_selection_and_entered (); + + MenuList& items = _note_context_menu.items(); + items.clear(); + + items.push_back(MenuElem(_("Delete"), + sigc::mem_fun(mrv, &MidiRegionView::delete_selection))); + items.push_back(MenuElem(_("Edit..."), + sigc::bind(sigc::mem_fun(*this, &Editor::edit_notes), &mrv))); + items.push_back(MenuElem(_("Legatize"), + sigc::bind(sigc::mem_fun(*this, &Editor::legatize_regions), rs, false))); + items.push_back(MenuElem(_("Quantize..."), + sigc::bind(sigc::mem_fun(*this, &Editor::quantize_regions), rs))); + items.push_back(MenuElem(_("Remove Overlap"), + sigc::bind(sigc::mem_fun(*this, &Editor::legatize_regions), rs, true))); + items.push_back(MenuElem(_("Transform..."), + sigc::bind(sigc::mem_fun(*this, &Editor::transform_regions), rs))); + + _note_context_menu.popup (event->button.button, event->button.time); +} + void Editor::zoom_vertical_modifier_released() { @@ -5622,9 +5984,10 @@ Editor::ui_parameter_changed (string parameter) { if (parameter == "icon-set") { while (!_cursor_stack.empty()) { - _cursor_stack.pop(); + _cursor_stack.pop_back(); } _cursors->set_cursor_set (ARDOUR_UI::config()->get_icon_set()); + _cursor_stack.push_back(_cursors->grabber); } else if (parameter == "draggable-playhead") { if (_verbose_cursor) { playhead_cursor->set_sensitive (ARDOUR_UI::config()->get_draggable_playhead());