X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Feditor.cc;h=6514f4665aee8d7e005cc3bd69227142cad5797f;hb=ff2d51ddd8288ec967efab2cb8192f43c893909e;hp=f7bcc4d6dcf542a6da119c943693a05779d10064;hpb=838a37a4fe0fa8bb39ba7de5cddf646db3728865;p=ardour.git diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index f7bcc4d6dc..6514f4665a 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2000-2006 Paul Davis + Copyright (C) 2000-2007 Paul Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -15,29 +15,36 @@ along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - $Id$ */ +/* Note: public Editor methods are documented in public_editor.h */ + #include #include #include #include #include +#include + #include #include #include -#include +#include #include +#include #include #include #include +#include #include #include #include +#include +#include #include #include @@ -45,16 +52,18 @@ #include #include #include -#include +#include #include +#include +#include #include #include +#include #include #include "ardour_ui.h" #include "editor.h" -#include "grouped_buttons.h" #include "keyboard.h" #include "marker.h" #include "playlist_selector.h" @@ -69,10 +78,13 @@ #include "editing.h" #include "public_editor.h" #include "crossfade_edit.h" -#include "audio_time_axis.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" #ifdef FFT_ANALYSIS #include "analysis_window.h" @@ -80,9 +92,9 @@ #include "i18n.h" -/* */ +#ifdef WITH_CMT #include "imageframe_socket_handler.h" -/* */ +#endif using namespace std; using namespace sigc; @@ -93,6 +105,7 @@ using namespace Glib; using namespace Gtkmm2ext; using namespace Editing; +using PBD::internationalize; using PBD::atoi; const double Editor::timebar_height = 15.0; @@ -100,7 +113,6 @@ const double Editor::timebar_height = 15.0; #include "editor_xpms" static const gchar *_snap_type_strings[] = { - N_("None"), N_("CD Frames"), N_("SMPTE Frames"), N_("SMPTE Seconds"), @@ -115,7 +127,6 @@ static const gchar *_snap_type_strings[] = { N_("Beats"), N_("Bars"), N_("Marks"), - N_("Edit Cursor"), N_("Region starts"), N_("Region ends"), N_("Region syncs"), @@ -124,19 +135,40 @@ static const gchar *_snap_type_strings[] = { }; static const gchar *_snap_mode_strings[] = { - N_("Normal"), + N_("No Grid"), + N_("Grid"), N_("Magnetic"), 0 }; +static const gchar *_edit_point_strings[] = { + N_("Playhead"), + N_("Marker"), + N_("Mouse"), + 0 +}; + static const gchar *_zoom_focus_strings[] = { N_("Left"), N_("Right"), N_("Center"), N_("Playhead"), - N_("Edit Cursor"), + N_("Mouse"), + N_("Active Mark"), + 0 +}; + +#ifdef USE_RUBBERBAND +static const gchar *_rb_opt_strings[] = { + N_("Mushy"), + N_("Smooth"), + N_("Balanced multitimbral mixture"), + N_("Unpitched percussion with stable notes"), + N_("Crisp monophonic instrumental"), + N_("Unpitched solo percussion"), 0 }; +#endif /* Soundfile drag-n-drop */ @@ -144,12 +176,18 @@ Gdk::Cursor* Editor::cross_hair_cursor = 0; Gdk::Cursor* Editor::selector_cursor = 0; Gdk::Cursor* Editor::trimmer_cursor = 0; Gdk::Cursor* Editor::grabber_cursor = 0; +Gdk::Cursor* Editor::grabber_edit_point_cursor = 0; Gdk::Cursor* Editor::zoom_cursor = 0; Gdk::Cursor* Editor::time_fx_cursor = 0; Gdk::Cursor* Editor::fader_cursor = 0; Gdk::Cursor* Editor::speaker_cursor = 0; +Gdk::Cursor* Editor::midi_pencil_cursor = 0; +Gdk::Cursor* Editor::midi_select_cursor = 0; +Gdk::Cursor* Editor::midi_resize_cursor = 0; +Gdk::Cursor* Editor::midi_erase_cursor = 0; Gdk::Cursor* Editor::wait_cursor = 0; Gdk::Cursor* Editor::timebar_cursor = 0; +Gdk::Cursor* Editor::transparent_cursor = 0; void show_me_the_size (Requisition* r, const char* what) @@ -157,36 +195,30 @@ show_me_the_size (Requisition* r, const char* what) cerr << "size of " << what << " = " << r->width << " x " << r->height << endl; } -void -check_adjustment (Gtk::Adjustment* adj) -{ - cerr << "CHANGE adj = " - << adj->get_lower () << ' ' - << adj->get_upper () << ' ' - << adj->get_value () << ' ' - << adj->get_step_increment () << ' ' - << adj->get_page_increment () << ' ' - << adj->get_page_size () << ' ' - << endl; - +void +DragInfo::clear_copied_locations () +{ + for (list::iterator i = copied_locations.begin(); i != copied_locations.end(); ++i) { + delete *i; + } + copied_locations.clear (); } -Editor::Editor (AudioEngine& eng) - : engine (eng), - +Editor::Editor () + : /* time display buttons */ minsec_label (_("Mins:Secs")), bbt_label (_("Bars:Beats")), smpte_label (_("Timecode")), - frame_label (_("Frames")), + frame_label (_("Samples")), tempo_label (_("Tempo")), meter_label (_("Meter")), mark_label (_("Location Markers")), range_mark_label (_("Range Markers")), transport_mark_label (_("Loop/Punch Ranges")), - - edit_packer (3, 3, false), + cd_mark_label (_("CD Markers")), + edit_packer (3, 4, true), /* the values here don't matter: layout widgets reset them as needed. @@ -197,7 +229,7 @@ Editor::Editor (AudioEngine& eng) /* tool bar related */ - edit_cursor_clock (X_("editcursor"), false, X_("EditCursorClock"), true), + edit_point_clock (X_("editpoint"), false, X_("EditPointClock"), true), zoom_range_clock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, true), toolbar_selection_clock_table (2,3), @@ -205,13 +237,14 @@ Editor::Editor (AudioEngine& eng) automation_mode_button (_("mode")), global_automation_button (_("automation")), - /* */ +#ifdef WITH_CMT image_socket_listener(0), - /* */ +#endif /* nudge */ - nudge_clock (X_("nudge"), false, X_("NudgeClock"), true, true) + nudge_clock (X_("nudge"), false, X_("NudgeClock"), true, true), + meters_running(false) { constructed = false; @@ -221,41 +254,45 @@ Editor::Editor (AudioEngine& eng) PublicEditor::_instance = this; session = 0; + _have_idled = false; - selection = new Selection; - cut_buffer = new Selection; + selection = new Selection (this); + cut_buffer = new Selection (this); selection->TimeChanged.connect (mem_fun(*this, &Editor::time_selection_changed)); selection->TracksChanged.connect (mem_fun(*this, &Editor::track_selection_changed)); selection->RegionsChanged.connect (mem_fun(*this, &Editor::region_selection_changed)); selection->PointsChanged.connect (mem_fun(*this, &Editor::point_selection_changed)); + selection->MarkersChanged.connect (mem_fun(*this, &Editor::marker_selection_changed)); clicked_regionview = 0; - clicked_trackview = 0; - clicked_audio_trackview = 0; + clicked_axisview = 0; + clicked_routeview = 0; clicked_crossfadeview = 0; clicked_control_point = 0; - latest_regionview = 0; last_update_frame = 0; drag_info.item = 0; current_mixer_strip = 0; current_bbt_points = 0; - - snap_type_strings = I18N (_snap_type_strings); - snap_mode_strings = I18N (_snap_mode_strings); - zoom_focus_strings = I18N(_zoom_focus_strings); - - snap_type = SnapToFrame; - set_snap_to (snap_type); - snap_mode = SnapNormal; - set_snap_mode (snap_mode); + + snap_type_strings = I18N (_snap_type_strings); + snap_mode_strings = I18N (_snap_mode_strings); + zoom_focus_strings = I18N (_zoom_focus_strings); + edit_point_strings = I18N (_edit_point_strings); +#ifdef USE_RUBBERBAND + rb_opt_strings = I18N (_rb_opt_strings); +#endif + snap_threshold = 5.0; bbt_beat_subdivision = 4; canvas_width = 0; canvas_height = 0; + last_autoscroll_x = 0; + last_autoscroll_y = 0; autoscroll_active = false; autoscroll_timeout_tag = -1; interthread_progress_window = 0; + logo_item = 0; #ifdef FFT_ANALYSIS analysis_window = 0; @@ -266,15 +303,16 @@ Editor::Editor (AudioEngine& eng) _show_waveforms = true; _show_waveforms_recording = true; first_action_message = 0; - export_dialog = 0; show_gain_after_trim = false; ignore_route_list_reorder = false; no_route_list_redisplay = false; verbose_cursor_on = true; route_removal = false; - track_spacing = 0; show_automatic_regions_in_region_list = true; - region_list_sort_type = (Editing::RegionListSortType) 0; + last_item_entered = 0; + last_item_entered_n = 0; + + region_list_sort_type = (Editing::RegionListSortType) 0; have_pending_keyboard_selection = false; _follow_playhead = true; _xfade_visibility = true; @@ -292,34 +330,53 @@ Editor::Editor (AudioEngine& eng) new_transport_marker_menu = 0; editor_mixer_strip_width = Wide; show_editor_mixer_when_tracks_arrive = false; + region_edit_menu_split_multichannel_item = 0; region_edit_menu_split_item = 0; temp_location = 0; - region_edit_menu_split_multichannel_item = 0; leftmost_frame = 0; ignore_mouse_mode_toggle = false; + ignore_midi_edit_mode_toggle = false; current_stepping_trackview = 0; entered_track = 0; entered_regionview = 0; + entered_marker = 0; clear_entered_track = false; _new_regionviews_show_envelope = false; - current_timestretch = 0; + current_timefx = 0; in_edit_group_row_change = false; last_canvas_frame = 0; - edit_cursor = 0; playhead_cursor = 0; button_release_can_deselect = true; - canvas_idle_queued = false; _dragging_playhead = false; - - location_marker_color = color_map[cLocationMarker]; - location_range_color = color_map[cLocationRange]; - location_cd_marker_color = color_map[cLocationCDMarker]; - location_loop_color = color_map[cLocationLoop]; - location_punch_color = color_map[cLocationPunch]; + _dragging_edit_point = false; + _dragging_hscrollbar = false; + select_new_marker = false; + zoomed_to_region = false; + rhythm_ferret = 0; + allow_vertical_scroll = false; + no_save_visual = false; + need_resize_line = false; + resize_line_y = 0; + old_resize_line_y = -1; + no_region_list_redisplay = false; + resize_idle_id = -1; + + _scrubbing = false; + scrubbing_direction = 0; + + sfbrowser = 0; + ignore_route_order_sync = false; + + location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get(); + location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get(); + location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get(); + location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get(); + location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get(); range_marker_drag_rect = 0; marker_drag_line = 0; - + tempo_map_change_idle_handler_id = -1; + set_midi_edit_mode (MidiEditPencil, true); set_mouse_mode (MouseObject, true); frames_per_unit = 2048; /* too early to use reset_zoom () */ @@ -333,16 +390,10 @@ Editor::Editor (AudioEngine& eng) initialize_canvas (); edit_controls_vbox.set_spacing (0); - horizontal_adjustment.signal_value_changed().connect (mem_fun(*this, &Editor::canvas_horizontally_scrolled)); - vertical_adjustment.signal_value_changed().connect (mem_fun(*this, &Editor::tie_vertical_scrolling)); - - track_canvas.set_hadjustment (horizontal_adjustment); - track_canvas.set_vadjustment (vertical_adjustment); - time_canvas.set_hadjustment (horizontal_adjustment); + horizontal_adjustment.signal_value_changed().connect (mem_fun(*this, &Editor::scroll_canvas_horizontally), false); + vertical_adjustment.signal_value_changed().connect (mem_fun(*this, &Editor::tie_vertical_scrolling), true); + track_canvas->signal_map_event().connect (mem_fun (*this, &Editor::track_canvas_map_handler)); - track_canvas.signal_map_event().connect (mem_fun (*this, &Editor::track_canvas_map_handler)); - time_canvas.signal_map_event().connect (mem_fun (*this, &Editor::time_canvas_map_handler)); - controls_layout.add (edit_controls_vbox); controls_layout.set_name ("EditControlsBase"); controls_layout.add_events (Gdk::SCROLL_MASK); @@ -355,24 +406,32 @@ Editor::Editor (AudioEngine& eng) edit_vscrollbar.set_adjustment (vertical_adjustment); edit_hscrollbar.set_adjustment (horizontal_adjustment); - edit_hscrollbar.signal_button_press_event().connect (mem_fun(*this, &Editor::hscrollbar_button_press)); - edit_hscrollbar.signal_button_release_event().connect (mem_fun(*this, &Editor::hscrollbar_button_release)); + edit_hscrollbar.signal_button_press_event().connect (mem_fun(*this, &Editor::hscrollbar_button_press), false); + edit_hscrollbar.signal_button_release_event().connect (mem_fun(*this, &Editor::hscrollbar_button_release), false); edit_hscrollbar.signal_size_allocate().connect (mem_fun(*this, &Editor::hscrollbar_allocate)); edit_hscrollbar.set_name ("EditorHScrollbar"); build_cursors (); setup_toolbar (); + setup_midi_toolbar (); - edit_cursor_clock.ValueChanged.connect (mem_fun(*this, &Editor::edit_cursor_clock_changed)); + edit_point_clock.ValueChanged.connect (mem_fun(*this, &Editor::edit_point_clock_changed)); + 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; + pad_line_1->show(); + time_pad->show(); + + time_canvas_vbox.pack_start (*_ruler_separator, false, false); time_canvas_vbox.pack_start (*minsec_ruler, false, false); time_canvas_vbox.pack_start (*smpte_ruler, false, false); time_canvas_vbox.pack_start (*frames_ruler, false, false); time_canvas_vbox.pack_start (*bbt_ruler, false, false); - time_canvas_vbox.pack_start (time_canvas, true, true); - time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars)); - + //time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2); + time_canvas_vbox.set_size_request (-1, -1); bbt_label.set_name ("EditorTimeButton"); bbt_label.set_size_request (-1, (int)timebar_height); bbt_label.set_alignment (1.0, 0.5); @@ -389,6 +448,7 @@ Editor::Editor (AudioEngine& eng) frame_label.set_size_request (-1, (int)timebar_height); frame_label.set_alignment (1.0, 0.5); frame_label.set_padding (5,0); + tempo_label.set_name ("EditorTimeButton"); tempo_label.set_size_request (-1, (int)timebar_height); tempo_label.set_alignment (1.0, 0.5); @@ -401,6 +461,10 @@ Editor::Editor (AudioEngine& eng) mark_label.set_size_request (-1, (int)timebar_height); mark_label.set_alignment (1.0, 0.5); mark_label.set_padding (5,0); + cd_mark_label.set_name ("EditorTimeButton"); + 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); range_mark_label.set_name ("EditorTimeButton"); range_mark_label.set_size_request (-1, (int)timebar_height); range_mark_label.set_alignment (1.0, 0.5); @@ -409,26 +473,31 @@ Editor::Editor (AudioEngine& eng) 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); + + ruler_label_vbox.pack_start (minsec_label, false, false); + ruler_label_vbox.pack_start (smpte_label, false, false); + ruler_label_vbox.pack_start (frame_label, false, false); + ruler_label_vbox.pack_start (bbt_label, false, false); - time_button_vbox.pack_start (minsec_label, false, false); - time_button_vbox.pack_start (smpte_label, false, false); - time_button_vbox.pack_start (frame_label, false, false); - time_button_vbox.pack_start (bbt_label, false, false); + ruler_label_event_box.add (ruler_label_vbox); + ruler_label_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK); + ruler_label_event_box.set_name ("TimebarLabelBase"); + ruler_label_event_box.signal_button_release_event().connect (mem_fun(*this, &Editor::ruler_label_button_release)); + time_button_vbox.pack_start (meter_label, false, false); time_button_vbox.pack_start (tempo_label, false, false); time_button_vbox.pack_start (mark_label, false, false); 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.set_name ("TimebarLabelBase"); + time_button_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK); time_button_event_box.signal_button_release_event().connect (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); 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); @@ -438,17 +507,36 @@ Editor::Editor (AudioEngine& eng) edit_packer.set_homogeneous (false); edit_packer.set_border_width (0); edit_packer.set_name ("EditorWindow"); + +#ifndef THE_OLD_WAY + + edit_packer.attach (ruler_label_event_box, 0, 1, 0, 1, FILL, SHRINK, 0, 0); + + edit_packer.attach (time_button_event_box, 0, 1, 1, 2, FILL, SHRINK, 0, 0); + edit_packer.attach (time_canvas_event_box, 1, 2, 0, 1, FILL|EXPAND, FILL, 0, 0); + + edit_packer.attach (controls_layout, 0, 1, 2, 3, FILL, FILL|EXPAND, 0, 0); + edit_packer.attach (track_canvas_event_box, 1, 2, 1, 3, FILL|EXPAND, FILL|EXPAND, 0, 0); + + edit_packer.attach (zoom_box, 0, 1, 3, 4, FILL, FILL, 0, 0); + edit_packer.attach (edit_hscrollbar, 1, 2, 3, 4, FILL|EXPAND, FILL, 0, 0); + + edit_packer.attach (edit_vscrollbar, 3, 4, 2, 3, FILL, FILL|EXPAND, 0, 0); + +#else - edit_packer.attach (edit_vscrollbar, 0, 1, 1, 3, FILL, FILL|EXPAND, 0, 0); + edit_packer.attach (edit_vscrollbar, 0, 1, 0, 4, FILL, FILL|EXPAND, 0, 0); - edit_packer.attach (time_button_event_box, 1, 2, 0, 1, FILL, FILL, 0, 0); + edit_packer.attach (ruler_label_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0); + edit_packer.attach (time_button_event_box, 1, 2, 1, 2, FILL, SHRINK, 0, 0); edit_packer.attach (time_canvas_event_box, 2, 3, 0, 1, FILL|EXPAND, FILL, 0, 0); - edit_packer.attach (controls_layout, 1, 2, 1, 2, FILL, FILL|EXPAND, 0, 0); - edit_packer.attach (track_canvas_event_box, 2, 3, 1, 2, FILL|EXPAND, FILL|EXPAND, 0, 0); + edit_packer.attach (controls_layout, 1, 2, 2, 3, FILL, FILL|EXPAND, 0, 0); + edit_packer.attach (track_canvas_event_box, 2, 3, 1, 3, FILL|EXPAND, FILL|EXPAND, 0, 0); - edit_packer.attach (zoom_box, 1, 2, 2, 3, FILL, FILL, 0, 0); - edit_packer.attach (edit_hscrollbar, 2, 3, 2, 3, FILL|EXPAND, FILL, 0, 0); + edit_packer.attach (zoom_box, 1, 2, 3, 4, FILL, FILL, 0, 0); + edit_packer.attach (edit_hscrollbar, 2, 3, 3, 4, FILL|EXPAND, FILL, 0, 0); +#endif bottom_hbox.set_border_width (2); bottom_hbox.set_spacing (3); @@ -461,14 +549,15 @@ Editor::Editor (AudioEngine& eng) route_list_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1)); route_list_display.set_headers_visible (true); route_list_display.set_name ("TrackListDisplay"); - route_list_display.get_selection()->set_mode (SELECTION_NONE); + route_list_display.get_selection()->set_mode (SELECTION_SINGLE); route_list_display.set_reorderable (true); route_list_display.set_size_request (100,-1); + route_list_display.add_object_drag (route_display_columns.route.index(), "routes"); CellRendererToggle* route_list_visible_cell = dynamic_cast(route_list_display.get_column_cell_renderer (0)); route_list_visible_cell->property_activatable() = true; route_list_visible_cell->property_radio() = false; - + route_display_model->signal_row_deleted().connect (mem_fun (*this, &Editor::route_list_delete)); route_display_model->signal_row_changed().connect (mem_fun (*this, &Editor::route_list_change)); @@ -510,7 +599,7 @@ Editor::Editor (AudioEngine& eng) edit_group_display.set_name ("EditGroupList"); edit_group_display.get_selection()->set_mode (SELECTION_SINGLE); - edit_group_display.set_headers_visible (false); + edit_group_display.set_headers_visible (true); edit_group_display.set_reorderable (false); edit_group_display.set_rules_hint (true); edit_group_display.set_size_request (75, -1); @@ -548,6 +637,10 @@ Editor::Editor (AudioEngine& eng) region_list_display.set_size_request (100, -1); region_list_display.set_name ("RegionListDisplay"); + /* Try to prevent single mouse presses from initiating edits. + This relies on a hack in gtktreeview.c:gtk_treeview_button_press() + */ + region_list_display.set_data ("mouse-edits-require-mod1", (gpointer) 0x1); region_list_model = TreeStore::create (region_list_columns); region_list_model->set_sort_func (0, mem_fun (*this, &Editor::region_list_sorter)); @@ -555,7 +648,16 @@ Editor::Editor (AudioEngine& eng) region_list_display.set_model (region_list_model); region_list_display.append_column (_("Regions"), region_list_columns.name); - region_list_display.set_headers_visible (false); + region_list_display.append_column (_("Start"), region_list_columns.start); + region_list_display.append_column (_("End"), region_list_columns.end); + region_list_display.append_column (_("Length"), region_list_columns.length); + region_list_display.append_column (_("Used"), region_list_columns.used); + region_list_display.append_column (_("Path to parent file"), region_list_columns.path); + region_list_display.set_headers_visible (true); + + CellRendererText* region_name_cell = dynamic_cast(region_list_display.get_column_cell_renderer (0)); + region_name_cell->property_editable() = true; + region_name_cell->signal_edited().connect (mem_fun (*this, &Editor::region_name_edit)); region_list_display.get_selection()->set_select_function (mem_fun (*this, &Editor::region_list_selection_filter)); @@ -566,7 +668,7 @@ Editor::Editor (AudioEngine& eng) region_list_display.get_selection()->set_mode (SELECTION_MULTIPLE); region_list_display.add_object_drag (region_list_columns.region.index(), "regions"); - + /* setup DnD handling */ list region_list_target_table; @@ -579,8 +681,8 @@ Editor::Editor (AudioEngine& eng) region_list_display.signal_drag_data_received().connect (mem_fun(*this, &Editor::region_list_display_drag_data_received)); region_list_scroller.add (region_list_display); - region_list_scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC); - + region_list_scroller.set_policy (POLICY_AUTOMATIC, POLICY_AUTOMATIC); + region_list_display.signal_key_press_event().connect (mem_fun(*this, &Editor::region_list_display_key_press)); region_list_display.signal_key_release_event().connect (mem_fun(*this, &Editor::region_list_display_key_release)); region_list_display.signal_button_press_event().connect (mem_fun(*this, &Editor::region_list_display_button_press), false); @@ -588,6 +690,9 @@ Editor::Editor (AudioEngine& eng) region_list_display.get_selection()->signal_changed().connect (mem_fun(*this, &Editor::region_list_selection_changed)); // region_list_display.signal_popup_menu().connect (bind (mem_fun (*this, &Editor::show_region_list_display_context_menu), 1, 0)); + ARDOUR_UI::instance()->secondary_clock.mode_changed.connect (mem_fun(*this, &Editor::redisplay_regions)); + ARDOUR::Region::RegionPropertyChanged.connect (mem_fun(*this, &Editor::update_region_row)); + named_selection_scroller.add (named_selection_display); named_selection_scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC); @@ -623,24 +728,28 @@ Editor::Editor (AudioEngine& eng) nlabel = manage (new Label (_("Regions"))); nlabel->set_angle (-90); - the_notebook.append_page (region_list_scroller, *nlabel); + the_notebook.append_page (region_list_scroller, *nlabel); nlabel = manage (new Label (_("Tracks/Busses"))); nlabel->set_angle (-90); - the_notebook.append_page (route_list_scroller, *nlabel); + the_notebook.append_page (route_list_scroller, *nlabel); nlabel = manage (new Label (_("Snapshots"))); nlabel->set_angle (-90); the_notebook.append_page (snapshot_display_scroller, *nlabel); nlabel = manage (new Label (_("Edit Groups"))); nlabel->set_angle (-90); the_notebook.append_page (*edit_group_display_packer, *nlabel); - nlabel = manage (new Label (_("Chunks"))); - nlabel->set_angle (-90); - the_notebook.append_page (named_selection_scroller, *nlabel); + + if (!Profile->get_sae()) { + nlabel = manage (new Label (_("Chunks"))); + nlabel->set_angle (-90); + the_notebook.append_page (named_selection_scroller, *nlabel); + } the_notebook.set_show_tabs (true); the_notebook.set_scrollable (true); the_notebook.popup_enable (); the_notebook.set_tab_pos (Gtk::POS_RIGHT); + the_notebook.show_all (); post_maximal_editor_width = 0; post_maximal_pane_position = 0; @@ -649,7 +758,8 @@ Editor::Editor (AudioEngine& eng) edit_pane.signal_size_allocate().connect (bind (mem_fun(*this, &Editor::pane_allocation_handler), static_cast (&edit_pane))); - top_hbox.pack_start (toolbar_frame, true, true); + top_hbox.pack_start (toolbar_frame, false, true); + top_hbox.pack_start (midi_toolbar_frame, false, true); HBox *hbox = manage (new HBox); hbox->pack_start (edit_pane, true, true); @@ -662,12 +772,21 @@ Editor::Editor (AudioEngine& eng) set_name ("EditorWindow"); add_accel_group (ActionManager::ui_manager->get_accel_group()); + status_bar_hpacker.show (); + + vpacker.pack_end (status_bar_hpacker, false, false); vpacker.pack_end (global_hpacker, true, true); /* register actions now so that set_state() can find them and set toggles/checks etc */ register_actions (); - + + snap_type = SnapToBeat; + set_snap_to (snap_type); + snap_mode = SnapOff; + set_snap_mode (snap_mode); + set_edit_point_preference (EditAtMouse, true); + XMLNode* node = ARDOUR_UI::instance()->editor_settings(); set_state (*node); @@ -710,7 +829,10 @@ Editor::Editor (AudioEngine& eng) set_icon_list (window_icons); set_default_icon_list (window_icons); } - set_title (_("ardour: editor")); + + WindowTitle title(Glib::get_application_name()); + title += _("Editor"); + set_title (title.get_string()); set_wmclass (X_("ardour_editor"), "Ardour"); add (vpacker); @@ -725,8 +847,10 @@ Editor::Editor (AudioEngine& eng) ControlProtocol::ZoomIn.connect (bind (mem_fun (*this, &Editor::temporal_zoom_step), false)); ControlProtocol::ZoomOut.connect (bind (mem_fun (*this, &Editor::temporal_zoom_step), true)); ControlProtocol::ScrollTimeline.connect (mem_fun (*this, &Editor::control_scroll)); + BasicUI::AccessAction.connect (mem_fun (*this, &Editor::access_action)); Config->ParameterChanged.connect (mem_fun (*this, &Editor::parameter_changed)); + Route::SyncOrderKeys.connect (mem_fun (*this, &Editor::sync_order_keys)); constructed = true; instant_save (); @@ -734,9 +858,8 @@ Editor::Editor (AudioEngine& eng) Editor::~Editor() { - /* */ - if(image_socket_listener) - { +#ifdef WITH_CMT + if(image_socket_listener) { if(image_socket_listener->is_connected()) { image_socket_listener->close_connection() ; @@ -745,7 +868,12 @@ Editor::~Editor() delete image_socket_listener ; image_socket_listener = 0 ; } - /* */ +#endif + + if (track_canvas) { + delete track_canvas; + track_canvas = 0; + } } void @@ -802,8 +930,10 @@ Editor::set_entered_track (TimeAxisView* tav) void Editor::show_window () { - show_all (); - present (); + show_all_children (); + + /* re-hide editor list if necessary */ + editor_list_button_toggled (); /* now reset all audio_time_axis heights, because widgets might need to be re-hidden @@ -815,15 +945,8 @@ Editor::show_window () tv = (static_cast(*i)); tv->reset_height (); } -} -void -Editor::tie_vertical_scrolling () -{ - double y1 = vertical_adjustment.get_value(); - controls_layout.get_vadjustment()->set_value (y1); - playhead_cursor->set_y_axis(y1); - edit_cursor->set_y_axis(y1); + present (); } void @@ -834,20 +957,32 @@ Editor::instant_save () } if (session) { - session->add_instant_xml(get_state(), session->path()); + session->add_instant_xml(get_state()); } else { - Config->add_instant_xml(get_state(), get_user_ardour_path()); + Config->add_instant_xml(get_state()); } } void -Editor::edit_cursor_clock_changed() +Editor::edit_point_clock_changed() { - if (edit_cursor->current_frame != edit_cursor_clock.current_time()) { - edit_cursor->set_position (edit_cursor_clock.current_time()); + if (_dragging_edit_point) { + return; + } + + if (selection->markers.empty()) { + return; + } + + bool ignored; + Location* loc = find_location_from_marker (selection->markers.front(), ignored); + + if (!loc) { + return; } -} + loc->move_to (edit_point_clock.current_time()); +} void Editor::zoom_adjustment_changed () @@ -860,10 +995,10 @@ Editor::zoom_adjustment_changed () if (fpu < 1.0) { fpu = 1.0; - zoom_range_clock.set ((nframes_t) floor (fpu * canvas_width)); + zoom_range_clock.set ((nframes64_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 ((nframes_t) floor (fpu * canvas_width)); + zoom_range_clock.set ((nframes64_t) floor (fpu * canvas_width)); } temporal_zoom (fpu); @@ -879,43 +1014,89 @@ Editor::control_scroll (float fraction) } double step = fraction * current_page_frames(); - nframes_t target; - if ((fraction < 0.0f) && (session->transport_frame() < (nframes_t) fabs(step))) { - target = 0; - } else if ((fraction > 0.0f) && (max_frames - session->transport_frame() < step)) { - target = (max_frames - (current_page_frames()*2)); // allow room for slop in where the PH is on the screen + /* + _control_scroll_target is an optional + + it acts like a pointer to an nframes64_t, with + a operator conversion to boolean to check + that it has a value could possibly use + playhead_cursor->current_frame to store the + value and a boolean in the class to know + when it's out of date + */ + + if (!_control_scroll_target) { + _control_scroll_target = session->transport_frame(); + _dragging_playhead = true; + } + + if ((fraction < 0.0f) && (*_control_scroll_target < (nframes64_t) fabs(step))) { + *_control_scroll_target = 0; + } else if ((fraction > 0.0f) && (max_frames - *_control_scroll_target < step)) { + *_control_scroll_target = max_frames - (current_page_frames()*2); // allow room for slop in where the PH is on the screen } else { - target = (session->transport_frame() + (nframes_t) floor ((fraction * current_page_frames()))); + *_control_scroll_target += (nframes64_t) floor (step); } /* move visuals, we'll catch up with it later */ - playhead_cursor->set_position (target); - - if (target > (current_page_frames() / 2)) { + playhead_cursor->set_position (*_control_scroll_target); + UpdateAllTransportClocks (*_control_scroll_target); + + if (*_control_scroll_target > (current_page_frames() / 2)) { /* try to center PH in window */ - reset_x_origin (target - (current_page_frames()/2)); + reset_x_origin (*_control_scroll_target - (current_page_frames()/2)); } else { reset_x_origin (0); } - /* cancel the existing */ + /* + Now we do a timeout to actually bring the session to the right place + according to the playhead. This is to avoid reading disk buffers on every + call to control_scroll, which is driven by ScrollTimeline and therefore + probably by a control surface wheel which can generate lots of events. + */ + /* cancel the existing timeout */ control_scroll_connection.disconnect (); - /* add the next one */ + /* add the next timeout */ - control_scroll_connection = Glib::signal_timeout().connect (bind (mem_fun (*this, &Editor::deferred_control_scroll), target), 50); + control_scroll_connection = Glib::signal_timeout().connect (bind (mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250); } bool -Editor::deferred_control_scroll (nframes_t target) +Editor::deferred_control_scroll (nframes64_t target) { - session->request_locate (target); + session->request_locate (*_control_scroll_target, session->transport_rolling()); + // reset for next stream + _control_scroll_target = boost::none; + _dragging_playhead = false; return false; } +void +Editor::access_action (std::string action_group, std::string action_item) +{ + if (!session) { + return; + } + + ENSURE_GUI_THREAD (bind (mem_fun (*this, &Editor::access_action), action_group, action_item)); + + cout<< "OSC: Recieved: "<< action_item << endl; + + RefPtr act; + act = ActionManager::get_action( action_group.c_str(), action_item.c_str() ); + + if (act) { + act->activate(); + } + + +} + void Editor::on_realize () { @@ -928,6 +1109,7 @@ Editor::start_scrolling () { scroll_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun(*this, &Editor::update_current_screen)); + } void @@ -937,7 +1119,7 @@ Editor::stop_scrolling () } void -Editor::map_position_change (nframes_t frame) +Editor::map_position_change (nframes64_t frame) { ENSURE_GUI_THREAD (bind (mem_fun(*this, &Editor::map_position_change), frame)); @@ -950,7 +1132,7 @@ Editor::map_position_change (nframes_t frame) } void -Editor::center_screen (nframes_t frame) +Editor::center_screen (nframes64_t frame) { double page = canvas_width * frames_per_unit; @@ -963,12 +1145,12 @@ Editor::center_screen (nframes_t frame) } void -Editor::center_screen_internal (nframes_t frame, float page) +Editor::center_screen_internal (nframes64_t frame, float page) { page /= 2; if (frame > page) { - frame -= (nframes_t) page; + frame -= (nframes64_t) page; } else { frame = 0; } @@ -981,14 +1163,16 @@ Editor::handle_new_duration () { ENSURE_GUI_THREAD (mem_fun (*this, &Editor::handle_new_duration)); - nframes_t new_end = session->get_maximum_extent() + (nframes_t) floorf (current_page_frames() * 0.10f); + nframes64_t new_end = session->get_maximum_extent() + (nframes64_t) floorf (current_page_frames() * 0.10f); if (new_end > last_canvas_frame) { last_canvas_frame = new_end; - reset_scrolling_region (); + horizontal_adjustment.set_upper (last_canvas_frame / frames_per_unit); + //reset_scrolling_region (); } horizontal_adjustment.set_value (leftmost_frame/frames_per_unit); + //cerr << "Editor::handle_new_duration () called ha v:l:u:ps:lcf = " << horizontal_adjustment.get_value() << ":" << horizontal_adjustment.get_lower() << ":" << horizontal_adjustment.get_upper() << ":" << horizontal_adjustment.get_page_size() << ":" << last_canvas_frame << endl;//DEBUG } void @@ -1007,24 +1191,21 @@ Editor::update_title () if (session) { bool dirty = session->dirty(); - string wintitle = _("ardour: editor: "); - - if (dirty) { - wintitle += '['; - } - - wintitle += session->name(); + string session_name; if (session->snap_name() != session->name()) { - wintitle += ':'; - wintitle += session->snap_name(); + session_name = session->snap_name(); + } else { + session_name = session->name(); } if (dirty) { - wintitle += ']'; + session_name = "*" + session_name; } - set_title (wintitle); + WindowTitle title(session_name); + title += Glib::get_application_name(); + set_title (title.get_string()); } } @@ -1033,6 +1214,10 @@ Editor::connect_to_session (Session *t) { session = t; + /* there are never any selected regions at startup */ + + sensitize_the_right_region_actions (false); + XMLNode* node = ARDOUR_UI::instance()->editor_settings(); set_state (*node); @@ -1057,8 +1242,8 @@ Editor::connect_to_session (Session *t) session_connections.push_back (session->TransportStateChange.connect (mem_fun(*this, &Editor::map_transport_state))); session_connections.push_back (session->PositionChanged.connect (mem_fun(*this, &Editor::map_position_change))); session_connections.push_back (session->RouteAdded.connect (mem_fun(*this, &Editor::handle_new_route))); - session_connections.push_back (session->AudioRegionAdded.connect (mem_fun(*this, &Editor::handle_new_audio_region))); - session_connections.push_back (session->AudioRegionRemoved.connect (mem_fun(*this, &Editor::handle_audio_region_removed))); + session_connections.push_back (session->RegionsAdded.connect (mem_fun(*this, &Editor::handle_new_regions))); + session_connections.push_back (session->RegionRemoved.connect (mem_fun(*this, &Editor::handle_region_removed))); session_connections.push_back (session->DurationChanged.connect (mem_fun(*this, &Editor::handle_new_duration))); session_connections.push_back (session->edit_group_added.connect (mem_fun(*this, &Editor::add_edit_group))); session_connections.push_back (session->edit_group_removed.connect (mem_fun(*this, &Editor::edit_groups_changed))); @@ -1075,10 +1260,17 @@ Editor::connect_to_session (Session *t) edit_groups_changed (); - edit_cursor_clock.set_session (session); + edit_point_clock.set_session (session); zoom_range_clock.set_session (session); _playlist_selector->set_session (session); nudge_clock.set_session (session); + nudge_clock.set (session->frame_rate() * 5); // default of 5 seconds + + playhead_cursor->canvas_item.show (); + + if (rhythm_ferret) { + rhythm_ferret->set_session (session); + } #ifdef FFT_ANALYSIS if (analysis_window != 0) @@ -1093,8 +1285,7 @@ Editor::connect_to_session (Session *t) } session->locations()->add (loc, false); session->set_auto_loop_location (loc); - } - else { + } else { // force name loc->set_name (_("Loop")); } @@ -1107,8 +1298,7 @@ Editor::connect_to_session (Session *t) } session->locations()->add (loc, false); session->set_auto_punch_location (loc); - } - else { + } else { // force name loc->set_name (_("Punch")); } @@ -1124,6 +1314,10 @@ Editor::connect_to_session (Session *t) session->locations()->StateChanged.connect (mem_fun(*this, &Editor::refresh_location_display_s)); session->locations()->end_location()->changed.connect (mem_fun(*this, &Editor::end_location_changed)); + if (sfbrowser) { + sfbrowser->set_session (session); + } + handle_new_duration (); redisplay_regions (); @@ -1146,17 +1340,17 @@ Editor::connect_to_session (Session *t) if (ARDOUR_UI::instance()->session_is_new ()) { - TreeModel::Children rows = route_display_model->children(); + TreeModel::Children rows = route_display_model->children(); TreeModel::Children::iterator i; no_route_list_redisplay = true; for (i = rows.begin(); i != rows.end(); ++i) { TimeAxisView *tv = (*i)[route_display_columns.tv]; - AudioTimeAxisView *atv; + RouteTimeAxisView *rtv; - if ((atv = dynamic_cast(tv)) != 0) { - if (atv->route()->master()) { + if ((rtv = dynamic_cast(tv)) != 0) { + if (rtv->route()->is_master()) { route_list_display.get_selection()->unselect (i); } } @@ -1165,10 +1359,23 @@ Editor::connect_to_session (Session *t) no_route_list_redisplay = false; redisplay_route_list (); } + + switch (snap_type) { + case SnapToRegionStart: + case SnapToRegionEnd: + case SnapToRegionSync: + case SnapToRegionBoundary: + build_region_boundary_cache (); + break; + + default: + break; + } - /* register for undo history */ + /* register for undo history */ + session->register_with_memento_command_factory(_id, this); - session->register_with_memento_command_factory(_id, this); + start_updating (); } void @@ -1203,16 +1410,44 @@ Editor::build_cursors () mask = Bitmap::create (speaker_cursor_mask_bits, speaker_cursor_width, speaker_cursor_height); speaker_cursor = new Gdk::Cursor (source, mask, ffg, fbg, speaker_cursor_x_hot, speaker_cursor_y_hot); } + + { + RefPtr bits; + char pix[4] = { 0, 0, 0, 0 }; + bits = Bitmap::create (pix, 2, 2); + Gdk::Color c; + transparent_cursor = new Gdk::Cursor (bits, bits, c, c, 0, 0); + } + + { + RefPtr bits; + char pix[4] = { 0, 0, 0, 0 }; + bits = Bitmap::create (pix, 2, 2); + Gdk::Color c; + transparent_cursor = new Gdk::Cursor (bits, bits, c, c, 0, 0); + } + grabber_cursor = new Gdk::Cursor (HAND2); + + { + Glib::RefPtr grabber_edit_point_pixbuf (::get_icon ("grabber_edit_point")); + grabber_edit_point_cursor = new Gdk::Cursor (Gdk::Display::get_default(), grabber_edit_point_pixbuf, 5, 17); + } + cross_hair_cursor = new Gdk::Cursor (CROSSHAIR); trimmer_cursor = new Gdk::Cursor (SB_H_DOUBLE_ARROW); selector_cursor = new Gdk::Cursor (XTERM); time_fx_cursor = new Gdk::Cursor (SIZING); wait_cursor = new Gdk::Cursor (WATCH); timebar_cursor = new Gdk::Cursor(LEFT_PTR); + midi_pencil_cursor = new Gdk::Cursor (PENCIL); + midi_select_cursor = new Gdk::Cursor (CENTER_PTR); + midi_resize_cursor = new Gdk::Cursor (SIZING); + midi_erase_cursor = new Gdk::Cursor (DRAPED_BOX); } +/** 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) { @@ -1232,35 +1467,46 @@ Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* i case FadeInItem: case FadeInHandleItem: if (arv->audio_region()->fade_in_active()) { - items.push_back (MenuElem (_("Deactivate"), bind (mem_fun (*arv, &AudioRegionView::set_fade_in_active), false))); + items.push_back (MenuElem (_("Deactivate"), bind (mem_fun (*this, &Editor::set_fade_in_active), false))); } else { - items.push_back (MenuElem (_("Activate"), bind (mem_fun (*arv, &AudioRegionView::set_fade_in_active), true))); + items.push_back (MenuElem (_("Activate"), bind (mem_fun (*this, &Editor::set_fade_in_active), true))); } items.push_back (SeparatorElem()); - - items.push_back (MenuElem (_("Linear"), bind (mem_fun (*arv, &AudioRegionView::set_fade_in_shape), AudioRegion::Linear))); - items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*arv, &AudioRegionView::set_fade_in_shape), AudioRegion::LogB))); - items.push_back (MenuElem (_("Slow"), bind (mem_fun (*arv, &AudioRegionView::set_fade_in_shape), AudioRegion::Fast))); - items.push_back (MenuElem (_("Fast"), bind (mem_fun (*arv, &AudioRegionView::set_fade_in_shape), AudioRegion::LogA))); - items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*arv, &AudioRegionView::set_fade_in_shape), AudioRegion::Slow))); + + if (Profile->get_sae()) { + items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear))); + items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast))); + } else { + items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear))); + items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast))); + items.push_back (MenuElem (_("Slow"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogB))); + items.push_back (MenuElem (_("Fast"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogA))); + items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Slow))); + } + break; case FadeOutItem: case FadeOutHandleItem: if (arv->audio_region()->fade_out_active()) { - items.push_back (MenuElem (_("Deactivate"), bind (mem_fun (*arv, &AudioRegionView::set_fade_out_active), false))); + items.push_back (MenuElem (_("Deactivate"), bind (mem_fun (*this, &Editor::set_fade_out_active), false))); } else { - items.push_back (MenuElem (_("Activate"), bind (mem_fun (*arv, &AudioRegionView::set_fade_out_active), true))); + items.push_back (MenuElem (_("Activate"), bind (mem_fun (*this, &Editor::set_fade_out_active), true))); } items.push_back (SeparatorElem()); - items.push_back (MenuElem (_("Linear"), bind (mem_fun (*arv, &AudioRegionView::set_fade_out_shape), AudioRegion::Linear))); - items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*arv, &AudioRegionView::set_fade_out_shape), AudioRegion::Fast))); - items.push_back (MenuElem (_("Slow"), bind (mem_fun (*arv, &AudioRegionView::set_fade_out_shape), AudioRegion::LogB))); - items.push_back (MenuElem (_("Fast"), bind (mem_fun (*arv, &AudioRegionView::set_fade_out_shape), AudioRegion::LogA))); - items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*arv, &AudioRegionView::set_fade_out_shape), AudioRegion::Slow))); + if (Profile->get_sae()) { + items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear))); + items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow))); + } else { + items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear))); + items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow))); + items.push_back (MenuElem (_("Slow"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogA))); + items.push_back (MenuElem (_("Fast"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogB))); + items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Fast))); + } break; @@ -1275,10 +1521,10 @@ Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* i } void -Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection, nframes_t frame) +Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection, nframes64_t frame) { using namespace Menu_Helpers; - Menu* (Editor::*build_menu_function)(nframes_t); + Menu* (Editor::*build_menu_function)(nframes64_t); Menu *menu; switch (item_type) { @@ -1305,7 +1551,7 @@ Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, break; case StreamItem: - if (clicked_audio_trackview->get_diskstream()) { + if (clicked_routeview->get_diskstream()) { build_menu_function = &Editor::build_track_context_menu; } else { build_menu_function = &Editor::build_track_bus_context_menu; @@ -1328,10 +1574,10 @@ Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, case RegionViewNameHighlight: if (!with_selection) { if (region_edit_menu_split_item) { - if (clicked_regionview && clicked_regionview->region()->covers (edit_cursor->current_frame)) { - ActionManager::set_sensitive (ActionManager::edit_cursor_in_region_sensitive_actions, true); + if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) { + ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true); } else { - ActionManager::set_sensitive (ActionManager::edit_cursor_in_region_sensitive_actions, false); + ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false); } } /* @@ -1361,7 +1607,7 @@ Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, return; } - if (clicked_audio_trackview && clicked_audio_trackview->audio_track()) { + if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) { /* Bounce to disk */ @@ -1370,7 +1616,7 @@ Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, edit_items.push_back (SeparatorElem()); - switch (clicked_audio_trackview->audio_track()->freeze_state()) { + switch (clicked_routeview->audio_track()->freeze_state()) { case AudioTrack::NoFreeze: edit_items.push_back (MenuElem (_("Freeze"), mem_fun(*this, &Editor::freeze_route))); break; @@ -1386,11 +1632,15 @@ Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, } + if (item_type == StreamItem && clicked_routeview) { + clicked_routeview->build_underlay_menu(menu); + } + menu->popup (button, time); } Menu* -Editor::build_track_context_menu (nframes_t ignored) +Editor::build_track_context_menu (nframes64_t ignored) { using namespace Menu_Helpers; @@ -1402,7 +1652,7 @@ Editor::build_track_context_menu (nframes_t ignored) } Menu* -Editor::build_track_bus_context_menu (nframes_t ignored) +Editor::build_track_bus_context_menu (nframes64_t ignored) { using namespace Menu_Helpers; @@ -1414,23 +1664,33 @@ Editor::build_track_bus_context_menu (nframes_t ignored) } Menu* -Editor::build_track_region_context_menu (nframes_t frame) +Editor::build_track_region_context_menu (nframes64_t frame) { using namespace Menu_Helpers; MenuList& edit_items = track_region_context_menu.items(); edit_items.clear(); - AudioTimeAxisView* atv = dynamic_cast (clicked_trackview); + RouteTimeAxisView* rtv = dynamic_cast (clicked_axisview); - if (atv) { + if (rtv) { boost::shared_ptr ds; boost::shared_ptr pl; - if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()))) { - Playlist::RegionList* regions = pl->regions_at ((nframes_t) floor ( (double)frame * ds->speed())); - for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) { - add_region_context_items (atv->audio_view(), (*i), edit_items); + if ((ds = rtv->get_diskstream()) && ((pl = ds->playlist()))) { + Playlist::RegionList* regions = pl->regions_at ((nframes64_t) floor ( (double)frame * ds->speed())); + + if (selection->regions.size() > 1) { + // there's already a multiple selection: just add a + // single region context menu that will act on all + // selected regions + boost::shared_ptr dummy_region; // = NULL + add_region_context_items (rtv->view(), dummy_region, edit_items); + } else { + for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) { + add_region_context_items (rtv->view(), (*i), edit_items); + } } + delete regions; } } @@ -1441,13 +1701,13 @@ Editor::build_track_region_context_menu (nframes_t frame) } Menu* -Editor::build_track_crossfade_context_menu (nframes_t frame) +Editor::build_track_crossfade_context_menu (nframes64_t frame) { using namespace Menu_Helpers; MenuList& edit_items = track_crossfade_context_menu.items(); edit_items.clear (); - AudioTimeAxisView* atv = dynamic_cast (clicked_trackview); + AudioTimeAxisView* atv = dynamic_cast (clicked_axisview); if (atv) { boost::shared_ptr ds; @@ -1467,10 +1727,17 @@ Editor::build_track_crossfade_context_menu (nframes_t frame) add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many); } - for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) { - add_region_context_items (atv->audio_view(), (*i), edit_items); + if (selection->regions.size() > 1) { + // there's already a multiple selection: just add a + // single region context menu that will act on all + // selected regions + boost::shared_ptr dummy_region; // = NULL + add_region_context_items (atv->audio_view(), dummy_region, edit_items); + } else { + for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) { + add_region_context_items (atv->audio_view(), (*i), edit_items); + } } - delete regions; } } @@ -1518,21 +1785,23 @@ Editor::analyze_range_selection() } #endif /* FFT_ANALYSIS */ - - Menu* -Editor::build_track_selection_context_menu (nframes_t ignored) +Editor::build_track_selection_context_menu (nframes64_t ignored) { using namespace Menu_Helpers; MenuList& edit_items = track_selection_context_menu.items(); edit_items.clear (); add_selection_context_items (edit_items); - add_dstream_context_items (edit_items); + // edit_items.push_back (SeparatorElem()); + // add_dstream_context_items (edit_items); return &track_selection_context_menu; } +/** 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) { @@ -1590,8 +1859,22 @@ Editor::xfade_edit_right_region () } } +/** Add an element to a menu, settings its sensitivity. + * @param m Menu to add to. + * @param e Element to add. + * @param s true to make sensitive, false to make insensitive + */ +void +Editor::add_item_with_sensitivity (Menu_Helpers::MenuList& m, Menu_Helpers::MenuElem e, bool s) const +{ + m.push_back (e); + if (!s) { + m.back().set_sensitive (false); + } +} + void -Editor::add_region_context_items (AudioStreamView* sv, boost::shared_ptr region, Menu_Helpers::MenuList& edit_items) +Editor::add_region_context_items (StreamView* sv, boost::shared_ptr region, Menu_Helpers::MenuList& edit_items) { using namespace Menu_Helpers; Menu *region_menu = manage (new Menu); @@ -1599,95 +1882,147 @@ Editor::add_region_context_items (AudioStreamView* sv, boost::shared_ptr region_menu->set_name ("ArdourContextMenu"); boost::shared_ptr ar; + boost::shared_ptr mr; if (region) { ar = boost::dynamic_pointer_cast (region); - } + mr = boost::dynamic_pointer_cast (region); - /* when this particular menu pops up, make the relevant region - become selected. - */ + /* when this particular menu pops up, make the relevant region + become selected. + */ - // region_menu->signal_map_event().connect (bind (mem_fun(*this, &Editor::set_selected_regionview_from_map_event), sv, boost::weak_ptr(region))); + region_menu->signal_map_event().connect ( + bind (mem_fun(*this, &Editor::set_selected_regionview_from_map_event), sv, boost::weak_ptr(region))); + + items.push_back (MenuElem (_("Rename"), mem_fun(*this, &Editor::rename_region))); + items.push_back (MenuElem (_("Popup region editor"), mem_fun(*this, &Editor::edit_region))); + } - items.push_back (MenuElem (_("Popup region editor"), mem_fun(*this, &Editor::edit_region))); items.push_back (MenuElem (_("Raise to top layer"), mem_fun(*this, &Editor::raise_region_to_top))); items.push_back (MenuElem (_("Lower to bottom layer"), mem_fun (*this, &Editor::lower_region_to_bottom))); items.push_back (SeparatorElem()); - items.push_back (MenuElem (_("Define sync point"), mem_fun(*this, &Editor::set_region_sync_from_edit_cursor))); + items.push_back (MenuElem (_("Define sync point"), mem_fun(*this, &Editor::set_region_sync_from_edit_point))); items.push_back (MenuElem (_("Remove sync point"), mem_fun(*this, &Editor::remove_region_sync))); items.push_back (SeparatorElem()); - items.push_back (MenuElem (_("Audition"), mem_fun(*this, &Editor::audition_selected_region))); + items.push_back (MenuElem (_("Audition"), mem_fun(*this, &Editor::play_selected_region))); items.push_back (MenuElem (_("Export"), mem_fun(*this, &Editor::export_region))); items.push_back (MenuElem (_("Bounce"), mem_fun(*this, &Editor::bounce_region_selection))); #ifdef FFT_ANALYSIS - items.push_back (MenuElem (_("Analyze region"), mem_fun(*this, &Editor::analyze_region_selection))); + if (ar) { + items.push_back (MenuElem (_("Spectral Analysis"), mem_fun(*this, &Editor::analyze_region_selection))); + } #endif items.push_back (SeparatorElem()); - items.push_back (CheckMenuElem (_("Lock"), mem_fun(*this, &Editor::toggle_region_lock))); - region_lock_item = static_cast(&items.back()); - if (region->locked()) { + sigc::connection fooc; + boost::shared_ptr region_to_check; + + if (region) { + region_to_check = region; + } else { + region_to_check = selection->regions.front()->region(); + } + + items.push_back (CheckMenuElem (_("Lock"))); + CheckMenuItem* region_lock_item = static_cast(&items.back()); + if (region_to_check->locked()) { region_lock_item->set_active(); } - items.push_back (CheckMenuElem (_("Mute"), mem_fun(*this, &Editor::toggle_region_mute))); - region_mute_item = static_cast(&items.back()); - if (region->muted()) { + region_lock_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_lock)); + + items.push_back (CheckMenuElem (_("Glue to Bars&Beats"))); + CheckMenuItem* bbt_glue_item = static_cast(&items.back()); + + switch (region_to_check->positional_lock_style()) { + case Region::MusicTime: + bbt_glue_item->set_active (true); + break; + default: + bbt_glue_item->set_active (false); + break; + } + + bbt_glue_item->signal_activate().connect (bind (mem_fun (*this, &Editor::set_region_lock_style), Region::MusicTime)); + + items.push_back (CheckMenuElem (_("Mute"))); + CheckMenuItem* region_mute_item = static_cast(&items.back()); + fooc = region_mute_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_mute)); + if (region_to_check->muted()) { + fooc.block (true); region_mute_item->set_active(); + fooc.block (false); } - items.push_back (CheckMenuElem (_("Opaque"), mem_fun(*this, &Editor::toggle_region_opaque))); - region_opaque_item = static_cast(&items.back()); - if (region->opaque()) { - region_opaque_item->set_active(); + + if (!Profile->get_sae()) { + items.push_back (CheckMenuElem (_("Opaque"))); + CheckMenuItem* region_opaque_item = static_cast(&items.back()); + fooc = region_opaque_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_opaque)); + if (region_to_check->opaque()) { + fooc.block (true); + region_opaque_item->set_active(); + fooc.block (false); + } } - + items.push_back (CheckMenuElem (_("Original position"), mem_fun(*this, &Editor::naturalize))); - if (region->at_natural_position()) { + if (region_to_check->at_natural_position()) { items.back().set_sensitive (false); } - + items.push_back (SeparatorElem()); - + if (ar) { RegionView* rv = sv->find_view (ar); AudioRegionView* arv = dynamic_cast(rv); - - items.push_back (MenuElem (_("Reset Envelope"), mem_fun(*this, &Editor::reset_region_gain_envelopes))); - items.push_back (CheckMenuElem (_("Envelope Visible"), mem_fun(*this, &Editor::toggle_gain_envelope_visibility))); - region_envelope_visible_item = static_cast (&items.back()); - - if (arv->envelope_visible()) { - region_envelope_visible_item->set_active (true); - } - - items.push_back (CheckMenuElem (_("Envelope Active"), mem_fun(*this, &Editor::toggle_gain_envelope_active))); - region_envelope_active_item = static_cast (&items.back()); + if (!Profile->get_sae()) { + items.push_back (MenuElem (_("Reset Envelope"), mem_fun(*this, &Editor::reset_region_gain_envelopes))); + + items.push_back (CheckMenuElem (_("Envelope Visible"))); + CheckMenuItem* region_envelope_visible_item = static_cast (&items.back()); + fooc = region_envelope_visible_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_gain_envelope_visibility)); + if (arv->envelope_visible()) { + fooc.block (true); + region_envelope_visible_item->set_active (true); + fooc.block (false); + } + + items.push_back (CheckMenuElem (_("Envelope Active"))); + CheckMenuItem* region_envelope_active_item = static_cast (&items.back()); + fooc = region_envelope_active_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_gain_envelope_active)); + + if (ar->envelope_active()) { + fooc.block (true); + region_envelope_active_item->set_active (true); + fooc.block (false); + } - if (ar->envelope_active()) { - region_envelope_active_item->set_active (true); + items.push_back (SeparatorElem()); } - items.push_back (SeparatorElem()); - if (ar->scale_amplitude() != 1.0f) { items.push_back (MenuElem (_("DeNormalize"), mem_fun(*this, &Editor::denormalize_region))); } else { items.push_back (MenuElem (_("Normalize"), mem_fun(*this, &Editor::normalize_region))); } + + } else if (mr) { + items.push_back (MenuElem (_("Quantize"), mem_fun(*this, &Editor::quantize_region))); + items.push_back (SeparatorElem()); } + items.push_back (MenuElem (_("Reverse"), mem_fun(*this, &Editor::reverse_region))); items.push_back (SeparatorElem()); - /* range related stuff */ items.push_back (MenuElem (_("Add Range Markers"), mem_fun (*this, &Editor::add_location_from_audio_region))); - items.push_back (MenuElem (_("Set Range Selection"), mem_fun (*this, &Editor::set_selection_from_audio_region))); + items.push_back (MenuElem (_("Set Range Selection"), mem_fun (*this, &Editor::set_selection_from_region))); items.push_back (SeparatorElem()); /* Nudge region */ @@ -1696,8 +2031,8 @@ Editor::add_region_context_items (AudioStreamView* sv, boost::shared_ptr MenuList& nudge_items = nudge_menu->items(); nudge_menu->set_name ("ArdourContextMenu"); - nudge_items.push_back (MenuElem (_("Nudge fwd"), (bind (mem_fun(*this, &Editor::nudge_forward), false)))); - nudge_items.push_back (MenuElem (_("Nudge bwd"), (bind (mem_fun(*this, &Editor::nudge_backward), false)))); + nudge_items.push_back (MenuElem (_("Nudge fwd"), (bind (mem_fun(*this, &Editor::nudge_forward), false, false)))); + nudge_items.push_back (MenuElem (_("Nudge bwd"), (bind (mem_fun(*this, &Editor::nudge_backward), false, false)))); nudge_items.push_back (MenuElem (_("Nudge fwd by capture offset"), (mem_fun(*this, &Editor::nudge_forward_capture_offset)))); nudge_items.push_back (MenuElem (_("Nudge bwd by capture offset"), (mem_fun(*this, &Editor::nudge_backward_capture_offset)))); @@ -1708,8 +2043,10 @@ Editor::add_region_context_items (AudioStreamView* sv, boost::shared_ptr MenuList& trim_items = trim_menu->items(); trim_menu->set_name ("ArdourContextMenu"); - trim_items.push_back (MenuElem (_("Start to edit cursor"), mem_fun(*this, &Editor::trim_region_from_edit_cursor))); - trim_items.push_back (MenuElem (_("Edit cursor to end"), mem_fun(*this, &Editor::trim_region_to_edit_cursor))); + trim_items.push_back (MenuElem (_("Start to edit point"), mem_fun(*this, &Editor::trim_region_from_edit_point))); + trim_items.push_back (MenuElem (_("Edit point to end"), mem_fun(*this, &Editor::trim_region_to_edit_point))); + trim_items.push_back (MenuElem (_("Trim To Loop"), mem_fun(*this, &Editor::trim_region_to_loop))); + trim_items.push_back (MenuElem (_("Trim To Punch"), mem_fun(*this, &Editor::trim_region_to_punch))); items.push_back (MenuElem (_("Trim"), *trim_menu)); items.push_back (SeparatorElem()); @@ -1720,10 +2057,11 @@ Editor::add_region_context_items (AudioStreamView* sv, boost::shared_ptr items.push_back (MenuElem (_("Make mono regions"), (mem_fun(*this, &Editor::split_multichannel_region)))); region_edit_menu_split_multichannel_item = &items.back(); - items.push_back (MenuElem (_("Duplicate"), (bind (mem_fun(*this, &Editor::duplicate_dialog), true)))); + items.push_back (MenuElem (_("Duplicate"), (bind (mem_fun(*this, &Editor::duplicate_dialog), false)))); + items.push_back (MenuElem (_("Multi-Duplicate"), (bind (mem_fun(*this, &Editor::duplicate_dialog), true)))); items.push_back (MenuElem (_("Fill Track"), (mem_fun(*this, &Editor::region_fill_track)))); items.push_back (SeparatorElem()); - items.push_back (MenuElem (_("Remove"), mem_fun(*this, &Editor::remove_clicked_region))); + items.push_back (MenuElem (_("Remove"), mem_fun(*this, &Editor::remove_selected_regions))); /* OK, stick the region submenu at the top of the list, and then add the standard items. @@ -1734,7 +2072,7 @@ Editor::add_region_context_items (AudioStreamView* sv, boost::shared_ptr */ string::size_type pos = 0; - string menu_item_name = region->name(); + string menu_item_name = (region) ? region->name() : _("Selected regions"); while ((pos = menu_item_name.find ("_", pos)) != string::npos) { menu_item_name.replace (pos, 1, "__"); @@ -1745,6 +2083,9 @@ Editor::add_region_context_items (AudioStreamView* sv, boost::shared_ptr edit_items.push_back (SeparatorElem()); } +/** Add context menu items relevant to selection ranges. + * @param edit_items List to add the items to. + */ void Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items) { @@ -1754,36 +2095,42 @@ Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items) selection_menu->set_name ("ArdourContextMenu"); items.push_back (MenuElem (_("Play range"), mem_fun(*this, &Editor::play_selection))); - items.push_back (MenuElem (_("Loop range"), mem_fun(*this, &Editor::set_route_loop_selection))); + items.push_back (MenuElem (_("Loop range"), bind (mem_fun(*this, &Editor::set_loop_from_selection), true))); #ifdef FFT_ANALYSIS items.push_back (SeparatorElem()); - items.push_back (MenuElem (_("Analyze range"), mem_fun(*this, &Editor::analyze_range_selection))); + items.push_back (MenuElem (_("Spectral Analysis"), mem_fun(*this, &Editor::analyze_range_selection))); #endif items.push_back (SeparatorElem()); - items.push_back (MenuElem (_("Separate range to track"), mem_fun(*this, &Editor::separate_region_from_selection))); - items.push_back (MenuElem (_("Separate range to region list"), mem_fun(*this, &Editor::new_region_from_selection))); + items.push_back (MenuElem (_("Extend Range to End of Region"), bind (mem_fun(*this, &Editor::extend_selection_to_end_of_region), false))); + items.push_back (MenuElem (_("Extend Range to Start of Region"), bind (mem_fun(*this, &Editor::extend_selection_to_start_of_region), false))); + + items.push_back (SeparatorElem()); + items.push_back (MenuElem (_("Convert to region in-place"), mem_fun(*this, &Editor::separate_region_from_selection))); + items.push_back (MenuElem (_("Convert to region in region list"), mem_fun(*this, &Editor::new_region_from_selection))); items.push_back (SeparatorElem()); items.push_back (MenuElem (_("Select all in range"), mem_fun(*this, &Editor::select_all_selectables_using_time_selection))); + + items.push_back (SeparatorElem()); + items.push_back (MenuElem (_("Set loop from selection"), bind (mem_fun(*this, &Editor::set_loop_from_selection), false))); + items.push_back (MenuElem (_("Set punch from selection"), mem_fun(*this, &Editor::set_punch_from_selection))); + items.push_back (SeparatorElem()); items.push_back (MenuElem (_("Add Range Markers"), mem_fun (*this, &Editor::add_location_from_selection))); - items.push_back (MenuElem (_("Set range to loop range"), mem_fun(*this, &Editor::set_selection_from_loop))); - items.push_back (MenuElem (_("Set range to punch range"), mem_fun(*this, &Editor::set_selection_from_punch))); items.push_back (SeparatorElem()); items.push_back (MenuElem (_("Crop region to range"), mem_fun(*this, &Editor::crop_region_to_selection))); items.push_back (MenuElem (_("Fill range with region"), mem_fun(*this, &Editor::region_fill_selection))); items.push_back (MenuElem (_("Duplicate range"), bind (mem_fun(*this, &Editor::duplicate_dialog), false))); items.push_back (MenuElem (_("Create chunk from range"), mem_fun(*this, &Editor::create_named_selection))); items.push_back (SeparatorElem()); - items.push_back (MenuElem (_("Bounce range"), mem_fun(*this, &Editor::bounce_range_selection))); - items.push_back (MenuElem (_("Export range"), mem_fun(*this, &Editor::export_selection))); - - edit_items.push_back (MenuElem (_("Range"), *selection_menu)); - edit_items.push_back (SeparatorElem()); + items.push_back (MenuElem (_("Consolidate range"), bind (mem_fun(*this, &Editor::bounce_range_selection), true))); + items.push_back (MenuElem (_("Bounce range to region list"), bind (mem_fun(*this, &Editor::bounce_range_selection), false))); + items.push_back (MenuElem (_("Export range"), mem_fun(*this, &Editor::export_range))); } + void Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items) { @@ -1795,7 +2142,7 @@ Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items) MenuList& play_items = play_menu->items(); play_menu->set_name ("ArdourContextMenu"); - play_items.push_back (MenuElem (_("Play from edit cursor"), mem_fun(*this, &Editor::play_from_edit_cursor))); + play_items.push_back (MenuElem (_("Play from edit point"), mem_fun(*this, &Editor::play_from_edit_point))); play_items.push_back (MenuElem (_("Play from start"), mem_fun(*this, &Editor::play_from_start))); play_items.push_back (MenuElem (_("Play region"), mem_fun(*this, &Editor::play_selected_region))); play_items.push_back (SeparatorElem()); @@ -1817,11 +2164,14 @@ Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items) select_items.push_back (MenuElem (_("Set range to loop range"), mem_fun(*this, &Editor::set_selection_from_loop))); select_items.push_back (MenuElem (_("Set range to punch range"), mem_fun(*this, &Editor::set_selection_from_punch))); select_items.push_back (SeparatorElem()); - select_items.push_back (MenuElem (_("Select all after edit cursor"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), edit_cursor, true))); - select_items.push_back (MenuElem (_("Select all before edit cursor"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), edit_cursor, false))); - select_items.push_back (MenuElem (_("Select all after playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true))); - select_items.push_back (MenuElem (_("Select all before playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false))); - select_items.push_back (MenuElem (_("Select all between cursors"), bind (mem_fun(*this, &Editor::select_all_selectables_between_cursors), playhead_cursor, edit_cursor))); + select_items.push_back (MenuElem (_("Select All After Edit Point"), bind (mem_fun(*this, &Editor::select_all_selectables_using_edit), true))); + select_items.push_back (MenuElem (_("Select All Before Edit Point"), bind (mem_fun(*this, &Editor::select_all_selectables_using_edit), false))); + select_items.push_back (MenuElem (_("Select All After Playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true))); + select_items.push_back (MenuElem (_("Select All Before Playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false))); + select_items.push_back (MenuElem (_("Select All Between Playhead & Edit Point"), bind (mem_fun(*this, &Editor::select_all_selectables_between), false))); + select_items.push_back (MenuElem (_("Select All Within Playhead & Edit Point"), bind (mem_fun(*this, &Editor::select_all_selectables_between), true))); + select_items.push_back (MenuElem (_("Select Range Between Playhead & Edit Point"), mem_fun(*this, &Editor::select_range_between))); + select_items.push_back (SeparatorElem()); edit_items.push_back (MenuElem (_("Select"), *select_menu)); @@ -1834,8 +2184,7 @@ Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items) cutnpaste_items.push_back (MenuElem (_("Cut"), mem_fun(*this, &Editor::cut))); cutnpaste_items.push_back (MenuElem (_("Copy"), mem_fun(*this, &Editor::copy))); - cutnpaste_items.push_back (MenuElem (_("Paste at edit cursor"), bind (mem_fun(*this, &Editor::paste), 1.0f))); - cutnpaste_items.push_back (MenuElem (_("Paste at mouse"), mem_fun(*this, &Editor::mouse_paste))); + cutnpaste_items.push_back (MenuElem (_("Paste"), bind (mem_fun(*this, &Editor::paste), 1.0f))); cutnpaste_items.push_back (SeparatorElem()); @@ -1852,7 +2201,7 @@ Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items) edit_items.push_back (SeparatorElem()); edit_items.push_back (MenuElem (_("Insert Selected Region"), bind (mem_fun(*this, &Editor::insert_region_list_selection), 1.0f))); - edit_items.push_back (MenuElem (_("Insert Existing Audio"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack))); + edit_items.push_back (MenuElem (_("Insert Existing Media"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack))); /* Nudge track */ @@ -1862,9 +2211,9 @@ Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items) edit_items.push_back (SeparatorElem()); nudge_items.push_back (MenuElem (_("Nudge entire track fwd"), (bind (mem_fun(*this, &Editor::nudge_track), false, true)))); - nudge_items.push_back (MenuElem (_("Nudge track after edit cursor fwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, true)))); + nudge_items.push_back (MenuElem (_("Nudge track after edit point fwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, true)))); nudge_items.push_back (MenuElem (_("Nudge entire track bwd"), (bind (mem_fun(*this, &Editor::nudge_track), false, false)))); - nudge_items.push_back (MenuElem (_("Nudge track after edit cursor bwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, false)))); + nudge_items.push_back (MenuElem (_("Nudge track after edit point bwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, false)))); edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu)); } @@ -1880,7 +2229,7 @@ Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items) MenuList& play_items = play_menu->items(); play_menu->set_name ("ArdourContextMenu"); - play_items.push_back (MenuElem (_("Play from edit cursor"), mem_fun(*this, &Editor::play_from_edit_cursor))); + play_items.push_back (MenuElem (_("Play from edit point"), mem_fun(*this, &Editor::play_from_edit_point))); play_items.push_back (MenuElem (_("Play from start"), mem_fun(*this, &Editor::play_from_start))); edit_items.push_back (MenuElem (_("Play"), *play_menu)); @@ -1895,8 +2244,8 @@ Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items) select_items.push_back (MenuElem (_("Invert selection in track"), mem_fun(*this, &Editor::invert_selection_in_track))); select_items.push_back (MenuElem (_("Invert selection"), mem_fun(*this, &Editor::invert_selection))); select_items.push_back (SeparatorElem()); - select_items.push_back (MenuElem (_("Select all after edit cursor"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), edit_cursor, true))); - select_items.push_back (MenuElem (_("Select all before edit cursor"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), edit_cursor, false))); + select_items.push_back (MenuElem (_("Select all after edit point"), bind (mem_fun(*this, &Editor::select_all_selectables_using_edit), true))); + select_items.push_back (MenuElem (_("Select all before edit point"), bind (mem_fun(*this, &Editor::select_all_selectables_using_edit), false))); select_items.push_back (MenuElem (_("Select all after playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true))); select_items.push_back (MenuElem (_("Select all before playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false))); @@ -1918,20 +2267,26 @@ Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items) edit_items.push_back (SeparatorElem()); nudge_items.push_back (MenuElem (_("Nudge entire track fwd"), (bind (mem_fun(*this, &Editor::nudge_track), false, true)))); - nudge_items.push_back (MenuElem (_("Nudge track after edit cursor fwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, true)))); + nudge_items.push_back (MenuElem (_("Nudge track after edit point fwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, true)))); nudge_items.push_back (MenuElem (_("Nudge entire track bwd"), (bind (mem_fun(*this, &Editor::nudge_track), false, false)))); - nudge_items.push_back (MenuElem (_("Nudge track after edit cursor bwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, false)))); + nudge_items.push_back (MenuElem (_("Nudge track after edit point bwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, false)))); edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu)); } -/* CURSOR SETTING AND MARKS AND STUFF */ - void Editor::set_snap_to (SnapType st) { + unsigned int snap_ind = (unsigned int)st; + snap_type = st; - string str = snap_type_strings[(int) st]; + + if (snap_ind > snap_type_strings.size() - 1) { + snap_ind = 0; + snap_type = (SnapType)snap_ind; + } + + string str = snap_type_strings[snap_ind]; if (str != snap_type_selector.get_active_text()) { snap_type_selector.set_active_text (str); @@ -1945,7 +2300,17 @@ Editor::set_snap_to (SnapType st) case SnapToAEighthBeat: case SnapToAQuarterBeat: case SnapToAThirdBeat: - update_tempo_based_rulers (); + compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + (nframes_t)(edit_packer.get_width() * frames_per_unit)); + update_tempo_based_rulers (); + break; + + case SnapToRegionStart: + case SnapToRegionEnd: + case SnapToRegionSync: + case SnapToRegionBoundary: + build_region_boundary_cache (); + break; + default: /* relax */ break; @@ -1964,17 +2329,85 @@ Editor::set_snap_mode (SnapMode mode) instant_save (); } - -int -Editor::set_state (const XMLNode& node) +void +Editor::set_edit_point_preference (EditPoint ep, bool force) { - const XMLProperty* prop; - XMLNode* geometry; - int x, y, xoff, yoff; - Gdk::Geometry g; + bool changed = (_edit_point != ep); - if ((prop = node.property ("id")) != 0) { - _id = prop->value (); + _edit_point = ep; + string str = edit_point_strings[(int)ep]; + + if (str != edit_point_selector.get_active_text ()) { + edit_point_selector.set_active_text (str); + } + + set_canvas_cursor (); + + if (!force && !changed) { + return; + } + + switch (zoom_focus) { + case ZoomFocusMouse: + case ZoomFocusPlayhead: + case ZoomFocusEdit: + switch (_edit_point) { + case EditAtMouse: + set_zoom_focus (ZoomFocusMouse); + break; + case EditAtPlayhead: + set_zoom_focus (ZoomFocusPlayhead); + break; + case EditAtSelectedMarker: + set_zoom_focus (ZoomFocusEdit); + break; + } + break; + default: + break; + } + + const char* action; + + switch (_edit_point) { + case EditAtPlayhead: + action = "edit-at-playhead"; + break; + case EditAtSelectedMarker: + action = "edit-at-marker"; + break; + case EditAtMouse: + action = "edit-at-mouse"; + break; + } + + Glib::RefPtr act = ActionManager::get_action ("Editor", action); + if (act) { + Glib::RefPtr::cast_dynamic(act)->set_active (true); + } + + nframes64_t foo; + bool in_track_canvas; + + if (!mouse_frame (foo, in_track_canvas)) { + in_track_canvas = false; + } + + reset_canvas_action_sensitivity (in_track_canvas); + + instant_save (); +} + +int +Editor::set_state (const XMLNode& node) +{ + const XMLProperty* prop; + XMLNode* geometry; + int x, y, xoff, yoff; + Gdk::Geometry g; + + if ((prop = node.property ("id")) != 0) { + _id = prop->value (); } if ((geometry = find_named_node (node, "geometry")) == 0) { @@ -2000,7 +2433,7 @@ Editor::set_state (const XMLNode& node) move (x, y); if (session && (prop = node.property ("playhead"))) { - nframes_t pos = atol (prop->value().c_str()); + nframes64_t pos = atol (prop->value().c_str()); playhead_cursor->set_position (pos); } else { playhead_cursor->set_position (0); @@ -2014,11 +2447,8 @@ Editor::set_state (const XMLNode& node) horizontal_adjustment.set_value (0); } - if (session && (prop = node.property ("edit-cursor"))) { - nframes_t pos = atol (prop->value().c_str()); - edit_cursor->set_position (pos); - } else { - edit_cursor->set_position (0); + if ((prop = node.property ("mixer-width"))) { + editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width)); } if ((prop = node.property ("zoom-focus"))) { @@ -2037,6 +2467,10 @@ Editor::set_state (const XMLNode& node) set_snap_mode ((SnapMode) atoi (prop->value())); } + if ((prop = node.property ("edit-point"))) { + set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true); + } + if ((prop = node.property ("mouse-mode"))) { MouseMode m = str2mousemode(prop->value()); mouse_mode = MouseMode ((int) m + 1); /* lie, force mode switch */ @@ -2049,7 +2483,7 @@ Editor::set_state (const XMLNode& node) if ((prop = node.property ("show-waveforms"))) { bool yn = (prop->value() == "yes"); _show_waveforms = !yn; - RefPtr act = ActionManager::get_action (X_("Editor"), X_("ToggleWaveformVisibility")); + RefPtr act = ActionManager::get_action (X_("Editor"), X_("toggle-waveform-visible")); if (act) { RefPtr tact = RefPtr::cast_dynamic(act); /* do it twice to force the change */ @@ -2084,16 +2518,16 @@ Editor::set_state (const XMLNode& node) if ((prop = node.property ("follow-playhead"))) { bool yn = (prop->value() == "yes"); + set_follow_playhead (yn); RefPtr act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead")); if (act) { RefPtr tact = RefPtr::cast_dynamic(act); - /* do it twice to force the change */ - tact->set_active (!yn); - tact->set_active (yn); + if (tact->get_active() != yn) { + tact->set_active (yn); + } } } - if ((prop = node.property ("region-list-sort-type"))) { region_list_sort_type = (Editing::RegionListSortType) -1; // force change reset_region_list_sort_type(str2regionlistsorttype(prop->value())); @@ -2119,6 +2553,22 @@ Editor::set_state (const XMLNode& node) tact->set_active (yn); } } + + if ((prop = node.property ("show-editor-list"))) { + + Glib::RefPtr act = ActionManager::get_action (X_("Editor"), X_("show-editor-list")); + assert(act); + if (act) { + + Glib::RefPtr tact = Glib::RefPtr::cast_dynamic(act); + bool yn = (prop->value() == X_("yes")); + + /* do it twice to force the change */ + + tact->set_active (!yn); + tact->set_active (yn); + } + } return 0; @@ -2161,6 +2611,8 @@ Editor::get_state () node->add_child_nocopy (*geometry); } + maybe_add_mixer_strip_width (*node); + snprintf (buf, sizeof(buf), "%d", (int) zoom_focus); node->add_property ("zoom-focus", buf); snprintf (buf, sizeof(buf), "%f", frames_per_unit); @@ -2170,10 +2622,10 @@ Editor::get_state () snprintf (buf, sizeof(buf), "%d", (int) snap_mode); node->add_property ("snap-mode", buf); - snprintf (buf, sizeof (buf), "%" PRIu32, playhead_cursor->current_frame); + node->add_property ("edit-point", enum_2_string (_edit_point)); + + snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame); node->add_property ("playhead", buf); - snprintf (buf, sizeof (buf), "%" PRIu32, edit_cursor->current_frame); - node->add_property ("edit-cursor", buf); node->add_property ("show-waveforms", _show_waveforms ? "yes" : "no"); node->add_property ("show-waveforms-recording", _show_waveforms_recording ? "yes" : "no"); @@ -2188,6 +2640,12 @@ Editor::get_state () Glib::RefPtr tact = Glib::RefPtr::cast_dynamic(act); node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no"); } + + act = ActionManager::get_action (X_("Editor"), X_("show-editor-list")); + if (act) { + Glib::RefPtr tact = Glib::RefPtr::cast_dynamic(act); + node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no"); + } return *node; } @@ -2210,38 +2668,41 @@ Editor::trackview_by_y_position (double y) } void -Editor::snap_to (nframes_t& start, int32_t direction, bool for_mark) +Editor::snap_to (nframes64_t& start, int32_t direction, bool for_mark) { - Location* before = 0; - Location* after = 0; - - if (!session) { + if (!session || snap_mode == SnapOff) { return; } - const nframes_t one_second = session->frame_rate(); - const nframes_t one_minute = session->frame_rate() * 60; - const nframes_t one_smpte_second = (nframes_t)(rint(session->smpte_frames_per_second()) * session->frames_per_smpte_frame()); - nframes_t one_smpte_minute = (nframes_t)(rint(session->smpte_frames_per_second()) * session->frames_per_smpte_frame() * 60); - nframes_t presnap = start; + snap_to_internal (start, direction, for_mark); +} - switch (snap_type) { - case SnapToFrame: - break; +void +Editor::snap_to_internal (nframes64_t& start, int32_t direction, bool for_mark) +{ + Location* before = 0; + Location* after = 0; + + const nframes64_t one_second = session->frame_rate(); + const nframes64_t one_minute = session->frame_rate() * 60; + const nframes64_t one_smpte_second = (nframes64_t)(rint(session->smpte_frames_per_second()) * session->frames_per_smpte_frame()); + nframes64_t one_smpte_minute = (nframes64_t)(rint(session->smpte_frames_per_second()) * session->frames_per_smpte_frame() * 60); + nframes64_t presnap = start; + switch (snap_type) { case SnapToCDFrame: - if (direction) { - start = (nframes_t) ceil ((double) start / (one_second / 75)) * (one_second / 75); + if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) { + start = (nframes64_t) ceil ((double) start / (one_second / 75)) * (one_second / 75); } else { - start = (nframes_t) floor ((double) start / (one_second / 75)) * (one_second / 75); + start = (nframes64_t) floor ((double) start / (one_second / 75)) * (one_second / 75); } break; case SnapToSMPTEFrame: - if (fmod((double)start, (double)session->frames_per_smpte_frame()) > (session->frames_per_smpte_frame() / 2)) { - start = (nframes_t) (ceil ((double) start / session->frames_per_smpte_frame()) * session->frames_per_smpte_frame()); + if (((direction == 0) && (fmod((double)start, (double)session->frames_per_smpte_frame()) > (session->frames_per_smpte_frame() / 2))) || (direction > 0)) { + start = (nframes64_t) (ceil ((double) start / session->frames_per_smpte_frame()) * session->frames_per_smpte_frame()); } else { - start = (nframes_t) (floor ((double) start / session->frames_per_smpte_frame()) * session->frames_per_smpte_frame()); + start = (nframes64_t) (floor ((double) start / session->frames_per_smpte_frame()) * session->frames_per_smpte_frame()); } break; @@ -2252,10 +2713,10 @@ Editor::snap_to (nframes_t& start, int32_t direction, bool for_mark) } else { start -= session->smpte_offset (); } - if (start % one_smpte_second > one_smpte_second / 2) { - start = (nframes_t) ceil ((double) start / one_smpte_second) * one_smpte_second; + if (((direction == 0) && (start % one_smpte_second > one_smpte_second / 2)) || direction > 0) { + start = (nframes64_t) ceil ((double) start / one_smpte_second) * one_smpte_second; } else { - start = (nframes_t) floor ((double) start / one_smpte_second) * one_smpte_second; + start = (nframes64_t) floor ((double) start / one_smpte_second) * one_smpte_second; } if (session->smpte_offset_negative()) @@ -2273,10 +2734,10 @@ Editor::snap_to (nframes_t& start, int32_t direction, bool for_mark) } else { start -= session->smpte_offset (); } - if (start % one_smpte_minute > one_smpte_minute / 2) { - start = (nframes_t) ceil ((double) start / one_smpte_minute) * one_smpte_minute; + if (((direction == 0) && (start % one_smpte_minute > one_smpte_minute / 2)) || direction > 0) { + start = (nframes64_t) ceil ((double) start / one_smpte_minute) * one_smpte_minute; } else { - start = (nframes_t) floor ((double) start / one_smpte_minute) * one_smpte_minute; + start = (nframes64_t) floor ((double) start / one_smpte_minute) * one_smpte_minute; } if (session->smpte_offset_negative()) { @@ -2287,18 +2748,18 @@ Editor::snap_to (nframes_t& start, int32_t direction, bool for_mark) break; case SnapToSeconds: - if (start % one_second > one_second / 2) { - start = (nframes_t) ceil ((double) start / one_second) * one_second; + if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) { + start = (nframes64_t) ceil ((double) start / one_second) * one_second; } else { - start = (nframes_t) floor ((double) start / one_second) * one_second; + start = (nframes64_t) floor ((double) start / one_second) * one_second; } break; case SnapToMinutes: - if (start % one_minute > one_minute / 2) { - start = (nframes_t) ceil ((double) start / one_minute) * one_minute; + if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) { + start = (nframes64_t) ceil ((double) start / one_minute) * one_minute; } else { - start = (nframes_t) floor ((double) start / one_minute) * one_minute; + start = (nframes64_t) floor ((double) start / one_minute) * one_minute; } break; @@ -2311,27 +2772,23 @@ Editor::snap_to (nframes_t& start, int32_t direction, bool for_mark) break; case SnapToAThirtysecondBeat: - start = session->tempo_map().round_to_beat_subdivision (start, 32); - break; + start = session->tempo_map().round_to_beat_subdivision (start, 32); + break; case SnapToASixteenthBeat: - start = session->tempo_map().round_to_beat_subdivision (start, 16); - break; + start = session->tempo_map().round_to_beat_subdivision (start, 16); + break; case SnapToAEighthBeat: - start = session->tempo_map().round_to_beat_subdivision (start, 8); - break; + start = session->tempo_map().round_to_beat_subdivision (start, 8); + break; case SnapToAQuarterBeat: - start = session->tempo_map().round_to_beat_subdivision (start, 4); - break; - - case SnapToAThirdBeat: - start = session->tempo_map().round_to_beat_subdivision (start, 3); - break; + start = session->tempo_map().round_to_beat_subdivision (start, 4); + break; - case SnapToEditCursor: - start = edit_cursor->current_frame; + case SnapToAThirdBeat: + start = session->tempo_map().round_to_beat_subdivision (start, 3); break; case SnapToMark: @@ -2379,7 +2836,7 @@ Editor::snap_to (nframes_t& start, int32_t direction, bool for_mark) case SnapToRegionSync: case SnapToRegionBoundary: if (!region_boundary_cache.empty()) { - vector::iterator i; + vector::iterator i; if (direction > 0) { i = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start); @@ -2388,11 +2845,19 @@ Editor::snap_to (nframes_t& start, int32_t direction, bool for_mark) } if (i != region_boundary_cache.end()) { + + /* lower bound doesn't quite to the right thing for our purposes */ + + if (direction < 0 && i != region_boundary_cache.begin()) { + --i; + } + start = *i; + } else { start = region_boundary_cache.back(); } - } + } break; } @@ -2414,18 +2879,62 @@ Editor::snap_to (nframes_t& start, int32_t direction, bool for_mark) } default: + /* handled at entry */ return; } } +double +Editor::snap_length_beats (nframes64_t start) +{ + if (!session) { + return 1.0; + } + + /* FIXME: This could/should also work with non-tempo based snap settings (ie seconds) */ + + switch (snap_type) { + case SnapToBar: + return session->tempo_map().meter_at(start).beats_per_bar(); + + case SnapToBeat: + return 1.0; + + case SnapToAThirtysecondBeat: + return 1.0 / (double)32.0; + break; + + case SnapToASixteenthBeat: + return 1.0 / (double)16.0; + break; + + case SnapToAEighthBeat: + return 1.0 / (double)8.0; + break; + + case SnapToAQuarterBeat: + return 1.0 / (double)4.0; + break; + + case SnapToAThirdBeat: + return 1.0 / (double)3.0; + + default: + return 1.0; + } +} + void Editor::setup_toolbar () { string pixmap_path; +#ifdef GTKOSX + const guint32 FUDGE = 38; // Combo's are stupid - they steal space from the entry for the button +#else const guint32 FUDGE = 18; // Combo's are stupid - they steal space from the entry for the button - +#endif /* Mode Buttons (tool selection) */ @@ -2434,12 +2943,17 @@ Editor::setup_toolbar () mouse_move_button.add (*(manage (new Image (::get_icon("tool_object"))))); mouse_move_button.set_relief(Gtk::RELIEF_NONE); mouse_mode_buttons.push_back (&mouse_move_button); - mouse_select_button.add (*(manage (new Image (get_xpm("tool_range.xpm"))))); - mouse_select_button.set_relief(Gtk::RELIEF_NONE); - mouse_mode_buttons.push_back (&mouse_select_button); - mouse_gain_button.add (*(manage (new Image (::get_icon("tool_gain"))))); - mouse_gain_button.set_relief(Gtk::RELIEF_NONE); - mouse_mode_buttons.push_back (&mouse_gain_button); + + if (!Profile->get_sae()) { + mouse_select_button.add (*(manage (new Image (get_xpm("tool_range.xpm"))))); + mouse_select_button.set_relief(Gtk::RELIEF_NONE); + mouse_mode_buttons.push_back (&mouse_select_button); + + mouse_gain_button.add (*(manage (new Image (::get_icon("tool_gain"))))); + mouse_gain_button.set_relief(Gtk::RELIEF_NONE); + mouse_mode_buttons.push_back (&mouse_gain_button); + } + mouse_zoom_button.add (*(manage (new Image (::get_icon("tool_zoom"))))); mouse_zoom_button.set_relief(Gtk::RELIEF_NONE); mouse_mode_buttons.push_back (&mouse_zoom_button); @@ -2448,6 +2962,9 @@ Editor::setup_toolbar () mouse_mode_buttons.push_back (&mouse_timefx_button); mouse_audition_button.add (*(manage (new Image (::get_icon("tool_audition"))))); mouse_audition_button.set_relief(Gtk::RELIEF_NONE); + mouse_note_button.add (*(manage (new Image (::get_icon("tool_note"))))); + mouse_note_button.set_relief(Gtk::RELIEF_NONE); + mouse_mode_buttons.push_back (&mouse_note_button); mouse_mode_buttons.push_back (&mouse_audition_button); mouse_mode_button_set = new GroupedButtons (mouse_mode_buttons); @@ -2457,16 +2974,22 @@ Editor::setup_toolbar () mode_box->set_spacing(4); mouse_mode_button_box.set_spacing(1); mouse_mode_button_box.pack_start(mouse_move_button, true, true); - mouse_mode_button_box.pack_start(mouse_select_button, true, true); + if (!Profile->get_sae()) { + mouse_mode_button_box.pack_start(mouse_select_button, true, true); + } mouse_mode_button_box.pack_start(mouse_zoom_button, true, true); - mouse_mode_button_box.pack_start(mouse_gain_button, true, true); + if (!Profile->get_sae()) { + mouse_mode_button_box.pack_start(mouse_gain_button, true, true); + } mouse_mode_button_box.pack_start(mouse_timefx_button, true, true); mouse_mode_button_box.pack_start(mouse_audition_button, true, true); + mouse_mode_button_box.pack_start(mouse_note_button, true, true); mouse_mode_button_box.set_homogeneous(true); vector edit_mode_strings; - edit_mode_strings.push_back (edit_mode_to_string (Splice)); edit_mode_strings.push_back (edit_mode_to_string (Slide)); + edit_mode_strings.push_back (edit_mode_to_string (Splice)); + edit_mode_strings.push_back (edit_mode_to_string (Lock)); edit_mode_selector.set_name ("EditModeSelector"); Gtkmm2ext::set_size_request_to_display_given_text (edit_mode_selector, longest (edit_mode_strings).c_str(), 2+FUDGE, 10); @@ -2494,6 +3017,7 @@ Editor::setup_toolbar () mouse_zoom_button.set_name ("MouseModeButton"); mouse_timefx_button.set_name ("MouseModeButton"); mouse_audition_button.set_name ("MouseModeButton"); + mouse_note_button.set_name ("MouseModeButton"); ARDOUR_UI::instance()->tooltips().set_tip (mouse_move_button, _("Select/Move Objects")); ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Select/Move Ranges")); @@ -2501,6 +3025,7 @@ Editor::setup_toolbar () ARDOUR_UI::instance()->tooltips().set_tip (mouse_zoom_button, _("Select Zoom Range")); ARDOUR_UI::instance()->tooltips().set_tip (mouse_timefx_button, _("Stretch/Shrink Regions")); ARDOUR_UI::instance()->tooltips().set_tip (mouse_audition_button, _("Listen to Specific Regions")); + ARDOUR_UI::instance()->tooltips().set_tip (mouse_note_button, _("Edit MIDI Notes")); mouse_move_button.unset_flags (CAN_FOCUS); mouse_select_button.unset_flags (CAN_FOCUS); @@ -2508,6 +3033,7 @@ Editor::setup_toolbar () mouse_zoom_button.unset_flags (CAN_FOCUS); mouse_timefx_button.unset_flags (CAN_FOCUS); mouse_audition_button.unset_flags (CAN_FOCUS); + mouse_note_button.unset_flags (CAN_FOCUS); mouse_select_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseRange)); mouse_select_button.signal_button_release_event().connect (mem_fun(*this, &Editor::mouse_select_button_release)); @@ -2517,6 +3043,7 @@ Editor::setup_toolbar () mouse_zoom_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseZoom)); mouse_timefx_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseTimeFX)); mouse_audition_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseAudition)); + mouse_note_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseNote)); // mouse_move_button.set_active (true); @@ -2524,7 +3051,7 @@ Editor::setup_toolbar () /* Zoom */ zoom_box.set_spacing (1); - zoom_box.set_border_width (2); + zoom_box.set_border_width (0); zoom_in_button.set_name ("EditorTimeButton"); zoom_in_button.set_size_request(-1,16); @@ -2545,7 +3072,7 @@ Editor::setup_toolbar () ARDOUR_UI::instance()->tooltips().set_tip (zoom_out_full_button, _("Zoom to Session")); zoom_focus_selector.set_name ("ZoomFocusSelector"); - Gtkmm2ext::set_size_request_to_display_given_text (zoom_focus_selector, "Edit Cursor", FUDGE, 0); + Gtkmm2ext::set_size_request_to_display_given_text (zoom_focus_selector, _("Playhead"), FUDGE, 0); set_popdown_strings (zoom_focus_selector, zoom_focus_strings); zoom_focus_selector.signal_changed().connect (mem_fun(*this, &Editor::zoom_focus_selection_done)); ARDOUR_UI::instance()->tooltips().set_tip (zoom_focus_selector, _("Zoom focus")); @@ -2555,26 +3082,31 @@ Editor::setup_toolbar () zoom_box.pack_start (zoom_in_button, false, false); zoom_box.pack_start (zoom_out_full_button, false, false); - /* Edit Cursor / Snap */ - snap_box.set_spacing (1); snap_box.set_border_width (2); snap_type_selector.set_name ("SnapTypeSelector"); - Gtkmm2ext::set_size_request_to_display_given_text (snap_type_selector, "SMPTE Seconds", 2+FUDGE, 10); + Gtkmm2ext::set_size_request_to_display_given_text (snap_type_selector, _("SMPTE Seconds"), 2+FUDGE, 10); set_popdown_strings (snap_type_selector, snap_type_strings); snap_type_selector.signal_changed().connect (mem_fun(*this, &Editor::snap_type_selection_done)); - ARDOUR_UI::instance()->tooltips().set_tip (snap_type_selector, _("Unit to snap cursors and ranges to")); + ARDOUR_UI::instance()->tooltips().set_tip (snap_type_selector, _("Snap/Grid Units")); snap_mode_selector.set_name ("SnapModeSelector"); - Gtkmm2ext::set_size_request_to_display_given_text (snap_mode_selector, "Magnetic Snap", 2+FUDGE, 10); + Gtkmm2ext::set_size_request_to_display_given_text (snap_mode_selector, _("Magnetic Snap"), 2+FUDGE, 10); set_popdown_strings (snap_mode_selector, snap_mode_strings); snap_mode_selector.signal_changed().connect (mem_fun(*this, &Editor::snap_mode_selection_done)); + ARDOUR_UI::instance()->tooltips().set_tip (snap_mode_selector, _("Snap/Grid Mode")); + + edit_point_selector.set_name ("SnapModeSelector"); + Gtkmm2ext::set_size_request_to_display_given_text (edit_point_selector, _("Playhead"), 2+FUDGE, 10); + set_popdown_strings (edit_point_selector, edit_point_strings); + edit_point_selector.signal_changed().connect (mem_fun(*this, &Editor::edit_point_selection_done)); + ARDOUR_UI::instance()->tooltips().set_tip (edit_point_selector, _("Edit point")); - snap_box.pack_start (edit_cursor_clock, false, false); + snap_box.pack_start (edit_point_clock, false, false); snap_box.pack_start (snap_mode_selector, false, false); snap_box.pack_start (snap_type_selector, false, false); - + snap_box.pack_start (edit_point_selector, false, false); /* Nudge */ @@ -2582,8 +3114,8 @@ Editor::setup_toolbar () nudge_box->set_spacing(1); nudge_box->set_border_width (2); - nudge_forward_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::nudge_forward), false)); - nudge_backward_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::nudge_backward), false)); + nudge_forward_button.signal_button_release_event().connect (mem_fun(*this, &Editor::nudge_forward_release), false); + nudge_backward_button.signal_button_release_event().connect (mem_fun(*this, &Editor::nudge_backward_release), false); nudge_box->pack_start (nudge_backward_button, false, false); nudge_box->pack_start (nudge_forward_button, false, false); @@ -2592,10 +3124,10 @@ Editor::setup_toolbar () /* Pack everything in... */ - HBox* hbox = new HBox; + HBox* hbox = manage (new HBox); hbox->set_spacing(10); - tools_tearoff = new TearOff (*hbox); + tools_tearoff = manage (new TearOff (*hbox)); tools_tearoff->set_name ("MouseModeBase"); tools_tearoff->Detach.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast(&toolbar_hbox), @@ -2628,6 +3160,98 @@ Editor::setup_toolbar () toolbar_frame.add (toolbar_base); } + +void +Editor::setup_midi_toolbar () +{ + string pixmap_path; + + /* Mode Buttons (tool selection) */ + + vector midi_tool_buttons; + + midi_tool_pencil_button.add (*(manage (new Image (::get_icon("midi_tool_pencil"))))); + midi_tool_pencil_button.set_relief(Gtk::RELIEF_NONE); + midi_tool_buttons.push_back (&midi_tool_pencil_button); + midi_tool_select_button.add (*(manage (new Image (::get_icon("midi_tool_select"))))); + midi_tool_select_button.set_relief(Gtk::RELIEF_NONE); + midi_tool_buttons.push_back (&midi_tool_select_button); + midi_tool_resize_button.add (*(manage (new Image (::get_icon("strip_width"))))); + midi_tool_resize_button.set_relief(Gtk::RELIEF_NONE); + midi_tool_buttons.push_back (&midi_tool_resize_button); + midi_tool_erase_button.add (*(manage (new Image (::get_icon("midi_tool_erase"))))); + midi_tool_erase_button.set_relief(Gtk::RELIEF_NONE); + midi_tool_buttons.push_back (&midi_tool_erase_button); + + midi_tool_pencil_button.set_active(true); + + midi_tool_button_set = new GroupedButtons (midi_tool_buttons); + + midi_tool_button_box.set_border_width (2); + midi_tool_button_box.set_spacing(1); + midi_tool_button_box.pack_start(midi_tool_pencil_button, true, true); + midi_tool_button_box.pack_start(midi_tool_select_button, true, true); + midi_tool_button_box.pack_start(midi_tool_resize_button, true, true); + midi_tool_button_box.pack_start(midi_tool_erase_button, true, true); + midi_tool_button_box.set_homogeneous(true); + + midi_tool_pencil_button.set_name ("MouseModeButton"); + midi_tool_select_button.set_name ("MouseModeButton"); + midi_tool_resize_button.set_name ("MouseModeButton"); + midi_tool_erase_button.set_name ("MouseModeButton"); + + ARDOUR_UI::instance()->tooltips().set_tip (midi_tool_pencil_button, _("Add/Move/Stretch Notes")); + ARDOUR_UI::instance()->tooltips().set_tip (midi_tool_select_button, _("Select/Move Notes")); + ARDOUR_UI::instance()->tooltips().set_tip (midi_tool_resize_button, _("Resize Notes")); + ARDOUR_UI::instance()->tooltips().set_tip (midi_tool_erase_button, _("Erase Notes")); + + midi_tool_pencil_button.unset_flags (CAN_FOCUS); + midi_tool_select_button.unset_flags (CAN_FOCUS); + midi_tool_resize_button.unset_flags (CAN_FOCUS); + midi_tool_erase_button.unset_flags (CAN_FOCUS); + + midi_tool_pencil_button.signal_toggled().connect (bind (mem_fun(*this, + &Editor::midi_edit_mode_toggled), Editing::MidiEditPencil)); + midi_tool_select_button.signal_toggled().connect (bind (mem_fun(*this, + &Editor::midi_edit_mode_toggled), Editing::MidiEditSelect)); + midi_tool_resize_button.signal_toggled().connect (bind (mem_fun(*this, + &Editor::midi_edit_mode_toggled), Editing::MidiEditResize)); + midi_tool_erase_button.signal_toggled().connect (bind (mem_fun(*this, + &Editor::midi_edit_mode_toggled), Editing::MidiEditErase)); + + /* Pack everything in... */ + + midi_tools_tearoff = manage (new TearOff (midi_tool_button_box)); + midi_tools_tearoff->set_name ("MouseModeBase"); + + /* + midi_tools_tearoff->Detach.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast(&midi_toolbar_hbox), + &midi_tools_tearoff->tearoff_window())); + midi_tools_tearoff->Attach.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast (&midi_toolbar_hbox), + &midi_tools_tearoff->tearoff_window(), 0)); + midi_tools_tearoff->Hidden.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast(&midi_toolbar_hbox), + &midi_tools_tearoff->tearoff_window())); + midi_tools_tearoff->Visible.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast (&midi_toolbar_hbox), + &midi_tools_tearoff->tearoff_window(), 0)); + */ + + midi_toolbar_hbox.set_spacing (10); + midi_toolbar_hbox.set_border_width (1); + + midi_toolbar_hbox.pack_start (*midi_tools_tearoff, false, true); + + midi_tool_button_box.show_all (); + midi_toolbar_hbox.show_all(); + midi_tools_tearoff->show_all(); + + midi_toolbar_base.set_name ("ToolBarBase"); + midi_toolbar_base.add (midi_toolbar_hbox); + + midi_toolbar_frame.set_shadow_type (SHADOW_OUT); + midi_toolbar_frame.set_name ("BaseFrame"); + midi_toolbar_frame.add (midi_toolbar_base); +} + int Editor::convert_drop_to_paths (vector& paths, const RefPtr& context, @@ -2645,7 +3269,7 @@ Editor::convert_drop_to_paths (vector& paths, vector uris = data.get_uris(); if (uris.empty()) { - + /* This is seriously fucked up. Nautilus doesn't say that its URI lists are actually URI lists. So do it by hand. */ @@ -2695,10 +3319,34 @@ Editor::convert_drop_to_paths (vector& paths, } for (vector::iterator i = uris.begin(); i != uris.end(); ++i) { + if ((*i).substr (0,7) == "file://") { - string p = *i; + + ustring p = *i; PBD::url_decode (p); - paths.push_back (p.substr (7)); + + // scan forward past three slashes + + ustring::size_type slashcnt = 0; + ustring::size_type n = 0; + ustring::iterator x = p.begin(); + + while (slashcnt < 3 && x != p.end()) { + if ((*x) == '/') { + slashcnt++; + } else if (slashcnt == 3) { + break; + } + ++n; + ++x; + } + + if (slashcnt != 3 || x == p.end()) { + error << _("malformed URL passed to drag-n-drop code") << endmsg; + continue; + } + + paths.push_back (p.substr (n - 1)); } } @@ -2719,13 +3367,15 @@ Editor::map_transport_state () if (session->transport_stopped()) { have_pending_keyboard_selection = false; } + + update_loop_range_view (true); } /* UNDO/REDO */ -Editor::State::State () +Editor::State::State (PublicEditor const * e) { - selection = new Selection; + selection = new Selection (e); } Editor::State::~State () @@ -2736,7 +3386,7 @@ Editor::State::~State () UndoAction Editor::get_memento () const { - State *state = new State; + State *state = new State (this); store_state (*state); return bind (mem_fun (*(const_cast(this)), &Editor::restore_state), state); @@ -2779,1536 +3429,1723 @@ Editor::commit_reversible_command () } } -struct TrackViewByPositionSorter +void +Editor::set_edit_group_solo (Route& route, bool yn) { - bool operator() (const TimeAxisView* a, const TimeAxisView *b) { - return a->y_position < b->y_position; - } -}; + RouteGroup *edit_group; -bool -Editor::extend_selection_to_track (TimeAxisView& view) -{ - if (selection->selected (&view)) { - /* already selected, do nothing */ - return false; + if ((edit_group = route.edit_group()) != 0) { + edit_group->apply (&Route::set_solo, yn, this); + } else { + route.set_solo (yn, this); } +} - if (selection->tracks.empty()) { +void +Editor::set_edit_group_mute (Route& route, bool yn) +{ + RouteGroup *edit_group = 0; - if (!selection->selected (&view)) { - selection->set (&view); - return true; + if ((edit_group == route.edit_group()) != 0) { + edit_group->apply (&Route::set_mute, yn, this); + } else { + route.set_mute (yn, this); + } +} + +void +Editor::history_changed () +{ + string label; + + if (undo_action && session) { + if (session->undo_depth() == 0) { + label = _("Undo"); } else { - return false; + label = string_compose(_("Undo (%1)"), session->next_undo()); } - } - - /* something is already selected, so figure out which range of things to add */ - - TrackViewList to_be_added; - TrackViewList sorted = track_views; - TrackViewByPositionSorter cmp; - bool passed_clicked = false; - bool forwards = true; - - sorted.sort (cmp); - - if (!selection->selected (&view)) { - to_be_added.push_back (&view); + undo_action->property_label() = label; } - /* figure out if we should go forward or backwards */ + if (redo_action && session) { + if (session->redo_depth() == 0) { + label = _("Redo"); + } else { + label = string_compose(_("Redo (%1)"), session->next_redo()); + } + redo_action->property_label() = label; + } +} - for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) { +void +Editor::duplicate_dialog (bool with_dialog) +{ + float times = 1.0f; - if ((*i) == &view) { - passed_clicked = true; + if (mouse_mode == MouseRange) { + if (selection->time.length() == 0) { + return; } + } - if (selection->selected (*i)) { - if (passed_clicked) { - forwards = true; - } else { - forwards = false; - } - break; + RegionSelection rs; + get_regions_for_action (rs); + + if (mouse_mode != MouseRange) { + + if (rs.empty()) { + return; } } - - passed_clicked = false; - if (forwards) { + if (with_dialog) { - for (TrackViewList::iterator i = sorted.begin(); i != sorted.end(); ++i) { - - if ((*i) == &view) { - passed_clicked = true; - continue; - } - - if (passed_clicked) { - if ((*i)->hidden()) { - continue; - } - if (selection->selected (*i)) { - break; - } else if (!(*i)->hidden()) { - to_be_added.push_back (*i); - } - } + ArdourDialog win ("Duplication Dialog"); + Label label (_("Number of Duplications:")); + Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0); + SpinButton spinner (adjustment, 0.0, 1); + HBox hbox; + + win.get_vbox()->set_spacing (12); + win.get_vbox()->pack_start (hbox); + hbox.set_border_width (6); + hbox.pack_start (label, PACK_EXPAND_PADDING, 12); + + /* dialogs have ::add_action_widget() but that puts the spinner in the wrong + place, visually. so do this by hand. + */ + + hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12); + spinner.signal_activate().connect (sigc::bind (mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT)); + spinner.grab_focus(); + + hbox.show (); + label.show (); + spinner.show (); + + win.add_button (Stock::CANCEL, RESPONSE_CANCEL); + win.add_button (_("Duplicate"), RESPONSE_ACCEPT); + win.set_default_response (RESPONSE_ACCEPT); + + win.set_position (WIN_POS_MOUSE); + + spinner.grab_focus (); + + switch (win.run ()) { + case RESPONSE_ACCEPT: + break; + default: + return; } + + times = adjustment.get_value(); + } + if (mouse_mode == MouseRange) { + duplicate_selection (times); } else { + duplicate_some_regions (rs, times); + } +} - for (TrackViewList::reverse_iterator r = sorted.rbegin(); r != sorted.rend(); ++r) { - - if ((*r) == &view) { - passed_clicked = true; - continue; - } - - if (passed_clicked) { - - if ((*r)->hidden()) { - continue; - } - - if (selection->selected (*r)) { - break; - } else if (!(*r)->hidden()) { - to_be_added.push_back (*r); - } - } - } +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); } - - if (!to_be_added.empty()) { - selection->add (to_be_added); - return true; + 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 false; + return y; } +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); +} -bool -Editor::set_selected_track (TimeAxisView& view, Selection::Operation op, bool no_remove) +void +Editor::set_verbose_canvas_cursor_text (const string & txt) { - bool commit = false; + verbose_canvas_cursor->property_text() = txt.c_str(); +} - switch (op) { - case Selection::Toggle: - if (selection->selected (&view)) { - if (!no_remove) { - selection->remove (&view); - commit = true; - } - } else { - selection->add (&view); - commit = false; - } - break; +void +Editor::set_edit_mode (EditMode m) +{ + Config->set_edit_mode (m); +} - case Selection::Add: - if (!selection->selected (&view)) { - selection->add (&view); - commit = true; - } +void +Editor::cycle_edit_mode () +{ + switch (Config->get_edit_mode()) { + case Slide: + Config->set_edit_mode (Splice); break; - - case Selection::Set: - if (selection->selected (&view) && selection->tracks.size() == 1) { - /* no commit necessary */ - } else { - selection->set (&view); - commit = true; - } + case Splice: + Config->set_edit_mode (Lock); break; - - case Selection::Extend: - commit = extend_selection_to_track (view); + case Lock: + Config->set_edit_mode (Slide); break; } - - return commit; } -bool -Editor::set_selected_track_from_click (Selection::Operation op, bool no_remove) +void +Editor::edit_mode_selection_done () { - if (!clicked_trackview) { - return false; + if (session == 0) { + return; } - - return set_selected_track (*clicked_trackview, op, no_remove); -} -bool -Editor::set_selected_control_point_from_click (Selection::Operation op, bool no_remove) -{ - if (!clicked_control_point) { - return false; + string choice = edit_mode_selector.get_active_text(); + EditMode mode = Slide; + + if (choice == _("Splice Edit")) { + mode = Splice; + } else if (choice == _("Slide Edit")) { + mode = Slide; + } else if (choice == _("Lock Edit")) { + mode = Lock; } - /* select this point and any others that it represents */ + Config->set_edit_mode (mode); +} - double y1, y2; - nframes_t x1, x2; +void +Editor::snap_type_selection_done () +{ + string choice = snap_type_selector.get_active_text(); + SnapType snaptype = SnapToBeat; - x1 = pixel_to_frame (clicked_control_point->get_x() - 10); - x2 = pixel_to_frame (clicked_control_point->get_x() + 10); - y1 = clicked_control_point->get_x() - 10; - y2 = clicked_control_point->get_y() + 10; + if (choice == _("Beats/3")) { + snaptype = SnapToAThirdBeat; + } else if (choice == _("Beats/4")) { + snaptype = SnapToAQuarterBeat; + } else if (choice == _("Beats/8")) { + snaptype = SnapToAEighthBeat; + } else if (choice == _("Beats/16")) { + snaptype = SnapToASixteenthBeat; + } else if (choice == _("Beats/32")) { + snaptype = SnapToAThirtysecondBeat; + } else if (choice == _("Beats")) { + snaptype = SnapToBeat; + } else if (choice == _("Bars")) { + snaptype = SnapToBar; + } else if (choice == _("Marks")) { + snaptype = SnapToMark; + } else if (choice == _("Region starts")) { + snaptype = SnapToRegionStart; + } else if (choice == _("Region ends")) { + snaptype = SnapToRegionEnd; + } else if (choice == _("Region bounds")) { + snaptype = SnapToRegionBoundary; + } else if (choice == _("Region syncs")) { + snaptype = SnapToRegionSync; + } else if (choice == _("CD Frames")) { + snaptype = SnapToCDFrame; + } else if (choice == _("SMPTE Frames")) { + snaptype = SnapToSMPTEFrame; + } else if (choice == _("SMPTE Seconds")) { + snaptype = SnapToSMPTESeconds; + } else if (choice == _("SMPTE Minutes")) { + snaptype = SnapToSMPTEMinutes; + } else if (choice == _("Seconds")) { + snaptype = SnapToSeconds; + } else if (choice == _("Minutes")) { + snaptype = SnapToMinutes; + } - return select_all_within (x1, x2, y1, y2, op); -} + RefPtr ract = snap_type_action (snaptype); + if (ract) { + ract->set_active (); + } +} void -Editor::get_relevant_audio_tracks (set& relevant_tracks) +Editor::snap_mode_selection_done () { - /* step one: get all selected tracks and all tracks in the relevant edit groups */ - - for (TrackSelection::iterator ti = selection->tracks.begin(); ti != selection->tracks.end(); ++ti) { - - AudioTimeAxisView* atv = dynamic_cast(*ti); + string choice = snap_mode_selector.get_active_text(); + SnapMode mode = SnapNormal; - if (!atv) { - continue; - } + if (choice == _("No Grid")) { + mode = SnapOff; + } else if (choice == _("Grid")) { + mode = SnapNormal; + } else if (choice == _("Magnetic")) { + mode = SnapMagnetic; + } - RouteGroup* group = atv->route()->edit_group(); + RefPtr ract = snap_mode_action (mode); - if (group && group->is_active()) { - - /* active group for this track, loop over all tracks and get every member of the group */ + if (ract) { + ract->set_active (true); + } +} - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - - AudioTimeAxisView* tatv; - - if ((tatv = dynamic_cast (*i)) != 0) { - - if (tatv->route()->edit_group() == group) { - relevant_tracks.insert (tatv); - } - } - } +void +Editor::cycle_edit_point (bool with_marker) +{ + switch (_edit_point) { + case EditAtMouse: + set_edit_point_preference (EditAtPlayhead); + break; + case EditAtPlayhead: + if (with_marker) { + set_edit_point_preference (EditAtSelectedMarker); } else { - relevant_tracks.insert (atv); + set_edit_point_preference (EditAtMouse); } + break; + case EditAtSelectedMarker: + set_edit_point_preference (EditAtMouse); + break; } } void -Editor::mapover_audio_tracks (slot sl) +Editor::edit_point_selection_done () { - set relevant_tracks; + string choice = edit_point_selector.get_active_text(); + EditPoint ep = EditAtSelectedMarker; - get_relevant_audio_tracks (relevant_tracks); + if (choice == _("Marker")) { + set_edit_point_preference (EditAtSelectedMarker); + } else if (choice == _("Playhead")) { + set_edit_point_preference (EditAtPlayhead); + } else { + set_edit_point_preference (EditAtMouse); + } - uint32_t sz = relevant_tracks.size(); + RefPtr ract = edit_point_action (ep); - for (set::iterator ati = relevant_tracks.begin(); ati != relevant_tracks.end(); ++ati) { - sl (**ati, sz); + if (ract) { + ract->set_active (true); } } void -Editor::mapped_set_selected_regionview_from_click (RouteTimeAxisView& tv, uint32_t ignored, - RegionView* basis, vector* all_equivs) +Editor::zoom_focus_selection_done () { - boost::shared_ptr pl; - vector > results; - RegionView* marv; - boost::shared_ptr ds; + string choice = zoom_focus_selector.get_active_text(); + ZoomFocus focus_type = ZoomFocusLeft; - if ((ds = tv.get_diskstream()) == 0) { - /* bus */ - return; - } + if (choice == _("Left")) { + focus_type = ZoomFocusLeft; + } else if (choice == _("Right")) { + focus_type = ZoomFocusRight; + } else if (choice == _("Center")) { + focus_type = ZoomFocusCenter; + } else if (choice == _("Play")) { + focus_type = ZoomFocusPlayhead; + } else if (choice == _("Edit")) { + focus_type = ZoomFocusEdit; + } else if (choice == _("Active Mark")) { + focus_type = ZoomFocusEdit; + } else if (choice == _("Active Mark")) { + focus_type = ZoomFocusEdit; + } else { + focus_type = ZoomFocusMouse; + } + + RefPtr ract = zoom_focus_action (focus_type); - if (&tv == &basis->get_time_axis_view()) { - /* looking in same track as the original */ - return; + if (ract) { + ract->set_active (); } +} - if ((pl = ds->playlist()) != 0) { - pl->get_equivalent_regions (basis->region(), results); - } - - for (vector >::iterator ir = results.begin(); ir != results.end(); ++ir) { - if ((marv = tv.view()->find_view (*ir)) != 0) { - all_equivs->push_back (marv); - } +gint +Editor::edit_controls_button_release (GdkEventButton* ev) +{ + if (Keyboard::is_context_menu_event (ev)) { + ARDOUR_UI::instance()->add_route (this); } + return TRUE; } -bool -Editor::set_selected_regionview_from_click (bool press, Selection::Operation op, bool no_track_remove) +gint +Editor::mouse_select_button_release (GdkEventButton* ev) { - vector all_equivalent_regions; - bool commit = false; + /* this handles just right-clicks */ - if (!clicked_regionview || !clicked_audio_trackview) { + if (ev->button != 3) { return false; } - if (press) { - button_release_can_deselect = false; - } - - if (op == Selection::Toggle || op == Selection::Set) { - - mapover_audio_tracks (bind (mem_fun (*this, &Editor::mapped_set_selected_regionview_from_click), - clicked_regionview, &all_equivalent_regions)); - - - /* add clicked regionview since we skipped all other regions in the same track as the one it was in */ - - all_equivalent_regions.push_back (clicked_regionview); - - switch (op) { - case Selection::Toggle: - - if (clicked_regionview->get_selected()) { - if (press) { - - /* whatever was clicked was selected already; do nothing here but allow - the button release to deselect it - */ + return true; +} - button_release_can_deselect = true; +Editor::TrackViewList * +Editor::get_valid_views (TimeAxisView* track, RouteGroup* group) +{ + TrackViewList *v; + TrackViewList::iterator i; - } else { + v = new TrackViewList; - if (button_release_can_deselect) { + if (track == 0 && group == 0) { - /* just remove this one region, but only on a permitted button release */ + /* all views */ - selection->remove (clicked_regionview); - commit = true; + for (i = track_views.begin(); i != track_views.end (); ++i) { + v->push_back (*i); + } - /* no more deselect action on button release till a new press - finds an already selected object. - */ + } else if ((track != 0 && group == 0) || (track != 0 && group != 0 && !group->is_active())) { + + /* just the view for this track + */ - button_release_can_deselect = false; - } - } + v->push_back (track); - } else { + } else { + + /* views for all tracks in the edit group */ + + for (i = track_views.begin(); i != track_views.end (); ++i) { - if (press) { - /* add all the equivalent regions, but only on button press */ - - if (!all_equivalent_regions.empty()) { - commit = true; - } - - for (vector::iterator i = all_equivalent_regions.begin(); i != all_equivalent_regions.end(); ++i) { - selection->add (*i); - } - } - } - break; - - case Selection::Set: - if (!clicked_regionview->get_selected()) { - selection->set (all_equivalent_regions); - commit = true; - } else { - /* no commit necessary: clicked on an already selected region */ - goto out; + if (group == 0 || (*i)->edit_group() == group) { + v->push_back (*i); } - break; - - default: - /* silly compiler */ - break; } + } + + return v; +} - } else if (op == Selection::Extend) { +void +Editor::set_zoom_focus (ZoomFocus f) +{ + string str = zoom_focus_strings[(int)f]; - list results; - nframes_t last_frame; - nframes_t first_frame; + if (str != zoom_focus_selector.get_active_text()) { + zoom_focus_selector.set_active_text (str); + } + + if (zoom_focus != f) { + zoom_focus = f; - /* 1. find the last selected regionview in the track that was clicked in */ + ZoomFocusChanged (); /* EMIT_SIGNAL */ - last_frame = 0; - first_frame = max_frames; + instant_save (); + } +} - for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) { - if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) { - - if ((*x)->region()->last_frame() > last_frame) { - last_frame = (*x)->region()->last_frame(); - } - - if ((*x)->region()->first_frame() < first_frame) { - first_frame = (*x)->region()->first_frame(); - } - } - } - - /* 2. figure out the boundaries for our search for new objects */ - - switch (clicked_regionview->region()->coverage (first_frame, last_frame)) { - case OverlapNone: - if (last_frame < clicked_regionview->region()->first_frame()) { - first_frame = last_frame; - last_frame = clicked_regionview->region()->last_frame(); - } else { - last_frame = first_frame; - first_frame = clicked_regionview->region()->first_frame(); - } - break; +void +Editor::ensure_float (Window& win) +{ + win.set_transient_for (*this); +} - case OverlapExternal: - if (last_frame < clicked_regionview->region()->first_frame()) { - first_frame = last_frame; - last_frame = clicked_regionview->region()->last_frame(); - } else { - last_frame = first_frame; - first_frame = clicked_regionview->region()->first_frame(); - } - break; +void +Editor::pane_allocation_handler (Allocation &alloc, Paned* which) +{ + /* recover or initialize pane positions. do this here rather than earlier because + we don't want the positions to change the child allocations, which they seem to do. + */ - case OverlapInternal: - if (last_frame < clicked_regionview->region()->first_frame()) { - first_frame = last_frame; - last_frame = clicked_regionview->region()->last_frame(); - } else { - last_frame = first_frame; - first_frame = clicked_regionview->region()->first_frame(); - } - break; + int pos; + XMLProperty* prop; + char buf[32]; + XMLNode* node = ARDOUR_UI::instance()->editor_settings(); + int width, height; + static int32_t done; + XMLNode* geometry; - case OverlapStart: - case OverlapEnd: - /* nothing to do except add clicked region to selection, since it - overlaps with the existing selection in this track. - */ - break; - } + if ((geometry = find_named_node (*node, "geometry")) == 0) { + width = default_width; + height = default_height; + } else { + width = atoi(geometry->property("x_size")->value()); + height = atoi(geometry->property("y_size")->value()); + } - /* 2. find all selectable objects (regionviews in this case) between that one and the end of the - one that was clicked. - */ + if (which == static_cast (&edit_pane)) { - set relevant_tracks; - - get_relevant_audio_tracks (relevant_tracks); - - for (set::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) { - (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results); + if (done) { + return; } - - /* 3. convert to a vector of audio regions */ - vector regions; - - for (list::iterator x = results.begin(); x != results.end(); ++x) { - RegionView* arv; - - if ((arv = dynamic_cast(*x)) != 0) { - regions.push_back (arv); - } + if (!geometry || (prop = geometry->property ("edit_pane_pos")) == 0) { + /* initial allocation is 90% to canvas, 10% to notebook */ + pos = (int) floor (alloc.get_width() * 0.90f); + snprintf (buf, sizeof(buf), "%d", pos); + } else { + pos = atoi (prop->value()); } - if (!regions.empty()) { - selection->add (regions); - commit = true; + if ((done = GTK_WIDGET(edit_pane.gobj())->allocation.width > pos)) { + edit_pane.set_position (pos); + pre_maximal_pane_position = pos; } } - - out: - return commit; } void -Editor::set_selected_regionview_from_region_list (boost::shared_ptr region, Selection::Operation op) +Editor::detach_tearoff (Box* b, Window* w) { - vector all_equivalent_regions; - - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - - RouteTimeAxisView* tatv; - - if ((tatv = dynamic_cast (*i)) != 0) { - - boost::shared_ptr pl; - vector > results; - RegionView* marv; - boost::shared_ptr ds; - - if ((ds = tatv->get_diskstream()) == 0) { - /* bus */ - continue; - } - - if ((pl = (ds->playlist())) != 0) { - pl->get_region_list_equivalent_regions (region, results); - } - - for (vector >::iterator ir = results.begin(); ir != results.end(); ++ir) { - if ((marv = tatv->view()->find_view (*ir)) != 0) { - all_equivalent_regions.push_back (marv); - } - } - - } - } - - begin_reversible_command (_("set selected regions")); - - switch (op) { - case Selection::Toggle: - /* XXX this is not correct */ - selection->toggle (all_equivalent_regions); - break; - case Selection::Set: - selection->set (all_equivalent_regions); - break; - case Selection::Extend: - selection->add (all_equivalent_regions); - break; - case Selection::Add: - selection->add (all_equivalent_regions); - break; + if (tools_tearoff->torn_off() && + mouse_mode_tearoff->torn_off()) { + top_hbox.remove (toolbar_frame); } - - commit_reversible_command () ; } -bool -Editor::set_selected_regionview_from_map_event (GdkEventAny* ev, StreamView* sv, boost::weak_ptr weak_r) +void +Editor::reattach_tearoff (Box* b, Window* w, int32_t n) { - RegionView* rv; - boost::shared_ptr r (weak_r.lock()); - - if (!r) { - return true; - } - - boost::shared_ptr ar; - - if ((ar = boost::dynamic_pointer_cast (r)) == 0) { - return true; - } - - if ((rv = sv->find_view (ar)) == 0) { - return true; - } - - /* don't reset the selection if its something other than - a single other region. - */ - - if (selection->regions.size() > 1) { - return true; + if (toolbar_frame.get_parent() == 0) { + top_hbox.pack_end (toolbar_frame); } - - begin_reversible_command (_("set selected regions")); - - selection->set (rv); - - commit_reversible_command () ; - - return true; } void -Editor::set_edit_group_solo (Route& route, bool yn) +Editor::set_show_measures (bool yn) { - RouteGroup *edit_group; + if (_show_measures != yn) { + hide_measures (); - if ((edit_group = route.edit_group()) != 0) { - edit_group->apply (&Route::set_solo, yn, this); - } else { - route.set_solo (yn, this); + if ((_show_measures = yn) == true) { + draw_measures (); + } + instant_save (); } } void -Editor::set_edit_group_mute (Route& route, bool yn) +Editor::toggle_follow_playhead () { - RouteGroup *edit_group = 0; - - if ((edit_group == route.edit_group()) != 0) { - edit_group->apply (&Route::set_mute, yn, this); - } else { - route.set_mute (yn, this); + RefPtr act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead")); + if (act) { + RefPtr tact = RefPtr::cast_dynamic(act); + set_follow_playhead (tact->get_active()); } } - + void -Editor::history_changed () +Editor::set_follow_playhead (bool yn) { - string label; - - if (undo_action && session) { - if (session->undo_depth() == 0) { - label = _("Undo"); - } else { - label = string_compose(_("Undo (%1)"), session->next_undo()); + if (_follow_playhead != yn) { + if ((_follow_playhead = yn) == true) { + /* catch up */ + update_current_screen (); } - undo_action->property_label() = label; + instant_save (); } +} - if (redo_action && session) { - if (session->redo_depth() == 0) { - label = _("Redo"); - } else { - label = string_compose(_("Redo (%1)"), session->next_redo()); - } - redo_action->property_label() = label; +void +Editor::toggle_xfade_active (boost::weak_ptr wxfade) +{ + boost::shared_ptr xfade (wxfade.lock()); + if (xfade) { + xfade->set_active (!xfade->active()); } } void -Editor::duplicate_dialog (bool dup_region) +Editor::toggle_xfade_length (boost::weak_ptr wxfade) { - if (dup_region) { - if (clicked_regionview == 0) { - return; - } - } else { - if (selection->time.length() == 0) { - return; - } + boost::shared_ptr xfade (wxfade.lock()); + if (xfade) { + xfade->set_follow_overlap (!xfade->following_overlap()); } +} - ArdourDialog win ("duplicate dialog"); - Entry entry; - Label label (_("Duplicate how many times?")); - - win.get_vbox()->pack_start (label); - win.add_action_widget (entry, RESPONSE_ACCEPT); - win.add_button (Stock::OK, RESPONSE_ACCEPT); - win.add_button (Stock::CANCEL, RESPONSE_CANCEL); - - win.set_position (WIN_POS_MOUSE); - - entry.set_text ("1"); - set_size_request_to_display_given_text (entry, X_("12345678"), 20, 15); - entry.select_region (0, -1); - entry.grab_focus (); +void +Editor::edit_xfade (boost::weak_ptr wxfade) +{ + boost::shared_ptr xfade (wxfade.lock()); + if (!xfade) { + return; + } - switch (win.run ()) { + CrossfadeEditor cew (*session, xfade, xfade->fade_in().get_min_y(), 1.0); + + ensure_float (cew); + + switch (cew.run ()) { case RESPONSE_ACCEPT: break; default: return; } - - string text = entry.get_text(); - float times; - - if (sscanf (text.c_str(), "%f", ×) == 1) { - if (dup_region) { - RegionSelection regions; - regions.add (clicked_regionview); - duplicate_some_regions (regions, times); - } else { - duplicate_selection (times); - } - } + + cew.apply (); + xfade->StateChanged (Change (~0)); } -void -Editor::show_verbose_canvas_cursor () +PlaylistSelector& +Editor::playlist_selector () const { - verbose_canvas_cursor->raise_to_top(); - verbose_canvas_cursor->show(); - verbose_cursor_visible = true; + return *_playlist_selector; } -void -Editor::hide_verbose_canvas_cursor () +nframes64_t +Editor::get_nudge_distance (nframes64_t pos, nframes64_t& next) { - verbose_canvas_cursor->hide(); - verbose_cursor_visible = false; -} + nframes64_t ret; -void -Editor::set_verbose_canvas_cursor (const string & txt, double x, double y) -{ - /* XXX get origin of canvas relative to root window, - add x and y and check compared to gdk_screen_{width,height} - */ - verbose_canvas_cursor->property_text() = txt.c_str(); - verbose_canvas_cursor->property_x() = x; - verbose_canvas_cursor->property_y() = y; + ret = nudge_clock.current_duration (pos); + next = ret + 1; /* XXXX fix me */ + + return ret; } void -Editor::set_verbose_canvas_cursor_text (const string & txt) +Editor::end_location_changed (Location* location) { - verbose_canvas_cursor->property_text() = txt.c_str(); + ENSURE_GUI_THREAD (bind (mem_fun(*this, &Editor::end_location_changed), location)); + //reset_scrolling_region (); + horizontal_adjustment.set_upper ( location->start()); } -void -Editor::edit_mode_selection_done () +int +Editor::playlist_deletion_dialog (boost::shared_ptr pl) { - if (session == 0) { - return; + ArdourDialog dialog ("playlist deletion dialog"); + Label label (string_compose (_("Playlist %1 is currently unused.\n" + "If left alone, no audio files used by it will be cleaned.\n" + "If deleted, audio files used by it alone by will cleaned."), + pl->name())); + + dialog.set_position (WIN_POS_CENTER); + dialog.get_vbox()->pack_start (label); + + label.show (); + + dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT); + dialog.add_button (_("Keep playlist"), RESPONSE_REJECT); + dialog.add_button (_("Cancel"), RESPONSE_CANCEL); + + switch (dialog.run ()) { + case RESPONSE_ACCEPT: + /* delete the playlist */ + return 0; + break; + + case RESPONSE_REJECT: + /* keep the playlist */ + return 1; + break; + + default: + break; } - string choice = edit_mode_selector.get_active_text(); - EditMode mode = Slide; + return -1; +} - if (choice == _("Splice Edit")) { - mode = Splice; - } else if (choice == _("Slide Edit")) { - mode = Slide; +bool +Editor::audio_region_selection_covers (nframes64_t where) +{ + for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) { + if ((*a)->region()->covers (where)) { + return true; + } } - Config->set_edit_mode (mode); -} + return false; +} void -Editor::snap_type_selection_done () +Editor::prepare_for_cleanup () { - string choice = snap_type_selector.get_active_text(); - SnapType snaptype = SnapToFrame; + cut_buffer->clear_regions (); + cut_buffer->clear_playlists (); - if (choice == _("Beats/3")) { - snaptype = SnapToAThirdBeat; - } else if (choice == _("Beats/4")) { - snaptype = SnapToAQuarterBeat; - } else if (choice == _("Beats/8")) { - snaptype = SnapToAEighthBeat; - } else if (choice == _("Beats/16")) { - snaptype = SnapToASixteenthBeat; - } else if (choice == _("Beats/32")) { - snaptype = SnapToAThirtysecondBeat; - } else if (choice == _("Beats")) { - snaptype = SnapToBeat; - } else if (choice == _("Bars")) { - snaptype = SnapToBar; - } else if (choice == _("Marks")) { - snaptype = SnapToMark; - } else if (choice == _("Edit Cursor")) { - snaptype = SnapToEditCursor; - } else if (choice == _("Region starts")) { - snaptype = SnapToRegionStart; - } else if (choice == _("Region ends")) { - snaptype = SnapToRegionEnd; - } else if (choice == _("Region bounds")) { - snaptype = SnapToRegionBoundary; - } else if (choice == _("Region syncs")) { - snaptype = SnapToRegionSync; - } else if (choice == _("CD Frames")) { - snaptype = SnapToCDFrame; - } else if (choice == _("SMPTE Frames")) { - snaptype = SnapToSMPTEFrame; - } else if (choice == _("SMPTE Seconds")) { - snaptype = SnapToSMPTESeconds; - } else if (choice == _("SMPTE Minutes")) { - snaptype = SnapToSMPTEMinutes; - } else if (choice == _("Seconds")) { - snaptype = SnapToSeconds; - } else if (choice == _("Minutes")) { - snaptype = SnapToMinutes; - } else if (choice == _("None")) { - snaptype = SnapToFrame; - } + selection->clear_regions (); + selection->clear_playlists (); - RefPtr ract = snap_type_action (snaptype); - if (ract) { - ract->set_active (); - } -} + no_region_list_redisplay = true; +} void -Editor::snap_mode_selection_done () +Editor::finish_cleanup () { - string choice = snap_mode_selector.get_active_text(); - SnapMode mode = SnapNormal; + no_region_list_redisplay = false; + redisplay_regions (); +} - if (choice == _("Normal")) { - mode = SnapNormal; - } else if (choice == _("Magnetic")) { - mode = SnapMagnetic; +Location* +Editor::transport_loop_location() +{ + if (session) { + return session->locations()->auto_loop_location(); + } else { + return 0; } +} - RefPtr ract = snap_mode_action (mode); +Location* +Editor::transport_punch_location() +{ + if (session) { + return session->locations()->auto_punch_location(); + } else { + return 0; + } +} - if (ract) { - ract->set_active (true); +bool +Editor::control_layout_scroll (GdkEventScroll* ev) +{ + switch (ev->direction) { + case GDK_SCROLL_UP: + scroll_tracks_up_line (); + return true; + break; + + case GDK_SCROLL_DOWN: + scroll_tracks_down_line (); + return true; + + default: + /* no left/right handling yet */ + break; } + + return false; } + +/** A new snapshot has been selected. + */ void -Editor::zoom_focus_selection_done () +Editor::snapshot_display_selection_changed () { - string choice = zoom_focus_selector.get_active_text(); - ZoomFocus focus_type = ZoomFocusLeft; + if (snapshot_display.get_selection()->count_selected_rows() > 0) { - if (choice == _("Left")) { - focus_type = ZoomFocusLeft; - } else if (choice == _("Right")) { - focus_type = ZoomFocusRight; - } else if (choice == _("Center")) { - focus_type = ZoomFocusCenter; - } else if (choice == _("Playhead")) { - focus_type = ZoomFocusPlayhead; - } else if (choice == _("Edit Cursor")) { - focus_type = ZoomFocusEdit; - } - - RefPtr ract = zoom_focus_action (focus_type); + TreeModel::iterator i = snapshot_display.get_selection()->get_selected(); + + Glib::ustring snap_name = (*i)[snapshot_display_columns.real_name]; - if (ract) { - ract->set_active (); + if (snap_name.length() == 0) { + return; + } + + if (session->snap_name() == snap_name) { + return; + } + + ARDOUR_UI::instance()->load_session(session->path(), string (snap_name)); } -} +} -gint -Editor::edit_controls_button_release (GdkEventButton* ev) +bool +Editor::snapshot_display_button_press (GdkEventButton* ev) { - if (Keyboard::is_context_menu_event (ev)) { - ARDOUR_UI::instance()->add_route (); + if (ev->button == 3) { + /* Right-click on the snapshot list. Work out which snapshot it + was over. */ + Gtk::TreeModel::Path path; + Gtk::TreeViewColumn* col; + int cx; + int cy; + snapshot_display.get_path_at_pos ((int) ev->x, (int) ev->y, path, col, cx, cy); + Gtk::TreeModel::iterator iter = snapshot_display_model->get_iter (path); + if (iter) { + Gtk::TreeModel::Row row = *iter; + popup_snapshot_context_menu (ev->button, ev->time, row[snapshot_display_columns.real_name]); + } + return true; } - return TRUE; + + return false; } + +/** Pop up the snapshot display context menu. + * @param button Button used to open the menu. + * @param time Menu open time. + * @snapshot_name Name of the snapshot that the menu click was over. + */ + void -Editor::track_selection_changed () +Editor::popup_snapshot_context_menu (int button, int32_t time, Glib::ustring snapshot_name) { - switch (selection->tracks.size()){ - case 0: - break; - default: - set_selected_mixer_strip (*(selection->tracks.front())); - break; - } + using namespace Menu_Helpers; - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - (*i)->set_selected (false); - if (mouse_mode == MouseRange) { - (*i)->hide_selection (); - } - } + MenuList& items (snapshot_context_menu.items()); + items.clear (); - for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) { - (*i)->set_selected (true); - if (mouse_mode == MouseRange) { - (*i)->show_selection (selection->time); - } - } + const bool modification_allowed = (session->snap_name() != snapshot_name && session->name() != snapshot_name); + + add_item_with_sensitivity (items, MenuElem (_("Remove"), bind (mem_fun (*this, &Editor::remove_snapshot), snapshot_name)), modification_allowed); + + add_item_with_sensitivity (items, MenuElem (_("Rename"), bind (mem_fun (*this, &Editor::rename_snapshot), snapshot_name)), modification_allowed); + + snapshot_context_menu.popup (button, time); } void -Editor::time_selection_changed () +Editor::rename_snapshot (Glib::ustring old_name) { - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - (*i)->hide_selection (); - } + ArdourPrompter prompter(true); - if (selection->tracks.empty()) { - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - (*i)->show_selection (selection->time); - } - } else { - for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) { - (*i)->show_selection (selection->time); - } - } + string new_name; - if (selection->time.empty()) { - ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false); - } else { - ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true); + prompter.set_name ("Prompter"); + prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT); + prompter.set_prompt (_("New name of snapshot")); + prompter.set_initial_text (old_name); + + if (prompter.run() == RESPONSE_ACCEPT) { + prompter.get_result (new_name); + if (new_name.length()) { + session->rename_state (old_name, new_name); + redisplay_snapshots (); + } } } + void -Editor::region_selection_changed () +Editor::remove_snapshot (Glib::ustring name) { - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - (*i)->set_selected_regionviews (selection->regions); + vector choices; + + std::string prompt = string_compose (_("Do you really want to remove snapshot \"%1\" ?\n(cannot be undone)"), name); + + choices.push_back (_("No, do nothing.")); + choices.push_back (_("Yes, remove it.")); + + Gtkmm2ext::Choice prompter (prompt, choices); + + if (prompter.run () == 1) { + session->remove_state (name); + redisplay_snapshots (); } } void -Editor::point_selection_changed () +Editor::redisplay_snapshots () { - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - (*i)->set_selected_points (selection->points); + if (session == 0) { + return; } -} -gint -Editor::mouse_select_button_release (GdkEventButton* ev) -{ - /* this handles just right-clicks */ + vector state_file_paths; - if (ev->button != 3) { - return FALSE; - } + get_state_files_in_directory (session->session_directory().root_path(), + state_file_paths); - return TRUE; -} + if (state_file_paths.empty()) return; -Editor::TrackViewList * -Editor::get_valid_views (TimeAxisView* track, RouteGroup* group) -{ - TrackViewList *v; - TrackViewList::iterator i; + vector state_file_names(get_file_names_no_extension(state_file_paths)); - v = new TrackViewList; + snapshot_display_model->clear (); - if (track == 0 && group == 0) { + for (vector::iterator i = state_file_names.begin(); + i != state_file_names.end(); ++i) + { + string statename = (*i); + TreeModel::Row row = *(snapshot_display_model->append()); + + /* this lingers on in case we ever want to change the visible + name of the snapshot. + */ + + string display_name; + display_name = statename; - /* all views */ + if (statename == session->snap_name()) { + snapshot_display.get_selection()->select(row); + } + + row[snapshot_display_columns.visible_name] = display_name; + row[snapshot_display_columns.real_name] = statename; + } +} - for (i = track_views.begin(); i != track_views.end (); ++i) { - v->push_back (*i); - } +void +Editor::session_state_saved (string snap_name) +{ + ENSURE_GUI_THREAD (bind (mem_fun(*this, &Editor::session_state_saved), snap_name)); + redisplay_snapshots (); +} - } else if (track != 0 && group == 0 || (track != 0 && group != 0 && !group->is_active())) { - - /* just the view for this track - */ +void +Editor::maximise_editing_space () +{ + mouse_mode_tearoff->set_visible (false); + tools_tearoff->set_visible (false); - v->push_back (track); + pre_maximal_pane_position = edit_pane.get_position(); + pre_maximal_editor_width = this->get_width(); - } else { - - /* views for all tracks in the edit group */ - - for (i = track_views.begin(); i != track_views.end (); ++i) { + if(post_maximal_pane_position == 0) { + post_maximal_pane_position = edit_pane.get_width(); + } - if (group == 0 || (*i)->edit_group() == group) { - v->push_back (*i); - } - } + fullscreen(); + + if(post_maximal_editor_width) { + edit_pane.set_position (post_maximal_pane_position - + abs(post_maximal_editor_width - pre_maximal_editor_width)); + } else { + edit_pane.set_position (post_maximal_pane_position); } - - return v; } void -Editor::set_zoom_focus (ZoomFocus f) +Editor::restore_editing_space () { - string str = zoom_focus_strings[(int)f]; + // user changed width of pane during fullscreen - if (str != zoom_focus_selector.get_active_text()) { - zoom_focus_selector.set_active_text (str); + if(post_maximal_pane_position != edit_pane.get_position()) { + post_maximal_pane_position = edit_pane.get_position(); } - - if (zoom_focus != f) { - zoom_focus = f; - ZoomFocusChanged (); /* EMIT_SIGNAL */ + unfullscreen(); - instant_save (); - } + mouse_mode_tearoff->set_visible (true); + tools_tearoff->set_visible (true); + post_maximal_editor_width = this->get_width(); + + edit_pane.set_position (pre_maximal_pane_position + abs(this->get_width() - pre_maximal_editor_width)); +} + +/** + * Make new playlists for a given track and also any others that belong + * to the same active edit group. + * @param v Track. + */ + +void +Editor::new_playlists (TimeAxisView* v) +{ + begin_reversible_command (_("new playlists")); + mapover_tracks (mem_fun (*this, &Editor::mapped_use_new_playlist), v); + commit_reversible_command (); } +/** + * Use a copy of the current playlist for a given track and also any others that belong + * to the same active edit group. + * @param v Track. + */ + void -Editor::ensure_float (Window& win) +Editor::copy_playlists (TimeAxisView* v) { - win.set_transient_for (*this); + begin_reversible_command (_("copy playlists")); + mapover_tracks (mem_fun (*this, &Editor::mapped_use_copy_playlist), v); + commit_reversible_command (); } +/** + * Clear the current playlist for a given track and also any others that belong + * to the same active edit group. + * @param v Track. + */ + void -Editor::pane_allocation_handler (Allocation &alloc, Paned* which) +Editor::clear_playlists (TimeAxisView* v) { - /* recover or initialize pane positions. do this here rather than earlier because - we don't want the positions to change the child allocations, which they seem to do. - */ + begin_reversible_command (_("clear playlists")); + mapover_tracks (mem_fun (*this, &Editor::mapped_clear_playlist), v); + commit_reversible_command (); +} - int pos; - XMLProperty* prop; - char buf[32]; - XMLNode* node = ARDOUR_UI::instance()->editor_settings(); - int width, height; - static int32_t done; - XMLNode* geometry; +void +Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz) +{ + atv.use_new_playlist (sz > 1 ? false : true); +} - if ((geometry = find_named_node (*node, "geometry")) == 0) { - width = default_width; - height = default_height; - } else { - width = atoi(geometry->property("x_size")->value()); - height = atoi(geometry->property("y_size")->value()); - } +void +Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz) +{ + atv.use_copy_playlist (sz > 1 ? false : true); +} - if (which == static_cast (&edit_pane)) { +void +Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t sz) +{ + atv.clear_playlist (); +} - if (done) { - return; - } +bool +Editor::on_key_press_event (GdkEventKey* ev) +{ + return key_press_focus_accelerator_handler (*this, ev); +} - if (!geometry || (prop = geometry->property ("edit_pane_pos")) == 0) { - /* initial allocation is 90% to canvas, 10% to notebook */ - pos = (int) floor (alloc.get_width() * 0.90f); - snprintf (buf, sizeof(buf), "%d", pos); - } else { - pos = atoi (prop->value()); - } - - if ((done = GTK_WIDGET(edit_pane.gobj())->allocation.width > pos)) { - edit_pane.set_position (pos); - pre_maximal_pane_position = pos; - } - } +bool +Editor::on_key_release_event (GdkEventKey* ev) +{ + return Gtk::Window::on_key_release_event (ev); + // return key_press_focus_accelerator_handler (*this, ev); } void -Editor::detach_tearoff (Box* b, Window* w) +Editor::reset_x_origin (nframes64_t frame) { - if (tools_tearoff->torn_off() && - mouse_mode_tearoff->torn_off()) { - top_hbox.remove (toolbar_frame); - } + //cerr << "resetting x origin" << endl; + queue_visual_change (frame); } void -Editor::reattach_tearoff (Box* b, Window* w, int32_t n) +Editor::reset_zoom (double fpu) { - if (toolbar_frame.get_parent() == 0) { - top_hbox.pack_end (toolbar_frame); - } + queue_visual_change (fpu); } void -Editor::set_show_measures (bool yn) +Editor::reposition_and_zoom (nframes64_t frame, double fpu) { - if (_show_measures != yn) { - hide_measures (); + //cerr << "Editor::reposition_and_zoom () called ha v:l:u:ps:fpu = " << horizontal_adjustment.get_value() << ":" << horizontal_adjustment.get_lower() << ":" << horizontal_adjustment.get_upper() << ":" << horizontal_adjustment.get_page_size() << ":" << frames_per_unit << endl;//DEBUG + reset_x_origin (frame); + reset_zoom (fpu); - if ((_show_measures = yn) == true) { - draw_measures (); - } - DisplayControlChanged (ShowMeasures); - instant_save (); + if (!no_save_visual) { + undo_visual_stack.push_back (current_visual_state(false)); } } -void -Editor::toggle_follow_playhead () +Editor::VisualState* +Editor::current_visual_state (bool with_tracks) { - RefPtr act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead")); - if (act) { - RefPtr tact = RefPtr::cast_dynamic(act); - set_follow_playhead (tact->get_active()); + VisualState* vs = new VisualState; + vs->y_position = vertical_adjustment.get_value(); + vs->frames_per_unit = frames_per_unit; + vs->leftmost_frame = leftmost_frame; + vs->zoom_focus = zoom_focus; + vs->zoomed_to_region = zoomed_to_region; + + 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())); + } } + + return vs; } void -Editor::set_follow_playhead (bool yn) +Editor::undo_visual_state () { - if (_follow_playhead != yn) { - if ((_follow_playhead = yn) == true) { - /* catch up */ - update_current_screen (); - } - DisplayControlChanged (FollowPlayhead); - instant_save (); + if (undo_visual_stack.empty()) { + return; } + + VisualState* vs = undo_visual_stack.back(); + undo_visual_stack.pop_back(); + use_visual_state (*vs); + redo_visual_stack.push_back (vs); } void -Editor::toggle_xfade_active (boost::weak_ptr wxfade) +Editor::redo_visual_state () { - boost::shared_ptr xfade (wxfade.lock()); - if (xfade) { - xfade->set_active (!xfade->active()); + if (redo_visual_stack.empty()) { + return; } + + VisualState* vs = redo_visual_stack.back(); + redo_visual_stack.pop_back(); + use_visual_state (*vs); + undo_visual_stack.push_back (vs); } void -Editor::toggle_xfade_length (boost::weak_ptr wxfade) +Editor::swap_visual_state () { - boost::shared_ptr xfade (wxfade.lock()); - if (xfade) { - xfade->set_follow_overlap (!xfade->following_overlap()); + if (undo_visual_stack.empty()) { + redo_visual_state (); + } else { + undo_visual_state (); } } void -Editor::edit_xfade (boost::weak_ptr wxfade) +Editor::use_visual_state (VisualState& vs) { - boost::shared_ptr xfade (wxfade.lock()); + no_save_visual = true; - if (!xfade) { - return; - } + vertical_adjustment.set_value (vs.y_position); - CrossfadeEditor cew (*session, xfade, xfade->fade_in().get_min_y(), 1.0); - - ensure_float (cew); - - switch (cew.run ()) { - case RESPONSE_ACCEPT: - break; - default: - return; + 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)); + } } + + if (!vs.track_states.empty()) { + update_route_visibility (); + } - cew.apply (); - xfade->StateChanged (Change (~0)); + no_save_visual = false; } -PlaylistSelector& -Editor::playlist_selector () const +void +Editor::set_frames_per_unit (double fpu) { - return *_playlist_selector; -} + /* 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. + */ -nframes_t -Editor::get_nudge_distance (nframes_t pos, nframes_t& next) -{ - nframes_t ret; + if (fpu == frames_per_unit) { + return; + } - ret = nudge_clock.current_duration (pos); - next = ret + 1; /* XXXX fix me */ + if (fpu < 2.0) { + fpu = 2.0; + } - return ret; + + /* don't allow zooms that fit more than the maximum number + of frames into an 800 pixel wide space. + */ + + if (max_frames / fpu < 800.0) { + return; + } + + frames_per_unit = fpu; + post_zoom (); } void -Editor::end_location_changed (Location* location) +Editor::post_zoom () { - ENSURE_GUI_THREAD (bind (mem_fun(*this, &Editor::end_location_changed), location)); - reset_scrolling_region (); -} + // convert fpu to frame count -int -Editor::playlist_deletion_dialog (boost::shared_ptr pl) -{ - ArdourDialog dialog ("playlist deletion dialog"); - Label label (string_compose (_("Playlist %1 is currently unused.\n" - "If left alone, no audio files used by it will be cleaned.\n" - "If deleted, audio files used by it alone by will cleaned."), - pl->name())); - - dialog.set_position (WIN_POS_CENTER); - dialog.get_vbox()->pack_start (label); + nframes64_t frames = (nframes64_t) floor (frames_per_unit * canvas_width); - label.show (); + if (frames_per_unit != zoom_range_clock.current_duration()) { + zoom_range_clock.set (frames); + } - dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT); - dialog.add_button (_("Keep playlist"), RESPONSE_CANCEL); - dialog.add_button (_("Cancel"), RESPONSE_CANCEL); + if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) { + if (!selection->tracks.empty()) { + for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) { + (*i)->reshow_selection (selection->time); + } + } else { + for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { + (*i)->reshow_selection (selection->time); + } + } + } - switch (dialog.run ()) { - case RESPONSE_ACCEPT: - /* delete the playlist */ - return 0; - break; + ZoomChanged (); /* EMIT_SIGNAL */ - case RESPONSE_REJECT: - /* keep the playlist */ - return 1; - break; + reset_hscrollbar_stepping (); + //reset_scrolling_region (); - default: - break; + if (playhead_cursor) { + playhead_cursor->set_position (playhead_cursor->current_frame); } - return -1; + instant_save (); } -bool -Editor::audio_region_selection_covers (nframes_t where) +void +Editor::queue_visual_change (nframes64_t where) { - for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) { - if ((*a)->region()->covers (where)) { - return true; - } - } +// pending_visual_change.pending = VisualChange::Type (pending_visual_change.pending | VisualChange::TimeOrigin); +// pending_visual_change.time_origin = where; - return false; + if (pending_visual_change.idle_handler_id < 0) { + pending_visual_change.pending = VisualChange::Type (pending_visual_change.pending | VisualChange::TimeOrigin); + pending_visual_change.time_origin = where; + pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this); + } } void -Editor::prepare_for_cleanup () +Editor::queue_visual_change (double fpu) { - cut_buffer->clear_regions (); - cut_buffer->clear_playlists (); - - selection->clear_regions (); - selection->clear_playlists (); -} + pending_visual_change.pending = VisualChange::Type (pending_visual_change.pending | VisualChange::ZoomLevel); + pending_visual_change.frames_per_unit = fpu; -Location* -Editor::transport_loop_location() -{ - if (session) { - return session->locations()->auto_loop_location(); - } else { - return 0; + if (pending_visual_change.idle_handler_id < 0) { + pending_visual_change.idle_handler_id = g_idle_add ( _idle_visual_changer, this); } + } -Location* -Editor::transport_punch_location() +int +Editor::_idle_visual_changer (void* arg) { - if (session) { - return session->locations()->auto_punch_location(); - } else { - return 0; - } + return static_cast(arg)->idle_visual_changer (); } -bool -Editor::control_layout_scroll (GdkEventScroll* ev) +int +Editor::idle_visual_changer () { - switch (ev->direction) { - case GDK_SCROLL_UP: - scroll_tracks_up_line (); - return true; - break; + VisualChange::Type p = pending_visual_change.pending; + pending_visual_change.pending = (VisualChange::Type) 0; - case GDK_SCROLL_DOWN: - scroll_tracks_down_line (); - return true; - - default: - /* no left/right handling yet */ - break; + 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 + (nframes_t)(edit_packer.get_width() * pending_visual_change.frames_per_unit)); + compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + (nframes_t)(edit_packer.get_width() * pending_visual_change.frames_per_unit)); + update_tempo_based_rulers (); } + if (p & VisualChange::TimeOrigin) { - return false; -} + nframes64_t csf, cef; + nframes64_t current_time_origin = (nframes64_t) floor (horizontal_adjustment.get_value() * frames_per_unit); -void -Editor::snapshot_display_selection_changed () -{ - if (snapshot_display.get_selection()->count_selected_rows() > 0) { + if (session) { + csf = session->current_start_frame(); + cef = session->current_end_frame() + (current_page_frames() / 24);// Add a little extra so we can see the end marker + } - TreeModel::iterator i = snapshot_display.get_selection()->get_selected(); - - Glib::ustring snap_name = (*i)[snapshot_display_columns.real_name]; + /* if we seek beyond the current end of the canvas, move the end */ - if (snap_name.length() == 0) { - return; - } - - if (session->snap_name() == snap_name) { - return; + if (current_time_origin != pending_visual_change.time_origin) { + //if (horizontal_adjustment.get_upper() < pending_visual_change.time_origin) { + last_canvas_frame = (cef > (pending_visual_change.time_origin + current_page_frames())) ? cef : pending_visual_change.time_origin + current_page_frames(); + horizontal_adjustment.set_upper ((cef - csf) / frames_per_unit); + //} + horizontal_adjustment.set_value (pending_visual_change.time_origin / frames_per_unit); + } else { + update_fixed_rulers(); + redisplay_tempo (true); } - - ARDOUR_UI::instance()->load_session(session->path(), string (snap_name)); } + //cerr << "Editor::idle_visual_changer () called ha v:l:u:ps:fpu = " << horizontal_adjustment.get_value() << ":" << horizontal_adjustment.get_lower() << ":" << horizontal_adjustment.get_upper() << ":" << horizontal_adjustment.get_page_size() << ":" << frames_per_unit << endl;//DEBUG + pending_visual_change.idle_handler_id = -1; + return 0; /* this is always a one-shot call */ } -bool -Editor::snapshot_display_button_press (GdkEventButton* ev) -{ - return false; -} - +struct EditorOrderTimeAxisSorter { + bool operator() (const TimeAxisView* a, const TimeAxisView* b) const { + return a->order < b->order; + } +}; + void -Editor::redisplay_snapshots () +Editor::sort_track_selection (TrackSelection* sel) { - if (session == 0) { - return; + EditorOrderTimeAxisSorter cmp; + + if (sel) { + sel->sort (cmp); + } else { + selection->tracks.sort (cmp); } +} - vector* states; +nframes64_t +Editor::get_preferred_edit_position (bool ignore_playhead) +{ + bool ignored; + nframes64_t where = 0; + EditPoint ep = _edit_point; - if ((states = session->possible_states()) == 0) { - return; + if (entered_marker) { + return entered_marker->position(); } - snapshot_display_model->clear (); + if (ignore_playhead && ep == EditAtPlayhead) { + ep = EditAtSelectedMarker; + } - for (vector::iterator i = states->begin(); i != states->end(); ++i) { - string statename = *(*i); - TreeModel::Row row = *(snapshot_display_model->append()); - - /* this lingers on in case we ever want to change the visible - name of the snapshot. - */ + switch (ep) { + case EditAtPlayhead: + where = session->audible_frame(); + break; - string display_name; - display_name = statename; - - if (statename == session->snap_name()) { - snapshot_display.get_selection()->select(row); + case EditAtSelectedMarker: + if (!selection->markers.empty()) { + bool is_start; + Location* loc = find_location_from_marker (selection->markers.front(), is_start); + if (loc) { + if (is_start) { + where = loc->start(); + } else { + where = loc->end(); + } + break; + } } + /* fallthru */ - row[snapshot_display_columns.visible_name] = display_name; - row[snapshot_display_columns.real_name] = statename; + default: + case EditAtMouse: + if (!mouse_frame (where, ignored)) { + /* XXX not right but what can we do ? */ + return 0; + } + snap_to (where); + break; } - delete states; + return where; } void -Editor::session_state_saved (string snap_name) +Editor::set_loop_range (nframes64_t start, nframes64_t end, string cmd) { - ENSURE_GUI_THREAD (bind (mem_fun(*this, &Editor::session_state_saved), snap_name)); - redisplay_snapshots (); + if (!session) return; + + begin_reversible_command (cmd); + + Location* tll; + + if ((tll = transport_loop_location()) == 0) { + Location* loc = new Location (start, end, _("Loop"), Location::IsAutoLoop); + XMLNode &before = session->locations()->get_state(); + session->locations()->add (loc, true); + session->set_auto_loop_location (loc); + XMLNode &after = session->locations()->get_state(); + session->add_command (new MementoCommand(*(session->locations()), &before, &after)); + } else { + XMLNode &before = tll->get_state(); + tll->set_hidden (false, this); + tll->set (start, end); + XMLNode &after = tll->get_state(); + session->add_command (new MementoCommand(*tll, &before, &after)); + } + + commit_reversible_command (); +} + +void +Editor::set_punch_range (nframes64_t start, nframes64_t end, string cmd) +{ + if (!session) return; + + begin_reversible_command (cmd); + + Location* tpl; + + if ((tpl = transport_punch_location()) == 0) { + Location* loc = new Location (start, end, _("Loop"), Location::IsAutoPunch); + XMLNode &before = session->locations()->get_state(); + session->locations()->add (loc, true); + session->set_auto_loop_location (loc); + XMLNode &after = session->locations()->get_state(); + session->add_command (new MementoCommand(*(session->locations()), &before, &after)); + } + else { + XMLNode &before = tpl->get_state(); + tpl->set_hidden (false, this); + tpl->set (start, end); + XMLNode &after = tpl->get_state(); + session->add_command (new MementoCommand(*tpl, &before, &after)); + } + + commit_reversible_command (); } void -Editor::maximise_editing_space () +Editor::get_regions_at (RegionSelection& rs, nframes64_t where, const TrackSelection& ts) const { - initial_ruler_update_required = true; + const TrackSelection* tracks; - mouse_mode_tearoff->set_visible (false); - tools_tearoff->set_visible (false); + if (ts.empty()) { + tracks = &track_views; + } else { + tracks = &ts; + } - pre_maximal_pane_position = edit_pane.get_position(); - pre_maximal_editor_width = this->get_width(); + for (TrackSelection::const_iterator t = tracks->begin(); t != tracks->end(); ++t) { + + AudioTimeAxisView* atv = dynamic_cast(*t); - if(post_maximal_pane_position == 0) { - post_maximal_pane_position = edit_pane.get_width(); - } + if (atv) { + boost::shared_ptr ds; + boost::shared_ptr pl; + + if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()))) { + Playlist::RegionList* regions = pl->regions_at ((nframes64_t) floor ( (double)where * ds->speed())); - fullscreen(); - if(post_maximal_editor_width) { - edit_pane.set_position (post_maximal_pane_position - - abs(post_maximal_editor_width - pre_maximal_editor_width)); - } else { - edit_pane.set_position (post_maximal_pane_position); + for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) { + + RegionView* rv = atv->audio_view()->find_view (*i); + + if (rv) { + rs.add (rv); + } + } + + delete regions; + } + } } } void -Editor::restore_editing_space () +Editor::get_regions_after (RegionSelection& rs, nframes64_t where, const TrackSelection& ts) const { - initial_ruler_update_required = true; + const TrackSelection* tracks; - // user changed width of pane during fullscreen - if(post_maximal_pane_position != edit_pane.get_position()) { - post_maximal_pane_position = edit_pane.get_position(); + if (ts.empty()) { + tracks = &track_views; + } else { + tracks = &ts; } - unfullscreen(); - - mouse_mode_tearoff->set_visible (true); - tools_tearoff->set_visible (true); - post_maximal_editor_width = this->get_width(); + for (TrackSelection::const_iterator t = tracks->begin(); t != tracks->end(); ++t) { + + AudioTimeAxisView* atv = dynamic_cast(*t); + if (atv) { + boost::shared_ptr ds; + boost::shared_ptr pl; + + if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()))) { - edit_pane.set_position ( - pre_maximal_pane_position + abs(this->get_width() - pre_maximal_editor_width) - ); -} + Playlist::RegionList* regions = pl->regions_touched ((nframes64_t) floor ( (double)where * ds->speed()), max_frames); -void -Editor::new_playlists () -{ - begin_reversible_command (_("new playlists")); - mapover_audio_tracks (mem_fun (*this, &Editor::mapped_use_new_playlist)); - commit_reversible_command (); -} + for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) { -void -Editor::copy_playlists () -{ - begin_reversible_command (_("copy playlists")); - mapover_audio_tracks (mem_fun (*this, &Editor::mapped_use_copy_playlist)); - commit_reversible_command (); -} + RegionView* rv = atv->audio_view()->find_view (*i); -void -Editor::clear_playlists () -{ - begin_reversible_command (_("clear playlists")); - mapover_audio_tracks (mem_fun (*this, &Editor::mapped_clear_playlist)); - commit_reversible_command (); -} + if (rv) { + rs.push_back (rv); + } + } -void -Editor::mapped_use_new_playlist (AudioTimeAxisView& atv, uint32_t sz) -{ - atv.use_new_playlist (sz > 1 ? false : true); + delete regions; + } + } + } } void -Editor::mapped_use_copy_playlist (AudioTimeAxisView& atv, uint32_t sz) +Editor::get_regions_for_action (RegionSelection& rs, bool allow_entered) { - atv.use_copy_playlist (sz > 1 ? false : true); -} + if (selection->regions.empty()) { -void -Editor::mapped_clear_playlist (AudioTimeAxisView& atv, uint32_t sz) -{ - atv.clear_playlist (); -} + if (selection->tracks.empty()) { -bool -Editor::on_key_press_event (GdkEventKey* ev) -{ - return key_press_focus_accelerator_handler (*this, ev); -} + /* no regions or tracks selected + */ + + if (entered_regionview && mouse_mode == Editing::MouseObject) { -void -Editor::reset_x_origin (nframes_t frame) -{ - queue_visual_change (frame); -} + /* entered regionview is valid and we're in object mode - + just use entered regionview + */ -void -Editor::reset_zoom (double fpu) -{ - queue_visual_change (fpu); -} + rs.add (entered_regionview); + } -void -Editor::reposition_and_zoom (nframes_t frame, double fpu) -{ - reset_x_origin (frame); - reset_zoom (fpu); -} + return; -void -Editor::set_frames_per_unit (double fpu) -{ - nframes_t frames; + } else { - /* 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. - */ + /* no regions selected, so get all regions at the edit point across + all selected tracks. + */ - if (fpu == frames_per_unit) { - return; - } + nframes64_t where = get_preferred_edit_position(); + get_regions_at (rs, where, selection->tracks); - if (fpu < 2.0) { - fpu = 2.0; - } + /* if the entered regionview wasn't selected and neither was its track + then add it. + */ - // convert fpu to frame count + if (entered_regionview != 0 && + !selection->selected (entered_regionview) && + !selection->selected (&entered_regionview->get_time_axis_view())) { + rs.add (entered_regionview); + } + } - frames = (nframes_t) floor (fpu * canvas_width); - - /* don't allow zooms that fit more than the maximum number - of frames into an 800 pixel wide space. - */ + } else { + + /* just use the selected regions */ - if (max_frames / fpu < 800.0) { - return; - } + rs = selection->regions; - if (fpu == frames_per_unit) { - return; - } + /* if the entered regionview wasn't selected and we allow this sort of thing, + then add it. + */ - frames_per_unit = fpu; + if (allow_entered && entered_regionview && !selection->selected (entered_regionview)) { + rs.add (entered_regionview); + } - if (frames != zoom_range_clock.current_duration()) { - zoom_range_clock.set (frames); } +} - if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) { - if (!selection->tracks.empty()) { - for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) { - (*i)->reshow_selection (selection->time); +void +Editor::get_regions_corresponding_to (boost::shared_ptr region, vector& regions) +{ + + for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { + + RouteTimeAxisView* tatv; + + if ((tatv = dynamic_cast (*i)) != 0) { + + boost::shared_ptr pl; + vector > results; + RegionView* marv; + boost::shared_ptr ds; + + if ((ds = tatv->get_diskstream()) == 0) { + /* bus */ + continue; } - } else { - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - (*i)->reshow_selection (selection->time); + + if ((pl = (ds->playlist())) != 0) { + pl->get_region_list_equivalent_regions (region, results); + } + + for (vector >::iterator ir = results.begin(); ir != results.end(); ++ir) { + if ((marv = tatv->view()->find_view (*ir)) != 0) { + regions.push_back (marv); + } } + } } +} - ZoomChanged (); /* EMIT_SIGNAL */ - - reset_hscrollbar_stepping (); - reset_scrolling_region (); - - if (edit_cursor) edit_cursor->set_position (edit_cursor->current_frame); - if (playhead_cursor) playhead_cursor->set_position (playhead_cursor->current_frame); +void +Editor::show_rhythm_ferret () +{ + if (rhythm_ferret == 0) { + rhythm_ferret = new RhythmFerret(*this); + } - instant_save (); + rhythm_ferret->set_session (session); + rhythm_ferret->show (); + rhythm_ferret->present (); } -void -Editor::canvas_horizontally_scrolled () +void +Editor::first_idle () { - /* this is the core function that controls horizontal scrolling of the canvas. it is called - whenever the horizontal_adjustment emits its "value_changed" signal. it executes in an - idle handler. - */ + MessageDialog* dialog = 0; - leftmost_frame = (nframes_t) floor (horizontal_adjustment.get_value() * frames_per_unit); - nframes_t rightmost_frame = leftmost_frame + current_page_frames (); - - if (rightmost_frame > last_canvas_frame) { - last_canvas_frame = rightmost_frame; - reset_scrolling_region (); + if (track_views.size() > 1) { + dialog = new MessageDialog (*this, + _("Please wait while Ardour loads visual data"), + true, + Gtk::MESSAGE_INFO, + Gtk::BUTTONS_NONE); + dialog->present (); + ARDOUR_UI::instance()->flush_pending (); + } + + for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) { + (*t)->first_idle(); } - update_fixed_rulers (); - tempo_map_changed (Change (0)); + if (dialog) { + delete dialog; + } + + _have_idled = true; } void -Editor::queue_visual_change (nframes_t where) +Editor::start_resize_line_ops () { - pending_visual_change.pending = VisualChange::Type (pending_visual_change.pending | VisualChange::TimeOrigin); - pending_visual_change.time_origin = where; - - if (pending_visual_change.idle_handler_id < 0) { - pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this); - } +#if 0 + old_resize_line_y = -1; + resize_line_y = -1; + need_resize_line = true; +#endif } void -Editor::queue_visual_change (double fpu) +Editor::end_resize_line_ops () { - pending_visual_change.pending = VisualChange::Type (pending_visual_change.pending | VisualChange::ZoomLevel); - pending_visual_change.frames_per_unit = fpu; +#if 0 + need_resize_line = false; - if (pending_visual_change.idle_handler_id < 0) { - pending_visual_change.idle_handler_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE, _idle_visual_changer, this, 0); + if (old_resize_line_y >= 0) { + Gdk::Rectangle r (0, old_resize_line_y, (int) canvas_width, 3); + Glib::RefPtr win = get_window(); + cerr << "Final invalidation at " << old_resize_line_y << endl; + win->invalidate_rect (r, false); } +#endif } -int -Editor::_idle_visual_changer (void* arg) +void +Editor::queue_draw_resize_line (int at) { - return static_cast(arg)->idle_visual_changer (); +#if 0 + Glib::RefPtr win = get_window(); + + resize_line_y = at; + + if (win && canvas_width) { + + int controls_width = controls_layout.get_width(); + int xroot, discard; + + controls_layout.get_window()->get_origin (xroot, discard); + + if (old_resize_line_y >= 0) { + + /* redraw where it used to be */ + + + Gdk::Rectangle r (0, old_resize_line_y - 1, controls_width + (int) canvas_width, 3); + win->invalidate_rect (r, true); + cerr << "invalidate " << xroot << "," << old_resize_line_y - 1 << ' ' + << controls_width + canvas_width << " x 3\n"; + } + + /* draw where it is */ + + Gdk::Rectangle r (0, at - 1, controls_width + (int) canvas_width, 3); + win->invalidate_rect (r, true); + } +#endif } -int -Editor::idle_visual_changer () +bool +Editor::on_expose_event (GdkEventExpose* ev) { - VisualChange::Type p = pending_visual_change.pending; + /* cerr << "+++ editor expose " + << ev->area.x << ',' << ev->area.y + << ' ' + << ev->area.width << " x " << ev->area.height + << " need reize ? " << need_resize_line + << endl; + */ + bool ret = Window::on_expose_event (ev); - pending_visual_change.pending = (VisualChange::Type) 0; - pending_visual_change.idle_handler_id = -1; - - if (p & VisualChange::ZoomLevel) { - set_frames_per_unit (pending_visual_change.frames_per_unit); - } +#if 0 + if (need_resize_line) { + + int xroot, yroot, discard; + int controls_width; - if (p & VisualChange::TimeOrigin) { - if (pending_visual_change.time_origin != leftmost_frame) { - horizontal_adjustment.set_value (pending_visual_change.time_origin/frames_per_unit); - /* the signal handler will do the rest */ + /* Our root coordinates for drawing the line will be the left edge + of the track controls, and the upper left edge of our own window. + */ + + get_window()->get_origin (discard, yroot); + controls_layout.get_window()->get_origin (xroot, discard); + controls_width = controls_layout.get_width(); + + GdkRectangle lr; + GdkRectangle intersection; + + lr.x = 0; + lr.y = resize_line_y; + lr.width = controls_width + (int) canvas_width; + lr.height = 3; + + if (gdk_rectangle_intersect (&lr, &ev->area, &intersection)) { + + Glib::RefPtr style (get_style()); + Glib::RefPtr black_gc (style->get_black_gc ()); + Glib::RefPtr gc = wrap (black_gc->gobj_copy(), false); + + /* draw on root window */ + + GdkWindow* win = gdk_get_default_root_window(); + + gc->set_subwindow (Gdk::INCLUDE_INFERIORS); + gc->set_line_attributes (3, Gdk::LINE_SOLID, + Gdk::CAP_NOT_LAST, + Gdk::JOIN_MITER); + + gdk_draw_line (win, gc->gobj(), + 0, + resize_line_y, + (int) canvas_width + controls_width, + resize_line_y); +#if 0 + cerr << "drew line @ " << xroot << ", " << yroot + resize_line_y + << " to " << xroot + (int) canvas_width + controls_width + << ", " << yroot + resize_line_y + << endl; +#endif + old_resize_line_y = resize_line_y; + cerr << "NEXT EXPOSE SHOULD BE AT " << old_resize_line_y << endl; } else { - update_fixed_rulers(); - tempo_map_changed (Change (0)); + cerr << "no intersect with " + << lr.x << ',' << lr.y + << ' ' + << lr.width << " x " << lr.height + << endl; } } - return 0; + //cerr << "--- editor expose\n"; +#endif + + return ret; +} + +static gboolean +_idle_resizer (gpointer arg) +{ + return ((Editor*)arg)->idle_resize (); } -struct EditorOrderTimeAxisSorter { - bool operator() (const TimeAxisView* a, const TimeAxisView* b) const { - return a->order < b->order; - } -}; - void -Editor::sort_track_selection () +Editor::add_to_idle_resize (TimeAxisView* view, uint32_t h) { - EditorOrderTimeAxisSorter cmp; - selection->tracks.sort (cmp); + if (resize_idle_id < 0) { + resize_idle_id = g_idle_add (_idle_resizer, this); + } + + resize_idle_target = h; + + pending_resizes.push_back (view); + + if (selection->selected (view) && !selection->tracks.empty()) { + pending_resizes.insert (pending_resizes.end(), selection->tracks.begin(), selection->tracks.end()); + } } +bool +Editor::idle_resize () +{ + for (vector::iterator i = pending_resizes.begin(); i != pending_resizes.end(); ++i) { + (*i)->idle_resize (resize_idle_target); + } + pending_resizes.clear(); + resize_idle_id = -1; + return false; +}