X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Feditor.cc;h=d19f5f6424d35edac0a1cc6dada990465bf230b8;hb=8fc660e76e50920d744942c241275849b7b9720e;hp=353f4099f49dbde28d3022a18b72d1ae0980d602;hpb=f7bff95fbce75cb0d4c717b1400677edd814f7be;p=ardour.git diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index 353f4099f4..d19f5f6424 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -19,7 +19,6 @@ /* Note: public Editor methods are documented in public_editor.h */ -#define __STDC_LIMIT_MACROS 1 #include #include #include @@ -114,6 +113,10 @@ #include "editor_regions.h" #include "editor_locations.h" #include "editor_snapshots.h" +#include "editor_summary.h" +#include "region_layering_order_editor.h" +#include "mouse_cursors.h" +#include "editor_cursors.h" #include "i18n.h" @@ -135,8 +138,6 @@ using Gtkmm2ext::Keyboard; const double Editor::timebar_height = 15.0; -#include "editor_xpms" - static const gchar *_snap_type_strings[] = { N_("CD Frames"), N_("Timecode Frames"), @@ -147,6 +148,7 @@ static const gchar *_snap_type_strings[] = { N_("Beats/32"), N_("Beats/28"), N_("Beats/24"), + N_("Beats/20"), N_("Beats/16"), N_("Beats/14"), N_("Beats/12"), @@ -200,29 +202,11 @@ static const gchar *_rb_opt_strings[] = { N_("Unpitched percussion with stable notes"), N_("Crisp monophonic instrumental"), N_("Unpitched solo percussion"), + N_("Resample without preserving pitch"), 0 }; #endif -/* Soundfile drag-n-drop */ - -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) { @@ -293,7 +277,9 @@ Editor::Editor () , meters_running(false) , _pending_locate_request (false) , _pending_initial_locate (false) + , _last_cut_copy_source_track (0) + , _region_selection_change_updates_region_list (true) { constructed = false; @@ -312,6 +298,7 @@ Editor::Editor () clicked_crossfadeview = 0; clicked_control_point = 0; last_update_frame = 0; + pre_press_cursor = 0; _drags = new DragManager (this); current_mixer_strip = 0; current_bbt_points = 0; @@ -323,6 +310,7 @@ Editor::Editor () edit_point_strings = I18N (_edit_point_strings); #ifdef USE_RUBBERBAND rb_opt_strings = I18N (_rb_opt_strings); + rb_current_opt = 4; #endif snap_threshold = 5.0; @@ -342,7 +330,6 @@ Editor::Editor () show_gain_after_trim = false; verbose_cursor_on = true; last_item_entered = 0; - last_item_entered_n = 0; have_pending_keyboard_selection = false; _follow_playhead = true; @@ -375,11 +362,8 @@ Editor::Editor () _dragging_edit_point = false; select_new_marker = false; rhythm_ferret = 0; + layering_order_editor = 0; _bundle_manager = 0; - for (ARDOUR::DataType::iterator i = ARDOUR::DataType::begin(); i != ARDOUR::DataType::end(); ++i) { - _global_port_matrix[*i] = 0; - } - allow_vertical_scroll = false; no_save_visual = false; resize_idle_id = -1; @@ -399,6 +383,8 @@ Editor::Editor () frames_per_unit = 2048; /* too early to use reset_zoom () */ + _scroll_callbacks = 0; + zoom_focus = ZoomFocusLeft; set_zoom_focus (ZoomFocusLeft); zoom_range_clock.ValueChanged.connect (sigc::mem_fun(*this, &Editor::zoom_adjustment_changed)); @@ -493,7 +479,7 @@ Editor::Editor () controls_layout.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_release)); controls_layout_size_request_connection = controls_layout.signal_size_request().connect (sigc::mem_fun (*this, &Editor::controls_layout_size_request)); - build_cursors (); + _cursors = new MouseCursors; ArdourCanvas::Canvas* time_pad = manage(new ArdourCanvas::Canvas()); ArdourCanvas::SimpleLine* pad_line_1 = manage(new ArdourCanvas::SimpleLine(*time_pad->root(), @@ -528,7 +514,6 @@ Editor::Editor () edit_packer.set_border_width (0); edit_packer.set_name ("EditorWindow"); - edit_packer.attach (zoom_vbox, 0, 1, 0, 2, SHRINK, FILL, 0, 0); /* labels for the rulers */ edit_packer.attach (ruler_label_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0); /* labels for the marker "tracks" */ @@ -574,27 +559,32 @@ Editor::Editor () the_notebook.show_all (); post_maximal_editor_width = 0; - post_maximal_pane_position = 0; + post_maximal_horizontal_pane_position = 0; + post_maximal_editor_height = 0; + post_maximal_vertical_pane_position = 0; - VPaned *editor_summary_pane = manage(new VPaned()); - editor_summary_pane->pack1(edit_packer); + editor_summary_pane.pack1(edit_packer); Button* summary_arrows_left_left = manage (new Button); summary_arrows_left_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE))); - summary_arrows_left_left->signal_clicked().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left)); + summary_arrows_left_left->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press))); + summary_arrows_left_left->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_release)); Button* summary_arrows_left_right = manage (new Button); summary_arrows_left_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE))); - summary_arrows_left_right->signal_clicked().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right)); + summary_arrows_left_right->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press))); + summary_arrows_left_right->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_release)); VBox* summary_arrows_left = manage (new VBox); summary_arrows_left->pack_start (*summary_arrows_left_left); summary_arrows_left->pack_start (*summary_arrows_left_right); Button* summary_arrows_right_left = manage (new Button); summary_arrows_right_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE))); - summary_arrows_right_left->signal_clicked().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left)); + summary_arrows_right_left->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press))); + summary_arrows_right_left->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_release)); Button* summary_arrows_right_right = manage (new Button); summary_arrows_right_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE))); - summary_arrows_right_right->signal_clicked().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right)); + summary_arrows_right_right->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press))); + summary_arrows_right_right->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_release)); VBox* summary_arrows_right = manage (new VBox); summary_arrows_right->pack_start (*summary_arrows_right_left); summary_arrows_right->pack_start (*summary_arrows_right_right); @@ -608,11 +598,15 @@ Editor::Editor () _summary_hbox.pack_start (*summary_frame, true, true); _summary_hbox.pack_start (*summary_arrows_right, false, false); - editor_summary_pane->pack2 (_summary_hbox); + editor_summary_pane.pack2 (_summary_hbox); - edit_pane.pack1 (*editor_summary_pane, true, true); + edit_pane.pack1 (editor_summary_pane, true, true); edit_pane.pack2 (the_notebook, false, true); + editor_summary_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun (*this, &Editor::pane_allocation_handler), static_cast (&editor_summary_pane))); + + /* XXX: editor_summary_pane might need similar special OS X treatment to the edit_pane */ + edit_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Editor::pane_allocation_handler), static_cast (&edit_pane))); #ifdef GTKOSX Glib::PropertyProxy proxy = edit_pane.property_position(); @@ -650,9 +644,6 @@ Editor::Editor () set_mouse_mode (MouseObject, true); set_edit_point_preference (EditAtMouse, true); - XMLNode* node = ARDOUR_UI::instance()->editor_settings(); - set_state (*node, Stateful::loading_state_version); - _playlist_selector = new PlaylistSelector(); _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast (_playlist_selector))); @@ -693,7 +684,7 @@ Editor::Editor () WindowTitle title(Glib::get_application_name()); title += _("Editor"); set_title (title.get_string()); - set_wmclass (X_("ardour_editor"), "Ardour"); + set_wmclass (X_("ardour_editor"), PROGRAM_NAME); add (vpacker); add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK); @@ -717,10 +708,17 @@ Editor::Editor () TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context()); - _last_normalization_value = 0; + _ignore_region_action = false; + _last_region_menu_was_main = false; + _popup_region_menu_item = 0; + + _show_marker_lines = false; + _over_region_trim_target = false; constructed = true; instant_save (); + + setup_fade_images (); } Editor::~Editor() @@ -736,7 +734,7 @@ Editor::~Editor() image_socket_listener = 0 ; } #endif - + delete _routes; delete _route_groups; delete track_canvas; @@ -768,6 +766,10 @@ Editor::catch_vanishing_regionview (RegionView *rv) if (entered_regionview == rv) { set_entered_regionview (0); } + + if (!_all_region_actions_sensitized) { + sensitize_all_region_actions (true); + } } void @@ -782,7 +784,14 @@ Editor::set_entered_regionview (RegionView* rv) } if ((entered_regionview = rv) != 0) { - entered_regionview->entered (); + entered_regionview->entered (internal_editing ()); + } + + if (!_all_region_actions_sensitized && _last_region_menu_was_main) { + /* This RegionView entry might have changed what region actions + are allowed, so sensitize them all in case a key is pressed. + */ + sensitize_all_region_actions (true); } } @@ -822,8 +831,6 @@ Editor::show_window () tv = (static_cast(*i)); tv->reset_height (); } - - reset_zoom (frames_per_unit); } present (); @@ -854,10 +861,10 @@ Editor::zoom_adjustment_changed () if (fpu < 1.0) { fpu = 1.0; - zoom_range_clock.set ((nframes64_t) floor (fpu * _canvas_width)); + zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width)); } else if (fpu > _session->current_end_frame() / _canvas_width) { fpu = _session->current_end_frame() / _canvas_width; - zoom_range_clock.set ((nframes64_t) floor (fpu * _canvas_width)); + zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width)); } temporal_zoom (fpu); @@ -877,7 +884,7 @@ Editor::control_scroll (float fraction) /* _control_scroll_target is an optional - it acts like a pointer to an nframes64_t, with + it acts like a pointer to an framepos_t, with a operator conversion to boolean to check that it has a value could possibly use playhead_cursor->current_frame to store the @@ -890,12 +897,12 @@ Editor::control_scroll (float fraction) _dragging_playhead = true; } - if ((fraction < 0.0f) && (*_control_scroll_target < (nframes64_t) fabs(step))) { + if ((fraction < 0.0f) && (*_control_scroll_target < (framepos_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 if ((fraction > 0.0f) && (max_framepos - *_control_scroll_target < step)) { + *_control_scroll_target = max_framepos - (current_page_frames()*2); // allow room for slop in where the PH is on the screen } else { - *_control_scroll_target += (nframes64_t) floor (step); + *_control_scroll_target += (framepos_t) floor (step); } /* move visuals, we'll catch up with it later */ @@ -926,7 +933,7 @@ Editor::control_scroll (float fraction) } bool -Editor::deferred_control_scroll (nframes64_t /*target*/) +Editor::deferred_control_scroll (framepos_t /*target*/) { _session->request_locate (*_control_scroll_target, _session->transport_rolling()); // reset for next stream @@ -960,20 +967,23 @@ Editor::on_realize () } void -Editor::map_position_change (nframes64_t frame) +Editor::map_position_change (framepos_t frame) { ENSURE_GUI_THREAD (*this, &Editor::map_position_change, frame) - if (_session == 0 || !_follow_playhead) { + if (_session == 0) { return; } - center_screen (frame); + if (_follow_playhead) { + center_screen (frame); + } + playhead_cursor->set_position (frame); } void -Editor::center_screen (nframes64_t frame) +Editor::center_screen (framepos_t frame) { double page = _canvas_width * frames_per_unit; @@ -986,12 +996,12 @@ Editor::center_screen (nframes64_t frame) } void -Editor::center_screen_internal (nframes64_t frame, float page) +Editor::center_screen_internal (framepos_t frame, float page) { page /= 2; if (frame > page) { - frame -= (nframes64_t) page; + frame -= (framepos_t) page; } else { frame = 0; } @@ -1060,9 +1070,6 @@ Editor::set_session (Session *t) compute_fixed_ruler_scale (); - /* there are never any selected regions at startup */ - sensitize_the_right_region_actions (false); - XMLNode* node = ARDOUR_UI::instance()->editor_settings(); set_state (*node, Stateful::loading_state_version); @@ -1078,6 +1085,7 @@ Editor::set_session (Session *t) but use Gtkmm2ext::UI::instance()->call_slot(); */ + _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), ui_bind(&Editor::step_edit_status_change, this, _1), gui_context()); _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context()); _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context()); _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context()); @@ -1110,7 +1118,7 @@ Editor::set_session (Session *t) Location* loc = _session->locations()->auto_loop_location(); if (loc == 0) { - loc = new Location (0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden)); + loc = new Location (*_session, 0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden)); if (loc->start() == loc->end()) { loc->set_end (loc->start() + 1); } @@ -1123,7 +1131,7 @@ Editor::set_session (Session *t) loc = _session->locations()->auto_punch_location(); if (loc == 0) { - loc = new Location (0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden)); + loc = new Location (*_session, 0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden)); if (loc->start() == loc->end()) { loc->set_end (loc->start() + 1); } @@ -1167,76 +1175,26 @@ Editor::set_session (Session *t) /* register for undo history */ _session->register_with_memento_command_factory(_id, this); + ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated)); + start_updating_meters (); } void -Editor::build_cursors () +Editor::action_pre_activated (Glib::RefPtr const & a) { - using namespace Gdk; - - Gdk::Color mbg ("#000000" ); /* Black */ - Gdk::Color mfg ("#0000ff" ); /* Blue. */ - - { - RefPtr source, mask; - source = Bitmap::create (mag_bits, mag_width, mag_height); - mask = Bitmap::create (magmask_bits, mag_width, mag_height); - zoom_cursor = new Gdk::Cursor (source, mask, mfg, mbg, mag_x_hot, mag_y_hot); - } - - Gdk::Color fbg ("#ffffff" ); - Gdk::Color ffg ("#000000" ); - - { - RefPtr source, mask; - - source = Bitmap::create (fader_cursor_bits, fader_cursor_width, fader_cursor_height); - mask = Bitmap::create (fader_cursor_mask_bits, fader_cursor_width, fader_cursor_height); - fader_cursor = new Gdk::Cursor (source, mask, ffg, fbg, fader_cursor_x_hot, fader_cursor_y_hot); - } - - { - RefPtr source, mask; - source = Bitmap::create (speaker_cursor_bits, speaker_cursor_width, speaker_cursor_height); - 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); - } - + if (a->get_name() == "RegionMenu") { + /* When the main menu's region menu is opened, we setup the actions so that they look right + in the menu. I can't find a way of getting a signal when this menu is subsequently closed, + so we resensitize all region actions when the entered regionview or the region selection + changes. HOWEVER we can't always resensitize on entered_regionview change because that + happens after the region context menu is opened. So we set a flag here, too. - 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); + What a carry on :( + */ + sensitize_the_right_region_actions (); + _last_region_menu_was_main = true; } - - 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 */ @@ -1267,14 +1225,57 @@ Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* i items.push_back (SeparatorElem()); if (Profile->get_sae()) { - items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear))); - items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast))); + + items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear))); + items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast))); + } else { - items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear))); - items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast))); - items.push_back (MenuElem (_("Slow"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogB))); - items.push_back (MenuElem (_("Fast"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogA))); - items.push_back (MenuElem (_("Fastest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Slow))); + + items.push_back ( + ImageMenuElem ( + _("Linear"), + *_fade_in_images[FadeLinear], + sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear) + ) + ); + + dynamic_cast(&items.back())->set_always_show_image (); + + items.push_back ( + ImageMenuElem ( + _("Slowest"), + *_fade_in_images[FadeFast], + sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast) + )); + + dynamic_cast(&items.back())->set_always_show_image (); + + items.push_back ( + ImageMenuElem ( + _("Slow"), + *_fade_in_images[FadeLogB], + sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogB) + )); + + dynamic_cast(&items.back())->set_always_show_image (); + + items.push_back ( + ImageMenuElem ( + _("Fast"), + *_fade_in_images[FadeLogA], + sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogA) + )); + + dynamic_cast(&items.back())->set_always_show_image (); + + items.push_back ( + ImageMenuElem ( + _("Fastest"), + *_fade_in_images[FadeSlow], + sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow) + )); + + dynamic_cast(&items.back())->set_always_show_image (); } break; @@ -1290,14 +1291,55 @@ Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* i items.push_back (SeparatorElem()); if (Profile->get_sae()) { - items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear))); - items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow))); + items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear))); + items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow))); } else { - items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear))); - items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow))); - items.push_back (MenuElem (_("Slow"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogA))); - items.push_back (MenuElem (_("Fast"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogB))); - items.push_back (MenuElem (_("Fastest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Fast))); + + items.push_back ( + ImageMenuElem ( + _("Linear"), + *_fade_out_images[FadeLinear], + sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear) + ) + ); + + dynamic_cast(&items.back())->set_always_show_image (); + + items.push_back ( + ImageMenuElem ( + _("Slowest"), + *_fade_out_images[FadeFast], + sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow) + )); + + dynamic_cast(&items.back())->set_always_show_image (); + + items.push_back ( + ImageMenuElem ( + _("Slow"), + *_fade_out_images[FadeLogB], + sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogA) + )); + + dynamic_cast(&items.back())->set_always_show_image (); + + items.push_back ( + ImageMenuElem ( + _("Fast"), + *_fade_out_images[FadeLogA], + sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogB) + )); + + dynamic_cast(&items.back())->set_always_show_image (); + + items.push_back ( + ImageMenuElem ( + _("Fastest"), + *_fade_out_images[FadeSlow], + sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast) + )); + + dynamic_cast(&items.back())->set_always_show_image (); } break; @@ -1313,16 +1355,18 @@ 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, nframes64_t frame) +Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection) { using namespace Menu_Helpers; - Menu* (Editor::*build_menu_function)(nframes64_t); + Menu* (Editor::*build_menu_function)(); Menu *menu; switch (item_type) { case RegionItem: case RegionViewName: case RegionViewNameHighlight: + case LeftFrameHandle: + case RightFrameHandle: if (with_selection) { build_menu_function = &Editor::build_track_selection_context_menu; } else { @@ -1355,7 +1399,7 @@ Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, return; } - menu = (this->*build_menu_function)(frame); + menu = (this->*build_menu_function)(); menu->set_name ("ArdourContextMenu"); /* now handle specific situations */ @@ -1364,6 +1408,8 @@ Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, case RegionItem: case RegionViewName: case RegionViewNameHighlight: + case LeftFrameHandle: + case RightFrameHandle: if (!with_selection) { if (region_edit_menu_split_item) { if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) { @@ -1425,11 +1471,18 @@ Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, clicked_routeview->build_underlay_menu(menu); } + /* When the region menu is opened, we setup the actions so that they look right + in the menu. + */ + sensitize_the_right_region_actions (); + _last_region_menu_was_main = false; + + menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true)); menu->popup (button, time); } Menu* -Editor::build_track_context_menu (nframes64_t) +Editor::build_track_context_menu () { using namespace Menu_Helpers; @@ -1441,7 +1494,7 @@ Editor::build_track_context_menu (nframes64_t) } Menu* -Editor::build_track_bus_context_menu (nframes64_t) +Editor::build_track_bus_context_menu () { using namespace Menu_Helpers; @@ -1453,34 +1506,31 @@ Editor::build_track_bus_context_menu (nframes64_t) } Menu* -Editor::build_track_region_context_menu (nframes64_t frame) +Editor::build_track_region_context_menu () { using namespace Menu_Helpers; MenuList& edit_items = track_region_context_menu.items(); edit_items.clear(); + /* we've just cleared the track region context menu, so the menu that these + two items were on will have disappeared; stop them dangling. + */ + region_edit_menu_split_item = 0; + region_edit_menu_split_multichannel_item = 0; + RouteTimeAxisView* rtv = dynamic_cast (clicked_axisview); if (rtv) { boost::shared_ptr tr; boost::shared_ptr pl; - if ((tr = rtv->track()) && ((pl = tr->playlist()))) { - Playlist::RegionList* regions = pl->regions_at ((nframes64_t) floor ( (double)frame * tr->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::reverse_iterator i = regions->rbegin(); i != regions->rend(); ++i) { - add_region_context_items (rtv->view(), (*i), edit_items); - } - } - - delete regions; + /* Don't offer a region submenu if we are in internal edit mode, as we don't select regions in this + mode and so offering region context is somewhat confusing. + */ + if ((tr = rtv->track()) && ((pl = tr->playlist())) && !internal_editing()) { + framepos_t const framepos = (framepos_t) floor ((double) get_preferred_edit_position() * tr->speed()); + uint32_t regions_at = pl->count_regions_at (framepos); + add_region_context_items (edit_items, regions_at > 1); } } @@ -1490,7 +1540,7 @@ Editor::build_track_region_context_menu (nframes64_t frame) } Menu* -Editor::build_track_crossfade_context_menu (nframes64_t frame) +Editor::build_track_crossfade_context_menu () { using namespace Menu_Helpers; MenuList& edit_items = track_crossfade_context_menu.items(); @@ -1505,10 +1555,9 @@ Editor::build_track_crossfade_context_menu (nframes64_t frame) if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast (pl)) != 0)) { - Playlist::RegionList* regions = pl->regions_at (frame); AudioPlaylist::Crossfades xfades; - apl->crossfades_at (frame, xfades); + apl->crossfades_at (get_preferred_edit_position (), xfades); bool many = xfades.size() > 1; @@ -1516,18 +1565,9 @@ Editor::build_track_crossfade_context_menu (nframes64_t frame) add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many); } - 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::reverse_iterator i = regions->rbegin(); i != regions->rend(); ++i) { - add_region_context_items (atv->audio_view(), (*i), edit_items); - } - } - delete regions; + framepos_t framepos = (framepos_t) floor ((double) get_preferred_edit_position() * tr->speed()); + uint32_t regions_at = pl->count_regions_at (framepos); + add_region_context_items (edit_items, regions_at > 1); } } @@ -1537,7 +1577,7 @@ Editor::build_track_crossfade_context_menu (nframes64_t frame) } void -Editor::analyze_region_selection() +Editor::analyze_region_selection () { if (analysis_window == 0) { analysis_window = new AnalysisWindow(); @@ -1573,7 +1613,7 @@ Editor::analyze_range_selection() } Menu* -Editor::build_track_selection_context_menu (nframes64_t) +Editor::build_track_selection_context_menu () { using namespace Menu_Helpers; MenuList& edit_items = track_selection_context_menu.items(); @@ -1605,7 +1645,7 @@ Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_pt } items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr (xfade)))); - items.push_back (MenuElem (_("Edit"), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr (xfade)))); + items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr (xfade)))); if (xfade->can_follow_overlap()) { @@ -1647,221 +1687,10 @@ Editor::xfade_edit_right_region () } void -Editor::add_region_context_items (StreamView* sv, boost::shared_ptr region, Menu_Helpers::MenuList& edit_items) +Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, bool multiple_regions_at_position) { using namespace Menu_Helpers; - Gtk::MenuItem* foo_item; - Menu *region_menu = manage (new Menu); - MenuList& items = region_menu->items(); - 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. - */ - - region_menu->signal_map_event().connect ( - sigc::bind (sigc::mem_fun(*this, &Editor::set_selected_regionview_from_map_event), sv, boost::weak_ptr(region))); - - items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &Editor::rename_region))); - if (mr && internal_editing()) { - items.push_back (MenuElem (_("List editor..."), sigc::mem_fun(*this, &Editor::show_midi_list_editor))); - } else { - items.push_back (MenuElem (_("Region Properties..."), sigc::mem_fun(*this, &Editor::edit_region))); - } - } - - items.push_back (MenuElem (_("Raise to Top Layer"), sigc::mem_fun(*this, &Editor::raise_region_to_top))); - items.push_back (MenuElem (_("Lower to Bottom Layer"), sigc::mem_fun (*this, &Editor::lower_region_to_bottom))); - items.push_back (SeparatorElem()); - items.push_back (MenuElem (_("Define Sync Point"), sigc::mem_fun(*this, &Editor::set_region_sync_from_edit_point))); - if (_edit_point == EditAtMouse) { - items.back ().set_sensitive (false); - } - items.push_back (MenuElem (_("Remove Sync Point"), sigc::mem_fun(*this, &Editor::remove_region_sync))); - items.push_back (SeparatorElem()); - - items.push_back (MenuElem (_("Audition"), sigc::mem_fun(*this, &Editor::play_selected_region))); - items.push_back (MenuElem (_("Export..."), sigc::mem_fun(*this, &Editor::export_region))); - items.push_back (MenuElem (_("Bounce"), sigc::mem_fun(*this, &Editor::bounce_region_selection))); - - if (ar) { - items.push_back (MenuElem (_("Spectral Analysis..."), sigc::mem_fun(*this, &Editor::analyze_region_selection))); - } - - items.push_back (SeparatorElem()); - - 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(); - } - region_lock_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_region_lock)); - - items.push_back (CheckMenuElem (_("Glue to Bars and 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 (sigc::mem_fun (*this, &Editor::toggle_region_lock_style)); - - items.push_back (CheckMenuElem (_("Mute"))); - CheckMenuItem* region_mute_item = static_cast(&items.back()); - fooc = region_mute_item->signal_activate().connect (sigc::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 (MenuElem (_("Transpose..."), mem_fun(*this, &Editor::pitch_shift_regions))); - - if (!Profile->get_sae()) { - items.push_back (CheckMenuElem (_("Opaque"))); - CheckMenuItem* region_opaque_item = static_cast(&items.back()); - fooc = region_opaque_item->signal_activate().connect (sigc::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"), sigc::mem_fun(*this, &Editor::naturalize))); - 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); - - if (!Profile->get_sae()) { - items.push_back (MenuElem (_("Reset Envelope"), sigc::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 (sigc::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 (sigc::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); - } - - items.push_back (SeparatorElem()); - } - - items.push_back (MenuElem (_("Normalize..."), sigc::mem_fun(*this, &Editor::normalize_region))); - if (ar->scale_amplitude() != 1) { - items.push_back (MenuElem (_("Reset Gain"), sigc::mem_fun(*this, &Editor::reset_region_scale_amplitude))); - } - - } else if (mr) { - items.push_back (MenuElem (_("Quantize"), sigc::mem_fun(*this, &Editor::quantize_region))); - items.push_back (MenuElem (_("Fork"), sigc::mem_fun(*this, &Editor::fork_region))); - items.push_back (SeparatorElem()); - } - - items.push_back (MenuElem (_("Strip Silence..."), sigc::mem_fun (*this, &Editor::strip_region_silence))); - items.push_back (MenuElem (_("Reverse"), sigc::mem_fun(*this, &Editor::reverse_region))); - items.push_back (SeparatorElem()); - - /* range related stuff */ - - items.push_back (MenuElem (_("Add Single Range"), sigc::mem_fun (*this, &Editor::add_location_from_audio_region))); - items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_locations_from_audio_region))); - if (selection->regions.size() < 2) { - items.back().set_sensitive (false); - } - - items.push_back (MenuElem (_("Set Range Selection"), sigc::mem_fun (*this, &Editor::set_selection_from_region))); - items.push_back (SeparatorElem()); - - /* Nudge region */ - - Menu *nudge_menu = manage (new Menu()); - MenuList& nudge_items = nudge_menu->items(); - nudge_menu->set_name ("ArdourContextMenu"); - - nudge_items.push_back (MenuElem (_("Nudge Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_forward), false, false)))); - nudge_items.push_back (MenuElem (_("Nudge Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_backward), false, false)))); - nudge_items.push_back (MenuElem (_("Nudge Forward by Capture Offset"), (sigc::mem_fun(*this, &Editor::nudge_forward_capture_offset)))); - nudge_items.push_back (MenuElem (_("Nudge Backward by Capture Offset"), (sigc::mem_fun(*this, &Editor::nudge_backward_capture_offset)))); - - items.push_back (MenuElem (_("Nudge"), *nudge_menu)); - items.push_back (SeparatorElem()); - - Menu *trim_menu = manage (new Menu); - MenuList& trim_items = trim_menu->items(); - trim_menu->set_name ("ArdourContextMenu"); - - trim_items.push_back (MenuElem (_("Start to Edit Point"), sigc::mem_fun(*this, &Editor::trim_region_from_edit_point))); - foo_item = &trim_items.back(); - if (_edit_point == EditAtMouse) { - foo_item->set_sensitive (false); - } - trim_items.push_back (MenuElem (_("Edit Point to End"), sigc::mem_fun(*this, &Editor::trim_region_to_edit_point))); - foo_item = &trim_items.back(); - if (_edit_point == EditAtMouse) { - foo_item->set_sensitive (false); - } - trim_items.push_back (MenuElem (_("Trim to Loop"), sigc::mem_fun(*this, &Editor::trim_region_to_loop))); - trim_items.push_back (MenuElem (_("Trim to Punch"), sigc::mem_fun(*this, &Editor::trim_region_to_punch))); - - items.push_back (MenuElem (_("Trim"), *trim_menu)); - items.push_back (SeparatorElem()); - - items.push_back (MenuElem (_("Split"), (sigc::mem_fun(*this, &Editor::split)))); - region_edit_menu_split_item = &items.back(); - - if (_edit_point == EditAtMouse) { - region_edit_menu_split_item->set_sensitive (false); - } - - items.push_back (MenuElem (_("Make Mono Regions"), (sigc::mem_fun(*this, &Editor::split_multichannel_region)))); - region_edit_menu_split_multichannel_item = &items.back(); - - items.push_back (MenuElem (_("Duplicate"), (sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)))); - items.push_back (MenuElem (_("Multi-Duplicate..."), (sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), true)))); - items.push_back (MenuElem (_("Fill Track"), (sigc::mem_fun(*this, &Editor::region_fill_track)))); - items.push_back (SeparatorElem()); - items.push_back (MenuElem (_("Remove"), sigc::mem_fun(*this, &Editor::remove_selected_regions))); - + /* OK, stick the region submenu at the top of the list, and then add the standard items. */ @@ -1870,15 +1699,28 @@ Editor::add_region_context_items (StreamView* sv, boost::shared_ptr regi meaning for menu titles. */ + RegionSelection rs = get_regions_from_selection_and_entered (); + string::size_type pos = 0; - string menu_item_name = (region) ? region->name() : _("Selected Regions"); + string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions"); while ((pos = menu_item_name.find ("_", pos)) != string::npos) { menu_item_name.replace (pos, 1, "__"); pos += 2; } - edit_items.push_back (MenuElem (menu_item_name, *region_menu)); + if (_popup_region_menu_item == 0) { + _popup_region_menu_item = new MenuItem (menu_item_name); + _popup_region_menu_item->set_submenu (*dynamic_cast (ActionManager::get_widget (X_("/PopupRegionMenu")))); + _popup_region_menu_item->show (); + } else { + _popup_region_menu_item->set_label (menu_item_name); + } + + edit_items.push_back (*_popup_region_menu_item); + if (multiple_regions_at_position && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) { + edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region")->create_menu_item ())); + } edit_items.push_back (SeparatorElem()); } @@ -1903,7 +1745,7 @@ Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items) } edit_items.push_back (SeparatorElem()); - edit_items.push_back (MenuElem (_("Silence Range"), sigc::mem_fun(*this, &Editor::separate_region_from_selection))); + edit_items.push_back (MenuElem (_("Convert to region in-place"), mem_fun(*this, &Editor::separate_region_from_selection))); edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection))); edit_items.push_back (SeparatorElem()); @@ -1926,7 +1768,7 @@ Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items) edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true))); edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false))); edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true))); - edit_items.push_back (MenuElem (_("Export Range"), sigc::mem_fun(*this, &Editor::export_range))); + edit_items.push_back (MenuElem (_("Export Range"), sigc::mem_fun(*this, &Editor::export_selection))); } @@ -1945,7 +1787,7 @@ Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items) play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start))); play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region))); play_items.push_back (SeparatorElem()); - play_items.push_back (MenuElem (_("Loop Region"), sigc::mem_fun(*this, &Editor::loop_selected_region))); + play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true))); edit_items.push_back (MenuElem (_("Play"), *play_menu)); @@ -1985,8 +1827,8 @@ Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items) cutnpaste_items.push_back (SeparatorElem()); - cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun(*this, &Editor::align), ARDOUR::SyncPoint))); - cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun(*this, &Editor::align_relative), ARDOUR::SyncPoint))); + cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint))); + cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint))); edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu)); @@ -2103,6 +1945,7 @@ Editor::set_snap_to (SnapType st) case SnapToBeatDiv32: case SnapToBeatDiv28: case SnapToBeatDiv24: + case SnapToBeatDiv20: case SnapToBeatDiv16: case SnapToBeatDiv14: case SnapToBeatDiv12: @@ -2182,7 +2025,7 @@ Editor::set_edit_point_preference (EditPoint ep, bool force) Glib::RefPtr::cast_dynamic(act)->set_active (true); } - nframes64_t foo; + framepos_t foo; bool in_track_canvas; if (!mouse_frame (foo, in_track_canvas)) { @@ -2262,7 +2105,7 @@ Editor::set_state (const XMLNode& node, int /*version*/) move (x, y); if (_session && (prop = node.property ("playhead"))) { - nframes64_t pos; + framepos_t pos; sscanf (prop->value().c_str(), "%" PRIi64, &pos); playhead_cursor->set_position (pos); } else { @@ -2279,6 +2122,8 @@ Editor::set_state (const XMLNode& node, int /*version*/) if ((prop = node.property ("zoom"))) { reset_zoom (PBD::atof (prop->value())); + } else { + reset_zoom (frames_per_unit); } if ((prop = node.property ("snap-to"))) { @@ -2297,7 +2142,7 @@ Editor::set_state (const XMLNode& node, int /*version*/) } if ((prop = node.property ("left-frame")) != 0){ - nframes64_t pos; + framepos_t pos; if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) { reset_x_origin (pos); } @@ -2327,7 +2172,7 @@ Editor::set_state (const XMLNode& node, int /*version*/) if ((prop = node.property ("show-measures"))) { bool yn = string_is_affirmative (prop->value()); - _show_measures = !yn; + _show_measures = yn; RefPtr act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility")); if (act) { RefPtr tact = RefPtr::cast_dynamic(act); @@ -2375,40 +2220,49 @@ Editor::set_state (const XMLNode& node, int /*version*/) if ((prop = node.property ("show-editor-mixer"))) { Glib::RefPtr act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer")); - if (act) { - - Glib::RefPtr tact = Glib::RefPtr::cast_dynamic(act); - bool yn = string_is_affirmative (prop->value()); + assert (act); - /* do it twice to force the change */ - - tact->set_active (!yn); - tact->set_active (yn); - } + Glib::RefPtr tact = Glib::RefPtr::cast_dynamic(act); + bool yn = string_is_affirmative (prop->value()); + + /* do it twice to force the change */ + + tact->set_active (!yn); + tact->set_active (yn); } 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 = string_is_affirmative (prop->value()); + assert (act); - /* do it twice to force the change */ - - tact->set_active (!yn); - tact->set_active (yn); - } + Glib::RefPtr tact = Glib::RefPtr::cast_dynamic(act); + bool yn = string_is_affirmative (prop->value()); + + /* do it twice to force the change */ + + tact->set_active (!yn); + tact->set_active (yn); } if ((prop = node.property (X_("editor-list-page")))) { the_notebook.set_current_page (atoi (prop->value ())); } - if ((prop = node.property (X_("editor-pane-position")))) { - edit_pane.set_position (atoi (prop->value ())); + if ((prop = node.property (X_("show-marker-lines")))) { + Glib::RefPtr act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines")); + assert (act); + Glib::RefPtr tact = Glib::RefPtr::cast_dynamic (act); + bool yn = string_is_affirmative (prop->value ()); + + tact->set_active (!yn); + tact->set_active (yn); + } + + XMLNodeList children = node.children (); + for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) { + selection->set_state (**i, Stateful::current_state_version); + _regions->set_state (**i); } return 0; @@ -2446,7 +2300,9 @@ Editor::get_state () snprintf(buf, sizeof(buf), "%d", yoff); geometry->add_property("y-off", string(buf)); snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast(&edit_pane)->gobj())); - geometry->add_property("edit_pane_pos", string(buf)); + geometry->add_property("edit-horizontal-pane-pos", string(buf)); + snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast(&editor_summary_pane)->gobj())); + geometry->add_property("edit-vertical-pane-pos", string(buf)); node->add_child_nocopy (*geometry); } @@ -2495,9 +2351,11 @@ Editor::get_state () snprintf (buf, sizeof (buf), "%d", the_notebook.get_current_page ()); node->add_property (X_("editor-list-page"), buf); - snprintf (buf, sizeof (buf), "%d", edit_pane.get_position ()); - node->add_property (X_("editor-pane-position"), buf); + node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no"); + node->add_child_nocopy (selection->get_state ()); + node->add_child_nocopy (_regions->get_state ()); + return *node; } @@ -2528,7 +2386,7 @@ Editor::trackview_by_y_position (double y) * @param event Event to get current key modifier information from, or 0. */ void -Editor::snap_to_with_modifier (nframes64_t& start, GdkEvent const * event, int32_t direction, bool for_mark) +Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_t direction, bool for_mark) { if (!_session || !event) { return; @@ -2546,7 +2404,7 @@ Editor::snap_to_with_modifier (nframes64_t& start, GdkEvent const * event, int32 } void -Editor::snap_to (nframes64_t& start, int32_t direction, bool for_mark) +Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark) { if (!_session || _snap_mode == SnapOff) { return; @@ -2556,17 +2414,17 @@ Editor::snap_to (nframes64_t& start, int32_t direction, bool for_mark) } void -Editor::timecode_snap_to_internal (nframes64_t& start, int32_t direction, bool /*for_mark*/) +Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /*for_mark*/) { - const nframes64_t one_timecode_second = (nframes64_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame()); - nframes64_t one_timecode_minute = (nframes64_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60); + const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame()); + framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60); switch (_snap_type) { case SnapToTimecodeFrame: if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) { - start = (nframes64_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame()); + start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame()); } else { - start = (nframes64_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame()); + start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame()); } break; @@ -2578,9 +2436,9 @@ Editor::timecode_snap_to_internal (nframes64_t& start, int32_t direction, bool / start -= _session->timecode_offset (); } if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) { - start = (nframes64_t) ceil ((double) start / one_timecode_second) * one_timecode_second; + start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second; } else { - start = (nframes64_t) floor ((double) start / one_timecode_second) * one_timecode_second; + start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second; } if (_session->timecode_offset_negative()) @@ -2599,9 +2457,9 @@ Editor::timecode_snap_to_internal (nframes64_t& start, int32_t direction, bool / start -= _session->timecode_offset (); } if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) { - start = (nframes64_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute; + start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute; } else { - start = (nframes64_t) floor ((double) start / one_timecode_minute) * one_timecode_minute; + start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute; } if (_session->timecode_offset_negative()) { @@ -2617,13 +2475,13 @@ Editor::timecode_snap_to_internal (nframes64_t& start, int32_t direction, bool / } void -Editor::snap_to_internal (nframes64_t& start, int32_t direction, bool for_mark) +Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark) { - const nframes64_t one_second = _session->frame_rate(); - const nframes64_t one_minute = _session->frame_rate() * 60; - nframes64_t presnap = start; - nframes64_t before; - nframes64_t after; + const framepos_t one_second = _session->frame_rate(); + const framepos_t one_minute = _session->frame_rate() * 60; + framepos_t presnap = start; + framepos_t before; + framepos_t after; switch (_snap_type) { case SnapToTimecodeFrame: @@ -2633,25 +2491,25 @@ Editor::snap_to_internal (nframes64_t& start, int32_t direction, bool for_mark) case SnapToCDFrame: 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); + start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75); } else { - start = (nframes64_t) floor ((double) start / (one_second / 75)) * (one_second / 75); + start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75); } break; case SnapToSeconds: if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) { - start = (nframes64_t) ceil ((double) start / one_second) * one_second; + start = (framepos_t) ceil ((double) start / one_second) * one_second; } else { - start = (nframes64_t) floor ((double) start / one_second) * one_second; + start = (framepos_t) floor ((double) start / one_second) * one_second; } break; case SnapToMinutes: if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) { - start = (nframes64_t) ceil ((double) start / one_minute) * one_minute; + start = (framepos_t) ceil ((double) start / one_minute) * one_minute; } else { - start = (nframes64_t) floor ((double) start / one_minute) * one_minute; + start = (framepos_t) floor ((double) start / one_minute) * one_minute; } break; @@ -2672,6 +2530,9 @@ Editor::snap_to_internal (nframes64_t& start, int32_t direction, bool for_mark) case SnapToBeatDiv24: start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction); break; + case SnapToBeatDiv20: + start = _session->tempo_map().round_to_beat_subdivision (start, 20, direction); + break; case SnapToBeatDiv16: start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction); break; @@ -2713,11 +2574,11 @@ Editor::snap_to_internal (nframes64_t& start, int32_t direction, bool for_mark) _session->locations()->marks_either_side (start, before, after); - if (before == max_frames) { + if (before == max_framepos) { start = after; - } else if (after == max_frames) { + } else if (after == max_framepos) { start = before; - } else if (before != max_frames && after != max_frames) { + } else if (before != max_framepos && after != max_framepos) { /* have before and after */ if ((start - before) < (after - start)) { start = before; @@ -2734,8 +2595,8 @@ Editor::snap_to_internal (nframes64_t& start, int32_t direction, bool for_mark) case SnapToRegionBoundary: if (!region_boundary_cache.empty()) { - vector::iterator prev = region_boundary_cache.end (); - vector::iterator next = region_boundary_cache.end (); + vector::iterator prev = region_boundary_cache.end (); + vector::iterator next = region_boundary_cache.end (); if (direction > 0) { next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start); @@ -2748,8 +2609,8 @@ Editor::snap_to_internal (nframes64_t& start, int32_t direction, bool for_mark) prev--; } - nframes64_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev; - nframes64_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next; + framepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev; + framepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next; if (start > (p + n) / 2) { start = n; @@ -2886,8 +2747,8 @@ Editor::setup_toolbar () /* Zoom */ - zoom_box.set_spacing (1); - zoom_box.set_border_width (0); + _zoom_box.set_spacing (1); + _zoom_box.set_border_width (0); zoom_in_button.set_name ("EditorTimeButton"); zoom_in_button.set_image (*(manage (new Image (Stock::ZOOM_IN, Gtk::ICON_SIZE_MENU)))); @@ -2905,10 +2766,12 @@ Editor::setup_toolbar () set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true); zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done)); - zoom_box.pack_start (zoom_out_button, false, false); - zoom_box.pack_start (zoom_in_button, false, false); - zoom_box.pack_start (zoom_out_full_button, false, false); + _zoom_box.pack_start (zoom_out_button, false, false); + _zoom_box.pack_start (zoom_in_button, false, false); + _zoom_box.pack_start (zoom_out_full_button, false, false); + _zoom_box.pack_start (zoom_focus_selector); + /* Track zoom buttons */ tav_expand_button.set_name ("TrackHeightButton"); tav_expand_button.set_size_request(-1,20); @@ -2920,18 +2783,20 @@ Editor::setup_toolbar () tav_shrink_button.add (*(manage (new Image (::get_icon("tav_shrink"))))); tav_shrink_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), false)); - track_zoom_box.set_spacing (1); - track_zoom_box.set_border_width (0); - - track_zoom_box.pack_start (tav_shrink_button, false, false); - track_zoom_box.pack_start (tav_expand_button, false, false); - - HBox* zbc = manage (new HBox); - zbc->pack_start (zoom_focus_selector, PACK_SHRINK); - zoom_vbox.pack_start (*zbc, PACK_SHRINK); - zoom_vbox.pack_start (zoom_box, PACK_SHRINK); - zoom_vbox.pack_start (track_zoom_box, PACK_SHRINK); - + _zoom_box.pack_start (tav_shrink_button); + _zoom_box.pack_start (tav_expand_button); + + _zoom_tearoff = manage (new TearOff (_zoom_box)); + + _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast(&toolbar_hbox), + &_zoom_tearoff->tearoff_window())); + _zoom_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast (&toolbar_hbox), + &_zoom_tearoff->tearoff_window(), 0)); + _zoom_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast(&toolbar_hbox), + &_zoom_tearoff->tearoff_window())); + _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast (&toolbar_hbox), + &_zoom_tearoff->tearoff_window(), 0)); + snap_box.set_spacing (1); snap_box.set_border_width (2); @@ -2991,6 +2856,7 @@ Editor::setup_toolbar () toolbar_hbox.set_border_width (1); toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false); + toolbar_hbox.pack_start (*_zoom_tearoff, false, false); toolbar_hbox.pack_start (*_tools_tearoff, false, false); hbox->pack_start (snap_box, false, false); @@ -3011,13 +2877,13 @@ void Editor::setup_tooltips () { ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects")); - ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Gain Automation")); + ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain")); ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range")); - ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions")); + ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes")); ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions")); ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges")); ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)")); - ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: context-click for possible operations")); + ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations")); ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards")); ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards")); ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In")); @@ -3031,6 +2897,7 @@ Editor::setup_tooltips () ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point")); ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes")); ARDOUR_UI::instance()->set_tip (midi_panic_button, _("Send note off and reset controller messages on all MIDI channels")); + ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode")); } void @@ -3065,7 +2932,7 @@ Editor::setup_midi_toolbar () int Editor::convert_drop_to_paths ( - vector& paths, + vector& paths, const RefPtr& /*context*/, gint /*x*/, gint /*y*/, @@ -3076,8 +2943,8 @@ Editor::convert_drop_to_paths ( if (_session == 0) { return -1; } - - vector uris = data.get_uris(); + + vector uris = data.get_uris(); if (uris.empty()) { @@ -3095,7 +2962,7 @@ Editor::convert_drop_to_paths ( THERE MAY BE NO NULL TERMINATING CHAR!!! */ - ustring txt = data.get_text(); + string txt = data.get_text(); const char* p; const char* q; @@ -3123,7 +2990,7 @@ Editor::convert_drop_to_paths ( if (q > p) { - uris.push_back (ustring (p, q - p + 1)); + uris.push_back (string (p, q - p + 1)); } } } @@ -3139,18 +3006,18 @@ Editor::convert_drop_to_paths ( } } - for (vector::iterator i = uris.begin(); i != uris.end(); ++i) { + for (vector::iterator i = uris.begin(); i != uris.end(); ++i) { if ((*i).substr (0,7) == "file://") { - ustring p = *i; + string p = *i; PBD::url_decode (p); // scan forward past three slashes - ustring::size_type slashcnt = 0; - ustring::size_type n = 0; - ustring::iterator x = p.begin(); + string::size_type slashcnt = 0; + string::size_type n = 0; + string::iterator x = p.begin(); while (slashcnt < 3 && x != p.end()) { if ((*x) == '/') { @@ -3279,14 +3146,10 @@ Editor::duplicate_dialog (bool with_dialog) } } - RegionSelection rs; - get_regions_for_action (rs); - - if (mouse_mode != MouseRange) { + RegionSelection rs = get_regions_from_selection_and_entered (); - if (rs.empty()) { - return; - } + if (mouse_mode != MouseRange && rs.empty()) { + return; } if (with_dialog) { @@ -3377,7 +3240,7 @@ Editor::clamp_verbose_cursor_y (double y) } void -Editor::show_verbose_canvas_cursor_with (const string & txt) +Editor::show_verbose_canvas_cursor_with (const string & txt, int32_t xoffset, int32_t yoffset) { verbose_canvas_cursor->property_text() = txt.c_str(); @@ -3387,6 +3250,9 @@ Editor::show_verbose_canvas_cursor_with (const string & txt) track_canvas->get_pointer (x, y); track_canvas->window_to_world (x, y, wx, wy); + wx += xoffset; + wy += yoffset; + /* don't get too close to the edge */ verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (wx); verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (wy); @@ -3438,22 +3304,7 @@ Editor::cycle_edit_mode () void Editor::edit_mode_selection_done () { - if (_session == 0) { - return; - } - - 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; - } - - Config->set_edit_mode (mode); + Config->set_edit_mode (string_to_edit_mode (edit_mode_selector.get_active_text ())); } void @@ -3484,6 +3335,8 @@ Editor::snap_type_selection_done () snaptype = SnapToBeatDiv14; } else if (choice == _("Beats/16")) { snaptype = SnapToBeatDiv16; + } else if (choice == _("Beats/20")) { + snaptype = SnapToBeatDiv20; } else if (choice == _("Beats/24")) { snaptype = SnapToBeatDiv24; } else if (choice == _("Beats/28")) { @@ -3670,7 +3523,14 @@ Editor::pane_allocation_handler (Allocation &alloc, Paned* which) char buf[32]; XMLNode* node = ARDOUR_UI::instance()->editor_settings(); int width, height; - static int32_t done; + + enum Pane { + Horizontal = 0x1, + Vertical = 0x2 + }; + + static Pane done; + XMLNode* geometry; width = default_width; @@ -3678,15 +3538,11 @@ Editor::pane_allocation_handler (Allocation &alloc, Paned* which) if ((geometry = find_named_node (*node, "geometry")) != 0) { - if ((prop = geometry->property ("x_size")) == 0) { - prop = geometry->property ("x-size"); - } + prop = geometry->property ("x-size"); if (prop) { width = atoi (prop->value()); } - if ((prop = geometry->property ("y_size")) == 0) { - prop = geometry->property ("y-size"); - } + prop = geometry->property ("y-size"); if (prop) { height = atoi (prop->value()); } @@ -3694,11 +3550,11 @@ Editor::pane_allocation_handler (Allocation &alloc, Paned* which) if (which == static_cast (&edit_pane)) { - if (done) { + if (done & Horizontal) { return; } - if (!geometry || (prop = geometry->property ("edit-pane-pos")) == 0) { + if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) { /* initial allocation is 90% to canvas, 10% to notebook */ pos = (int) floor (alloc.get_width() * 0.90f); snprintf (buf, sizeof(buf), "%d", pos); @@ -3706,10 +3562,33 @@ Editor::pane_allocation_handler (Allocation &alloc, Paned* which) pos = atoi (prop->value()); } - if ((done = GTK_WIDGET(edit_pane.gobj())->allocation.width > pos)) { + if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) { edit_pane.set_position (pos); - pre_maximal_pane_position = pos; + pre_maximal_horizontal_pane_position = pos; + } + + done = (Pane) (done | Horizontal); + + } else if (which == static_cast (&editor_summary_pane)) { + + if (done & Vertical) { + return; + } + + if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) { + /* initial allocation is 90% to canvas, 10% to summary */ + pos = (int) floor (alloc.get_height() * 0.90f); + snprintf (buf, sizeof(buf), "%d", pos); + } else { + pos = atoi (prop->value()); + } + + if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) { + editor_summary_pane.set_position (pos); + pre_maximal_vertical_pane_position = pos; } + + done = (Pane) (done | Vertical); } } @@ -3841,7 +3720,7 @@ Editor::playlist_selector () const } Evoral::MusicalTime -Editor::get_grid_type_as_beats (bool& success, nframes64_t position) +Editor::get_grid_type_as_beats (bool& success, framepos_t position) { success = true; @@ -3859,6 +3738,9 @@ Editor::get_grid_type_as_beats (bool& success, nframes64_t position) case SnapToBeatDiv24: return 1.0/24.0; break; + case SnapToBeatDiv20: + return 1.0/20.0; + break; case SnapToBeatDiv16: return 1.0/16.0; break; @@ -3917,10 +3799,10 @@ Editor::get_grid_type_as_beats (bool& success, nframes64_t position) return 0.0; } -nframes64_t -Editor::get_nudge_distance (nframes64_t pos, nframes64_t& next) +framecnt_t +Editor::get_nudge_distance (framepos_t pos, framecnt_t& next) { - nframes64_t ret; + framecnt_t ret; ret = nudge_clock.current_duration (pos); next = ret + 1; /* XXXX fix me */ @@ -3965,7 +3847,7 @@ Editor::playlist_deletion_dialog (boost::shared_ptr pl) } bool -Editor::audio_region_selection_covers (nframes64_t where) +Editor::audio_region_selection_covers (framepos_t where) { for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) { if ((*a)->region()->covers (where)) { @@ -4040,10 +3922,8 @@ Editor::control_layout_scroll (GdkEventScroll* ev) } void -Editor::session_state_saved (string snap_name) +Editor::session_state_saved (string) { - ENSURE_GUI_THREAD (*this, &Editor::session_state_saved, snap_name); - update_title (); _snapshots->redisplay (); } @@ -4053,40 +3933,68 @@ Editor::maximise_editing_space () { _mouse_mode_tearoff->set_visible (false); _tools_tearoff->set_visible (false); + _zoom_tearoff->set_visible (false); - pre_maximal_pane_position = edit_pane.get_position(); - pre_maximal_editor_width = this->get_width(); + pre_maximal_horizontal_pane_position = edit_pane.get_position (); + pre_maximal_vertical_pane_position = editor_summary_pane.get_position (); + pre_maximal_editor_width = this->get_width (); + pre_maximal_editor_height = this->get_height (); - if(post_maximal_pane_position == 0) { - post_maximal_pane_position = edit_pane.get_width(); + if (post_maximal_horizontal_pane_position == 0) { + post_maximal_horizontal_pane_position = edit_pane.get_width(); } - fullscreen(); + if (post_maximal_vertical_pane_position == 0) { + post_maximal_vertical_pane_position = editor_summary_pane.get_height(); + } + + fullscreen (); - if(post_maximal_editor_width) { - edit_pane.set_position (post_maximal_pane_position - + if (post_maximal_editor_width) { + edit_pane.set_position (post_maximal_horizontal_pane_position - abs(post_maximal_editor_width - pre_maximal_editor_width)); } else { - edit_pane.set_position (post_maximal_pane_position); + edit_pane.set_position (post_maximal_horizontal_pane_position); } + + if (post_maximal_editor_height) { + editor_summary_pane.set_position (post_maximal_vertical_pane_position - + abs(post_maximal_editor_height - pre_maximal_editor_height)); + } else { + editor_summary_pane.set_position (post_maximal_vertical_pane_position); + } + + if (Config->get_keep_tearoffs()) { + _mouse_mode_tearoff->set_visible (true); + _tools_tearoff->set_visible (true); + _zoom_tearoff->set_visible (true); + } + } void Editor::restore_editing_space () { - // user changed width of pane during fullscreen + // user changed width/height of panes during fullscreen - if(post_maximal_pane_position != edit_pane.get_position()) { - post_maximal_pane_position = edit_pane.get_position(); + if (post_maximal_horizontal_pane_position != edit_pane.get_position()) { + post_maximal_horizontal_pane_position = edit_pane.get_position(); } + if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) { + post_maximal_vertical_pane_position = editor_summary_pane.get_position(); + } + unfullscreen(); _mouse_mode_tearoff->set_visible (true); _tools_tearoff->set_visible (true); + _zoom_tearoff->set_visible (true); post_maximal_editor_width = this->get_width(); + post_maximal_editor_height = this->get_height(); - edit_pane.set_position (pre_maximal_pane_position + abs(this->get_width() - pre_maximal_editor_width)); + edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width)); + editor_summary_pane.set_position (pre_maximal_vertical_pane_position + abs(this->get_height() - pre_maximal_editor_height)); } /** @@ -4171,7 +4079,7 @@ Editor::on_key_release_event (GdkEventKey* ev) * @param frame New x origin. */ void -Editor::reset_x_origin (nframes64_t frame) +Editor::reset_x_origin (framepos_t frame) { queue_visual_change (frame); } @@ -4189,7 +4097,7 @@ Editor::reset_zoom (double fpu) } void -Editor::reposition_and_zoom (nframes64_t frame, double fpu) +Editor::reposition_and_zoom (framepos_t frame, double fpu) { reset_x_origin (frame); reset_zoom (fpu); @@ -4307,7 +4215,7 @@ Editor::set_frames_per_unit (double fpu) of frames into an 800 pixel wide space. */ - if (max_frames / fpu < 800.0) { + if (max_framepos / fpu < 800.0) { return; } @@ -4323,7 +4231,7 @@ Editor::post_zoom () { // convert fpu to frame count - nframes64_t frames = (nframes64_t) floor (frames_per_unit * _canvas_width); + framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width); if (frames_per_unit != zoom_range_clock.current_duration()) { zoom_range_clock.set (frames); @@ -4346,11 +4254,13 @@ Editor::post_zoom () refresh_location_display(); _summary->set_overlays_dirty (); + update_marker_labels (); + instant_save (); } void -Editor::queue_visual_change (nframes64_t where) +Editor::queue_visual_change (framepos_t where) { pending_visual_change.add (VisualChange::TimeOrigin); pending_visual_change.time_origin = where; @@ -4395,7 +4305,19 @@ Editor::idle_visual_changer () VisualChange::Type p = pending_visual_change.pending; pending_visual_change.pending = (VisualChange::Type) 0; - double last_time_origin = horizontal_position (); + double const last_time_origin = horizontal_position (); + + if (p & VisualChange::TimeOrigin) { + /* This is a bit of a hack, but set_frames_per_unit + below will (if called) end up with the + CrossfadeViews looking at Editor::leftmost_frame, + and if we're changing origin and zoom in the same + operation it will be the wrong value unless we + update it here. + */ + + leftmost_frame = pending_visual_change.time_origin; + } if (p & VisualChange::ZoomLevel) { set_frames_per_unit (pending_visual_change.frames_per_unit); @@ -4442,11 +4364,11 @@ Editor::sort_track_selection (TrackViewList* sel) } } -nframes64_t +framepos_t Editor::get_preferred_edit_position (bool ignore_playhead) { bool ignored; - nframes64_t where = 0; + framepos_t where = 0; EditPoint ep = _edit_point; if (entered_marker) { @@ -4491,7 +4413,7 @@ Editor::get_preferred_edit_position (bool ignore_playhead) } void -Editor::set_loop_range (nframes64_t start, nframes64_t end, string cmd) +Editor::set_loop_range (framepos_t start, framepos_t end, string cmd) { if (!_session) return; @@ -4500,7 +4422,7 @@ Editor::set_loop_range (nframes64_t start, nframes64_t end, string cmd) Location* tll; if ((tll = transport_loop_location()) == 0) { - Location* loc = new Location (start, end, _("Loop"), Location::IsAutoLoop); + Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop); XMLNode &before = _session->locations()->get_state(); _session->locations()->add (loc, true); _session->set_auto_loop_location (loc); @@ -4518,7 +4440,7 @@ Editor::set_loop_range (nframes64_t start, nframes64_t end, string cmd) } void -Editor::set_punch_range (nframes64_t start, nframes64_t end, string cmd) +Editor::set_punch_range (framepos_t start, framepos_t end, string cmd) { if (!_session) return; @@ -4527,7 +4449,7 @@ Editor::set_punch_range (nframes64_t start, nframes64_t end, string cmd) Location* tpl; if ((tpl = transport_punch_location()) == 0) { - Location* loc = new Location (start, end, _("Loop"), Location::IsAutoPunch); + Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoPunch); XMLNode &before = _session->locations()->get_state(); _session->locations()->add (loc, true); _session->set_auto_loop_location (loc); @@ -4551,7 +4473,7 @@ Editor::set_punch_range (nframes64_t start, nframes64_t end, string cmd) * @param ts Tracks to look on; if this is empty, all tracks are examined. */ void -Editor::get_regions_at (RegionSelection& rs, nframes64_t where, const TrackViewList& ts) const +Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const { const TrackViewList* tracks; @@ -4570,7 +4492,7 @@ Editor::get_regions_at (RegionSelection& rs, nframes64_t where, const TrackViewL if ((tr = rtv->track()) && ((pl = tr->playlist()))) { Playlist::RegionList* regions = pl->regions_at ( - (nframes64_t) floor ( (double)where * tr->speed())); + (framepos_t) floor ( (double)where * tr->speed())); for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) { RegionView* rv = rtv->view()->find_view (*i); @@ -4586,7 +4508,7 @@ Editor::get_regions_at (RegionSelection& rs, nframes64_t where, const TrackViewL } void -Editor::get_regions_after (RegionSelection& rs, nframes64_t where, const TrackViewList& ts) const +Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const { const TrackViewList* tracks; @@ -4605,7 +4527,7 @@ Editor::get_regions_after (RegionSelection& rs, nframes64_t where, const TrackVi if ((tr = rtv->track()) && ((pl = tr->playlist()))) { Playlist::RegionList* regions = pl->regions_touched ( - (nframes64_t) floor ( (double)where * tr->speed()), max_frames); + (framepos_t) floor ( (double)where * tr->speed()), max_framepos); for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) { @@ -4622,47 +4544,74 @@ Editor::get_regions_after (RegionSelection& rs, nframes64_t where, const TrackVi } } -/** Find all regions which are either: - * - selected or - * - the entered_regionview (if allow_entered == true) or - * - under the preferred edit position AND on a selected track, or on a track - * which is in the same active edit-enable route group as a selected region (if allow_edit_position == true) +/** Get regions using the following conditions: + * 1. If the edit point is `mouse': + * if the mouse is over a selected region, or no region, return all selected regions. + * if the mouse is over an unselected region, return just that region. + * 2. For all other edit points: + * return the selected regions AND those that are both under the edit position + * AND on a selected track, or on a track which is in the same active edit-enabled route group + * as a selected region. + * + * The rationale here is that the mouse edit point is special in that its position describes + * both a time and a track; the other edit modes only describe a time. + * * @param rs Returned region list. - * @param allow_entered true to include the entered_regionview in the list. */ -void -Editor::get_regions_for_action (RegionSelection& rs, bool allow_entered, bool allow_edit_position) -{ - /* Start with selected regions */ - rs = selection->regions; - /* Add the entered_regionview, if requested */ - if (allow_entered && entered_regionview) { - rs.add (entered_regionview); +RegionSelection +Editor::get_regions_from_selection_and_edit_point () +{ + if (_edit_point == EditAtMouse) { + if (entered_regionview == 0 || selection->regions.contains (entered_regionview)) { + return selection->regions; + } else { + RegionSelection rs; + rs.add (entered_regionview); + return rs; + } } - if (allow_edit_position) { + /* We're using the edit point, but its not EditAtMouse */ - TrackViewList tracks = selection->tracks; + /* Start with selected regions */ + RegionSelection rs = selection->regions; - /* tracks is currently the set of selected tracks; add any other tracks that - * have regions that are in the same edit-activated route group as one of - * our regions */ - for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) { + TrackViewList tracks = selection->tracks; - RouteGroup* g = (*i)->get_time_axis_view().route_group (); - if (g && g->is_active() && g->is_edit()) { - tracks.add (axis_views_from_routes (g->route_list())); - } - + /* Tracks is currently the set of selected tracks; add any other tracks that + have regions that are in the same edit-activated route group as one of + our regions. + */ + for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) { + + RouteGroup* g = (*i)->get_time_axis_view().route_group (); + if (g && g->is_active() && g->is_edit()) { + tracks.add (axis_views_from_routes (g->route_list())); } + + } + + if (!tracks.empty()) { + /* now find regions that are at the edit position on those tracks */ + framepos_t const where = get_preferred_edit_position (); + get_regions_at (rs, where, tracks); + } - if (!tracks.empty()) { - /* now find regions that are at the edit position on those tracks */ - nframes64_t const where = get_preferred_edit_position (); - get_regions_at (rs, where, tracks); - } + return rs; +} + + +RegionSelection +Editor::get_regions_from_selection_and_entered () +{ + RegionSelection rs = selection->regions; + + if (rs.empty() && entered_regionview) { + rs.add (entered_regionview); } + + return rs; } void @@ -4710,16 +4659,6 @@ Editor::show_rhythm_ferret () rhythm_ferret->present (); } -void -Editor::show_global_port_matrix (ARDOUR::DataType t) -{ - if (_global_port_matrix[t] == 0) { - _global_port_matrix[t] = new GlobalPortMatrixWindow (_session, t); - } - - _global_port_matrix[t]->show (); -} - void Editor::first_idle () { @@ -4831,12 +4770,6 @@ Editor::region_view_added (RegionView *) _summary->set_dirty (); } -void -Editor::streamview_height_changed () -{ - _summary->set_dirty (); -} - TimeAxisView* Editor::axis_view_from_route (boost::shared_ptr r) const { @@ -4900,7 +4833,6 @@ Editor::handle_new_route (RouteList& routes) rtv->effective_gain_display (); rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added)); - rtv->view()->HeightChanged.connect (sigc::mem_fun (*this, &Editor::streamview_height_changed)); } _routes->routes_added (new_views); @@ -4923,14 +4855,22 @@ Editor::timeaxisview_deleted (TimeAxisView *tv) } ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv); - + RouteTimeAxisView* rtav = dynamic_cast (tv); + _routes->route_removed (tv); if (tv == entered_track) { entered_track = 0; } - + + TimeAxisView::Children c = tv->get_child_list (); + for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) { + if (entered_track == i->get()) { + entered_track = 0; + } + } + /* remove it from the list of track views */ TrackViewList::iterator i; @@ -4942,7 +4882,6 @@ Editor::timeaxisview_deleted (TimeAxisView *tv) /* update whatever the current mixer strip is displaying, if revelant */ boost::shared_ptr route; - RouteTimeAxisView* rtav = dynamic_cast (tv); if (rtav) { route = rtav->route (); @@ -5005,12 +4944,13 @@ Editor::foreach_time_axis_view (sigc::slot theslot) } } +/** Find a RouteTimeAxisView by the ID of its route */ RouteTimeAxisView* -Editor::get_route_view_by_id (PBD::ID& id) +Editor::get_route_view_by_route_id (PBD::ID& id) const { RouteTimeAxisView* v; - for(TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { + for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) { if((v = dynamic_cast(*i)) != 0) { if(v->route()->id() == id) { return v; @@ -5080,6 +5020,16 @@ Editor::show_region_in_region_list () _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region)); } +void +Editor::step_edit_status_change (bool yn) +{ + if (yn) { + start_step_editing (); + } else { + stop_step_editing (); + } +} + void Editor::start_step_editing () { @@ -5105,28 +5055,70 @@ Editor::check_step_edit () return true; // do it again, till we stop } -void -Editor::horizontal_scroll_left () +bool +Editor::horizontal_scroll_left_press () { + ++_scroll_callbacks; + + if (_scroll_connection.connected() && _scroll_callbacks < 5) { + /* delay the first auto-repeat */ + return true; + } + double x = leftmost_position() - current_page_frames() / 5; if (x < 0) { x = 0; } reset_x_origin (x); + + /* do hacky auto-repeat */ + if (!_scroll_connection.connected ()) { + _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press), 100); + _scroll_callbacks = 0; + } + + return true; } void -Editor::horizontal_scroll_right () +Editor::horizontal_scroll_left_release () +{ + _scroll_connection.disconnect (); +} + +bool +Editor::horizontal_scroll_right_press () { + ++_scroll_callbacks; + + if (_scroll_connection.connected() && _scroll_callbacks < 5) { + /* delay the first auto-repeat */ + return true; + } + reset_x_origin (leftmost_position() + current_page_frames() / 5); + + /* do hacky auto-repeat */ + if (!_scroll_connection.connected ()) { + _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press), 100); + _scroll_callbacks = 0; + } + + return true; +} + +void +Editor::horizontal_scroll_right_release () +{ + _scroll_connection.disconnect (); } /** Queue a change for the Editor viewport x origin to follow the playhead */ void Editor::reset_x_origin_to_follow_playhead () { - nframes64_t const frame = playhead_cursor->current_frame; + framepos_t const frame = playhead_cursor->current_frame; if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) { @@ -5142,7 +5134,7 @@ Editor::reset_x_origin_to_follow_playhead () if (frame < leftmost_frame) { /* moving left */ - nframes64_t l = 0; + framepos_t l = 0; if (_session->transport_rolling()) { /* rolling; end up with the playhead at the right of the page */ l = frame - current_page_frames (); @@ -5196,7 +5188,7 @@ Editor::super_rapid_screen_update () /* PLAYHEAD AND VIEWPORT */ - nframes64_t const frame = _session->audible_frame(); + framepos_t const frame = _session->audible_frame(); /* There are a few reasons why we might not update the playhead / viewport stuff: * @@ -5330,3 +5322,71 @@ Editor::show_editor_list (bool yn) the_notebook.hide(); } } + +void +Editor::change_region_layering_order () +{ + framepos_t const position = get_preferred_edit_position (); + + if (!clicked_routeview) { + if (layering_order_editor) { + layering_order_editor->hide (); + } + return; + } + + boost::shared_ptr track = boost::dynamic_pointer_cast (clicked_routeview->route()); + + if (!track) { + return; + } + + boost::shared_ptr pl = track->playlist(); + + if (!pl) { + return; + } + + if (layering_order_editor == 0) { + layering_order_editor = new RegionLayeringOrderEditor(*this); + } + + layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position); + layering_order_editor->maybe_present (); +} + +void +Editor::update_region_layering_order_editor () +{ + if (layering_order_editor && layering_order_editor->is_visible ()) { + change_region_layering_order (); + } +} + +void +Editor::setup_fade_images () +{ + _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear"))); + _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut"))); + _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut"))); + _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut"))); + _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut"))); + + _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear"))); + _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut"))); + _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut"))); + _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut"))); + _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut"))); +} + + +/** @return Gtk::manage()d menu item for a given action from `editor_actions' */ +Gtk::MenuItem& +Editor::action_menu_item (std::string const & name) +{ + Glib::RefPtr a = editor_actions->get_action (name); + assert (a); + + return *manage (a->create_menu_item ()); +} +