X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Feditor.cc;h=c830b5cfe4326660d662a1164a7d4ec19f19d7e5;hb=07a505b1b271f64a1ffb2da32da0548d32cd13de;hp=36154b82d3115048d011742b8a281b94e1f18f13;hpb=eabe5b31f88fc2723980cb812e836c02b296b80f;p=ardour.git diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index 36154b82d3..bde5b65893 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -43,6 +43,8 @@ #include "pbd/enumwriter.h" #include "pbd/memento_command.h" #include "pbd/unknown_type.h" +#include "pbd/unwind.h" +#include "pbd/stacktrace.h" #include #include @@ -58,67 +60,62 @@ #include "gtkmm2ext/choice.h" #include "gtkmm2ext/cell_renderer_pixbuf_toggle.h" -#include "ardour/audio_diskstream.h" #include "ardour/audio_track.h" -#include "ardour/audioplaylist.h" +#include "ardour/audioengine.h" #include "ardour/audioregion.h" #include "ardour/location.h" -#include "ardour/midi_region.h" -#include "ardour/plugin_manager.h" #include "ardour/profile.h" #include "ardour/route_group.h" -#include "ardour/session_directory.h" -#include "ardour/session_route.h" -#include "ardour/session_state_utils.h" +#include "ardour/session_playlists.h" #include "ardour/tempo.h" #include "ardour/utils.h" -#include "ardour/session_playlists.h" -#include "ardour/audioengine.h" + +#include "canvas/debug.h" +#include "canvas/text.h" #include "control_protocol/control_protocol.h" -#include "editor.h" -#include "debug.h" -#include "keyboard.h" -#include "marker.h" -#include "playlist_selector.h" -#include "audio_region_view.h" -#include "rgb_macros.h" -#include "selection.h" -#include "audio_streamview.h" -#include "time_axis_view.h" -#include "audio_time_axis.h" -#include "utils.h" -#include "crossfade_view.h" -#include "canvas-noevent-text.h" -#include "editing.h" -#include "public_editor.h" -#include "crossfade_edit.h" -#include "canvas_impl.h" #include "actions.h" -#include "sfdb_ui.h" -#include "gui_thread.h" -#include "simpleline.h" -#include "rhythm_ferret.h" #include "actions.h" -#include "tempo_lines.h" #include "analysis_window.h" +#include "audio_clock.h" +#include "audio_region_view.h" +#include "audio_streamview.h" +#include "audio_time_axis.h" +#include "automation_time_axis.h" #include "bundle_manager.h" -#include "global_port_matrix.h" +#include "crossfade_edit.h" +#include "debug.h" +#include "editing.h" +#include "editor.h" +#include "editor_cursors.h" #include "editor_drag.h" #include "editor_group_tabs.h" -#include "automation_time_axis.h" -#include "editor_routes.h" -#include "midi_time_axis.h" -#include "mixer_strip.h" -#include "editor_route_groups.h" -#include "editor_regions.h" #include "editor_locations.h" +#include "editor_regions.h" +#include "editor_route_groups.h" +#include "editor_routes.h" #include "editor_snapshots.h" #include "editor_summary.h" -#include "region_layering_order_editor.h" +#include "global_port_matrix.h" +#include "gui_object.h" +#include "gui_thread.h" +#include "keyboard.h" +#include "marker.h" +#include "midi_time_axis.h" +#include "mixer_strip.h" +#include "mixer_ui.h" #include "mouse_cursors.h" -#include "editor_cursors.h" +#include "playlist_selector.h" +#include "public_editor.h" +#include "region_layering_order_editor.h" +#include "rgb_macros.h" +#include "rhythm_ferret.h" +#include "selection.h" +#include "sfdb_ui.h" +#include "tempo_lines.h" +#include "time_axis_view.h" +#include "utils.h" #include "i18n.h" @@ -147,6 +144,8 @@ static const gchar *_snap_type_strings[] = { N_("Timecode Minutes"), N_("Seconds"), N_("Minutes"), + N_("Beats/128"), + N_("Beats/64"), N_("Beats/32"), N_("Beats/28"), N_("Beats/24"), @@ -209,24 +208,22 @@ static const gchar *_rb_opt_strings[] = { }; #endif -void -show_me_the_size (Requisition* r, const char* what) -{ - cerr << "size of " << what << " = " << r->width << " x " << r->height << endl; -} - -#ifdef GTKOSX static void pane_size_watcher (Paned* pane) { /* if the handle of a pane vanishes into (at least) the tabs of a notebook, - it is no longer accessible. so stop that. this doesn't happen on X11, - just the quartz backend. + it is: + + X: hard to access + Quartz: impossible to access + + so stop that by preventing it from ever getting too narrow. 35 + pixels is basically a rough guess at the tab width. ugh. */ - int max_width_of_lhs = GTK_WIDGET(pane->gobj())->allocation.width - 25; + int max_width_of_lhs = GTK_WIDGET(pane->gobj())->allocation.width - 35; gint pos = pane->get_position (); @@ -234,7 +231,6 @@ pane_size_watcher (Paned* pane) pane->set_position (max_width_of_lhs); } } -#endif Editor::Editor () : _join_object_range_state (JOIN_OBJECT_RANGE_NONE) @@ -250,6 +246,9 @@ Editor::Editor () , range_mark_label (_("Range Markers")) , transport_mark_label (_("Loop/Punch Ranges")) , cd_mark_label (_("CD Markers")) +#ifdef WITH_VIDEOTIMELINE + , videotl_label (_("Video Timeline")) +#endif , edit_packer (4, 4, true) /* the values here don't matter: layout widgets @@ -260,15 +259,13 @@ Editor::Editor () /* tool bar related */ - , zoom_range_clock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, false, true) + , zoom_range_clock (new AudioClock (X_("zoomrange"), false, X_("zoom range"), true, false, true)) , toolbar_selection_clock_table (2,3) , automation_mode_button (_("mode")) - , global_automation_button (_("automation")) , _toolbar_viewport (*manage (new Gtk::Adjustment (0, 0, 1e10)), *manage (new Gtk::Adjustment (0, 0, 1e10))) - , midi_panic_button (_("Panic")) #ifdef WITH_CMT , image_socket_listener(0) @@ -276,13 +273,16 @@ Editor::Editor () /* nudge */ - , nudge_clock (X_("nudge"), false, X_("NudgeClock"), true, false, true) + , nudge_clock (new AudioClock (X_("nudge"), false, X_("nudge"), true, false, true)) , meters_running(false) , _pending_locate_request (false) , _pending_initial_locate (false) , _last_cut_copy_source_track (0) , _region_selection_change_updates_region_list (true) + , _following_mixer_selection (false) + , _control_point_toggled_on_press (false) + , _stepping_axis_view (0) { constructed = false; @@ -298,13 +298,11 @@ Editor::Editor () clicked_regionview = 0; clicked_axisview = 0; clicked_routeview = 0; - clicked_crossfadeview = 0; clicked_control_point = 0; last_update_frame = 0; pre_press_cursor = 0; _drags = new DragManager (this); current_mixer_strip = 0; - current_bbt_points = 0; tempo_lines = 0; snap_type_strings = I18N (_snap_type_strings); @@ -318,8 +316,8 @@ Editor::Editor () snap_threshold = 5.0; bbt_beat_subdivision = 4; - _canvas_width = 0; - _canvas_height = 0; + _visible_canvas_width = 0; + _visible_canvas_height = 0; last_autoscroll_x = 0; last_autoscroll_y = 0; autoscroll_active = false; @@ -330,14 +328,12 @@ Editor::Editor () current_interthread_info = 0; _show_measures = true; + _maximised = false; show_gain_after_trim = false; - verbose_cursor_on = true; - last_item_entered = 0; have_pending_keyboard_selection = false; _follow_playhead = true; _stationary_playhead = false; - _xfade_visibility = true; editor_ruler_menu = 0; no_ruler_shown_update = false; marker_menu = 0; @@ -365,9 +361,9 @@ Editor::Editor () select_new_marker = false; rhythm_ferret = 0; layering_order_editor = 0; - _bundle_manager = 0; no_save_visual = false; resize_idle_id = -1; + within_track_canvas = false; scrubbing_direction = 0; @@ -383,75 +379,85 @@ Editor::Editor () _internal_editing = false; current_canvas_cursor = 0; - frames_per_unit = 2048; /* too early to use reset_zoom () */ + frames_per_pixel = 2048; /* too early to use reset_zoom () */ _scroll_callbacks = 0; zoom_focus = ZoomFocusLeft; set_zoom_focus (ZoomFocusLeft); - zoom_range_clock.ValueChanged.connect (sigc::mem_fun(*this, &Editor::zoom_adjustment_changed)); + zoom_range_clock->ValueChanged.connect (sigc::mem_fun(*this, &Editor::zoom_adjustment_changed)); - bbt_label.set_name ("EditorTimeButton"); + bbt_label.set_name ("EditorRulerLabel"); bbt_label.set_size_request (-1, (int)timebar_height); bbt_label.set_alignment (1.0, 0.5); bbt_label.set_padding (5,0); bbt_label.hide (); bbt_label.set_no_show_all(); - minsec_label.set_name ("EditorTimeButton"); + minsec_label.set_name ("EditorRulerLabel"); minsec_label.set_size_request (-1, (int)timebar_height); minsec_label.set_alignment (1.0, 0.5); minsec_label.set_padding (5,0); minsec_label.hide (); minsec_label.set_no_show_all(); - timecode_label.set_name ("EditorTimeButton"); + timecode_label.set_name ("EditorRulerLabel"); timecode_label.set_size_request (-1, (int)timebar_height); timecode_label.set_alignment (1.0, 0.5); timecode_label.set_padding (5,0); timecode_label.hide (); timecode_label.set_no_show_all(); - samples_label.set_name ("EditorTimeButton"); + samples_label.set_name ("EditorRulerLabel"); samples_label.set_size_request (-1, (int)timebar_height); samples_label.set_alignment (1.0, 0.5); samples_label.set_padding (5,0); samples_label.hide (); samples_label.set_no_show_all(); - tempo_label.set_name ("EditorTimeButton"); + tempo_label.set_name ("EditorRulerLabel"); tempo_label.set_size_request (-1, (int)timebar_height); tempo_label.set_alignment (1.0, 0.5); tempo_label.set_padding (5,0); tempo_label.hide(); tempo_label.set_no_show_all(); - - meter_label.set_name ("EditorTimeButton"); + + meter_label.set_name ("EditorRulerLabel"); meter_label.set_size_request (-1, (int)timebar_height); meter_label.set_alignment (1.0, 0.5); meter_label.set_padding (5,0); meter_label.hide(); meter_label.set_no_show_all(); - - mark_label.set_name ("EditorTimeButton"); + + mark_label.set_name ("EditorRulerLabel"); mark_label.set_size_request (-1, (int)timebar_height); mark_label.set_alignment (1.0, 0.5); mark_label.set_padding (5,0); mark_label.hide(); mark_label.set_no_show_all(); - - cd_mark_label.set_name ("EditorTimeButton"); + + cd_mark_label.set_name ("EditorRulerLabel"); cd_mark_label.set_size_request (-1, (int)timebar_height); cd_mark_label.set_alignment (1.0, 0.5); cd_mark_label.set_padding (5,0); cd_mark_label.hide(); cd_mark_label.set_no_show_all(); - - range_mark_label.set_name ("EditorTimeButton"); + +#ifdef WITH_VIDEOTIMELINE + videotl_bar_height = 4; + videotl_label.set_name ("EditorRulerLabel"); + videotl_label.set_size_request (-1, (int)timebar_height * videotl_bar_height); + videotl_label.set_alignment (1.0, 0.5); + videotl_label.set_padding (5,0); + videotl_label.hide(); + videotl_label.set_no_show_all(); +#endif + + range_mark_label.set_name ("EditorRulerLabel"); range_mark_label.set_size_request (-1, (int)timebar_height); range_mark_label.set_alignment (1.0, 0.5); range_mark_label.set_padding (5,0); range_mark_label.hide(); range_mark_label.set_no_show_all(); - - transport_mark_label.set_name ("EditorTimeButton"); + + transport_mark_label.set_name ("EditorRulerLabel"); transport_mark_label.set_size_request (-1, (int)timebar_height); transport_mark_label.set_alignment (1.0, 0.5); transport_mark_label.set_padding (5,0); @@ -460,20 +466,20 @@ Editor::Editor () initialize_rulers (); initialize_canvas (); - + _summary = new EditorSummary (this); selection->TimeChanged.connect (sigc::mem_fun(*this, &Editor::time_selection_changed)); selection->TracksChanged.connect (sigc::mem_fun(*this, &Editor::track_selection_changed)); - + editor_regions_selection_changed_connection = selection->RegionsChanged.connect (sigc::mem_fun(*this, &Editor::region_selection_changed)); - + selection->PointsChanged.connect (sigc::mem_fun(*this, &Editor::point_selection_changed)); selection->MarkersChanged.connect (sigc::mem_fun(*this, &Editor::marker_selection_changed)); edit_controls_vbox.set_spacing (0); vertical_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &Editor::tie_vertical_scrolling), true); - track_canvas->signal_map_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_map_handler)); + _track_canvas->signal_map_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_map_handler)); HBox* h = manage (new HBox); _group_tabs = new EditorGroupTabs (this); @@ -490,13 +496,14 @@ Editor::Editor () _cursors = new MouseCursors; - ArdourCanvas::Canvas* time_pad = manage(new ArdourCanvas::Canvas()); - ArdourCanvas::SimpleLine* pad_line_1 = manage(new ArdourCanvas::SimpleLine(*time_pad->root(), - 0.0, 1.0, 100.0, 1.0)); - - pad_line_1->property_color_rgba() = 0xFF0000FF; + ArdourCanvas::GtkCanvas* time_pad = manage (new ArdourCanvas::GtkCanvas ()); + + ArdourCanvas::Line* pad_line_1 = new ArdourCanvas::Line (time_pad->root()); + pad_line_1->set (ArdourCanvas::Duple (0.0, 1.0), ArdourCanvas::Duple (100.0, 1.0)); + pad_line_1->set_outline_color (0xFF0000FF); pad_line_1->show(); - + + // CAIROCANVAS time_pad->show(); time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2); @@ -506,15 +513,15 @@ Editor::Editor () ruler_label_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK); ruler_label_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release)); - time_button_event_box.add (time_button_vbox); - time_button_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK); - time_button_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release)); + time_bars_event_box.add (time_bars_vbox); + time_bars_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK); + time_bars_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release)); /* these enable us to have a dedicated window (for cursor setting, etc.) for the canvas areas. */ - track_canvas_event_box.add (*track_canvas); + track_canvas_event_box.add (*_track_canvas_viewport); time_canvas_event_box.add (time_canvas_vbox); time_canvas_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK); @@ -527,14 +534,16 @@ Editor::Editor () /* labels for the rulers */ edit_packer.attach (ruler_label_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0); - /* labels for the marker "tracks" */ - edit_packer.attach (time_button_event_box, 1, 2, 1, 2, FILL, SHRINK, 0, 0); + /* labels for the marker "tracks" (time bars) */ + edit_packer.attach (time_bars_event_box, 1, 2, 1, 2, FILL, SHRINK, 0, 0); /* the rulers */ edit_packer.attach (time_canvas_event_box, 2, 3, 0, 1, FILL|EXPAND, FILL, 0, 0); /* track controls */ edit_packer.attach (controls_layout, 0, 2, 2, 3, FILL, FILL|EXPAND, 0, 0); - /* main canvas */ - edit_packer.attach (track_canvas_event_box, 2, 3, 1, 3, FILL|EXPAND, FILL|EXPAND, 0, 0); + /* time bars canvas */ + edit_packer.attach (*_time_bars_canvas_viewport, 2, 3, 1, 2, FILL, FILL, 0, 0); + /* track canvas */ + edit_packer.attach (track_canvas_event_box, 2, 3, 2, 3, FILL|EXPAND, FILL|EXPAND, 0, 0); bottom_hbox.set_border_width (2); bottom_hbox.set_spacing (3); @@ -548,7 +557,7 @@ Editor::Editor () add_notebook_page (_("Regions"), _regions->widget ()); add_notebook_page (_("Tracks & Busses"), _routes->widget ()); add_notebook_page (_("Snapshots"), _snapshots->widget ()); - add_notebook_page (_("Route Groups"), _route_groups->widget ()); + add_notebook_page (_("Track & Bus Groups"), _route_groups->widget ()); add_notebook_page (_("Ranges & Marks"), _locations->widget ()); _the_notebook.set_show_tabs (true); @@ -557,10 +566,6 @@ Editor::Editor () _the_notebook.set_tab_pos (Gtk::POS_RIGHT); _the_notebook.show_all (); - post_maximal_editor_width = 0; - post_maximal_horizontal_pane_position = 0; - post_maximal_editor_height = 0; - post_maximal_vertical_pane_position = 0; _notebook_shrunk = false; editor_summary_pane.pack1(edit_packer); @@ -569,12 +574,12 @@ Editor::Editor () summary_arrows_left_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE))); summary_arrows_left_left->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), LEFT))); summary_arrows_left_left->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release)); - + Button* summary_arrows_left_right = manage (new Button); summary_arrows_left_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE))); summary_arrows_left_right->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), RIGHT))); summary_arrows_left_right->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release)); - + VBox* summary_arrows_left = manage (new VBox); summary_arrows_left->pack_start (*summary_arrows_left_left); summary_arrows_left->pack_start (*summary_arrows_left_right); @@ -583,26 +588,26 @@ Editor::Editor () summary_arrows_right_up->add (*manage (new Arrow (ARROW_UP, SHADOW_NONE))); summary_arrows_right_up->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), UP))); summary_arrows_right_up->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release)); - + Button* summary_arrows_right_down = manage (new Button); summary_arrows_right_down->add (*manage (new Arrow (ARROW_DOWN, SHADOW_NONE))); summary_arrows_right_down->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), DOWN))); summary_arrows_right_down->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release)); - + VBox* summary_arrows_right = manage (new VBox); summary_arrows_right->pack_start (*summary_arrows_right_up); summary_arrows_right->pack_start (*summary_arrows_right_down); Frame* summary_frame = manage (new Frame); summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN); - + summary_frame->add (*_summary); summary_frame->show (); _summary_hbox.pack_start (*summary_arrows_left, false, false); _summary_hbox.pack_start (*summary_frame, true, true); _summary_hbox.pack_start (*summary_arrows_right, false, false); - + editor_summary_pane.pack2 (_summary_hbox); edit_pane.pack1 (editor_summary_pane, true, true); @@ -610,13 +615,13 @@ Editor::Editor () editor_summary_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun (*this, &Editor::pane_allocation_handler), static_cast (&editor_summary_pane))); - /* XXX: editor_summary_pane might need similar special OS X treatment to the edit_pane */ + /* XXX: editor_summary_pane might need similar to the edit_pane */ edit_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Editor::pane_allocation_handler), static_cast (&edit_pane))); -#ifdef GTKOSX + Glib::PropertyProxy proxy = edit_pane.property_position(); proxy.signal_changed().connect (bind (sigc::ptr_fun (pane_size_watcher), static_cast (&edit_pane))); -#endif + top_hbox.pack_start (toolbar_frame); HBox *hbox = manage (new HBox); @@ -638,29 +643,39 @@ Editor::Editor () /* register actions now so that set_state() can find them and set toggles/checks etc */ register_actions (); + /* when we start using our own keybinding system for the editor, this + * will be uncommented + */ + // load_bindings (); setup_toolbar (); - setup_midi_toolbar (); _snap_type = SnapToBeat; set_snap_to (_snap_type); _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; + internal_snap_mode = _snap_mode; set_edit_point_preference (EditAtMouse, true); _playlist_selector = new PlaylistSelector(); _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast (_playlist_selector))); - RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), ui_bind (&Editor::catch_vanishing_regionview, this, _1), gui_context()); + RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), boost::bind (&Editor::catch_vanishing_regionview, this, _1), gui_context()); /* nudge stuff */ - nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right"))))); - nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left"))))); + nudge_forward_button.set_name ("zoom button"); + nudge_forward_button.add_elements (ArdourButton::FlatFace); + nudge_forward_button.set_image(::get_icon("nudge_right")); - nudge_forward_button.set_name ("TransportButton"); - nudge_backward_button.set_name ("TransportButton"); + nudge_backward_button.set_name ("zoom button"); + nudge_backward_button.add_elements (ArdourButton::FlatFace); + nudge_backward_button.set_image(::get_icon("nudge_left")); fade_context_menu.set_name ("ArdourContextMenu"); @@ -682,7 +697,7 @@ Editor::Editor () window_icons.push_back (icon); } if (!window_icons.empty()) { - set_icon_list (window_icons); + // set_icon_list (window_icons); set_default_icon_list (window_icons); } @@ -697,21 +712,40 @@ Editor::Editor () signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler)); signal_delete_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close)); + Gtkmm2ext::Keyboard::the_keyboard().ShiftReleased.connect (sigc::mem_fun (*this, &Editor::shift_key_released)); + /* allow external control surfaces/protocols to do various things */ ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context()); ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context()); ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context()); - ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), ui_bind (&Editor::control_scroll, this, _1), gui_context()); - BasicUI::AccessAction.connect (*this, invalidator (*this), ui_bind (&Editor::access_action, this, _1, _2), gui_context()); - + ControlProtocol::Undo.connect (*this, invalidator (*this), boost::bind (&Editor::undo, this, true), gui_context()); + ControlProtocol::Redo.connect (*this, invalidator (*this), boost::bind (&Editor::redo, this, true), gui_context()); + ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), boost::bind (&Editor::control_scroll, this, _1), gui_context()); + ControlProtocol::StepTracksUp.connect (*this, invalidator (*this), boost::bind (&Editor::control_step_tracks_up, this), gui_context()); + ControlProtocol::StepTracksDown.connect (*this, invalidator (*this), boost::bind (&Editor::control_step_tracks_down, this), gui_context()); + ControlProtocol::GotoView.connect (*this, invalidator (*this), boost::bind (&Editor::control_view, this, _1), gui_context()); + ControlProtocol::CloseDialog.connect (*this, invalidator (*this), Keyboard::close_current_dialog, gui_context()); + ControlProtocol::VerticalZoomInAll.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_in_all, this), gui_context()); + ControlProtocol::VerticalZoomOutAll.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_out_all, this), gui_context()); + ControlProtocol::VerticalZoomInSelected.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_in_selected, this), gui_context()); + ControlProtocol::VerticalZoomOutSelected.connect (*this, invalidator (*this), boost::bind (&Editor::control_vertical_zoom_out_selected, this), gui_context()); + + ControlProtocol::AddRouteToSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Add), gui_context()); + ControlProtocol::RemoveRouteFromSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Toggle), gui_context()); + ControlProtocol::SetRouteSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Set), gui_context()); + ControlProtocol::ToggleRouteSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_select, this, _1, Selection::Toggle), gui_context()); + ControlProtocol::ClearRouteSelection.connect (*this, invalidator (*this), boost::bind (&Editor::control_unselect, this), gui_context()); + + BasicUI::AccessAction.connect (*this, invalidator (*this), boost::bind (&Editor::access_action, this, _1, _2), gui_context()); + /* problematic: has to return a value and thus cannot be x-thread */ Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1)); - Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context()); + Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&Editor::parameter_changed, this, _1), gui_context()); - TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context()); + TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Editor::timeaxisview_deleted, this, _1), gui_context()); _ignore_region_action = false; _last_region_menu_was_main = false; @@ -754,7 +788,7 @@ Editor::~Editor() delete button_bindings; delete _routes; delete _route_groups; - delete track_canvas; + delete _track_canvas_viewport; delete _drags; } @@ -765,7 +799,6 @@ Editor::button_settings () const XMLNode* node = find_named_node (*settings, X_("Buttons")); if (!node) { - cerr << "new empty Button node\n"; node = new XMLNode (X_("Buttons")); } @@ -779,6 +812,12 @@ Editor::add_toplevel_controls (Container& cont) cont.show_all (); } +bool +Editor::get_smart_mode () const +{ + return ( (current_mouse_mode() == Editing::MouseObject) && smart_mode_action->get_active() ); +} + void Editor::catch_vanishing_regionview (RegionView *rv) { @@ -801,6 +840,8 @@ Editor::catch_vanishing_regionview (RegionView *rv) if (!_all_region_actions_sensitized) { sensitize_all_region_actions (true); } + + _over_region_trim_target = false; } void @@ -868,6 +909,7 @@ Editor::show_window () if (current_mixer_strip) { current_mixer_strip->hide_things (); + current_mixer_strip->parameter_changed ("mixer-strip-visibility"); } } @@ -895,19 +937,102 @@ Editor::zoom_adjustment_changed () return; } - double fpu = zoom_range_clock.current_duration() / _canvas_width; - - if (fpu < 1.0) { - fpu = 1.0; - zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width)); - } else if (fpu > _session->current_end_frame() / _canvas_width) { - fpu = _session->current_end_frame() / _canvas_width; - zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width)); + double fpu = zoom_range_clock->current_duration() / _visible_canvas_width; + bool clamped = clamp_frames_per_pixel (fpu); + + if (clamped) { + zoom_range_clock->set ((framepos_t) floor (fpu * _visible_canvas_width)); } temporal_zoom (fpu); } +void +Editor::control_vertical_zoom_in_all () +{ + tav_zoom_smooth (false, true); +} + +void +Editor::control_vertical_zoom_out_all () +{ + tav_zoom_smooth (true, true); +} + +void +Editor::control_vertical_zoom_in_selected () +{ + tav_zoom_smooth (false, false); +} + +void +Editor::control_vertical_zoom_out_selected () +{ + tav_zoom_smooth (true, false); +} + +void +Editor::control_view (uint32_t view) +{ + goto_visual_state (view); +} + +void +Editor::control_unselect () +{ + selection->clear_tracks (); +} + +void +Editor::control_select (uint32_t rid, Selection::Operation op) +{ + /* handles the (static) signal from the ControlProtocol class that + * requests setting the selected track to a given RID + */ + + if (!_session) { + return; + } + + boost::shared_ptr r = _session->route_by_remote_id (rid); + + if (!r) { + return; + } + + TimeAxisView* tav = axis_view_from_route (r); + + if (tav) { + switch (op) { + case Selection::Add: + selection->add (tav); + break; + case Selection::Toggle: + selection->toggle (tav); + break; + case Selection::Extend: + break; + case Selection::Set: + selection->set (tav); + break; + } + } else { + selection->clear_tracks (); + } +} + +void +Editor::control_step_tracks_up () +{ + scroll_tracks_up_line (); +} + +void +Editor::control_step_tracks_down () +{ + scroll_tracks_down_line (); +} + void Editor::control_scroll (float fraction) { @@ -1023,7 +1148,7 @@ Editor::map_position_change (framepos_t frame) void Editor::center_screen (framepos_t frame) { - double page = _canvas_width * frames_per_unit; + double const page = _visible_canvas_width * frames_per_pixel; /* if we're off the page, then scroll. */ @@ -1071,6 +1196,8 @@ Editor::update_title () WindowTitle title(session_name); title += Glib::get_application_name(); set_title (title.get_string()); + } else { + /* ::session_going_away() will have taken care of it */ } } @@ -1083,9 +1210,9 @@ Editor::set_session (Session *t) return; } - zoom_range_clock.set_session (_session); + zoom_range_clock->set_session (_session); _playlist_selector->set_session (_session); - nudge_clock.set_session (_session); + nudge_clock->set_session (_session); _summary->set_session (_session); _group_tabs->set_session (_session); _route_groups->set_session (_session); @@ -1108,59 +1235,16 @@ Editor::set_session (Session *t) compute_fixed_ruler_scale (); - XMLNode* node = ARDOUR_UI::instance()->editor_settings(); - set_state (*node, Stateful::loading_state_version); - - /* catch up with the playhead */ - - _session->request_locate (playhead_cursor->current_frame); - _pending_initial_locate = true; - - update_title (); - - /* These signals can all be emitted by a non-GUI thread. Therefore the - handlers for them must not attempt to directly interact with the GUI, - but use Gtkmm2ext::UI::instance()->call_slot(); - */ - - _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), ui_bind(&Editor::step_edit_status_change, this, _1), gui_context()); - _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context()); - _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context()); - _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context()); - _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context()); - _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context()); - _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context()); - _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context()); - _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Editor::session_state_saved, this, _1), gui_context()); - _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context()); - _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_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), ui_bind (&Editor::refresh_location_display_s, this, _1), gui_context()); - _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context()); - - if (Profile->get_sae()) { - Timecode::BBT_Time bbt; - bbt.bars = 0; - bbt.beats = 0; - bbt.ticks = 120; - framepos_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1); - nudge_clock.set_mode(AudioClock::BBT); - nudge_clock.set (pos, true, 0, AudioClock::BBT); - - } else { - nudge_clock.set (_session->frame_rate() * 5, true, 0, AudioClock::Timecode); // default of 5 seconds - } - - playhead_cursor->canvas_item.show (); + /* Make sure we have auto loop and auto punch ranges */ Location* loc = _session->locations()->auto_loop_location(); if (loc == 0) { loc = new Location (*_session, 0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden)); - + if (loc->start() == loc->end()) { loc->set_end (loc->start() + 1); } - + _session->locations()->add (loc, false); _session->set_auto_loop_location (loc); } else { @@ -1169,14 +1253,14 @@ Editor::set_session (Session *t) } loc = _session->locations()->auto_punch_location(); - + if (loc == 0) { loc = new Location (*_session, 0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden)); - + if (loc->start() == loc->end()) { loc->set_end (loc->start() + 1); } - + _session->locations()->add (loc, false); _session->set_auto_punch_location (loc); } else { @@ -1184,24 +1268,59 @@ Editor::set_session (Session *t) loc->set_name (_("Punch")); } + refresh_location_display (); + + /* This must happen after refresh_location_display(), as (amongst other things) we restore + the selected Marker; this needs the LocationMarker list to be available. + */ + XMLNode* node = ARDOUR_UI::instance()->editor_settings(); + set_state (*node, Stateful::loading_state_version); + + /* catch up with the playhead */ + + _session->request_locate (playhead_cursor->current_frame ()); + _pending_initial_locate = true; + + update_title (); + + /* These signals can all be emitted by a non-GUI thread. Therefore the + handlers for them must not attempt to directly interact with the GUI, + but use Gtkmm2ext::UI::instance()->call_slot(); + */ + + _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::step_edit_status_change, this, _1), gui_context()); + _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context()); + _session->PositionChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_position_change, this, _1), gui_context()); + _session->RouteAdded.connect (_session_connections, invalidator (*this), boost::bind (&Editor::add_routes, this, _1), gui_context()); + _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context()); + _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::tempo_map_changed, this, _1), gui_context()); + _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context()); + _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::parameter_changed, this, _1), gui_context()); + _session->StateSaved.connect (_session_connections, invalidator (*this), boost::bind (&Editor::session_state_saved, this, _1), gui_context()); + _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 (); + boost::function pc (boost::bind (&Editor::parameter_changed, this, _1)); Config->map_parameters (pc); _session->config.map_parameters (pc); - refresh_location_display (); - restore_ruler_visibility (); //tempo_map_changed (PropertyChange (0)); _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - (static_cast(*i))->set_samples_per_unit (frames_per_unit); + (static_cast(*i))->set_frames_per_pixel (frames_per_pixel); } super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect ( sigc::mem_fun (*this, &Editor::super_rapid_screen_update) ); - + switch (_snap_type) { case SnapToRegionStart: case SnapToRegionEnd: @@ -1215,7 +1334,7 @@ Editor::set_session (Session *t) } /* register for undo history */ - _session->register_with_memento_command_factory(_id, this); + _session->register_with_memento_command_factory(id(), this); ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated)); @@ -1239,6 +1358,101 @@ Editor::action_pre_activated (Glib::RefPtr const & a) } } +void +Editor::fill_xfade_menu (Menu_Helpers::MenuList& items, bool start) +{ + using namespace Menu_Helpers; + + void (Editor::*emf)(FadeShape); + std::map* images; + + if (start) { + images = &_xfade_in_images; + emf = &Editor::set_fade_in_shape; + } else { + images = &_xfade_out_images; + emf = &Editor::set_fade_out_shape; + } + + items.push_back ( + ImageMenuElem ( + _("Linear (for highly correlated material)"), + *(*images)[FadeLinear], + sigc::bind (sigc::mem_fun (*this, emf), FadeLinear) + ) + ); + + dynamic_cast(&items.back())->set_always_show_image (); + + items.push_back ( + ImageMenuElem ( + _("Constant power"), + *(*images)[FadeConstantPower], + sigc::bind (sigc::mem_fun (*this, emf), FadeConstantPower) + )); + + dynamic_cast(&items.back())->set_always_show_image (); + + items.push_back ( + ImageMenuElem ( + _("Symmetric"), + *(*images)[FadeSymmetric], + sigc::bind (sigc::mem_fun (*this, emf), FadeSymmetric) + ) + ); + + dynamic_cast(&items.back())->set_always_show_image (); + + items.push_back ( + ImageMenuElem ( + _("Slow"), + *(*images)[FadeSlow], + sigc::bind (sigc::mem_fun (*this, emf), FadeSlow) + )); + + dynamic_cast(&items.back())->set_always_show_image (); + + items.push_back ( + ImageMenuElem ( + _("Fast"), + *(*images)[FadeFast], + sigc::bind (sigc::mem_fun (*this, emf), FadeFast) + )); + + dynamic_cast(&items.back())->set_always_show_image (); +} + +/** Pop up a context menu for when the user clicks on a start crossfade */ +void +Editor::popup_xfade_in_context_menu (int button, int32_t time, ArdourCanvas::Item* /*item*/, ItemType /*item_type*/) +{ + using namespace Menu_Helpers; + + MenuList& items (xfade_in_context_menu.items()); + + if (items.empty()) { + fill_xfade_menu (items, true); + } + + xfade_in_context_menu.popup (button, time); +} + +/** Pop up a context menu for when the user clicks on an end crossfade */ +void +Editor::popup_xfade_out_context_menu (int button, int32_t time, ArdourCanvas::Item* /*item*/, ItemType /*item_type*/) +{ + using namespace Menu_Helpers; + + MenuList& items (xfade_out_context_menu.items()); + + if (items.empty()) { + fill_xfade_menu (items, false); + } + + xfade_out_context_menu.popup (button, time); +} + + /** Pop up a context menu for when the user clicks on a fade in or fade out */ void Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type) @@ -1252,7 +1466,6 @@ Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* i } MenuList& items (fade_context_menu.items()); - items.clear (); switch (item_type) { @@ -1263,16 +1476,16 @@ Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* i } else { items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true))); } - + items.push_back (SeparatorElem()); - + if (Profile->get_sae()) { items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear))); items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast))); } else { - + items.push_back ( ImageMenuElem ( _("Linear"), @@ -1280,43 +1493,41 @@ Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* i sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear) ) ); - + dynamic_cast(&items.back())->set_always_show_image (); - + items.push_back ( ImageMenuElem ( - _("Slowest"), - *_fade_in_images[FadeFast], - sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast) + _("Slow"), + *_fade_in_images[FadeSlow], + sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow) )); - + dynamic_cast(&items.back())->set_always_show_image (); - + items.push_back ( ImageMenuElem ( - _("Slow"), - *_fade_in_images[FadeLogB], - sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogB) + _("Fast"), + *_fade_in_images[FadeFast], + sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast) )); - + dynamic_cast(&items.back())->set_always_show_image (); - + items.push_back ( ImageMenuElem ( - _("Fast"), - *_fade_in_images[FadeLogA], - sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogA) + _("Symmetric"), + *_fade_in_images[FadeSymmetric], + sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSymmetric) )); - - dynamic_cast(&items.back())->set_always_show_image (); - + items.push_back ( ImageMenuElem ( - _("Fastest"), - *_fade_in_images[FadeSlow], - sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow) + _("Constant power"), + *_fade_in_images[FadeConstantPower], + sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeConstantPower) )); - + dynamic_cast(&items.back())->set_always_show_image (); } @@ -1349,38 +1560,36 @@ Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* i items.push_back ( ImageMenuElem ( - _("Slowest"), - *_fade_out_images[FadeFast], + _("Slow"), + *_fade_out_images[FadeSlow], sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow) )); - + dynamic_cast(&items.back())->set_always_show_image (); - + items.push_back ( ImageMenuElem ( - _("Slow"), - *_fade_out_images[FadeLogB], - sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogA) + _("Fast"), + *_fade_out_images[FadeFast], + sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast) )); - + dynamic_cast(&items.back())->set_always_show_image (); - + items.push_back ( ImageMenuElem ( - _("Fast"), - *_fade_out_images[FadeLogA], - sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogB) + _("Symmetric"), + *_fade_out_images[FadeSymmetric], + sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSymmetric) )); - - dynamic_cast(&items.back())->set_always_show_image (); - + items.push_back ( ImageMenuElem ( - _("Fastest"), - *_fade_out_images[FadeSlow], - sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast) + _("Constant power"), + *_fade_out_images[FadeConstantPower], + sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeConstantPower) )); - + dynamic_cast(&items.back())->set_always_show_image (); } @@ -1424,10 +1633,6 @@ Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, } break; - case CrossfadeViewItem: - build_menu_function = &Editor::build_track_crossfade_context_menu; - break; - case StreamItem: if (clicked_routeview->track()) { build_menu_function = &Editor::build_track_context_menu; @@ -1473,9 +1678,6 @@ Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, case SelectionItem: break; - case CrossfadeViewItem: - break; - case StreamItem: break; @@ -1543,90 +1745,37 @@ Editor::build_track_bus_context_menu () MenuList& edit_items = track_context_menu.items(); edit_items.clear(); - add_bus_context_items (edit_items); - return &track_context_menu; -} - -Menu* -Editor::build_track_region_context_menu () -{ - using namespace Menu_Helpers; - MenuList& edit_items = track_region_context_menu.items(); - edit_items.clear(); - - /* we've just cleared the track region context menu, so the menu that these - two items were on will have disappeared; stop them dangling. - */ - region_edit_menu_split_item = 0; - region_edit_menu_split_multichannel_item = 0; - - /* we might try to use items that are currently attached to a crossfade menu, - so clear that, too. - */ - track_crossfade_context_menu.items().clear (); - - RouteTimeAxisView* rtv = dynamic_cast (clicked_axisview); - - if (rtv) { - boost::shared_ptr tr; - boost::shared_ptr pl; - - if ((tr = rtv->track())) { - add_region_context_items (edit_items, tr); - } - } - - add_dstream_context_items (edit_items); - - return &track_region_context_menu; + add_bus_context_items (edit_items); + return &track_context_menu; } Menu* -Editor::build_track_crossfade_context_menu () +Editor::build_track_region_context_menu () { using namespace Menu_Helpers; - MenuList& edit_items = track_crossfade_context_menu.items(); - edit_items.clear (); + MenuList& edit_items = track_region_context_menu.items(); + edit_items.clear(); - /* we might try to use items that are currently attached to a crossfade menu, - so clear that, too. + /* we've just cleared the track region context menu, so the menu that these + two items were on will have disappeared; stop them dangling. */ - track_region_context_menu.items().clear (); + region_edit_menu_split_item = 0; + region_edit_menu_split_multichannel_item = 0; - AudioTimeAxisView* atv = dynamic_cast (clicked_axisview); + RouteTimeAxisView* rtv = dynamic_cast (clicked_axisview); - if (atv) { + if (rtv) { boost::shared_ptr tr; boost::shared_ptr pl; - boost::shared_ptr apl; - - if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast (pl)) != 0)) { - - AudioPlaylist::Crossfades xfades; - framepos_t where; - bool ignored; - - /* The xfade menu is a bit of a special case, as we always use the mouse position - to decide whether or not to display it (rather than the edit point). No particularly - strong reasons for this, other than it is a bit surprising to right-click on a xfade - and not get a menu. - */ - mouse_frame (where, ignored); - apl->crossfades_at (where, xfades); - - bool const many = xfades.size() > 1; - - for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) { - add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many); - } + if ((tr = rtv->track())) { add_region_context_items (edit_items, tr); } } add_dstream_context_items (edit_items); - return &track_crossfade_context_menu; + return &track_region_context_menu; } void @@ -1679,77 +1828,17 @@ Editor::build_track_selection_context_menu () return &track_selection_context_menu; } -/** Add context menu items relevant to crossfades. - * @param edit_items List to add the items to. - */ -void -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); - MenuList& items = xfade_menu->items(); - xfade_menu->set_name ("ArdourContextMenu"); - string str; - - if (xfade->active()) { - str = _("Mute"); - } else { - str = _("Unmute"); - } - - items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr (xfade)))); - items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr (xfade)))); - - if (xfade->can_follow_overlap()) { - - if (xfade->following_overlap()) { - str = _("Convert to Short"); - } else { - str = _("Convert to Full"); - } - - items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade))); - } - - if (many) { - str = xfade->out()->name(); - str += "->"; - str += xfade->in()->name(); - } else { - str = _("Crossfade"); - } - - edit_items.push_back (MenuElem (str, *xfade_menu)); - edit_items.push_back (SeparatorElem()); -} - -void -Editor::xfade_edit_left_region () -{ - if (clicked_crossfadeview) { - clicked_crossfadeview->left_view.show_region_editor (); - } -} - -void -Editor::xfade_edit_right_region () -{ - if (clicked_crossfadeview) { - clicked_crossfadeview->right_view.show_region_editor (); - } -} - void Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::shared_ptr track) { using namespace Menu_Helpers; - + /* OK, stick the region submenu at the top of the list, and then add the standard items. */ RegionSelection rs = get_regions_from_selection_and_entered (); - + string::size_type pos = 0; string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions"); @@ -1770,18 +1859,11 @@ Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::sha _popup_region_menu_item->set_label (menu_item_name); } - /* Use the mouse position rather than the edit point to decide whether to show the `choose top region' - dialogue. If we use the edit point it gets a bit messy because the user still has to click over - *some* region in order to get the region context menu stuff to be displayed at all. - */ - - framepos_t mouse; - bool ignored; - mouse_frame (mouse, ignored); + const framepos_t position = get_preferred_edit_position (false, true); edit_items.push_back (*_popup_region_menu_item); - if (track->playlist()->count_regions_at (mouse) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) { - edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region")->create_menu_item ())); + if (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()); } @@ -1800,11 +1882,35 @@ Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items) edit_items.push_back (SeparatorElem()); edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection))); - if (!selection->regions.empty()) { - edit_items.push_back (SeparatorElem()); - edit_items.push_back (MenuElem (_("Extend Range to End of Region"), sigc::bind (sigc::mem_fun(*this, &Editor::extend_selection_to_end_of_region), false))); - edit_items.push_back (MenuElem (_("Extend Range to Start of Region"), sigc::bind (sigc::mem_fun(*this, &Editor::extend_selection_to_start_of_region), false))); - } + edit_items.push_back (SeparatorElem()); + + edit_items.push_back ( + MenuElem ( + _("Move Range Start to Previous Region Boundary"), + sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, false) + ) + ); + + edit_items.push_back ( + MenuElem ( + _("Move Range Start to Next Region Boundary"), + sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, true) + ) + ); + + edit_items.push_back ( + MenuElem ( + _("Move Range End to Previous Region Boundary"), + sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, false) + ) + ); + + edit_items.push_back ( + MenuElem ( + _("Move Range End to Next Region Boundary"), + sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, true) + ) + ); edit_items.push_back (SeparatorElem()); edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection))); @@ -1823,14 +1929,14 @@ Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items) edit_items.push_back (SeparatorElem()); edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection))); edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection))); - edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false))); + edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_range), false))); edit_items.push_back (SeparatorElem()); edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false))); edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true))); edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false))); 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))); + edit_items.push_back (MenuElem (_("Export Range..."), sigc::mem_fun(*this, &Editor::export_selection))); } @@ -1885,7 +1991,7 @@ Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items) cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut))); cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy))); - cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f))); + cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f, true))); cutnpaste_items.push_back (SeparatorElem()); @@ -1907,10 +2013,10 @@ Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items) nudge_menu->set_name ("ArdourContextMenu"); edit_items.push_back (SeparatorElem()); - nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true)))); - nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true)))); - nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false)))); - nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false)))); + nudge_items.push_back (MenuElem (_("Nudge Entire Track Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true)))); + nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true)))); + nudge_items.push_back (MenuElem (_("Nudge Entire Track Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false)))); + nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false)))); edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu)); } @@ -1956,17 +2062,17 @@ Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items) cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut))); cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy))); - cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f))); + cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f, true))); Menu *nudge_menu = manage (new Menu()); MenuList& nudge_items = nudge_menu->items(); nudge_menu->set_name ("ArdourContextMenu"); edit_items.push_back (SeparatorElem()); - nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true)))); - nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true)))); - nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false)))); - nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false)))); + nudge_items.push_back (MenuElem (_("Nudge Entire Track Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true)))); + nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Later"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true)))); + nudge_items.push_back (MenuElem (_("Nudge Entire Track Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false)))); + nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Earlier"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false)))); edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu)); } @@ -2004,6 +2110,8 @@ Editor::set_snap_to (SnapType st) instant_save (); switch (_snap_type) { + case SnapToBeatDiv128: + case SnapToBeatDiv64: case SnapToBeatDiv32: case SnapToBeatDiv28: case SnapToBeatDiv24: @@ -2018,10 +2126,17 @@ Editor::set_snap_to (SnapType st) case SnapToBeatDiv5: case SnapToBeatDiv4: case SnapToBeatDiv3: - case SnapToBeatDiv2: - compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames()); - update_tempo_based_rulers (); + case SnapToBeatDiv2: { + ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_begin; + ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_end; + + compute_current_bbt_points (leftmost_frame, leftmost_frame + current_page_frames(), + current_bbt_points_begin, current_bbt_points_end); + compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames(), + current_bbt_points_begin, current_bbt_points_end); + update_tempo_based_rulers (current_bbt_points_begin, current_bbt_points_end); break; + } case SnapToRegionStart: case SnapToRegionEnd: @@ -2041,9 +2156,16 @@ Editor::set_snap_to (SnapType st) void Editor::set_snap_mode (SnapMode mode) { - _snap_mode = mode; string str = snap_mode_strings[(int)mode]; + if (_internal_editing) { + internal_snap_mode = mode; + } else { + pre_internal_snap_mode = mode; + } + + _snap_mode = mode; + if (str != snap_mode_selector.get_active_text ()) { snap_mode_selector.set_active_text (str); } @@ -2104,19 +2226,15 @@ Editor::set_state (const XMLNode& node, int /*version*/) { const XMLProperty* prop; XMLNode* geometry; - int x, y, xoff, yoff; + int x, y; Gdk::Geometry g; - if ((prop = node.property ("id")) != 0) { - _id = prop->value (); - } + set_id (node); g.base_width = default_width; g.base_height = default_height; x = 1; y = 1; - xoff = 0; - yoff = 21; if ((geometry = find_named_node (node, "geometry")) != 0) { @@ -2148,24 +2266,11 @@ Editor::set_state (const XMLNode& node, int /*version*/) if (prop) { y = atoi (prop->value()); } - - if ((prop = geometry->property ("x_off")) == 0) { - prop = geometry->property ("x-off"); - } - if (prop) { - xoff = atoi (prop->value()); - } - if ((prop = geometry->property ("y_off")) == 0) { - prop = geometry->property ("y-off"); - } - if (prop) { - yoff = atoi (prop->value()); - } } set_default_size (g.base_width, g.base_height); move (x, y); - + if (_session && (prop = node.property ("playhead"))) { framepos_t pos; sscanf (prop->value().c_str(), "%" PRIi64, &pos); @@ -2173,27 +2278,44 @@ Editor::set_state (const XMLNode& node, int /*version*/) } else { playhead_cursor->set_position (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())); + set_zoom_focus ((ZoomFocus) string_2_enum (prop->value(), zoom_focus)); } if ((prop = node.property ("zoom"))) { reset_zoom (PBD::atof (prop->value())); } else { - reset_zoom (frames_per_unit); + reset_zoom (frames_per_pixel); } if ((prop = node.property ("snap-to"))) { - set_snap_to ((SnapType) atoi (prop->value())); + set_snap_to ((SnapType) string_2_enum (prop->value(), _snap_type)); } if ((prop = node.property ("snap-mode"))) { - set_snap_mode ((SnapMode) atoi (prop->value())); + set_snap_mode ((SnapMode) string_2_enum (prop->value(), _snap_mode)); + } + + if ((prop = node.property ("internal-snap-to"))) { + internal_snap_type = (SnapType) string_2_enum (prop->value(), internal_snap_type); + } + + if ((prop = node.property ("internal-snap-mode"))) { + internal_snap_mode = (SnapMode) string_2_enum (prop->value(), internal_snap_mode); + } + + if ((prop = node.property ("pre-internal-snap-to"))) { + 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); } if ((prop = node.property ("mouse-mode"))) { @@ -2206,6 +2328,9 @@ Editor::set_state (const XMLNode& node, int /*version*/) if ((prop = node.property ("left-frame")) != 0) { framepos_t pos; if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) { + if (pos < 0) { + pos = 0; + } reset_x_origin (pos); } } @@ -2225,7 +2350,14 @@ Editor::set_state (const XMLNode& node, int /*version*/) } if ((prop = node.property ("join-object-range"))) { - join_object_range_button.set_active (string_is_affirmative (prop->value ())); + RefPtr act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object-range")); + bool yn = string_is_affirmative (prop->value()); + if (act) { + RefPtr tact = RefPtr::cast_dynamic(act); + tact->set_active (!yn); + tact->set_active (yn); + } + set_mouse_mode(mouse_mode, true); } if ((prop = node.property ("edit-point"))) { @@ -2257,7 +2389,7 @@ Editor::set_state (const XMLNode& node, int /*version*/) } if ((prop = node.property ("stationary-playhead"))) { - bool yn = (prop->value() == "yes"); + bool yn = string_is_affirmative (prop->value()); set_stationary_playhead (yn); RefPtr act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead")); if (act) { @@ -2267,18 +2399,12 @@ Editor::set_state (const XMLNode& node, int /*version*/) } } } - + if ((prop = node.property ("region-list-sort-type"))) { RegionListSortType st; _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true); } - if ((prop = node.property ("xfades-visible"))) { - bool yn = string_is_affirmative (prop->value()); - _xfade_visibility = !yn; - // set_xfade_visibility (yn); - } - if ((prop = node.property ("show-editor-mixer"))) { Glib::RefPtr act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer")); @@ -2286,9 +2412,9 @@ Editor::set_state (const XMLNode& node, int /*version*/) Glib::RefPtr tact = Glib::RefPtr::cast_dynamic(act); bool yn = string_is_affirmative (prop->value()); - + /* do it twice to force the change */ - + tact->set_active (!yn); tact->set_active (yn); } @@ -2300,9 +2426,9 @@ Editor::set_state (const XMLNode& node, int /*version*/) Glib::RefPtr tact = Glib::RefPtr::cast_dynamic(act); bool yn = string_is_affirmative (prop->value()); - + /* do it twice to force the change */ - + tact->set_active (!yn); tact->set_active (yn); } @@ -2327,6 +2453,22 @@ Editor::set_state (const XMLNode& node, int /*version*/) _regions->set_state (**i); } + if ((prop = node.property ("maximised"))) { + bool yn = string_is_affirmative (prop->value()); + if (yn) { + ActionManager::do_action ("Common", "ToggleMaximalEditor"); + } + } + + if ((prop = node.property ("nudge-clock-value"))) { + framepos_t f; + sscanf (prop->value().c_str(), "%" PRId64, &f); + nudge_clock->set (f); + } else { + nudge_clock->set_mode (AudioClock::Timecode); + nudge_clock->set (_session->frame_rate() * 5, true); + } + return 0; } @@ -2336,15 +2478,14 @@ Editor::get_state () XMLNode* node = new XMLNode ("Editor"); char buf[32]; - _id.print (buf, sizeof (buf)); + id().print (buf, sizeof (buf)); node->add_property ("id", buf); if (is_realized()) { Glib::RefPtr win = get_window(); - int x, y, xoff, yoff, width, height; + int x, y, width, height; win->get_root_origin(x, y); - win->get_position(xoff, yoff); win->get_size(width, height); XMLNode* geometry = new XMLNode ("geometry"); @@ -2357,15 +2498,9 @@ Editor::get_state () geometry->add_property("x-pos", string(buf)); snprintf(buf, sizeof(buf), "%d", y); geometry->add_property("y-pos", string(buf)); - snprintf(buf, sizeof(buf), "%d", xoff); - geometry->add_property("x-off", string(buf)); - snprintf(buf, sizeof(buf), "%d", yoff); - geometry->add_property("y-off", string(buf)); snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast(&edit_pane)->gobj())); geometry->add_property("edit-horizontal-pane-pos", string(buf)); geometry->add_property("notebook-shrunk", _notebook_shrunk ? "1" : "0"); - snprintf(buf,sizeof(buf), "%d",pre_maximal_horizontal_pane_position); - geometry->add_property("pre-maximal-horizontal-pane-position", string(buf)); snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast(&editor_summary_pane)->gobj())); geometry->add_property("edit-vertical-pane-pos", string(buf)); @@ -2374,18 +2509,18 @@ Editor::get_state () 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); + node->add_property ("zoom-focus", enum_2_string (zoom_focus)); + snprintf (buf, sizeof(buf), "%f", frames_per_pixel); node->add_property ("zoom", buf); - snprintf (buf, sizeof(buf), "%d", (int) _snap_type); - node->add_property ("snap-to", buf); - snprintf (buf, sizeof(buf), "%d", (int) _snap_mode); - node->add_property ("snap-mode", buf); - + node->add_property ("snap-to", enum_2_string (_snap_type)); + node->add_property ("snap-mode", enum_2_string (_snap_mode)); + node->add_property ("internal-snap-to", enum_2_string (internal_snap_type)); + node->add_property ("internal-snap-mode", enum_2_string (internal_snap_mode)); + node->add_property ("pre-internal-snap-to", enum_2_string (pre_internal_snap_type)); + node->add_property ("pre-internal-snap-mode", enum_2_string (pre_internal_snap_mode)); node->add_property ("edit-point", enum_2_string (_edit_point)); - snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame); + snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame ()); node->add_property ("playhead", buf); snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame); node->add_property ("left-frame", buf); @@ -2393,13 +2528,13 @@ Editor::get_state () node->add_property ("y-origin", buf); node->add_property ("show-measures", _show_measures ? "yes" : "no"); + node->add_property ("maximised", _maximised ? "yes" : "no"); node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no"); node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no"); - node->add_property ("xfades-visible", _xfade_visibility ? "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", join_object_range_button.get_active () ? "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")); if (act) { @@ -2420,13 +2555,16 @@ Editor::get_state () XMLNode* bb = new XMLNode (X_("Buttons")); button_bindings->save (*bb); node->add_child_nocopy (*bb); - } + } node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no"); node->add_child_nocopy (selection->get_state ()); node->add_child_nocopy (_regions->get_state ()); - + + snprintf (buf, sizeof (buf), "%" PRId64, nudge_clock->current_duration()); + node->add_property ("nudge-clock-value", buf); + return *node; } @@ -2435,14 +2573,14 @@ Editor::get_state () /** @param y y offset from the top of all trackviews. * @return pair: TimeAxisView that y is over, layer index. * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is - * in stacked region display mode, otherwise 0. + * in stacked or expanded region display mode, otherwise 0. */ -std::pair +std::pair Editor::trackview_by_y_position (double y) { for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) { - std::pair const r = (*iter)->covers_y_position (y); + std::pair const r = (*iter)->covers_y_position (y); if (r.first) { return r; } @@ -2588,6 +2726,12 @@ Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark) start = _session->tempo_map().round_to_beat (start, direction); break; + case SnapToBeatDiv128: + start = _session->tempo_map().round_to_beat_subdivision (start, 128, direction); + break; + case SnapToBeatDiv64: + start = _session->tempo_map().round_to_beat_subdivision (start, 64, direction); + break; case SnapToBeatDiv32: start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction); break; @@ -2641,7 +2785,10 @@ Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark) _session->locations()->marks_either_side (start, before, after); - if (before == max_framepos) { + if (before == max_framepos && after == max_framepos) { + /* No marks to snap to, so just don't snap */ + return; + } else if (before == max_framepos) { start = after; } else if (after == max_framepos) { start = before; @@ -2716,44 +2863,47 @@ Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark) void Editor::setup_toolbar () { - string pixmap_path; - - /* Mode Buttons (tool selection) */ - - mouse_move_button.set_relief(Gtk::RELIEF_NONE); - mouse_select_button.set_relief(Gtk::RELIEF_NONE); - mouse_gain_button.set_relief(Gtk::RELIEF_NONE); - mouse_zoom_button.set_relief(Gtk::RELIEF_NONE); - mouse_timefx_button.set_relief(Gtk::RELIEF_NONE); - mouse_audition_button.set_relief(Gtk::RELIEF_NONE); - // internal_edit_button.set_relief(Gtk::RELIEF_NONE); - join_object_range_button.set_relief(Gtk::RELIEF_NONE); - HBox* mode_box = manage(new HBox); mode_box->set_border_width (2); mode_box->set_spacing(4); - /* table containing mode buttons */ + HBox* mouse_mode_box = manage (new HBox); + HBox* mouse_mode_hbox = manage (new HBox); + VBox* mouse_mode_vbox = manage (new VBox); + Alignment* mouse_mode_align = manage (new Alignment); - HBox* mouse_mode_button_box = manage (new HBox ()); + Glib::RefPtr mouse_mode_size_group = SizeGroup::create (SIZE_GROUP_BOTH); +// mouse_mode_size_group->add_widget (smart_mode_button); + mouse_mode_size_group->add_widget (mouse_move_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); - if (Profile->get_sae()) { - mouse_mode_button_box->pack_start (mouse_move_button); - } else { - mouse_mode_button_box->pack_start (mouse_move_button); - mouse_mode_button_box->pack_start (join_object_range_button); - mouse_mode_button_box->pack_start (mouse_select_button); - } + /* make them just a bit bigger */ + mouse_move_button.set_size_request (-1, 30); - mouse_mode_button_box->pack_start (mouse_zoom_button); + mouse_mode_hbox->set_spacing (2); - if (!Profile->get_sae()) { - mouse_mode_button_box->pack_start (mouse_gain_button); - } + mouse_mode_hbox->pack_start (smart_mode_button, false, false); + mouse_mode_hbox->pack_start (mouse_move_button, false, false); + mouse_mode_hbox->pack_start (mouse_select_button, false, false); + mouse_mode_hbox->pack_start (mouse_zoom_button, false, false); + 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, 8); + + mouse_mode_vbox->pack_start (*mouse_mode_hbox); - mouse_mode_button_box->pack_start (mouse_timefx_button); - mouse_mode_button_box->pack_start (mouse_audition_button); - mouse_mode_button_box->pack_start (internal_edit_button); + mouse_mode_align->add (*mouse_mode_vbox); + mouse_mode_align->set (0.5, 1.0, 0.0, 0.0); + + mouse_mode_box->pack_start (*mouse_mode_align, false, false); edit_mode_strings.push_back (edit_mode_to_string (Slide)); if (!Profile->get_sae()) { @@ -2762,11 +2912,11 @@ Editor::setup_toolbar () edit_mode_strings.push_back (edit_mode_to_string (Lock)); edit_mode_selector.set_name ("EditModeSelector"); - set_popdown_strings (edit_mode_selector, edit_mode_strings, true); + set_popdown_strings (edit_mode_selector, edit_mode_strings); edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done)); - mode_box->pack_start (edit_mode_selector); - mode_box->pack_start (*mouse_mode_button_box); + mode_box->pack_start (edit_mode_selector, false, false); + mode_box->pack_start (*mouse_mode_box, false, false); _mouse_mode_tearoff = manage (new TearOff (*mode_box)); _mouse_mode_tearoff->set_name ("MouseModeBase"); @@ -2785,75 +2935,64 @@ Editor::setup_toolbar () _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast (&toolbar_hbox), &_mouse_mode_tearoff->tearoff_window(), 1)); - mouse_move_button.set_mode (false); - mouse_select_button.set_mode (false); - mouse_gain_button.set_mode (false); - mouse_zoom_button.set_mode (false); - mouse_timefx_button.set_mode (false); - mouse_audition_button.set_mode (false); - join_object_range_button.set_mode (false); - - mouse_move_button.set_name ("MouseModeButton"); - mouse_select_button.set_name ("MouseModeButton"); - mouse_gain_button.set_name ("MouseModeButton"); - mouse_zoom_button.set_name ("MouseModeButton"); - mouse_timefx_button.set_name ("MouseModeButton"); - mouse_audition_button.set_name ("MouseModeButton"); - internal_edit_button.set_name ("MouseModeButton"); - join_object_range_button.set_name ("MouseModeButton"); - - mouse_move_button.unset_flags (CAN_FOCUS); - mouse_select_button.unset_flags (CAN_FOCUS); - mouse_gain_button.unset_flags (CAN_FOCUS); - mouse_zoom_button.unset_flags (CAN_FOCUS); - mouse_timefx_button.unset_flags (CAN_FOCUS); - mouse_audition_button.unset_flags (CAN_FOCUS); - internal_edit_button.unset_flags (CAN_FOCUS); - join_object_range_button.unset_flags (CAN_FOCUS); - /* Zoom */ - _zoom_box.set_spacing (1); - _zoom_box.set_border_width (0); + _zoom_box.set_spacing (2); + _zoom_box.set_border_width (2); - zoom_in_button.set_name ("EditorTimeButton"); - zoom_in_button.set_image (*(manage (new Image (::get_icon ("zoom_in"))))); - zoom_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), false)); - - zoom_out_button.set_name ("EditorTimeButton"); - zoom_out_button.set_image (*(manage (new Image (::get_icon ("zoom_out"))))); - zoom_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), true)); + RefPtr act; - zoom_out_full_button.set_name ("EditorTimeButton"); - zoom_out_full_button.set_image (*(manage (new Image (::get_icon ("zoom_full"))))); - zoom_out_full_button.signal_clicked().connect (sigc::mem_fun(*this, &Editor::temporal_zoom_session)); + zoom_in_button.set_name ("zoom button"); + zoom_in_button.add_elements ( ArdourButton::FlatFace ); + zoom_in_button.set_tweaks ((ArdourButton::Tweaks) (ArdourButton::ShowClick) ); + zoom_in_button.set_image(::get_icon ("zoom_in")); + act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-in")); + zoom_in_button.set_related_action (act); + + zoom_out_button.set_name ("zoom button"); + zoom_out_button.add_elements ( ArdourButton::FlatFace ); + zoom_out_button.set_tweaks ((ArdourButton::Tweaks) (ArdourButton::ShowClick) ); + zoom_out_button.set_image(::get_icon ("zoom_out")); + act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-out")); + zoom_out_button.set_related_action (act); + + zoom_out_full_button.set_name ("zoom button"); + zoom_out_full_button.add_elements ( ArdourButton::FlatFace ); + zoom_out_full_button.set_tweaks ((ArdourButton::Tweaks) (ArdourButton::ShowClick) ); + zoom_out_full_button.set_image(::get_icon ("zoom_full")); + act = ActionManager::get_action (X_("Editor"), X_("zoom-to-session")); + zoom_out_full_button.set_related_action (act); zoom_focus_selector.set_name ("ZoomFocusSelector"); - set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true); + set_popdown_strings (zoom_focus_selector, zoom_focus_strings); zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done)); _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); - _zoom_box.pack_start (zoom_focus_selector); - + _zoom_box.pack_start (zoom_focus_selector, false, false); + /* Track zoom buttons */ - tav_expand_button.set_name ("TrackHeightButton"); + tav_expand_button.set_name ("zoom button"); + tav_expand_button.add_elements ( ArdourButton::FlatFace ); + tav_expand_button.set_tweaks ((ArdourButton::Tweaks) (ArdourButton::ShowClick) ); tav_expand_button.set_size_request (-1, 20); - tav_expand_button.add (*(manage (new Image (::get_icon ("tav_exp"))))); - RefPtr act = ActionManager::get_action (X_("Editor"), X_("expand-tracks")); - act->connect_proxy (tav_expand_button); + tav_expand_button.set_image(::get_icon ("tav_exp")); + act = ActionManager::get_action (X_("Editor"), X_("expand-tracks")); + tav_expand_button.set_related_action (act); - tav_shrink_button.set_name ("TrackHeightButton"); + tav_shrink_button.set_name ("zoom button"); + tav_shrink_button.add_elements ( ArdourButton::FlatFace ); + tav_shrink_button.set_tweaks ((ArdourButton::Tweaks) (ArdourButton::ShowClick) ); tav_shrink_button.set_size_request (-1, 20); - tav_shrink_button.add (*(manage (new Image (::get_icon ("tav_shrink"))))); + tav_shrink_button.set_image(::get_icon ("tav_shrink")); act = ActionManager::get_action (X_("Editor"), X_("shrink-tracks")); - act->connect_proxy (tav_shrink_button); + tav_shrink_button.set_related_action (act); _zoom_box.pack_start (tav_shrink_button); _zoom_box.pack_start (tav_expand_button); - + _zoom_tearoff = manage (new TearOff (_zoom_box)); _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast(&toolbar_hbox), @@ -2864,20 +3003,20 @@ Editor::setup_toolbar () &_zoom_tearoff->tearoff_window())); _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast (&toolbar_hbox), &_zoom_tearoff->tearoff_window(), 0)); - - snap_box.set_spacing (1); + + snap_box.set_spacing (2); snap_box.set_border_width (2); snap_type_selector.set_name ("SnapTypeSelector"); - set_popdown_strings (snap_type_selector, snap_type_strings, true); + set_popdown_strings (snap_type_selector, snap_type_strings); snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done)); snap_mode_selector.set_name ("SnapModeSelector"); - set_popdown_strings (snap_mode_selector, snap_mode_strings, true); + set_popdown_strings (snap_mode_selector, snap_mode_strings); snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done)); edit_point_selector.set_name ("EditPointSelector"); - set_popdown_strings (edit_point_selector, edit_point_strings, true); + set_popdown_strings (edit_point_selector, edit_point_strings); edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done)); snap_box.pack_start (snap_mode_selector, false, false); @@ -2887,15 +3026,18 @@ Editor::setup_toolbar () /* Nudge */ HBox *nudge_box = manage (new HBox); - nudge_box->set_spacing(1); + nudge_box->set_spacing (2); nudge_box->set_border_width (2); nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false); nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false); + nudge_forward_button.set_tweaks ((ArdourButton::Tweaks) (ArdourButton::ShowClick) ); + nudge_backward_button.set_tweaks ((ArdourButton::Tweaks) (ArdourButton::ShowClick) ); + nudge_box->pack_start (nudge_backward_button, false, false); nudge_box->pack_start (nudge_forward_button, false, false); - nudge_box->pack_start (nudge_clock, false, false); + nudge_box->pack_start (*nudge_clock, false, false); /* Pack everything in... */ @@ -2906,7 +3048,7 @@ Editor::setup_toolbar () _tools_tearoff = manage (new TearOff (*hbox)); _tools_tearoff->set_name ("MouseModeBase"); _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false); - + if (Profile->get_sae()) { _tools_tearoff->set_can_be_torn_off (false); } @@ -2928,7 +3070,11 @@ Editor::setup_toolbar () toolbar_hbox.pack_start (*_tools_tearoff, false, false); hbox->pack_start (snap_box, false, false); - hbox->pack_start (*nudge_box, false, false); + if (!Profile->get_small_screen()) { + hbox->pack_start (*nudge_box, false, false); + } else { + ARDOUR_UI::instance()->editor_transport_box().pack_start (*nudge_box, false, false); + } hbox->pack_start (panic_box, false, false); hbox->show_all (); @@ -2943,23 +3089,23 @@ Editor::setup_toolbar () toolbar_frame.set_shadow_type (SHADOW_OUT); toolbar_frame.set_name ("BaseFrame"); toolbar_frame.add (_toolbar_viewport); - - DPIReset.connect (sigc::mem_fun (*this, &Editor::resize_text_widgets)); } void Editor::setup_tooltips () { - ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects")); + 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_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 (join_object_range_button, _("Select/Move Objects or Ranges")); - ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)")); + ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Note Level Editing")); 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 Forwards")); - ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards")); + ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Later")); + ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Earlier")); ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In")); ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out")); ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session")); @@ -2969,39 +3115,8 @@ Editor::setup_tooltips () ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units")); ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode")); ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point")); - ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes")); - ARDOUR_UI::instance()->set_tip (midi_panic_button, _("Send note off and reset controller messages on all MIDI channels")); ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode")); -} - -void -Editor::midi_panic () -{ - cerr << "MIDI panic\n"; - - if (_session) { - _session->midi_panic(); - } -} - -void -Editor::setup_midi_toolbar () -{ - RefPtr act; - - /* Midi sound notes */ - midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes"))))); - midi_sound_notes.set_relief(Gtk::RELIEF_NONE); - midi_sound_notes.unset_flags (CAN_FOCUS); - - /* Panic */ - - act = ActionManager::get_action (X_("MIDI"), X_("panic")); - midi_panic_button.set_name("MidiPanicButton"); - act->connect_proxy (midi_panic_button); - - panic_box.pack_start (midi_sound_notes , true, true); - panic_box.pack_start (midi_panic_button, true, true); + ARDOUR_UI::instance()->set_tip (nudge_clock, _("Nudge Clock\n(controls distance used to nudge regions and selections)")); } int @@ -3017,7 +3132,7 @@ Editor::convert_drop_to_paths ( if (_session == 0) { return -1; } - + vector uris = data.get_uris(); if (uris.empty()) { @@ -3041,8 +3156,8 @@ Editor::convert_drop_to_paths ( const char* q; p = (const char *) malloc (txt.length() + 1); - txt.copy ((char *) p, txt.length(), 0); - ((char*)p)[txt.length()] = '\0'; + txt.copy (const_cast (p), txt.length(), 0); + const_cast(p)[txt.length()] = '\0'; while (p) { @@ -3084,14 +3199,13 @@ Editor::convert_drop_to_paths ( if ((*i).substr (0,7) == "file://") { - string p = *i; - PBD::url_decode (p); + string const p = PBD::url_decode (*i); // scan forward past three slashes string::size_type slashcnt = 0; string::size_type n = 0; - string::iterator x = p.begin(); + string::const_iterator x = p.begin(); while (slashcnt < 3 && x != p.end()) { if ((*x) == '/') { @@ -3124,7 +3238,7 @@ Editor::new_tempo_section () void Editor::map_transport_state () { - ENSURE_GUI_THREAD (*this, &Editor::map_transport_state) + ENSURE_GUI_THREAD (*this, &Editor::map_transport_state); if (_session && _session->transport_stopped()) { have_pending_keyboard_selection = false; @@ -3135,16 +3249,6 @@ Editor::map_transport_state () /* UNDO/REDO */ -Editor::State::State (PublicEditor const * e) -{ - selection = new Selection (e); -} - -Editor::State::~State () -{ - delete selection; -} - void Editor::begin_reversible_command (string name) { @@ -3176,9 +3280,9 @@ Editor::history_changed () if (undo_action && _session) { if (_session->undo_depth() == 0) { - label = _("Undo"); + label = S_("Command|Undo"); } else { - label = string_compose(_("Undo (%1)"), _session->next_undo()); + label = string_compose(S_("Command|Undo (%1)"), _session->next_undo()); } undo_action->property_label() = label; } @@ -3194,19 +3298,13 @@ Editor::history_changed () } void -Editor::duplicate_dialog (bool with_dialog) +Editor::duplicate_range (bool with_dialog) { float times = 1.0f; - if (mouse_mode == MouseRange) { - if (selection->time.length() == 0) { - return; - } - } - RegionSelection rs = get_regions_from_selection_and_entered (); - if (mouse_mode != MouseRange && rs.empty()) { + if ( selection->time.length() == 0 && rs.empty()) { return; } @@ -3253,86 +3351,20 @@ Editor::duplicate_dialog (bool with_dialog) times = adjustment.get_value(); } - if (mouse_mode == MouseRange) { - duplicate_selection (times); + if ((current_mouse_mode() == Editing::MouseRange)) { + if (selection->time.length()) { + duplicate_selection (times); + } + } else if (get_smart_mode()) { + if (selection->time.length()) { + duplicate_selection (times); + } else + duplicate_some_regions (rs, times); } else { duplicate_some_regions (rs, times); } } -void -Editor::show_verbose_canvas_cursor () -{ - verbose_canvas_cursor->raise_to_top(); - verbose_canvas_cursor->show(); - verbose_cursor_visible = true; -} - -void -Editor::hide_verbose_canvas_cursor () -{ - verbose_canvas_cursor->hide(); - verbose_cursor_visible = false; -} - -double -Editor::clamp_verbose_cursor_x (double x) -{ - if (x < 0) { - x = 0; - } else { - x = min (_canvas_width - 200.0, x); - } - return x; -} - -double -Editor::clamp_verbose_cursor_y (double y) -{ - if (y < canvas_timebars_vsize) { - y = canvas_timebars_vsize; - } else { - y = min (_canvas_height - 50, y); - } - return y; -} - -void -Editor::show_verbose_canvas_cursor_with (const string & txt, int32_t xoffset, int32_t yoffset) -{ - verbose_canvas_cursor->property_text() = txt.c_str(); - - int x, y; - double wx, wy; - - track_canvas->get_pointer (x, y); - track_canvas->window_to_world (x, y, wx, wy); - - wx += xoffset; - wy += yoffset; - - /* don't get too close to the edge */ - verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (wx); - verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (wy); - - show_verbose_canvas_cursor (); -} - -void -Editor::set_verbose_canvas_cursor (const string & txt, double x, double y) -{ - verbose_canvas_cursor->property_text() = txt.c_str(); - /* don't get too close to the edge */ - verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (x); - verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (y); -} - -void -Editor::set_verbose_canvas_cursor_text (const string & txt) -{ - verbose_canvas_cursor->property_text() = txt.c_str(); -} - void Editor::set_edit_mode (EditMode m) { @@ -3405,6 +3437,10 @@ Editor::snap_type_selection_done () snaptype = SnapToBeatDiv28; } else if (choice == _("Beats/32")) { snaptype = SnapToBeatDiv32; + } else if (choice == _("Beats/64")) { + snaptype = SnapToBeatDiv64; + } else if (choice == _("Beats/128")) { + snaptype = SnapToBeatDiv128; } else if (choice == _("Beats")) { snaptype = SnapToBeat; } else if (choice == _("Bars")) { @@ -3528,16 +3564,19 @@ Editor::zoom_focus_selection_done () } } -gint +bool Editor::edit_controls_button_release (GdkEventButton* ev) { if (Keyboard::is_context_menu_event (ev)) { ARDOUR_UI::instance()->add_route (this); + } else if (ev->button == 1) { + selection->clear_tracks (); } - return TRUE; + + return true; } -gint +bool Editor::mouse_select_button_release (GdkEventButton* ev) { /* this handles just right-clicks */ @@ -3560,13 +3599,35 @@ Editor::set_zoom_focus (ZoomFocus f) if (zoom_focus != f) { zoom_focus = f; - - ZoomFocusChanged (); /* EMIT_SIGNAL */ - instant_save (); } } +void +Editor::cycle_zoom_focus () +{ + switch (zoom_focus) { + case ZoomFocusLeft: + set_zoom_focus (ZoomFocusRight); + break; + case ZoomFocusRight: + set_zoom_focus (ZoomFocusCenter); + break; + case ZoomFocusCenter: + set_zoom_focus (ZoomFocusPlayhead); + break; + case ZoomFocusPlayhead: + set_zoom_focus (ZoomFocusMouse); + break; + case ZoomFocusMouse: + set_zoom_focus (ZoomFocusEdit); + break; + case ZoomFocusEdit: + set_zoom_focus (ZoomFocusLeft); + break; + } +} + void Editor::ensure_float (Window& win) { @@ -3584,7 +3645,6 @@ Editor::pane_allocation_handler (Allocation &alloc, Paned* which) XMLProperty* prop; char buf[32]; XMLNode* node = ARDOUR_UI::instance()->editor_settings(); - int width, height; enum Pane { Horizontal = 0x1, @@ -3592,23 +3652,8 @@ Editor::pane_allocation_handler (Allocation &alloc, Paned* which) }; static Pane done; - - XMLNode* geometry; - - width = default_width; - height = default_height; - - if ((geometry = find_named_node (*node, "geometry")) != 0) { - prop = geometry->property ("x-size"); - if (prop) { - width = atoi (prop->value()); - } - prop = geometry->property ("y-size"); - if (prop) { - height = atoi (prop->value()); - } - } + XMLNode* geometry = find_named_node (*node, "geometry"); if (which == static_cast (&edit_pane)) { @@ -3620,10 +3665,6 @@ Editor::pane_allocation_handler (Allocation &alloc, Paned* which) _notebook_shrunk = string_is_affirmative (prop->value ()); } - if (geometry && (prop = geometry->property ("pre-maximal-horizontal-pane-position"))) { - pre_maximal_horizontal_pane_position = atoi (prop->value ()); - } - if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) { /* initial allocation is 90% to canvas, 10% to notebook */ pos = (int) floor (alloc.get_width() * 0.90f); @@ -3634,13 +3675,10 @@ Editor::pane_allocation_handler (Allocation &alloc, Paned* which) if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) { edit_pane.set_position (pos); - if (pre_maximal_horizontal_pane_position == 0) { - pre_maximal_horizontal_pane_position = pos; - } } done = (Pane) (done | Horizontal); - + } else if (which == static_cast (&editor_summary_pane)) { if (done & Vertical) { @@ -3652,12 +3690,12 @@ Editor::pane_allocation_handler (Allocation &alloc, Paned* which) pos = (int) floor (alloc.get_height() * 0.90f); snprintf (buf, sizeof(buf), "%d", pos); } else { + pos = atoi (prop->value()); } if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) { editor_summary_pane.set_position (pos); - pre_maximal_vertical_pane_position = pos; } done = (Pane) (done | Vertical); @@ -3667,7 +3705,9 @@ Editor::pane_allocation_handler (Allocation &alloc, Paned* which) void Editor::detach_tearoff (Box* /*b*/, Window* /*w*/) { - if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) { + if ((_tools_tearoff->torn_off() || !_tools_tearoff->visible()) && + (_mouse_mode_tearoff->torn_off() || !_mouse_mode_tearoff->visible()) && + (_zoom_tearoff->torn_off() || !_zoom_tearoff->visible())) { top_hbox.remove (toolbar_frame); } } @@ -3687,9 +3727,10 @@ Editor::set_show_measures (bool yn) hide_measures (); if ((_show_measures = yn) == true) { - if (tempo_lines) + if (tempo_lines) { tempo_lines->show(); - draw_measures (); + } + (void) redraw_measures (); } instant_save (); } @@ -3743,51 +3784,6 @@ Editor::set_stationary_playhead (bool yn) } } -void -Editor::toggle_xfade_active (boost::weak_ptr wxfade) -{ - boost::shared_ptr xfade (wxfade.lock()); - if (xfade) { - xfade->set_active (!xfade->active()); - } -} - -void -Editor::toggle_xfade_length (boost::weak_ptr wxfade) -{ - boost::shared_ptr xfade (wxfade.lock()); - if (xfade) { - xfade->set_follow_overlap (!xfade->following_overlap()); - } -} - -void -Editor::edit_xfade (boost::weak_ptr wxfade) -{ - boost::shared_ptr xfade (wxfade.lock()); - - if (!xfade) { - return; - } - - CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0); - - ensure_float (cew); - - switch (cew.run ()) { - case RESPONSE_ACCEPT: - break; - default: - return; - } - - cew.apply (); - PropertyChange all_crossfade_properties; - all_crossfade_properties.add (ARDOUR::Properties::active); - all_crossfade_properties.add (ARDOUR::Properties::follow_overlap); - xfade->PropertyChanged (all_crossfade_properties); -} - PlaylistSelector& Editor::playlist_selector () const { @@ -3804,6 +3800,12 @@ Editor::get_grid_type_as_beats (bool& success, framepos_t position) 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; @@ -3852,7 +3854,7 @@ Editor::get_grid_type_as_beats (bool& success, framepos_t position) case SnapToBar: if (_session) { - return _session->tempo_map().meter_at (position).beats_per_bar(); + return _session->tempo_map().meter_at (position).divisions_per_bar(); } break; @@ -3879,7 +3881,7 @@ Editor::get_nudge_distance (framepos_t pos, framecnt_t& next) { framecnt_t ret; - ret = nudge_clock.current_duration (pos); + ret = nudge_clock->current_duration (pos); next = ret + 1; /* XXXX fix me */ return ret; @@ -3890,8 +3892,8 @@ Editor::playlist_deletion_dialog (boost::shared_ptr pl) { ArdourDialog dialog (_("Playlist Deletion")); 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."), + "If it is kept, its audio files will not be cleaned.\n" + "If it is deleted, audio files used by it alone will be cleaned."), pl->name())); dialog.set_position (WIN_POS_CENTER); @@ -3899,8 +3901,8 @@ Editor::playlist_deletion_dialog (boost::shared_ptr pl) label.show (); - dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT); - dialog.add_button (_("Keep playlist"), RESPONSE_REJECT); + dialog.add_button (_("Delete Playlist"), RESPONSE_ACCEPT); + dialog.add_button (_("Keep Playlist"), RESPONSE_REJECT); dialog.add_button (_("Cancel"), RESPONSE_CANCEL); switch (dialog.run ()) { @@ -3999,81 +4001,41 @@ Editor::control_layout_scroll (GdkEventScroll* ev) void Editor::session_state_saved (string) { - update_title (); + update_title (); _snapshots->redisplay (); } void -Editor::maximise_editing_space () +Editor::update_tearoff_visibility() { - _mouse_mode_tearoff->set_visible (false); - _tools_tearoff->set_visible (false); - _zoom_tearoff->set_visible (false); - - pre_maximal_horizontal_pane_position = edit_pane.get_position (); - pre_maximal_vertical_pane_position = editor_summary_pane.get_position (); - pre_maximal_editor_width = this->get_width (); - pre_maximal_editor_height = this->get_height (); + bool visible = Config->get_keep_tearoffs(); + _mouse_mode_tearoff->set_visible (visible); + _tools_tearoff->set_visible (visible); + _zoom_tearoff->set_visible (visible); +} - if (post_maximal_horizontal_pane_position == 0) { - post_maximal_horizontal_pane_position = edit_pane.get_width(); +void +Editor::maximise_editing_space () +{ + if (_maximised) { + return; } - if (post_maximal_vertical_pane_position == 0) { - post_maximal_vertical_pane_position = editor_summary_pane.get_height(); - } - fullscreen (); - if (post_maximal_editor_width) { - edit_pane.set_position (post_maximal_horizontal_pane_position - - abs(post_maximal_editor_width - pre_maximal_editor_width)); - } else { - edit_pane.set_position (post_maximal_horizontal_pane_position); - } - - if (post_maximal_editor_height) { - editor_summary_pane.set_position (post_maximal_vertical_pane_position - - abs(post_maximal_editor_height - pre_maximal_editor_height)); - } else { - editor_summary_pane.set_position (post_maximal_vertical_pane_position); - } - - if (Config->get_keep_tearoffs()) { - _mouse_mode_tearoff->set_visible (true); - _tools_tearoff->set_visible (true); - if (Config->get_show_zoom_tools ()) { - _zoom_tearoff->set_visible (true); - } - } - + _maximised = true; } void Editor::restore_editing_space () { - // user changed width/height of panes during fullscreen - - if (post_maximal_horizontal_pane_position != edit_pane.get_position()) { - post_maximal_horizontal_pane_position = edit_pane.get_position(); + if (!_maximised) { + return; } - if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) { - post_maximal_vertical_pane_position = editor_summary_pane.get_position(); - } - unfullscreen(); - _mouse_mode_tearoff->set_visible (true); - _tools_tearoff->set_visible (true); - if (Config->get_show_zoom_tools ()) { - _zoom_tearoff->set_visible (true); - } - post_maximal_editor_width = this->get_width(); - post_maximal_editor_height = this->get_height(); - - edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width)); - editor_summary_pane.set_position (pre_maximal_vertical_pane_position + abs(this->get_height() - pre_maximal_editor_height)); + _maximised = false; } /** @@ -4088,7 +4050,7 @@ Editor::new_playlists (TimeAxisView* v) begin_reversible_command (_("new playlists")); vector > playlists; _session->playlists->get (playlists); - mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id); + mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::select.property_id); commit_reversible_command (); } @@ -4104,7 +4066,7 @@ Editor::copy_playlists (TimeAxisView* v) begin_reversible_command (_("copy playlists")); vector > playlists; _session->playlists->get (playlists); - mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id); + mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::select.property_id); commit_reversible_command (); } @@ -4119,7 +4081,7 @@ Editor::clear_playlists (TimeAxisView* v) begin_reversible_command (_("clear playlists")); vector > playlists; _session->playlists->get (playlists); - mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id); + mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::select.property_id); commit_reversible_command (); } @@ -4160,19 +4122,31 @@ Editor::on_key_release_event (GdkEventKey* ev) void Editor::reset_x_origin (framepos_t frame) { - queue_visual_change (frame); + pending_visual_change.add (VisualChange::TimeOrigin); + pending_visual_change.time_origin = frame; + ensure_visual_change_idle_handler (); } void Editor::reset_y_origin (double y) { - queue_visual_change_y (y); + pending_visual_change.add (VisualChange::YOrigin); + pending_visual_change.y_origin = y; + ensure_visual_change_idle_handler (); } void -Editor::reset_zoom (double fpu) +Editor::reset_zoom (double fpp) { - queue_visual_change (fpu); + clamp_frames_per_pixel (fpp); + + if (fpp == frames_per_pixel) { + return; + } + + pending_visual_change.add (VisualChange::ZoomLevel); + pending_visual_change.frames_per_pixel = fpp; + ensure_visual_change_idle_handler (); } void @@ -4186,19 +4160,27 @@ Editor::reposition_and_zoom (framepos_t frame, double fpu) } } +Editor::VisualState::VisualState (bool with_tracks) + : gui_state (with_tracks ? new GUIObjectState : 0) +{ +} + +Editor::VisualState::~VisualState () +{ + delete gui_state; +} + Editor::VisualState* Editor::current_visual_state (bool with_tracks) { - VisualState* vs = new VisualState; + VisualState* vs = new VisualState (with_tracks); vs->y_position = vertical_adjustment.get_value(); - vs->frames_per_unit = frames_per_unit; + vs->frames_per_pixel = frames_per_pixel; vs->leftmost_frame = leftmost_frame; vs->zoom_focus = zoom_focus; - if (with_tracks) { - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - vs->track_states.push_back (TAVState ((*i), &(*i)->get_state())); - } + if (with_tracks) { + *vs->gui_state = *ARDOUR_UI::instance()->gui_object_state; } return vs; @@ -4211,10 +4193,12 @@ Editor::undo_visual_state () return; } - redo_visual_stack.push_back (current_visual_state()); - VisualState* vs = undo_visual_stack.back(); undo_visual_stack.pop_back(); + + + redo_visual_stack.push_back (current_visual_state (vs ? vs->gui_state != 0 : false)); + use_visual_state (*vs); } @@ -4225,10 +4209,11 @@ Editor::redo_visual_state () return; } - undo_visual_stack.push_back (current_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)); + use_visual_state (*vs); } @@ -4245,78 +4230,51 @@ Editor::swap_visual_state () void Editor::use_visual_state (VisualState& vs) { - no_save_visual = true; + PBD::Unwinder nsv (no_save_visual, true); _routes->suspend_redisplay (); vertical_adjustment.set_value (vs.y_position); set_zoom_focus (vs.zoom_focus); - reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit); - - for (list::iterator i = vs.track_states.begin(); i != vs.track_states.end(); ++i) { - TrackViewList::iterator t; - - /* check if the track still exists - it could have been deleted */ - - if ((t = find (track_views.begin(), track_views.end(), i->first)) != track_views.end()) { - (*t)->set_state (*(i->second), Stateful::loading_state_version); + reposition_and_zoom (vs.leftmost_frame, vs.frames_per_pixel); + + if (vs.gui_state) { + *ARDOUR_UI::instance()->gui_object_state = *vs.gui_state; + + for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { + (*i)->reset_visual_state (); } } - - if (!vs.track_states.empty()) { - _routes->update_visibility (); - } - + _routes->update_visibility (); _routes->resume_redisplay (); - - no_save_visual = false; } +/** 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. + * @param fpu New frames per unit; should already have been clamped so that it is sensible. + */ void -Editor::set_frames_per_unit (double fpu) +Editor::set_frames_per_pixel (double fpp) { - /* 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 (tempo_lines) { + tempo_lines->tempo_map_changed(); } - if (fpu < 2.0) { - fpu = 2.0; - } + frames_per_pixel = fpp; + /* convert fpu to frame count */ - /* don't allow zooms that fit more than the maximum number - of frames into an 800 pixel wide space. - */ + framepos_t frames = (framepos_t) floor (frames_per_pixel * _visible_canvas_width); - if (max_framepos / fpu < 800.0) { - return; + if (frames_per_pixel != zoom_range_clock->current_duration()) { + zoom_range_clock->set (frames); } - if (tempo_lines) - tempo_lines->tempo_map_changed(); - - frames_per_unit = fpu; - post_zoom (); -} - -void -Editor::post_zoom () -{ - // convert fpu to frame count - - framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width); + bool const showing_time_selection = selection->time.length() > 0; - if (frames_per_unit != zoom_range_clock.current_duration()) { - zoom_range_clock.set (frames); - } - - if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) { + if (showing_time_selection && selection->time.start () != selection->time.end_frame ()) { for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) { (*i)->reshow_selection (selection->time); } @@ -4327,7 +4285,7 @@ Editor::post_zoom () //reset_scrolling_region (); if (playhead_cursor) { - playhead_cursor->set_position (playhead_cursor->current_frame); + playhead_cursor->set_position (playhead_cursor->current_frame ()); } refresh_location_display(); @@ -4338,37 +4296,27 @@ Editor::post_zoom () instant_save (); } +#ifdef WITH_VIDEOTIMELINE void -Editor::queue_visual_change (framepos_t where) -{ - pending_visual_change.add (VisualChange::TimeOrigin); - pending_visual_change.time_origin = where; - ensure_visual_change_idle_handler (); -} - -void -Editor::queue_visual_change (double fpu) +Editor::queue_visual_videotimeline_update () { - pending_visual_change.add (VisualChange::ZoomLevel); - pending_visual_change.frames_per_unit = fpu; - - ensure_visual_change_idle_handler (); -} - -void -Editor::queue_visual_change_y (double y) -{ - pending_visual_change.add (VisualChange::YOrigin); - pending_visual_change.y_origin = y; - + /* TODO: + * pending_visual_change.add (VisualChange::VideoTimeline); + * or maybe even more specific: which videotimeline-image + * currently it calls update_video_timeline() to update + * _all outdated_ images on the video-timeline. + * see 'exposeimg()' in video_image_frame.cc + */ ensure_visual_change_idle_handler (); } +#endif 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); + pending_visual_change.being_handled = false; } } @@ -4381,34 +4329,49 @@ Editor::_idle_visual_changer (void* arg) int Editor::idle_visual_changer () { + /* set_horizontal_position() below (and maybe other calls) call + gtk_main_iteration(), so it's possible that a signal will be handled + half-way through this method. If this signal wants an + idle_visual_changer we must schedule another one after this one, so + mark the idle_handler_id as -1 here to allow that. Also make a note + that we are doing the visual change, so that changes in response to + super-rapid-screen-update can be dropped if we are still processing + the last one. + */ + + pending_visual_change.idle_handler_id = -1; + pending_visual_change.being_handled = true; + VisualChange::Type p = pending_visual_change.pending; pending_visual_change.pending = (VisualChange::Type) 0; double const last_time_origin = horizontal_position (); - if (p & VisualChange::TimeOrigin) { - /* This is a bit of a hack, but set_frames_per_unit - below will (if called) end up with the - CrossfadeViews looking at Editor::leftmost_frame, - and if we're changing origin and zoom in the same - operation it will be the wrong value unless we - update it here. - */ + if (p & VisualChange::ZoomLevel) { + set_frames_per_pixel (pending_visual_change.frames_per_pixel); + + compute_fixed_ruler_scale (); - leftmost_frame = pending_visual_change.time_origin; + ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_begin; + ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_end; + + compute_current_bbt_points (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames(), + current_bbt_points_begin, current_bbt_points_end); + compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames(), + current_bbt_points_begin, current_bbt_points_end); + update_tempo_based_rulers (current_bbt_points_begin, current_bbt_points_end); } +#ifdef WITH_VIDEOTIMELINE if (p & VisualChange::ZoomLevel) { - set_frames_per_unit (pending_visual_change.frames_per_unit); - - compute_fixed_ruler_scale (); - compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames()); - compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames()); - update_tempo_based_rulers (); + update_video_timeline(); } +#endif + if (p & VisualChange::TimeOrigin) { - set_horizontal_position (pending_visual_change.time_origin / frames_per_unit); + set_horizontal_position (pending_visual_change.time_origin / frames_per_pixel); } + if (p & VisualChange::YOrigin) { vertical_adjustment.set_value (pending_visual_change.y_origin); } @@ -4418,10 +4381,15 @@ Editor::idle_visual_changer () update_fixed_rulers (); redisplay_tempo (true); } +#ifdef WITH_VIDEOTIMELINE + if (!(p & VisualChange::ZoomLevel)) { + update_video_timeline(); + } +#endif _summary->set_overlays_dirty (); - pending_visual_change.idle_handler_id = -1; + pending_visual_change.being_handled = false; return 0; /* this is always a one-shot call */ } @@ -4432,24 +4400,23 @@ struct EditorOrderTimeAxisSorter { }; void -Editor::sort_track_selection (TrackViewList* sel) +Editor::sort_track_selection (TrackViewList& sel) { EditorOrderTimeAxisSorter cmp; - - if (sel) { - sel->sort (cmp); - } else { - selection->tracks.sort (cmp); - } + sel.sort (cmp); } framepos_t -Editor::get_preferred_edit_position (bool ignore_playhead) +Editor::get_preferred_edit_position (bool ignore_playhead, bool from_context_menu) { bool ignored; framepos_t where = 0; EditPoint ep = _edit_point; + if (from_context_menu && (ep == EditAtMouse)) { + return event_frame (&context_click_event, 0, 0); + } + if (entered_marker) { DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position())); return entered_marker->position(); @@ -4532,7 +4499,7 @@ Editor::set_punch_range (framepos_t start, framepos_t end, string cmd) Location* tpl; if ((tpl = transport_punch_location()) == 0) { - Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoPunch); + Location* loc = new Location (*_session, start, end, _("Punch"), Location::IsAutoPunch); XMLNode &before = _session->locations()->get_state(); _session->locations()->add (loc, true); _session->set_auto_loop_location (loc); @@ -4576,17 +4543,15 @@ Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewLi if ((tr = rtv->track()) && ((pl = tr->playlist()))) { - Playlist::RegionList* regions = pl->regions_at ( + boost::shared_ptr regions = pl->regions_at ( (framepos_t) floor ( (double) where * tr->speed())); - for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) { + for (RegionList::iterator i = regions->begin(); i != regions->end(); ++i) { RegionView* rv = rtv->view()->find_view (*i); if (rv) { rs.add (rv); } } - - delete regions; } } } @@ -4611,19 +4576,17 @@ Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackVie if ((tr = rtv->track()) && ((pl = tr->playlist()))) { - Playlist::RegionList* regions = pl->regions_touched ( + boost::shared_ptr regions = pl->regions_touched ( (framepos_t) floor ( (double)where * tr->speed()), max_framepos); - for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) { + for (RegionList::iterator i = regions->begin(); i != regions->end(); ++i) { RegionView* rv = rtv->view()->find_view (*i); if (rv) { - rs.push_back (rv); + rs.add (rv); } } - - delete regions; } } } @@ -4637,7 +4600,7 @@ Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackVie RegionSelection Editor::get_regions_from_selection () { - return get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id); + return get_equivalent_regions (selection->regions, ARDOUR::Properties::select.property_id); } /** Get regions using the following method: @@ -4646,16 +4609,15 @@ Editor::get_regions_from_selection () * the edit point is `mouse' and the mouse is over an unselected * region. In this case, start with just that region. * - * Then, make an initial track list of the tracks that these - * regions are on, and if the edit point is not `mouse', add the - * selected tracks. + * Then, add equivalent regions in active edit groups to the region list. * - * Look at this track list and add any other tracks that are on the - * same active edit-enabled route group as one of the initial tracks. + * Then, search the list of selected tracks to find any selected tracks which + * do not contain regions already in the region list. If there are no selected + * tracks and 'No Selection = All Tracks' is active, search all tracks rather + * than just the selected. * - * Finally take the initial region list and add any regions that are - * under the edit point on one of the tracks on the track list to get - * the returned region list. + * Add any regions that are under the edit point on these tracks to get the + * returned region list. * * The rationale here is that the mouse edit point is special in that * its position describes both a time and a track; the other edit @@ -4670,7 +4632,7 @@ RegionSelection Editor::get_regions_from_selection_and_edit_point () { RegionSelection regions; - + if (_edit_point == EditAtMouse && entered_regionview && !selection->regions.contains (entered_regionview)) { regions.add (entered_regionview); } else { @@ -4683,22 +4645,40 @@ Editor::get_regions_from_selection_and_edit_point () tracks = selection->tracks; } - /* Add any other tracks that have regions that are in the same + /* Add any other regions that are in the same edit-activated route group as one of our regions. */ - for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) { - - RouteGroup* g = (*i)->get_time_axis_view().route_group (); - - if (g && g->is_active() && g->is_edit()) { - tracks.add (axis_views_from_routes (g->route_list())); - } + regions = get_equivalent_regions (regions, ARDOUR::Properties::select.property_id); + framepos_t const where = get_preferred_edit_position (); + + if (_route_groups->all_group_active_button().get_active() && tracks.empty()) { + /* tracks is empty (no track selected), and 'No Selection = All Tracks' + * is enabled, so consider all tracks + */ + tracks = track_views; } - + if (!tracks.empty()) { - /* now find regions that are at the edit position on those tracks */ - framepos_t const where = get_preferred_edit_position (); - get_regions_at (regions, where, tracks); + /* now search the selected tracks for tracks which don't + already contain regions to be acted upon, and get regions at + the edit point on those tracks too. + */ + TrackViewList tracks_without_relevant_regions; + + for (TrackViewList::iterator t = tracks.begin (); t != tracks.end (); ++t) { + if (!regions.involves (**t)) { + /* there are no equivalent regions on this track */ + tracks_without_relevant_regions.push_back (*t); + } + } + + if (!tracks_without_relevant_regions.empty()) { + /* there are some selected tracks with neither selected + * regions or their equivalents: act upon all regions in + * those tracks + */ + get_regions_at (regions, where, tracks_without_relevant_regions); + } } return regions; @@ -4713,16 +4693,16 @@ RegionSelection Editor::get_regions_from_selection_and_entered () { RegionSelection regions = selection->regions; - + if (regions.empty() && entered_regionview) { regions.add (entered_regionview); } - return get_equivalent_regions (regions, ARDOUR::Properties::edit.property_id); + return get_equivalent_regions (regions, ARDOUR::Properties::select.property_id); } void -Editor::get_regions_corresponding_to (boost::shared_ptr region, vector& regions) +Editor::get_regions_corresponding_to (boost::shared_ptr region, vector& regions, bool src_comparison) { for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { @@ -4741,7 +4721,11 @@ Editor::get_regions_corresponding_to (boost::shared_ptr region, vectorplaylist())) != 0) { - pl->get_region_list_equivalent_regions (region, results); + if (src_comparison) { + pl->get_source_equivalent_regions (region, results); + } else { + pl->get_region_list_equivalent_regions (region, results); + } } for (vector >::iterator ir = results.begin(); ir != results.end(); ++ir) { @@ -4770,13 +4754,13 @@ void Editor::first_idle () { MessageDialog* dialog = 0; - + if (track_views.size() > 1) { - dialog = new MessageDialog (*this, - string_compose (_("Please wait while %1 loads visual data"), PROGRAM_NAME), - true, - Gtk::MESSAGE_INFO, - Gtk::BUTTONS_NONE); + dialog = new MessageDialog ( + *this, + string_compose (_("Please wait while %1 loads visual data."), PROGRAM_NAME), + true + ); dialog->present (); ARDOUR_UI::instance()->flush_pending (); } @@ -4789,7 +4773,6 @@ Editor::first_idle () _routes->redisplay (); delete dialog; - _have_idled = true; } @@ -4862,9 +4845,11 @@ Editor::located () { ENSURE_GUI_THREAD (*this, &Editor::located); - playhead_cursor->set_position (_session->audible_frame ()); - if (_follow_playhead && !_pending_initial_locate) { - reset_x_origin_to_follow_playhead (); + if (_session) { + playhead_cursor->set_position (_session->audible_frame ()); + if (_follow_playhead && !_pending_initial_locate) { + reset_x_origin_to_follow_playhead (); + } } _pending_locate_request = false; @@ -4914,9 +4899,8 @@ Editor::axis_views_from_routes (boost::shared_ptr r) const return t; } - void -Editor::handle_new_route (RouteList& routes) +Editor::add_routes (RouteList& routes) { ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes) @@ -4933,9 +4917,11 @@ Editor::handle_new_route (RouteList& routes) DataType dt = route->input()->default_type(); if (dt == ARDOUR::DataType::AUDIO) { - rtv = new AudioTimeAxisView (*this, _session, route, *track_canvas); + rtv = new AudioTimeAxisView (*this, _session, *_track_canvas); + rtv->set_route (route); } else if (dt == ARDOUR::DataType::MIDI) { - rtv = new MidiTimeAxisView (*this, _session, route, *track_canvas); + rtv = new MidiTimeAxisView (*this, _session, *_track_canvas); + rtv->set_route (route); } else { throw unknown_type(); } @@ -4945,6 +4931,12 @@ Editor::handle_new_route (RouteList& routes) 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)); } @@ -4970,7 +4962,7 @@ Editor::timeaxisview_deleted (TimeAxisView *tv) ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv); RouteTimeAxisView* rtav = dynamic_cast (tv); - + _routes->route_removed (tv); if (tv == entered_track) { @@ -4998,7 +4990,7 @@ Editor::timeaxisview_deleted (TimeAxisView *tv) if (rtav) { route = rtav->route (); - } + } if (current_mixer_strip && current_mixer_strip->route() == route) { @@ -5011,18 +5003,18 @@ Editor::timeaxisview_deleted (TimeAxisView *tv) } else { next_tv = (*i); } - - + + if (next_tv) { set_selected_mixer_strip (*next_tv); } else { /* make the editor mixer strip go away setting the * button to inactive (which also unticks the menu option) */ - + ActionManager::uncheck_toggleaction ("/Editor/show-editor-mixer"); } - } + } } void @@ -5033,19 +5025,19 @@ Editor::hide_track_in_display (TimeAxisView* tv, bool apply_to_selection) TrackSelection::iterator j = i; ++j; - + hide_track_in_display (*i, false); - + i = j; } } else { RouteTimeAxisView* rtv = dynamic_cast (tv); - + if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) { // this will hide the mixer strip set_selected_mixer_strip (*tv); } - + _routes->hide_track_in_display (*tv); } } @@ -5071,7 +5063,7 @@ Editor::foreach_time_axis_view (sigc::slot theslot) /** Find a RouteTimeAxisView by the ID of its route */ RouteTimeAxisView* -Editor::get_route_view_by_route_id (PBD::ID& id) const +Editor::get_route_view_by_route_id (const PBD::ID& id) const { RouteTimeAxisView* v; @@ -5184,7 +5176,7 @@ bool Editor::scroll_press (Direction dir) { ++_scroll_callbacks; - + if (_scroll_connection.connected() && _scroll_callbacks < 5) { /* delay the first auto-repeat */ return true; @@ -5214,7 +5206,7 @@ Editor::scroll_press (Direction dir) _scroll_connection = Glib::signal_timeout().connect ( sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), dir), 100 ); - + _scroll_callbacks = 0; } @@ -5231,46 +5223,47 @@ Editor::scroll_release () void Editor::reset_x_origin_to_follow_playhead () { - framepos_t const frame = playhead_cursor->current_frame; + framepos_t const frame = playhead_cursor->current_frame (); if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) { if (_session->transport_speed() < 0) { - + if (frame > (current_page_frames() / 2)) { center_screen (frame-(current_page_frames()/2)); } else { center_screen (current_page_frames()/2); } - + } else { - + + framepos_t l = 0; + if (frame < leftmost_frame) { /* moving left */ - framepos_t l = 0; if (_session->transport_rolling()) { /* rolling; end up with the playhead at the right of the page */ l = frame - current_page_frames (); } else { - /* not rolling: end up with the playhead 3/4 of the way along the page */ - l = frame - (3 * current_page_frames() / 4); - } - - if (l < 0) { - l = 0; + /* not rolling: end up with the playhead 1/4 of the way along the page */ + l = frame - current_page_frames() / 4; } - - center_screen_internal (l + (current_page_frames() / 2), current_page_frames ()); } else { /* moving right */ if (_session->transport_rolling()) { /* rolling: end up with the playhead on the left of the page */ - center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ()); + l = frame; } else { - /* not rolling: end up with the playhead 1/4 of the way along the page */ - center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ()); + /* not rolling: end up with the playhead 3/4 of the way along the page */ + l = frame - 3 * current_page_frames() / 4; } } + + if (l < 0) { + l = 0; + } + + center_screen_internal (l + (current_page_frames() / 2), current_page_frames ()); } } } @@ -5323,32 +5316,39 @@ Editor::super_rapid_screen_update () if (!_stationary_playhead) { - if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) { + if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0 && !pending_visual_change.being_handled) { + /* We only do this if we aren't already + handling a visual change (ie if + pending_visual_change.being_handled is + false) so that these requests don't stack + up there are too many of them to handle in + time. + */ reset_x_origin_to_follow_playhead (); } } else { - + /* don't do continuous scroll till the new position is in the rightmost quarter of the editor canvas */ -#if 0 - // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code - double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit; +#if 0 + // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code + double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_pixel; if (target <= 0.0) { target = 0.0; } - if (fabs(target - current) < current_page_frames() / frames_per_unit) { + if (fabs(target - current) < current_page_frames() / frames_per_pixel) { target = (target * 0.15) + (current * 0.85); } else { /* relax */ } - + current = target; set_horizontal_position (current); #endif } - + } } @@ -5361,20 +5361,19 @@ Editor::session_going_away () _session_connections.drop_connections (); super_rapid_screen_update_connection.disconnect (); - + selection->clear (); cut_buffer->clear (); clicked_regionview = 0; clicked_axisview = 0; clicked_routeview = 0; - clicked_crossfadeview = 0; entered_regionview = 0; entered_track = 0; last_update_frame = 0; _drags->abort (); - playhead_cursor->canvas_item.hide (); + playhead_cursor->hide (); /* rip everything out of the list displays */ @@ -5401,8 +5400,8 @@ Editor::session_going_away () } track_views.clear (); - zoom_range_clock.set_session (0); - nudge_clock.set_session (0); + zoom_range_clock->set_session (0); + nudge_clock->set_session (0); editor_list_button.set_active(false); editor_list_button.set_sensitive(false); @@ -5412,9 +5411,8 @@ Editor::session_going_away () hide_measures (); clear_marker_display (); - delete current_bbt_points; - current_bbt_points = 0; - + stop_step_editing (); + /* get rid of any existing editor mixer strip */ WindowTitle title(Glib::get_application_name()); @@ -5437,10 +5435,10 @@ Editor::show_editor_list (bool yn) } void -Editor::change_region_layering_order () +Editor::change_region_layering_order (bool from_context_menu) { - framepos_t const position = get_preferred_edit_position (); - + const framepos_t position = get_preferred_edit_position (false, from_context_menu); + if (!clicked_routeview) { if (layering_order_editor) { layering_order_editor->hide (); @@ -5459,12 +5457,13 @@ Editor::change_region_layering_order () if (!pl) { return; } - + if (layering_order_editor == 0) { - layering_order_editor = new RegionLayeringOrderEditor(*this); + layering_order_editor = new RegionLayeringOrderEditor (*this); + layering_order_editor->set_position (WIN_POS_MOUSE); } - layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position); + layering_order_editor->set_context (clicked_routeview->name(), _session, clicked_routeview, pl, position); layering_order_editor->maybe_present (); } @@ -5472,26 +5471,38 @@ void Editor::update_region_layering_order_editor () { if (layering_order_editor && layering_order_editor->is_visible ()) { - change_region_layering_order (); + change_region_layering_order (true); } } void Editor::setup_fade_images () { - _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear"))); - _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut"))); - _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut"))); - _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut"))); - _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut"))); + _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadein-linear"))); + _fade_in_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadein-short-cut"))); + _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadein-slow-cut"))); + _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadein-fast-cut"))); + _fade_in_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadein-long-cut"))); + + _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadeout-linear"))); + _fade_out_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadeout-short-cut"))); + _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadeout-slow-cut"))); + _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadeout-fast-cut"))); + _fade_out_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadeout-long-cut"))); + + _xfade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadeout-linear"))); + _xfade_in_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadeout-short-cut"))); + _xfade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadeout-slow-cut"))); + _xfade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadeout-fast-cut"))); + _xfade_in_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadeout-long-cut"))); - _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear"))); - _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut"))); - _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut"))); - _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut"))); - _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut"))); -} + _xfade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadeout-linear"))); + _xfade_out_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadeout-short-cut"))); + _xfade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadeout-slow-cut"))); + _xfade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadeout-fast-cut"))); + _xfade_out_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadeout-long-cut"))); +} /** @return Gtk::manage()d menu item for a given action from `editor_actions' */ Gtk::MenuItem& @@ -5499,20 +5510,10 @@ Editor::action_menu_item (std::string const & name) { Glib::RefPtr a = editor_actions->get_action (name); assert (a); - + return *manage (a->create_menu_item ()); } -void -Editor::resize_text_widgets () -{ - set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_FUDGE+10, 15); - set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_FUDGE+10, 15); - set_size_request_to_display_given_text (snap_type_selector, snap_type_strings, COMBO_FUDGE+10, 15); - set_size_request_to_display_given_text (snap_mode_selector, snap_mode_strings, COMBO_FUDGE+10, 15); - set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_FUDGE+10, 15); -} - void Editor::add_notebook_page (string const & name, Gtk::Widget& widget) { @@ -5537,10 +5538,16 @@ Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page) /* double-click on a notebook tab shrinks or expands the notebook */ if (_notebook_shrunk) { - edit_pane.set_position (pre_maximal_horizontal_pane_position); + if (pre_notebook_shrink_pane_width) { + edit_pane.set_position (*pre_notebook_shrink_pane_width); + } _notebook_shrunk = false; } else { - pre_maximal_horizontal_pane_position = edit_pane.get_position (); + pre_notebook_shrink_pane_width = edit_pane.get_position(); + + /* this expands the LHS of the edit pane to cover the notebook + PAGE but leaves the tabs visible. + */ edit_pane.set_position (edit_pane.get_position() + page->get_width()); _notebook_shrunk = true; } @@ -5549,3 +5556,34 @@ Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page) return true; } +void +Editor::popup_control_point_context_menu (ArdourCanvas::Item* item, GdkEvent* event) +{ + using namespace Menu_Helpers; + + MenuList& items = _control_point_context_menu.items (); + items.clear (); + + items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun (*this, &Editor::edit_control_point), item))); + items.push_back (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun (*this, &Editor::remove_control_point), item))); + if (!can_remove_control_point (item)) { + items.back().set_sensitive (false); + } + + _control_point_context_menu.popup (event->button.button, event->button.time); +} + +void +Editor::shift_key_released () +{ + _stepping_axis_view = 0; +} + + +void +Editor::save_canvas_state () +{ + XMLTree* tree = static_cast(_track_canvas)->get_state (); + string path = string_compose ("%1/canvas-state.xml", _session->path()); + tree->write (path); +}