X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Feditor.cc;h=cd1b9e4b7c613e00bf2c90dc42e2ca3f69efdb2e;hb=991fab113fc37836a7e8cefaad209241d239ae4b;hp=b3e228571473cc34c96ba3db87e50294a8ccb8b9;hpb=22e41063d5b0c4c80eab3e3a375b734ca7d76169;p=ardour.git diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index b3e2285714..cd1b9e4b7c 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,9 +15,10 @@ 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 @@ -28,15 +29,20 @@ #include #include +#include #include +#include #include #include #include +#include #include #include #include +#include +#include #include #include @@ -53,7 +59,6 @@ #include "ardour_ui.h" #include "editor.h" -#include "grouped_buttons.h" #include "keyboard.h" #include "marker.h" #include "playlist_selector.h" @@ -62,12 +67,12 @@ #include "selection.h" #include "audio_streamview.h" #include "time_axis_view.h" +#include "audio_time_axis.h" #include "utils.h" #include "crossfade_view.h" #include "editing.h" #include "public_editor.h" #include "crossfade_edit.h" -#include "audio_time_axis.h" #include "canvas_impl.h" #include "actions.h" #include "gui_thread.h" @@ -78,9 +83,9 @@ #include "i18n.h" -/* */ +#ifdef WITH_CMT #include "imageframe_socket_handler.h" -/* */ +#endif using namespace std; using namespace sigc; @@ -98,7 +103,7 @@ const double Editor::timebar_height = 15.0; #include "editor_xpms" -static const gchar *snap_type_strings[] = { +static const gchar *_snap_type_strings[] = { N_("None"), N_("CD Frames"), N_("SMPTE Frames"), @@ -122,18 +127,18 @@ static const gchar *snap_type_strings[] = { 0 }; -static const gchar *snap_mode_strings[] = { - N_("Normal Snap"), - N_("Magnetic Snap"), +static const gchar *_snap_mode_strings[] = { + N_("Normal"), + N_("Magnetic"), 0 }; -static const gchar *zoom_focus_strings[] = { +static const gchar *_zoom_focus_strings[] = { N_("Left"), N_("Right"), N_("Center"), - N_("Playhead"), - N_("Edit Cursor"), + N_("Play"), + N_("Edit"), 0 }; @@ -147,6 +152,7 @@ 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::note_cursor = 0; Gdk::Cursor* Editor::wait_cursor = 0; Gdk::Cursor* Editor::timebar_cursor = 0; @@ -170,14 +176,13 @@ check_adjustment (Gtk::Adjustment* adj) } -Editor::Editor (AudioEngine& eng) - : engine (eng), - +Editor::Editor () + : /* time display buttons */ minsec_label (_("Mins:Secs")), bbt_label (_("Bars:Beats")), - smpte_label (_("SMPTE")), + smpte_label (_("Timecode")), frame_label (_("Frames")), tempo_label (_("Tempo")), meter_label (_("Meter")), @@ -196,21 +201,21 @@ Editor::Editor (AudioEngine& eng) /* tool bar related */ - edit_cursor_clock (X_("EditCursorClock"), true), - zoom_range_clock (X_("ZoomRangeClock"), true, true), + edit_cursor_clock (X_("editcursor"), false, X_("EditCursorClock"), true), + zoom_range_clock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, true), toolbar_selection_clock_table (2,3), automation_mode_button (_("mode")), global_automation_button (_("automation")), - /* */ +#ifdef WITH_CMT image_socket_listener(0), - /* */ +#endif /* nudge */ - nudge_clock (X_("NudgeClock"), true, true) + nudge_clock (X_("nudge"), false, X_("NudgeClock"), true, true) { constructed = false; @@ -240,6 +245,10 @@ Editor::Editor (AudioEngine& eng) 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; @@ -267,7 +276,6 @@ Editor::Editor (AudioEngine& eng) no_route_list_redisplay = false; verbose_cursor_on = true; route_removal = false; - track_spacing = 0; show_automatic_regions_in_region_list = true; region_list_sort_type = (Editing::RegionListSortType) 0; have_pending_keyboard_selection = false; @@ -279,16 +287,15 @@ Editor::Editor (AudioEngine& eng) route_list_menu = 0; region_list_menu = 0; marker_menu = 0; + start_end_marker_menu = 0; range_marker_menu = 0; marker_menu_item = 0; tm_marker_menu = 0; transport_marker_menu = 0; new_transport_marker_menu = 0; editor_mixer_strip_width = Wide; - repos_zoom_queued = false; - region_edit_menu_split_item = 0; + show_editor_mixer_when_tracks_arrive = false; temp_location = 0; - region_edit_menu_split_multichannel_item = 0; leftmost_frame = 0; ignore_mouse_mode_toggle = false; current_stepping_trackview = 0; @@ -302,19 +309,22 @@ Editor::Editor (AudioEngine& eng) edit_cursor = 0; playhead_cursor = 0; button_release_can_deselect = true; + canvas_idle_queued = false; + _dragging_playhead = false; + _dragging_hscrollbar = false; - location_marker_color = color_map[cLocationMarker]; - location_range_color = color_map[cLocationRange]; - location_cd_marker_color = color_map[cLocationCDMarker]; - location_loop_color = color_map[cLocationLoop]; - location_punch_color = color_map[cLocationPunch]; + location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get(); + location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get(); + location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get(); + location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get(); + location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get(); range_marker_drag_rect = 0; marker_drag_line = 0; set_mouse_mode (MouseObject, true); - frames_per_unit = 2048; /* too early to use set_frames_per_unit */ + frames_per_unit = 2048; /* too early to use reset_zoom () */ reset_hscrollbar_stepping (); zoom_focus = ZoomFocusLeft; @@ -347,21 +357,24 @@ Editor::Editor (AudioEngine& eng) edit_vscrollbar.set_adjustment (vertical_adjustment); edit_hscrollbar.set_adjustment (horizontal_adjustment); - edit_hscrollbar.signal_button_press_event().connect (mem_fun(*this, &Editor::hscrollbar_button_press)); - edit_hscrollbar.signal_button_release_event().connect (mem_fun(*this, &Editor::hscrollbar_button_release)); + edit_hscrollbar.signal_button_press_event().connect (mem_fun(*this, &Editor::hscrollbar_button_press), false); + edit_hscrollbar.signal_button_release_event().connect (mem_fun(*this, &Editor::hscrollbar_button_release), false); edit_hscrollbar.signal_size_allocate().connect (mem_fun(*this, &Editor::hscrollbar_allocate)); + edit_hscrollbar.set_name ("EditorHScrollbar"); + build_cursors (); setup_toolbar (); edit_cursor_clock.ValueChanged.connect (mem_fun(*this, &Editor::edit_cursor_clock_changed)); + time_canvas_vbox.pack_start (*_ruler_separator, false, false); time_canvas_vbox.pack_start (*minsec_ruler, false, false); time_canvas_vbox.pack_start (*smpte_ruler, false, false); time_canvas_vbox.pack_start (*frames_ruler, false, false); time_canvas_vbox.pack_start (*bbt_ruler, false, false); time_canvas_vbox.pack_start (time_canvas, true, true); - time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars)); + time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2); bbt_label.set_name ("EditorTimeButton"); bbt_label.set_size_request (-1, (int)timebar_height); @@ -414,6 +427,9 @@ Editor::Editor (AudioEngine& eng) time_button_event_box.set_name ("TimebarLabelBase"); time_button_event_box.signal_button_release_event().connect (mem_fun(*this, &Editor::ruler_label_button_release)); + time_button_frame.add(time_button_event_box); + time_button_frame.property_shadow_type() = Gtk::SHADOW_OUT; + /* these enable us to have a dedicated window (for cursor setting, etc.) for the canvas areas. */ @@ -429,14 +445,15 @@ Editor::Editor (AudioEngine& eng) edit_packer.set_border_width (0); edit_packer.set_name ("EditorWindow"); - edit_packer.attach (edit_vscrollbar, 0, 1, 1, 3, FILL, FILL|EXPAND, 0, 0); + edit_packer.attach (edit_vscrollbar, 3, 4, 1, 2, FILL, FILL|EXPAND, 0, 0); - edit_packer.attach (time_button_event_box, 1, 2, 0, 1, FILL, FILL, 0, 0); - edit_packer.attach (time_canvas_event_box, 2, 3, 0, 1, FILL|EXPAND, FILL, 0, 0); + edit_packer.attach (time_button_frame, 0, 2, 0, 1, FILL, FILL, 0, 0); + edit_packer.attach (time_canvas_event_box, 2, 4, 0, 1, FILL|EXPAND, FILL, 0, 0); edit_packer.attach (controls_layout, 1, 2, 1, 2, FILL, FILL|EXPAND, 0, 0); edit_packer.attach (track_canvas_event_box, 2, 3, 1, 2, FILL|EXPAND, FILL|EXPAND, 0, 0); + edit_packer.attach (zoom_box, 1, 2, 2, 3, FILL, FILL, 0, 0); edit_packer.attach (edit_hscrollbar, 2, 3, 2, 3, FILL|EXPAND, FILL, 0, 0); bottom_hbox.set_border_width (2); @@ -444,7 +461,7 @@ Editor::Editor (AudioEngine& eng) route_display_model = ListStore::create(route_display_columns); route_list_display.set_model (route_display_model); - route_list_display.append_column (_("Visible"), route_display_columns.visible); + route_list_display.append_column (_("Show"), route_display_columns.visible); route_list_display.append_column (_("Name"), route_display_columns.text); route_list_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0)); route_list_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1)); @@ -470,7 +487,7 @@ Editor::Editor (AudioEngine& eng) edit_group_display.set_model (group_model); edit_group_display.append_column (_("Name"), group_columns.text); edit_group_display.append_column (_("Active"), group_columns.is_active); - edit_group_display.append_column (_("Visible"), group_columns.is_visible); + edit_group_display.append_column (_("Show"), group_columns.is_visible); edit_group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0)); edit_group_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1)); edit_group_display.get_column (2)->set_data (X_("colnum"), GUINT_TO_POINTER(2)); @@ -495,12 +512,11 @@ Editor::Editor (AudioEngine& eng) active_cell->property_activatable() = true; active_cell->property_radio() = false; - edit_group_display.set_name ("EditGroupList"); - group_model->signal_row_changed().connect (mem_fun (*this, &Editor::edit_group_row_change)); edit_group_display.set_name ("EditGroupList"); edit_group_display.get_selection()->set_mode (SELECTION_SINGLE); + edit_group_display.set_headers_visible (true); edit_group_display.set_reorderable (false); edit_group_display.set_rules_hint (true); edit_group_display.set_size_request (75, -1); @@ -511,7 +527,7 @@ Editor::Editor (AudioEngine& eng) edit_group_display.signal_button_press_event().connect (mem_fun(*this, &Editor::edit_group_list_button_press_event), false); VBox* edit_group_display_packer = manage (new VBox()); - HButtonBox* edit_group_display_button_box = manage (new HButtonBox()); + HBox* edit_group_display_button_box = manage (new HBox()); edit_group_display_button_box->set_homogeneous (true); Button* edit_group_add_button = manage (new Button ()); @@ -546,7 +562,6 @@ 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.set_hover_expand (true); region_list_display.get_selection()->set_select_function (mem_fun (*this, &Editor::region_list_selection_filter)); @@ -587,11 +602,12 @@ Editor::Editor (AudioEngine& eng) named_selection_display.append_column (_("Chunks"), named_selection_columns.text); named_selection_display.set_headers_visible (false); named_selection_display.set_size_request (100, -1); - named_selection_display.set_name ("RegionListDisplay"); + named_selection_display.set_name ("NamedSelectionDisplay"); named_selection_display.get_selection()->set_mode (SELECTION_SINGLE); named_selection_display.set_size_request (100, -1); - named_selection_display.signal_button_press_event().connect (mem_fun(*this, &Editor::named_selection_display_button_press), false); + named_selection_display.signal_button_release_event().connect (mem_fun(*this, &Editor::named_selection_display_button_release), false); + named_selection_display.signal_key_release_event().connect (mem_fun(*this, &Editor::named_selection_display_key_release), false); named_selection_display.get_selection()->signal_changed().connect (mem_fun (*this, &Editor::named_selection_display_selection_changed)); /* SNAPSHOTS */ @@ -599,7 +615,7 @@ Editor::Editor (AudioEngine& eng) snapshot_display_model = ListStore::create (snapshot_display_columns); snapshot_display.set_model (snapshot_display_model); snapshot_display.append_column (X_("snapshot"), snapshot_display_columns.visible_name); - snapshot_display.set_name ("SnapshotDisplayList"); + snapshot_display.set_name ("SnapshotDisplay"); snapshot_display.set_size_request (75, -1); snapshot_display.set_headers_visible (false); snapshot_display.set_reorderable (false); @@ -613,10 +629,10 @@ 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); @@ -632,6 +648,8 @@ Editor::Editor (AudioEngine& eng) the_notebook.popup_enable (); the_notebook.set_tab_pos (Gtk::POS_RIGHT); + post_maximal_editor_width = 0; + post_maximal_pane_position = 0; edit_pane.pack1 (edit_packer, true, true); edit_pane.pack2 (the_notebook, false, true); @@ -677,8 +695,32 @@ Editor::Editor (AudioEngine& eng) fade_context_menu.set_name ("ArdourContextMenu"); - set_title (_("ardour: editor")); - set_wmclass (_("ardour_editor"), "Ardour"); + /* icons, titles, WM stuff */ + + list > window_icons; + Glib::RefPtr icon; + + if ((icon = ::get_icon ("ardour_icon_16px")) != 0) { + window_icons.push_back (icon); + } + if ((icon = ::get_icon ("ardour_icon_22px")) != 0) { + window_icons.push_back (icon); + } + if ((icon = ::get_icon ("ardour_icon_32px")) != 0) { + window_icons.push_back (icon); + } + if ((icon = ::get_icon ("ardour_icon_48px")) != 0) { + window_icons.push_back (icon); + } + if (!window_icons.empty()) { + set_icon_list (window_icons); + set_default_icon_list (window_icons); + } + + WindowTitle title(Glib::get_application_name()); + title += _("Editor"); + set_title (title.get_string()); + set_wmclass (X_("ardour_editor"), "Ardour"); add (vpacker); add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK); @@ -701,7 +743,7 @@ Editor::Editor (AudioEngine& eng) Editor::~Editor() { - /* */ +#ifdef WITH_CMT if(image_socket_listener) { if(image_socket_listener->is_connected()) @@ -712,7 +754,7 @@ Editor::~Editor() delete image_socket_listener ; image_socket_listener = 0 ; } - /* */ +#endif } void @@ -771,6 +813,9 @@ Editor::show_window () { show_all (); present (); + + /* 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 @@ -793,65 +838,6 @@ Editor::tie_vertical_scrolling () edit_cursor->set_y_axis(y1); } -void -Editor::set_frames_per_unit (double fpu) -{ - nframes_t frames; - - if (fpu == frames_per_unit) { - return; - } - - if (fpu < 2.0) { - fpu = 2.0; - } - - // convert fpu to frame count - - frames = (nframes_t) floor (fpu * canvas_width); - - /* don't allow zooms that fit more than the maximum number - of frames into an 800 pixel wide space. - */ - - if (max_frames / fpu < 800.0) { - return; - } - - if (fpu == frames_per_unit) { - return; - } - - frames_per_unit = fpu; - - if (frames != zoom_range_clock.current_duration()) { - zoom_range_clock.set (frames); - } - - if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) { - if (!selection->tracks.empty()) { - for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) { - (*i)->reshow_selection (selection->time); - } - } else { - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - (*i)->reshow_selection (selection->time); - } - } - } - - ZoomChanged (); /* EMIT_SIGNAL */ - - reset_hscrollbar_stepping (); - reset_scrolling_region (); - - if (edit_cursor) edit_cursor->set_position (edit_cursor->current_frame); - if (playhead_cursor) playhead_cursor->set_position (playhead_cursor->current_frame); - - instant_save (); - -} - void Editor::instant_save () { @@ -860,29 +846,9 @@ Editor::instant_save () } if (session) { - session->add_instant_xml(get_state(), session->path()); - } else { - Config->add_instant_xml(get_state(), get_user_ardour_path()); - } -} - -void -Editor::reposition_x_origin (nframes_t frame) -{ - if (frame != leftmost_frame) { - leftmost_frame = frame; - - nframes_t rightmost_frame = leftmost_frame + current_page_frames (); - - if (rightmost_frame > last_canvas_frame) { - last_canvas_frame = rightmost_frame; - reset_scrolling_region (); - } - - horizontal_adjustment.set_value (frame/frames_per_unit); + session->add_instant_xml(get_state()); } else { - update_fixed_rulers(); - tempo_map_changed (Change (0)); + Config->add_instant_xml(get_state()); } } @@ -941,9 +907,9 @@ Editor::control_scroll (float fraction) if (target > (current_page_frames() / 2)) { /* try to center PH in window */ - reposition_x_origin (target - (current_page_frames()/2)); + reset_x_origin (target - (current_page_frames()/2)); } else { - reposition_x_origin (0); + reset_x_origin (0); } /* cancel the existing */ @@ -962,36 +928,6 @@ Editor::deferred_control_scroll (nframes_t target) return false; } -void -Editor::canvas_horizontally_scrolled () -{ - - leftmost_frame = (nframes_t) floor (horizontal_adjustment.get_value() * frames_per_unit); - update_fixed_rulers (); - tempo_map_changed (Change (0)); - -} - -void -Editor::reposition_and_zoom (nframes_t frame, double nfpu) -{ - if (!repos_zoom_queued) { - repos_zoom_queued = true; - Glib::signal_idle().connect (bind (mem_fun(*this, &Editor::deferred_reposition_and_zoom), frame, nfpu)); - } -} - -gint -Editor::deferred_reposition_and_zoom (nframes_t frame, double nfpu) -{ - - set_frames_per_unit (nfpu); - reposition_x_origin (frame); - repos_zoom_queued = false; - - return FALSE; -} - void Editor::on_realize () { @@ -1049,7 +985,7 @@ Editor::center_screen_internal (nframes_t frame, float page) frame = 0; } - reposition_x_origin (frame); + reset_x_origin (frame); } void @@ -1061,6 +997,7 @@ Editor::handle_new_duration () if (new_end > last_canvas_frame) { last_canvas_frame = new_end; + horizontal_adjustment.set_upper (last_canvas_frame / frames_per_unit); reset_scrolling_region (); } @@ -1083,24 +1020,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()); } } @@ -1109,6 +1043,13 @@ Editor::connect_to_session (Session *t) { session = t; + XMLNode* node = ARDOUR_UI::instance()->editor_settings(); + set_state (*node); + + /* catch up with the playhead */ + + session->request_locate (playhead_cursor->current_frame); + if (first_action_message) { first_action_message->hide(); } @@ -1116,6 +1057,7 @@ Editor::connect_to_session (Session *t) update_title (); session->GoingAway.connect (mem_fun(*this, &Editor::session_going_away)); + session->history().Changed.connect (mem_fun (*this, &Editor::history_changed)); /* These signals can all be emitted by a non-GUI thread. Therefore the handlers for them must not attempt to directly interact with the GUI, @@ -1161,8 +1103,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")); } @@ -1175,8 +1116,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")); } @@ -1204,32 +1144,17 @@ Editor::connect_to_session (Session *t) (static_cast(*i))->set_samples_per_unit (frames_per_unit); } - /* ::reposition_x_origin() doesn't work right here, since the old - position may be zero already, and it does nothing in such - circumstances. - */ - - leftmost_frame = 0; - - horizontal_adjustment.set_value (0); - restore_ruler_visibility (); //tempo_map_changed (Change (0)); session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); - edit_cursor->set_position (0); - playhead_cursor->set_position (0); - start_scrolling (); - XMLNode* node = ARDOUR_UI::instance()->editor_settings(); - set_state (*node); - /* don't show master bus in a new session */ if (ARDOUR_UI::instance()->session_is_new ()) { - TreeModel::Children rows = route_display_model->children(); + TreeModel::Children rows = route_display_model->children(); TreeModel::Children::iterator i; no_route_list_redisplay = true; @@ -1239,7 +1164,7 @@ Editor::connect_to_session (Session *t) RouteTimeAxisView *rtv; if ((rtv = dynamic_cast(tv)) != 0) { - if (rtv->route()->master()) { + if (rtv->route()->is_master()) { route_list_display.get_selection()->unselect (i); } } @@ -1249,9 +1174,9 @@ Editor::connect_to_session (Session *t) redisplay_route_list (); } - /* register for undo history */ + /* register for undo history */ - session->register_with_memento_command_factory(_id, this); + session->register_with_memento_command_factory(_id, this); } void @@ -1286,7 +1211,7 @@ 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); } - + grabber_cursor = new Gdk::Cursor (HAND2); cross_hair_cursor = new Gdk::Cursor (CROSSHAIR); trimmer_cursor = new Gdk::Cursor (SB_H_DOUBLE_ARROW); @@ -1294,8 +1219,10 @@ Editor::build_cursors () time_fx_cursor = new Gdk::Cursor (SIZING); wait_cursor = new Gdk::Cursor (WATCH); timebar_cursor = new Gdk::Cursor(LEFT_PTR); + note_cursor = new Gdk::Cursor (PENCIL); } +/** 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) { @@ -1308,7 +1235,6 @@ Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* i } MenuList& items (fade_context_menu.items()); - AudioRegion& ar (*arv->audio_region().get()); // FIXME items.clear (); @@ -1316,37 +1242,38 @@ 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 (ar, &AudioRegion::set_fade_in_shape), AudioRegion::Linear))); - items.push_back (MenuElem (_("Slowest"), bind (mem_fun (ar, &AudioRegion::set_fade_in_shape), AudioRegion::LogB))); - items.push_back (MenuElem (_("Slow"), bind (mem_fun (ar, &AudioRegion::set_fade_in_shape), AudioRegion::Fast))); - items.push_back (MenuElem (_("Fast"), bind (mem_fun (ar, &AudioRegion::set_fade_in_shape), AudioRegion::LogA))); - items.push_back (MenuElem (_("Fastest"), bind (mem_fun (ar, &AudioRegion::set_fade_in_shape), AudioRegion::Slow))); + 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 (ar, &AudioRegion::set_fade_out_shape), AudioRegion::Linear))); - items.push_back (MenuElem (_("Slowest"), bind (mem_fun (ar, &AudioRegion::set_fade_out_shape), AudioRegion::Fast))); - items.push_back (MenuElem (_("Slow"), bind (mem_fun (ar, &AudioRegion::set_fade_out_shape), AudioRegion::LogB))); - items.push_back (MenuElem (_("Fast"), bind (mem_fun (ar, &AudioRegion::set_fade_out_shape), AudioRegion::LogA))); - items.push_back (MenuElem (_("Fastest"), bind (mem_fun (ar, &AudioRegion::set_fade_out_shape), AudioRegion::Slow))); + items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear))); + items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow))); + items.push_back (MenuElem (_("Slow"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogA))); + items.push_back (MenuElem (_("Fast"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogB))); + items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Fast))); break; + default: fatal << _("programming error: ") << X_("non-fade canvas item passed to popup_fade_context_menu()") @@ -1357,191 +1284,38 @@ Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* i fade_context_menu.popup (button, time); } +/* Pop up the general track context menu for when the user clicks pretty much anywhere in a track or bus */ void -Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection, nframes_t frame) -{ - using namespace Menu_Helpers; - Menu* (Editor::*build_menu_function)(nframes_t); - Menu *menu; - - switch (item_type) { - case RegionItem: - case RegionViewName: - case RegionViewNameHighlight: - if (with_selection) { - build_menu_function = &Editor::build_track_selection_context_menu; - } else { - build_menu_function = &Editor::build_track_region_context_menu; - } - break; - - case SelectionItem: - if (with_selection) { - build_menu_function = &Editor::build_track_selection_context_menu; - } else { - build_menu_function = &Editor::build_track_context_menu; - } - break; - - case CrossfadeViewItem: - build_menu_function = &Editor::build_track_crossfade_context_menu; - break; - - case StreamItem: - if (clicked_routeview->is_track()) { - build_menu_function = &Editor::build_track_context_menu; - } else { - build_menu_function = &Editor::build_track_bus_context_menu; - } - break; - - default: - /* probably shouldn't happen but if it does, we don't care */ - return; - } - - menu = (this->*build_menu_function)(frame); - menu->set_name ("ArdourContextMenu"); - - /* now handle specific situations */ - - switch (item_type) { - case RegionItem: - case RegionViewName: - 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); - } else { - ActionManager::set_sensitive (ActionManager::edit_cursor_in_region_sensitive_actions, false); - } - } - /* - if (region_edit_menu_split_multichannel_item) { - if (clicked_regionview && clicked_regionview->region().n_channels() > 1) { - // GTK2FIX find the action, change its sensitivity - // region_edit_menu_split_multichannel_item->set_sensitive (true); - } else { - // GTK2FIX see above - // region_edit_menu_split_multichannel_item->set_sensitive (false); - } - }*/ - } - break; - - case SelectionItem: - break; - - case CrossfadeViewItem: - break; - - case StreamItem: - break; - - default: - /* probably shouldn't happen but if it does, we don't care */ - return; - } - - if (clicked_routeview && clicked_routeview->audio_track()) { - - /* Bounce to disk */ - - using namespace Menu_Helpers; - MenuList& edit_items = menu->items(); - - edit_items.push_back (SeparatorElem()); - - switch (clicked_routeview->audio_track()->freeze_state()) { - case AudioTrack::NoFreeze: - edit_items.push_back (MenuElem (_("Freeze"), mem_fun(*this, &Editor::freeze_route))); - break; - - case AudioTrack::Frozen: - edit_items.push_back (MenuElem (_("Unfreeze"), mem_fun(*this, &Editor::unfreeze_route))); - break; - - case AudioTrack::UnFrozen: - edit_items.push_back (MenuElem (_("Freeze"), mem_fun(*this, &Editor::freeze_route))); - break; - } - - } - - menu->popup (button, time); -} - -Menu* -Editor::build_track_context_menu (nframes_t ignored) +Editor::popup_track_context_menu (int button, int32_t time, nframes_t frame) { - using namespace Menu_Helpers; - - MenuList& edit_items = track_context_menu.items(); - edit_items.clear(); - - add_dstream_context_items (edit_items); - return &track_context_menu; + build_track_context_menu (frame)->popup (button, time); } Menu* -Editor::build_track_bus_context_menu (nframes_t ignored) +Editor::build_track_context_menu (nframes_t frame) { using namespace Menu_Helpers; - MenuList& edit_items = track_context_menu.items(); - edit_items.clear(); - - add_bus_context_items (edit_items); - return &track_context_menu; -} - -Menu* -Editor::build_track_region_context_menu (nframes_t frame) -{ - using namespace Menu_Helpers; - MenuList& edit_items = track_region_context_menu.items(); + Menu* menu = manage (new Menu); + MenuList& edit_items = menu->items(); edit_items.clear(); - AudioTimeAxisView* atv = dynamic_cast (clicked_axisview); - - if (atv) { - boost::shared_ptr ds; - Playlist* 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); - } - delete regions; - } - } - - add_dstream_context_items (edit_items); - - return &track_region_context_menu; -} - -Menu* -Editor::build_track_crossfade_context_menu (nframes_t frame) -{ - using namespace Menu_Helpers; - MenuList& edit_items = track_crossfade_context_menu.items(); - edit_items.clear (); + /* Build the general `track' context menu, adding what is appropriate given + the current selection */ + /* XXX: currently crossfades can't be selected, so we can't use the selection + to decide which crossfades to mention in the menu. I believe this will + change at some point. For now we have to use clicked_trackview to decide. */ AudioTimeAxisView* atv = dynamic_cast (clicked_axisview); if (atv) { boost::shared_ptr ds; - Playlist* pl; - AudioPlaylist* apl; + boost::shared_ptr pl; + boost::shared_ptr apl; - if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()) != 0) && ((apl = dynamic_cast (pl)) != 0)) { + if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast (pl)) != 0)) { - Playlist::RegionList* regions = pl->regions_at (frame); AudioPlaylist::Crossfades xfades; - apl->crossfades_at (frame, xfades); bool many = xfades.size() > 1; @@ -1549,18 +1323,24 @@ Editor::build_track_crossfade_context_menu (nframes_t frame) for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) { 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->time.empty()) { + add_selection_context_items (edit_items); + } - delete regions; - } + if (!selection->regions.empty()) { + add_region_context_items (edit_items); } - add_dstream_context_items (edit_items); + if (!selection->tracks.empty()) { + add_bus_or_audio_track_context_items (edit_items); + } + + menu->set_name ("ArdourContextMenu"); - return &track_crossfade_context_menu; + return menu; } #ifdef FFT_ANALYSIS @@ -1602,22 +1382,11 @@ Editor::analyze_range_selection() #endif /* FFT_ANALYSIS */ - -Menu* -Editor::build_track_selection_context_menu (nframes_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); - - 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, Crossfade* xfade, Menu_Helpers::MenuList& edit_items, bool many) +Editor::add_crossfade_context_items (AudioStreamView* view, boost::shared_ptr xfade, Menu_Helpers::MenuList& edit_items, bool many) { using namespace Menu_Helpers; Menu *xfade_menu = manage (new Menu); @@ -1631,8 +1400,8 @@ Editor::add_crossfade_context_items (AudioStreamView* view, Crossfade* xfade, Me str = _("Unmute"); } - items.push_back (MenuElem (str, bind (mem_fun(*this, &Editor::toggle_xfade_active), xfade))); - items.push_back (MenuElem (_("Edit"), bind (mem_fun(*this, &Editor::edit_xfade), xfade))); + items.push_back (MenuElem (str, bind (mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr (xfade)))); + items.push_back (MenuElem (_("Edit"), bind (mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr (xfade)))); if (xfade->can_follow_overlap()) { @@ -1673,36 +1442,48 @@ 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); + } +} + +/** Add context menu items relevant to regions. + * @param edit_items List to add the items to. + */ void -Editor::add_region_context_items (AudioStreamView* sv, boost::shared_ptr region, Menu_Helpers::MenuList& edit_items) +Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items) { using namespace Menu_Helpers; - Menu *region_menu = manage (new Menu); - MenuList& items = region_menu->items(); + Menu *region_menu = manage (new Menu); + MenuList& items = region_menu->items(); region_menu->set_name ("ArdourContextMenu"); - boost::shared_ptr ar; - - if (region) { - ar = boost::dynamic_pointer_cast (region); - } + items.push_back (MenuElem (_("Edit..."), 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))); - /* when this particular menu pops up, make the relevant region - become selected. - */ + Menu* sync_point_menu = manage (new Menu); + MenuList& sync_point_items = sync_point_menu->items(); + sync_point_menu->set_name("ArdourContextMenu"); + + sync_point_items.push_back (MenuElem (_("Define"), mem_fun(*this, &Editor::set_region_sync_from_edit_cursor))); + sync_point_items.push_back (MenuElem (_("Remove"), mem_fun(*this, &Editor::remove_region_sync))); - 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 (_("Sync points"), *sync_point_menu)); - 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 (_("Remove sync point"), mem_fun(*this, &Editor::remove_region_sync))); - items.push_back (SeparatorElem()); + add_item_with_sensitivity (items, MenuElem (_("Audition"), mem_fun(*this, &Editor::audition_selected_region)), selection->regions.size() == 1); + + add_item_with_sensitivity (items, MenuElem (_("Export"), mem_fun(*this, &Editor::export_region)), selection->regions.size() == 1); - items.push_back (MenuElem (_("Audition"), mem_fun(*this, &Editor::audition_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 @@ -1711,48 +1492,62 @@ Editor::add_region_context_items (AudioStreamView* sv, boost::shared_ptr items.push_back (SeparatorElem()); - /* XXX hopefully this nonsense will go away with SigC++ 2.X, where the compiler - might be able to figure out which overloaded member function to use in - a bind() call ... + items.push_back (MenuElem (_("Lock"), bind (mem_fun (*this, &Editor::set_region_lock), true))); + items.push_back (MenuElem (_("Unlock"), bind (mem_fun (*this, &Editor::set_region_lock), false))); + items.push_back (MenuElem (_("Lock Position"), bind (mem_fun (*this, &Editor::set_region_position_lock), true))); + items.push_back (MenuElem (_("Unlock Position"), bind (mem_fun (*this, &Editor::set_region_position_lock), false))); + items.push_back (MenuElem (_("Mute"), bind (mem_fun (*this, &Editor::set_region_mute), true))); + items.push_back (MenuElem (_("Unmute"), bind (mem_fun (*this, &Editor::set_region_mute), false))); + items.push_back (MenuElem (_("Opaque"), bind (mem_fun (*this, &Editor::set_region_opaque), true))); + items.push_back (MenuElem (_("Transparent"), bind (mem_fun (*this, &Editor::set_region_opaque), false))); + + /* We allow "Original position" if at least one region is not at its + natural position */ + RegionSelection::iterator i = selection->regions.begin(); + while (i != selection->regions.end() && (*i)->region()->at_natural_position() == true) { + ++i; + } - void (Editor::*type_A_pmf)(void (Region::*pmf)(bool), bool) = &Editor::region_selection_op; - - items.push_back (MenuElem (_("Lock"), bind (mem_fun(*this, type_A_pmf), &Region::set_locked, true))); - items.push_back (MenuElem (_("Unlock"), bind (mem_fun(*this, type_A_pmf), &Region::set_locked, false))); + add_item_with_sensitivity (items, MenuElem (_("Original position"), mem_fun(*this, &Editor::naturalize)), i != selection->regions.end()); + items.push_back (SeparatorElem()); - if (region->muted()) { - items.push_back (MenuElem (_("Unmute"), bind (mem_fun(*this, type_A_pmf), &Region::set_muted, false))); - } else { - items.push_back (MenuElem (_("Mute"), bind (mem_fun(*this, type_A_pmf), &Region::set_muted, true))); + /* Find out if we have a selected audio region */ + i = selection->regions.begin(); + while (i != selection->regions.end() && boost::dynamic_pointer_cast((*i)->region()) == 0) { + ++i; } - items.push_back (SeparatorElem()); + bool const have_selected_audio_region = (i != selection->regions.end()); - items.push_back (MenuElem (_("Original position"), mem_fun(*this, &Editor::naturalize))); - items.push_back (SeparatorElem()); + if (have_selected_audio_region) { + Menu* envelopes_menu = manage (new Menu); + MenuList& envelopes_items = envelopes_menu->items(); + envelopes_menu->set_name ("ArdourContextMenu"); - if (ar) { + envelopes_items.push_back (MenuElem (_("Reset"), mem_fun(*this, &Editor::reset_region_gain_envelopes))); + envelopes_items.push_back (MenuElem (_("Visible"), bind (mem_fun (*this, &Editor::set_gain_envelope_visibility), true))); + envelopes_items.push_back (MenuElem (_("Invisible"), bind (mem_fun (*this, &Editor::set_gain_envelope_visibility), false))); + envelopes_items.push_back (MenuElem (_("Active"), bind (mem_fun (*this, &Editor::set_gain_envelope_active), true))); + envelopes_items.push_back (MenuElem (_("Inactive"), bind (mem_fun (*this, &Editor::set_gain_envelope_active), false))); - items.push_back (MenuElem (_("Toggle envelope visibility"), mem_fun(*this, &Editor::toggle_gain_envelope_visibility))); - items.push_back (MenuElem (_("Toggle envelope active"), mem_fun(*this, &Editor::toggle_gain_envelope_active))); - items.push_back (SeparatorElem()); - - if (ar->scale_amplitude() != 1.0f) { - items.push_back (MenuElem (_("DeNormalize"), mem_fun(*this, &Editor::denormalize_region))); - } else { - items.push_back (MenuElem (_("Normalize"), mem_fun(*this, &Editor::normalize_region))); - } + items.push_back (MenuElem (_("Envelopes"), *envelopes_menu)); + + items.push_back (MenuElem (_("Denormalize"), mem_fun (*this, &Editor::denormalize_regions))); + items.push_back (MenuElem (_("Normalize"), mem_fun (*this, &Editor::normalize_regions))); } - items.push_back (MenuElem (_("Reverse"), mem_fun(*this, &Editor::reverse_region))); + + items.push_back (MenuElem (_("Reverse"), mem_fun(*this, &Editor::reverse_regions))); 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"), mem_fun (*this, &Editor::set_selection_from_audio_region))); + + add_item_with_sensitivity (items, MenuElem (_("Add range markers"), mem_fun (*this, &Editor::add_location_from_audio_region)), selection->regions.size() == 1); + + add_item_with_sensitivity (items, MenuElem (_("Set range selection"), mem_fun (*this, &Editor::set_selection_from_audio_region)), selection->regions.size() == 1); + items.push_back (SeparatorElem()); /* Nudge region */ @@ -1761,13 +1556,12 @@ 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 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)))); + nudge_items.push_back (MenuElem (_("Nudge forward"), (bind (mem_fun(*this, &Editor::nudge_forward), false)))); + nudge_items.push_back (MenuElem (_("Nudge backward"), (bind (mem_fun(*this, &Editor::nudge_backward), false)))); + nudge_items.push_back (MenuElem (_("Nudge forward by capture offset"), (mem_fun(*this, &Editor::nudge_forward_capture_offset)))); + nudge_items.push_back (MenuElem (_("Nudge backward by capture offset"), (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(); @@ -1777,41 +1571,24 @@ Editor::add_region_context_items (AudioStreamView* sv, boost::shared_ptr trim_items.push_back (MenuElem (_("Edit cursor to end"), mem_fun(*this, &Editor::trim_region_to_edit_cursor))); items.push_back (MenuElem (_("Trim"), *trim_menu)); - items.push_back (SeparatorElem()); - items.push_back (MenuElem (_("Split"), (mem_fun(*this, &Editor::split_region)))); - region_edit_menu_split_item = &items.back(); - 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 (_("Fill Track"), (mem_fun(*this, &Editor::region_fill_track)))); + 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 (SeparatorElem()); - items.push_back (MenuElem (_("Destroy"), mem_fun(*this, &Editor::destroy_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. */ - /* we have to hack up the region name because "_" has a special - meaning for menu titles. - */ - - string::size_type pos = 0; - string menu_item_name = region->name(); - - while ((pos = menu_item_name.find ("_", pos)) != string::npos) { - menu_item_name.replace (pos, 1, "__"); - pos += 2; - } - + string const menu_item_name = selection->regions.size() > 1 ? _("Regions") : _("Region"); edit_items.push_back (MenuElem (menu_item_name, *region_menu)); - 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) { @@ -1835,13 +1612,14 @@ Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items) 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 (_("Add range markers"), mem_fun (*this, &Editor::add_location_from_selection))); items.push_back (MenuElem (_("Set range to loop range"), mem_fun(*this, &Editor::set_selection_from_loop))); items.push_back (MenuElem (_("Set range to punch range"), mem_fun(*this, &Editor::set_selection_from_punch))); items.push_back (SeparatorElem()); items.push_back (MenuElem (_("Crop region to range"), mem_fun(*this, &Editor::crop_region_to_selection))); items.push_back (MenuElem (_("Fill range with region"), mem_fun(*this, &Editor::region_fill_selection))); items.push_back (MenuElem (_("Duplicate range"), bind (mem_fun(*this, &Editor::duplicate_dialog), false))); - items.push_back (MenuElem (_("Create chunk from range"), mem_fun(*this, &Editor::name_selection))); + items.push_back (MenuElem (_("Create chunk from range"), mem_fun(*this, &Editor::create_named_selection))); items.push_back (SeparatorElem()); items.push_back (MenuElem (_("Bounce range"), mem_fun(*this, &Editor::bounce_range_selection))); items.push_back (MenuElem (_("Export range"), mem_fun(*this, &Editor::export_selection))); @@ -1850,22 +1628,44 @@ Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items) edit_items.push_back (SeparatorElem()); } +/** Add context menu items relevant to busses or audio tracks. + * @param edit_items List to add the items to. + */ void -Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items) +Editor::add_bus_or_audio_track_context_items (Menu_Helpers::MenuList& edit_items) { using namespace Menu_Helpers; + /* We add every possible action here, and de-sensitize things + that aren't allowed. The sensitivity logic is a bit spread out; + on the one hand I'm using things like can_cut_copy (), which is + reasonably complicated and so perhaps better near the function that + it expresses sensitivity for, and on the other hand checks + in this function as well. You can't really have can_* for everything + or the number of methods would get silly. */ + + bool const one_selected_region = selection->regions.size() == 1; + + /* Count the number of selected audio tracks */ + int n_audio_tracks = 0; + for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) { + RouteTimeAxisView const * r = dynamic_cast(*i); + if (r && r->is_audio_track()) { + n_audio_tracks++; + } + } + /* Playback */ Menu *play_menu = manage (new Menu); MenuList& play_items = play_menu->items(); play_menu->set_name ("ArdourContextMenu"); - play_items.push_back (MenuElem (_("Play from edit cursor"))); + play_items.push_back (MenuElem (_("Play from edit cursor"), mem_fun(*this, &Editor::play_from_edit_cursor))); 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()); - play_items.push_back (MenuElem (_("Loop Region"), mem_fun(*this, &Editor::loop_selected_region))); + add_item_with_sensitivity (play_items, MenuElem (_("Play region"), mem_fun(*this, &Editor::play_selected_region)), one_selected_region); + + add_item_with_sensitivity (play_items, MenuElem (_("Loop region"), mem_fun(*this, &Editor::loop_selected_region)), one_selected_region); edit_items.push_back (MenuElem (_("Play"), *play_menu)); @@ -1874,21 +1674,33 @@ Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items) Menu *select_menu = manage (new Menu); MenuList& select_items = select_menu->items(); select_menu->set_name ("ArdourContextMenu"); + + string str = selection->tracks.size() == 1 ? _("Select all in track") : _("Select all in tracks"); + + select_items.push_back (MenuElem (str, bind (mem_fun(*this, &Editor::select_all_in_selected_tracks), Selection::Set))); + + select_items.push_back (MenuElem (_("Select all"), bind (mem_fun(*this, &Editor::select_all), Selection::Set))); + + str = selection->tracks.size() == 1 ? _("Invert selection in track") : _("Invert selection in tracks"); + + select_items.push_back (MenuElem (str, mem_fun(*this, &Editor::invert_selection_in_selected_tracks))); - select_items.push_back (MenuElem (_("Select All in track"), bind (mem_fun(*this, &Editor::select_all_in_track), Selection::Set))); - select_items.push_back (MenuElem (_("Select All"), bind (mem_fun(*this, &Editor::select_all), Selection::Set))); - 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 (_("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()); + + if (n_audio_tracks) { + 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 (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 (SeparatorElem()); + + if (n_audio_tracks) { + select_items.push_back (MenuElem (_("Select all between cursors"), bind (mem_fun(*this, &Editor::select_all_selectables_between_cursors), playhead_cursor, edit_cursor))); + } edit_items.push_back (MenuElem (_("Select"), *select_menu)); @@ -1897,108 +1709,81 @@ Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items) Menu *cutnpaste_menu = manage (new Menu); MenuList& cutnpaste_items = cutnpaste_menu->items(); cutnpaste_menu->set_name ("ArdourContextMenu"); - - 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 (SeparatorElem()); - - cutnpaste_items.push_back (MenuElem (_("Align"), bind (mem_fun(*this, &Editor::align), ARDOUR::SyncPoint))); - cutnpaste_items.push_back (MenuElem (_("Align Relative"), bind (mem_fun(*this, &Editor::align_relative), ARDOUR::SyncPoint))); - - cutnpaste_items.push_back (SeparatorElem()); - cutnpaste_items.push_back (MenuElem (_("Insert chunk"), bind (mem_fun(*this, &Editor::paste_named_selection), 1.0f))); - - edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu)); - - /* Adding new material */ + add_item_with_sensitivity (cutnpaste_items, MenuElem (_("Cut"), mem_fun(*this, &Editor::cut)), can_cut_copy ()); - 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))); - - /* Nudge track */ - - Menu *nudge_menu = manage (new Menu()); - MenuList& nudge_items = nudge_menu->items(); - nudge_menu->set_name ("ArdourContextMenu"); + add_item_with_sensitivity (cutnpaste_items, MenuElem (_("Copy"), mem_fun(*this, &Editor::copy)), can_cut_copy ()); - 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 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)))); - - edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu)); -} - -void -Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items) -{ - using namespace Menu_Helpers; + if (n_audio_tracks) { + 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 (SeparatorElem()); + + cutnpaste_items.push_back (MenuElem (_("Align"), bind (mem_fun(*this, &Editor::align), ARDOUR::SyncPoint))); + cutnpaste_items.push_back (MenuElem (_("Align relative"), bind (mem_fun(*this, &Editor::align_relative), ARDOUR::SyncPoint))); + cutnpaste_items.push_back (MenuElem (_("Insert chunk"), bind (mem_fun(*this, &Editor::paste_named_selection), 1.0f))); + } else { + cutnpaste_items.push_back (MenuElem (_("Paste"), bind (mem_fun(*this, &Editor::paste), 1.0f))); + } - /* Playback */ + edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu)); + + if (n_audio_tracks) { - Menu *play_menu = manage (new Menu); - MenuList& play_items = play_menu->items(); - play_menu->set_name ("ArdourContextMenu"); + Menu *track_menu = manage (new Menu); + MenuList& track_items = track_menu->items(); + track_menu->set_name ("ArdourContextMenu"); + + /* Adding new material */ - play_items.push_back (MenuElem (_("Play from edit cursor"))); - play_items.push_back (MenuElem (_("Play from start"), mem_fun(*this, &Editor::play_from_start))); - edit_items.push_back (MenuElem (_("Play"), *play_menu)); + add_item_with_sensitivity (track_items, MenuElem (_("Insert selected region"), bind (mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)), n_audio_tracks == 1); + + add_item_with_sensitivity (track_items, MenuElem (_("Insert existing audio"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)), n_audio_tracks == 1); + + /* Nudge */ - /* Selection */ + Menu *nudge_menu = manage (new Menu()); + MenuList& nudge_items = nudge_menu->items(); + nudge_menu->set_name ("ArdourContextMenu"); - Menu *select_menu = manage (new Menu); - MenuList& select_items = select_menu->items(); - select_menu->set_name ("ArdourContextMenu"); - - select_items.push_back (MenuElem (_("Select All in track"), bind (mem_fun(*this, &Editor::select_all_in_track), Selection::Set))); - select_items.push_back (MenuElem (_("Select All"), bind (mem_fun(*this, &Editor::select_all), Selection::Set))); - 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 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))); + str = selection->tracks.size() == 1 ? _("Nudge track forward") : _("Nude tracks forward"); + nudge_items.push_back (MenuElem (str, (bind (mem_fun(*this, &Editor::nudge_selected_tracks), false, true)))); - edit_items.push_back (MenuElem (_("Select"), *select_menu)); + str = selection->tracks.size() == 1 ? _("Nudge track after edit cursor forward") : _("Nudge tracks after edit cursor forward"); + + nudge_items.push_back (MenuElem (str, (bind (mem_fun(*this, &Editor::nudge_selected_tracks), true, true)))); - /* Cut-n-Paste */ + str = selection->tracks.size() == 1 ? _("Nudge track backward") : _("Nudge tracks backward"); + + nudge_items.push_back (MenuElem (str, (bind (mem_fun(*this, &Editor::nudge_selected_tracks), false, false)))); - Menu *cutnpaste_menu = manage (new Menu); - MenuList& cutnpaste_items = cutnpaste_menu->items(); - cutnpaste_menu->set_name ("ArdourContextMenu"); - - 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"), bind (mem_fun(*this, &Editor::paste), 1.0f))); + str = selection->tracks.size() == 1 ? _("Nudge track after edit cursor backward") : _("Nudge tracks after edit cursor backward"); + + nudge_items.push_back (MenuElem (str, (bind (mem_fun(*this, &Editor::nudge_selected_tracks), true, false)))); + + track_items.push_back (MenuElem (_("Nudge"), *nudge_menu)); - Menu *nudge_menu = manage (new Menu()); - MenuList& nudge_items = nudge_menu->items(); - nudge_menu->set_name ("ArdourContextMenu"); - - edit_items.push_back (SeparatorElem()); - nudge_items.push_back (MenuElem (_("Nudge entire track 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 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)))); + /* Freeze */ + track_items.push_back (MenuElem (_("Freeze"), mem_fun(*this, &Editor::freeze_routes))); + track_items.push_back (MenuElem (_("Unfreeze"), mem_fun(*this, &Editor::unfreeze_routes))); - edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu)); + str = selection->tracks.size() == 1 ? _("Track") : _("Tracks"); + edit_items.push_back (MenuElem (str, *track_menu)); + } } /* CURSOR SETTING AND MARKS AND STUFF */ void Editor::set_snap_to (SnapType st) -{ +{ snap_type = st; - vector txt = internationalize (snap_type_strings); - snap_type_selector.set_active_text (txt[(int)st]); + string str = snap_type_strings[(int) st]; + + if (str != snap_type_selector.get_active_text()) { + snap_type_selector.set_active_text (str); + } instant_save (); @@ -2019,8 +1804,11 @@ void Editor::set_snap_mode (SnapMode mode) { snap_mode = mode; - vector txt = internationalize (snap_mode_strings); - snap_mode_selector.set_active_text (txt[(int)mode]); + string str = snap_mode_strings[(int)mode]; + + if (str != snap_mode_selector.get_active_text ()) { + snap_mode_selector.set_active_text (str); + } instant_save (); } @@ -2059,12 +1847,38 @@ Editor::set_state (const XMLNode& node) set_default_size (g.base_width, g.base_height); move (x, y); + if (session && (prop = node.property ("playhead"))) { + nframes_t pos = atol (prop->value().c_str()); + playhead_cursor->set_position (pos); + } else { + playhead_cursor->set_position (0); + + /* reset_x_origin() doesn't work right here, since the old + position may be zero already, and it does nothing in such + circumstances. + */ + + leftmost_frame = 0; + horizontal_adjustment.set_value (0); + } + + if (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"))) { set_zoom_focus ((ZoomFocus) atoi (prop->value())); } if ((prop = node.property ("zoom"))) { - set_frames_per_unit (PBD::atof (prop->value())); + reset_zoom (PBD::atof (prop->value())); } if ((prop = node.property ("snap-to"))) { @@ -2122,16 +1936,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())); @@ -2147,6 +1961,23 @@ Editor::set_state (const XMLNode& node) Glib::RefPtr act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer")); 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); + } + } + + 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")); @@ -2198,6 +2029,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); @@ -2207,6 +2040,11 @@ 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 ("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"); node->add_property ("show-measures", _show_measures ? "yes" : "no"); @@ -2220,6 +2058,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; } @@ -2229,18 +2073,11 @@ Editor::get_state () TimeAxisView * Editor::trackview_by_y_position (double y) { - TrackViewList::iterator iter; - TimeAxisView *tv; - - for (iter = track_views.begin(); iter != track_views.end(); ++iter) { + for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) { - tv = *iter; - - if (tv->hidden()) { - continue; - } + TimeAxisView *tv; - if (tv->y_position <= y && y < ((tv->y_position + tv->height + track_spacing))) { + if ((tv = (*iter)->covers_y_position (y)) != 0) { return tv; } } @@ -2249,7 +2086,7 @@ Editor::trackview_by_y_position (double y) } void -Editor::snap_to (nframes_t& start, int32_t direction, bool for_mark) +Editor::snap_to (nframes64_t& start, int32_t direction, bool for_mark) { Location* before = 0; Location* after = 0; @@ -2258,10 +2095,11 @@ Editor::snap_to (nframes_t& start, int32_t direction, bool for_mark) return; } - const nframes_t one_second = session->frame_rate(); - const nframes_t one_minute = session->frame_rate() * 60; - - nframes_t presnap = start; + const nframes64_t one_second = session->frame_rate(); + const nframes64_t one_minute = session->frame_rate() * 60; + const nframes64_t one_smpte_second = (nframes64_t)(rint(session->smpte_frames_per_second()) * session->frames_per_smpte_frame()); + nframes64_t one_smpte_minute = (nframes64_t)(rint(session->smpte_frames_per_second()) * session->frames_per_smpte_frame() * 60); + nframes64_t presnap = start; switch (snap_type) { case SnapToFrame: @@ -2274,8 +2112,9 @@ Editor::snap_to (nframes_t& start, int32_t direction, bool for_mark) start = (nframes_t) floor ((double) start / (one_second / 75)) * (one_second / 75); } break; + case SnapToSMPTEFrame: - if (direction) { + if (fmod((double)start, (double)session->frames_per_smpte_frame()) > (session->frames_per_smpte_frame() / 2)) { start = (nframes_t) (ceil ((double) start / session->frames_per_smpte_frame()) * session->frames_per_smpte_frame()); } else { start = (nframes_t) (floor ((double) start / session->frames_per_smpte_frame()) * session->frames_per_smpte_frame()); @@ -2289,10 +2128,10 @@ Editor::snap_to (nframes_t& start, int32_t direction, bool for_mark) } else { start -= session->smpte_offset (); } - if (direction > 0) { - start = (nframes_t) ceil ((double) start / one_second) * one_second; + if (start % one_smpte_second > one_smpte_second / 2) { + start = (nframes_t) ceil ((double) start / one_smpte_second) * one_smpte_second; } else { - start = (nframes_t) floor ((double) start / one_second) * one_second; + start = (nframes_t) floor ((double) start / one_smpte_second) * one_smpte_second; } if (session->smpte_offset_negative()) @@ -2310,10 +2149,10 @@ Editor::snap_to (nframes_t& start, int32_t direction, bool for_mark) } else { start -= session->smpte_offset (); } - if (direction) { - start = (nframes_t) ceil ((double) start / one_minute) * one_minute; + if (start % one_smpte_minute > one_smpte_minute / 2) { + start = (nframes_t) ceil ((double) start / one_smpte_minute) * one_smpte_minute; } else { - start = (nframes_t) floor ((double) start / one_minute) * one_minute; + start = (nframes_t) floor ((double) start / one_smpte_minute) * one_smpte_minute; } if (session->smpte_offset_negative()) { @@ -2324,7 +2163,7 @@ Editor::snap_to (nframes_t& start, int32_t direction, bool for_mark) break; case SnapToSeconds: - if (direction) { + if (start % one_second > one_second / 2) { start = (nframes_t) ceil ((double) start / one_second) * one_second; } else { start = (nframes_t) floor ((double) start / one_second) * one_second; @@ -2332,7 +2171,7 @@ Editor::snap_to (nframes_t& start, int32_t direction, bool for_mark) break; case SnapToMinutes: - if (direction) { + if (start % one_minute > one_minute / 2) { start = (nframes_t) ceil ((double) start / one_minute) * one_minute; } else { start = (nframes_t) floor ((double) start / one_minute) * one_minute; @@ -2485,6 +2324,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); @@ -2499,6 +2341,7 @@ Editor::setup_toolbar () 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; @@ -2531,6 +2374,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")); @@ -2538,6 +2382,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); @@ -2545,6 +2390,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)); @@ -2554,6 +2400,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); @@ -2564,35 +2411,33 @@ Editor::setup_toolbar () zoom_box.set_border_width (2); zoom_in_button.set_name ("EditorTimeButton"); + zoom_in_button.set_size_request(-1,16); zoom_in_button.add (*(manage (new Image (::get_icon("zoom_in"))))); zoom_in_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::temporal_zoom_step), false)); ARDOUR_UI::instance()->tooltips().set_tip (zoom_in_button, _("Zoom In")); zoom_out_button.set_name ("EditorTimeButton"); + zoom_out_button.set_size_request(-1,16); zoom_out_button.add (*(manage (new Image (::get_icon("zoom_out"))))); zoom_out_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::temporal_zoom_step), true)); ARDOUR_UI::instance()->tooltips().set_tip (zoom_out_button, _("Zoom Out")); zoom_out_full_button.set_name ("EditorTimeButton"); + zoom_out_full_button.set_size_request(-1,16); zoom_out_full_button.add (*(manage (new Image (::get_icon("zoom_full"))))); zoom_out_full_button.signal_clicked().connect (mem_fun(*this, &Editor::temporal_zoom_session)); ARDOUR_UI::instance()->tooltips().set_tip (zoom_out_full_button, _("Zoom to Session")); - - zoom_box.pack_start (zoom_out_button, false, false); - zoom_box.pack_start (zoom_in_button, false, false); - zoom_box.pack_start (zoom_range_clock, false, false); - zoom_box.pack_start (zoom_out_full_button, false, false); - - ARDOUR_UI::instance()->tooltips().set_tip (zoom_range_clock, _("Current Zoom Range\n(Width of visible area)")); - + zoom_focus_selector.set_name ("ZoomFocusSelector"); - Gtkmm2ext::set_size_request_to_display_given_text (zoom_focus_selector, "Focus Center", 2+FUDGE, 0); - set_popdown_strings (zoom_focus_selector, internationalize (zoom_focus_strings)); + Gtkmm2ext::set_size_request_to_display_given_text (zoom_focus_selector, "Center", FUDGE, 0); + set_popdown_strings (zoom_focus_selector, zoom_focus_strings); zoom_focus_selector.signal_changed().connect (mem_fun(*this, &Editor::zoom_focus_selection_done)); ARDOUR_UI::instance()->tooltips().set_tip (zoom_focus_selector, _("Zoom focus")); - zoom_box.pack_start (zoom_focus_selector, false, false); - + zoom_box.pack_start (zoom_focus_selector, true, true); + zoom_box.pack_start (zoom_out_button, false, false); + zoom_box.pack_start (zoom_in_button, false, false); + zoom_box.pack_start (zoom_out_full_button, false, false); /* Edit Cursor / Snap */ @@ -2601,13 +2446,13 @@ Editor::setup_toolbar () snap_type_selector.set_name ("SnapTypeSelector"); Gtkmm2ext::set_size_request_to_display_given_text (snap_type_selector, "SMPTE Seconds", 2+FUDGE, 10); - set_popdown_strings (snap_type_selector, internationalize (snap_type_strings)); + 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")); snap_mode_selector.set_name ("SnapModeSelector"); Gtkmm2ext::set_size_request_to_display_given_text (snap_mode_selector, "Magnetic Snap", 2+FUDGE, 10); - set_popdown_strings (snap_mode_selector, internationalize (snap_mode_strings)); + set_popdown_strings (snap_mode_selector, snap_mode_strings); snap_mode_selector.signal_changed().connect (mem_fun(*this, &Editor::snap_mode_selection_done)); snap_box.pack_start (edit_cursor_clock, false, false); @@ -2654,7 +2499,7 @@ Editor::setup_toolbar () hbox->pack_start (snap_box, false, false); - hbox->pack_start (zoom_box, false, false); + // hbox->pack_start (zoom_box, false, false); hbox->pack_start (*nudge_box, false, false); hbox->show_all (); @@ -2758,6 +2603,8 @@ Editor::map_transport_state () if (session->transport_stopped()) { have_pending_keyboard_selection = false; } + + update_loop_range_view (true); } /* UNDO/REDO */ @@ -2818,597 +2665,116 @@ Editor::commit_reversible_command () } } -bool -Editor::set_selected_track_from_click (bool press, Selection::Operation op, bool with_undo, bool no_remove) +void +Editor::set_edit_group_solo (Route& route, bool yn) { - bool commit = false; + RouteGroup *edit_group; - if (!clicked_axisview) { - return false; + if ((edit_group = route.edit_group()) != 0) { + edit_group->apply (&Route::set_solo, yn, this); + } else { + route.set_solo (yn, this); } +} - switch (op) { - case Selection::Toggle: - if (selection->selected (clicked_axisview)) { - if (!no_remove) { - selection->remove (clicked_axisview); - commit = true; - } - } else { - selection->add (clicked_axisview); - commit = false; - } - break; - - case Selection::Set: - if (selection->selected (clicked_axisview) && selection->tracks.size() == 1) { - /* no commit necessary */ - } +void +Editor::set_edit_group_mute (Route& route, bool yn) +{ + RouteGroup *edit_group = 0; - selection->set (clicked_axisview); - break; - - case Selection::Extend: - /* not defined yet */ - break; + if ((edit_group == route.edit_group()) != 0) { + edit_group->apply (&Route::set_mute, yn, this); + } else { + route.set_mute (yn, this); } - - return commit; } - -bool -Editor::set_selected_control_point_from_click (bool press, Selection::Operation op, bool with_undo, bool no_remove) + +void +Editor::history_changed () { - if (!clicked_control_point) { - return false; - } - - /* select this point and any others that it represents */ - - double y1, y2; - nframes_t x1, x2; + string label; - 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 (undo_action && session) { + if (session->undo_depth() == 0) { + label = _("Undo"); + } else { + label = string_compose(_("Undo (%1)"), session->next_undo()); + } + undo_action->property_label() = label; + } - return select_all_within (x1, x2, y1, y2, op); + 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::get_relevant_tracks (RouteTimeAxisView& base, set& relevant_tracks) +Editor::duplicate_dialog (bool dup_region) { - /* step one: get all selected tracks and all tracks in the relevant edit groups */ + if (selection->regions.empty() && (selection->time.length() == 0)) { + return; + } - for (TrackSelection::iterator ti = selection->tracks.begin(); ti != selection->tracks.end(); ++ti) { + ArdourDialog win ("duplicate dialog"); + Label label (_("Duplicate how many times?")); + Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0); + SpinButton spinner (adjustment); - RouteTimeAxisView* atv = dynamic_cast(*ti); + win.get_vbox()->set_spacing (12); + win.get_vbox()->pack_start (label); - if (!atv) { - continue; - } + /* dialogs have ::add_action_widget() but that puts the spinner in the wrong + place, visually. so do this by hand. + */ - RouteGroup* group = atv->route()->edit_group(); + win.get_vbox()->pack_start (spinner); + spinner.signal_activate().connect (sigc::bind (mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT)); - if (group && group->is_active()) { - - /* active group for this track, loop over all tracks and get every member of the group */ + label.show (); + spinner.show (); - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - - RouteTimeAxisView* tatv; - - if ((tatv = dynamic_cast (*i)) != 0) { - - if (tatv->route()->edit_group() == group) { - relevant_tracks.insert (tatv); - } - } - } + win.add_button (Stock::OK, RESPONSE_ACCEPT); + win.add_button (Stock::CANCEL, RESPONSE_CANCEL); - } else { + win.set_position (WIN_POS_MOUSE); - /* no active group, or no group */ + spinner.grab_focus (); - relevant_tracks.insert (&base); - } + switch (win.run ()) { + case RESPONSE_ACCEPT: + break; + default: + return; + } + float times = adjustment.get_value(); + + if (!selection->regions.empty()) { + duplicate_some_regions (selection->regions, times); + } else { + duplicate_selection (times); } } void -Editor::mapover_tracks (slot sl) +Editor::show_verbose_canvas_cursor () { - set relevant_tracks; - - if (!clicked_routeview) { - return; - } - - get_relevant_tracks (*clicked_routeview, relevant_tracks); - - uint32_t sz = relevant_tracks.size(); - - for (set::iterator ati = relevant_tracks.begin(); ati != relevant_tracks.end(); ++ati) { - sl (**ati, sz); - } + verbose_canvas_cursor->raise_to_top(); + verbose_canvas_cursor->show(); + verbose_cursor_visible = true; } void -Editor::mapped_set_selected_regionview_from_click (RouteTimeAxisView& tv, uint32_t ignored, - RegionView* basis, vector* all_equivs) +Editor::hide_verbose_canvas_cursor () { - Playlist* pl; - vector > results; - RegionView* marv; - boost::shared_ptr ds; - - if ((ds = tv.get_diskstream()) == 0) { - /* bus */ - return; - } - - if (&tv == &basis->get_time_axis_view()) { - /* looking in same track as the original */ - return; - } - - - if ((pl = dynamic_cast(ds->playlist())) != 0) { - pl->get_equivalent_regions (basis->region(), results); - } - - for (vector >::iterator ir = results.begin(); ir != results.end(); ++ir) { - if ((marv = tv.view()->find_view (*ir)) != 0) { - all_equivs->push_back (marv); - } - } -} - -bool -Editor::set_selected_regionview_from_click (bool press, Selection::Operation op, bool no_track_remove) -{ - vector all_equivalent_regions; - bool commit = false; - - if (!clicked_regionview || !clicked_routeview) { - return false; - } - - if (op == Selection::Toggle || op == Selection::Set) { - - mapover_tracks (bind (mem_fun (*this, &Editor::mapped_set_selected_regionview_from_click), - clicked_regionview, &all_equivalent_regions)); - - - /* add clicked regionview since we skipped all other regions in the same track as the one it was in */ - - all_equivalent_regions.push_back (clicked_regionview); - - switch (op) { - case Selection::Toggle: - - if (clicked_regionview->get_selected()) { - if (press) { - - /* whatever was clicked was selected already; do nothing here but allow - the button release to deselect it - */ - - button_release_can_deselect = true; - - } else { - - if (button_release_can_deselect) { - - /* just remove this one region, but only on a permitted button release */ - - selection->remove (clicked_regionview); - commit = true; - - /* no more deselect action on button release till a new press - finds an already selected object. - */ - - button_release_can_deselect = false; - } - } - - } else { - - if (press) { - /* add all the equivalent regions, but only on button press */ - - if (!all_equivalent_regions.empty()) { - commit = true; - } - - for (vector::iterator i = all_equivalent_regions.begin(); i != all_equivalent_regions.end(); ++i) { - selection->add (*i); - } - } - } - break; - - case Selection::Set: - if (!clicked_regionview->get_selected()) { - selection->set (all_equivalent_regions); - commit = true; - } else { - /* no commit necessary: clicked on an already selected region */ - goto out; - } - break; - - default: - /* silly compiler */ - break; - } - - } else if (op == Selection::Extend) { - - list results; - nframes_t last_frame; - nframes_t first_frame; - - /* 1. find the last selected regionview in the track that was clicked in */ - - last_frame = 0; - first_frame = max_frames; - - for (RegionSelection::iterator x = selection->regions.begin(); x != selection->regions.end(); ++x) { - if (&(*x)->get_time_axis_view() == &clicked_regionview->get_time_axis_view()) { - - if ((*x)->region()->last_frame() > last_frame) { - last_frame = (*x)->region()->last_frame(); - } - - if ((*x)->region()->first_frame() < first_frame) { - first_frame = (*x)->region()->first_frame(); - } - } - } - - /* 2. figure out the boundaries for our search for new objects */ - - switch (clicked_regionview->region()->coverage (first_frame, last_frame)) { - case OverlapNone: - if (last_frame < clicked_regionview->region()->first_frame()) { - first_frame = last_frame; - last_frame = clicked_regionview->region()->last_frame(); - } else { - last_frame = first_frame; - first_frame = clicked_regionview->region()->first_frame(); - } - break; - - case OverlapExternal: - if (last_frame < clicked_regionview->region()->first_frame()) { - first_frame = last_frame; - last_frame = clicked_regionview->region()->last_frame(); - } else { - last_frame = first_frame; - first_frame = clicked_regionview->region()->first_frame(); - } - break; - - case OverlapInternal: - if (last_frame < clicked_regionview->region()->first_frame()) { - first_frame = last_frame; - last_frame = clicked_regionview->region()->last_frame(); - } else { - last_frame = first_frame; - first_frame = clicked_regionview->region()->first_frame(); - } - break; - - case OverlapStart: - case OverlapEnd: - /* nothing to do except add clicked region to selection, since it - overlaps with the existing selection in this track. - */ - break; - } - - /* 2. find all selectable objects (regionviews in this case) between that one and the end of the - one that was clicked. - */ - - set relevant_tracks; - - get_relevant_tracks (*clicked_routeview, relevant_tracks); - - for (set::iterator t = relevant_tracks.begin(); t != relevant_tracks.end(); ++t) { - (*t)->get_selectables (first_frame, last_frame, -1.0, -1.0, results); - } - - /* 3. convert to a vector of audio regions */ - - vector regions; - - for (list::iterator x = results.begin(); x != results.end(); ++x) { - RegionView* arv; - - if ((arv = dynamic_cast(*x)) != 0) { - regions.push_back (arv); - } - } - - if (!regions.empty()) { - selection->add (regions); - commit = true; - } - } - - out: - return commit; -} - -void -Editor::set_selected_regionview_from_region_list (boost::shared_ptr region, Selection::Operation op) -{ - vector all_equivalent_regions; - - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - - RouteTimeAxisView* tatv; - - if ((tatv = dynamic_cast (*i)) != 0) { - - Playlist* pl; - vector > results; - RegionView* marv; - boost::shared_ptr ds; - - if ((ds = tatv->get_diskstream()) == 0) { - /* bus */ - continue; - } - - if ((pl = dynamic_cast(ds->playlist())) != 0) { - pl->get_region_list_equivalent_regions (region, results); - } - - for (vector >::iterator ir = results.begin(); ir != results.end(); ++ir) { - if ((marv = tatv->view()->find_view (*ir)) != 0) { - all_equivalent_regions.push_back (marv); - } - } - - } - } - - begin_reversible_command (_("set selected regions")); - - switch (op) { - case Selection::Toggle: - /* XXX this is not correct */ - selection->toggle (all_equivalent_regions); - break; - case Selection::Set: - selection->set (all_equivalent_regions); - break; - case Selection::Extend: - selection->add (all_equivalent_regions); - break; - } - - commit_reversible_command () ; -} - -bool -Editor::set_selected_regionview_from_map_event (GdkEventAny* ev, StreamView* sv, boost::weak_ptr weak_r) -{ - RegionView* rv; - boost::shared_ptr r (weak_r.lock()); - - if (!r) { - return true; - } - - boost::shared_ptr ar; - - if ((ar = boost::dynamic_pointer_cast (r)) == 0) { - return true; - } - - if ((rv = sv->find_view (ar)) == 0) { - return true; - } - - /* don't reset the selection if its something other than - a single other region. - */ - - if (selection->regions.size() > 1) { - return true; - } - - begin_reversible_command (_("set selected regions")); - - selection->set (rv); - - commit_reversible_command () ; - - return true; -} - -void -Editor::set_edit_group_solo (Route& route, bool yn) -{ - RouteGroup *edit_group; - - if ((edit_group = route.edit_group()) != 0) { - edit_group->apply (&Route::set_solo, yn, this); - } else { - route.set_solo (yn, this); - } -} - -void -Editor::set_edit_group_mute (Route& route, bool yn) -{ - 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); - } -} - -void -Editor::set_edit_menu (Menu& menu) -{ - edit_menu = &menu; - edit_menu->signal_map_event().connect (mem_fun(*this, &Editor::edit_menu_map_handler)); -} - -bool -Editor::edit_menu_map_handler (GdkEventAny* ev) -{ - using namespace Menu_Helpers; - MenuList& edit_items = edit_menu->items(); - string label; - - /* Nuke all the old items */ - - edit_items.clear (); - - if (session == 0) { - return false; - } - - if (session->undo_depth() == 0) { - label = _("Undo"); - } else { - label = string_compose(_("Undo (%1)"), session->next_undo()); - } - - edit_items.push_back (MenuElem (label, bind (mem_fun(*this, &Editor::undo), 1U))); - - if (session->undo_depth() == 0) { - edit_items.back().set_sensitive (false); - } - - if (session->redo_depth() == 0) { - label = _("Redo"); - } else { - label = string_compose(_("Redo (%1)"), session->next_redo()); - } - - edit_items.push_back (MenuElem (label, bind (mem_fun(*this, &Editor::redo), 1U))); - if (session->redo_depth() == 0) { - edit_items.back().set_sensitive (false); - } - - vector mitems; - - edit_items.push_back (SeparatorElem()); - edit_items.push_back (MenuElem (_("Cut"), mem_fun(*this, &Editor::cut))); - mitems.push_back (&edit_items.back()); - edit_items.push_back (MenuElem (_("Copy"), mem_fun(*this, &Editor::copy))); - mitems.push_back (&edit_items.back()); - edit_items.push_back (MenuElem (_("Paste"), bind (mem_fun(*this, &Editor::paste), 1.0f))); - mitems.push_back (&edit_items.back()); - edit_items.push_back (SeparatorElem()); - edit_items.push_back (MenuElem (_("Align"), bind (mem_fun(*this, &Editor::align), ARDOUR::SyncPoint))); - mitems.push_back (&edit_items.back()); - edit_items.push_back (MenuElem (_("Align Relative"), bind (mem_fun(*this, &Editor::align_relative), ARDOUR::SyncPoint))); - mitems.push_back (&edit_items.back()); - edit_items.push_back (SeparatorElem()); - - if (selection->empty()) { - for (vector::iterator i = mitems.begin(); i != mitems.end(); ++i) { - (*i)->set_sensitive (false); - } - } - - Menu* import_menu = manage (new Menu()); - import_menu->set_name ("ArdourContextMenu"); - MenuList& import_items = import_menu->items(); - - import_items.push_back (MenuElem (_("... as new track"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportAsTrack))); - import_items.push_back (MenuElem (_("... as new region"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportAsRegion))); - - edit_items.push_back (MenuElem (_("Import audio (copy)"), *import_menu)); - edit_items.push_back (SeparatorElem()); - - edit_items.push_back (MenuElem (_("Remove last capture"), mem_fun(*this, &Editor::remove_last_capture))); - - if (!session->have_captured()) { - edit_items.back().set_sensitive (false); - } - - return false; -} - -void -Editor::duplicate_dialog (bool dup_region) -{ - if (dup_region) { - if (clicked_regionview == 0) { - return; - } - } else { - if (selection->time.length() == 0) { - return; - } - } - - 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, entry.get_text_length()); - entry.grab_focus (); - - - switch (win.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); - } - } -} - -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; -} + verbose_canvas_cursor->hide(); + verbose_cursor_visible = false; +} void Editor::set_verbose_canvas_cursor (const string & txt, double x, double y) @@ -3449,10 +2815,6 @@ Editor::edit_mode_selection_done () void Editor::snap_type_selection_done () { - if (session == 0) { - return; - } - string choice = snap_type_selector.get_active_text(); SnapType snaptype = SnapToFrame; @@ -3498,35 +2860,34 @@ Editor::snap_type_selection_done () snaptype = SnapToFrame; } - set_snap_to (snaptype); + RefPtr ract = snap_type_action (snaptype); + if (ract) { + ract->set_active (); + } } void Editor::snap_mode_selection_done () { - if(session == 0) { - return; - } - string choice = snap_mode_selector.get_active_text(); SnapMode mode = SnapNormal; - if (choice == _("Normal Snap")) { + if (choice == _("Normal")) { mode = SnapNormal; - } else if (choice == _("Magnetic Snap")) { + } else if (choice == _("Magnetic")) { mode = SnapMagnetic; } - set_snap_mode (mode); + RefPtr ract = snap_mode_action (mode); + + if (ract) { + ract->set_active (true); + } } void Editor::zoom_focus_selection_done () { - if (session == 0) { - return; - } - string choice = zoom_focus_selector.get_active_text(); ZoomFocus focus_type = ZoomFocusLeft; @@ -3536,100 +2897,38 @@ Editor::zoom_focus_selection_done () focus_type = ZoomFocusRight; } else if (choice == _("Center")) { focus_type = ZoomFocusCenter; - } else if (choice == _("Playhead")) { + } else if (choice == _("Play")) { focus_type = ZoomFocusPlayhead; - } else if (choice == _("Edit Cursor")) { + } else if (choice == _("Edit")) { focus_type = ZoomFocusEdit; } + + RefPtr ract = zoom_focus_action (focus_type); - set_zoom_focus (focus_type); + if (ract) { + ract->set_active (); + } } gint Editor::edit_controls_button_release (GdkEventButton* ev) { if (Keyboard::is_context_menu_event (ev)) { - ARDOUR_UI::instance()->add_route (); + ARDOUR_UI::instance()->add_route (this); } return TRUE; } -void -Editor::track_selection_changed () -{ - switch (selection->tracks.size()){ - case 0: - break; - default: - set_selected_mixer_strip (*(selection->tracks.front())); - break; - } - - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - (*i)->set_selected (false); - if (mouse_mode == MouseRange) { - (*i)->hide_selection (); - } - } - - 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); - } - } -} - -void -Editor::time_selection_changed () -{ - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - (*i)->hide_selection (); - } - - if (selection->tracks.empty()) { - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - (*i)->show_selection (selection->time); - } - } else { - for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) { - (*i)->show_selection (selection->time); - } - } - - if (selection->time.empty()) { - ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, false); - } else { - ActionManager::set_sensitive (ActionManager::time_selection_sensitive_actions, true); - } -} - -void -Editor::region_selection_changed () -{ - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - (*i)->set_selected_regionviews (selection->regions); - } -} - -void -Editor::point_selection_changed () -{ - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - (*i)->set_selected_points (selection->points); - } -} - gint Editor::mouse_select_button_release (GdkEventButton* ev) { /* this handles just right-clicks */ if (ev->button != 3) { - return FALSE; + return false; } - return TRUE; + return true; } Editor::TrackViewList * @@ -3673,8 +2972,11 @@ Editor::get_valid_views (TimeAxisView* track, RouteGroup* group) void Editor::set_zoom_focus (ZoomFocus f) { - vector txt = internationalize (zoom_focus_strings); - zoom_focus_selector.set_active_text (txt[(int)f]); + string str = zoom_focus_strings[(int)f]; + + if (str != zoom_focus_selector.get_active_text()) { + zoom_focus_selector.set_active_text (str); + } if (zoom_focus != f) { zoom_focus = f; @@ -3761,7 +3063,6 @@ Editor::set_show_measures (bool yn) if ((_show_measures = yn) == true) { draw_measures (); } - DisplayControlChanged (ShowMeasures); instant_save (); } } @@ -3784,27 +3085,38 @@ Editor::set_follow_playhead (bool yn) /* catch up */ update_current_screen (); } - DisplayControlChanged (FollowPlayhead); instant_save (); } } void -Editor::toggle_xfade_active (Crossfade* xfade) +Editor::toggle_xfade_active (boost::weak_ptr wxfade) { - xfade->set_active (!xfade->active()); + boost::shared_ptr xfade (wxfade.lock()); + if (xfade) { + xfade->set_active (!xfade->active()); + } } void -Editor::toggle_xfade_length (Crossfade* xfade) +Editor::toggle_xfade_length (boost::weak_ptr wxfade) { - xfade->set_follow_overlap (!xfade->following_overlap()); + boost::shared_ptr xfade (wxfade.lock()); + if (xfade) { + xfade->set_follow_overlap (!xfade->following_overlap()); + } } void -Editor::edit_xfade (Crossfade* xfade) +Editor::edit_xfade (boost::weak_ptr wxfade) { - CrossfadeEditor cew (*session, *xfade, xfade->fade_in().get_min_y(), 1.0); + boost::shared_ptr xfade (wxfade.lock()); + + if (!xfade) { + return; + } + + CrossfadeEditor cew (*session, xfade, xfade->fade_in().get_min_y(), 1.0); ensure_float (cew); @@ -3844,19 +3156,21 @@ Editor::end_location_changed (Location* location) } int -Editor::playlist_deletion_dialog (Playlist* pl) +Editor::playlist_deletion_dialog (boost::shared_ptr pl) { ArdourDialog dialog ("playlist deletion dialog"); Label label (string_compose (_("Playlist %1 is currently unused.\n" - "If left alone, no audio files used by it will be cleaned.\n" - "If deleted, audio files used by it alone by will cleaned."), - pl->name())); - + "If left alone, no audio files used by it will be cleaned.\n" + "If deleted, audio files used by it alone by will cleaned."), + pl->name())); + dialog.set_position (WIN_POS_CENTER); dialog.get_vbox()->pack_start (label); + label.show (); + dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT); - dialog.add_button (_("Keep playlist"), RESPONSE_CANCEL); + dialog.add_button (_("Keep playlist"), RESPONSE_REJECT); dialog.add_button (_("Cancel"), RESPONSE_CANCEL); switch (dialog.run ()) { @@ -3940,6 +3254,9 @@ Editor::control_layout_scroll (GdkEventScroll* ev) return false; } + +/** A new snapshot has been selected. + */ void Editor::snapshot_display_selection_changed () { @@ -3964,7 +3281,87 @@ Editor::snapshot_display_selection_changed () bool Editor::snapshot_display_button_press (GdkEventButton* ev) { - return false; + if (ev->button == 3) { + /* Right-click on the snapshot list. Work out which snapshot it + was over. */ + Gtk::TreeModel::Path path; + Gtk::TreeViewColumn* col; + int cx; + int cy; + snapshot_display.get_path_at_pos ((int) ev->x, (int) ev->y, path, col, cx, cy); + Gtk::TreeModel::iterator iter = snapshot_display_model->get_iter (path); + if (iter) { + Gtk::TreeModel::Row row = *iter; + popup_snapshot_context_menu (ev->button, ev->time, row[snapshot_display_columns.real_name]); + } + return true; + } + + return false; +} + + +/** Pop up the snapshot display context menu. + * @param button Button used to open the menu. + * @param time Menu open time. + * @snapshot_name Name of the snapshot that the menu click was over. + */ + +void +Editor::popup_snapshot_context_menu (int button, int32_t time, Glib::ustring snapshot_name) +{ + using namespace Menu_Helpers; + + MenuList& items (snapshot_context_menu.items()); + items.clear (); + + const bool modification_allowed = (session->snap_name() != snapshot_name && session->name() != snapshot_name); + + 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::rename_snapshot (Glib::ustring old_name) +{ + ArdourPrompter prompter(true); + + string new_name; + + prompter.set_name ("Prompter"); + prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT); + prompter.set_prompt (_("New name of snapshot")); + prompter.set_initial_text (old_name); + + if (prompter.run() == RESPONSE_ACCEPT) { + prompter.get_result (new_name); + if (new_name.length()) { + session->rename_state (old_name, new_name); + redisplay_snapshots (); + } + } +} + + +void +Editor::remove_snapshot (Glib::ustring name) +{ + vector choices; + + std::string prompt = string_compose (_("Do you really want to remove snapshot \"%1\" ?\n(cannot be undone)"), name); + + choices.push_back (_("No, do nothing.")); + choices.push_back (_("Yes, remove it.")); + + Gtkmm2ext::Choice prompter (prompt, choices); + + if (prompter.run () == 1) { + session->remove_state (name); + redisplay_snapshots (); + } } void @@ -3985,7 +3382,19 @@ Editor::redisplay_snapshots () for (vector::iterator i = states->begin(); i != states->end(); ++i) { string statename = *(*i); TreeModel::Row row = *(snapshot_display_model->append()); - row[snapshot_display_columns.visible_name] = statename; + + /* this lingers on in case we ever want to change the visible + name of the snapshot. + */ + + string display_name; + display_name = statename; + + if (statename == session->snap_name()) { + snapshot_display.get_selection()->select(row); + } + + row[snapshot_display_columns.visible_name] = display_name; row[snapshot_display_columns.real_name] = statename; } @@ -4002,24 +3411,48 @@ Editor::session_state_saved (string snap_name) void Editor::maximise_editing_space () { + initial_ruler_update_required = true; + mouse_mode_tearoff->set_visible (false); tools_tearoff->set_visible (false); pre_maximal_pane_position = edit_pane.get_position(); - edit_pane.set_position (edit_pane.get_width()); + pre_maximal_editor_width = this->get_width(); + + if(post_maximal_pane_position == 0) { + post_maximal_pane_position = edit_pane.get_width(); + } + 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); + } } void Editor::restore_editing_space () { + initial_ruler_update_required = true; + + // user changed width of pane during fullscreen + if(post_maximal_pane_position != edit_pane.get_position()) { + post_maximal_pane_position = edit_pane.get_position(); + } + + unfullscreen(); + 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); - unfullscreen(); + edit_pane.set_position ( + pre_maximal_pane_position + abs(this->get_width() - pre_maximal_editor_width) + ); } void @@ -4070,3 +3503,160 @@ Editor::on_key_press_event (GdkEventKey* ev) return key_press_focus_accelerator_handler (*this, ev); } +void +Editor::reset_x_origin (nframes_t frame) +{ + queue_visual_change (frame); +} + +void +Editor::reset_zoom (double fpu) +{ + queue_visual_change (fpu); +} + +void +Editor::reposition_and_zoom (nframes_t frame, double fpu) +{ + reset_x_origin (frame); + reset_zoom (fpu); +} + +void +Editor::set_frames_per_unit (double fpu) +{ + nframes_t frames; + + /* this is the core function that controls the zoom level of the canvas. it is called + whenever one or more calls are made to reset_zoom(). it executes in an idle handler. + */ + + if (fpu == frames_per_unit) { + return; + } + + if (fpu < 2.0) { + fpu = 2.0; + } + + // convert fpu to frame count + + frames = (nframes_t) floor (fpu * canvas_width); + + /* don't allow zooms that fit more than the maximum number + of frames into an 800 pixel wide space. + */ + + if (max_frames / fpu < 800.0) { + return; + } + + if (fpu == frames_per_unit) { + return; + } + + frames_per_unit = fpu; + + if (frames != zoom_range_clock.current_duration()) { + zoom_range_clock.set (frames); + } + + if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) { + if (!selection->tracks.empty()) { + for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) { + (*i)->reshow_selection (selection->time); + } + } else { + for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { + (*i)->reshow_selection (selection->time); + } + } + } + + ZoomChanged (); /* EMIT_SIGNAL */ + + reset_hscrollbar_stepping (); + reset_scrolling_region (); + + if (edit_cursor) edit_cursor->set_position (edit_cursor->current_frame); + if (playhead_cursor) playhead_cursor->set_position (playhead_cursor->current_frame); + + instant_save (); +} + +void +Editor::queue_visual_change (nframes_t where) +{ + pending_visual_change.pending = VisualChange::Type (pending_visual_change.pending | VisualChange::TimeOrigin); + pending_visual_change.time_origin = where; + + if (pending_visual_change.idle_handler_id < 0) { + pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this); + } +} + +void +Editor::queue_visual_change (double fpu) +{ + pending_visual_change.pending = VisualChange::Type (pending_visual_change.pending | VisualChange::ZoomLevel); + pending_visual_change.frames_per_unit = fpu; + + if (pending_visual_change.idle_handler_id < 0) { + pending_visual_change.idle_handler_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE, _idle_visual_changer, this, 0); + } +} + +int +Editor::_idle_visual_changer (void* arg) +{ + return static_cast(arg)->idle_visual_changer (); +} + +int +Editor::idle_visual_changer () +{ + VisualChange::Type p = pending_visual_change.pending; + + pending_visual_change.pending = (VisualChange::Type) 0; + pending_visual_change.idle_handler_id = -1; + + if (p & VisualChange::ZoomLevel) { + set_frames_per_unit (pending_visual_change.frames_per_unit); + } + + if (p & VisualChange::TimeOrigin) { + if (pending_visual_change.time_origin != leftmost_frame) { + horizontal_adjustment.set_value (pending_visual_change.time_origin/frames_per_unit); + /* the signal handler will do the rest */ + } else { + update_fixed_rulers(); + redisplay_tempo (true); + } + } + + return 0; /* this is always a one-shot call */ +} + +struct EditorOrderTimeAxisSorter { + bool operator() (const TimeAxisView* a, const TimeAxisView* b) const { + return a->order < b->order; + } +}; + +void +Editor::sort_track_selection () +{ + EditorOrderTimeAxisSorter cmp; + selection->tracks.sort (cmp); +} + +nframes_t +Editor::edit_cursor_position(bool sync) +{ + if (sync && edit_cursor->current_frame != edit_cursor_clock.current_time()) { + edit_cursor_clock.set(edit_cursor->current_frame, true); + } + + return edit_cursor->current_frame; +} +