X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Feditor.cc;h=5be3f245c7520be92f736fbc67b2f1f3ef2d8776;hb=6ab6ad5bb732512d04593b1a5d566d5e787d45ed;hp=aad0f7b11a81d8e5d8a0b691247f2d7ab7a0cefd;hpb=ded4a143db069785aa33eeb6dc9da02770e3ae8e;p=ardour.git diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index aad0f7b11a..5be3f245c7 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -19,7 +19,6 @@ /* Note: public Editor methods are documented in public_editor.h */ -#define __STDC_LIMIT_MACROS 1 #include #include #include @@ -50,13 +49,14 @@ #include #include -#include -#include -#include -#include -#include -#include -#include +#include "gtkmm2ext/bindings.h" +#include "gtkmm2ext/grouped_buttons.h" +#include "gtkmm2ext/gtk_ui.h" +#include "gtkmm2ext/tearoff.h" +#include "gtkmm2ext/utils.h" +#include "gtkmm2ext/window_title.h" +#include "gtkmm2ext/choice.h" +#include "gtkmm2ext/cell_renderer_pixbuf_toggle.h" #include "ardour/audio_diskstream.h" #include "ardour/audio_track.h" @@ -77,45 +77,50 @@ #include "control_protocol/control_protocol.h" -#include "editor.h" -#include "keyboard.h" -#include "marker.h" -#include "playlist_selector.h" +#include "actions.h" +#include "actions.h" +#include "analysis_window.h" +#include "audio_clock.h" #include "audio_region_view.h" -#include "rgb_macros.h" -#include "selection.h" #include "audio_streamview.h" -#include "time_axis_view.h" #include "audio_time_axis.h" -#include "utils.h" -#include "crossfade_view.h" +#include "automation_time_axis.h" +#include "bundle_manager.h" #include "canvas-noevent-text.h" -#include "editing.h" -#include "public_editor.h" -#include "crossfade_edit.h" #include "canvas_impl.h" -#include "actions.h" -#include "sfdb_ui.h" -#include "gui_thread.h" -#include "simpleline.h" -#include "rhythm_ferret.h" -#include "actions.h" -#include "tempo_lines.h" -#include "analysis_window.h" -#include "bundle_manager.h" -#include "global_port_matrix.h" +#include "crossfade_edit.h" +#include "crossfade_view.h" +#include "debug.h" +#include "editing.h" +#include "editor.h" +#include "editor_cursors.h" #include "editor_drag.h" #include "editor_group_tabs.h" -#include "automation_time_axis.h" -#include "editor_routes.h" -#include "midi_time_axis.h" -#include "mixer_strip.h" -#include "editor_route_groups.h" -#include "editor_regions.h" #include "editor_locations.h" +#include "editor_regions.h" +#include "editor_route_groups.h" +#include "editor_routes.h" #include "editor_snapshots.h" #include "editor_summary.h" +#include "global_port_matrix.h" +#include "gui_object.h" +#include "gui_thread.h" +#include "keyboard.h" +#include "marker.h" +#include "midi_time_axis.h" +#include "mixer_strip.h" +#include "mouse_cursors.h" +#include "playlist_selector.h" +#include "public_editor.h" #include "region_layering_order_editor.h" +#include "rgb_macros.h" +#include "rhythm_ferret.h" +#include "selection.h" +#include "sfdb_ui.h" +#include "simpleline.h" +#include "tempo_lines.h" +#include "time_axis_view.h" +#include "utils.h" #include "i18n.h" @@ -137,8 +142,6 @@ using Gtkmm2ext::Keyboard; const double Editor::timebar_height = 15.0; -#include "editor_xpms" - static const gchar *_snap_type_strings[] = { N_("CD Frames"), N_("Timecode Frames"), @@ -208,32 +211,6 @@ static const gchar *_rb_opt_strings[] = { }; #endif -/* Soundfile drag-n-drop */ - -Gdk::Cursor* Editor::cross_hair_cursor = 0; -Gdk::Cursor* Editor::selector_cursor = 0; -Gdk::Cursor* Editor::trimmer_cursor = 0; -Gdk::Cursor* Editor::left_side_trim_cursor = 0; -Gdk::Cursor* Editor::right_side_trim_cursor = 0; -Gdk::Cursor* Editor::fade_in_cursor = 0; -Gdk::Cursor* Editor::fade_out_cursor = 0; -Gdk::Cursor* Editor::grabber_cursor = 0; -Gdk::Cursor* Editor::grabber_note_cursor = 0; -Gdk::Cursor* Editor::grabber_edit_point_cursor = 0; -Gdk::Cursor* Editor::zoom_in_cursor = 0; -Gdk::Cursor* Editor::zoom_out_cursor = 0; -Gdk::Cursor* Editor::time_fx_cursor = 0; -Gdk::Cursor* Editor::fader_cursor = 0; -Gdk::Cursor* Editor::speaker_cursor = 0; -Gdk::Cursor* Editor::midi_pencil_cursor = 0; -Gdk::Cursor* Editor::midi_select_cursor = 0; -Gdk::Cursor* Editor::midi_resize_cursor = 0; -Gdk::Cursor* Editor::midi_erase_cursor = 0; -Gdk::Cursor* Editor::wait_cursor = 0; -Gdk::Cursor* Editor::timebar_cursor = 0; -Gdk::Cursor* Editor::transparent_cursor = 0; -Gdk::Cursor* Editor::up_down_cursor = 0; - void show_me_the_size (Requisition* r, const char* what) { @@ -244,20 +221,20 @@ show_me_the_size (Requisition* r, const char* what) static void pane_size_watcher (Paned* pane) { - /* if the handle of a pane vanishes into (at least) the tabs of a notebook, - it is no longer accessible. so stop that. this doesn't happen on X11, - just the quartz backend. + /* if the handle of a pane vanishes into (at least) the tabs of a notebook, + it is no longer accessible. so stop that. this doesn't happen on X11, + just the quartz backend. - ugh. - */ + ugh. + */ - int max_width_of_lhs = GTK_WIDGET(pane->gobj())->allocation.width - 25; + int max_width_of_lhs = GTK_WIDGET(pane->gobj())->allocation.width - 25; - gint pos = pane->get_position (); + gint pos = pane->get_position (); - if (pos > max_width_of_lhs) { - pane->set_position (max_width_of_lhs); - } + if (pos > max_width_of_lhs) { + pane->set_position (max_width_of_lhs); + } } #endif @@ -268,7 +245,7 @@ Editor::Editor () , minsec_label (_("Mins:Secs")) , bbt_label (_("Bars:Beats")) , timecode_label (_("Timecode")) - , frame_label (_("Samples")) + , samples_label (_("Samples")) , tempo_label (_("Tempo")) , meter_label (_("Meter")) , mark_label (_("Location Markers")) @@ -285,13 +262,13 @@ Editor::Editor () /* tool bar related */ - , zoom_range_clock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, false, true) + , zoom_range_clock (new AudioClock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, false, true)) , toolbar_selection_clock_table (2,3) , automation_mode_button (_("mode")) - , global_automation_button (_("automation")) + , _toolbar_viewport (*manage (new Gtk::Adjustment (0, 0, 1e10)), *manage (new Gtk::Adjustment (0, 0, 1e10))) , midi_panic_button (_("Panic")) #ifdef WITH_CMT @@ -300,13 +277,13 @@ Editor::Editor () /* nudge */ - , nudge_clock (X_("nudge"), false, X_("NudgeClock"), true, false, true) + , nudge_clock (new AudioClock (X_("nudge"), false, X_("NudgeClock"), true, false, true)) , meters_running(false) , _pending_locate_request (false) , _pending_initial_locate (false) , _last_cut_copy_source_track (0) - , _block_region_list_update_if_empty (false) + , _region_selection_change_updates_region_list (true) { constructed = false; @@ -355,7 +332,6 @@ Editor::Editor () current_interthread_info = 0; _show_measures = true; show_gain_after_trim = false; - verbose_cursor_on = true; last_item_entered = 0; have_pending_keyboard_selection = false; @@ -365,7 +341,6 @@ Editor::Editor () editor_ruler_menu = 0; no_ruler_shown_update = false; marker_menu = 0; - session_range_marker_menu = 0; range_marker_menu = 0; marker_menu_item = 0; tempo_or_meter_marker_menu = 0; @@ -389,8 +364,7 @@ Editor::Editor () _dragging_edit_point = false; select_new_marker = false; rhythm_ferret = 0; - layering_order_editor = 0; - _bundle_manager = 0; + layering_order_editor = 0; no_save_visual = false; resize_idle_id = -1; @@ -414,7 +388,7 @@ Editor::Editor () zoom_focus = ZoomFocusLeft; set_zoom_focus (ZoomFocusLeft); - zoom_range_clock.ValueChanged.connect (sigc::mem_fun(*this, &Editor::zoom_adjustment_changed)); + zoom_range_clock->ValueChanged.connect (sigc::mem_fun(*this, &Editor::zoom_adjustment_changed)); bbt_label.set_name ("EditorTimeButton"); bbt_label.set_size_request (-1, (int)timebar_height); @@ -434,12 +408,12 @@ Editor::Editor () timecode_label.set_padding (5,0); timecode_label.hide (); timecode_label.set_no_show_all(); - frame_label.set_name ("EditorTimeButton"); - frame_label.set_size_request (-1, (int)timebar_height); - frame_label.set_alignment (1.0, 0.5); - frame_label.set_padding (5,0); - frame_label.hide (); - frame_label.set_no_show_all(); + samples_label.set_name ("EditorTimeButton"); + samples_label.set_size_request (-1, (int)timebar_height); + samples_label.set_alignment (1.0, 0.5); + samples_label.set_padding (5,0); + samples_label.hide (); + samples_label.set_no_show_all(); tempo_label.set_name ("EditorTimeButton"); tempo_label.set_size_request (-1, (int)timebar_height); @@ -447,30 +421,35 @@ Editor::Editor () tempo_label.set_padding (5,0); tempo_label.hide(); tempo_label.set_no_show_all(); + meter_label.set_name ("EditorTimeButton"); meter_label.set_size_request (-1, (int)timebar_height); meter_label.set_alignment (1.0, 0.5); meter_label.set_padding (5,0); meter_label.hide(); meter_label.set_no_show_all(); + mark_label.set_name ("EditorTimeButton"); mark_label.set_size_request (-1, (int)timebar_height); mark_label.set_alignment (1.0, 0.5); mark_label.set_padding (5,0); mark_label.hide(); mark_label.set_no_show_all(); + cd_mark_label.set_name ("EditorTimeButton"); cd_mark_label.set_size_request (-1, (int)timebar_height); cd_mark_label.set_alignment (1.0, 0.5); cd_mark_label.set_padding (5,0); cd_mark_label.hide(); cd_mark_label.set_no_show_all(); + range_mark_label.set_name ("EditorTimeButton"); range_mark_label.set_size_request (-1, (int)timebar_height); range_mark_label.set_alignment (1.0, 0.5); range_mark_label.set_padding (5,0); range_mark_label.hide(); range_mark_label.set_no_show_all(); + transport_mark_label.set_name ("EditorTimeButton"); transport_mark_label.set_size_request (-1, (int)timebar_height); transport_mark_label.set_alignment (1.0, 0.5); @@ -480,11 +459,14 @@ Editor::Editor () initialize_rulers (); initialize_canvas (); + _summary = new EditorSummary (this); selection->TimeChanged.connect (sigc::mem_fun(*this, &Editor::time_selection_changed)); selection->TracksChanged.connect (sigc::mem_fun(*this, &Editor::track_selection_changed)); + editor_regions_selection_changed_connection = selection->RegionsChanged.connect (sigc::mem_fun(*this, &Editor::region_selection_changed)); + selection->PointsChanged.connect (sigc::mem_fun(*this, &Editor::point_selection_changed)); selection->MarkersChanged.connect (sigc::mem_fun(*this, &Editor::marker_selection_changed)); @@ -504,15 +486,16 @@ Editor::Editor () controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK); controls_layout.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_release)); - controls_layout_size_request_connection = controls_layout.signal_size_request().connect (sigc::mem_fun (*this, &Editor::controls_layout_size_request)); - build_cursors (); + _cursors = new MouseCursors; ArdourCanvas::Canvas* time_pad = manage(new ArdourCanvas::Canvas()); ArdourCanvas::SimpleLine* pad_line_1 = manage(new ArdourCanvas::SimpleLine(*time_pad->root(), 0.0, 1.0, 100.0, 1.0)); + pad_line_1->property_color_rgba() = 0xFF0000FF; pad_line_1->show(); + time_pad->show(); time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2); @@ -561,74 +544,68 @@ Editor::Editor () _snapshots = new EditorSnapshots (this); _locations = new EditorLocations (this); - Gtk::Label* nlabel; - - nlabel = manage (new Label (_("Regions"))); - nlabel->set_angle (-90); - the_notebook.append_page (_regions->widget (), *nlabel); - nlabel = manage (new Label (_("Tracks & Busses"))); - nlabel->set_angle (-90); - the_notebook.append_page (_routes->widget (), *nlabel); - nlabel = manage (new Label (_("Snapshots"))); - nlabel->set_angle (-90); - the_notebook.append_page (_snapshots->widget (), *nlabel); - nlabel = manage (new Label (_("Route Groups"))); - nlabel->set_angle (-90); - the_notebook.append_page (_route_groups->widget (), *nlabel); - nlabel = manage (new Label (_("Ranges & Marks"))); - nlabel->set_angle (-90); - the_notebook.append_page (_locations->widget (), *nlabel); - - the_notebook.set_show_tabs (true); - the_notebook.set_scrollable (true); - the_notebook.popup_disable (); - the_notebook.set_tab_pos (Gtk::POS_RIGHT); - the_notebook.show_all (); - + add_notebook_page (_("Regions"), _regions->widget ()); + add_notebook_page (_("Tracks & Busses"), _routes->widget ()); + add_notebook_page (_("Snapshots"), _snapshots->widget ()); + add_notebook_page (_("Route Groups"), _route_groups->widget ()); + add_notebook_page (_("Ranges & Marks"), _locations->widget ()); + + _the_notebook.set_show_tabs (true); + _the_notebook.set_scrollable (true); + _the_notebook.popup_disable (); + _the_notebook.set_tab_pos (Gtk::POS_RIGHT); + _the_notebook.show_all (); + post_maximal_editor_width = 0; post_maximal_horizontal_pane_position = 0; post_maximal_editor_height = 0; post_maximal_vertical_pane_position = 0; + _notebook_shrunk = false; editor_summary_pane.pack1(edit_packer); Button* summary_arrows_left_left = manage (new Button); summary_arrows_left_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE))); - summary_arrows_left_left->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press))); - summary_arrows_left_left->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_release)); + summary_arrows_left_left->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), LEFT))); + summary_arrows_left_left->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release)); + Button* summary_arrows_left_right = manage (new Button); summary_arrows_left_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE))); - summary_arrows_left_right->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press))); - summary_arrows_left_right->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_release)); + summary_arrows_left_right->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), RIGHT))); + summary_arrows_left_right->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release)); + VBox* summary_arrows_left = manage (new VBox); summary_arrows_left->pack_start (*summary_arrows_left_left); summary_arrows_left->pack_start (*summary_arrows_left_right); - Button* summary_arrows_right_left = manage (new Button); - summary_arrows_right_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE))); - summary_arrows_right_left->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press))); - summary_arrows_right_left->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_release)); - Button* summary_arrows_right_right = manage (new Button); - summary_arrows_right_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE))); - summary_arrows_right_right->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press))); - summary_arrows_right_right->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_release)); + Button* summary_arrows_right_up = manage (new Button); + summary_arrows_right_up->add (*manage (new Arrow (ARROW_UP, SHADOW_NONE))); + summary_arrows_right_up->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), UP))); + summary_arrows_right_up->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release)); + + Button* summary_arrows_right_down = manage (new Button); + summary_arrows_right_down->add (*manage (new Arrow (ARROW_DOWN, SHADOW_NONE))); + summary_arrows_right_down->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), DOWN))); + summary_arrows_right_down->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release)); + VBox* summary_arrows_right = manage (new VBox); - summary_arrows_right->pack_start (*summary_arrows_right_left); - summary_arrows_right->pack_start (*summary_arrows_right_right); + summary_arrows_right->pack_start (*summary_arrows_right_up); + summary_arrows_right->pack_start (*summary_arrows_right_down); Frame* summary_frame = manage (new Frame); summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN); + summary_frame->add (*_summary); summary_frame->show (); _summary_hbox.pack_start (*summary_arrows_left, false, false); _summary_hbox.pack_start (*summary_frame, true, true); _summary_hbox.pack_start (*summary_arrows_right, false, false); - + editor_summary_pane.pack2 (_summary_hbox); edit_pane.pack1 (editor_summary_pane, true, true); - edit_pane.pack2 (the_notebook, false, true); + edit_pane.pack2 (_the_notebook, false, true); editor_summary_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun (*this, &Editor::pane_allocation_handler), static_cast (&editor_summary_pane))); @@ -636,10 +613,10 @@ Editor::Editor () edit_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Editor::pane_allocation_handler), static_cast (&edit_pane))); #ifdef GTKOSX - Glib::PropertyProxy proxy = edit_pane.property_position(); - proxy.signal_changed().connect (bind (sigc::ptr_fun (pane_size_watcher), static_cast (&edit_pane))); + Glib::PropertyProxy proxy = edit_pane.property_position(); + proxy.signal_changed().connect (bind (sigc::ptr_fun (pane_size_watcher), static_cast (&edit_pane))); #endif - top_hbox.pack_start (toolbar_frame, false, true); + top_hbox.pack_start (toolbar_frame); HBox *hbox = manage (new HBox); hbox->pack_start (edit_pane, true, true); @@ -669,6 +646,7 @@ Editor::Editor () _snap_mode = SnapOff; set_snap_mode (_snap_mode); set_mouse_mode (MouseObject, true); + pre_internal_mouse_mode = MouseObject; set_edit_point_preference (EditAtMouse, true); _playlist_selector = new PlaylistSelector(); @@ -704,7 +682,7 @@ Editor::Editor () window_icons.push_back (icon); } if (!window_icons.empty()) { - set_icon_list (window_icons); + // set_icon_list (window_icons); set_default_icon_list (window_icons); } @@ -725,8 +703,9 @@ Editor::Editor () ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context()); ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context()); ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), ui_bind (&Editor::control_scroll, this, _1), gui_context()); + ControlProtocol::SelectByRID.connect (*this, invalidator (*this), ui_bind (&Editor::control_select, this, _1), gui_context()); BasicUI::AccessAction.connect (*this, invalidator (*this), ui_bind (&Editor::access_action, this, _1, _2), gui_context()); - + /* problematic: has to return a value and thus cannot be x-thread */ Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1)); @@ -735,7 +714,23 @@ Editor::Editor () TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context()); - _last_normalization_value = 0; + _ignore_region_action = false; + _last_region_menu_was_main = false; + _popup_region_menu_item = 0; + + _show_marker_lines = false; + _over_region_trim_target = false; + + /* Button bindings */ + + button_bindings = new Bindings; + + XMLNode* node = button_settings(); + if (node) { + for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) { + button_bindings->load (**i); + } + } constructed = true; instant_save (); @@ -756,13 +751,28 @@ Editor::~Editor() image_socket_listener = 0 ; } #endif - + + delete button_bindings; delete _routes; delete _route_groups; delete track_canvas; delete _drags; } +XMLNode* +Editor::button_settings () const +{ + XMLNode* settings = ARDOUR_UI::instance()->editor_settings(); + XMLNode* node = find_named_node (*settings, X_("Buttons")); + + if (!node) { + cerr << "new empty Button node\n"; + node = new XMLNode (X_("Buttons")); + } + + return node; +} + void Editor::add_toplevel_controls (Container& cont) { @@ -788,6 +798,12 @@ Editor::catch_vanishing_regionview (RegionView *rv) if (entered_regionview == rv) { set_entered_regionview (0); } + + if (!_all_region_actions_sensitized) { + sensitize_all_region_actions (true); + } + + _over_region_trim_target = false; } void @@ -804,6 +820,13 @@ Editor::set_entered_regionview (RegionView* rv) if ((entered_regionview = rv) != 0) { entered_regionview->entered (internal_editing ()); } + + if (!_all_region_actions_sensitized && _last_region_menu_was_main) { + /* This RegionView entry might have changed what region actions + are allowed, so sensitize them all in case a key is pressed. + */ + sensitize_all_region_actions (true); + } } void @@ -821,16 +844,19 @@ Editor::set_entered_track (TimeAxisView* tav) void Editor::show_window () { - if (! is_visible ()) { + if (!is_visible ()) { show_all (); - /* re-hide editor list if necessary */ - editor_list_button_toggled (); + /* XXX: this is a bit unfortunate; it would probably + be nicer if we could just call show () above rather + than needing the show_all () + */ - /* re-hide summary widget if necessary */ + /* re-hide stuff if necessary */ + editor_list_button_toggled (); parameter_changed ("show-summary"); - - parameter_changed ("show-edit-group-tabs"); + parameter_changed ("show-group-tabs"); + parameter_changed ("show-zoom-tools"); /* now reset all audio_time_axis heights, because widgets might need to be re-hidden @@ -842,6 +868,10 @@ Editor::show_window () tv = (static_cast(*i)); tv->reset_height (); } + + if (current_mixer_strip) { + current_mixer_strip->hide_things (); + } } present (); @@ -868,19 +898,45 @@ Editor::zoom_adjustment_changed () return; } - double fpu = zoom_range_clock.current_duration() / _canvas_width; + double fpu = zoom_range_clock->current_duration() / _canvas_width; if (fpu < 1.0) { fpu = 1.0; - zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width)); + zoom_range_clock->set ((framepos_t) floor (fpu * _canvas_width)); } else if (fpu > _session->current_end_frame() / _canvas_width) { fpu = _session->current_end_frame() / _canvas_width; - zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width)); + zoom_range_clock->set ((framepos_t) floor (fpu * _canvas_width)); } temporal_zoom (fpu); } +void +Editor::control_select (uint32_t rid) +{ + /* handles the (static) signal from the ControlProtocol class that + * requests setting the selected track to a given RID + */ + + if (!_session) { + return; + } + + boost::shared_ptr r = _session->route_by_remote_id (rid); + + if (!r) { + return; + } + + TimeAxisView* tav = axis_view_from_route (r); + + if (tav) { + selection->set (tav); + } else { + selection->clear_tracks (); + } +} + void Editor::control_scroll (float fraction) { @@ -1056,9 +1112,9 @@ Editor::set_session (Session *t) return; } - zoom_range_clock.set_session (_session); + zoom_range_clock->set_session (_session); _playlist_selector->set_session (_session); - nudge_clock.set_session (_session); + nudge_clock->set_session (_session); _summary->set_session (_session); _group_tabs->set_session (_session); _route_groups->set_session (_session); @@ -1081,9 +1137,6 @@ Editor::set_session (Session *t) compute_fixed_ruler_scale (); - /* there are never any selected regions at startup */ - sensitize_the_right_region_actions (false); - XMLNode* node = ARDOUR_UI::instance()->editor_settings(); set_state (*node, Stateful::loading_state_version); @@ -1099,12 +1152,11 @@ Editor::set_session (Session *t) but use Gtkmm2ext::UI::instance()->call_slot(); */ - _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), ui_bind(&Editor::step_edit_status_change, this, _1), gui_context()); + _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), ui_bind(&Editor::step_edit_status_change, this, _1), gui_context()); _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context()); _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context()); _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context()); _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context()); - _session->TimecodeOffsetChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_just_timecode, this), gui_context()); _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context()); _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context()); _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context()); @@ -1112,20 +1164,20 @@ Editor::set_session (Session *t) _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context()); _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&Editor::location_gone, this, _1), gui_context()); _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context()); - _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display_s, this, _1), gui_context()); + _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display, this), gui_context()); _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context()); if (Profile->get_sae()) { - BBT_Time bbt; + Timecode::BBT_Time bbt; bbt.bars = 0; bbt.beats = 0; bbt.ticks = 120; - nframes_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1); - nudge_clock.set_mode(AudioClock::BBT); - nudge_clock.set (pos, true, 0, AudioClock::BBT); + framepos_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1); + nudge_clock->set_mode(AudioClock::BBT); + nudge_clock->set (pos, true, 0, AudioClock::BBT); } else { - nudge_clock.set (_session->frame_rate() * 5, true, 0, AudioClock::Timecode); // default of 5 seconds + nudge_clock->set (_session->frame_rate() * 5, true, 0, AudioClock::Timecode); // default of 5 seconds } playhead_cursor->canvas_item.show (); @@ -1133,9 +1185,11 @@ Editor::set_session (Session *t) Location* loc = _session->locations()->auto_loop_location(); if (loc == 0) { loc = new Location (*_session, 0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden)); + if (loc->start() == loc->end()) { loc->set_end (loc->start() + 1); } + _session->locations()->add (loc, false); _session->set_auto_loop_location (loc); } else { @@ -1144,11 +1198,14 @@ Editor::set_session (Session *t) } loc = _session->locations()->auto_punch_location(); + if (loc == 0) { loc = new Location (*_session, 0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden)); + if (loc->start() == loc->end()) { loc->set_end (loc->start() + 1); } + _session->locations()->add (loc, false); _session->set_auto_punch_location (loc); } else { @@ -1173,7 +1230,7 @@ Editor::set_session (Session *t) super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect ( sigc::mem_fun (*this, &Editor::super_rapid_screen_update) ); - + switch (_snap_type) { case SnapToRegionStart: case SnapToRegionEnd: @@ -1189,105 +1246,26 @@ Editor::set_session (Session *t) /* register for undo history */ _session->register_with_memento_command_factory(_id, this); + ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated)); + start_updating_meters (); } void -Editor::build_cursors () +Editor::action_pre_activated (Glib::RefPtr const & a) { - using namespace Gdk; - - { - Glib::RefPtr zoom_in_cursor_pixbuf (::get_icon ("zoom_in_cursor")); - zoom_in_cursor = new Gdk::Cursor (Gdk::Display::get_default(), zoom_in_cursor_pixbuf, 5, 5); - } - - { - Glib::RefPtr zoom_out_cursor_pixbuf (::get_icon ("zoom_out_cursor")); - zoom_out_cursor = new Gdk::Cursor (Gdk::Display::get_default(), zoom_out_cursor_pixbuf, 5, 5); - } - - Gdk::Color fbg ("#ffffff" ); - Gdk::Color ffg ("#000000" ); - - { - RefPtr source, mask; - - source = Bitmap::create (fader_cursor_bits, fader_cursor_width, fader_cursor_height); - mask = Bitmap::create (fader_cursor_mask_bits, fader_cursor_width, fader_cursor_height); - fader_cursor = new Gdk::Cursor (source, mask, ffg, fbg, fader_cursor_x_hot, fader_cursor_y_hot); - } - - { - RefPtr source, mask; - source = Bitmap::create (speaker_cursor_bits, speaker_cursor_width, speaker_cursor_height); - mask = Bitmap::create (speaker_cursor_mask_bits, speaker_cursor_width, speaker_cursor_height); - speaker_cursor = new Gdk::Cursor (source, mask, ffg, fbg, speaker_cursor_x_hot, speaker_cursor_y_hot); - } - - { - RefPtr bits; - char pix[4] = { 0, 0, 0, 0 }; - bits = Bitmap::create (pix, 2, 2); - Gdk::Color c; - transparent_cursor = new Gdk::Cursor (bits, bits, c, c, 0, 0); - } - - { - RefPtr bits; - char pix[4] = { 0, 0, 0, 0 }; - bits = Bitmap::create (pix, 2, 2); - Gdk::Color c; - transparent_cursor = new Gdk::Cursor (bits, bits, c, c, 0, 0); - } - - { - Glib::RefPtr grabber_pixbuf (::get_icon ("grabber")); - grabber_cursor = new Gdk::Cursor (Gdk::Display::get_default(), grabber_pixbuf, 5, 0); - } - - { - Glib::RefPtr grabber_note_pixbuf (::get_icon ("grabber_note")); - grabber_note_cursor = new Gdk::Cursor (Gdk::Display::get_default(), grabber_note_pixbuf, 5, 10); - } - - { - Glib::RefPtr grabber_edit_point_pixbuf (::get_icon ("grabber_edit_point")); - grabber_edit_point_cursor = new Gdk::Cursor (Gdk::Display::get_default(), grabber_edit_point_pixbuf, 5, 17); - } - - cross_hair_cursor = new Gdk::Cursor (CROSSHAIR); - trimmer_cursor = new Gdk::Cursor (SB_H_DOUBLE_ARROW); + if (a->get_name() == "RegionMenu") { + /* When the main menu's region menu is opened, we setup the actions so that they look right + in the menu. I can't find a way of getting a signal when this menu is subsequently closed, + so we resensitize all region actions when the entered regionview or the region selection + changes. HOWEVER we can't always resensitize on entered_regionview change because that + happens after the region context menu is opened. So we set a flag here, too. - { - Glib::RefPtr apixbuf (::get_icon ("trim_left_cursor")); - left_side_trim_cursor = new Gdk::Cursor (Gdk::Display::get_default(), apixbuf, 5, 11); - } - - { - Glib::RefPtr apixbuf (::get_icon ("trim_right_cursor")); - right_side_trim_cursor = new Gdk::Cursor (Gdk::Display::get_default(), apixbuf, 23, 11); - } - - { - Glib::RefPtr apixbuf (::get_icon ("fade_in_cursor")); - fade_in_cursor = new Gdk::Cursor (Gdk::Display::get_default(), apixbuf, 0, 40); - } - - { - Glib::RefPtr apixbuf (::get_icon ("fade_out_cursor")); - fade_out_cursor = new Gdk::Cursor (Gdk::Display::get_default(), apixbuf, 27, 40); + What a carry on :( + */ + sensitize_the_right_region_actions (); + _last_region_menu_was_main = true; } - - selector_cursor = new Gdk::Cursor (XTERM); - time_fx_cursor = new Gdk::Cursor (SIZING); - wait_cursor = new Gdk::Cursor (WATCH); - timebar_cursor = new Gdk::Cursor(LEFT_PTR); - midi_pencil_cursor = new Gdk::Cursor (PENCIL); - midi_select_cursor = new Gdk::Cursor (CENTER_PTR); - midi_resize_cursor = new Gdk::Cursor (SIZING); - midi_erase_cursor = new Gdk::Cursor (DRAPED_BOX); - up_down_cursor = new Gdk::Cursor (Gdk::SB_V_DOUBLE_ARROW); } /** Pop up a context menu for when the user clicks on a fade in or fade out */ @@ -1318,10 +1296,10 @@ Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* i items.push_back (SeparatorElem()); if (Profile->get_sae()) { - + items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear))); items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast))); - + } else { items.push_back ( @@ -1340,34 +1318,34 @@ Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* i *_fade_in_images[FadeFast], sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast) )); - + dynamic_cast(&items.back())->set_always_show_image (); - + items.push_back ( ImageMenuElem ( _("Slow"), *_fade_in_images[FadeLogB], sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogB) )); - + dynamic_cast(&items.back())->set_always_show_image (); - + items.push_back ( ImageMenuElem ( _("Fast"), *_fade_in_images[FadeLogA], sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogA) )); - + dynamic_cast(&items.back())->set_always_show_image (); - + items.push_back ( ImageMenuElem ( _("Fastest"), *_fade_in_images[FadeSlow], sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow) )); - + dynamic_cast(&items.back())->set_always_show_image (); } @@ -1404,34 +1382,34 @@ Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* i *_fade_out_images[FadeFast], sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow) )); - + dynamic_cast(&items.back())->set_always_show_image (); - + items.push_back ( ImageMenuElem ( _("Slow"), *_fade_out_images[FadeLogB], sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogA) )); - + dynamic_cast(&items.back())->set_always_show_image (); - + items.push_back ( ImageMenuElem ( _("Fast"), *_fade_out_images[FadeLogA], sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogB) )); - + dynamic_cast(&items.back())->set_always_show_image (); - + items.push_back ( ImageMenuElem ( _("Fastest"), *_fade_out_images[FadeSlow], sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast) )); - + dynamic_cast(&items.back())->set_always_show_image (); } @@ -1448,10 +1426,10 @@ Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* i } void -Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection, framepos_t frame) +Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection) { using namespace Menu_Helpers; - Menu* (Editor::*build_menu_function)(framepos_t); + Menu* (Editor::*build_menu_function)(); Menu *menu; switch (item_type) { @@ -1492,7 +1470,7 @@ Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, return; } - menu = (this->*build_menu_function)(frame); + menu = (this->*build_menu_function)(); menu->set_name ("ArdourContextMenu"); /* now handle specific situations */ @@ -1501,8 +1479,8 @@ Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, case RegionItem: case RegionViewName: case RegionViewNameHighlight: - case LeftFrameHandle: - case RightFrameHandle: + case LeftFrameHandle: + case RightFrameHandle: if (!with_selection) { if (region_edit_menu_split_item) { if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) { @@ -1564,11 +1542,18 @@ Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, clicked_routeview->build_underlay_menu(menu); } + /* When the region menu is opened, we setup the actions so that they look right + in the menu. + */ + sensitize_the_right_region_actions (); + _last_region_menu_was_main = false; + + menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true)); menu->popup (button, time); } Menu* -Editor::build_track_context_menu (framepos_t) +Editor::build_track_context_menu () { using namespace Menu_Helpers; @@ -1580,7 +1565,7 @@ Editor::build_track_context_menu (framepos_t) } Menu* -Editor::build_track_bus_context_menu (framepos_t) +Editor::build_track_bus_context_menu () { using namespace Menu_Helpers; @@ -1592,7 +1577,7 @@ Editor::build_track_bus_context_menu (framepos_t) } Menu* -Editor::build_track_region_context_menu (framepos_t frame) +Editor::build_track_region_context_menu () { using namespace Menu_Helpers; MenuList& edit_items = track_region_context_menu.items(); @@ -1604,36 +1589,19 @@ Editor::build_track_region_context_menu (framepos_t frame) region_edit_menu_split_item = 0; region_edit_menu_split_multichannel_item = 0; + /* we might try to use items that are currently attached to a crossfade menu, + so clear that, too. + */ + track_crossfade_context_menu.items().clear (); + RouteTimeAxisView* rtv = dynamic_cast (clicked_axisview); if (rtv) { boost::shared_ptr tr; boost::shared_ptr pl; - /* Don't offer a region submenu if we are in internal edit mode, as we don't select regions in this - mode and so offering region context is somewhat confusing. - */ - if ((tr = rtv->track()) && ((pl = tr->playlist())) && !internal_editing()) { - framepos_t framepos = (framepos_t) floor ((double)frame * tr->speed()); - uint32_t regions_at = pl->count_regions_at (framepos); - list > regions_for_menu; - - if (selection->regions.size() > 1) { - // there's already a multiple selection: just add a - // single region context menu that will act on all - // selected regions - - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { - regions_for_menu.push_back ((*i)->region ()); - } - } else { - boost::shared_ptr top_region = pl->top_region_at (framepos); - if (top_region) { - regions_for_menu.push_back (top_region); - } - } - - add_region_context_items (rtv->view(), regions_for_menu, edit_items, framepos, regions_at > 1); + if ((tr = rtv->track())) { + add_region_context_items (edit_items, tr); } } @@ -1643,12 +1611,17 @@ Editor::build_track_region_context_menu (framepos_t frame) } Menu* -Editor::build_track_crossfade_context_menu (framepos_t frame) +Editor::build_track_crossfade_context_menu () { using namespace Menu_Helpers; MenuList& edit_items = track_crossfade_context_menu.items(); edit_items.clear (); + /* we might try to use items that are currently attached to a crossfade menu, + so clear that, too. + */ + track_region_context_menu.items().clear (); + AudioTimeAxisView* atv = dynamic_cast (clicked_axisview); if (atv) { @@ -1659,35 +1632,24 @@ Editor::build_track_crossfade_context_menu (framepos_t frame) if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast (pl)) != 0)) { AudioPlaylist::Crossfades xfades; + framepos_t where; + bool ignored; - apl->crossfades_at (frame, xfades); + /* The xfade menu is a bit of a special case, as we always use the mouse position + to decide whether or not to display it (rather than the edit point). No particularly + strong reasons for this, other than it is a bit surprising to right-click on a xfade + and not get a menu. + */ + mouse_frame (where, ignored); + apl->crossfades_at (where, xfades); - bool many = xfades.size() > 1; + bool const many = xfades.size() > 1; for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) { add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many); } - framepos_t framepos = (framepos_t) floor ((double)frame * tr->speed()); - uint32_t regions_at = pl->count_regions_at (framepos); - list > regions_for_menu; - - if (selection->regions.size() > 1) { - // there's already a multiple selection: just add a - // single region context menu that will act on all - // selected regions - - for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { - regions_for_menu.push_back ((*i)->region ()); - } - } else { - boost::shared_ptr top_region = pl->top_region_at (framepos); - if (top_region) { - regions_for_menu.push_back (top_region); - } - } - - add_region_context_items (atv->audio_view(), regions_for_menu, edit_items, framepos, regions_at > 1); + add_region_context_items (edit_items, tr); } } @@ -1697,7 +1659,7 @@ Editor::build_track_crossfade_context_menu (framepos_t frame) } void -Editor::analyze_region_selection() +Editor::analyze_region_selection () { if (analysis_window == 0) { analysis_window = new AnalysisWindow(); @@ -1733,7 +1695,7 @@ Editor::analyze_range_selection() } Menu* -Editor::build_track_selection_context_menu (framepos_t) +Editor::build_track_selection_context_menu () { using namespace Menu_Helpers; MenuList& edit_items = track_selection_context_menu.items(); @@ -1807,313 +1769,48 @@ Editor::xfade_edit_right_region () } void -Editor::add_region_context_items (StreamView* sv, list > regions, Menu_Helpers::MenuList& edit_items, - framepos_t position, bool multiple_regions_at_position) +Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::shared_ptr track) { using namespace Menu_Helpers; - Gtk::MenuItem* foo_item; - Menu *region_menu = manage (new Menu); - MenuList& items = region_menu->items(); - region_menu->set_name ("ArdourContextMenu"); - - /* Look through the regions that we are handling and make notes about what we have got */ - - bool have_audio = false; - bool have_midi = false; - bool have_locked = false; - bool have_unlocked = false; - bool have_position_lock_style_audio = false; - bool have_position_lock_style_music = false; - bool have_muted = false; - bool have_unmuted = false; - bool have_opaque = false; - bool have_non_opaque = false; - bool have_not_at_natural_position = false; - bool have_envelope_visible = false; - bool have_envelope_invisible = false; - bool have_envelope_active = false; - bool have_envelope_inactive = false; - bool have_non_unity_scale_amplitude = false; - - for (list >::const_iterator i = regions.begin(); i != regions.end(); ++i) { - boost::shared_ptr ar = boost::dynamic_pointer_cast (*i); - if (ar) { - have_audio = true; - } - if (boost::dynamic_pointer_cast (*i)) { - have_midi = true; - } - - if ((*i)->locked()) { - have_locked = true; - } else { - have_unlocked = true; - } - - if ((*i)->position_lock_style() == MusicTime) { - have_position_lock_style_music = true; - } else { - have_position_lock_style_audio = true; - } - - if ((*i)->muted()) { - have_muted = true; - } else { - have_unmuted = true; - } - - if ((*i)->opaque()) { - have_opaque = true; - } else { - have_non_opaque = true; - } - - if (!(*i)->at_natural_position()) { - have_not_at_natural_position = true; - } - - if (ar) { - /* its a bit unfortunate that "envelope visible" is a view-only - property. we have to find the regionview to able to check - its current setting. - */ - - RegionView* rv = sv->find_view (ar); - have_envelope_invisible = true; - - if (rv) { - AudioRegionView* arv = dynamic_cast (rv); - if (arv && arv->envelope_visible()) { - have_envelope_visible = true; - } - } - - if (ar->envelope_active()) { - have_envelope_active = true; - } else { - have_envelope_inactive = true; - } - - if (ar->scale_amplitude() != 1) { - have_non_unity_scale_amplitude = true; - } - } - } - - if (regions.size() == 1) { - - /* when this particular menu pops up, make the relevant region - become selected. - */ - - region_menu->signal_map_event().connect ( - sigc::bind (sigc::mem_fun(*this, &Editor::set_selected_regionview_from_map_event), sv, boost::weak_ptr (regions.front())) - ); - - items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &Editor::rename_region))); - - if (have_midi) { - items.push_back (MenuElem (_("List Editor..."), sigc::mem_fun(*this, &Editor::show_midi_list_editor))); - } - - items.push_back (MenuElem (_("Region Properties..."), sigc::mem_fun(*this, &Editor::edit_region))); - } - - items.push_back (MenuElem (_("Raise to Top Layer"), sigc::mem_fun(*this, &Editor::raise_region_to_top))); - items.push_back (MenuElem (_("Lower to Bottom Layer"), sigc::mem_fun (*this, &Editor::lower_region_to_bottom))); - items.push_back (SeparatorElem()); - items.push_back (MenuElem (_("Define Sync Point"), sigc::mem_fun(*this, &Editor::set_region_sync_from_edit_point))); - if (_edit_point == EditAtMouse) { - items.back ().set_sensitive (false); - } - items.push_back (MenuElem (_("Remove Sync Point"), sigc::mem_fun(*this, &Editor::remove_region_sync))); - items.push_back (SeparatorElem()); - - items.push_back (MenuElem (_("Audition"), sigc::mem_fun(*this, &Editor::play_selected_region))); - items.push_back (MenuElem (_("Export..."), sigc::mem_fun(*this, &Editor::export_region))); - items.push_back (MenuElem (_("Bounce"), sigc::mem_fun(*this, &Editor::bounce_region_selection))); - - if (have_audio) { - items.push_back (MenuElem (_("Spectral Analysis..."), sigc::mem_fun(*this, &Editor::analyze_region_selection))); - } - - items.push_back (SeparatorElem()); - - items.push_back (CheckMenuElem (_("Lock"))); - CheckMenuItem* region_lock_item = static_cast(&items.back()); - if (have_locked && !have_unlocked) { - region_lock_item->set_active(); - } else if (have_locked && have_unlocked) { - region_lock_item->set_inconsistent (); - } - region_lock_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_region_lock)); - - items.push_back (CheckMenuElem (_("Glue to Bars and Beats"))); - CheckMenuItem* bbt_glue_item = static_cast(&items.back()); - - if (have_position_lock_style_music && !have_position_lock_style_audio) { - bbt_glue_item->set_active (); - } else if (have_position_lock_style_music && have_position_lock_style_audio) { - bbt_glue_item->set_inconsistent (); - } - - bbt_glue_item->signal_activate().connect (sigc::mem_fun (*this, &Editor::toggle_region_lock_style)); - - items.push_back (CheckMenuElem (_("Mute"))); - CheckMenuItem* region_mute_item = static_cast(&items.back()); - - if (have_muted && !have_unmuted) { - region_mute_item->set_active(); - } else if (have_muted && have_unmuted) { - region_mute_item->set_inconsistent (); - } - - region_mute_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_region_mute)); - - items.push_back (MenuElem (_("Transpose..."), mem_fun(*this, &Editor::pitch_shift_regions))); - - if (!Profile->get_sae()) { - items.push_back (CheckMenuElem (_("Opaque"))); - CheckMenuItem* region_opaque_item = static_cast(&items.back()); - if (have_opaque && !have_non_opaque) { - region_opaque_item->set_active(); - } else if (have_opaque && have_non_opaque) { - region_opaque_item->set_inconsistent (); - } - region_opaque_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_region_opaque)); - } - - items.push_back (CheckMenuElem (_("Original Position"), sigc::mem_fun(*this, &Editor::naturalize))); - if (!have_not_at_natural_position) { - items.back().set_sensitive (false); - } - - items.push_back (SeparatorElem()); - - if (have_audio) { - - if (!Profile->get_sae()) { - items.push_back (MenuElem (_("Reset Envelope"), sigc::mem_fun(*this, &Editor::reset_region_gain_envelopes))); - - items.push_back (CheckMenuElem (_("Envelope Visible"))); - CheckMenuItem* region_envelope_visible_item = static_cast (&items.back()); - if (have_envelope_visible && !have_envelope_invisible) { - region_envelope_visible_item->set_active (); - } else if (have_envelope_visible && have_envelope_invisible) { - region_envelope_visible_item->set_inconsistent (); - } - region_envelope_visible_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_gain_envelope_visibility)); - - items.push_back (CheckMenuElem (_("Envelope Active"))); - CheckMenuItem* region_envelope_active_item = static_cast (&items.back()); - - if (have_envelope_active && !have_envelope_inactive) { - region_envelope_active_item->set_active (); - } else if (have_envelope_active && have_envelope_inactive) { - region_envelope_active_item->set_inconsistent (); - } - - region_envelope_active_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_gain_envelope_active)); - items.push_back (SeparatorElem()); - } - - items.push_back (MenuElem (_("Normalize..."), sigc::mem_fun(*this, &Editor::normalize_region))); - if (have_non_unity_scale_amplitude) { - items.push_back (MenuElem (_("Reset Gain"), sigc::mem_fun(*this, &Editor::reset_region_scale_amplitude))); - } - - } else if (have_midi) { - items.push_back (MenuElem (_("Quantize"), sigc::mem_fun(*this, &Editor::quantize_region))); - items.push_back (MenuElem (_("Fork"), sigc::mem_fun(*this, &Editor::fork_region))); - items.push_back (SeparatorElem()); - } - - items.push_back (MenuElem (_("Strip Silence..."), sigc::mem_fun (*this, &Editor::strip_region_silence))); - items.push_back (MenuElem (_("Reverse"), sigc::mem_fun(*this, &Editor::reverse_region))); - items.push_back (SeparatorElem()); - - /* range related stuff */ - - items.push_back (MenuElem (_("Add Single Range"), sigc::mem_fun (*this, &Editor::add_location_from_audio_region))); - items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_locations_from_audio_region))); - if (selection->regions.size() < 2) { - items.back().set_sensitive (false); - } - - items.push_back (MenuElem (_("Set Range Selection"), sigc::mem_fun (*this, &Editor::set_selection_from_region))); - items.push_back (SeparatorElem()); - - /* Nudge region */ - - Menu *nudge_menu = manage (new Menu()); - MenuList& nudge_items = nudge_menu->items(); - nudge_menu->set_name ("ArdourContextMenu"); - - nudge_items.push_back (MenuElem (_("Nudge Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_forward), false, false)))); - nudge_items.push_back (MenuElem (_("Nudge Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_backward), false, false)))); - nudge_items.push_back (MenuElem (_("Nudge Forward by Capture Offset"), (sigc::mem_fun(*this, &Editor::nudge_forward_capture_offset)))); - nudge_items.push_back (MenuElem (_("Nudge Backward by Capture Offset"), (sigc::mem_fun(*this, &Editor::nudge_backward_capture_offset)))); - - items.push_back (MenuElem (_("Nudge"), *nudge_menu)); - items.push_back (SeparatorElem()); - - Menu *trim_menu = manage (new Menu); - MenuList& trim_items = trim_menu->items(); - trim_menu->set_name ("ArdourContextMenu"); - - trim_items.push_back (MenuElem (_("Start to Edit Point"), sigc::mem_fun(*this, &Editor::trim_region_from_edit_point))); - foo_item = &trim_items.back(); - if (_edit_point == EditAtMouse) { - foo_item->set_sensitive (false); - } - trim_items.push_back (MenuElem (_("Edit Point to End"), sigc::mem_fun(*this, &Editor::trim_region_to_edit_point))); - foo_item = &trim_items.back(); - if (_edit_point == EditAtMouse) { - foo_item->set_sensitive (false); - } - trim_items.push_back (MenuElem (_("Trim to Loop"), sigc::mem_fun(*this, &Editor::trim_region_to_loop))); - trim_items.push_back (MenuElem (_("Trim to Punch"), sigc::mem_fun(*this, &Editor::trim_region_to_punch))); - - items.push_back (MenuElem (_("Trim"), *trim_menu)); - items.push_back (SeparatorElem()); - - items.push_back (MenuElem (_("Split"), (sigc::mem_fun(*this, &Editor::split)))); - region_edit_menu_split_item = &items.back(); - - if (_edit_point == EditAtMouse) { - region_edit_menu_split_item->set_sensitive (false); - } - - if (have_audio) { - items.push_back (MenuElem (_("Make Mono Regions"), (sigc::mem_fun(*this, &Editor::split_multichannel_region)))); - region_edit_menu_split_multichannel_item = &items.back(); - } - - items.push_back (MenuElem (_("Duplicate"), (sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)))); - items.push_back (MenuElem (_("Multi-Duplicate..."), (sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), true)))); - items.push_back (MenuElem (_("Fill Track"), (sigc::mem_fun(*this, &Editor::region_fill_track)))); - items.push_back (SeparatorElem()); - items.push_back (MenuElem (_("Remove"), sigc::mem_fun(*this, &Editor::remove_selected_regions))); /* OK, stick the region submenu at the top of the list, and then add the standard items. */ + RegionSelection rs = get_regions_from_selection_and_entered (); + + string::size_type pos = 0; + string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions"); + /* 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 = (regions.size() == 1) ? regions.front()->name() : _("Selected Regions"); - while ((pos = menu_item_name.find ("_", pos)) != string::npos) { menu_item_name.replace (pos, 1, "__"); pos += 2; } - edit_items.push_back (MenuElem (menu_item_name, *region_menu)); - if (multiple_regions_at_position && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) { - edit_items.push_back (MenuElem (_("Choose Top Region..."), (bind (mem_fun(*this, &Editor::change_region_layering_order), position)))); + if (_popup_region_menu_item == 0) { + _popup_region_menu_item = new MenuItem (menu_item_name); + _popup_region_menu_item->set_submenu (*dynamic_cast (ActionManager::get_widget (X_("/PopupRegionMenu")))); + _popup_region_menu_item->show (); + } else { + _popup_region_menu_item->set_label (menu_item_name); + } + + /* Use the mouse position rather than the edit point to decide whether to show the `choose top region' + dialogue. If we use the edit point it gets a bit messy because the user still has to click over + *some* region in order to get the region context menu stuff to be displayed at all. + */ + + framepos_t mouse; + bool ignored; + mouse_frame (mouse, ignored); + + edit_items.push_back (*_popup_region_menu_item); + if (track->playlist()->count_regions_at (mouse) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) { + edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region")->create_menu_item ())); } edit_items.push_back (SeparatorElem()); } @@ -2132,14 +1829,38 @@ Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items) edit_items.push_back (SeparatorElem()); edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection))); - if (!selection->regions.empty()) { - edit_items.push_back (SeparatorElem()); - edit_items.push_back (MenuElem (_("Extend Range to End of Region"), sigc::bind (sigc::mem_fun(*this, &Editor::extend_selection_to_end_of_region), false))); - edit_items.push_back (MenuElem (_("Extend Range to Start of Region"), sigc::bind (sigc::mem_fun(*this, &Editor::extend_selection_to_start_of_region), false))); - } + edit_items.push_back (SeparatorElem()); + + edit_items.push_back ( + MenuElem ( + _("Move Range Start to Previous Region Boundary"), + sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, false) + ) + ); + + edit_items.push_back ( + MenuElem ( + _("Move Range Start to Next Region Boundary"), + sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, true) + ) + ); + + edit_items.push_back ( + MenuElem ( + _("Move Range End to Previous Region Boundary"), + sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, false) + ) + ); + + edit_items.push_back ( + MenuElem ( + _("Move Range End to Next Region Boundary"), + sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, true) + ) + ); edit_items.push_back (SeparatorElem()); - edit_items.push_back (MenuElem (_("Convert to region in-place"), mem_fun(*this, &Editor::separate_region_from_selection))); + edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection))); edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection))); edit_items.push_back (SeparatorElem()); @@ -2181,7 +1902,7 @@ Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items) play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start))); play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region))); play_items.push_back (SeparatorElem()); - play_items.push_back (MenuElem (_("Loop Region"), sigc::mem_fun(*this, &Editor::loop_selected_region))); + play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true))); edit_items.push_back (MenuElem (_("Play"), *play_menu)); @@ -2221,8 +1942,8 @@ Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items) cutnpaste_items.push_back (SeparatorElem()); - cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun(*this, &Editor::align), ARDOUR::SyncPoint))); - cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun(*this, &Editor::align_relative), ARDOUR::SyncPoint))); + cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint))); + cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint))); edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu)); @@ -2436,7 +2157,7 @@ Editor::set_state (const XMLNode& node, int /*version*/) { const XMLProperty* prop; XMLNode* geometry; - int x, y, xoff, yoff; + int x, y; Gdk::Geometry g; if ((prop = node.property ("id")) != 0) { @@ -2447,8 +2168,6 @@ Editor::set_state (const XMLNode& node, int /*version*/) g.base_height = default_height; x = 1; y = 1; - xoff = 0; - yoff = 21; if ((geometry = find_named_node (node, "geometry")) != 0) { @@ -2480,19 +2199,6 @@ Editor::set_state (const XMLNode& node, int /*version*/) if (prop) { y = atoi (prop->value()); } - - if ((prop = geometry->property ("x_off")) == 0) { - prop = geometry->property ("x-off"); - } - if (prop) { - xoff = atoi (prop->value()); - } - if ((prop = geometry->property ("y_off")) == 0) { - prop = geometry->property ("y-off"); - } - if (prop) { - yoff = atoi (prop->value()); - } } set_default_size (g.base_width, g.base_height); @@ -2500,12 +2206,12 @@ Editor::set_state (const XMLNode& node, int /*version*/) if (_session && (prop = node.property ("playhead"))) { framepos_t pos; - sscanf (prop->value().c_str(), "%" PRIi64, &pos); + sscanf (prop->value().c_str(), "%" PRIi64, &pos); playhead_cursor->set_position (pos); } else { playhead_cursor->set_position (0); } - + if ((prop = node.property ("mixer-width"))) { editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width)); } @@ -2535,7 +2241,7 @@ Editor::set_state (const XMLNode& node, int /*version*/) set_mouse_mode (MouseObject, true); } - if ((prop = node.property ("left-frame")) != 0){ + if ((prop = node.property ("left-frame")) != 0) { framepos_t pos; if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) { reset_x_origin (pos); @@ -2588,18 +2294,18 @@ Editor::set_state (const XMLNode& node, int /*version*/) } } - if ((prop = node.property ("stationary-playhead"))) { - bool yn = (prop->value() == "yes"); - set_stationary_playhead (yn); - RefPtr act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead")); - if (act) { - RefPtr tact = RefPtr::cast_dynamic(act); - if (tact->get_active() != yn) { - tact->set_active (yn); - } - } - } - + if ((prop = node.property ("stationary-playhead"))) { + bool yn = string_is_affirmative (prop->value()); + set_stationary_playhead (yn); + RefPtr act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead")); + if (act) { + RefPtr tact = RefPtr::cast_dynamic(act); + if (tact->get_active() != yn) { + tact->set_active (yn); + } + } + } + if ((prop = node.property ("region-list-sort-type"))) { RegionListSortType st; _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true); @@ -2614,36 +2320,43 @@ Editor::set_state (const XMLNode& node, int /*version*/) if ((prop = node.property ("show-editor-mixer"))) { Glib::RefPtr act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer")); - if (act) { + assert (act); - Glib::RefPtr tact = Glib::RefPtr::cast_dynamic(act); - bool yn = string_is_affirmative (prop->value()); + Glib::RefPtr tact = Glib::RefPtr::cast_dynamic(act); + bool yn = string_is_affirmative (prop->value()); - /* do it twice to force the change */ + /* do it twice to force the change */ - tact->set_active (!yn); - tact->set_active (yn); - } + 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) { + assert (act); - Glib::RefPtr tact = Glib::RefPtr::cast_dynamic(act); - bool yn = string_is_affirmative (prop->value()); + Glib::RefPtr tact = Glib::RefPtr::cast_dynamic(act); + bool yn = string_is_affirmative (prop->value()); - /* do it twice to force the change */ + /* do it twice to force the change */ - tact->set_active (!yn); - tact->set_active (yn); - } + tact->set_active (!yn); + tact->set_active (yn); } if ((prop = node.property (X_("editor-list-page")))) { - the_notebook.set_current_page (atoi (prop->value ())); + _the_notebook.set_current_page (atoi (prop->value ())); + } + + if ((prop = node.property (X_("show-marker-lines")))) { + Glib::RefPtr act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines")); + assert (act); + Glib::RefPtr tact = Glib::RefPtr::cast_dynamic (act); + bool yn = string_is_affirmative (prop->value ()); + + tact->set_active (!yn); + tact->set_active (yn); } XMLNodeList children = node.children (); @@ -2667,9 +2380,8 @@ Editor::get_state () if (is_realized()) { Glib::RefPtr win = get_window(); - int x, y, xoff, yoff, width, height; + int x, y, width, height; win->get_root_origin(x, y); - win->get_position(xoff, yoff); win->get_size(width, height); XMLNode* geometry = new XMLNode ("geometry"); @@ -2682,12 +2394,11 @@ Editor::get_state () geometry->add_property("x-pos", string(buf)); snprintf(buf, sizeof(buf), "%d", y); geometry->add_property("y-pos", string(buf)); - snprintf(buf, sizeof(buf), "%d", xoff); - geometry->add_property("x-off", string(buf)); - snprintf(buf, sizeof(buf), "%d", yoff); - geometry->add_property("y-off", string(buf)); snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast(&edit_pane)->gobj())); geometry->add_property("edit-horizontal-pane-pos", string(buf)); + geometry->add_property("notebook-shrunk", _notebook_shrunk ? "1" : "0"); + snprintf(buf,sizeof(buf), "%d",pre_maximal_horizontal_pane_position); + geometry->add_property("pre-maximal-horizontal-pane-position", string(buf)); snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast(&editor_summary_pane)->gobj())); geometry->add_property("edit-vertical-pane-pos", string(buf)); @@ -2716,7 +2427,7 @@ Editor::get_state () node->add_property ("show-measures", _show_measures ? "yes" : "no"); node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no"); - node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no"); + node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no"); node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no"); node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ())); node->add_property ("mouse-mode", enum2str(mouse_mode)); @@ -2735,12 +2446,20 @@ Editor::get_state () node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no"); } - snprintf (buf, sizeof (buf), "%d", the_notebook.get_current_page ()); + snprintf (buf, sizeof (buf), "%d", _the_notebook.get_current_page ()); node->add_property (X_("editor-list-page"), buf); + if (button_bindings) { + XMLNode* bb = new XMLNode (X_("Buttons")); + button_bindings->save (*bb); + node->add_child_nocopy (*bb); + } + + node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no"); + node->add_child_nocopy (selection->get_state ()); node->add_child_nocopy (_regions->get_state ()); - + return *node; } @@ -2814,11 +2533,10 @@ Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /* break; case SnapToTimecodeSeconds: - if (_session->timecode_offset_negative()) - { - start += _session->timecode_offset (); + if (_session->config.get_timecode_offset_negative()) { + start += _session->config.get_timecode_offset (); } else { - start -= _session->timecode_offset (); + start -= _session->config.get_timecode_offset (); } if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) { start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second; @@ -2826,31 +2544,28 @@ Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /* start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second; } - if (_session->timecode_offset_negative()) - { - start -= _session->timecode_offset (); + if (_session->config.get_timecode_offset_negative()) { + start -= _session->config.get_timecode_offset (); } else { - start += _session->timecode_offset (); + start += _session->config.get_timecode_offset (); } break; case SnapToTimecodeMinutes: - if (_session->timecode_offset_negative()) - { - start += _session->timecode_offset (); + if (_session->config.get_timecode_offset_negative()) { + start += _session->config.get_timecode_offset (); } else { - start -= _session->timecode_offset (); + start -= _session->config.get_timecode_offset (); } if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) { start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute; } else { start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute; } - if (_session->timecode_offset_negative()) - { - start -= _session->timecode_offset (); + if (_session->config.get_timecode_offset_negative()) { + start -= _session->config.get_timecode_offset (); } else { - start += _session->timecode_offset (); + start += _session->config.get_timecode_offset (); } break; default: @@ -3034,19 +2749,6 @@ Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark) void Editor::setup_toolbar () { - string pixmap_path; - - /* Mode Buttons (tool selection) */ - - mouse_move_button.set_relief(Gtk::RELIEF_NONE); - mouse_select_button.set_relief(Gtk::RELIEF_NONE); - mouse_gain_button.set_relief(Gtk::RELIEF_NONE); - mouse_zoom_button.set_relief(Gtk::RELIEF_NONE); - mouse_timefx_button.set_relief(Gtk::RELIEF_NONE); - mouse_audition_button.set_relief(Gtk::RELIEF_NONE); - // internal_edit_button.set_relief(Gtk::RELIEF_NONE); - join_object_range_button.set_relief(Gtk::RELIEF_NONE); - HBox* mode_box = manage(new HBox); mode_box->set_border_width (2); mode_box->set_spacing(4); @@ -3073,7 +2775,6 @@ Editor::setup_toolbar () mouse_mode_button_box->pack_start (mouse_audition_button); mouse_mode_button_box->pack_start (internal_edit_button); - vector edit_mode_strings; edit_mode_strings.push_back (edit_mode_to_string (Slide)); if (!Profile->get_sae()) { edit_mode_strings.push_back (edit_mode_to_string (Splice)); @@ -3096,13 +2797,13 @@ Editor::setup_toolbar () } _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast(&toolbar_hbox), - &_mouse_mode_tearoff->tearoff_window())); + &_mouse_mode_tearoff->tearoff_window())); _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast (&toolbar_hbox), - &_mouse_mode_tearoff->tearoff_window(), 1)); + &_mouse_mode_tearoff->tearoff_window(), 1)); _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast(&toolbar_hbox), - &_mouse_mode_tearoff->tearoff_window())); + &_mouse_mode_tearoff->tearoff_window())); _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast (&toolbar_hbox), - &_mouse_mode_tearoff->tearoff_window(), 1)); + &_mouse_mode_tearoff->tearoff_window(), 1)); mouse_move_button.set_mode (false); mouse_select_button.set_mode (false); @@ -3136,15 +2837,15 @@ Editor::setup_toolbar () _zoom_box.set_border_width (0); zoom_in_button.set_name ("EditorTimeButton"); - zoom_in_button.set_image (*(manage (new Image (Stock::ZOOM_IN, Gtk::ICON_SIZE_MENU)))); + zoom_in_button.set_image (*(manage (new Image (::get_icon ("zoom_in"))))); zoom_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), false)); zoom_out_button.set_name ("EditorTimeButton"); - zoom_out_button.set_image (*(manage (new Image (Stock::ZOOM_OUT, Gtk::ICON_SIZE_MENU)))); + zoom_out_button.set_image (*(manage (new Image (::get_icon ("zoom_out"))))); zoom_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), true)); zoom_out_full_button.set_name ("EditorTimeButton"); - zoom_out_full_button.set_image (*(manage (new Image (Stock::ZOOM_100, Gtk::ICON_SIZE_MENU)))); + zoom_out_full_button.set_image (*(manage (new Image (::get_icon ("zoom_full"))))); zoom_out_full_button.signal_clicked().connect (sigc::mem_fun(*this, &Editor::temporal_zoom_session)); zoom_focus_selector.set_name ("ZoomFocusSelector"); @@ -3156,21 +2857,23 @@ Editor::setup_toolbar () _zoom_box.pack_start (zoom_out_full_button, false, false); _zoom_box.pack_start (zoom_focus_selector); - + /* Track zoom buttons */ tav_expand_button.set_name ("TrackHeightButton"); - tav_expand_button.set_size_request(-1,20); - tav_expand_button.add (*(manage (new Image (::get_icon("tav_exp"))))); - tav_expand_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), true)); + tav_expand_button.set_size_request (-1, 20); + tav_expand_button.add (*(manage (new Image (::get_icon ("tav_exp"))))); + RefPtr act = ActionManager::get_action (X_("Editor"), X_("expand-tracks")); + act->connect_proxy (tav_expand_button); tav_shrink_button.set_name ("TrackHeightButton"); - tav_shrink_button.set_size_request(-1,20); - tav_shrink_button.add (*(manage (new Image (::get_icon("tav_shrink"))))); - tav_shrink_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), false)); + tav_shrink_button.set_size_request (-1, 20); + tav_shrink_button.add (*(manage (new Image (::get_icon ("tav_shrink"))))); + act = ActionManager::get_action (X_("Editor"), X_("shrink-tracks")); + act->connect_proxy (tav_shrink_button); _zoom_box.pack_start (tav_shrink_button); _zoom_box.pack_start (tav_expand_button); - + _zoom_tearoff = manage (new TearOff (_zoom_box)); _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast(&toolbar_hbox), @@ -3181,7 +2884,7 @@ Editor::setup_toolbar () &_zoom_tearoff->tearoff_window())); _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast (&toolbar_hbox), &_zoom_tearoff->tearoff_window(), 0)); - + snap_box.set_spacing (1); snap_box.set_border_width (2); @@ -3212,7 +2915,7 @@ Editor::setup_toolbar () nudge_box->pack_start (nudge_backward_button, false, false); nudge_box->pack_start (nudge_forward_button, false, false); - nudge_box->pack_start (nudge_clock, false, false); + nudge_box->pack_start (*nudge_clock, false, false); /* Pack everything in... */ @@ -3223,19 +2926,19 @@ Editor::setup_toolbar () _tools_tearoff = manage (new TearOff (*hbox)); _tools_tearoff->set_name ("MouseModeBase"); _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false); - + if (Profile->get_sae()) { _tools_tearoff->set_can_be_torn_off (false); } _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast(&toolbar_hbox), - &_tools_tearoff->tearoff_window())); + &_tools_tearoff->tearoff_window())); _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast (&toolbar_hbox), - &_tools_tearoff->tearoff_window(), 0)); + &_tools_tearoff->tearoff_window(), 0)); _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast(&toolbar_hbox), - &_tools_tearoff->tearoff_window())); + &_tools_tearoff->tearoff_window())); _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast (&toolbar_hbox), - &_tools_tearoff->tearoff_window(), 0)); + &_tools_tearoff->tearoff_window(), 0)); toolbar_hbox.set_spacing (10); toolbar_hbox.set_border_width (1); @@ -3245,7 +2948,11 @@ Editor::setup_toolbar () toolbar_hbox.pack_start (*_tools_tearoff, false, false); hbox->pack_start (snap_box, false, false); - hbox->pack_start (*nudge_box, false, false); + if (!Profile->get_small_screen()) { + hbox->pack_start (*nudge_box, false, false); + } else { + ARDOUR_UI::instance()->editor_transport_box().pack_start (*nudge_box, false, false); + } hbox->pack_start (panic_box, false, false); hbox->show_all (); @@ -3253,9 +2960,15 @@ Editor::setup_toolbar () toolbar_base.set_name ("ToolBarBase"); toolbar_base.add (toolbar_hbox); + _toolbar_viewport.add (toolbar_base); + /* stick to the required height but allow width to vary if there's not enough room */ + _toolbar_viewport.set_size_request (1, -1); + toolbar_frame.set_shadow_type (SHADOW_OUT); toolbar_frame.set_name ("BaseFrame"); - toolbar_frame.add (toolbar_base); + toolbar_frame.add (_toolbar_viewport); + + DPIReset.connect (sigc::mem_fun (*this, &Editor::resize_text_widgets)); } void @@ -3268,7 +2981,7 @@ Editor::setup_tooltips () ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions")); ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges")); ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)")); - ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: context-click for possible operations")); + ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations")); ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards")); ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards")); ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In")); @@ -3302,7 +3015,6 @@ Editor::setup_midi_toolbar () /* Midi sound notes */ midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes"))))); - midi_sound_notes.set_relief(Gtk::RELIEF_NONE); midi_sound_notes.unset_flags (CAN_FOCUS); /* Panic */ @@ -3328,7 +3040,7 @@ Editor::convert_drop_to_paths ( if (_session == 0) { return -1; } - + vector uris = data.get_uris(); if (uris.empty()) { @@ -3465,34 +3177,18 @@ Editor::begin_reversible_command (string name) } void -Editor::commit_reversible_command () +Editor::begin_reversible_command (GQuark q) { if (_session) { - _session->commit_reversible_command (); - } -} - -void -Editor::set_route_group_solo (Route& route, bool yn) -{ - RouteGroup *route_group; - - if ((route_group = route.route_group()) != 0) { - route_group->apply (&Route::set_solo, yn, this); - } else { - route.set_solo (yn, this); + _session->begin_reversible_command (q); } } void -Editor::set_route_group_mute (Route& route, bool yn) +Editor::commit_reversible_command () { - RouteGroup *route_group = 0; - - if ((route_group = route.route_group()) != 0) { - route_group->apply (&Route::set_mute, yn, this); - } else { - route.set_mute (yn, this); + if (_session) { + _session->commit_reversible_command (); } } @@ -3531,14 +3227,10 @@ Editor::duplicate_dialog (bool with_dialog) } } - RegionSelection rs; - get_regions_for_action (rs); - - if (mouse_mode != MouseRange) { + RegionSelection rs = get_regions_from_selection_and_entered (); - if (rs.empty()) { - return; - } + if (mouse_mode != MouseRange && rs.empty()) { + return; } if (with_dialog) { @@ -3556,112 +3248,39 @@ Editor::duplicate_dialog (bool with_dialog) /* dialogs have ::add_action_widget() but that puts the spinner in the wrong place, visually. so do this by hand. - */ - - hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12); - spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT)); - spinner.grab_focus(); - - hbox.show (); - label.show (); - spinner.show (); - - win.add_button (Stock::CANCEL, RESPONSE_CANCEL); - win.add_button (_("Duplicate"), RESPONSE_ACCEPT); - win.set_default_response (RESPONSE_ACCEPT); - - win.set_position (WIN_POS_MOUSE); - - spinner.grab_focus (); - - switch (win.run ()) { - case RESPONSE_ACCEPT: - break; - default: - return; - } - - times = adjustment.get_value(); - } - - if (mouse_mode == MouseRange) { - duplicate_selection (times); - } else { - duplicate_some_regions (rs, times); - } -} - -void -Editor::show_verbose_canvas_cursor () -{ - verbose_canvas_cursor->raise_to_top(); - verbose_canvas_cursor->show(); - verbose_cursor_visible = true; -} - -void -Editor::hide_verbose_canvas_cursor () -{ - verbose_canvas_cursor->hide(); - verbose_cursor_visible = false; -} - -double -Editor::clamp_verbose_cursor_x (double x) -{ - if (x < 0) { - x = 0; - } else { - x = min (_canvas_width - 200.0, x); - } - return x; -} - -double -Editor::clamp_verbose_cursor_y (double y) -{ - if (y < canvas_timebars_vsize) { - y = canvas_timebars_vsize; - } else { - y = min (_canvas_height - 50, y); - } - return y; -} + */ -void -Editor::show_verbose_canvas_cursor_with (const string & txt, int32_t xoffset, int32_t yoffset) -{ - verbose_canvas_cursor->property_text() = txt.c_str(); + hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12); + spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT)); + spinner.grab_focus(); - int x, y; - double wx, wy; + hbox.show (); + label.show (); + spinner.show (); - track_canvas->get_pointer (x, y); - track_canvas->window_to_world (x, y, wx, wy); + win.add_button (Stock::CANCEL, RESPONSE_CANCEL); + win.add_button (_("Duplicate"), RESPONSE_ACCEPT); + win.set_default_response (RESPONSE_ACCEPT); - wx += xoffset; - wy += yoffset; + win.set_position (WIN_POS_MOUSE); - /* don't get too close to the edge */ - verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (wx); - verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (wy); + spinner.grab_focus (); - show_verbose_canvas_cursor (); -} + switch (win.run ()) { + case RESPONSE_ACCEPT: + break; + default: + return; + } -void -Editor::set_verbose_canvas_cursor (const string & txt, double x, double y) -{ - verbose_canvas_cursor->property_text() = txt.c_str(); - /* don't get too close to the edge */ - verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (x); - verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (y); -} + times = adjustment.get_value(); + } -void -Editor::set_verbose_canvas_cursor_text (const string & txt) -{ - verbose_canvas_cursor->property_text() = txt.c_str(); + if (mouse_mode == MouseRange) { + duplicate_selection (times); + } else { + duplicate_some_regions (rs, times); + } } void @@ -3693,7 +3312,11 @@ Editor::cycle_edit_mode () void Editor::edit_mode_selection_done () { - Config->set_edit_mode (string_to_edit_mode (edit_mode_selector.get_active_text ())); + string s = edit_mode_selector.get_active_text (); + + if (!s.empty()) { + Config->set_edit_mode (string_to_edit_mode (s)); + } } void @@ -3855,16 +3478,19 @@ Editor::zoom_focus_selection_done () } } -gint +bool Editor::edit_controls_button_release (GdkEventButton* ev) { if (Keyboard::is_context_menu_event (ev)) { ARDOUR_UI::instance()->add_route (this); + } else if (ev->button == 1) { + selection->clear_tracks (); } - return TRUE; + + return true; } -gint +bool Editor::mouse_select_button_release (GdkEventButton* ev) { /* this handles just right-clicks */ @@ -3887,9 +3513,6 @@ Editor::set_zoom_focus (ZoomFocus f) if (zoom_focus != f) { zoom_focus = f; - - ZoomFocusChanged (); /* EMIT_SIGNAL */ - instant_save (); } } @@ -3911,7 +3534,6 @@ Editor::pane_allocation_handler (Allocation &alloc, Paned* which) XMLProperty* prop; char buf[32]; XMLNode* node = ARDOUR_UI::instance()->editor_settings(); - int width, height; enum Pane { Horizontal = 0x1, @@ -3919,23 +3541,8 @@ Editor::pane_allocation_handler (Allocation &alloc, Paned* which) }; static Pane done; - - XMLNode* geometry; - width = default_width; - height = default_height; - - if ((geometry = find_named_node (*node, "geometry")) != 0) { - - prop = geometry->property ("x-size"); - if (prop) { - width = atoi (prop->value()); - } - prop = geometry->property ("y-size"); - if (prop) { - height = atoi (prop->value()); - } - } + XMLNode* geometry = find_named_node (*node, "geometry"); if (which == static_cast (&edit_pane)) { @@ -3943,6 +3550,14 @@ Editor::pane_allocation_handler (Allocation &alloc, Paned* which) return; } + if (geometry && (prop = geometry->property ("notebook-shrunk"))) { + _notebook_shrunk = string_is_affirmative (prop->value ()); + } + + if (geometry && (prop = geometry->property ("pre-maximal-horizontal-pane-position"))) { + pre_maximal_horizontal_pane_position = atoi (prop->value ()); + } + if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) { /* initial allocation is 90% to canvas, 10% to notebook */ pos = (int) floor (alloc.get_width() * 0.90f); @@ -3953,11 +3568,13 @@ Editor::pane_allocation_handler (Allocation &alloc, Paned* which) if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) { edit_pane.set_position (pos); - pre_maximal_horizontal_pane_position = pos; + if (pre_maximal_horizontal_pane_position == 0) { + pre_maximal_horizontal_pane_position = pos; + } } done = (Pane) (done | Horizontal); - + } else if (which == static_cast (&editor_summary_pane)) { if (done & Vertical) { @@ -4022,11 +3639,14 @@ Editor::toggle_follow_playhead () } } +/** @param yn true to follow playhead, otherwise false. + * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false. + */ void -Editor::set_follow_playhead (bool yn) +Editor::set_follow_playhead (bool yn, bool catch_up) { if (_follow_playhead != yn) { - if ((_follow_playhead = yn) == true) { + if ((_follow_playhead = yn) == true && catch_up) { /* catch up */ reset_x_origin_to_follow_playhead (); } @@ -4037,24 +3657,24 @@ Editor::set_follow_playhead (bool yn) void Editor::toggle_stationary_playhead () { - RefPtr act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead")); - if (act) { - RefPtr tact = RefPtr::cast_dynamic(act); - set_stationary_playhead (tact->get_active()); - } + RefPtr act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead")); + if (act) { + RefPtr tact = RefPtr::cast_dynamic(act); + set_stationary_playhead (tact->get_active()); + } } void Editor::set_stationary_playhead (bool yn) { - if (_stationary_playhead != yn) { - if ((_stationary_playhead = yn) == true) { - /* catch up */ - // FIXME need a 3.0 equivalent of this 2.X call - // update_current_screen (); - } - instant_save (); - } + if (_stationary_playhead != yn) { + if ((_stationary_playhead = yn) == true) { + /* catch up */ + // FIXME need a 3.0 equivalent of this 2.X call + // update_current_screen (); + } + instant_save (); + } } void @@ -4193,7 +3813,7 @@ Editor::get_nudge_distance (framepos_t pos, framecnt_t& next) { framecnt_t ret; - ret = nudge_clock.current_duration (pos); + ret = nudge_clock->current_duration (pos); next = ret + 1; /* XXXX fix me */ return ret; @@ -4204,8 +3824,8 @@ Editor::playlist_deletion_dialog (boost::shared_ptr pl) { ArdourDialog dialog (_("Playlist Deletion")); Label label (string_compose (_("Playlist %1 is currently unused.\n" - "If left alone, no audio files used by it will be cleaned.\n" - "If deleted, audio files used by it alone by will cleaned."), + "If it is kept, its audio files will not be cleaned.\n" + "If it is deleted, audio files used by it alone will be cleaned."), pl->name())); dialog.set_position (WIN_POS_CENTER); @@ -4213,8 +3833,8 @@ Editor::playlist_deletion_dialog (boost::shared_ptr pl) label.show (); - dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT); - dialog.add_button (_("Keep playlist"), RESPONSE_REJECT); + dialog.add_button (_("Delete Playlist"), RESPONSE_ACCEPT); + dialog.add_button (_("Keep Playlist"), RESPONSE_REJECT); dialog.add_button (_("Cancel"), RESPONSE_CANCEL); switch (dialog.run ()) { @@ -4313,7 +3933,7 @@ Editor::control_layout_scroll (GdkEventScroll* ev) void Editor::session_state_saved (string) { - update_title (); + update_title (); _snapshots->redisplay (); } @@ -4336,7 +3956,7 @@ Editor::maximise_editing_space () if (post_maximal_vertical_pane_position == 0) { post_maximal_vertical_pane_position = editor_summary_pane.get_height(); } - + fullscreen (); if (post_maximal_editor_width) { @@ -4346,18 +3966,32 @@ Editor::maximise_editing_space () edit_pane.set_position (post_maximal_horizontal_pane_position); } - if (post_maximal_editor_height) { - editor_summary_pane.set_position (post_maximal_vertical_pane_position - - abs(post_maximal_editor_height - pre_maximal_editor_height)); - } else { - editor_summary_pane.set_position (post_maximal_vertical_pane_position); - } + /* Hack: we must do this in an idle handler for it to work; see comment in + restore_editing_space() + */ + + Glib::signal_idle().connect ( + sigc::bind ( + sigc::mem_fun (*this, &Editor::idle_reset_vertical_pane_position), + post_maximal_vertical_pane_position + ) + ); if (Config->get_keep_tearoffs()) { _mouse_mode_tearoff->set_visible (true); _tools_tearoff->set_visible (true); - _zoom_tearoff->set_visible (true); + if (Config->get_show_zoom_tools ()) { + _zoom_tearoff->set_visible (true); + } } + +} + +bool +Editor::idle_reset_vertical_pane_position (int p) +{ + editor_summary_pane.set_position (p); + return false; } void @@ -4372,17 +4006,29 @@ Editor::restore_editing_space () if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) { post_maximal_vertical_pane_position = editor_summary_pane.get_position(); } - + unfullscreen(); _mouse_mode_tearoff->set_visible (true); _tools_tearoff->set_visible (true); - _zoom_tearoff->set_visible (true); + if (Config->get_show_zoom_tools ()) { + _zoom_tearoff->set_visible (true); + } post_maximal_editor_width = this->get_width(); post_maximal_editor_height = this->get_height(); edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width)); - editor_summary_pane.set_position (pre_maximal_vertical_pane_position + abs(this->get_height() - pre_maximal_editor_height)); + + /* This is a bit of a hack, but it seems that if you set the vertical pane position + here it gets reset to some wrong value after this method has finished. Doing + the setup in an idle callback seems to work. + */ + Glib::signal_idle().connect ( + sigc::bind ( + sigc::mem_fun (*this, &Editor::idle_reset_vertical_pane_position), + pre_maximal_vertical_pane_position + ) + ); } /** @@ -4495,6 +4141,16 @@ Editor::reposition_and_zoom (framepos_t frame, double fpu) } } +Editor::VisualState::VisualState () + : gui_state (new GUIObjectState) +{ +} + +Editor::VisualState::~VisualState () +{ + delete gui_state; +} + Editor::VisualState* Editor::current_visual_state (bool with_tracks) { @@ -4504,10 +4160,8 @@ Editor::current_visual_state (bool with_tracks) vs->leftmost_frame = leftmost_frame; vs->zoom_focus = zoom_focus; - if (with_tracks) { - for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { - vs->track_states.push_back (TAVState ((*i), &(*i)->get_state())); - } + if (with_tracks) { + *(vs->gui_state) = *ARDOUR_UI::instance()->gui_object_state; } return vs; @@ -4562,22 +4216,14 @@ Editor::use_visual_state (VisualState& vs) set_zoom_focus (vs.zoom_focus); reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit); + + *ARDOUR_UI::instance()->gui_object_state = *vs.gui_state; - for (list::iterator i = vs.track_states.begin(); i != vs.track_states.end(); ++i) { - TrackViewList::iterator t; - - /* check if the track still exists - it could have been deleted */ - - if ((t = find (track_views.begin(), track_views.end(), i->first)) != track_views.end()) { - (*t)->set_state (*(i->second), Stateful::loading_state_version); - } - } - - - if (!vs.track_states.empty()) { - _routes->update_visibility (); + for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { + (*i)->reset_visual_state (); } + _routes->update_visibility (); _routes->resume_redisplay (); no_save_visual = false; @@ -4621,8 +4267,8 @@ Editor::post_zoom () framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width); - if (frames_per_unit != zoom_range_clock.current_duration()) { - zoom_range_clock.set (frames); + if (frames_per_unit != zoom_range_clock->current_duration()) { + zoom_range_clock->set (frames); } if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) { @@ -4642,6 +4288,8 @@ Editor::post_zoom () refresh_location_display(); _summary->set_overlays_dirty (); + update_marker_labels (); + instant_save (); } @@ -4691,7 +4339,19 @@ Editor::idle_visual_changer () VisualChange::Type p = pending_visual_change.pending; pending_visual_change.pending = (VisualChange::Type) 0; - double last_time_origin = horizontal_position (); + double const last_time_origin = horizontal_position (); + + if (p & VisualChange::TimeOrigin) { + /* This is a bit of a hack, but set_frames_per_unit + below will (if called) end up with the + CrossfadeViews looking at Editor::leftmost_frame, + and if we're changing origin and zoom in the same + operation it will be the wrong value unless we + update it here. + */ + + leftmost_frame = pending_visual_change.time_origin; + } if (p & VisualChange::ZoomLevel) { set_frames_per_unit (pending_visual_change.frames_per_unit); @@ -4746,6 +4406,7 @@ Editor::get_preferred_edit_position (bool ignore_playhead) EditPoint ep = _edit_point; if (entered_marker) { + DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position())); return entered_marker->position(); } @@ -4756,6 +4417,7 @@ Editor::get_preferred_edit_position (bool ignore_playhead) switch (ep) { case EditAtPlayhead: where = _session->audible_frame(); + DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where)); break; case EditAtSelectedMarker: @@ -4768,6 +4430,7 @@ Editor::get_preferred_edit_position (bool ignore_playhead) } else { where = loc->end(); } + DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where)); break; } } @@ -4780,6 +4443,7 @@ Editor::get_preferred_edit_position (bool ignore_playhead) return 0; } snap_to (where); + DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where)); break; } @@ -4858,7 +4522,9 @@ Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewLi } for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) { + RouteTimeAxisView* rtv = dynamic_cast(*t); + if (rtv) { boost::shared_ptr tr; boost::shared_ptr pl; @@ -4866,7 +4532,7 @@ Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewLi if ((tr = rtv->track()) && ((pl = tr->playlist()))) { Playlist::RegionList* regions = pl->regions_at ( - (framepos_t) floor ( (double)where * tr->speed())); + (framepos_t) floor ( (double) where * tr->speed())); for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) { RegionView* rv = rtv->view()->find_view (*i); @@ -4901,7 +4567,7 @@ Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackVie if ((tr = rtv->track()) && ((pl = tr->playlist()))) { Playlist::RegionList* regions = pl->regions_touched ( - (framepos_t) floor ( (double)where * tr->speed()), max_framepos); + (framepos_t) floor ( (double)where * tr->speed()), max_framepos); for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) { @@ -4918,47 +4584,96 @@ Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackVie } } -/** Find all regions which are either: - * - selected or - * - the entered_regionview (if allow_entered == true) or - * - under the preferred edit position AND on a selected track, or on a track - * which is in the same active edit-enable route group as a selected region (if allow_edit_position == true) - * @param rs Returned region list. - * @param allow_entered true to include the entered_regionview in the list. +/** Start with regions that are selected. Then add equivalent regions + * on tracks in the same active edit-enabled route group as any of + * the regions that we started with. */ -void -Editor::get_regions_for_action (RegionSelection& rs, bool allow_entered, bool allow_edit_position) + +RegionSelection +Editor::get_regions_from_selection () +{ + return get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id); +} + +/** Get regions using the following method: + * + * Make an initial region list using the selected regions, unless + * the edit point is `mouse' and the mouse is over an unselected + * region. In this case, start with just that region. + * + * Then, make an initial track list of the tracks that these + * regions are on, and if the edit point is not `mouse', add the + * selected tracks. + * + * Look at this track list and add any other tracks that are on the + * same active edit-enabled route group as one of the initial tracks. + * + * Finally take the initial region list and add any regions that are + * under the edit point on one of the tracks on the track list to get + * the returned region list. + * + * The rationale here is that the mouse edit point is special in that + * its position describes both a time and a track; the other edit + * modes only describe a time. Hence if the edit point is `mouse' we + * ignore selected tracks, as we assume the user means something by + * pointing at a particular track. Also in this case we take note of + * the region directly under the edit point, as there is always just one + * (rather than possibly several with non-mouse edit points). + */ + +RegionSelection +Editor::get_regions_from_selection_and_edit_point () { - /* Start with selected regions */ - rs = selection->regions; + RegionSelection regions; - /* Add the entered_regionview, if requested */ - if (allow_entered && entered_regionview) { - rs.add (entered_regionview); + if (_edit_point == EditAtMouse && entered_regionview && !selection->regions.contains (entered_regionview)) { + regions.add (entered_regionview); + } else { + regions = selection->regions; } - if (allow_edit_position) { + TrackViewList tracks; + + if (_edit_point != EditAtMouse) { + tracks = selection->tracks; + } - TrackViewList tracks = selection->tracks; + /* Add any other tracks that have regions that are in the same + edit-activated route group as one of our regions. + */ + for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) { - /* tracks is currently the set of selected tracks; add any other tracks that - * have regions that are in the same edit-activated route group as one of - * our regions */ - for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) { + RouteGroup* g = (*i)->get_time_axis_view().route_group (); - RouteGroup* g = (*i)->get_time_axis_view().route_group (); - if (g && g->is_active() && g->is_edit()) { - tracks.add (axis_views_from_routes (g->route_list())); - } - + if (g && g->is_active() && g->is_edit()) { + tracks.add (axis_views_from_routes (g->route_list())); } + } - if (!tracks.empty()) { - /* now find regions that are at the edit position on those tracks */ - framepos_t const where = get_preferred_edit_position (); - get_regions_at (rs, where, tracks); - } + if (!tracks.empty()) { + /* now find regions that are at the edit position on those tracks */ + framepos_t const where = get_preferred_edit_position (); + get_regions_at (regions, where, tracks); + } + + return regions; +} + +/** Start with regions that are selected, or the entered regionview if none are selected. + * Then add equivalent regions on tracks in the same active edit-enabled route group as any + * of the regions that we started with. + */ + +RegionSelection +Editor::get_regions_from_selection_and_entered () +{ + RegionSelection regions = selection->regions; + + if (regions.empty() && entered_regionview) { + regions.add (entered_regionview); } + + return get_equivalent_regions (regions, ARDOUR::Properties::edit.property_id); } void @@ -5089,7 +4804,7 @@ Editor::idle_resize () } } - _pending_resize_amount = 0; + _pending_resize_amount = 0; flush_canvas (); _group_tabs->set_dirty (); resize_idle_id = -1; @@ -5117,6 +4832,12 @@ Editor::region_view_added (RegionView *) _summary->set_dirty (); } +void +Editor::region_view_removed () +{ + _summary->set_dirty (); +} + TimeAxisView* Editor::axis_view_from_route (boost::shared_ptr r) const { @@ -5167,9 +4888,11 @@ Editor::handle_new_route (RouteList& routes) DataType dt = route->input()->default_type(); if (dt == ARDOUR::DataType::AUDIO) { - rtv = new AudioTimeAxisView (*this, _session, route, *track_canvas); + rtv = new AudioTimeAxisView (*this, _session, *track_canvas); + rtv->set_route (route); } else if (dt == ARDOUR::DataType::MIDI) { - rtv = new MidiTimeAxisView (*this, _session, route, *track_canvas); + rtv = new MidiTimeAxisView (*this, _session, *track_canvas); + rtv->set_route (route); } else { throw unknown_type(); } @@ -5179,18 +4902,24 @@ Editor::handle_new_route (RouteList& routes) rtv->effective_gain_display (); + if (internal_editing()) { + rtv->enter_internal_edit_mode (); + } else { + rtv->leave_internal_edit_mode (); + } + rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added)); + rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed)); } _routes->routes_added (new_views); + _summary->routes_added (new_views); if (show_editor_mixer_when_tracks_arrive) { show_editor_mixer (true); } editor_list_button.set_sensitive (true); - - _summary->set_dirty (); } void @@ -5204,7 +4933,7 @@ Editor::timeaxisview_deleted (TimeAxisView *tv) ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv); RouteTimeAxisView* rtav = dynamic_cast (tv); - + _routes->route_removed (tv); if (tv == entered_track) { @@ -5232,7 +4961,7 @@ Editor::timeaxisview_deleted (TimeAxisView *tv) if (rtav) { route = rtav->route (); - } + } if (current_mixer_strip && current_mixer_strip->route() == route) { @@ -5245,31 +4974,43 @@ Editor::timeaxisview_deleted (TimeAxisView *tv) } else { next_tv = (*i); } - - + + if (next_tv) { set_selected_mixer_strip (*next_tv); } else { /* make the editor mixer strip go away setting the * button to inactive (which also unticks the menu option) */ - + ActionManager::uncheck_toggleaction ("/Editor/show-editor-mixer"); } - } + } } void -Editor::hide_track_in_display (TimeAxisView* tv, bool /*temponly*/) +Editor::hide_track_in_display (TimeAxisView* tv, bool apply_to_selection) { - RouteTimeAxisView* rtv = dynamic_cast (tv); + if (apply_to_selection) { + for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ) { - if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) { - // this will hide the mixer strip - set_selected_mixer_strip (*tv); - } + TrackSelection::iterator j = i; + ++j; + + hide_track_in_display (*i, false); + + i = j; + } + } else { + RouteTimeAxisView* rtv = dynamic_cast (tv); + + if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) { + // this will hide the mixer strip + set_selected_mixer_strip (*tv); + } - _routes->hide_track_in_display (*tv); + _routes->hide_track_in_display (*tv); + } } bool @@ -5370,11 +5111,11 @@ Editor::show_region_in_region_list () void Editor::step_edit_status_change (bool yn) { - if (yn) { - start_step_editing (); - } else { - stop_step_editing (); - } + if (yn) { + start_step_editing (); + } else { + stop_step_editing (); + } } void @@ -5403,52 +5144,40 @@ Editor::check_step_edit () } bool -Editor::horizontal_scroll_left_press () +Editor::scroll_press (Direction dir) { ++_scroll_callbacks; - + if (_scroll_connection.connected() && _scroll_callbacks < 5) { /* delay the first auto-repeat */ return true; } - - double x = leftmost_position() - current_page_frames() / 5; - if (x < 0) { - x = 0; - } - - reset_x_origin (x); - /* do hacky auto-repeat */ - if (!_scroll_connection.connected ()) { - _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press), 100); - _scroll_callbacks = 0; - } + switch (dir) { + case LEFT: + scroll_backward (1); + break; - return true; -} + case RIGHT: + scroll_forward (1); + break; -void -Editor::horizontal_scroll_left_release () -{ - _scroll_connection.disconnect (); -} + case UP: + scroll_tracks_up_line (); + break; -bool -Editor::horizontal_scroll_right_press () -{ - ++_scroll_callbacks; - - if (_scroll_connection.connected() && _scroll_callbacks < 5) { - /* delay the first auto-repeat */ - return true; + case DOWN: + scroll_tracks_down_line (); + break; } - reset_x_origin (leftmost_position() + current_page_frames() / 5); - /* do hacky auto-repeat */ if (!_scroll_connection.connected ()) { - _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press), 100); + + _scroll_connection = Glib::signal_timeout().connect ( + sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), dir), 100 + ); + _scroll_callbacks = 0; } @@ -5456,7 +5185,7 @@ Editor::horizontal_scroll_right_press () } void -Editor::horizontal_scroll_right_release () +Editor::scroll_release () { _scroll_connection.disconnect (); } @@ -5470,15 +5199,15 @@ Editor::reset_x_origin_to_follow_playhead () if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) { if (_session->transport_speed() < 0) { - + if (frame > (current_page_frames() / 2)) { center_screen (frame-(current_page_frames()/2)); } else { center_screen (current_page_frames()/2); } - + } else { - + if (frame < leftmost_frame) { /* moving left */ framepos_t l = 0; @@ -5489,11 +5218,11 @@ Editor::reset_x_origin_to_follow_playhead () /* not rolling: end up with the playhead 3/4 of the way along the page */ l = frame - (3 * current_page_frames() / 4); } - + if (l < 0) { l = 0; } - + center_screen_internal (l + (current_page_frames() / 2), current_page_frames ()); } else { /* moving right */ @@ -5555,34 +5284,34 @@ Editor::super_rapid_screen_update () playhead_cursor->set_position (frame); } - if (!_stationary_playhead) { + if (!_stationary_playhead) { + + if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) { + reset_x_origin_to_follow_playhead (); + } - if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) { - reset_x_origin_to_follow_playhead (); - } + } else { - } else { - - /* don't do continuous scroll till the new position is in the rightmost quarter of the - editor canvas - */ -#if 0 - // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code - double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit; - if (target <= 0.0) { - target = 0.0; - } - if (fabs(target - current) < current_page_frames() / frames_per_unit) { - target = (target * 0.15) + (current * 0.85); - } else { - /* relax */ - } - - current = target; - set_horizontal_position (current); + /* don't do continuous scroll till the new position is in the rightmost quarter of the + editor canvas + */ +#if 0 + // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code + double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit; + if (target <= 0.0) { + target = 0.0; + } + if (fabs(target - current) < current_page_frames() / frames_per_unit) { + target = (target * 0.15) + (current * 0.85); + } else { + /* relax */ + } + + current = target; + set_horizontal_position (current); #endif - } - + } + } } @@ -5595,7 +5324,7 @@ Editor::session_going_away () _session_connections.drop_connections (); super_rapid_screen_update_connection.disconnect (); - + selection->clear (); cut_buffer->clear (); @@ -5635,8 +5364,8 @@ Editor::session_going_away () } track_views.clear (); - zoom_range_clock.set_session (0); - nudge_clock.set_session (0); + zoom_range_clock->set_session (0); + nudge_clock->set_session (0); editor_list_button.set_active(false); editor_list_button.set_sensitive(false); @@ -5664,47 +5393,49 @@ void Editor::show_editor_list (bool yn) { if (yn) { - the_notebook.show(); + _the_notebook.show (); } else { - the_notebook.hide(); + _the_notebook.hide (); } } void -Editor::change_region_layering_order (framepos_t position) +Editor::change_region_layering_order () { - if (!clicked_routeview) { - if (layering_order_editor) { - layering_order_editor->hide (); - } + framepos_t const position = get_preferred_edit_position (); + + if (!clicked_routeview) { + if (layering_order_editor) { + layering_order_editor->hide (); + } return; } - boost::shared_ptr track = boost::dynamic_pointer_cast (clicked_routeview->route()); + boost::shared_ptr track = boost::dynamic_pointer_cast (clicked_routeview->route()); - if (!track) { - return; - } + if (!track) { + return; + } boost::shared_ptr pl = track->playlist(); if (!pl) { - return; - } - - if (layering_order_editor == 0) { - layering_order_editor = new RegionLayeringOrderEditor(*this); - } + return; + } + + if (layering_order_editor == 0) { + layering_order_editor = new RegionLayeringOrderEditor(*this); + } - layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position); - layering_order_editor->maybe_present (); + layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position); + layering_order_editor->maybe_present (); } void -Editor::update_region_layering_order_editor (framepos_t frame) +Editor::update_region_layering_order_editor () { if (layering_order_editor && layering_order_editor->is_visible ()) { - change_region_layering_order (frame); + change_region_layering_order (); } } @@ -5724,3 +5455,76 @@ Editor::setup_fade_images () _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut"))); } + +/** @return Gtk::manage()d menu item for a given action from `editor_actions' */ +Gtk::MenuItem& +Editor::action_menu_item (std::string const & name) +{ + Glib::RefPtr a = editor_actions->get_action (name); + assert (a); + + return *manage (a->create_menu_item ()); +} + +void +Editor::resize_text_widgets () +{ + set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_FUDGE+10, 15); + set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_FUDGE+10, 15); + set_size_request_to_display_given_text (snap_type_selector, snap_type_strings, COMBO_FUDGE+10, 15); + set_size_request_to_display_given_text (snap_mode_selector, snap_mode_strings, COMBO_FUDGE+10, 15); + set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_FUDGE+10, 15); +} + +void +Editor::add_notebook_page (string const & name, Gtk::Widget& widget) +{ + EventBox* b = manage (new EventBox); + b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget)); + Label* l = manage (new Label (name)); + l->set_angle (-90); + b->add (*l); + b->show_all (); + _the_notebook.append_page (widget, *b); +} + +bool +Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page) +{ + if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) { + _the_notebook.set_current_page (_the_notebook.page_num (*page)); + } + + if (ev->type == GDK_2BUTTON_PRESS) { + + /* double-click on a notebook tab shrinks or expands the notebook */ + + if (_notebook_shrunk) { + edit_pane.set_position (pre_maximal_horizontal_pane_position); + _notebook_shrunk = false; + } else { + pre_maximal_horizontal_pane_position = edit_pane.get_position (); + edit_pane.set_position (edit_pane.get_position() + page->get_width()); + _notebook_shrunk = true; + } + } + + return true; +} + +void +Editor::popup_control_point_context_menu (ArdourCanvas::Item* item, GdkEvent* event) +{ + using namespace Menu_Helpers; + + MenuList& items = _control_point_context_menu.items (); + items.clear (); + + items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun (*this, &Editor::edit_control_point), item))); + items.push_back (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun (*this, &Editor::remove_control_point), item))); + if (!can_remove_control_point (item)) { + items.back().set_sensitive (false); + } + + _control_point_context_menu.popup (event->button.button, event->button.time); +}