2 Copyright (C) 2000-2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /* Note: public Editor methods are documented in public_editor.h */
30 #include "ardour_ui.h"
32 * ardour_ui.h include was moved to the top of the list
33 * due to a conflicting definition of 'Style' between
34 * Apple's MacTypes.h and BarController.
37 #include <boost/none.hpp>
39 #include <sigc++/bind.h>
41 #include "pbd/convert.h"
42 #include "pbd/error.h"
43 #include "pbd/enumwriter.h"
44 #include "pbd/memento_command.h"
45 #include "pbd/unknown_type.h"
47 #include <glibmm/miscutils.h>
48 #include <gtkmm/image.h>
49 #include <gdkmm/color.h>
50 #include <gdkmm/bitmap.h>
52 #include "gtkmm2ext/bindings.h"
53 #include "gtkmm2ext/grouped_buttons.h"
54 #include "gtkmm2ext/gtk_ui.h"
55 #include "gtkmm2ext/tearoff.h"
56 #include "gtkmm2ext/utils.h"
57 #include "gtkmm2ext/window_title.h"
58 #include "gtkmm2ext/choice.h"
59 #include "gtkmm2ext/cell_renderer_pixbuf_toggle.h"
61 #include "ardour/audio_diskstream.h"
62 #include "ardour/audio_track.h"
63 #include "ardour/audioplaylist.h"
64 #include "ardour/audioregion.h"
65 #include "ardour/location.h"
66 #include "ardour/midi_region.h"
67 #include "ardour/plugin_manager.h"
68 #include "ardour/profile.h"
69 #include "ardour/route_group.h"
70 #include "ardour/session_directory.h"
71 #include "ardour/session_route.h"
72 #include "ardour/session_state_utils.h"
73 #include "ardour/tempo.h"
74 #include "ardour/utils.h"
75 #include "ardour/session_playlists.h"
76 #include "ardour/audioengine.h"
78 #include "control_protocol/control_protocol.h"
84 #include "playlist_selector.h"
85 #include "audio_region_view.h"
86 #include "rgb_macros.h"
87 #include "selection.h"
88 #include "audio_streamview.h"
89 #include "time_axis_view.h"
90 #include "audio_time_axis.h"
92 #include "crossfade_view.h"
93 #include "canvas-noevent-text.h"
95 #include "public_editor.h"
96 #include "crossfade_edit.h"
97 #include "canvas_impl.h"
100 #include "gui_thread.h"
101 #include "simpleline.h"
102 #include "rhythm_ferret.h"
104 #include "tempo_lines.h"
105 #include "analysis_window.h"
106 #include "bundle_manager.h"
107 #include "global_port_matrix.h"
108 #include "editor_drag.h"
109 #include "editor_group_tabs.h"
110 #include "automation_time_axis.h"
111 #include "editor_routes.h"
112 #include "midi_time_axis.h"
113 #include "mixer_strip.h"
114 #include "editor_route_groups.h"
115 #include "editor_regions.h"
116 #include "editor_locations.h"
117 #include "editor_snapshots.h"
118 #include "editor_summary.h"
119 #include "region_layering_order_editor.h"
120 #include "mouse_cursors.h"
121 #include "editor_cursors.h"
126 #include "imageframe_socket_handler.h"
130 using namespace ARDOUR;
133 using namespace Glib;
134 using namespace Gtkmm2ext;
135 using namespace Editing;
137 using PBD::internationalize;
139 using Gtkmm2ext::Keyboard;
141 const double Editor::timebar_height = 15.0;
143 static const gchar *_snap_type_strings[] = {
145 N_("Timecode Frames"),
146 N_("Timecode Seconds"),
147 N_("Timecode Minutes"),
175 static const gchar *_snap_mode_strings[] = {
182 static const gchar *_edit_point_strings[] = {
189 static const gchar *_zoom_focus_strings[] = {
199 #ifdef USE_RUBBERBAND
200 static const gchar *_rb_opt_strings[] = {
203 N_("Balanced multitimbral mixture"),
204 N_("Unpitched percussion with stable notes"),
205 N_("Crisp monophonic instrumental"),
206 N_("Unpitched solo percussion"),
207 N_("Resample without preserving pitch"),
213 show_me_the_size (Requisition* r, const char* what)
215 cerr << "size of " << what << " = " << r->width << " x " << r->height << endl;
220 pane_size_watcher (Paned* pane)
222 /* if the handle of a pane vanishes into (at least) the tabs of a notebook,
223 it is no longer accessible. so stop that. this doesn't happen on X11,
224 just the quartz backend.
229 int max_width_of_lhs = GTK_WIDGET(pane->gobj())->allocation.width - 25;
231 gint pos = pane->get_position ();
233 if (pos > max_width_of_lhs) {
234 pane->set_position (max_width_of_lhs);
240 : _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
242 /* time display buttons */
243 , minsec_label (_("Mins:Secs"))
244 , bbt_label (_("Bars:Beats"))
245 , timecode_label (_("Timecode"))
246 , samples_label (_("Samples"))
247 , tempo_label (_("Tempo"))
248 , meter_label (_("Meter"))
249 , mark_label (_("Location Markers"))
250 , range_mark_label (_("Range Markers"))
251 , transport_mark_label (_("Loop/Punch Ranges"))
252 , cd_mark_label (_("CD Markers"))
253 , edit_packer (4, 4, true)
255 /* the values here don't matter: layout widgets
256 reset them as needed.
259 , vertical_adjustment (0.0, 0.0, 10.0, 400.0)
261 /* tool bar related */
263 , zoom_range_clock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, false, true)
265 , toolbar_selection_clock_table (2,3)
267 , automation_mode_button (_("mode"))
268 , global_automation_button (_("automation"))
270 , _toolbar_viewport (*manage (new Gtk::Adjustment (0, 0, 1e10)), *manage (new Gtk::Adjustment (0, 0, 1e10)))
271 , midi_panic_button (_("Panic"))
274 , image_socket_listener(0)
279 , nudge_clock (X_("nudge"), false, X_("NudgeClock"), true, false, true)
280 , meters_running(false)
281 , _pending_locate_request (false)
282 , _pending_initial_locate (false)
283 , _last_cut_copy_source_track (0)
285 , _region_selection_change_updates_region_list (true)
289 /* we are a singleton */
291 PublicEditor::_instance = this;
295 selection = new Selection (this);
296 cut_buffer = new Selection (this);
298 clicked_regionview = 0;
299 clicked_axisview = 0;
300 clicked_routeview = 0;
301 clicked_crossfadeview = 0;
302 clicked_control_point = 0;
303 last_update_frame = 0;
304 pre_press_cursor = 0;
305 _drags = new DragManager (this);
306 current_mixer_strip = 0;
307 current_bbt_points = 0;
310 snap_type_strings = I18N (_snap_type_strings);
311 snap_mode_strings = I18N (_snap_mode_strings);
312 zoom_focus_strings = I18N (_zoom_focus_strings);
313 edit_point_strings = I18N (_edit_point_strings);
314 #ifdef USE_RUBBERBAND
315 rb_opt_strings = I18N (_rb_opt_strings);
319 snap_threshold = 5.0;
320 bbt_beat_subdivision = 4;
323 last_autoscroll_x = 0;
324 last_autoscroll_y = 0;
325 autoscroll_active = false;
326 autoscroll_timeout_tag = -1;
331 current_interthread_info = 0;
332 _show_measures = true;
333 show_gain_after_trim = false;
334 verbose_cursor_on = true;
335 last_item_entered = 0;
337 have_pending_keyboard_selection = false;
338 _follow_playhead = true;
339 _stationary_playhead = false;
340 _xfade_visibility = true;
341 editor_ruler_menu = 0;
342 no_ruler_shown_update = false;
344 range_marker_menu = 0;
345 marker_menu_item = 0;
346 tempo_or_meter_marker_menu = 0;
347 transport_marker_menu = 0;
348 new_transport_marker_menu = 0;
349 editor_mixer_strip_width = Wide;
350 show_editor_mixer_when_tracks_arrive = false;
351 region_edit_menu_split_multichannel_item = 0;
352 region_edit_menu_split_item = 0;
355 current_stepping_trackview = 0;
357 entered_regionview = 0;
359 clear_entered_track = false;
362 button_release_can_deselect = true;
363 _dragging_playhead = false;
364 _dragging_edit_point = false;
365 select_new_marker = false;
367 layering_order_editor = 0;
369 no_save_visual = false;
372 scrubbing_direction = 0;
376 location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get();
377 location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get();
378 location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get();
379 location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get();
380 location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get();
382 _edit_point = EditAtMouse;
383 _internal_editing = false;
384 current_canvas_cursor = 0;
386 frames_per_unit = 2048; /* too early to use reset_zoom () */
388 _scroll_callbacks = 0;
390 zoom_focus = ZoomFocusLeft;
391 set_zoom_focus (ZoomFocusLeft);
392 zoom_range_clock.ValueChanged.connect (sigc::mem_fun(*this, &Editor::zoom_adjustment_changed));
394 bbt_label.set_name ("EditorTimeButton");
395 bbt_label.set_size_request (-1, (int)timebar_height);
396 bbt_label.set_alignment (1.0, 0.5);
397 bbt_label.set_padding (5,0);
399 bbt_label.set_no_show_all();
400 minsec_label.set_name ("EditorTimeButton");
401 minsec_label.set_size_request (-1, (int)timebar_height);
402 minsec_label.set_alignment (1.0, 0.5);
403 minsec_label.set_padding (5,0);
404 minsec_label.hide ();
405 minsec_label.set_no_show_all();
406 timecode_label.set_name ("EditorTimeButton");
407 timecode_label.set_size_request (-1, (int)timebar_height);
408 timecode_label.set_alignment (1.0, 0.5);
409 timecode_label.set_padding (5,0);
410 timecode_label.hide ();
411 timecode_label.set_no_show_all();
412 samples_label.set_name ("EditorTimeButton");
413 samples_label.set_size_request (-1, (int)timebar_height);
414 samples_label.set_alignment (1.0, 0.5);
415 samples_label.set_padding (5,0);
416 samples_label.hide ();
417 samples_label.set_no_show_all();
419 tempo_label.set_name ("EditorTimeButton");
420 tempo_label.set_size_request (-1, (int)timebar_height);
421 tempo_label.set_alignment (1.0, 0.5);
422 tempo_label.set_padding (5,0);
424 tempo_label.set_no_show_all();
426 meter_label.set_name ("EditorTimeButton");
427 meter_label.set_size_request (-1, (int)timebar_height);
428 meter_label.set_alignment (1.0, 0.5);
429 meter_label.set_padding (5,0);
431 meter_label.set_no_show_all();
433 mark_label.set_name ("EditorTimeButton");
434 mark_label.set_size_request (-1, (int)timebar_height);
435 mark_label.set_alignment (1.0, 0.5);
436 mark_label.set_padding (5,0);
438 mark_label.set_no_show_all();
440 cd_mark_label.set_name ("EditorTimeButton");
441 cd_mark_label.set_size_request (-1, (int)timebar_height);
442 cd_mark_label.set_alignment (1.0, 0.5);
443 cd_mark_label.set_padding (5,0);
444 cd_mark_label.hide();
445 cd_mark_label.set_no_show_all();
447 range_mark_label.set_name ("EditorTimeButton");
448 range_mark_label.set_size_request (-1, (int)timebar_height);
449 range_mark_label.set_alignment (1.0, 0.5);
450 range_mark_label.set_padding (5,0);
451 range_mark_label.hide();
452 range_mark_label.set_no_show_all();
454 transport_mark_label.set_name ("EditorTimeButton");
455 transport_mark_label.set_size_request (-1, (int)timebar_height);
456 transport_mark_label.set_alignment (1.0, 0.5);
457 transport_mark_label.set_padding (5,0);
458 transport_mark_label.hide();
459 transport_mark_label.set_no_show_all();
461 initialize_rulers ();
462 initialize_canvas ();
464 _summary = new EditorSummary (this);
466 selection->TimeChanged.connect (sigc::mem_fun(*this, &Editor::time_selection_changed));
467 selection->TracksChanged.connect (sigc::mem_fun(*this, &Editor::track_selection_changed));
469 editor_regions_selection_changed_connection = selection->RegionsChanged.connect (sigc::mem_fun(*this, &Editor::region_selection_changed));
471 selection->PointsChanged.connect (sigc::mem_fun(*this, &Editor::point_selection_changed));
472 selection->MarkersChanged.connect (sigc::mem_fun(*this, &Editor::marker_selection_changed));
474 edit_controls_vbox.set_spacing (0);
475 vertical_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &Editor::tie_vertical_scrolling), true);
476 track_canvas->signal_map_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_map_handler));
478 HBox* h = manage (new HBox);
479 _group_tabs = new EditorGroupTabs (this);
480 h->pack_start (*_group_tabs, PACK_SHRINK);
481 h->pack_start (edit_controls_vbox);
482 controls_layout.add (*h);
484 controls_layout.set_name ("EditControlsBase");
485 controls_layout.add_events (Gdk::SCROLL_MASK);
486 controls_layout.signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::control_layout_scroll), false);
488 controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
489 controls_layout.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_release));
491 _cursors = new MouseCursors;
493 ArdourCanvas::Canvas* time_pad = manage(new ArdourCanvas::Canvas());
494 ArdourCanvas::SimpleLine* pad_line_1 = manage(new ArdourCanvas::SimpleLine(*time_pad->root(),
495 0.0, 1.0, 100.0, 1.0));
497 pad_line_1->property_color_rgba() = 0xFF0000FF;
502 time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2);
503 time_canvas_vbox.set_size_request (-1, -1);
505 ruler_label_event_box.add (ruler_label_vbox);
506 ruler_label_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
507 ruler_label_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
509 time_button_event_box.add (time_button_vbox);
510 time_button_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
511 time_button_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
513 /* these enable us to have a dedicated window (for cursor setting, etc.)
514 for the canvas areas.
517 track_canvas_event_box.add (*track_canvas);
519 time_canvas_event_box.add (time_canvas_vbox);
520 time_canvas_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
522 edit_packer.set_col_spacings (0);
523 edit_packer.set_row_spacings (0);
524 edit_packer.set_homogeneous (false);
525 edit_packer.set_border_width (0);
526 edit_packer.set_name ("EditorWindow");
528 /* labels for the rulers */
529 edit_packer.attach (ruler_label_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0);
530 /* labels for the marker "tracks" */
531 edit_packer.attach (time_button_event_box, 1, 2, 1, 2, FILL, SHRINK, 0, 0);
533 edit_packer.attach (time_canvas_event_box, 2, 3, 0, 1, FILL|EXPAND, FILL, 0, 0);
535 edit_packer.attach (controls_layout, 0, 2, 2, 3, FILL, FILL|EXPAND, 0, 0);
537 edit_packer.attach (track_canvas_event_box, 2, 3, 1, 3, FILL|EXPAND, FILL|EXPAND, 0, 0);
539 bottom_hbox.set_border_width (2);
540 bottom_hbox.set_spacing (3);
542 _route_groups = new EditorRouteGroups (this);
543 _routes = new EditorRoutes (this);
544 _regions = new EditorRegions (this);
545 _snapshots = new EditorSnapshots (this);
546 _locations = new EditorLocations (this);
548 add_notebook_page (_("Regions"), _regions->widget ());
549 add_notebook_page (_("Tracks & Busses"), _routes->widget ());
550 add_notebook_page (_("Snapshots"), _snapshots->widget ());
551 add_notebook_page (_("Route Groups"), _route_groups->widget ());
552 add_notebook_page (_("Ranges & Marks"), _locations->widget ());
554 _the_notebook.set_show_tabs (true);
555 _the_notebook.set_scrollable (true);
556 _the_notebook.popup_disable ();
557 _the_notebook.set_tab_pos (Gtk::POS_RIGHT);
558 _the_notebook.show_all ();
560 post_maximal_editor_width = 0;
561 post_maximal_horizontal_pane_position = 0;
562 post_maximal_editor_height = 0;
563 post_maximal_vertical_pane_position = 0;
564 _notebook_shrunk = false;
566 editor_summary_pane.pack1(edit_packer);
568 Button* summary_arrows_left_left = manage (new Button);
569 summary_arrows_left_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
570 summary_arrows_left_left->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), LEFT)));
571 summary_arrows_left_left->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
573 Button* summary_arrows_left_right = manage (new Button);
574 summary_arrows_left_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
575 summary_arrows_left_right->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), RIGHT)));
576 summary_arrows_left_right->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
578 VBox* summary_arrows_left = manage (new VBox);
579 summary_arrows_left->pack_start (*summary_arrows_left_left);
580 summary_arrows_left->pack_start (*summary_arrows_left_right);
582 Button* summary_arrows_right_up = manage (new Button);
583 summary_arrows_right_up->add (*manage (new Arrow (ARROW_UP, SHADOW_NONE)));
584 summary_arrows_right_up->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), UP)));
585 summary_arrows_right_up->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
587 Button* summary_arrows_right_down = manage (new Button);
588 summary_arrows_right_down->add (*manage (new Arrow (ARROW_DOWN, SHADOW_NONE)));
589 summary_arrows_right_down->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), DOWN)));
590 summary_arrows_right_down->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
592 VBox* summary_arrows_right = manage (new VBox);
593 summary_arrows_right->pack_start (*summary_arrows_right_up);
594 summary_arrows_right->pack_start (*summary_arrows_right_down);
596 Frame* summary_frame = manage (new Frame);
597 summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN);
599 summary_frame->add (*_summary);
600 summary_frame->show ();
602 _summary_hbox.pack_start (*summary_arrows_left, false, false);
603 _summary_hbox.pack_start (*summary_frame, true, true);
604 _summary_hbox.pack_start (*summary_arrows_right, false, false);
606 editor_summary_pane.pack2 (_summary_hbox);
608 edit_pane.pack1 (editor_summary_pane, true, true);
609 edit_pane.pack2 (_the_notebook, false, true);
611 editor_summary_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun (*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&editor_summary_pane)));
613 /* XXX: editor_summary_pane might need similar special OS X treatment to the edit_pane */
615 edit_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&edit_pane)));
617 Glib::PropertyProxy<int> proxy = edit_pane.property_position();
618 proxy.signal_changed().connect (bind (sigc::ptr_fun (pane_size_watcher), static_cast<Paned*> (&edit_pane)));
620 top_hbox.pack_start (toolbar_frame);
622 HBox *hbox = manage (new HBox);
623 hbox->pack_start (edit_pane, true, true);
625 global_vpacker.pack_start (top_hbox, false, false);
626 global_vpacker.pack_start (*hbox, true, true);
628 global_hpacker.pack_start (global_vpacker, true, true);
630 set_name ("EditorWindow");
631 add_accel_group (ActionManager::ui_manager->get_accel_group());
633 status_bar_hpacker.show ();
635 vpacker.pack_end (status_bar_hpacker, false, false);
636 vpacker.pack_end (global_hpacker, true, true);
638 /* register actions now so that set_state() can find them and set toggles/checks etc */
643 setup_midi_toolbar ();
645 _snap_type = SnapToBeat;
646 set_snap_to (_snap_type);
647 _snap_mode = SnapOff;
648 set_snap_mode (_snap_mode);
649 set_mouse_mode (MouseObject, true);
650 pre_internal_mouse_mode = MouseObject;
651 set_edit_point_preference (EditAtMouse, true);
653 _playlist_selector = new PlaylistSelector();
654 _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
656 RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), ui_bind (&Editor::catch_vanishing_regionview, this, _1), gui_context());
660 nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
661 nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
663 nudge_forward_button.set_name ("TransportButton");
664 nudge_backward_button.set_name ("TransportButton");
666 fade_context_menu.set_name ("ArdourContextMenu");
668 /* icons, titles, WM stuff */
670 list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
671 Glib::RefPtr<Gdk::Pixbuf> icon;
673 if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
674 window_icons.push_back (icon);
676 if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
677 window_icons.push_back (icon);
679 if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
680 window_icons.push_back (icon);
682 if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
683 window_icons.push_back (icon);
685 if (!window_icons.empty()) {
686 // set_icon_list (window_icons);
687 set_default_icon_list (window_icons);
690 WindowTitle title(Glib::get_application_name());
691 title += _("Editor");
692 set_title (title.get_string());
693 set_wmclass (X_("ardour_editor"), PROGRAM_NAME);
696 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
698 signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
699 signal_delete_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
701 /* allow external control surfaces/protocols to do various things */
703 ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context());
704 ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context());
705 ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context());
706 ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), ui_bind (&Editor::control_scroll, this, _1), gui_context());
707 BasicUI::AccessAction.connect (*this, invalidator (*this), ui_bind (&Editor::access_action, this, _1, _2), gui_context());
709 /* problematic: has to return a value and thus cannot be x-thread */
711 Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
713 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
715 TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
717 _ignore_region_action = false;
718 _last_region_menu_was_main = false;
719 _popup_region_menu_item = 0;
721 _show_marker_lines = false;
722 _over_region_trim_target = false;
724 /* Button bindings */
726 button_bindings = new Bindings;
728 XMLNode* node = button_settings();
730 for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) {
731 button_bindings->load (**i);
738 setup_fade_images ();
744 if(image_socket_listener) {
745 if(image_socket_listener->is_connected())
747 image_socket_listener->close_connection() ;
750 delete image_socket_listener ;
751 image_socket_listener = 0 ;
755 delete button_bindings;
757 delete _route_groups;
763 Editor::button_settings () const
765 XMLNode* settings = ARDOUR_UI::instance()->editor_settings();
766 XMLNode* node = find_named_node (*settings, X_("Buttons"));
769 cerr << "new empty Button node\n";
770 node = new XMLNode (X_("Buttons"));
777 Editor::add_toplevel_controls (Container& cont)
779 vpacker.pack_start (cont, false, false);
784 Editor::catch_vanishing_regionview (RegionView *rv)
786 /* note: the selection will take care of the vanishing
787 audioregionview by itself.
790 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
794 if (clicked_regionview == rv) {
795 clicked_regionview = 0;
798 if (entered_regionview == rv) {
799 set_entered_regionview (0);
802 if (!_all_region_actions_sensitized) {
803 sensitize_all_region_actions (true);
806 _over_region_trim_target = false;
810 Editor::set_entered_regionview (RegionView* rv)
812 if (rv == entered_regionview) {
816 if (entered_regionview) {
817 entered_regionview->exited ();
820 if ((entered_regionview = rv) != 0) {
821 entered_regionview->entered (internal_editing ());
824 if (!_all_region_actions_sensitized && _last_region_menu_was_main) {
825 /* This RegionView entry might have changed what region actions
826 are allowed, so sensitize them all in case a key is pressed.
828 sensitize_all_region_actions (true);
833 Editor::set_entered_track (TimeAxisView* tav)
836 entered_track->exited ();
839 if ((entered_track = tav) != 0) {
840 entered_track->entered ();
845 Editor::show_window ()
847 if (!is_visible ()) {
850 /* XXX: this is a bit unfortunate; it would probably
851 be nicer if we could just call show () above rather
852 than needing the show_all ()
855 /* re-hide stuff if necessary */
856 editor_list_button_toggled ();
857 parameter_changed ("show-summary");
858 parameter_changed ("show-group-tabs");
859 parameter_changed ("show-zoom-tools");
861 /* now reset all audio_time_axis heights, because widgets might need
867 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
868 tv = (static_cast<TimeAxisView*>(*i));
872 if (current_mixer_strip) {
873 current_mixer_strip->hide_things ();
881 Editor::instant_save ()
883 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
888 _session->add_instant_xml(get_state());
890 Config->add_instant_xml(get_state());
895 Editor::zoom_adjustment_changed ()
901 double fpu = zoom_range_clock.current_duration() / _canvas_width;
905 zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width));
906 } else if (fpu > _session->current_end_frame() / _canvas_width) {
907 fpu = _session->current_end_frame() / _canvas_width;
908 zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width));
915 Editor::control_scroll (float fraction)
917 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
923 double step = fraction * current_page_frames();
926 _control_scroll_target is an optional<T>
928 it acts like a pointer to an framepos_t, with
929 a operator conversion to boolean to check
930 that it has a value could possibly use
931 playhead_cursor->current_frame to store the
932 value and a boolean in the class to know
933 when it's out of date
936 if (!_control_scroll_target) {
937 _control_scroll_target = _session->transport_frame();
938 _dragging_playhead = true;
941 if ((fraction < 0.0f) && (*_control_scroll_target < (framepos_t) fabs(step))) {
942 *_control_scroll_target = 0;
943 } else if ((fraction > 0.0f) && (max_framepos - *_control_scroll_target < step)) {
944 *_control_scroll_target = max_framepos - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
946 *_control_scroll_target += (framepos_t) floor (step);
949 /* move visuals, we'll catch up with it later */
951 playhead_cursor->set_position (*_control_scroll_target);
952 UpdateAllTransportClocks (*_control_scroll_target);
954 if (*_control_scroll_target > (current_page_frames() / 2)) {
955 /* try to center PH in window */
956 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
962 Now we do a timeout to actually bring the session to the right place
963 according to the playhead. This is to avoid reading disk buffers on every
964 call to control_scroll, which is driven by ScrollTimeline and therefore
965 probably by a control surface wheel which can generate lots of events.
967 /* cancel the existing timeout */
969 control_scroll_connection.disconnect ();
971 /* add the next timeout */
973 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
977 Editor::deferred_control_scroll (framepos_t /*target*/)
979 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
980 // reset for next stream
981 _control_scroll_target = boost::none;
982 _dragging_playhead = false;
987 Editor::access_action (std::string action_group, std::string action_item)
993 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
996 act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
1004 Editor::on_realize ()
1006 Window::on_realize ();
1011 Editor::map_position_change (framepos_t frame)
1013 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, frame)
1015 if (_session == 0) {
1019 if (_follow_playhead) {
1020 center_screen (frame);
1023 playhead_cursor->set_position (frame);
1027 Editor::center_screen (framepos_t frame)
1029 double page = _canvas_width * frames_per_unit;
1031 /* if we're off the page, then scroll.
1034 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
1035 center_screen_internal (frame, page);
1040 Editor::center_screen_internal (framepos_t frame, float page)
1045 frame -= (framepos_t) page;
1050 reset_x_origin (frame);
1055 Editor::update_title ()
1057 ENSURE_GUI_THREAD (*this, &Editor::update_title)
1060 bool dirty = _session->dirty();
1062 string session_name;
1064 if (_session->snap_name() != _session->name()) {
1065 session_name = _session->snap_name();
1067 session_name = _session->name();
1071 session_name = "*" + session_name;
1074 WindowTitle title(session_name);
1075 title += Glib::get_application_name();
1076 set_title (title.get_string());
1081 Editor::set_session (Session *t)
1083 SessionHandlePtr::set_session (t);
1089 zoom_range_clock.set_session (_session);
1090 _playlist_selector->set_session (_session);
1091 nudge_clock.set_session (_session);
1092 _summary->set_session (_session);
1093 _group_tabs->set_session (_session);
1094 _route_groups->set_session (_session);
1095 _regions->set_session (_session);
1096 _snapshots->set_session (_session);
1097 _routes->set_session (_session);
1098 _locations->set_session (_session);
1100 if (rhythm_ferret) {
1101 rhythm_ferret->set_session (_session);
1104 if (analysis_window) {
1105 analysis_window->set_session (_session);
1109 sfbrowser->set_session (_session);
1112 compute_fixed_ruler_scale ();
1114 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1115 set_state (*node, Stateful::loading_state_version);
1117 /* catch up with the playhead */
1119 _session->request_locate (playhead_cursor->current_frame);
1120 _pending_initial_locate = true;
1124 /* These signals can all be emitted by a non-GUI thread. Therefore the
1125 handlers for them must not attempt to directly interact with the GUI,
1126 but use Gtkmm2ext::UI::instance()->call_slot();
1129 _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), ui_bind(&Editor::step_edit_status_change, this, _1), gui_context());
1130 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1131 _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context());
1132 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context());
1133 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1134 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context());
1135 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1136 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
1137 _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Editor::session_state_saved, this, _1), gui_context());
1138 _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context());
1139 _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&Editor::location_gone, this, _1), gui_context());
1140 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1141 _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display_s, this, _1), gui_context());
1142 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1144 if (Profile->get_sae()) {
1145 Timecode::BBT_Time bbt;
1149 framepos_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1);
1150 nudge_clock.set_mode(AudioClock::BBT);
1151 nudge_clock.set (pos, true, 0, AudioClock::BBT);
1154 nudge_clock.set (_session->frame_rate() * 5, true, 0, AudioClock::Timecode); // default of 5 seconds
1157 playhead_cursor->canvas_item.show ();
1159 Location* loc = _session->locations()->auto_loop_location();
1161 loc = new Location (*_session, 0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1163 if (loc->start() == loc->end()) {
1164 loc->set_end (loc->start() + 1);
1167 _session->locations()->add (loc, false);
1168 _session->set_auto_loop_location (loc);
1171 loc->set_name (_("Loop"));
1174 loc = _session->locations()->auto_punch_location();
1177 loc = new Location (*_session, 0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1179 if (loc->start() == loc->end()) {
1180 loc->set_end (loc->start() + 1);
1183 _session->locations()->add (loc, false);
1184 _session->set_auto_punch_location (loc);
1187 loc->set_name (_("Punch"));
1190 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1191 Config->map_parameters (pc);
1192 _session->config.map_parameters (pc);
1194 refresh_location_display ();
1196 restore_ruler_visibility ();
1197 //tempo_map_changed (PropertyChange (0));
1198 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1200 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1201 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1204 super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
1205 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1208 switch (_snap_type) {
1209 case SnapToRegionStart:
1210 case SnapToRegionEnd:
1211 case SnapToRegionSync:
1212 case SnapToRegionBoundary:
1213 build_region_boundary_cache ();
1220 /* register for undo history */
1221 _session->register_with_memento_command_factory(_id, this);
1223 ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated));
1225 start_updating_meters ();
1229 Editor::action_pre_activated (Glib::RefPtr<Action> const & a)
1231 if (a->get_name() == "RegionMenu") {
1232 /* When the main menu's region menu is opened, we setup the actions so that they look right
1233 in the menu. I can't find a way of getting a signal when this menu is subsequently closed,
1234 so we resensitize all region actions when the entered regionview or the region selection
1235 changes. HOWEVER we can't always resensitize on entered_regionview change because that
1236 happens after the region context menu is opened. So we set a flag here, too.
1240 sensitize_the_right_region_actions ();
1241 _last_region_menu_was_main = true;
1245 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1247 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1249 using namespace Menu_Helpers;
1250 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1253 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1257 MenuList& items (fade_context_menu.items());
1261 switch (item_type) {
1263 case FadeInHandleItem:
1264 if (arv->audio_region()->fade_in_active()) {
1265 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1267 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1270 items.push_back (SeparatorElem());
1272 if (Profile->get_sae()) {
1274 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)));
1275 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)));
1282 *_fade_in_images[FadeLinear],
1283 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)
1287 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1292 *_fade_in_images[FadeFast],
1293 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)
1296 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1301 *_fade_in_images[FadeLogB],
1302 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogB)
1305 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1310 *_fade_in_images[FadeLogA],
1311 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogA)
1314 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1319 *_fade_in_images[FadeSlow],
1320 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow)
1323 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1329 case FadeOutHandleItem:
1330 if (arv->audio_region()->fade_out_active()) {
1331 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1333 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1336 items.push_back (SeparatorElem());
1338 if (Profile->get_sae()) {
1339 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)));
1340 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)));
1346 *_fade_out_images[FadeLinear],
1347 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)
1351 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1356 *_fade_out_images[FadeFast],
1357 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)
1360 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1365 *_fade_out_images[FadeLogB],
1366 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogA)
1369 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1374 *_fade_out_images[FadeLogA],
1375 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogB)
1378 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1383 *_fade_out_images[FadeSlow],
1384 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast)
1387 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1393 fatal << _("programming error: ")
1394 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1399 fade_context_menu.popup (button, time);
1403 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1405 using namespace Menu_Helpers;
1406 Menu* (Editor::*build_menu_function)();
1409 switch (item_type) {
1411 case RegionViewName:
1412 case RegionViewNameHighlight:
1413 case LeftFrameHandle:
1414 case RightFrameHandle:
1415 if (with_selection) {
1416 build_menu_function = &Editor::build_track_selection_context_menu;
1418 build_menu_function = &Editor::build_track_region_context_menu;
1423 if (with_selection) {
1424 build_menu_function = &Editor::build_track_selection_context_menu;
1426 build_menu_function = &Editor::build_track_context_menu;
1430 case CrossfadeViewItem:
1431 build_menu_function = &Editor::build_track_crossfade_context_menu;
1435 if (clicked_routeview->track()) {
1436 build_menu_function = &Editor::build_track_context_menu;
1438 build_menu_function = &Editor::build_track_bus_context_menu;
1443 /* probably shouldn't happen but if it does, we don't care */
1447 menu = (this->*build_menu_function)();
1448 menu->set_name ("ArdourContextMenu");
1450 /* now handle specific situations */
1452 switch (item_type) {
1454 case RegionViewName:
1455 case RegionViewNameHighlight:
1456 case LeftFrameHandle:
1457 case RightFrameHandle:
1458 if (!with_selection) {
1459 if (region_edit_menu_split_item) {
1460 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1461 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1463 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1466 if (region_edit_menu_split_multichannel_item) {
1467 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1468 region_edit_menu_split_multichannel_item->set_sensitive (true);
1470 region_edit_menu_split_multichannel_item->set_sensitive (false);
1479 case CrossfadeViewItem:
1486 /* probably shouldn't happen but if it does, we don't care */
1490 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1492 /* Bounce to disk */
1494 using namespace Menu_Helpers;
1495 MenuList& edit_items = menu->items();
1497 edit_items.push_back (SeparatorElem());
1499 switch (clicked_routeview->audio_track()->freeze_state()) {
1500 case AudioTrack::NoFreeze:
1501 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1504 case AudioTrack::Frozen:
1505 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1508 case AudioTrack::UnFrozen:
1509 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1515 if (item_type == StreamItem && clicked_routeview) {
1516 clicked_routeview->build_underlay_menu(menu);
1519 /* When the region menu is opened, we setup the actions so that they look right
1522 sensitize_the_right_region_actions ();
1523 _last_region_menu_was_main = false;
1525 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1526 menu->popup (button, time);
1530 Editor::build_track_context_menu ()
1532 using namespace Menu_Helpers;
1534 MenuList& edit_items = track_context_menu.items();
1537 add_dstream_context_items (edit_items);
1538 return &track_context_menu;
1542 Editor::build_track_bus_context_menu ()
1544 using namespace Menu_Helpers;
1546 MenuList& edit_items = track_context_menu.items();
1549 add_bus_context_items (edit_items);
1550 return &track_context_menu;
1554 Editor::build_track_region_context_menu ()
1556 using namespace Menu_Helpers;
1557 MenuList& edit_items = track_region_context_menu.items();
1560 /* we've just cleared the track region context menu, so the menu that these
1561 two items were on will have disappeared; stop them dangling.
1563 region_edit_menu_split_item = 0;
1564 region_edit_menu_split_multichannel_item = 0;
1566 /* we might try to use items that are currently attached to a crossfade menu,
1569 track_crossfade_context_menu.items().clear ();
1571 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1574 boost::shared_ptr<Track> tr;
1575 boost::shared_ptr<Playlist> pl;
1577 if ((tr = rtv->track())) {
1578 add_region_context_items (edit_items, tr);
1582 add_dstream_context_items (edit_items);
1584 return &track_region_context_menu;
1588 Editor::build_track_crossfade_context_menu ()
1590 using namespace Menu_Helpers;
1591 MenuList& edit_items = track_crossfade_context_menu.items();
1592 edit_items.clear ();
1594 /* we might try to use items that are currently attached to a crossfade menu,
1597 track_region_context_menu.items().clear ();
1599 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1602 boost::shared_ptr<Track> tr;
1603 boost::shared_ptr<Playlist> pl;
1604 boost::shared_ptr<AudioPlaylist> apl;
1606 if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1608 AudioPlaylist::Crossfades xfades;
1612 /* The xfade menu is a bit of a special case, as we always use the mouse position
1613 to decide whether or not to display it (rather than the edit point). No particularly
1614 strong reasons for this, other than it is a bit surprising to right-click on a xfade
1617 mouse_frame (where, ignored);
1618 apl->crossfades_at (where, xfades);
1620 bool const many = xfades.size() > 1;
1622 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1623 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1626 add_region_context_items (edit_items, tr);
1630 add_dstream_context_items (edit_items);
1632 return &track_crossfade_context_menu;
1636 Editor::analyze_region_selection ()
1638 if (analysis_window == 0) {
1639 analysis_window = new AnalysisWindow();
1642 analysis_window->set_session(_session);
1644 analysis_window->show_all();
1647 analysis_window->set_regionmode();
1648 analysis_window->analyze();
1650 analysis_window->present();
1654 Editor::analyze_range_selection()
1656 if (analysis_window == 0) {
1657 analysis_window = new AnalysisWindow();
1660 analysis_window->set_session(_session);
1662 analysis_window->show_all();
1665 analysis_window->set_rangemode();
1666 analysis_window->analyze();
1668 analysis_window->present();
1672 Editor::build_track_selection_context_menu ()
1674 using namespace Menu_Helpers;
1675 MenuList& edit_items = track_selection_context_menu.items();
1676 edit_items.clear ();
1678 add_selection_context_items (edit_items);
1679 // edit_items.push_back (SeparatorElem());
1680 // add_dstream_context_items (edit_items);
1682 return &track_selection_context_menu;
1685 /** Add context menu items relevant to crossfades.
1686 * @param edit_items List to add the items to.
1689 Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1691 using namespace Menu_Helpers;
1692 Menu *xfade_menu = manage (new Menu);
1693 MenuList& items = xfade_menu->items();
1694 xfade_menu->set_name ("ArdourContextMenu");
1697 if (xfade->active()) {
1703 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1704 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1706 if (xfade->can_follow_overlap()) {
1708 if (xfade->following_overlap()) {
1709 str = _("Convert to Short");
1711 str = _("Convert to Full");
1714 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1718 str = xfade->out()->name();
1720 str += xfade->in()->name();
1722 str = _("Crossfade");
1725 edit_items.push_back (MenuElem (str, *xfade_menu));
1726 edit_items.push_back (SeparatorElem());
1730 Editor::xfade_edit_left_region ()
1732 if (clicked_crossfadeview) {
1733 clicked_crossfadeview->left_view.show_region_editor ();
1738 Editor::xfade_edit_right_region ()
1740 if (clicked_crossfadeview) {
1741 clicked_crossfadeview->right_view.show_region_editor ();
1746 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::shared_ptr<Track> track)
1748 using namespace Menu_Helpers;
1750 /* OK, stick the region submenu at the top of the list, and then add
1754 RegionSelection rs = get_regions_from_selection_and_entered ();
1756 string::size_type pos = 0;
1757 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1759 /* we have to hack up the region name because "_" has a special
1760 meaning for menu titles.
1763 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1764 menu_item_name.replace (pos, 1, "__");
1768 if (_popup_region_menu_item == 0) {
1769 _popup_region_menu_item = new MenuItem (menu_item_name);
1770 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1771 _popup_region_menu_item->show ();
1773 _popup_region_menu_item->set_label (menu_item_name);
1776 /* Use the mouse position rather than the edit point to decide whether to show the `choose top region'
1777 dialogue. If we use the edit point it gets a bit messy because the user still has to click over
1778 *some* region in order to get the region context menu stuff to be displayed at all.
1783 mouse_frame (mouse, ignored);
1785 edit_items.push_back (*_popup_region_menu_item);
1786 if (track->playlist()->count_regions_at (mouse) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1787 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region")->create_menu_item ()));
1789 edit_items.push_back (SeparatorElem());
1792 /** Add context menu items relevant to selection ranges.
1793 * @param edit_items List to add the items to.
1796 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1798 using namespace Menu_Helpers;
1800 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1801 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1803 edit_items.push_back (SeparatorElem());
1804 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1806 edit_items.push_back (SeparatorElem());
1808 edit_items.push_back (
1810 _("Move Range Start to Previous Region Boundary"),
1811 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, false)
1815 edit_items.push_back (
1817 _("Move Range Start to Next Region Boundary"),
1818 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, true)
1822 edit_items.push_back (
1824 _("Move Range End to Previous Region Boundary"),
1825 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, false)
1829 edit_items.push_back (
1831 _("Move Range End to Next Region Boundary"),
1832 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, true)
1836 edit_items.push_back (SeparatorElem());
1837 edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection)));
1838 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1840 edit_items.push_back (SeparatorElem());
1841 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1843 edit_items.push_back (SeparatorElem());
1844 edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1845 edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1847 edit_items.push_back (SeparatorElem());
1848 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1850 edit_items.push_back (SeparatorElem());
1851 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1852 edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1853 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1855 edit_items.push_back (SeparatorElem());
1856 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1857 edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1858 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1859 edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1860 edit_items.push_back (MenuElem (_("Export Range"), sigc::mem_fun(*this, &Editor::export_selection)));
1865 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1867 using namespace Menu_Helpers;
1871 Menu *play_menu = manage (new Menu);
1872 MenuList& play_items = play_menu->items();
1873 play_menu->set_name ("ArdourContextMenu");
1875 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1876 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1877 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1878 play_items.push_back (SeparatorElem());
1879 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
1881 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1885 Menu *select_menu = manage (new Menu);
1886 MenuList& select_items = select_menu->items();
1887 select_menu->set_name ("ArdourContextMenu");
1889 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1890 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1891 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1892 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1893 select_items.push_back (SeparatorElem());
1894 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
1895 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
1896 select_items.push_back (SeparatorElem());
1897 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1898 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1899 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1900 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1901 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
1902 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
1903 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
1905 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1909 Menu *cutnpaste_menu = manage (new Menu);
1910 MenuList& cutnpaste_items = cutnpaste_menu->items();
1911 cutnpaste_menu->set_name ("ArdourContextMenu");
1913 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1914 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1915 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1917 cutnpaste_items.push_back (SeparatorElem());
1919 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
1920 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
1922 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
1924 /* Adding new material */
1926 edit_items.push_back (SeparatorElem());
1927 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
1928 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
1932 Menu *nudge_menu = manage (new Menu());
1933 MenuList& nudge_items = nudge_menu->items();
1934 nudge_menu->set_name ("ArdourContextMenu");
1936 edit_items.push_back (SeparatorElem());
1937 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1938 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1939 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1940 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1942 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1946 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
1948 using namespace Menu_Helpers;
1952 Menu *play_menu = manage (new Menu);
1953 MenuList& play_items = play_menu->items();
1954 play_menu->set_name ("ArdourContextMenu");
1956 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1957 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1958 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1962 Menu *select_menu = manage (new Menu);
1963 MenuList& select_items = select_menu->items();
1964 select_menu->set_name ("ArdourContextMenu");
1966 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1967 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1968 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1969 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1970 select_items.push_back (SeparatorElem());
1971 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1972 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1973 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1974 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1976 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1980 Menu *cutnpaste_menu = manage (new Menu);
1981 MenuList& cutnpaste_items = cutnpaste_menu->items();
1982 cutnpaste_menu->set_name ("ArdourContextMenu");
1984 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1985 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1986 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1988 Menu *nudge_menu = manage (new Menu());
1989 MenuList& nudge_items = nudge_menu->items();
1990 nudge_menu->set_name ("ArdourContextMenu");
1992 edit_items.push_back (SeparatorElem());
1993 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1994 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1995 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1996 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1998 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2002 Editor::snap_type() const
2008 Editor::snap_mode() const
2014 Editor::set_snap_to (SnapType st)
2016 unsigned int snap_ind = (unsigned int)st;
2020 if (snap_ind > snap_type_strings.size() - 1) {
2022 _snap_type = (SnapType)snap_ind;
2025 string str = snap_type_strings[snap_ind];
2027 if (str != snap_type_selector.get_active_text()) {
2028 snap_type_selector.set_active_text (str);
2033 switch (_snap_type) {
2034 case SnapToBeatDiv32:
2035 case SnapToBeatDiv28:
2036 case SnapToBeatDiv24:
2037 case SnapToBeatDiv20:
2038 case SnapToBeatDiv16:
2039 case SnapToBeatDiv14:
2040 case SnapToBeatDiv12:
2041 case SnapToBeatDiv10:
2042 case SnapToBeatDiv8:
2043 case SnapToBeatDiv7:
2044 case SnapToBeatDiv6:
2045 case SnapToBeatDiv5:
2046 case SnapToBeatDiv4:
2047 case SnapToBeatDiv3:
2048 case SnapToBeatDiv2:
2049 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
2050 update_tempo_based_rulers ();
2053 case SnapToRegionStart:
2054 case SnapToRegionEnd:
2055 case SnapToRegionSync:
2056 case SnapToRegionBoundary:
2057 build_region_boundary_cache ();
2065 SnapChanged (); /* EMIT SIGNAL */
2069 Editor::set_snap_mode (SnapMode mode)
2072 string str = snap_mode_strings[(int)mode];
2074 if (str != snap_mode_selector.get_active_text ()) {
2075 snap_mode_selector.set_active_text (str);
2081 Editor::set_edit_point_preference (EditPoint ep, bool force)
2083 bool changed = (_edit_point != ep);
2086 string str = edit_point_strings[(int)ep];
2088 if (str != edit_point_selector.get_active_text ()) {
2089 edit_point_selector.set_active_text (str);
2092 set_canvas_cursor ();
2094 if (!force && !changed) {
2098 const char* action=NULL;
2100 switch (_edit_point) {
2101 case EditAtPlayhead:
2102 action = "edit-at-playhead";
2104 case EditAtSelectedMarker:
2105 action = "edit-at-marker";
2108 action = "edit-at-mouse";
2112 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2114 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2118 bool in_track_canvas;
2120 if (!mouse_frame (foo, in_track_canvas)) {
2121 in_track_canvas = false;
2124 reset_canvas_action_sensitivity (in_track_canvas);
2130 Editor::set_state (const XMLNode& node, int /*version*/)
2132 const XMLProperty* prop;
2134 int x, y, xoff, yoff;
2137 if ((prop = node.property ("id")) != 0) {
2138 _id = prop->value ();
2141 g.base_width = default_width;
2142 g.base_height = default_height;
2148 if ((geometry = find_named_node (node, "geometry")) != 0) {
2152 if ((prop = geometry->property("x_size")) == 0) {
2153 prop = geometry->property ("x-size");
2156 g.base_width = atoi(prop->value());
2158 if ((prop = geometry->property("y_size")) == 0) {
2159 prop = geometry->property ("y-size");
2162 g.base_height = atoi(prop->value());
2165 if ((prop = geometry->property ("x_pos")) == 0) {
2166 prop = geometry->property ("x-pos");
2169 x = atoi (prop->value());
2172 if ((prop = geometry->property ("y_pos")) == 0) {
2173 prop = geometry->property ("y-pos");
2176 y = atoi (prop->value());
2179 if ((prop = geometry->property ("x_off")) == 0) {
2180 prop = geometry->property ("x-off");
2183 xoff = atoi (prop->value());
2185 if ((prop = geometry->property ("y_off")) == 0) {
2186 prop = geometry->property ("y-off");
2189 yoff = atoi (prop->value());
2193 set_default_size (g.base_width, g.base_height);
2196 if (_session && (prop = node.property ("playhead"))) {
2198 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2199 playhead_cursor->set_position (pos);
2201 playhead_cursor->set_position (0);
2204 if ((prop = node.property ("mixer-width"))) {
2205 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2208 if ((prop = node.property ("zoom-focus"))) {
2209 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2212 if ((prop = node.property ("zoom"))) {
2213 reset_zoom (PBD::atof (prop->value()));
2215 reset_zoom (frames_per_unit);
2218 if ((prop = node.property ("snap-to"))) {
2219 set_snap_to ((SnapType) atoi (prop->value()));
2222 if ((prop = node.property ("snap-mode"))) {
2223 set_snap_mode ((SnapMode) atoi (prop->value()));
2226 if ((prop = node.property ("mouse-mode"))) {
2227 MouseMode m = str2mousemode(prop->value());
2228 set_mouse_mode (m, true);
2230 set_mouse_mode (MouseObject, true);
2233 if ((prop = node.property ("left-frame")) != 0) {
2235 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2236 reset_x_origin (pos);
2240 if ((prop = node.property ("y-origin")) != 0) {
2241 reset_y_origin (atof (prop->value ()));
2244 if ((prop = node.property ("internal-edit"))) {
2245 bool yn = string_is_affirmative (prop->value());
2246 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2248 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2249 tact->set_active (!yn);
2250 tact->set_active (yn);
2254 if ((prop = node.property ("join-object-range"))) {
2255 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2258 if ((prop = node.property ("edit-point"))) {
2259 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2262 if ((prop = node.property ("show-measures"))) {
2263 bool yn = string_is_affirmative (prop->value());
2264 _show_measures = yn;
2265 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2267 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2268 /* do it twice to force the change */
2269 tact->set_active (!yn);
2270 tact->set_active (yn);
2274 if ((prop = node.property ("follow-playhead"))) {
2275 bool yn = string_is_affirmative (prop->value());
2276 set_follow_playhead (yn);
2277 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2279 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2280 if (tact->get_active() != yn) {
2281 tact->set_active (yn);
2286 if ((prop = node.property ("stationary-playhead"))) {
2287 bool yn = (prop->value() == "yes");
2288 set_stationary_playhead (yn);
2289 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2291 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2292 if (tact->get_active() != yn) {
2293 tact->set_active (yn);
2298 if ((prop = node.property ("region-list-sort-type"))) {
2299 RegionListSortType st;
2300 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2303 if ((prop = node.property ("xfades-visible"))) {
2304 bool yn = string_is_affirmative (prop->value());
2305 _xfade_visibility = !yn;
2306 // set_xfade_visibility (yn);
2309 if ((prop = node.property ("show-editor-mixer"))) {
2311 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2314 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2315 bool yn = string_is_affirmative (prop->value());
2317 /* do it twice to force the change */
2319 tact->set_active (!yn);
2320 tact->set_active (yn);
2323 if ((prop = node.property ("show-editor-list"))) {
2325 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2328 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2329 bool yn = string_is_affirmative (prop->value());
2331 /* do it twice to force the change */
2333 tact->set_active (!yn);
2334 tact->set_active (yn);
2337 if ((prop = node.property (X_("editor-list-page")))) {
2338 _the_notebook.set_current_page (atoi (prop->value ()));
2341 if ((prop = node.property (X_("show-marker-lines")))) {
2342 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2344 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2345 bool yn = string_is_affirmative (prop->value ());
2347 tact->set_active (!yn);
2348 tact->set_active (yn);
2351 XMLNodeList children = node.children ();
2352 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2353 selection->set_state (**i, Stateful::current_state_version);
2354 _regions->set_state (**i);
2361 Editor::get_state ()
2363 XMLNode* node = new XMLNode ("Editor");
2366 _id.print (buf, sizeof (buf));
2367 node->add_property ("id", buf);
2369 if (is_realized()) {
2370 Glib::RefPtr<Gdk::Window> win = get_window();
2372 int x, y, xoff, yoff, width, height;
2373 win->get_root_origin(x, y);
2374 win->get_position(xoff, yoff);
2375 win->get_size(width, height);
2377 XMLNode* geometry = new XMLNode ("geometry");
2379 snprintf(buf, sizeof(buf), "%d", width);
2380 geometry->add_property("x-size", string(buf));
2381 snprintf(buf, sizeof(buf), "%d", height);
2382 geometry->add_property("y-size", string(buf));
2383 snprintf(buf, sizeof(buf), "%d", x);
2384 geometry->add_property("x-pos", string(buf));
2385 snprintf(buf, sizeof(buf), "%d", y);
2386 geometry->add_property("y-pos", string(buf));
2387 snprintf(buf, sizeof(buf), "%d", xoff);
2388 geometry->add_property("x-off", string(buf));
2389 snprintf(buf, sizeof(buf), "%d", yoff);
2390 geometry->add_property("y-off", string(buf));
2391 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2392 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2393 geometry->add_property("notebook-shrunk", _notebook_shrunk ? "1" : "0");
2394 snprintf(buf,sizeof(buf), "%d",pre_maximal_horizontal_pane_position);
2395 geometry->add_property("pre-maximal-horizontal-pane-position", string(buf));
2396 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2397 geometry->add_property("edit-vertical-pane-pos", string(buf));
2399 node->add_child_nocopy (*geometry);
2402 maybe_add_mixer_strip_width (*node);
2404 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2405 node->add_property ("zoom-focus", buf);
2406 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2407 node->add_property ("zoom", buf);
2408 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2409 node->add_property ("snap-to", buf);
2410 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2411 node->add_property ("snap-mode", buf);
2413 node->add_property ("edit-point", enum_2_string (_edit_point));
2415 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2416 node->add_property ("playhead", buf);
2417 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2418 node->add_property ("left-frame", buf);
2419 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2420 node->add_property ("y-origin", buf);
2422 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2423 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2424 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2425 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2426 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2427 node->add_property ("mouse-mode", enum2str(mouse_mode));
2428 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2429 node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2431 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2433 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2434 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2437 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2439 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2440 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2443 snprintf (buf, sizeof (buf), "%d", _the_notebook.get_current_page ());
2444 node->add_property (X_("editor-list-page"), buf);
2446 if (button_bindings) {
2447 XMLNode* bb = new XMLNode (X_("Buttons"));
2448 button_bindings->save (*bb);
2449 node->add_child_nocopy (*bb);
2452 node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no");
2454 node->add_child_nocopy (selection->get_state ());
2455 node->add_child_nocopy (_regions->get_state ());
2462 /** @param y y offset from the top of all trackviews.
2463 * @return pair: TimeAxisView that y is over, layer index.
2464 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2465 * in stacked region display mode, otherwise 0.
2467 std::pair<TimeAxisView *, layer_t>
2468 Editor::trackview_by_y_position (double y)
2470 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2472 std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
2478 return std::make_pair ( (TimeAxisView *) 0, 0);
2481 /** Snap a position to the grid, if appropriate, taking into account current
2482 * grid settings and also the state of any snap modifier keys that may be pressed.
2483 * @param start Position to snap.
2484 * @param event Event to get current key modifier information from, or 0.
2487 Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2489 if (!_session || !event) {
2493 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2494 if (_snap_mode == SnapOff) {
2495 snap_to_internal (start, direction, for_mark);
2498 if (_snap_mode != SnapOff) {
2499 snap_to_internal (start, direction, for_mark);
2505 Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark)
2507 if (!_session || _snap_mode == SnapOff) {
2511 snap_to_internal (start, direction, for_mark);
2515 Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /*for_mark*/)
2517 const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2518 framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2520 switch (_snap_type) {
2521 case SnapToTimecodeFrame:
2522 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2523 start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2525 start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2529 case SnapToTimecodeSeconds:
2530 if (_session->config.get_timecode_offset_negative()) {
2531 start += _session->config.get_timecode_offset ();
2533 start -= _session->config.get_timecode_offset ();
2535 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2536 start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2538 start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2541 if (_session->config.get_timecode_offset_negative()) {
2542 start -= _session->config.get_timecode_offset ();
2544 start += _session->config.get_timecode_offset ();
2548 case SnapToTimecodeMinutes:
2549 if (_session->config.get_timecode_offset_negative()) {
2550 start += _session->config.get_timecode_offset ();
2552 start -= _session->config.get_timecode_offset ();
2554 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2555 start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2557 start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2559 if (_session->config.get_timecode_offset_negative()) {
2560 start -= _session->config.get_timecode_offset ();
2562 start += _session->config.get_timecode_offset ();
2566 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2572 Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark)
2574 const framepos_t one_second = _session->frame_rate();
2575 const framepos_t one_minute = _session->frame_rate() * 60;
2576 framepos_t presnap = start;
2580 switch (_snap_type) {
2581 case SnapToTimecodeFrame:
2582 case SnapToTimecodeSeconds:
2583 case SnapToTimecodeMinutes:
2584 return timecode_snap_to_internal (start, direction, for_mark);
2587 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2588 start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2590 start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2595 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2596 start = (framepos_t) ceil ((double) start / one_second) * one_second;
2598 start = (framepos_t) floor ((double) start / one_second) * one_second;
2603 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2604 start = (framepos_t) ceil ((double) start / one_minute) * one_minute;
2606 start = (framepos_t) floor ((double) start / one_minute) * one_minute;
2611 start = _session->tempo_map().round_to_bar (start, direction);
2615 start = _session->tempo_map().round_to_beat (start, direction);
2618 case SnapToBeatDiv32:
2619 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2621 case SnapToBeatDiv28:
2622 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2624 case SnapToBeatDiv24:
2625 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2627 case SnapToBeatDiv20:
2628 start = _session->tempo_map().round_to_beat_subdivision (start, 20, direction);
2630 case SnapToBeatDiv16:
2631 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2633 case SnapToBeatDiv14:
2634 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2636 case SnapToBeatDiv12:
2637 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2639 case SnapToBeatDiv10:
2640 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2642 case SnapToBeatDiv8:
2643 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2645 case SnapToBeatDiv7:
2646 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2648 case SnapToBeatDiv6:
2649 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2651 case SnapToBeatDiv5:
2652 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2654 case SnapToBeatDiv4:
2655 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2657 case SnapToBeatDiv3:
2658 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2660 case SnapToBeatDiv2:
2661 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2669 _session->locations()->marks_either_side (start, before, after);
2671 if (before == max_framepos) {
2673 } else if (after == max_framepos) {
2675 } else if (before != max_framepos && after != max_framepos) {
2676 /* have before and after */
2677 if ((start - before) < (after - start)) {
2686 case SnapToRegionStart:
2687 case SnapToRegionEnd:
2688 case SnapToRegionSync:
2689 case SnapToRegionBoundary:
2690 if (!region_boundary_cache.empty()) {
2692 vector<framepos_t>::iterator prev = region_boundary_cache.end ();
2693 vector<framepos_t>::iterator next = region_boundary_cache.end ();
2695 if (direction > 0) {
2696 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2698 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2701 if (next != region_boundary_cache.begin ()) {
2706 framepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2707 framepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2709 if (start > (p + n) / 2) {
2718 switch (_snap_mode) {
2724 if (presnap > start) {
2725 if (presnap > (start + unit_to_frame(snap_threshold))) {
2729 } else if (presnap < start) {
2730 if (presnap < (start - unit_to_frame(snap_threshold))) {
2736 /* handled at entry */
2744 Editor::setup_toolbar ()
2746 HBox* mode_box = manage(new HBox);
2747 mode_box->set_border_width (2);
2748 mode_box->set_spacing(4);
2750 /* table containing mode buttons */
2752 HBox* mouse_mode_button_box = manage (new HBox ());
2754 if (Profile->get_sae()) {
2755 mouse_mode_button_box->pack_start (mouse_move_button);
2757 mouse_mode_button_box->pack_start (mouse_move_button);
2758 mouse_mode_button_box->pack_start (join_object_range_button);
2759 mouse_mode_button_box->pack_start (mouse_select_button);
2762 mouse_mode_button_box->pack_start (mouse_zoom_button);
2764 if (!Profile->get_sae()) {
2765 mouse_mode_button_box->pack_start (mouse_gain_button);
2768 mouse_mode_button_box->pack_start (mouse_timefx_button);
2769 mouse_mode_button_box->pack_start (mouse_audition_button);
2770 mouse_mode_button_box->pack_start (internal_edit_button);
2772 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2773 if (!Profile->get_sae()) {
2774 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2776 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2778 edit_mode_selector.set_name ("EditModeSelector");
2779 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2780 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2782 mode_box->pack_start (edit_mode_selector);
2783 mode_box->pack_start (*mouse_mode_button_box);
2785 _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2786 _mouse_mode_tearoff->set_name ("MouseModeBase");
2787 _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2789 if (Profile->get_sae()) {
2790 _mouse_mode_tearoff->set_can_be_torn_off (false);
2793 _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2794 &_mouse_mode_tearoff->tearoff_window()));
2795 _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2796 &_mouse_mode_tearoff->tearoff_window(), 1));
2797 _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2798 &_mouse_mode_tearoff->tearoff_window()));
2799 _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2800 &_mouse_mode_tearoff->tearoff_window(), 1));
2802 mouse_move_button.set_mode (false);
2803 mouse_select_button.set_mode (false);
2804 mouse_gain_button.set_mode (false);
2805 mouse_zoom_button.set_mode (false);
2806 mouse_timefx_button.set_mode (false);
2807 mouse_audition_button.set_mode (false);
2808 join_object_range_button.set_mode (false);
2810 mouse_move_button.set_name ("MouseModeButton");
2811 mouse_select_button.set_name ("MouseModeButton");
2812 mouse_gain_button.set_name ("MouseModeButton");
2813 mouse_zoom_button.set_name ("MouseModeButton");
2814 mouse_timefx_button.set_name ("MouseModeButton");
2815 mouse_audition_button.set_name ("MouseModeButton");
2816 internal_edit_button.set_name ("MouseModeButton");
2817 join_object_range_button.set_name ("MouseModeButton");
2819 mouse_move_button.unset_flags (CAN_FOCUS);
2820 mouse_select_button.unset_flags (CAN_FOCUS);
2821 mouse_gain_button.unset_flags (CAN_FOCUS);
2822 mouse_zoom_button.unset_flags (CAN_FOCUS);
2823 mouse_timefx_button.unset_flags (CAN_FOCUS);
2824 mouse_audition_button.unset_flags (CAN_FOCUS);
2825 internal_edit_button.unset_flags (CAN_FOCUS);
2826 join_object_range_button.unset_flags (CAN_FOCUS);
2830 _zoom_box.set_spacing (1);
2831 _zoom_box.set_border_width (0);
2833 zoom_in_button.set_name ("EditorTimeButton");
2834 zoom_in_button.set_image (*(manage (new Image (::get_icon ("zoom_in")))));
2835 zoom_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), false));
2837 zoom_out_button.set_name ("EditorTimeButton");
2838 zoom_out_button.set_image (*(manage (new Image (::get_icon ("zoom_out")))));
2839 zoom_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), true));
2841 zoom_out_full_button.set_name ("EditorTimeButton");
2842 zoom_out_full_button.set_image (*(manage (new Image (::get_icon ("zoom_full")))));
2843 zoom_out_full_button.signal_clicked().connect (sigc::mem_fun(*this, &Editor::temporal_zoom_session));
2845 zoom_focus_selector.set_name ("ZoomFocusSelector");
2846 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
2847 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2849 _zoom_box.pack_start (zoom_out_button, false, false);
2850 _zoom_box.pack_start (zoom_in_button, false, false);
2851 _zoom_box.pack_start (zoom_out_full_button, false, false);
2853 _zoom_box.pack_start (zoom_focus_selector);
2855 /* Track zoom buttons */
2856 tav_expand_button.set_name ("TrackHeightButton");
2857 tav_expand_button.set_size_request (-1, 20);
2858 tav_expand_button.add (*(manage (new Image (::get_icon ("tav_exp")))));
2859 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("expand-tracks"));
2860 act->connect_proxy (tav_expand_button);
2862 tav_shrink_button.set_name ("TrackHeightButton");
2863 tav_shrink_button.set_size_request (-1, 20);
2864 tav_shrink_button.add (*(manage (new Image (::get_icon ("tav_shrink")))));
2865 act = ActionManager::get_action (X_("Editor"), X_("shrink-tracks"));
2866 act->connect_proxy (tav_shrink_button);
2868 _zoom_box.pack_start (tav_shrink_button);
2869 _zoom_box.pack_start (tav_expand_button);
2871 _zoom_tearoff = manage (new TearOff (_zoom_box));
2873 _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2874 &_zoom_tearoff->tearoff_window()));
2875 _zoom_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2876 &_zoom_tearoff->tearoff_window(), 0));
2877 _zoom_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2878 &_zoom_tearoff->tearoff_window()));
2879 _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2880 &_zoom_tearoff->tearoff_window(), 0));
2882 snap_box.set_spacing (1);
2883 snap_box.set_border_width (2);
2885 snap_type_selector.set_name ("SnapTypeSelector");
2886 set_popdown_strings (snap_type_selector, snap_type_strings, true);
2887 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2889 snap_mode_selector.set_name ("SnapModeSelector");
2890 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
2891 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2893 edit_point_selector.set_name ("EditPointSelector");
2894 set_popdown_strings (edit_point_selector, edit_point_strings, true);
2895 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2897 snap_box.pack_start (snap_mode_selector, false, false);
2898 snap_box.pack_start (snap_type_selector, false, false);
2899 snap_box.pack_start (edit_point_selector, false, false);
2903 HBox *nudge_box = manage (new HBox);
2904 nudge_box->set_spacing(1);
2905 nudge_box->set_border_width (2);
2907 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2908 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2910 nudge_box->pack_start (nudge_backward_button, false, false);
2911 nudge_box->pack_start (nudge_forward_button, false, false);
2912 nudge_box->pack_start (nudge_clock, false, false);
2915 /* Pack everything in... */
2917 HBox* hbox = manage (new HBox);
2918 hbox->set_spacing(10);
2920 _tools_tearoff = manage (new TearOff (*hbox));
2921 _tools_tearoff->set_name ("MouseModeBase");
2922 _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
2924 if (Profile->get_sae()) {
2925 _tools_tearoff->set_can_be_torn_off (false);
2928 _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2929 &_tools_tearoff->tearoff_window()));
2930 _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2931 &_tools_tearoff->tearoff_window(), 0));
2932 _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2933 &_tools_tearoff->tearoff_window()));
2934 _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2935 &_tools_tearoff->tearoff_window(), 0));
2937 toolbar_hbox.set_spacing (10);
2938 toolbar_hbox.set_border_width (1);
2940 toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
2941 toolbar_hbox.pack_start (*_zoom_tearoff, false, false);
2942 toolbar_hbox.pack_start (*_tools_tearoff, false, false);
2944 hbox->pack_start (snap_box, false, false);
2945 if (!Profile->get_small_screen()) {
2946 hbox->pack_start (*nudge_box, false, false);
2948 ARDOUR_UI::instance()->editor_transport_box().pack_start (*nudge_box, false, false);
2950 hbox->pack_start (panic_box, false, false);
2954 toolbar_base.set_name ("ToolBarBase");
2955 toolbar_base.add (toolbar_hbox);
2957 _toolbar_viewport.add (toolbar_base);
2958 /* stick to the required height but allow width to vary if there's not enough room */
2959 _toolbar_viewport.set_size_request (1, -1);
2961 toolbar_frame.set_shadow_type (SHADOW_OUT);
2962 toolbar_frame.set_name ("BaseFrame");
2963 toolbar_frame.add (_toolbar_viewport);
2965 DPIReset.connect (sigc::mem_fun (*this, &Editor::resize_text_widgets));
2969 Editor::setup_tooltips ()
2971 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
2972 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain"));
2973 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
2974 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
2975 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2976 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
2977 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
2978 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
2979 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
2980 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
2981 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
2982 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
2983 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
2984 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
2985 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
2986 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
2987 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
2988 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
2989 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
2990 ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
2991 ARDOUR_UI::instance()->set_tip (midi_panic_button, _("Send note off and reset controller messages on all MIDI channels"));
2992 ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode"));
2996 Editor::midi_panic ()
2998 cerr << "MIDI panic\n";
3001 _session->midi_panic();
3006 Editor::setup_midi_toolbar ()
3010 /* Midi sound notes */
3011 midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
3012 midi_sound_notes.unset_flags (CAN_FOCUS);
3016 act = ActionManager::get_action (X_("MIDI"), X_("panic"));
3017 midi_panic_button.set_name("MidiPanicButton");
3018 act->connect_proxy (midi_panic_button);
3020 panic_box.pack_start (midi_sound_notes , true, true);
3021 panic_box.pack_start (midi_panic_button, true, true);
3025 Editor::convert_drop_to_paths (
3026 vector<string>& paths,
3027 const RefPtr<Gdk::DragContext>& /*context*/,
3030 const SelectionData& data,
3034 if (_session == 0) {
3038 vector<string> uris = data.get_uris();
3042 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3043 are actually URI lists. So do it by hand.
3046 if (data.get_target() != "text/plain") {
3050 /* Parse the "uri-list" format that Nautilus provides,
3051 where each pathname is delimited by \r\n.
3053 THERE MAY BE NO NULL TERMINATING CHAR!!!
3056 string txt = data.get_text();
3060 p = (const char *) malloc (txt.length() + 1);
3061 txt.copy ((char *) p, txt.length(), 0);
3062 ((char*)p)[txt.length()] = '\0';
3068 while (g_ascii_isspace (*p))
3072 while (*q && (*q != '\n') && (*q != '\r')) {
3079 while (q > p && g_ascii_isspace (*q))
3084 uris.push_back (string (p, q - p + 1));
3088 p = strchr (p, '\n');
3100 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3102 if ((*i).substr (0,7) == "file://") {
3105 PBD::url_decode (p);
3107 // scan forward past three slashes
3109 string::size_type slashcnt = 0;
3110 string::size_type n = 0;
3111 string::iterator x = p.begin();
3113 while (slashcnt < 3 && x != p.end()) {
3116 } else if (slashcnt == 3) {
3123 if (slashcnt != 3 || x == p.end()) {
3124 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3128 paths.push_back (p.substr (n - 1));
3136 Editor::new_tempo_section ()
3142 Editor::map_transport_state ()
3144 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3146 if (_session && _session->transport_stopped()) {
3147 have_pending_keyboard_selection = false;
3150 update_loop_range_view (true);
3155 Editor::State::State (PublicEditor const * e)
3157 selection = new Selection (e);
3160 Editor::State::~State ()
3166 Editor::begin_reversible_command (string name)
3169 _session->begin_reversible_command (name);
3174 Editor::begin_reversible_command (GQuark q)
3177 _session->begin_reversible_command (q);
3182 Editor::commit_reversible_command ()
3185 _session->commit_reversible_command ();
3190 Editor::history_changed ()
3194 if (undo_action && _session) {
3195 if (_session->undo_depth() == 0) {
3198 label = string_compose(_("Undo (%1)"), _session->next_undo());
3200 undo_action->property_label() = label;
3203 if (redo_action && _session) {
3204 if (_session->redo_depth() == 0) {
3207 label = string_compose(_("Redo (%1)"), _session->next_redo());
3209 redo_action->property_label() = label;
3214 Editor::duplicate_dialog (bool with_dialog)
3218 if (mouse_mode == MouseRange) {
3219 if (selection->time.length() == 0) {
3224 RegionSelection rs = get_regions_from_selection_and_entered ();
3226 if (mouse_mode != MouseRange && rs.empty()) {
3232 ArdourDialog win (_("Duplicate"));
3233 Label label (_("Number of duplications:"));
3234 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3235 SpinButton spinner (adjustment, 0.0, 1);
3238 win.get_vbox()->set_spacing (12);
3239 win.get_vbox()->pack_start (hbox);
3240 hbox.set_border_width (6);
3241 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3243 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3244 place, visually. so do this by hand.
3247 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3248 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3249 spinner.grab_focus();
3255 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3256 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3257 win.set_default_response (RESPONSE_ACCEPT);
3259 win.set_position (WIN_POS_MOUSE);
3261 spinner.grab_focus ();
3263 switch (win.run ()) {
3264 case RESPONSE_ACCEPT:
3270 times = adjustment.get_value();
3273 if (mouse_mode == MouseRange) {
3274 duplicate_selection (times);
3276 duplicate_some_regions (rs, times);
3281 Editor::show_verbose_canvas_cursor ()
3283 verbose_canvas_cursor->raise_to_top();
3284 verbose_canvas_cursor->show();
3285 verbose_cursor_visible = true;
3289 Editor::hide_verbose_canvas_cursor ()
3291 verbose_canvas_cursor->hide();
3292 verbose_cursor_visible = false;
3296 Editor::clamp_verbose_cursor_x (double x)
3301 x = min (_canvas_width - 200.0, x);
3307 Editor::clamp_verbose_cursor_y (double y)
3309 if (y < canvas_timebars_vsize) {
3310 y = canvas_timebars_vsize;
3312 y = min (_canvas_height - 50, y);
3318 Editor::show_verbose_canvas_cursor_with (const string & txt, int32_t xoffset, int32_t yoffset)
3320 verbose_canvas_cursor->property_text() = txt.c_str();
3325 track_canvas->get_pointer (x, y);
3326 track_canvas->window_to_world (x, y, wx, wy);
3331 /* don't get too close to the edge */
3332 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (wx);
3333 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (wy);
3335 show_verbose_canvas_cursor ();
3339 Editor::set_verbose_canvas_cursor (const string & txt, double x, double y)
3341 verbose_canvas_cursor->property_text() = txt.c_str();
3342 /* don't get too close to the edge */
3343 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (x);
3344 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (y);
3348 Editor::set_verbose_canvas_cursor_text (const string & txt)
3350 verbose_canvas_cursor->property_text() = txt.c_str();
3354 Editor::set_edit_mode (EditMode m)
3356 Config->set_edit_mode (m);
3360 Editor::cycle_edit_mode ()
3362 switch (Config->get_edit_mode()) {
3364 if (Profile->get_sae()) {
3365 Config->set_edit_mode (Lock);
3367 Config->set_edit_mode (Splice);
3371 Config->set_edit_mode (Lock);
3374 Config->set_edit_mode (Slide);
3380 Editor::edit_mode_selection_done ()
3382 string s = edit_mode_selector.get_active_text ();
3385 Config->set_edit_mode (string_to_edit_mode (s));
3390 Editor::snap_type_selection_done ()
3392 string choice = snap_type_selector.get_active_text();
3393 SnapType snaptype = SnapToBeat;
3395 if (choice == _("Beats/2")) {
3396 snaptype = SnapToBeatDiv2;
3397 } else if (choice == _("Beats/3")) {
3398 snaptype = SnapToBeatDiv3;
3399 } else if (choice == _("Beats/4")) {
3400 snaptype = SnapToBeatDiv4;
3401 } else if (choice == _("Beats/5")) {
3402 snaptype = SnapToBeatDiv5;
3403 } else if (choice == _("Beats/6")) {
3404 snaptype = SnapToBeatDiv6;
3405 } else if (choice == _("Beats/7")) {
3406 snaptype = SnapToBeatDiv7;
3407 } else if (choice == _("Beats/8")) {
3408 snaptype = SnapToBeatDiv8;
3409 } else if (choice == _("Beats/10")) {
3410 snaptype = SnapToBeatDiv10;
3411 } else if (choice == _("Beats/12")) {
3412 snaptype = SnapToBeatDiv12;
3413 } else if (choice == _("Beats/14")) {
3414 snaptype = SnapToBeatDiv14;
3415 } else if (choice == _("Beats/16")) {
3416 snaptype = SnapToBeatDiv16;
3417 } else if (choice == _("Beats/20")) {
3418 snaptype = SnapToBeatDiv20;
3419 } else if (choice == _("Beats/24")) {
3420 snaptype = SnapToBeatDiv24;
3421 } else if (choice == _("Beats/28")) {
3422 snaptype = SnapToBeatDiv28;
3423 } else if (choice == _("Beats/32")) {
3424 snaptype = SnapToBeatDiv32;
3425 } else if (choice == _("Beats")) {
3426 snaptype = SnapToBeat;
3427 } else if (choice == _("Bars")) {
3428 snaptype = SnapToBar;
3429 } else if (choice == _("Marks")) {
3430 snaptype = SnapToMark;
3431 } else if (choice == _("Region starts")) {
3432 snaptype = SnapToRegionStart;
3433 } else if (choice == _("Region ends")) {
3434 snaptype = SnapToRegionEnd;
3435 } else if (choice == _("Region bounds")) {
3436 snaptype = SnapToRegionBoundary;
3437 } else if (choice == _("Region syncs")) {
3438 snaptype = SnapToRegionSync;
3439 } else if (choice == _("CD Frames")) {
3440 snaptype = SnapToCDFrame;
3441 } else if (choice == _("Timecode Frames")) {
3442 snaptype = SnapToTimecodeFrame;
3443 } else if (choice == _("Timecode Seconds")) {
3444 snaptype = SnapToTimecodeSeconds;
3445 } else if (choice == _("Timecode Minutes")) {
3446 snaptype = SnapToTimecodeMinutes;
3447 } else if (choice == _("Seconds")) {
3448 snaptype = SnapToSeconds;
3449 } else if (choice == _("Minutes")) {
3450 snaptype = SnapToMinutes;
3453 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3455 ract->set_active ();
3460 Editor::snap_mode_selection_done ()
3462 string choice = snap_mode_selector.get_active_text();
3463 SnapMode mode = SnapNormal;
3465 if (choice == _("No Grid")) {
3467 } else if (choice == _("Grid")) {
3469 } else if (choice == _("Magnetic")) {
3470 mode = SnapMagnetic;
3473 RefPtr<RadioAction> ract = snap_mode_action (mode);
3476 ract->set_active (true);
3481 Editor::cycle_edit_point (bool with_marker)
3483 switch (_edit_point) {
3485 set_edit_point_preference (EditAtPlayhead);
3487 case EditAtPlayhead:
3489 set_edit_point_preference (EditAtSelectedMarker);
3491 set_edit_point_preference (EditAtMouse);
3494 case EditAtSelectedMarker:
3495 set_edit_point_preference (EditAtMouse);
3501 Editor::edit_point_selection_done ()
3503 string choice = edit_point_selector.get_active_text();
3504 EditPoint ep = EditAtSelectedMarker;
3506 if (choice == _("Marker")) {
3507 set_edit_point_preference (EditAtSelectedMarker);
3508 } else if (choice == _("Playhead")) {
3509 set_edit_point_preference (EditAtPlayhead);
3511 set_edit_point_preference (EditAtMouse);
3514 RefPtr<RadioAction> ract = edit_point_action (ep);
3517 ract->set_active (true);
3522 Editor::zoom_focus_selection_done ()
3524 string choice = zoom_focus_selector.get_active_text();
3525 ZoomFocus focus_type = ZoomFocusLeft;
3527 if (choice == _("Left")) {
3528 focus_type = ZoomFocusLeft;
3529 } else if (choice == _("Right")) {
3530 focus_type = ZoomFocusRight;
3531 } else if (choice == _("Center")) {
3532 focus_type = ZoomFocusCenter;
3533 } else if (choice == _("Playhead")) {
3534 focus_type = ZoomFocusPlayhead;
3535 } else if (choice == _("Mouse")) {
3536 focus_type = ZoomFocusMouse;
3537 } else if (choice == _("Edit point")) {
3538 focus_type = ZoomFocusEdit;
3541 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3544 ract->set_active ();
3549 Editor::edit_controls_button_release (GdkEventButton* ev)
3551 if (Keyboard::is_context_menu_event (ev)) {
3552 ARDOUR_UI::instance()->add_route (this);
3553 } else if (ev->button == 1) {
3554 selection->clear_tracks ();
3561 Editor::mouse_select_button_release (GdkEventButton* ev)
3563 /* this handles just right-clicks */
3565 if (ev->button != 3) {
3573 Editor::set_zoom_focus (ZoomFocus f)
3575 string str = zoom_focus_strings[(int)f];
3577 if (str != zoom_focus_selector.get_active_text()) {
3578 zoom_focus_selector.set_active_text (str);
3581 if (zoom_focus != f) {
3588 Editor::ensure_float (Window& win)
3590 win.set_transient_for (*this);
3594 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3596 /* recover or initialize pane positions. do this here rather than earlier because
3597 we don't want the positions to change the child allocations, which they seem to do.
3603 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3615 width = default_width;
3616 height = default_height;
3618 if ((geometry = find_named_node (*node, "geometry")) != 0) {
3620 prop = geometry->property ("x-size");
3622 width = atoi (prop->value());
3624 prop = geometry->property ("y-size");
3626 height = atoi (prop->value());
3630 if (which == static_cast<Paned*> (&edit_pane)) {
3632 if (done & Horizontal) {
3636 if (geometry && (prop = geometry->property ("notebook-shrunk"))) {
3637 _notebook_shrunk = string_is_affirmative (prop->value ());
3640 if (geometry && (prop = geometry->property ("pre-maximal-horizontal-pane-position"))) {
3641 pre_maximal_horizontal_pane_position = atoi (prop->value ());
3644 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3645 /* initial allocation is 90% to canvas, 10% to notebook */
3646 pos = (int) floor (alloc.get_width() * 0.90f);
3647 snprintf (buf, sizeof(buf), "%d", pos);
3649 pos = atoi (prop->value());
3652 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3653 edit_pane.set_position (pos);
3654 if (pre_maximal_horizontal_pane_position == 0) {
3655 pre_maximal_horizontal_pane_position = pos;
3659 done = (Pane) (done | Horizontal);
3661 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3663 if (done & Vertical) {
3667 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3668 /* initial allocation is 90% to canvas, 10% to summary */
3669 pos = (int) floor (alloc.get_height() * 0.90f);
3670 snprintf (buf, sizeof(buf), "%d", pos);
3672 pos = atoi (prop->value());
3675 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3676 editor_summary_pane.set_position (pos);
3677 pre_maximal_vertical_pane_position = pos;
3680 done = (Pane) (done | Vertical);
3685 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3687 if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) {
3688 top_hbox.remove (toolbar_frame);
3693 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3695 if (toolbar_frame.get_parent() == 0) {
3696 top_hbox.pack_end (toolbar_frame);
3701 Editor::set_show_measures (bool yn)
3703 if (_show_measures != yn) {
3706 if ((_show_measures = yn) == true) {
3708 tempo_lines->show();
3716 Editor::toggle_follow_playhead ()
3718 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3720 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3721 set_follow_playhead (tact->get_active());
3725 /** @param yn true to follow playhead, otherwise false.
3726 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
3729 Editor::set_follow_playhead (bool yn, bool catch_up)
3731 if (_follow_playhead != yn) {
3732 if ((_follow_playhead = yn) == true && catch_up) {
3734 reset_x_origin_to_follow_playhead ();
3741 Editor::toggle_stationary_playhead ()
3743 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3745 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3746 set_stationary_playhead (tact->get_active());
3751 Editor::set_stationary_playhead (bool yn)
3753 if (_stationary_playhead != yn) {
3754 if ((_stationary_playhead = yn) == true) {
3756 // FIXME need a 3.0 equivalent of this 2.X call
3757 // update_current_screen ();
3764 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3766 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3768 xfade->set_active (!xfade->active());
3773 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3775 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3777 xfade->set_follow_overlap (!xfade->following_overlap());
3782 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3784 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3790 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3794 switch (cew.run ()) {
3795 case RESPONSE_ACCEPT:
3802 PropertyChange all_crossfade_properties;
3803 all_crossfade_properties.add (ARDOUR::Properties::active);
3804 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3805 xfade->PropertyChanged (all_crossfade_properties);
3809 Editor::playlist_selector () const
3811 return *_playlist_selector;
3815 Editor::get_grid_type_as_beats (bool& success, framepos_t position)
3819 switch (_snap_type) {
3824 case SnapToBeatDiv32:
3827 case SnapToBeatDiv28:
3830 case SnapToBeatDiv24:
3833 case SnapToBeatDiv20:
3836 case SnapToBeatDiv16:
3839 case SnapToBeatDiv14:
3842 case SnapToBeatDiv12:
3845 case SnapToBeatDiv10:
3848 case SnapToBeatDiv8:
3851 case SnapToBeatDiv7:
3854 case SnapToBeatDiv6:
3857 case SnapToBeatDiv5:
3860 case SnapToBeatDiv4:
3863 case SnapToBeatDiv3:
3866 case SnapToBeatDiv2:
3872 return _session->tempo_map().meter_at (position).beats_per_bar();
3877 case SnapToTimecodeFrame:
3878 case SnapToTimecodeSeconds:
3879 case SnapToTimecodeMinutes:
3882 case SnapToRegionStart:
3883 case SnapToRegionEnd:
3884 case SnapToRegionSync:
3885 case SnapToRegionBoundary:
3895 Editor::get_nudge_distance (framepos_t pos, framecnt_t& next)
3899 ret = nudge_clock.current_duration (pos);
3900 next = ret + 1; /* XXXX fix me */
3906 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3908 ArdourDialog dialog (_("Playlist Deletion"));
3909 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3910 "If left alone, no audio files used by it will be cleaned.\n"
3911 "If deleted, audio files used by it alone by will cleaned."),
3914 dialog.set_position (WIN_POS_CENTER);
3915 dialog.get_vbox()->pack_start (label);
3919 dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT);
3920 dialog.add_button (_("Keep playlist"), RESPONSE_REJECT);
3921 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3923 switch (dialog.run ()) {
3924 case RESPONSE_ACCEPT:
3925 /* delete the playlist */
3929 case RESPONSE_REJECT:
3930 /* keep the playlist */
3942 Editor::audio_region_selection_covers (framepos_t where)
3944 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3945 if ((*a)->region()->covers (where)) {
3954 Editor::prepare_for_cleanup ()
3956 cut_buffer->clear_regions ();
3957 cut_buffer->clear_playlists ();
3959 selection->clear_regions ();
3960 selection->clear_playlists ();
3962 _regions->suspend_redisplay ();
3966 Editor::finish_cleanup ()
3968 _regions->resume_redisplay ();
3972 Editor::transport_loop_location()
3975 return _session->locations()->auto_loop_location();
3982 Editor::transport_punch_location()
3985 return _session->locations()->auto_punch_location();
3992 Editor::control_layout_scroll (GdkEventScroll* ev)
3994 if (Keyboard::some_magic_widget_has_focus()) {
3998 switch (ev->direction) {
4000 scroll_tracks_up_line ();
4004 case GDK_SCROLL_DOWN:
4005 scroll_tracks_down_line ();
4009 /* no left/right handling yet */
4017 Editor::session_state_saved (string)
4020 _snapshots->redisplay ();
4024 Editor::maximise_editing_space ()
4026 _mouse_mode_tearoff->set_visible (false);
4027 _tools_tearoff->set_visible (false);
4028 _zoom_tearoff->set_visible (false);
4030 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
4031 pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
4032 pre_maximal_editor_width = this->get_width ();
4033 pre_maximal_editor_height = this->get_height ();
4035 if (post_maximal_horizontal_pane_position == 0) {
4036 post_maximal_horizontal_pane_position = edit_pane.get_width();
4039 if (post_maximal_vertical_pane_position == 0) {
4040 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
4045 if (post_maximal_editor_width) {
4046 edit_pane.set_position (post_maximal_horizontal_pane_position -
4047 abs(post_maximal_editor_width - pre_maximal_editor_width));
4049 edit_pane.set_position (post_maximal_horizontal_pane_position);
4052 if (post_maximal_editor_height) {
4053 editor_summary_pane.set_position (post_maximal_vertical_pane_position -
4054 abs(post_maximal_editor_height - pre_maximal_editor_height));
4056 editor_summary_pane.set_position (post_maximal_vertical_pane_position);
4059 if (Config->get_keep_tearoffs()) {
4060 _mouse_mode_tearoff->set_visible (true);
4061 _tools_tearoff->set_visible (true);
4062 if (Config->get_show_zoom_tools ()) {
4063 _zoom_tearoff->set_visible (true);
4070 Editor::restore_editing_space ()
4072 // user changed width/height of panes during fullscreen
4074 if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
4075 post_maximal_horizontal_pane_position = edit_pane.get_position();
4078 if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
4079 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
4084 _mouse_mode_tearoff->set_visible (true);
4085 _tools_tearoff->set_visible (true);
4086 if (Config->get_show_zoom_tools ()) {
4087 _zoom_tearoff->set_visible (true);
4089 post_maximal_editor_width = this->get_width();
4090 post_maximal_editor_height = this->get_height();
4092 edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
4093 editor_summary_pane.set_position (pre_maximal_vertical_pane_position + abs(this->get_height() - pre_maximal_editor_height));
4097 * Make new playlists for a given track and also any others that belong
4098 * to the same active route group with the `edit' property.
4103 Editor::new_playlists (TimeAxisView* v)
4105 begin_reversible_command (_("new playlists"));
4106 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4107 _session->playlists->get (playlists);
4108 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4109 commit_reversible_command ();
4113 * Use a copy of the current playlist for a given track and also any others that belong
4114 * to the same active route group with the `edit' property.
4119 Editor::copy_playlists (TimeAxisView* v)
4121 begin_reversible_command (_("copy playlists"));
4122 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4123 _session->playlists->get (playlists);
4124 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4125 commit_reversible_command ();
4128 /** Clear the current playlist for a given track and also any others that belong
4129 * to the same active route group with the `edit' property.
4134 Editor::clear_playlists (TimeAxisView* v)
4136 begin_reversible_command (_("clear playlists"));
4137 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4138 _session->playlists->get (playlists);
4139 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4140 commit_reversible_command ();
4144 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4146 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4150 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4152 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4156 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4158 atv.clear_playlist ();
4162 Editor::on_key_press_event (GdkEventKey* ev)
4164 return key_press_focus_accelerator_handler (*this, ev);
4168 Editor::on_key_release_event (GdkEventKey* ev)
4170 return Gtk::Window::on_key_release_event (ev);
4171 // return key_press_focus_accelerator_handler (*this, ev);
4174 /** Queue up a change to the viewport x origin.
4175 * @param frame New x origin.
4178 Editor::reset_x_origin (framepos_t frame)
4180 queue_visual_change (frame);
4184 Editor::reset_y_origin (double y)
4186 queue_visual_change_y (y);
4190 Editor::reset_zoom (double fpu)
4192 queue_visual_change (fpu);
4196 Editor::reposition_and_zoom (framepos_t frame, double fpu)
4198 reset_x_origin (frame);
4201 if (!no_save_visual) {
4202 undo_visual_stack.push_back (current_visual_state(false));
4206 Editor::VisualState*
4207 Editor::current_visual_state (bool with_tracks)
4209 VisualState* vs = new VisualState;
4210 vs->y_position = vertical_adjustment.get_value();
4211 vs->frames_per_unit = frames_per_unit;
4212 vs->leftmost_frame = leftmost_frame;
4213 vs->zoom_focus = zoom_focus;
4216 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4217 vs->track_states.push_back (TAVState ((*i), &(*i)->get_state()));
4225 Editor::undo_visual_state ()
4227 if (undo_visual_stack.empty()) {
4231 redo_visual_stack.push_back (current_visual_state());
4233 VisualState* vs = undo_visual_stack.back();
4234 undo_visual_stack.pop_back();
4235 use_visual_state (*vs);
4239 Editor::redo_visual_state ()
4241 if (redo_visual_stack.empty()) {
4245 undo_visual_stack.push_back (current_visual_state());
4247 VisualState* vs = redo_visual_stack.back();
4248 redo_visual_stack.pop_back();
4249 use_visual_state (*vs);
4253 Editor::swap_visual_state ()
4255 if (undo_visual_stack.empty()) {
4256 redo_visual_state ();
4258 undo_visual_state ();
4263 Editor::use_visual_state (VisualState& vs)
4265 no_save_visual = true;
4267 _routes->suspend_redisplay ();
4269 vertical_adjustment.set_value (vs.y_position);
4271 set_zoom_focus (vs.zoom_focus);
4272 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4274 for (list<TAVState>::iterator i = vs.track_states.begin(); i != vs.track_states.end(); ++i) {
4275 TrackViewList::iterator t;
4277 /* check if the track still exists - it could have been deleted */
4279 if ((t = find (track_views.begin(), track_views.end(), i->first)) != track_views.end()) {
4280 (*t)->set_state (*(i->second), Stateful::loading_state_version);
4285 if (!vs.track_states.empty()) {
4286 _routes->update_visibility ();
4289 _routes->resume_redisplay ();
4291 no_save_visual = false;
4295 Editor::set_frames_per_unit (double fpu)
4297 /* this is the core function that controls the zoom level of the canvas. it is called
4298 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4301 if (fpu == frames_per_unit) {
4310 /* don't allow zooms that fit more than the maximum number
4311 of frames into an 800 pixel wide space.
4314 if (max_framepos / fpu < 800.0) {
4319 tempo_lines->tempo_map_changed();
4321 frames_per_unit = fpu;
4326 Editor::post_zoom ()
4328 // convert fpu to frame count
4330 framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width);
4332 if (frames_per_unit != zoom_range_clock.current_duration()) {
4333 zoom_range_clock.set (frames);
4336 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4337 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4338 (*i)->reshow_selection (selection->time);
4342 ZoomChanged (); /* EMIT_SIGNAL */
4344 //reset_scrolling_region ();
4346 if (playhead_cursor) {
4347 playhead_cursor->set_position (playhead_cursor->current_frame);
4350 refresh_location_display();
4351 _summary->set_overlays_dirty ();
4353 update_marker_labels ();
4359 Editor::queue_visual_change (framepos_t where)
4361 pending_visual_change.add (VisualChange::TimeOrigin);
4362 pending_visual_change.time_origin = where;
4363 ensure_visual_change_idle_handler ();
4367 Editor::queue_visual_change (double fpu)
4369 pending_visual_change.add (VisualChange::ZoomLevel);
4370 pending_visual_change.frames_per_unit = fpu;
4372 ensure_visual_change_idle_handler ();
4376 Editor::queue_visual_change_y (double y)
4378 pending_visual_change.add (VisualChange::YOrigin);
4379 pending_visual_change.y_origin = y;
4381 ensure_visual_change_idle_handler ();
4385 Editor::ensure_visual_change_idle_handler ()
4387 if (pending_visual_change.idle_handler_id < 0) {
4388 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4393 Editor::_idle_visual_changer (void* arg)
4395 return static_cast<Editor*>(arg)->idle_visual_changer ();
4399 Editor::idle_visual_changer ()
4401 VisualChange::Type p = pending_visual_change.pending;
4402 pending_visual_change.pending = (VisualChange::Type) 0;
4404 double const last_time_origin = horizontal_position ();
4406 if (p & VisualChange::TimeOrigin) {
4407 /* This is a bit of a hack, but set_frames_per_unit
4408 below will (if called) end up with the
4409 CrossfadeViews looking at Editor::leftmost_frame,
4410 and if we're changing origin and zoom in the same
4411 operation it will be the wrong value unless we
4415 leftmost_frame = pending_visual_change.time_origin;
4418 if (p & VisualChange::ZoomLevel) {
4419 set_frames_per_unit (pending_visual_change.frames_per_unit);
4421 compute_fixed_ruler_scale ();
4422 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4423 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4424 update_tempo_based_rulers ();
4426 if (p & VisualChange::TimeOrigin) {
4427 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4429 if (p & VisualChange::YOrigin) {
4430 vertical_adjustment.set_value (pending_visual_change.y_origin);
4433 if (last_time_origin == horizontal_position ()) {
4434 /* changed signal not emitted */
4435 update_fixed_rulers ();
4436 redisplay_tempo (true);
4439 _summary->set_overlays_dirty ();
4441 pending_visual_change.idle_handler_id = -1;
4442 return 0; /* this is always a one-shot call */
4445 struct EditorOrderTimeAxisSorter {
4446 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4447 return a->order () < b->order ();
4452 Editor::sort_track_selection (TrackViewList* sel)
4454 EditorOrderTimeAxisSorter cmp;
4459 selection->tracks.sort (cmp);
4464 Editor::get_preferred_edit_position (bool ignore_playhead)
4467 framepos_t where = 0;
4468 EditPoint ep = _edit_point;
4470 if (entered_marker) {
4471 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position()));
4472 return entered_marker->position();
4475 if (ignore_playhead && ep == EditAtPlayhead) {
4476 ep = EditAtSelectedMarker;
4480 case EditAtPlayhead:
4481 where = _session->audible_frame();
4482 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where));
4485 case EditAtSelectedMarker:
4486 if (!selection->markers.empty()) {
4488 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4491 where = loc->start();
4495 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where));
4503 if (!mouse_frame (where, ignored)) {
4504 /* XXX not right but what can we do ? */
4508 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where));
4516 Editor::set_loop_range (framepos_t start, framepos_t end, string cmd)
4518 if (!_session) return;
4520 begin_reversible_command (cmd);
4524 if ((tll = transport_loop_location()) == 0) {
4525 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop);
4526 XMLNode &before = _session->locations()->get_state();
4527 _session->locations()->add (loc, true);
4528 _session->set_auto_loop_location (loc);
4529 XMLNode &after = _session->locations()->get_state();
4530 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4532 XMLNode &before = tll->get_state();
4533 tll->set_hidden (false, this);
4534 tll->set (start, end);
4535 XMLNode &after = tll->get_state();
4536 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4539 commit_reversible_command ();
4543 Editor::set_punch_range (framepos_t start, framepos_t end, string cmd)
4545 if (!_session) return;
4547 begin_reversible_command (cmd);
4551 if ((tpl = transport_punch_location()) == 0) {
4552 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoPunch);
4553 XMLNode &before = _session->locations()->get_state();
4554 _session->locations()->add (loc, true);
4555 _session->set_auto_loop_location (loc);
4556 XMLNode &after = _session->locations()->get_state();
4557 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4560 XMLNode &before = tpl->get_state();
4561 tpl->set_hidden (false, this);
4562 tpl->set (start, end);
4563 XMLNode &after = tpl->get_state();
4564 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4567 commit_reversible_command ();
4570 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4571 * @param rs List to which found regions are added.
4572 * @param where Time to look at.
4573 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4576 Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4578 const TrackViewList* tracks;
4581 tracks = &track_views;
4586 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4588 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4591 boost::shared_ptr<Track> tr;
4592 boost::shared_ptr<Playlist> pl;
4594 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4596 Playlist::RegionList* regions = pl->regions_at (
4597 (framepos_t) floor ( (double) where * tr->speed()));
4599 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4600 RegionView* rv = rtv->view()->find_view (*i);
4613 Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4615 const TrackViewList* tracks;
4618 tracks = &track_views;
4623 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4624 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4626 boost::shared_ptr<Track> tr;
4627 boost::shared_ptr<Playlist> pl;
4629 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4631 Playlist::RegionList* regions = pl->regions_touched (
4632 (framepos_t) floor ( (double)where * tr->speed()), max_framepos);
4634 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4636 RegionView* rv = rtv->view()->find_view (*i);
4649 /** Start with regions that are selected. Then add equivalent regions
4650 * on tracks in the same active edit-enabled route group as any of
4651 * the regions that we started with.
4655 Editor::get_regions_from_selection ()
4657 return get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
4660 /** Get regions using the following method:
4662 * Make an initial region list using the selected regions, unless
4663 * the edit point is `mouse' and the mouse is over an unselected
4664 * region. In this case, start with just that region.
4666 * Then, make an initial track list of the tracks that these
4667 * regions are on, and if the edit point is not `mouse', add the
4670 * Look at this track list and add any other tracks that are on the
4671 * same active edit-enabled route group as one of the initial tracks.
4673 * Finally take the initial region list and add any regions that are
4674 * under the edit point on one of the tracks on the track list to get
4675 * the returned region list.
4677 * The rationale here is that the mouse edit point is special in that
4678 * its position describes both a time and a track; the other edit
4679 * modes only describe a time. Hence if the edit point is `mouse' we
4680 * ignore selected tracks, as we assume the user means something by
4681 * pointing at a particular track. Also in this case we take note of
4682 * the region directly under the edit point, as there is always just one
4683 * (rather than possibly several with non-mouse edit points).
4687 Editor::get_regions_from_selection_and_edit_point ()
4689 RegionSelection regions;
4691 if (_edit_point == EditAtMouse && entered_regionview && !selection->regions.contains (entered_regionview)) {
4692 regions.add (entered_regionview);
4694 regions = selection->regions;
4697 TrackViewList tracks;
4699 if (_edit_point != EditAtMouse) {
4700 tracks = selection->tracks;
4703 /* Add any other tracks that have regions that are in the same
4704 edit-activated route group as one of our regions.
4706 for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) {
4708 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4710 if (g && g->is_active() && g->is_edit()) {
4711 tracks.add (axis_views_from_routes (g->route_list()));
4715 if (!tracks.empty()) {
4716 /* now find regions that are at the edit position on those tracks */
4717 framepos_t const where = get_preferred_edit_position ();
4718 get_regions_at (regions, where, tracks);
4724 /** Start with regions that are selected, or the entered regionview if none are selected.
4725 * Then add equivalent regions on tracks in the same active edit-enabled route group as any
4726 * of the regions that we started with.
4730 Editor::get_regions_from_selection_and_entered ()
4732 RegionSelection regions = selection->regions;
4734 if (regions.empty() && entered_regionview) {
4735 regions.add (entered_regionview);
4738 return get_equivalent_regions (regions, ARDOUR::Properties::edit.property_id);
4742 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4744 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4746 RouteTimeAxisView* tatv;
4748 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4750 boost::shared_ptr<Playlist> pl;
4751 vector<boost::shared_ptr<Region> > results;
4753 boost::shared_ptr<Track> tr;
4755 if ((tr = tatv->track()) == 0) {
4760 if ((pl = (tr->playlist())) != 0) {
4761 pl->get_region_list_equivalent_regions (region, results);
4764 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4765 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4766 regions.push_back (marv);
4775 Editor::show_rhythm_ferret ()
4777 if (rhythm_ferret == 0) {
4778 rhythm_ferret = new RhythmFerret(*this);
4781 rhythm_ferret->set_session (_session);
4782 rhythm_ferret->show ();
4783 rhythm_ferret->present ();
4787 Editor::first_idle ()
4789 MessageDialog* dialog = 0;
4791 if (track_views.size() > 1) {
4792 dialog = new MessageDialog (*this,
4793 string_compose (_("Please wait while %1 loads visual data"), PROGRAM_NAME),
4798 ARDOUR_UI::instance()->flush_pending ();
4801 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4805 // first idle adds route children (automation tracks), so we need to redisplay here
4806 _routes->redisplay ();
4814 Editor::_idle_resize (gpointer arg)
4816 return ((Editor*)arg)->idle_resize ();
4820 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4822 if (resize_idle_id < 0) {
4823 resize_idle_id = g_idle_add (_idle_resize, this);
4824 _pending_resize_amount = 0;
4827 /* make a note of the smallest resulting height, so that we can clamp the
4828 lower limit at TimeAxisView::hSmall */
4830 int32_t min_resulting = INT32_MAX;
4832 _pending_resize_amount += h;
4833 _pending_resize_view = view;
4835 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4837 if (selection->tracks.contains (_pending_resize_view)) {
4838 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4839 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4843 if (min_resulting < 0) {
4848 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4849 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4853 /** Handle pending resizing of tracks */
4855 Editor::idle_resize ()
4857 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4859 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4860 selection->tracks.contains (_pending_resize_view)) {
4862 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4863 if (*i != _pending_resize_view) {
4864 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4869 _pending_resize_amount = 0;
4871 _group_tabs->set_dirty ();
4872 resize_idle_id = -1;
4880 ENSURE_GUI_THREAD (*this, &Editor::located);
4882 playhead_cursor->set_position (_session->audible_frame ());
4883 if (_follow_playhead && !_pending_initial_locate) {
4884 reset_x_origin_to_follow_playhead ();
4887 _pending_locate_request = false;
4888 _pending_initial_locate = false;
4892 Editor::region_view_added (RegionView *)
4894 _summary->set_dirty ();
4898 Editor::region_view_removed ()
4900 _summary->set_dirty ();
4904 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4906 TrackViewList::const_iterator j = track_views.begin ();
4907 while (j != track_views.end()) {
4908 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4909 if (rtv && rtv->route() == r) {
4920 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4924 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4925 TimeAxisView* tv = axis_view_from_route (*i);
4936 Editor::handle_new_route (RouteList& routes)
4938 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4940 RouteTimeAxisView *rtv;
4941 list<RouteTimeAxisView*> new_views;
4943 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4944 boost::shared_ptr<Route> route = (*x);
4946 if (route->is_hidden() || route->is_monitor()) {
4950 DataType dt = route->input()->default_type();
4952 if (dt == ARDOUR::DataType::AUDIO) {
4953 rtv = new AudioTimeAxisView (*this, _session, route, *track_canvas);
4954 } else if (dt == ARDOUR::DataType::MIDI) {
4955 rtv = new MidiTimeAxisView (*this, _session, route, *track_canvas);
4957 throw unknown_type();
4960 new_views.push_back (rtv);
4961 track_views.push_back (rtv);
4963 rtv->effective_gain_display ();
4965 if (internal_editing()) {
4966 rtv->enter_internal_edit_mode ();
4968 rtv->leave_internal_edit_mode ();
4971 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4972 rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed));
4975 _routes->routes_added (new_views);
4976 _summary->routes_added (new_views);
4978 if (show_editor_mixer_when_tracks_arrive) {
4979 show_editor_mixer (true);
4982 editor_list_button.set_sensitive (true);
4986 Editor::timeaxisview_deleted (TimeAxisView *tv)
4988 if (_session && _session->deletion_in_progress()) {
4989 /* the situation is under control */
4993 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
4995 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
4997 _routes->route_removed (tv);
4999 if (tv == entered_track) {
5003 TimeAxisView::Children c = tv->get_child_list ();
5004 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
5005 if (entered_track == i->get()) {
5010 /* remove it from the list of track views */
5012 TrackViewList::iterator i;
5014 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
5015 i = track_views.erase (i);
5018 /* update whatever the current mixer strip is displaying, if revelant */
5020 boost::shared_ptr<Route> route;
5023 route = rtav->route ();
5026 if (current_mixer_strip && current_mixer_strip->route() == route) {
5028 TimeAxisView* next_tv;
5030 if (track_views.empty()) {
5032 } else if (i == track_views.end()) {
5033 next_tv = track_views.front();
5040 set_selected_mixer_strip (*next_tv);
5042 /* make the editor mixer strip go away setting the
5043 * button to inactive (which also unticks the menu option)
5046 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
5052 Editor::hide_track_in_display (TimeAxisView* tv, bool apply_to_selection)
5054 if (apply_to_selection) {
5055 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ) {
5057 TrackSelection::iterator j = i;
5060 hide_track_in_display (*i, false);
5065 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
5067 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
5068 // this will hide the mixer strip
5069 set_selected_mixer_strip (*tv);
5072 _routes->hide_track_in_display (*tv);
5077 Editor::sync_track_view_list_and_routes ()
5079 track_views = TrackViewList (_routes->views ());
5081 _summary->set_dirty ();
5082 _group_tabs->set_dirty ();
5084 return false; // do not call again (until needed)
5088 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
5090 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5095 /** Find a RouteTimeAxisView by the ID of its route */
5097 Editor::get_route_view_by_route_id (PBD::ID& id) const
5099 RouteTimeAxisView* v;
5101 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5102 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5103 if(v->route()->id() == id) {
5113 Editor::fit_route_group (RouteGroup *g)
5115 TrackViewList ts = axis_views_from_routes (g->route_list ());
5120 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5122 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5125 _session->cancel_audition ();
5129 if (_session->is_auditioning()) {
5130 _session->cancel_audition ();
5131 if (r == last_audition_region) {
5136 _session->audition_region (r);
5137 last_audition_region = r;
5142 Editor::hide_a_region (boost::shared_ptr<Region> r)
5144 r->set_hidden (true);
5148 Editor::show_a_region (boost::shared_ptr<Region> r)
5150 r->set_hidden (false);
5154 Editor::audition_region_from_region_list ()
5156 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5160 Editor::hide_region_from_region_list ()
5162 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5166 Editor::show_region_in_region_list ()
5168 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5172 Editor::step_edit_status_change (bool yn)
5175 start_step_editing ();
5177 stop_step_editing ();
5182 Editor::start_step_editing ()
5184 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5188 Editor::stop_step_editing ()
5190 step_edit_connection.disconnect ();
5194 Editor::check_step_edit ()
5196 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5197 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5199 mtv->check_step_edit ();
5203 return true; // do it again, till we stop
5207 Editor::scroll_press (Direction dir)
5209 ++_scroll_callbacks;
5211 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5212 /* delay the first auto-repeat */
5218 scroll_backward (1);
5226 scroll_tracks_up_line ();
5230 scroll_tracks_down_line ();
5234 /* do hacky auto-repeat */
5235 if (!_scroll_connection.connected ()) {
5237 _scroll_connection = Glib::signal_timeout().connect (
5238 sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), dir), 100
5241 _scroll_callbacks = 0;
5248 Editor::scroll_release ()
5250 _scroll_connection.disconnect ();
5253 /** Queue a change for the Editor viewport x origin to follow the playhead */
5255 Editor::reset_x_origin_to_follow_playhead ()
5257 framepos_t const frame = playhead_cursor->current_frame;
5259 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5261 if (_session->transport_speed() < 0) {
5263 if (frame > (current_page_frames() / 2)) {
5264 center_screen (frame-(current_page_frames()/2));
5266 center_screen (current_page_frames()/2);
5271 if (frame < leftmost_frame) {
5274 if (_session->transport_rolling()) {
5275 /* rolling; end up with the playhead at the right of the page */
5276 l = frame - current_page_frames ();
5278 /* not rolling: end up with the playhead 3/4 of the way along the page */
5279 l = frame - (3 * current_page_frames() / 4);
5286 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5289 if (_session->transport_rolling()) {
5290 /* rolling: end up with the playhead on the left of the page */
5291 center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5293 /* not rolling: end up with the playhead 1/4 of the way along the page */
5294 center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5302 Editor::super_rapid_screen_update ()
5304 if (!_session || !_session->engine().running()) {
5308 /* METERING / MIXER STRIPS */
5310 /* update track meters, if required */
5311 if (is_mapped() && meters_running) {
5312 RouteTimeAxisView* rtv;
5313 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5314 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5315 rtv->fast_update ();
5320 /* and any current mixer strip */
5321 if (current_mixer_strip) {
5322 current_mixer_strip->fast_update ();
5325 /* PLAYHEAD AND VIEWPORT */
5327 framepos_t const frame = _session->audible_frame();
5329 /* There are a few reasons why we might not update the playhead / viewport stuff:
5331 * 1. we don't update things when there's a pending locate request, otherwise
5332 * when the editor requests a locate there is a chance that this method
5333 * will move the playhead before the locate request is processed, causing
5335 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5336 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5339 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5341 last_update_frame = frame;
5343 if (!_dragging_playhead) {
5344 playhead_cursor->set_position (frame);
5347 if (!_stationary_playhead) {
5349 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5350 reset_x_origin_to_follow_playhead ();
5355 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5359 // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5360 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5361 if (target <= 0.0) {
5364 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5365 target = (target * 0.15) + (current * 0.85);
5371 set_horizontal_position (current);
5380 Editor::session_going_away ()
5382 _have_idled = false;
5384 _session_connections.drop_connections ();
5386 super_rapid_screen_update_connection.disconnect ();
5388 selection->clear ();
5389 cut_buffer->clear ();
5391 clicked_regionview = 0;
5392 clicked_axisview = 0;
5393 clicked_routeview = 0;
5394 clicked_crossfadeview = 0;
5395 entered_regionview = 0;
5397 last_update_frame = 0;
5400 playhead_cursor->canvas_item.hide ();
5402 /* rip everything out of the list displays */
5406 _route_groups->clear ();
5408 /* do this first so that deleting a track doesn't reset cms to null
5409 and thus cause a leak.
5412 if (current_mixer_strip) {
5413 if (current_mixer_strip->get_parent() != 0) {
5414 global_hpacker.remove (*current_mixer_strip);
5416 delete current_mixer_strip;
5417 current_mixer_strip = 0;
5420 /* delete all trackviews */
5422 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5425 track_views.clear ();
5427 zoom_range_clock.set_session (0);
5428 nudge_clock.set_session (0);
5430 editor_list_button.set_active(false);
5431 editor_list_button.set_sensitive(false);
5433 /* clear tempo/meter rulers */
5434 remove_metric_marks ();
5436 clear_marker_display ();
5438 delete current_bbt_points;
5439 current_bbt_points = 0;
5441 /* get rid of any existing editor mixer strip */
5443 WindowTitle title(Glib::get_application_name());
5444 title += _("Editor");
5446 set_title (title.get_string());
5448 SessionHandlePtr::session_going_away ();
5453 Editor::show_editor_list (bool yn)
5456 _the_notebook.show ();
5458 _the_notebook.hide ();
5463 Editor::change_region_layering_order ()
5465 framepos_t const position = get_preferred_edit_position ();
5467 if (!clicked_routeview) {
5468 if (layering_order_editor) {
5469 layering_order_editor->hide ();
5474 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
5480 boost::shared_ptr<Playlist> pl = track->playlist();
5486 if (layering_order_editor == 0) {
5487 layering_order_editor = new RegionLayeringOrderEditor(*this);
5490 layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position);
5491 layering_order_editor->maybe_present ();
5495 Editor::update_region_layering_order_editor ()
5497 if (layering_order_editor && layering_order_editor->is_visible ()) {
5498 change_region_layering_order ();
5503 Editor::setup_fade_images ()
5505 _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear")));
5506 _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut")));
5507 _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut")));
5508 _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut")));
5509 _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut")));
5511 _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear")));
5512 _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut")));
5513 _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut")));
5514 _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut")));
5515 _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut")));
5519 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
5521 Editor::action_menu_item (std::string const & name)
5523 Glib::RefPtr<Action> a = editor_actions->get_action (name);
5526 return *manage (a->create_menu_item ());
5530 Editor::resize_text_widgets ()
5532 set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_FUDGE+10, 15);
5533 set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_FUDGE+10, 15);
5534 set_size_request_to_display_given_text (snap_type_selector, snap_type_strings, COMBO_FUDGE+10, 15);
5535 set_size_request_to_display_given_text (snap_mode_selector, snap_mode_strings, COMBO_FUDGE+10, 15);
5536 set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_FUDGE+10, 15);
5540 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
5542 EventBox* b = manage (new EventBox);
5543 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
5544 Label* l = manage (new Label (name));
5548 _the_notebook.append_page (widget, *b);
5552 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
5554 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
5555 _the_notebook.set_current_page (_the_notebook.page_num (*page));
5558 if (ev->type == GDK_2BUTTON_PRESS) {
5560 /* double-click on a notebook tab shrinks or expands the notebook */
5562 if (_notebook_shrunk) {
5563 edit_pane.set_position (pre_maximal_horizontal_pane_position);
5564 _notebook_shrunk = false;
5566 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
5567 edit_pane.set_position (edit_pane.get_position() + page->get_width());
5568 _notebook_shrunk = true;