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 set_edit_point_preference (EditAtMouse, true);
652 _playlist_selector = new PlaylistSelector();
653 _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
655 RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), ui_bind (&Editor::catch_vanishing_regionview, this, _1), gui_context());
659 nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
660 nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
662 nudge_forward_button.set_name ("TransportButton");
663 nudge_backward_button.set_name ("TransportButton");
665 fade_context_menu.set_name ("ArdourContextMenu");
667 /* icons, titles, WM stuff */
669 list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
670 Glib::RefPtr<Gdk::Pixbuf> icon;
672 if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
673 window_icons.push_back (icon);
675 if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
676 window_icons.push_back (icon);
678 if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
679 window_icons.push_back (icon);
681 if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
682 window_icons.push_back (icon);
684 if (!window_icons.empty()) {
685 set_icon_list (window_icons);
686 set_default_icon_list (window_icons);
689 WindowTitle title(Glib::get_application_name());
690 title += _("Editor");
691 set_title (title.get_string());
692 set_wmclass (X_("ardour_editor"), PROGRAM_NAME);
695 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
697 signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
698 signal_delete_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
700 /* allow external control surfaces/protocols to do various things */
702 ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context());
703 ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context());
704 ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context());
705 ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), ui_bind (&Editor::control_scroll, this, _1), gui_context());
706 BasicUI::AccessAction.connect (*this, invalidator (*this), ui_bind (&Editor::access_action, this, _1, _2), gui_context());
708 /* problematic: has to return a value and thus cannot be x-thread */
710 Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
712 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
714 TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
716 _ignore_region_action = false;
717 _last_region_menu_was_main = false;
718 _popup_region_menu_item = 0;
720 _show_marker_lines = false;
721 _over_region_trim_target = false;
723 /* Button bindings */
725 button_bindings = new Bindings;
727 XMLNode* node = button_settings();
729 for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) {
730 button_bindings->load (**i);
737 setup_fade_images ();
743 if(image_socket_listener) {
744 if(image_socket_listener->is_connected())
746 image_socket_listener->close_connection() ;
749 delete image_socket_listener ;
750 image_socket_listener = 0 ;
754 delete button_bindings;
756 delete _route_groups;
762 Editor::button_settings () const
764 XMLNode* settings = ARDOUR_UI::instance()->editor_settings();
765 XMLNode* node = find_named_node (*settings, X_("Buttons"));
768 cerr << "new empty Button node\n";
769 node = new XMLNode (X_("Buttons"));
776 Editor::add_toplevel_controls (Container& cont)
778 vpacker.pack_start (cont, false, false);
783 Editor::catch_vanishing_regionview (RegionView *rv)
785 /* note: the selection will take care of the vanishing
786 audioregionview by itself.
789 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
793 if (clicked_regionview == rv) {
794 clicked_regionview = 0;
797 if (entered_regionview == rv) {
798 set_entered_regionview (0);
801 if (!_all_region_actions_sensitized) {
802 sensitize_all_region_actions (true);
807 Editor::set_entered_regionview (RegionView* rv)
809 if (rv == entered_regionview) {
813 if (entered_regionview) {
814 entered_regionview->exited ();
817 if ((entered_regionview = rv) != 0) {
818 entered_regionview->entered (internal_editing ());
821 if (!_all_region_actions_sensitized && _last_region_menu_was_main) {
822 /* This RegionView entry might have changed what region actions
823 are allowed, so sensitize them all in case a key is pressed.
825 sensitize_all_region_actions (true);
830 Editor::set_entered_track (TimeAxisView* tav)
833 entered_track->exited ();
836 if ((entered_track = tav) != 0) {
837 entered_track->entered ();
842 Editor::show_window ()
844 if (!is_visible ()) {
847 /* XXX: this is a bit unfortunate; it would probably
848 be nicer if we could just call show () above rather
849 than needing the show_all ()
852 /* re-hide stuff if necessary */
853 editor_list_button_toggled ();
854 parameter_changed ("show-summary");
855 parameter_changed ("show-group-tabs");
856 parameter_changed ("show-zoom-tools");
858 /* now reset all audio_time_axis heights, because widgets might need
864 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
865 tv = (static_cast<TimeAxisView*>(*i));
869 if (current_mixer_strip) {
870 current_mixer_strip->hide_things ();
878 Editor::instant_save ()
880 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
885 _session->add_instant_xml(get_state());
887 Config->add_instant_xml(get_state());
892 Editor::zoom_adjustment_changed ()
898 double fpu = zoom_range_clock.current_duration() / _canvas_width;
902 zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width));
903 } else if (fpu > _session->current_end_frame() / _canvas_width) {
904 fpu = _session->current_end_frame() / _canvas_width;
905 zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width));
912 Editor::control_scroll (float fraction)
914 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
920 double step = fraction * current_page_frames();
923 _control_scroll_target is an optional<T>
925 it acts like a pointer to an framepos_t, with
926 a operator conversion to boolean to check
927 that it has a value could possibly use
928 playhead_cursor->current_frame to store the
929 value and a boolean in the class to know
930 when it's out of date
933 if (!_control_scroll_target) {
934 _control_scroll_target = _session->transport_frame();
935 _dragging_playhead = true;
938 if ((fraction < 0.0f) && (*_control_scroll_target < (framepos_t) fabs(step))) {
939 *_control_scroll_target = 0;
940 } else if ((fraction > 0.0f) && (max_framepos - *_control_scroll_target < step)) {
941 *_control_scroll_target = max_framepos - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
943 *_control_scroll_target += (framepos_t) floor (step);
946 /* move visuals, we'll catch up with it later */
948 playhead_cursor->set_position (*_control_scroll_target);
949 UpdateAllTransportClocks (*_control_scroll_target);
951 if (*_control_scroll_target > (current_page_frames() / 2)) {
952 /* try to center PH in window */
953 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
959 Now we do a timeout to actually bring the session to the right place
960 according to the playhead. This is to avoid reading disk buffers on every
961 call to control_scroll, which is driven by ScrollTimeline and therefore
962 probably by a control surface wheel which can generate lots of events.
964 /* cancel the existing timeout */
966 control_scroll_connection.disconnect ();
968 /* add the next timeout */
970 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
974 Editor::deferred_control_scroll (framepos_t /*target*/)
976 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
977 // reset for next stream
978 _control_scroll_target = boost::none;
979 _dragging_playhead = false;
984 Editor::access_action (std::string action_group, std::string action_item)
990 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
993 act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
1001 Editor::on_realize ()
1003 Window::on_realize ();
1008 Editor::map_position_change (framepos_t frame)
1010 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, frame)
1012 if (_session == 0) {
1016 if (_follow_playhead) {
1017 center_screen (frame);
1020 playhead_cursor->set_position (frame);
1024 Editor::center_screen (framepos_t frame)
1026 double page = _canvas_width * frames_per_unit;
1028 /* if we're off the page, then scroll.
1031 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
1032 center_screen_internal (frame, page);
1037 Editor::center_screen_internal (framepos_t frame, float page)
1042 frame -= (framepos_t) page;
1047 reset_x_origin (frame);
1052 Editor::update_title ()
1054 ENSURE_GUI_THREAD (*this, &Editor::update_title)
1057 bool dirty = _session->dirty();
1059 string session_name;
1061 if (_session->snap_name() != _session->name()) {
1062 session_name = _session->snap_name();
1064 session_name = _session->name();
1068 session_name = "*" + session_name;
1071 WindowTitle title(session_name);
1072 title += Glib::get_application_name();
1073 set_title (title.get_string());
1078 Editor::set_session (Session *t)
1080 SessionHandlePtr::set_session (t);
1086 zoom_range_clock.set_session (_session);
1087 _playlist_selector->set_session (_session);
1088 nudge_clock.set_session (_session);
1089 _summary->set_session (_session);
1090 _group_tabs->set_session (_session);
1091 _route_groups->set_session (_session);
1092 _regions->set_session (_session);
1093 _snapshots->set_session (_session);
1094 _routes->set_session (_session);
1095 _locations->set_session (_session);
1097 if (rhythm_ferret) {
1098 rhythm_ferret->set_session (_session);
1101 if (analysis_window) {
1102 analysis_window->set_session (_session);
1106 sfbrowser->set_session (_session);
1109 compute_fixed_ruler_scale ();
1111 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1112 set_state (*node, Stateful::loading_state_version);
1114 /* catch up with the playhead */
1116 _session->request_locate (playhead_cursor->current_frame);
1117 _pending_initial_locate = true;
1121 /* These signals can all be emitted by a non-GUI thread. Therefore the
1122 handlers for them must not attempt to directly interact with the GUI,
1123 but use Gtkmm2ext::UI::instance()->call_slot();
1126 _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), ui_bind(&Editor::step_edit_status_change, this, _1), gui_context());
1127 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1128 _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context());
1129 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context());
1130 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1131 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context());
1132 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1133 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
1134 _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Editor::session_state_saved, this, _1), gui_context());
1135 _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context());
1136 _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&Editor::location_gone, this, _1), gui_context());
1137 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1138 _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display_s, this, _1), gui_context());
1139 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1141 if (Profile->get_sae()) {
1142 Timecode::BBT_Time bbt;
1146 framepos_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1);
1147 nudge_clock.set_mode(AudioClock::BBT);
1148 nudge_clock.set (pos, true, 0, AudioClock::BBT);
1151 nudge_clock.set (_session->frame_rate() * 5, true, 0, AudioClock::Timecode); // default of 5 seconds
1154 playhead_cursor->canvas_item.show ();
1156 Location* loc = _session->locations()->auto_loop_location();
1158 loc = new Location (*_session, 0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1160 if (loc->start() == loc->end()) {
1161 loc->set_end (loc->start() + 1);
1164 _session->locations()->add (loc, false);
1165 _session->set_auto_loop_location (loc);
1168 loc->set_name (_("Loop"));
1171 loc = _session->locations()->auto_punch_location();
1174 loc = new Location (*_session, 0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1176 if (loc->start() == loc->end()) {
1177 loc->set_end (loc->start() + 1);
1180 _session->locations()->add (loc, false);
1181 _session->set_auto_punch_location (loc);
1184 loc->set_name (_("Punch"));
1187 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1188 Config->map_parameters (pc);
1189 _session->config.map_parameters (pc);
1191 refresh_location_display ();
1193 restore_ruler_visibility ();
1194 //tempo_map_changed (PropertyChange (0));
1195 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1197 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1198 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1201 super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
1202 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1205 switch (_snap_type) {
1206 case SnapToRegionStart:
1207 case SnapToRegionEnd:
1208 case SnapToRegionSync:
1209 case SnapToRegionBoundary:
1210 build_region_boundary_cache ();
1217 /* register for undo history */
1218 _session->register_with_memento_command_factory(_id, this);
1220 ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated));
1222 start_updating_meters ();
1226 Editor::action_pre_activated (Glib::RefPtr<Action> const & a)
1228 if (a->get_name() == "RegionMenu") {
1229 /* When the main menu's region menu is opened, we setup the actions so that they look right
1230 in the menu. I can't find a way of getting a signal when this menu is subsequently closed,
1231 so we resensitize all region actions when the entered regionview or the region selection
1232 changes. HOWEVER we can't always resensitize on entered_regionview change because that
1233 happens after the region context menu is opened. So we set a flag here, too.
1237 sensitize_the_right_region_actions ();
1238 _last_region_menu_was_main = true;
1242 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1244 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1246 using namespace Menu_Helpers;
1247 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1250 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1254 MenuList& items (fade_context_menu.items());
1258 switch (item_type) {
1260 case FadeInHandleItem:
1261 if (arv->audio_region()->fade_in_active()) {
1262 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1264 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1267 items.push_back (SeparatorElem());
1269 if (Profile->get_sae()) {
1271 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)));
1272 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)));
1279 *_fade_in_images[FadeLinear],
1280 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)
1284 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1289 *_fade_in_images[FadeFast],
1290 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)
1293 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1298 *_fade_in_images[FadeLogB],
1299 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogB)
1302 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1307 *_fade_in_images[FadeLogA],
1308 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogA)
1311 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1316 *_fade_in_images[FadeSlow],
1317 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow)
1320 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1326 case FadeOutHandleItem:
1327 if (arv->audio_region()->fade_out_active()) {
1328 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1330 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1333 items.push_back (SeparatorElem());
1335 if (Profile->get_sae()) {
1336 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)));
1337 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)));
1343 *_fade_out_images[FadeLinear],
1344 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)
1348 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1353 *_fade_out_images[FadeFast],
1354 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)
1357 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1362 *_fade_out_images[FadeLogB],
1363 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogA)
1366 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1371 *_fade_out_images[FadeLogA],
1372 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogB)
1375 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1380 *_fade_out_images[FadeSlow],
1381 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast)
1384 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1390 fatal << _("programming error: ")
1391 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1396 fade_context_menu.popup (button, time);
1400 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1402 using namespace Menu_Helpers;
1403 Menu* (Editor::*build_menu_function)();
1406 switch (item_type) {
1408 case RegionViewName:
1409 case RegionViewNameHighlight:
1410 case LeftFrameHandle:
1411 case RightFrameHandle:
1412 if (with_selection) {
1413 build_menu_function = &Editor::build_track_selection_context_menu;
1415 build_menu_function = &Editor::build_track_region_context_menu;
1420 if (with_selection) {
1421 build_menu_function = &Editor::build_track_selection_context_menu;
1423 build_menu_function = &Editor::build_track_context_menu;
1427 case CrossfadeViewItem:
1428 build_menu_function = &Editor::build_track_crossfade_context_menu;
1432 if (clicked_routeview->track()) {
1433 build_menu_function = &Editor::build_track_context_menu;
1435 build_menu_function = &Editor::build_track_bus_context_menu;
1440 /* probably shouldn't happen but if it does, we don't care */
1444 menu = (this->*build_menu_function)();
1445 menu->set_name ("ArdourContextMenu");
1447 /* now handle specific situations */
1449 switch (item_type) {
1451 case RegionViewName:
1452 case RegionViewNameHighlight:
1453 case LeftFrameHandle:
1454 case RightFrameHandle:
1455 if (!with_selection) {
1456 if (region_edit_menu_split_item) {
1457 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1458 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1460 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1463 if (region_edit_menu_split_multichannel_item) {
1464 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1465 region_edit_menu_split_multichannel_item->set_sensitive (true);
1467 region_edit_menu_split_multichannel_item->set_sensitive (false);
1476 case CrossfadeViewItem:
1483 /* probably shouldn't happen but if it does, we don't care */
1487 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1489 /* Bounce to disk */
1491 using namespace Menu_Helpers;
1492 MenuList& edit_items = menu->items();
1494 edit_items.push_back (SeparatorElem());
1496 switch (clicked_routeview->audio_track()->freeze_state()) {
1497 case AudioTrack::NoFreeze:
1498 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1501 case AudioTrack::Frozen:
1502 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1505 case AudioTrack::UnFrozen:
1506 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1512 if (item_type == StreamItem && clicked_routeview) {
1513 clicked_routeview->build_underlay_menu(menu);
1516 /* When the region menu is opened, we setup the actions so that they look right
1519 sensitize_the_right_region_actions ();
1520 _last_region_menu_was_main = false;
1522 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1523 menu->popup (button, time);
1527 Editor::build_track_context_menu ()
1529 using namespace Menu_Helpers;
1531 MenuList& edit_items = track_context_menu.items();
1534 add_dstream_context_items (edit_items);
1535 return &track_context_menu;
1539 Editor::build_track_bus_context_menu ()
1541 using namespace Menu_Helpers;
1543 MenuList& edit_items = track_context_menu.items();
1546 add_bus_context_items (edit_items);
1547 return &track_context_menu;
1551 Editor::build_track_region_context_menu ()
1553 using namespace Menu_Helpers;
1554 MenuList& edit_items = track_region_context_menu.items();
1557 /* we've just cleared the track region context menu, so the menu that these
1558 two items were on will have disappeared; stop them dangling.
1560 region_edit_menu_split_item = 0;
1561 region_edit_menu_split_multichannel_item = 0;
1563 /* we might try to use items that are currently attached to a crossfade menu,
1566 track_crossfade_context_menu.items().clear ();
1568 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1571 boost::shared_ptr<Track> tr;
1572 boost::shared_ptr<Playlist> pl;
1574 if ((tr = rtv->track())) {
1575 add_region_context_items (edit_items, tr);
1579 add_dstream_context_items (edit_items);
1581 return &track_region_context_menu;
1585 Editor::build_track_crossfade_context_menu ()
1587 using namespace Menu_Helpers;
1588 MenuList& edit_items = track_crossfade_context_menu.items();
1589 edit_items.clear ();
1591 /* we might try to use items that are currently attached to a crossfade menu,
1594 track_region_context_menu.items().clear ();
1596 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1599 boost::shared_ptr<Track> tr;
1600 boost::shared_ptr<Playlist> pl;
1601 boost::shared_ptr<AudioPlaylist> apl;
1603 if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1605 AudioPlaylist::Crossfades xfades;
1609 /* The xfade menu is a bit of a special case, as we always use the mouse position
1610 to decide whether or not to display it (rather than the edit point). No particularly
1611 strong reasons for this, other than it is a bit surprising to right-click on a xfade
1614 mouse_frame (where, ignored);
1615 apl->crossfades_at (where, xfades);
1617 bool const many = xfades.size() > 1;
1619 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1620 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1623 add_region_context_items (edit_items, tr);
1627 add_dstream_context_items (edit_items);
1629 return &track_crossfade_context_menu;
1633 Editor::analyze_region_selection ()
1635 if (analysis_window == 0) {
1636 analysis_window = new AnalysisWindow();
1639 analysis_window->set_session(_session);
1641 analysis_window->show_all();
1644 analysis_window->set_regionmode();
1645 analysis_window->analyze();
1647 analysis_window->present();
1651 Editor::analyze_range_selection()
1653 if (analysis_window == 0) {
1654 analysis_window = new AnalysisWindow();
1657 analysis_window->set_session(_session);
1659 analysis_window->show_all();
1662 analysis_window->set_rangemode();
1663 analysis_window->analyze();
1665 analysis_window->present();
1669 Editor::build_track_selection_context_menu ()
1671 using namespace Menu_Helpers;
1672 MenuList& edit_items = track_selection_context_menu.items();
1673 edit_items.clear ();
1675 add_selection_context_items (edit_items);
1676 // edit_items.push_back (SeparatorElem());
1677 // add_dstream_context_items (edit_items);
1679 return &track_selection_context_menu;
1682 /** Add context menu items relevant to crossfades.
1683 * @param edit_items List to add the items to.
1686 Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1688 using namespace Menu_Helpers;
1689 Menu *xfade_menu = manage (new Menu);
1690 MenuList& items = xfade_menu->items();
1691 xfade_menu->set_name ("ArdourContextMenu");
1694 if (xfade->active()) {
1700 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1701 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1703 if (xfade->can_follow_overlap()) {
1705 if (xfade->following_overlap()) {
1706 str = _("Convert to Short");
1708 str = _("Convert to Full");
1711 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1715 str = xfade->out()->name();
1717 str += xfade->in()->name();
1719 str = _("Crossfade");
1722 edit_items.push_back (MenuElem (str, *xfade_menu));
1723 edit_items.push_back (SeparatorElem());
1727 Editor::xfade_edit_left_region ()
1729 if (clicked_crossfadeview) {
1730 clicked_crossfadeview->left_view.show_region_editor ();
1735 Editor::xfade_edit_right_region ()
1737 if (clicked_crossfadeview) {
1738 clicked_crossfadeview->right_view.show_region_editor ();
1743 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::shared_ptr<Track> track)
1745 using namespace Menu_Helpers;
1747 /* OK, stick the region submenu at the top of the list, and then add
1751 RegionSelection rs = get_regions_from_selection_and_entered ();
1753 string::size_type pos = 0;
1754 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1756 /* we have to hack up the region name because "_" has a special
1757 meaning for menu titles.
1760 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1761 menu_item_name.replace (pos, 1, "__");
1765 if (_popup_region_menu_item == 0) {
1766 _popup_region_menu_item = new MenuItem (menu_item_name);
1767 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1768 _popup_region_menu_item->show ();
1770 _popup_region_menu_item->set_label (menu_item_name);
1773 /* Use the mouse position rather than the edit point to decide whether to show the `choose top region'
1774 dialogue. If we use the edit point it gets a bit messy because the user still has to click over
1775 *some* region in order to get the region context menu stuff to be displayed at all.
1780 mouse_frame (mouse, ignored);
1782 edit_items.push_back (*_popup_region_menu_item);
1783 if (track->playlist()->count_regions_at (mouse) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1784 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region")->create_menu_item ()));
1786 edit_items.push_back (SeparatorElem());
1789 /** Add context menu items relevant to selection ranges.
1790 * @param edit_items List to add the items to.
1793 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1795 using namespace Menu_Helpers;
1797 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1798 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1800 edit_items.push_back (SeparatorElem());
1801 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1803 if (!selection->regions.empty()) {
1804 edit_items.push_back (SeparatorElem());
1805 edit_items.push_back (MenuElem (_("Extend Range to End of Region"), sigc::bind (sigc::mem_fun(*this, &Editor::extend_selection_to_end_of_region), false)));
1806 edit_items.push_back (MenuElem (_("Extend Range to Start of Region"), sigc::bind (sigc::mem_fun(*this, &Editor::extend_selection_to_start_of_region), false)));
1809 edit_items.push_back (SeparatorElem());
1810 edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection)));
1811 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1813 edit_items.push_back (SeparatorElem());
1814 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1816 edit_items.push_back (SeparatorElem());
1817 edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1818 edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1820 edit_items.push_back (SeparatorElem());
1821 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1823 edit_items.push_back (SeparatorElem());
1824 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1825 edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1826 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1828 edit_items.push_back (SeparatorElem());
1829 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1830 edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1831 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1832 edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1833 edit_items.push_back (MenuElem (_("Export Range"), sigc::mem_fun(*this, &Editor::export_selection)));
1838 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1840 using namespace Menu_Helpers;
1844 Menu *play_menu = manage (new Menu);
1845 MenuList& play_items = play_menu->items();
1846 play_menu->set_name ("ArdourContextMenu");
1848 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1849 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1850 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1851 play_items.push_back (SeparatorElem());
1852 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
1854 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1858 Menu *select_menu = manage (new Menu);
1859 MenuList& select_items = select_menu->items();
1860 select_menu->set_name ("ArdourContextMenu");
1862 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1863 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1864 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1865 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1866 select_items.push_back (SeparatorElem());
1867 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
1868 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
1869 select_items.push_back (SeparatorElem());
1870 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1871 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1872 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1873 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1874 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
1875 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
1876 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
1878 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1882 Menu *cutnpaste_menu = manage (new Menu);
1883 MenuList& cutnpaste_items = cutnpaste_menu->items();
1884 cutnpaste_menu->set_name ("ArdourContextMenu");
1886 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1887 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1888 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1890 cutnpaste_items.push_back (SeparatorElem());
1892 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
1893 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
1895 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
1897 /* Adding new material */
1899 edit_items.push_back (SeparatorElem());
1900 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
1901 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
1905 Menu *nudge_menu = manage (new Menu());
1906 MenuList& nudge_items = nudge_menu->items();
1907 nudge_menu->set_name ("ArdourContextMenu");
1909 edit_items.push_back (SeparatorElem());
1910 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1911 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1912 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1913 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1915 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1919 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
1921 using namespace Menu_Helpers;
1925 Menu *play_menu = manage (new Menu);
1926 MenuList& play_items = play_menu->items();
1927 play_menu->set_name ("ArdourContextMenu");
1929 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1930 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1931 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1935 Menu *select_menu = manage (new Menu);
1936 MenuList& select_items = select_menu->items();
1937 select_menu->set_name ("ArdourContextMenu");
1939 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1940 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1941 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1942 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1943 select_items.push_back (SeparatorElem());
1944 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1945 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1946 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1947 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1949 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1953 Menu *cutnpaste_menu = manage (new Menu);
1954 MenuList& cutnpaste_items = cutnpaste_menu->items();
1955 cutnpaste_menu->set_name ("ArdourContextMenu");
1957 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1958 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1959 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1961 Menu *nudge_menu = manage (new Menu());
1962 MenuList& nudge_items = nudge_menu->items();
1963 nudge_menu->set_name ("ArdourContextMenu");
1965 edit_items.push_back (SeparatorElem());
1966 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1967 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1968 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1969 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1971 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1975 Editor::snap_type() const
1981 Editor::snap_mode() const
1987 Editor::set_snap_to (SnapType st)
1989 unsigned int snap_ind = (unsigned int)st;
1993 if (snap_ind > snap_type_strings.size() - 1) {
1995 _snap_type = (SnapType)snap_ind;
1998 string str = snap_type_strings[snap_ind];
2000 if (str != snap_type_selector.get_active_text()) {
2001 snap_type_selector.set_active_text (str);
2006 switch (_snap_type) {
2007 case SnapToBeatDiv32:
2008 case SnapToBeatDiv28:
2009 case SnapToBeatDiv24:
2010 case SnapToBeatDiv20:
2011 case SnapToBeatDiv16:
2012 case SnapToBeatDiv14:
2013 case SnapToBeatDiv12:
2014 case SnapToBeatDiv10:
2015 case SnapToBeatDiv8:
2016 case SnapToBeatDiv7:
2017 case SnapToBeatDiv6:
2018 case SnapToBeatDiv5:
2019 case SnapToBeatDiv4:
2020 case SnapToBeatDiv3:
2021 case SnapToBeatDiv2:
2022 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
2023 update_tempo_based_rulers ();
2026 case SnapToRegionStart:
2027 case SnapToRegionEnd:
2028 case SnapToRegionSync:
2029 case SnapToRegionBoundary:
2030 build_region_boundary_cache ();
2038 SnapChanged (); /* EMIT SIGNAL */
2042 Editor::set_snap_mode (SnapMode mode)
2045 string str = snap_mode_strings[(int)mode];
2047 if (str != snap_mode_selector.get_active_text ()) {
2048 snap_mode_selector.set_active_text (str);
2054 Editor::set_edit_point_preference (EditPoint ep, bool force)
2056 bool changed = (_edit_point != ep);
2059 string str = edit_point_strings[(int)ep];
2061 if (str != edit_point_selector.get_active_text ()) {
2062 edit_point_selector.set_active_text (str);
2065 set_canvas_cursor ();
2067 if (!force && !changed) {
2071 const char* action=NULL;
2073 switch (_edit_point) {
2074 case EditAtPlayhead:
2075 action = "edit-at-playhead";
2077 case EditAtSelectedMarker:
2078 action = "edit-at-marker";
2081 action = "edit-at-mouse";
2085 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2087 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2091 bool in_track_canvas;
2093 if (!mouse_frame (foo, in_track_canvas)) {
2094 in_track_canvas = false;
2097 reset_canvas_action_sensitivity (in_track_canvas);
2103 Editor::set_state (const XMLNode& node, int /*version*/)
2105 const XMLProperty* prop;
2107 int x, y, xoff, yoff;
2110 if ((prop = node.property ("id")) != 0) {
2111 _id = prop->value ();
2114 g.base_width = default_width;
2115 g.base_height = default_height;
2121 if ((geometry = find_named_node (node, "geometry")) != 0) {
2125 if ((prop = geometry->property("x_size")) == 0) {
2126 prop = geometry->property ("x-size");
2129 g.base_width = atoi(prop->value());
2131 if ((prop = geometry->property("y_size")) == 0) {
2132 prop = geometry->property ("y-size");
2135 g.base_height = atoi(prop->value());
2138 if ((prop = geometry->property ("x_pos")) == 0) {
2139 prop = geometry->property ("x-pos");
2142 x = atoi (prop->value());
2145 if ((prop = geometry->property ("y_pos")) == 0) {
2146 prop = geometry->property ("y-pos");
2149 y = atoi (prop->value());
2152 if ((prop = geometry->property ("x_off")) == 0) {
2153 prop = geometry->property ("x-off");
2156 xoff = atoi (prop->value());
2158 if ((prop = geometry->property ("y_off")) == 0) {
2159 prop = geometry->property ("y-off");
2162 yoff = atoi (prop->value());
2166 set_default_size (g.base_width, g.base_height);
2169 if (_session && (prop = node.property ("playhead"))) {
2171 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2172 playhead_cursor->set_position (pos);
2174 playhead_cursor->set_position (0);
2177 if ((prop = node.property ("mixer-width"))) {
2178 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2181 if ((prop = node.property ("zoom-focus"))) {
2182 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2185 if ((prop = node.property ("zoom"))) {
2186 reset_zoom (PBD::atof (prop->value()));
2188 reset_zoom (frames_per_unit);
2191 if ((prop = node.property ("snap-to"))) {
2192 set_snap_to ((SnapType) atoi (prop->value()));
2195 if ((prop = node.property ("snap-mode"))) {
2196 set_snap_mode ((SnapMode) atoi (prop->value()));
2199 if ((prop = node.property ("mouse-mode"))) {
2200 MouseMode m = str2mousemode(prop->value());
2201 set_mouse_mode (m, true);
2203 set_mouse_mode (MouseObject, true);
2206 if ((prop = node.property ("left-frame")) != 0) {
2208 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2209 reset_x_origin (pos);
2213 if ((prop = node.property ("y-origin")) != 0) {
2214 reset_y_origin (atof (prop->value ()));
2217 if ((prop = node.property ("internal-edit"))) {
2218 bool yn = string_is_affirmative (prop->value());
2219 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2221 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2222 tact->set_active (!yn);
2223 tact->set_active (yn);
2227 if ((prop = node.property ("join-object-range"))) {
2228 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2231 if ((prop = node.property ("edit-point"))) {
2232 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2235 if ((prop = node.property ("show-measures"))) {
2236 bool yn = string_is_affirmative (prop->value());
2237 _show_measures = yn;
2238 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2240 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2241 /* do it twice to force the change */
2242 tact->set_active (!yn);
2243 tact->set_active (yn);
2247 if ((prop = node.property ("follow-playhead"))) {
2248 bool yn = string_is_affirmative (prop->value());
2249 set_follow_playhead (yn);
2250 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2252 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2253 if (tact->get_active() != yn) {
2254 tact->set_active (yn);
2259 if ((prop = node.property ("stationary-playhead"))) {
2260 bool yn = (prop->value() == "yes");
2261 set_stationary_playhead (yn);
2262 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2264 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2265 if (tact->get_active() != yn) {
2266 tact->set_active (yn);
2271 if ((prop = node.property ("region-list-sort-type"))) {
2272 RegionListSortType st;
2273 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2276 if ((prop = node.property ("xfades-visible"))) {
2277 bool yn = string_is_affirmative (prop->value());
2278 _xfade_visibility = !yn;
2279 // set_xfade_visibility (yn);
2282 if ((prop = node.property ("show-editor-mixer"))) {
2284 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2287 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2288 bool yn = string_is_affirmative (prop->value());
2290 /* do it twice to force the change */
2292 tact->set_active (!yn);
2293 tact->set_active (yn);
2296 if ((prop = node.property ("show-editor-list"))) {
2298 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2301 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2302 bool yn = string_is_affirmative (prop->value());
2304 /* do it twice to force the change */
2306 tact->set_active (!yn);
2307 tact->set_active (yn);
2310 if ((prop = node.property (X_("editor-list-page")))) {
2311 _the_notebook.set_current_page (atoi (prop->value ()));
2314 if ((prop = node.property (X_("show-marker-lines")))) {
2315 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2317 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2318 bool yn = string_is_affirmative (prop->value ());
2320 tact->set_active (!yn);
2321 tact->set_active (yn);
2324 XMLNodeList children = node.children ();
2325 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2326 selection->set_state (**i, Stateful::current_state_version);
2327 _regions->set_state (**i);
2334 Editor::get_state ()
2336 XMLNode* node = new XMLNode ("Editor");
2339 _id.print (buf, sizeof (buf));
2340 node->add_property ("id", buf);
2342 if (is_realized()) {
2343 Glib::RefPtr<Gdk::Window> win = get_window();
2345 int x, y, xoff, yoff, width, height;
2346 win->get_root_origin(x, y);
2347 win->get_position(xoff, yoff);
2348 win->get_size(width, height);
2350 XMLNode* geometry = new XMLNode ("geometry");
2352 snprintf(buf, sizeof(buf), "%d", width);
2353 geometry->add_property("x-size", string(buf));
2354 snprintf(buf, sizeof(buf), "%d", height);
2355 geometry->add_property("y-size", string(buf));
2356 snprintf(buf, sizeof(buf), "%d", x);
2357 geometry->add_property("x-pos", string(buf));
2358 snprintf(buf, sizeof(buf), "%d", y);
2359 geometry->add_property("y-pos", string(buf));
2360 snprintf(buf, sizeof(buf), "%d", xoff);
2361 geometry->add_property("x-off", string(buf));
2362 snprintf(buf, sizeof(buf), "%d", yoff);
2363 geometry->add_property("y-off", string(buf));
2364 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2365 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2366 geometry->add_property("notebook-shrunk", _notebook_shrunk ? "1" : "0");
2367 snprintf(buf,sizeof(buf), "%d",pre_maximal_horizontal_pane_position);
2368 geometry->add_property("pre-maximal-horizontal-pane-position", string(buf));
2369 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2370 geometry->add_property("edit-vertical-pane-pos", string(buf));
2372 node->add_child_nocopy (*geometry);
2375 maybe_add_mixer_strip_width (*node);
2377 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2378 node->add_property ("zoom-focus", buf);
2379 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2380 node->add_property ("zoom", buf);
2381 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2382 node->add_property ("snap-to", buf);
2383 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2384 node->add_property ("snap-mode", buf);
2386 node->add_property ("edit-point", enum_2_string (_edit_point));
2388 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2389 node->add_property ("playhead", buf);
2390 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2391 node->add_property ("left-frame", buf);
2392 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2393 node->add_property ("y-origin", buf);
2395 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2396 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2397 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2398 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2399 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2400 node->add_property ("mouse-mode", enum2str(mouse_mode));
2401 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2402 node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2404 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2406 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2407 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2410 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2412 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2413 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2416 snprintf (buf, sizeof (buf), "%d", _the_notebook.get_current_page ());
2417 node->add_property (X_("editor-list-page"), buf);
2419 if (button_bindings) {
2420 XMLNode* bb = new XMLNode (X_("Buttons"));
2421 button_bindings->save (*bb);
2422 node->add_child_nocopy (*bb);
2425 node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no");
2427 node->add_child_nocopy (selection->get_state ());
2428 node->add_child_nocopy (_regions->get_state ());
2435 /** @param y y offset from the top of all trackviews.
2436 * @return pair: TimeAxisView that y is over, layer index.
2437 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2438 * in stacked region display mode, otherwise 0.
2440 std::pair<TimeAxisView *, layer_t>
2441 Editor::trackview_by_y_position (double y)
2443 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2445 std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
2451 return std::make_pair ( (TimeAxisView *) 0, 0);
2454 /** Snap a position to the grid, if appropriate, taking into account current
2455 * grid settings and also the state of any snap modifier keys that may be pressed.
2456 * @param start Position to snap.
2457 * @param event Event to get current key modifier information from, or 0.
2460 Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2462 if (!_session || !event) {
2466 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2467 if (_snap_mode == SnapOff) {
2468 snap_to_internal (start, direction, for_mark);
2471 if (_snap_mode != SnapOff) {
2472 snap_to_internal (start, direction, for_mark);
2478 Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark)
2480 if (!_session || _snap_mode == SnapOff) {
2484 snap_to_internal (start, direction, for_mark);
2488 Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /*for_mark*/)
2490 const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2491 framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2493 switch (_snap_type) {
2494 case SnapToTimecodeFrame:
2495 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2496 start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2498 start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2502 case SnapToTimecodeSeconds:
2503 if (_session->config.get_timecode_offset_negative()) {
2504 start += _session->config.get_timecode_offset ();
2506 start -= _session->config.get_timecode_offset ();
2508 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2509 start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2511 start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2514 if (_session->config.get_timecode_offset_negative()) {
2515 start -= _session->config.get_timecode_offset ();
2517 start += _session->config.get_timecode_offset ();
2521 case SnapToTimecodeMinutes:
2522 if (_session->config.get_timecode_offset_negative()) {
2523 start += _session->config.get_timecode_offset ();
2525 start -= _session->config.get_timecode_offset ();
2527 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2528 start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2530 start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2532 if (_session->config.get_timecode_offset_negative()) {
2533 start -= _session->config.get_timecode_offset ();
2535 start += _session->config.get_timecode_offset ();
2539 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2545 Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark)
2547 const framepos_t one_second = _session->frame_rate();
2548 const framepos_t one_minute = _session->frame_rate() * 60;
2549 framepos_t presnap = start;
2553 switch (_snap_type) {
2554 case SnapToTimecodeFrame:
2555 case SnapToTimecodeSeconds:
2556 case SnapToTimecodeMinutes:
2557 return timecode_snap_to_internal (start, direction, for_mark);
2560 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2561 start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2563 start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2568 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2569 start = (framepos_t) ceil ((double) start / one_second) * one_second;
2571 start = (framepos_t) floor ((double) start / one_second) * one_second;
2576 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2577 start = (framepos_t) ceil ((double) start / one_minute) * one_minute;
2579 start = (framepos_t) floor ((double) start / one_minute) * one_minute;
2584 start = _session->tempo_map().round_to_bar (start, direction);
2588 start = _session->tempo_map().round_to_beat (start, direction);
2591 case SnapToBeatDiv32:
2592 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2594 case SnapToBeatDiv28:
2595 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2597 case SnapToBeatDiv24:
2598 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2600 case SnapToBeatDiv20:
2601 start = _session->tempo_map().round_to_beat_subdivision (start, 20, direction);
2603 case SnapToBeatDiv16:
2604 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2606 case SnapToBeatDiv14:
2607 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2609 case SnapToBeatDiv12:
2610 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2612 case SnapToBeatDiv10:
2613 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2615 case SnapToBeatDiv8:
2616 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2618 case SnapToBeatDiv7:
2619 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2621 case SnapToBeatDiv6:
2622 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2624 case SnapToBeatDiv5:
2625 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2627 case SnapToBeatDiv4:
2628 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2630 case SnapToBeatDiv3:
2631 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2633 case SnapToBeatDiv2:
2634 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2642 _session->locations()->marks_either_side (start, before, after);
2644 if (before == max_framepos) {
2646 } else if (after == max_framepos) {
2648 } else if (before != max_framepos && after != max_framepos) {
2649 /* have before and after */
2650 if ((start - before) < (after - start)) {
2659 case SnapToRegionStart:
2660 case SnapToRegionEnd:
2661 case SnapToRegionSync:
2662 case SnapToRegionBoundary:
2663 if (!region_boundary_cache.empty()) {
2665 vector<framepos_t>::iterator prev = region_boundary_cache.end ();
2666 vector<framepos_t>::iterator next = region_boundary_cache.end ();
2668 if (direction > 0) {
2669 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2671 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2674 if (next != region_boundary_cache.begin ()) {
2679 framepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2680 framepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2682 if (start > (p + n) / 2) {
2691 switch (_snap_mode) {
2697 if (presnap > start) {
2698 if (presnap > (start + unit_to_frame(snap_threshold))) {
2702 } else if (presnap < start) {
2703 if (presnap < (start - unit_to_frame(snap_threshold))) {
2709 /* handled at entry */
2717 Editor::setup_toolbar ()
2721 /* Mode Buttons (tool selection) */
2723 mouse_move_button.set_relief(Gtk::RELIEF_NONE);
2724 mouse_select_button.set_relief(Gtk::RELIEF_NONE);
2725 mouse_gain_button.set_relief(Gtk::RELIEF_NONE);
2726 mouse_zoom_button.set_relief(Gtk::RELIEF_NONE);
2727 mouse_timefx_button.set_relief(Gtk::RELIEF_NONE);
2728 mouse_audition_button.set_relief(Gtk::RELIEF_NONE);
2729 // internal_edit_button.set_relief(Gtk::RELIEF_NONE);
2730 join_object_range_button.set_relief(Gtk::RELIEF_NONE);
2732 HBox* mode_box = manage(new HBox);
2733 mode_box->set_border_width (2);
2734 mode_box->set_spacing(4);
2736 /* table containing mode buttons */
2738 HBox* mouse_mode_button_box = manage (new HBox ());
2740 if (Profile->get_sae()) {
2741 mouse_mode_button_box->pack_start (mouse_move_button);
2743 mouse_mode_button_box->pack_start (mouse_move_button);
2744 mouse_mode_button_box->pack_start (join_object_range_button);
2745 mouse_mode_button_box->pack_start (mouse_select_button);
2748 mouse_mode_button_box->pack_start (mouse_zoom_button);
2750 if (!Profile->get_sae()) {
2751 mouse_mode_button_box->pack_start (mouse_gain_button);
2754 mouse_mode_button_box->pack_start (mouse_timefx_button);
2755 mouse_mode_button_box->pack_start (mouse_audition_button);
2756 mouse_mode_button_box->pack_start (internal_edit_button);
2758 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2759 if (!Profile->get_sae()) {
2760 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2762 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2764 edit_mode_selector.set_name ("EditModeSelector");
2765 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2766 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2768 mode_box->pack_start (edit_mode_selector);
2769 mode_box->pack_start (*mouse_mode_button_box);
2771 _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2772 _mouse_mode_tearoff->set_name ("MouseModeBase");
2773 _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2775 if (Profile->get_sae()) {
2776 _mouse_mode_tearoff->set_can_be_torn_off (false);
2779 _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2780 &_mouse_mode_tearoff->tearoff_window()));
2781 _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2782 &_mouse_mode_tearoff->tearoff_window(), 1));
2783 _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2784 &_mouse_mode_tearoff->tearoff_window()));
2785 _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2786 &_mouse_mode_tearoff->tearoff_window(), 1));
2788 mouse_move_button.set_mode (false);
2789 mouse_select_button.set_mode (false);
2790 mouse_gain_button.set_mode (false);
2791 mouse_zoom_button.set_mode (false);
2792 mouse_timefx_button.set_mode (false);
2793 mouse_audition_button.set_mode (false);
2794 join_object_range_button.set_mode (false);
2796 mouse_move_button.set_name ("MouseModeButton");
2797 mouse_select_button.set_name ("MouseModeButton");
2798 mouse_gain_button.set_name ("MouseModeButton");
2799 mouse_zoom_button.set_name ("MouseModeButton");
2800 mouse_timefx_button.set_name ("MouseModeButton");
2801 mouse_audition_button.set_name ("MouseModeButton");
2802 internal_edit_button.set_name ("MouseModeButton");
2803 join_object_range_button.set_name ("MouseModeButton");
2805 mouse_move_button.unset_flags (CAN_FOCUS);
2806 mouse_select_button.unset_flags (CAN_FOCUS);
2807 mouse_gain_button.unset_flags (CAN_FOCUS);
2808 mouse_zoom_button.unset_flags (CAN_FOCUS);
2809 mouse_timefx_button.unset_flags (CAN_FOCUS);
2810 mouse_audition_button.unset_flags (CAN_FOCUS);
2811 internal_edit_button.unset_flags (CAN_FOCUS);
2812 join_object_range_button.unset_flags (CAN_FOCUS);
2816 _zoom_box.set_spacing (1);
2817 _zoom_box.set_border_width (0);
2819 zoom_in_button.set_name ("EditorTimeButton");
2820 zoom_in_button.set_image (*(manage (new Image (::get_icon ("zoom_in")))));
2821 zoom_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), false));
2823 zoom_out_button.set_name ("EditorTimeButton");
2824 zoom_out_button.set_image (*(manage (new Image (::get_icon ("zoom_out")))));
2825 zoom_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), true));
2827 zoom_out_full_button.set_name ("EditorTimeButton");
2828 zoom_out_full_button.set_image (*(manage (new Image (::get_icon ("zoom_full")))));
2829 zoom_out_full_button.signal_clicked().connect (sigc::mem_fun(*this, &Editor::temporal_zoom_session));
2831 zoom_focus_selector.set_name ("ZoomFocusSelector");
2832 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
2833 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2835 _zoom_box.pack_start (zoom_out_button, false, false);
2836 _zoom_box.pack_start (zoom_in_button, false, false);
2837 _zoom_box.pack_start (zoom_out_full_button, false, false);
2839 _zoom_box.pack_start (zoom_focus_selector);
2841 /* Track zoom buttons */
2842 tav_expand_button.set_name ("TrackHeightButton");
2843 tav_expand_button.set_size_request (-1, 20);
2844 tav_expand_button.add (*(manage (new Image (::get_icon ("tav_exp")))));
2845 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("expand-tracks"));
2846 act->connect_proxy (tav_expand_button);
2848 tav_shrink_button.set_name ("TrackHeightButton");
2849 tav_shrink_button.set_size_request (-1, 20);
2850 tav_shrink_button.add (*(manage (new Image (::get_icon ("tav_shrink")))));
2851 act = ActionManager::get_action (X_("Editor"), X_("shrink-tracks"));
2852 act->connect_proxy (tav_shrink_button);
2854 _zoom_box.pack_start (tav_shrink_button);
2855 _zoom_box.pack_start (tav_expand_button);
2857 _zoom_tearoff = manage (new TearOff (_zoom_box));
2859 _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2860 &_zoom_tearoff->tearoff_window()));
2861 _zoom_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2862 &_zoom_tearoff->tearoff_window(), 0));
2863 _zoom_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2864 &_zoom_tearoff->tearoff_window()));
2865 _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2866 &_zoom_tearoff->tearoff_window(), 0));
2868 snap_box.set_spacing (1);
2869 snap_box.set_border_width (2);
2871 snap_type_selector.set_name ("SnapTypeSelector");
2872 set_popdown_strings (snap_type_selector, snap_type_strings, true);
2873 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2875 snap_mode_selector.set_name ("SnapModeSelector");
2876 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
2877 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2879 edit_point_selector.set_name ("EditPointSelector");
2880 set_popdown_strings (edit_point_selector, edit_point_strings, true);
2881 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2883 snap_box.pack_start (snap_mode_selector, false, false);
2884 snap_box.pack_start (snap_type_selector, false, false);
2885 snap_box.pack_start (edit_point_selector, false, false);
2889 HBox *nudge_box = manage (new HBox);
2890 nudge_box->set_spacing(1);
2891 nudge_box->set_border_width (2);
2893 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2894 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2896 nudge_box->pack_start (nudge_backward_button, false, false);
2897 nudge_box->pack_start (nudge_forward_button, false, false);
2898 nudge_box->pack_start (nudge_clock, false, false);
2901 /* Pack everything in... */
2903 HBox* hbox = manage (new HBox);
2904 hbox->set_spacing(10);
2906 _tools_tearoff = manage (new TearOff (*hbox));
2907 _tools_tearoff->set_name ("MouseModeBase");
2908 _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
2910 if (Profile->get_sae()) {
2911 _tools_tearoff->set_can_be_torn_off (false);
2914 _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2915 &_tools_tearoff->tearoff_window()));
2916 _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2917 &_tools_tearoff->tearoff_window(), 0));
2918 _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2919 &_tools_tearoff->tearoff_window()));
2920 _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2921 &_tools_tearoff->tearoff_window(), 0));
2923 toolbar_hbox.set_spacing (10);
2924 toolbar_hbox.set_border_width (1);
2926 toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
2927 toolbar_hbox.pack_start (*_zoom_tearoff, false, false);
2928 toolbar_hbox.pack_start (*_tools_tearoff, false, false);
2930 hbox->pack_start (snap_box, false, false);
2931 hbox->pack_start (*nudge_box, false, false);
2932 hbox->pack_start (panic_box, false, false);
2936 toolbar_base.set_name ("ToolBarBase");
2937 toolbar_base.add (toolbar_hbox);
2939 _toolbar_viewport.add (toolbar_base);
2940 /* stick to the required height but allow width to vary if there's not enough room */
2941 _toolbar_viewport.set_size_request (1, -1);
2943 toolbar_frame.set_shadow_type (SHADOW_OUT);
2944 toolbar_frame.set_name ("BaseFrame");
2945 toolbar_frame.add (_toolbar_viewport);
2947 DPIReset.connect (sigc::mem_fun (*this, &Editor::resize_text_widgets));
2951 Editor::setup_tooltips ()
2953 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
2954 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain"));
2955 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
2956 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
2957 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2958 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
2959 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
2960 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
2961 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
2962 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
2963 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
2964 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
2965 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
2966 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
2967 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
2968 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
2969 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
2970 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
2971 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
2972 ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
2973 ARDOUR_UI::instance()->set_tip (midi_panic_button, _("Send note off and reset controller messages on all MIDI channels"));
2974 ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode"));
2978 Editor::midi_panic ()
2980 cerr << "MIDI panic\n";
2983 _session->midi_panic();
2988 Editor::setup_midi_toolbar ()
2992 /* Midi sound notes */
2993 midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
2994 midi_sound_notes.set_relief(Gtk::RELIEF_NONE);
2995 midi_sound_notes.unset_flags (CAN_FOCUS);
2999 act = ActionManager::get_action (X_("MIDI"), X_("panic"));
3000 midi_panic_button.set_name("MidiPanicButton");
3001 act->connect_proxy (midi_panic_button);
3003 panic_box.pack_start (midi_sound_notes , true, true);
3004 panic_box.pack_start (midi_panic_button, true, true);
3008 Editor::convert_drop_to_paths (
3009 vector<string>& paths,
3010 const RefPtr<Gdk::DragContext>& /*context*/,
3013 const SelectionData& data,
3017 if (_session == 0) {
3021 vector<string> uris = data.get_uris();
3025 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3026 are actually URI lists. So do it by hand.
3029 if (data.get_target() != "text/plain") {
3033 /* Parse the "uri-list" format that Nautilus provides,
3034 where each pathname is delimited by \r\n.
3036 THERE MAY BE NO NULL TERMINATING CHAR!!!
3039 string txt = data.get_text();
3043 p = (const char *) malloc (txt.length() + 1);
3044 txt.copy ((char *) p, txt.length(), 0);
3045 ((char*)p)[txt.length()] = '\0';
3051 while (g_ascii_isspace (*p))
3055 while (*q && (*q != '\n') && (*q != '\r')) {
3062 while (q > p && g_ascii_isspace (*q))
3067 uris.push_back (string (p, q - p + 1));
3071 p = strchr (p, '\n');
3083 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3085 if ((*i).substr (0,7) == "file://") {
3088 PBD::url_decode (p);
3090 // scan forward past three slashes
3092 string::size_type slashcnt = 0;
3093 string::size_type n = 0;
3094 string::iterator x = p.begin();
3096 while (slashcnt < 3 && x != p.end()) {
3099 } else if (slashcnt == 3) {
3106 if (slashcnt != 3 || x == p.end()) {
3107 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3111 paths.push_back (p.substr (n - 1));
3119 Editor::new_tempo_section ()
3125 Editor::map_transport_state ()
3127 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3129 if (_session && _session->transport_stopped()) {
3130 have_pending_keyboard_selection = false;
3133 update_loop_range_view (true);
3138 Editor::State::State (PublicEditor const * e)
3140 selection = new Selection (e);
3143 Editor::State::~State ()
3149 Editor::begin_reversible_command (string name)
3152 _session->begin_reversible_command (name);
3157 Editor::begin_reversible_command (GQuark q)
3160 _session->begin_reversible_command (q);
3165 Editor::commit_reversible_command ()
3168 _session->commit_reversible_command ();
3173 Editor::history_changed ()
3177 if (undo_action && _session) {
3178 if (_session->undo_depth() == 0) {
3181 label = string_compose(_("Undo (%1)"), _session->next_undo());
3183 undo_action->property_label() = label;
3186 if (redo_action && _session) {
3187 if (_session->redo_depth() == 0) {
3190 label = string_compose(_("Redo (%1)"), _session->next_redo());
3192 redo_action->property_label() = label;
3197 Editor::duplicate_dialog (bool with_dialog)
3201 if (mouse_mode == MouseRange) {
3202 if (selection->time.length() == 0) {
3207 RegionSelection rs = get_regions_from_selection_and_entered ();
3209 if (mouse_mode != MouseRange && rs.empty()) {
3215 ArdourDialog win (_("Duplicate"));
3216 Label label (_("Number of duplications:"));
3217 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3218 SpinButton spinner (adjustment, 0.0, 1);
3221 win.get_vbox()->set_spacing (12);
3222 win.get_vbox()->pack_start (hbox);
3223 hbox.set_border_width (6);
3224 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3226 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3227 place, visually. so do this by hand.
3230 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3231 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3232 spinner.grab_focus();
3238 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3239 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3240 win.set_default_response (RESPONSE_ACCEPT);
3242 win.set_position (WIN_POS_MOUSE);
3244 spinner.grab_focus ();
3246 switch (win.run ()) {
3247 case RESPONSE_ACCEPT:
3253 times = adjustment.get_value();
3256 if (mouse_mode == MouseRange) {
3257 duplicate_selection (times);
3259 duplicate_some_regions (rs, times);
3264 Editor::show_verbose_canvas_cursor ()
3266 verbose_canvas_cursor->raise_to_top();
3267 verbose_canvas_cursor->show();
3268 verbose_cursor_visible = true;
3272 Editor::hide_verbose_canvas_cursor ()
3274 verbose_canvas_cursor->hide();
3275 verbose_cursor_visible = false;
3279 Editor::clamp_verbose_cursor_x (double x)
3284 x = min (_canvas_width - 200.0, x);
3290 Editor::clamp_verbose_cursor_y (double y)
3292 if (y < canvas_timebars_vsize) {
3293 y = canvas_timebars_vsize;
3295 y = min (_canvas_height - 50, y);
3301 Editor::show_verbose_canvas_cursor_with (const string & txt, int32_t xoffset, int32_t yoffset)
3303 verbose_canvas_cursor->property_text() = txt.c_str();
3308 track_canvas->get_pointer (x, y);
3309 track_canvas->window_to_world (x, y, wx, wy);
3314 /* don't get too close to the edge */
3315 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (wx);
3316 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (wy);
3318 show_verbose_canvas_cursor ();
3322 Editor::set_verbose_canvas_cursor (const string & txt, double x, double y)
3324 verbose_canvas_cursor->property_text() = txt.c_str();
3325 /* don't get too close to the edge */
3326 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (x);
3327 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (y);
3331 Editor::set_verbose_canvas_cursor_text (const string & txt)
3333 verbose_canvas_cursor->property_text() = txt.c_str();
3337 Editor::set_edit_mode (EditMode m)
3339 Config->set_edit_mode (m);
3343 Editor::cycle_edit_mode ()
3345 switch (Config->get_edit_mode()) {
3347 if (Profile->get_sae()) {
3348 Config->set_edit_mode (Lock);
3350 Config->set_edit_mode (Splice);
3354 Config->set_edit_mode (Lock);
3357 Config->set_edit_mode (Slide);
3363 Editor::edit_mode_selection_done ()
3365 string s = edit_mode_selector.get_active_text ();
3368 Config->set_edit_mode (string_to_edit_mode (s));
3373 Editor::snap_type_selection_done ()
3375 string choice = snap_type_selector.get_active_text();
3376 SnapType snaptype = SnapToBeat;
3378 if (choice == _("Beats/2")) {
3379 snaptype = SnapToBeatDiv2;
3380 } else if (choice == _("Beats/3")) {
3381 snaptype = SnapToBeatDiv3;
3382 } else if (choice == _("Beats/4")) {
3383 snaptype = SnapToBeatDiv4;
3384 } else if (choice == _("Beats/5")) {
3385 snaptype = SnapToBeatDiv5;
3386 } else if (choice == _("Beats/6")) {
3387 snaptype = SnapToBeatDiv6;
3388 } else if (choice == _("Beats/7")) {
3389 snaptype = SnapToBeatDiv7;
3390 } else if (choice == _("Beats/8")) {
3391 snaptype = SnapToBeatDiv8;
3392 } else if (choice == _("Beats/10")) {
3393 snaptype = SnapToBeatDiv10;
3394 } else if (choice == _("Beats/12")) {
3395 snaptype = SnapToBeatDiv12;
3396 } else if (choice == _("Beats/14")) {
3397 snaptype = SnapToBeatDiv14;
3398 } else if (choice == _("Beats/16")) {
3399 snaptype = SnapToBeatDiv16;
3400 } else if (choice == _("Beats/20")) {
3401 snaptype = SnapToBeatDiv20;
3402 } else if (choice == _("Beats/24")) {
3403 snaptype = SnapToBeatDiv24;
3404 } else if (choice == _("Beats/28")) {
3405 snaptype = SnapToBeatDiv28;
3406 } else if (choice == _("Beats/32")) {
3407 snaptype = SnapToBeatDiv32;
3408 } else if (choice == _("Beats")) {
3409 snaptype = SnapToBeat;
3410 } else if (choice == _("Bars")) {
3411 snaptype = SnapToBar;
3412 } else if (choice == _("Marks")) {
3413 snaptype = SnapToMark;
3414 } else if (choice == _("Region starts")) {
3415 snaptype = SnapToRegionStart;
3416 } else if (choice == _("Region ends")) {
3417 snaptype = SnapToRegionEnd;
3418 } else if (choice == _("Region bounds")) {
3419 snaptype = SnapToRegionBoundary;
3420 } else if (choice == _("Region syncs")) {
3421 snaptype = SnapToRegionSync;
3422 } else if (choice == _("CD Frames")) {
3423 snaptype = SnapToCDFrame;
3424 } else if (choice == _("Timecode Frames")) {
3425 snaptype = SnapToTimecodeFrame;
3426 } else if (choice == _("Timecode Seconds")) {
3427 snaptype = SnapToTimecodeSeconds;
3428 } else if (choice == _("Timecode Minutes")) {
3429 snaptype = SnapToTimecodeMinutes;
3430 } else if (choice == _("Seconds")) {
3431 snaptype = SnapToSeconds;
3432 } else if (choice == _("Minutes")) {
3433 snaptype = SnapToMinutes;
3436 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3438 ract->set_active ();
3443 Editor::snap_mode_selection_done ()
3445 string choice = snap_mode_selector.get_active_text();
3446 SnapMode mode = SnapNormal;
3448 if (choice == _("No Grid")) {
3450 } else if (choice == _("Grid")) {
3452 } else if (choice == _("Magnetic")) {
3453 mode = SnapMagnetic;
3456 RefPtr<RadioAction> ract = snap_mode_action (mode);
3459 ract->set_active (true);
3464 Editor::cycle_edit_point (bool with_marker)
3466 switch (_edit_point) {
3468 set_edit_point_preference (EditAtPlayhead);
3470 case EditAtPlayhead:
3472 set_edit_point_preference (EditAtSelectedMarker);
3474 set_edit_point_preference (EditAtMouse);
3477 case EditAtSelectedMarker:
3478 set_edit_point_preference (EditAtMouse);
3484 Editor::edit_point_selection_done ()
3486 string choice = edit_point_selector.get_active_text();
3487 EditPoint ep = EditAtSelectedMarker;
3489 if (choice == _("Marker")) {
3490 set_edit_point_preference (EditAtSelectedMarker);
3491 } else if (choice == _("Playhead")) {
3492 set_edit_point_preference (EditAtPlayhead);
3494 set_edit_point_preference (EditAtMouse);
3497 RefPtr<RadioAction> ract = edit_point_action (ep);
3500 ract->set_active (true);
3505 Editor::zoom_focus_selection_done ()
3507 string choice = zoom_focus_selector.get_active_text();
3508 ZoomFocus focus_type = ZoomFocusLeft;
3510 if (choice == _("Left")) {
3511 focus_type = ZoomFocusLeft;
3512 } else if (choice == _("Right")) {
3513 focus_type = ZoomFocusRight;
3514 } else if (choice == _("Center")) {
3515 focus_type = ZoomFocusCenter;
3516 } else if (choice == _("Playhead")) {
3517 focus_type = ZoomFocusPlayhead;
3518 } else if (choice == _("Mouse")) {
3519 focus_type = ZoomFocusMouse;
3520 } else if (choice == _("Edit point")) {
3521 focus_type = ZoomFocusEdit;
3524 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3527 ract->set_active ();
3532 Editor::edit_controls_button_release (GdkEventButton* ev)
3534 if (Keyboard::is_context_menu_event (ev)) {
3535 ARDOUR_UI::instance()->add_route (this);
3541 Editor::mouse_select_button_release (GdkEventButton* ev)
3543 /* this handles just right-clicks */
3545 if (ev->button != 3) {
3553 Editor::set_zoom_focus (ZoomFocus f)
3555 string str = zoom_focus_strings[(int)f];
3557 if (str != zoom_focus_selector.get_active_text()) {
3558 zoom_focus_selector.set_active_text (str);
3561 if (zoom_focus != f) {
3564 ZoomFocusChanged (); /* EMIT_SIGNAL */
3571 Editor::ensure_float (Window& win)
3573 win.set_transient_for (*this);
3577 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3579 /* recover or initialize pane positions. do this here rather than earlier because
3580 we don't want the positions to change the child allocations, which they seem to do.
3586 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3598 width = default_width;
3599 height = default_height;
3601 if ((geometry = find_named_node (*node, "geometry")) != 0) {
3603 prop = geometry->property ("x-size");
3605 width = atoi (prop->value());
3607 prop = geometry->property ("y-size");
3609 height = atoi (prop->value());
3613 if (which == static_cast<Paned*> (&edit_pane)) {
3615 if (done & Horizontal) {
3619 if (geometry && (prop = geometry->property ("notebook-shrunk"))) {
3620 _notebook_shrunk = string_is_affirmative (prop->value ());
3623 if (geometry && (prop = geometry->property ("pre-maximal-horizontal-pane-position"))) {
3624 pre_maximal_horizontal_pane_position = atoi (prop->value ());
3627 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3628 /* initial allocation is 90% to canvas, 10% to notebook */
3629 pos = (int) floor (alloc.get_width() * 0.90f);
3630 snprintf (buf, sizeof(buf), "%d", pos);
3632 pos = atoi (prop->value());
3635 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3636 edit_pane.set_position (pos);
3637 if (pre_maximal_horizontal_pane_position == 0) {
3638 pre_maximal_horizontal_pane_position = pos;
3642 done = (Pane) (done | Horizontal);
3644 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3646 if (done & Vertical) {
3650 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3651 /* initial allocation is 90% to canvas, 10% to summary */
3652 pos = (int) floor (alloc.get_height() * 0.90f);
3653 snprintf (buf, sizeof(buf), "%d", pos);
3655 pos = atoi (prop->value());
3658 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3659 editor_summary_pane.set_position (pos);
3660 pre_maximal_vertical_pane_position = pos;
3663 done = (Pane) (done | Vertical);
3668 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3670 if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) {
3671 top_hbox.remove (toolbar_frame);
3676 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3678 if (toolbar_frame.get_parent() == 0) {
3679 top_hbox.pack_end (toolbar_frame);
3684 Editor::set_show_measures (bool yn)
3686 if (_show_measures != yn) {
3689 if ((_show_measures = yn) == true) {
3691 tempo_lines->show();
3699 Editor::toggle_follow_playhead ()
3701 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3703 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3704 set_follow_playhead (tact->get_active());
3708 /** @param yn true to follow playhead, otherwise false.
3709 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
3712 Editor::set_follow_playhead (bool yn, bool catch_up)
3714 if (_follow_playhead != yn) {
3715 if ((_follow_playhead = yn) == true && catch_up) {
3717 reset_x_origin_to_follow_playhead ();
3724 Editor::toggle_stationary_playhead ()
3726 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3728 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3729 set_stationary_playhead (tact->get_active());
3734 Editor::set_stationary_playhead (bool yn)
3736 if (_stationary_playhead != yn) {
3737 if ((_stationary_playhead = yn) == true) {
3739 // FIXME need a 3.0 equivalent of this 2.X call
3740 // update_current_screen ();
3747 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3749 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3751 xfade->set_active (!xfade->active());
3756 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3758 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3760 xfade->set_follow_overlap (!xfade->following_overlap());
3765 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3767 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3773 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3777 switch (cew.run ()) {
3778 case RESPONSE_ACCEPT:
3785 PropertyChange all_crossfade_properties;
3786 all_crossfade_properties.add (ARDOUR::Properties::active);
3787 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3788 xfade->PropertyChanged (all_crossfade_properties);
3792 Editor::playlist_selector () const
3794 return *_playlist_selector;
3798 Editor::get_grid_type_as_beats (bool& success, framepos_t position)
3802 switch (_snap_type) {
3807 case SnapToBeatDiv32:
3810 case SnapToBeatDiv28:
3813 case SnapToBeatDiv24:
3816 case SnapToBeatDiv20:
3819 case SnapToBeatDiv16:
3822 case SnapToBeatDiv14:
3825 case SnapToBeatDiv12:
3828 case SnapToBeatDiv10:
3831 case SnapToBeatDiv8:
3834 case SnapToBeatDiv7:
3837 case SnapToBeatDiv6:
3840 case SnapToBeatDiv5:
3843 case SnapToBeatDiv4:
3846 case SnapToBeatDiv3:
3849 case SnapToBeatDiv2:
3855 return _session->tempo_map().meter_at (position).beats_per_bar();
3860 case SnapToTimecodeFrame:
3861 case SnapToTimecodeSeconds:
3862 case SnapToTimecodeMinutes:
3865 case SnapToRegionStart:
3866 case SnapToRegionEnd:
3867 case SnapToRegionSync:
3868 case SnapToRegionBoundary:
3878 Editor::get_nudge_distance (framepos_t pos, framecnt_t& next)
3882 ret = nudge_clock.current_duration (pos);
3883 next = ret + 1; /* XXXX fix me */
3889 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3891 ArdourDialog dialog (_("Playlist Deletion"));
3892 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3893 "If left alone, no audio files used by it will be cleaned.\n"
3894 "If deleted, audio files used by it alone by will cleaned."),
3897 dialog.set_position (WIN_POS_CENTER);
3898 dialog.get_vbox()->pack_start (label);
3902 dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT);
3903 dialog.add_button (_("Keep playlist"), RESPONSE_REJECT);
3904 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3906 switch (dialog.run ()) {
3907 case RESPONSE_ACCEPT:
3908 /* delete the playlist */
3912 case RESPONSE_REJECT:
3913 /* keep the playlist */
3925 Editor::audio_region_selection_covers (framepos_t where)
3927 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3928 if ((*a)->region()->covers (where)) {
3937 Editor::prepare_for_cleanup ()
3939 cut_buffer->clear_regions ();
3940 cut_buffer->clear_playlists ();
3942 selection->clear_regions ();
3943 selection->clear_playlists ();
3945 _regions->suspend_redisplay ();
3949 Editor::finish_cleanup ()
3951 _regions->resume_redisplay ();
3955 Editor::transport_loop_location()
3958 return _session->locations()->auto_loop_location();
3965 Editor::transport_punch_location()
3968 return _session->locations()->auto_punch_location();
3975 Editor::control_layout_scroll (GdkEventScroll* ev)
3977 if (Keyboard::some_magic_widget_has_focus()) {
3981 switch (ev->direction) {
3983 scroll_tracks_up_line ();
3987 case GDK_SCROLL_DOWN:
3988 scroll_tracks_down_line ();
3992 /* no left/right handling yet */
4000 Editor::session_state_saved (string)
4003 _snapshots->redisplay ();
4007 Editor::maximise_editing_space ()
4009 _mouse_mode_tearoff->set_visible (false);
4010 _tools_tearoff->set_visible (false);
4011 _zoom_tearoff->set_visible (false);
4013 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
4014 pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
4015 pre_maximal_editor_width = this->get_width ();
4016 pre_maximal_editor_height = this->get_height ();
4018 if (post_maximal_horizontal_pane_position == 0) {
4019 post_maximal_horizontal_pane_position = edit_pane.get_width();
4022 if (post_maximal_vertical_pane_position == 0) {
4023 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
4028 if (post_maximal_editor_width) {
4029 edit_pane.set_position (post_maximal_horizontal_pane_position -
4030 abs(post_maximal_editor_width - pre_maximal_editor_width));
4032 edit_pane.set_position (post_maximal_horizontal_pane_position);
4035 if (post_maximal_editor_height) {
4036 editor_summary_pane.set_position (post_maximal_vertical_pane_position -
4037 abs(post_maximal_editor_height - pre_maximal_editor_height));
4039 editor_summary_pane.set_position (post_maximal_vertical_pane_position);
4042 if (Config->get_keep_tearoffs()) {
4043 _mouse_mode_tearoff->set_visible (true);
4044 _tools_tearoff->set_visible (true);
4045 if (Config->get_show_zoom_tools ()) {
4046 _zoom_tearoff->set_visible (true);
4053 Editor::restore_editing_space ()
4055 // user changed width/height of panes during fullscreen
4057 if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
4058 post_maximal_horizontal_pane_position = edit_pane.get_position();
4061 if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
4062 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
4067 _mouse_mode_tearoff->set_visible (true);
4068 _tools_tearoff->set_visible (true);
4069 if (Config->get_show_zoom_tools ()) {
4070 _zoom_tearoff->set_visible (true);
4072 post_maximal_editor_width = this->get_width();
4073 post_maximal_editor_height = this->get_height();
4075 edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
4076 editor_summary_pane.set_position (pre_maximal_vertical_pane_position + abs(this->get_height() - pre_maximal_editor_height));
4080 * Make new playlists for a given track and also any others that belong
4081 * to the same active route group with the `edit' property.
4086 Editor::new_playlists (TimeAxisView* v)
4088 begin_reversible_command (_("new playlists"));
4089 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4090 _session->playlists->get (playlists);
4091 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4092 commit_reversible_command ();
4096 * Use a copy of the current playlist for a given track and also any others that belong
4097 * to the same active route group with the `edit' property.
4102 Editor::copy_playlists (TimeAxisView* v)
4104 begin_reversible_command (_("copy playlists"));
4105 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4106 _session->playlists->get (playlists);
4107 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4108 commit_reversible_command ();
4111 /** Clear the current playlist for a given track and also any others that belong
4112 * to the same active route group with the `edit' property.
4117 Editor::clear_playlists (TimeAxisView* v)
4119 begin_reversible_command (_("clear playlists"));
4120 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4121 _session->playlists->get (playlists);
4122 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4123 commit_reversible_command ();
4127 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4129 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4133 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4135 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4139 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4141 atv.clear_playlist ();
4145 Editor::on_key_press_event (GdkEventKey* ev)
4147 return key_press_focus_accelerator_handler (*this, ev);
4151 Editor::on_key_release_event (GdkEventKey* ev)
4153 return Gtk::Window::on_key_release_event (ev);
4154 // return key_press_focus_accelerator_handler (*this, ev);
4157 /** Queue up a change to the viewport x origin.
4158 * @param frame New x origin.
4161 Editor::reset_x_origin (framepos_t frame)
4163 queue_visual_change (frame);
4167 Editor::reset_y_origin (double y)
4169 queue_visual_change_y (y);
4173 Editor::reset_zoom (double fpu)
4175 queue_visual_change (fpu);
4179 Editor::reposition_and_zoom (framepos_t frame, double fpu)
4181 reset_x_origin (frame);
4184 if (!no_save_visual) {
4185 undo_visual_stack.push_back (current_visual_state(false));
4189 Editor::VisualState*
4190 Editor::current_visual_state (bool with_tracks)
4192 VisualState* vs = new VisualState;
4193 vs->y_position = vertical_adjustment.get_value();
4194 vs->frames_per_unit = frames_per_unit;
4195 vs->leftmost_frame = leftmost_frame;
4196 vs->zoom_focus = zoom_focus;
4199 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4200 vs->track_states.push_back (TAVState ((*i), &(*i)->get_state()));
4208 Editor::undo_visual_state ()
4210 if (undo_visual_stack.empty()) {
4214 redo_visual_stack.push_back (current_visual_state());
4216 VisualState* vs = undo_visual_stack.back();
4217 undo_visual_stack.pop_back();
4218 use_visual_state (*vs);
4222 Editor::redo_visual_state ()
4224 if (redo_visual_stack.empty()) {
4228 undo_visual_stack.push_back (current_visual_state());
4230 VisualState* vs = redo_visual_stack.back();
4231 redo_visual_stack.pop_back();
4232 use_visual_state (*vs);
4236 Editor::swap_visual_state ()
4238 if (undo_visual_stack.empty()) {
4239 redo_visual_state ();
4241 undo_visual_state ();
4246 Editor::use_visual_state (VisualState& vs)
4248 no_save_visual = true;
4250 _routes->suspend_redisplay ();
4252 vertical_adjustment.set_value (vs.y_position);
4254 set_zoom_focus (vs.zoom_focus);
4255 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4257 for (list<TAVState>::iterator i = vs.track_states.begin(); i != vs.track_states.end(); ++i) {
4258 TrackViewList::iterator t;
4260 /* check if the track still exists - it could have been deleted */
4262 if ((t = find (track_views.begin(), track_views.end(), i->first)) != track_views.end()) {
4263 (*t)->set_state (*(i->second), Stateful::loading_state_version);
4268 if (!vs.track_states.empty()) {
4269 _routes->update_visibility ();
4272 _routes->resume_redisplay ();
4274 no_save_visual = false;
4278 Editor::set_frames_per_unit (double fpu)
4280 /* this is the core function that controls the zoom level of the canvas. it is called
4281 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4284 if (fpu == frames_per_unit) {
4293 /* don't allow zooms that fit more than the maximum number
4294 of frames into an 800 pixel wide space.
4297 if (max_framepos / fpu < 800.0) {
4302 tempo_lines->tempo_map_changed();
4304 frames_per_unit = fpu;
4309 Editor::post_zoom ()
4311 // convert fpu to frame count
4313 framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width);
4315 if (frames_per_unit != zoom_range_clock.current_duration()) {
4316 zoom_range_clock.set (frames);
4319 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4320 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4321 (*i)->reshow_selection (selection->time);
4325 ZoomChanged (); /* EMIT_SIGNAL */
4327 //reset_scrolling_region ();
4329 if (playhead_cursor) {
4330 playhead_cursor->set_position (playhead_cursor->current_frame);
4333 refresh_location_display();
4334 _summary->set_overlays_dirty ();
4336 update_marker_labels ();
4342 Editor::queue_visual_change (framepos_t where)
4344 pending_visual_change.add (VisualChange::TimeOrigin);
4345 pending_visual_change.time_origin = where;
4346 ensure_visual_change_idle_handler ();
4350 Editor::queue_visual_change (double fpu)
4352 pending_visual_change.add (VisualChange::ZoomLevel);
4353 pending_visual_change.frames_per_unit = fpu;
4355 ensure_visual_change_idle_handler ();
4359 Editor::queue_visual_change_y (double y)
4361 pending_visual_change.add (VisualChange::YOrigin);
4362 pending_visual_change.y_origin = y;
4364 ensure_visual_change_idle_handler ();
4368 Editor::ensure_visual_change_idle_handler ()
4370 if (pending_visual_change.idle_handler_id < 0) {
4371 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4376 Editor::_idle_visual_changer (void* arg)
4378 return static_cast<Editor*>(arg)->idle_visual_changer ();
4382 Editor::idle_visual_changer ()
4384 VisualChange::Type p = pending_visual_change.pending;
4385 pending_visual_change.pending = (VisualChange::Type) 0;
4387 double const last_time_origin = horizontal_position ();
4389 if (p & VisualChange::TimeOrigin) {
4390 /* This is a bit of a hack, but set_frames_per_unit
4391 below will (if called) end up with the
4392 CrossfadeViews looking at Editor::leftmost_frame,
4393 and if we're changing origin and zoom in the same
4394 operation it will be the wrong value unless we
4398 leftmost_frame = pending_visual_change.time_origin;
4401 if (p & VisualChange::ZoomLevel) {
4402 set_frames_per_unit (pending_visual_change.frames_per_unit);
4404 compute_fixed_ruler_scale ();
4405 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4406 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4407 update_tempo_based_rulers ();
4409 if (p & VisualChange::TimeOrigin) {
4410 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4412 if (p & VisualChange::YOrigin) {
4413 vertical_adjustment.set_value (pending_visual_change.y_origin);
4416 if (last_time_origin == horizontal_position ()) {
4417 /* changed signal not emitted */
4418 update_fixed_rulers ();
4419 redisplay_tempo (true);
4422 _summary->set_overlays_dirty ();
4424 pending_visual_change.idle_handler_id = -1;
4425 return 0; /* this is always a one-shot call */
4428 struct EditorOrderTimeAxisSorter {
4429 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4430 return a->order () < b->order ();
4435 Editor::sort_track_selection (TrackViewList* sel)
4437 EditorOrderTimeAxisSorter cmp;
4442 selection->tracks.sort (cmp);
4447 Editor::get_preferred_edit_position (bool ignore_playhead)
4450 framepos_t where = 0;
4451 EditPoint ep = _edit_point;
4453 if (entered_marker) {
4454 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position()));
4455 return entered_marker->position();
4458 if (ignore_playhead && ep == EditAtPlayhead) {
4459 ep = EditAtSelectedMarker;
4463 case EditAtPlayhead:
4464 where = _session->audible_frame();
4465 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where));
4468 case EditAtSelectedMarker:
4469 if (!selection->markers.empty()) {
4471 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4474 where = loc->start();
4478 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where));
4486 if (!mouse_frame (where, ignored)) {
4487 /* XXX not right but what can we do ? */
4491 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where));
4499 Editor::set_loop_range (framepos_t start, framepos_t end, string cmd)
4501 if (!_session) return;
4503 begin_reversible_command (cmd);
4507 if ((tll = transport_loop_location()) == 0) {
4508 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop);
4509 XMLNode &before = _session->locations()->get_state();
4510 _session->locations()->add (loc, true);
4511 _session->set_auto_loop_location (loc);
4512 XMLNode &after = _session->locations()->get_state();
4513 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4515 XMLNode &before = tll->get_state();
4516 tll->set_hidden (false, this);
4517 tll->set (start, end);
4518 XMLNode &after = tll->get_state();
4519 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4522 commit_reversible_command ();
4526 Editor::set_punch_range (framepos_t start, framepos_t end, string cmd)
4528 if (!_session) return;
4530 begin_reversible_command (cmd);
4534 if ((tpl = transport_punch_location()) == 0) {
4535 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoPunch);
4536 XMLNode &before = _session->locations()->get_state();
4537 _session->locations()->add (loc, true);
4538 _session->set_auto_loop_location (loc);
4539 XMLNode &after = _session->locations()->get_state();
4540 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4543 XMLNode &before = tpl->get_state();
4544 tpl->set_hidden (false, this);
4545 tpl->set (start, end);
4546 XMLNode &after = tpl->get_state();
4547 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4550 commit_reversible_command ();
4553 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4554 * @param rs List to which found regions are added.
4555 * @param where Time to look at.
4556 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4559 Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4561 const TrackViewList* tracks;
4564 tracks = &track_views;
4569 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4571 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4574 boost::shared_ptr<Track> tr;
4575 boost::shared_ptr<Playlist> pl;
4577 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4579 Playlist::RegionList* regions = pl->regions_at (
4580 (framepos_t) floor ( (double) where * tr->speed()));
4582 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4583 RegionView* rv = rtv->view()->find_view (*i);
4596 Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4598 const TrackViewList* tracks;
4601 tracks = &track_views;
4606 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4607 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4609 boost::shared_ptr<Track> tr;
4610 boost::shared_ptr<Playlist> pl;
4612 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4614 Playlist::RegionList* regions = pl->regions_touched (
4615 (framepos_t) floor ( (double)where * tr->speed()), max_framepos);
4617 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4619 RegionView* rv = rtv->view()->find_view (*i);
4632 /** Start with regions that are selected. Then add equivalent regions
4633 * on tracks in the same active edit-enabled route group as any of
4634 * the regions that we started with.
4638 Editor::get_regions_from_selection ()
4640 return get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
4643 /** Get regions using the following method:
4645 * Make an initial region list using the selected regions, unless
4646 * the edit point is `mouse' and the mouse is over an unselected
4647 * region. In this case, start with just that region.
4649 * Then, make an initial track list of the tracks that these
4650 * regions are on, and if the edit point is not `mouse', add the
4653 * Look at this track list and add any other tracks that are on the
4654 * same active edit-enabled route group as one of the initial tracks.
4656 * Finally take the initial region list and add any regions that are
4657 * under the edit point on one of the tracks on the track list to get
4658 * the returned region list.
4660 * The rationale here is that the mouse edit point is special in that
4661 * its position describes both a time and a track; the other edit
4662 * modes only describe a time. Hence if the edit point is `mouse' we
4663 * ignore selected tracks, as we assume the user means something by
4664 * pointing at a particular track. Also in this case we take note of
4665 * the region directly under the edit point, as there is always just one
4666 * (rather than possibly several with non-mouse edit points).
4670 Editor::get_regions_from_selection_and_edit_point ()
4672 RegionSelection regions;
4674 if (_edit_point == EditAtMouse && entered_regionview && !selection->regions.contains (entered_regionview)) {
4675 regions.add (entered_regionview);
4677 regions = selection->regions;
4680 TrackViewList tracks;
4682 if (_edit_point != EditAtMouse) {
4683 tracks = selection->tracks;
4686 /* Add any other tracks that have regions that are in the same
4687 edit-activated route group as one of our regions.
4689 for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) {
4691 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4693 if (g && g->is_active() && g->is_edit()) {
4694 tracks.add (axis_views_from_routes (g->route_list()));
4698 if (!tracks.empty()) {
4699 /* now find regions that are at the edit position on those tracks */
4700 framepos_t const where = get_preferred_edit_position ();
4701 get_regions_at (regions, where, tracks);
4707 /** Start with regions that are selected, or the entered regionview if none are selected.
4708 * Then add equivalent regions on tracks in the same active edit-enabled route group as any
4709 * of the regions that we started with.
4713 Editor::get_regions_from_selection_and_entered ()
4715 RegionSelection regions = selection->regions;
4717 if (regions.empty() && entered_regionview) {
4718 regions.add (entered_regionview);
4721 return get_equivalent_regions (regions, ARDOUR::Properties::edit.property_id);
4725 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4727 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4729 RouteTimeAxisView* tatv;
4731 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4733 boost::shared_ptr<Playlist> pl;
4734 vector<boost::shared_ptr<Region> > results;
4736 boost::shared_ptr<Track> tr;
4738 if ((tr = tatv->track()) == 0) {
4743 if ((pl = (tr->playlist())) != 0) {
4744 pl->get_region_list_equivalent_regions (region, results);
4747 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4748 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4749 regions.push_back (marv);
4758 Editor::show_rhythm_ferret ()
4760 if (rhythm_ferret == 0) {
4761 rhythm_ferret = new RhythmFerret(*this);
4764 rhythm_ferret->set_session (_session);
4765 rhythm_ferret->show ();
4766 rhythm_ferret->present ();
4770 Editor::first_idle ()
4772 MessageDialog* dialog = 0;
4774 if (track_views.size() > 1) {
4775 dialog = new MessageDialog (*this,
4776 string_compose (_("Please wait while %1 loads visual data"), PROGRAM_NAME),
4781 ARDOUR_UI::instance()->flush_pending ();
4784 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4788 // first idle adds route children (automation tracks), so we need to redisplay here
4789 _routes->redisplay ();
4797 Editor::_idle_resize (gpointer arg)
4799 return ((Editor*)arg)->idle_resize ();
4803 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4805 if (resize_idle_id < 0) {
4806 resize_idle_id = g_idle_add (_idle_resize, this);
4807 _pending_resize_amount = 0;
4810 /* make a note of the smallest resulting height, so that we can clamp the
4811 lower limit at TimeAxisView::hSmall */
4813 int32_t min_resulting = INT32_MAX;
4815 _pending_resize_amount += h;
4816 _pending_resize_view = view;
4818 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4820 if (selection->tracks.contains (_pending_resize_view)) {
4821 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4822 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4826 if (min_resulting < 0) {
4831 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4832 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4836 /** Handle pending resizing of tracks */
4838 Editor::idle_resize ()
4840 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4842 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4843 selection->tracks.contains (_pending_resize_view)) {
4845 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4846 if (*i != _pending_resize_view) {
4847 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4852 _pending_resize_amount = 0;
4854 _group_tabs->set_dirty ();
4855 resize_idle_id = -1;
4863 ENSURE_GUI_THREAD (*this, &Editor::located);
4865 playhead_cursor->set_position (_session->audible_frame ());
4866 if (_follow_playhead && !_pending_initial_locate) {
4867 reset_x_origin_to_follow_playhead ();
4870 _pending_locate_request = false;
4871 _pending_initial_locate = false;
4875 Editor::region_view_added (RegionView *)
4877 _summary->set_dirty ();
4881 Editor::region_view_removed ()
4883 _summary->set_dirty ();
4887 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4889 TrackViewList::const_iterator j = track_views.begin ();
4890 while (j != track_views.end()) {
4891 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4892 if (rtv && rtv->route() == r) {
4903 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4907 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4908 TimeAxisView* tv = axis_view_from_route (*i);
4919 Editor::handle_new_route (RouteList& routes)
4921 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4923 RouteTimeAxisView *rtv;
4924 list<RouteTimeAxisView*> new_views;
4926 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4927 boost::shared_ptr<Route> route = (*x);
4929 if (route->is_hidden() || route->is_monitor()) {
4933 DataType dt = route->input()->default_type();
4935 if (dt == ARDOUR::DataType::AUDIO) {
4936 rtv = new AudioTimeAxisView (*this, _session, route, *track_canvas);
4937 } else if (dt == ARDOUR::DataType::MIDI) {
4938 rtv = new MidiTimeAxisView (*this, _session, route, *track_canvas);
4940 throw unknown_type();
4943 new_views.push_back (rtv);
4944 track_views.push_back (rtv);
4946 rtv->effective_gain_display ();
4948 if (internal_editing()) {
4949 rtv->enter_internal_edit_mode ();
4951 rtv->leave_internal_edit_mode ();
4954 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4955 rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed));
4958 _routes->routes_added (new_views);
4959 _summary->routes_added (new_views);
4961 if (show_editor_mixer_when_tracks_arrive) {
4962 show_editor_mixer (true);
4965 editor_list_button.set_sensitive (true);
4969 Editor::timeaxisview_deleted (TimeAxisView *tv)
4971 if (_session && _session->deletion_in_progress()) {
4972 /* the situation is under control */
4976 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
4978 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
4980 _routes->route_removed (tv);
4982 if (tv == entered_track) {
4986 TimeAxisView::Children c = tv->get_child_list ();
4987 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
4988 if (entered_track == i->get()) {
4993 /* remove it from the list of track views */
4995 TrackViewList::iterator i;
4997 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
4998 i = track_views.erase (i);
5001 /* update whatever the current mixer strip is displaying, if revelant */
5003 boost::shared_ptr<Route> route;
5006 route = rtav->route ();
5009 if (current_mixer_strip && current_mixer_strip->route() == route) {
5011 TimeAxisView* next_tv;
5013 if (track_views.empty()) {
5015 } else if (i == track_views.end()) {
5016 next_tv = track_views.front();
5023 set_selected_mixer_strip (*next_tv);
5025 /* make the editor mixer strip go away setting the
5026 * button to inactive (which also unticks the menu option)
5029 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
5035 Editor::hide_track_in_display (TimeAxisView* tv, bool apply_to_selection)
5037 if (apply_to_selection) {
5038 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ) {
5040 TrackSelection::iterator j = i;
5043 hide_track_in_display (*i, false);
5048 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
5050 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
5051 // this will hide the mixer strip
5052 set_selected_mixer_strip (*tv);
5055 _routes->hide_track_in_display (*tv);
5060 Editor::sync_track_view_list_and_routes ()
5062 track_views = TrackViewList (_routes->views ());
5064 _summary->set_dirty ();
5065 _group_tabs->set_dirty ();
5067 return false; // do not call again (until needed)
5071 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
5073 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5078 /** Find a RouteTimeAxisView by the ID of its route */
5080 Editor::get_route_view_by_route_id (PBD::ID& id) const
5082 RouteTimeAxisView* v;
5084 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5085 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5086 if(v->route()->id() == id) {
5096 Editor::fit_route_group (RouteGroup *g)
5098 TrackViewList ts = axis_views_from_routes (g->route_list ());
5103 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5105 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5108 _session->cancel_audition ();
5112 if (_session->is_auditioning()) {
5113 _session->cancel_audition ();
5114 if (r == last_audition_region) {
5119 _session->audition_region (r);
5120 last_audition_region = r;
5125 Editor::hide_a_region (boost::shared_ptr<Region> r)
5127 r->set_hidden (true);
5131 Editor::show_a_region (boost::shared_ptr<Region> r)
5133 r->set_hidden (false);
5137 Editor::audition_region_from_region_list ()
5139 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5143 Editor::hide_region_from_region_list ()
5145 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5149 Editor::show_region_in_region_list ()
5151 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5155 Editor::step_edit_status_change (bool yn)
5158 start_step_editing ();
5160 stop_step_editing ();
5165 Editor::start_step_editing ()
5167 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5171 Editor::stop_step_editing ()
5173 step_edit_connection.disconnect ();
5177 Editor::check_step_edit ()
5179 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5180 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5182 mtv->check_step_edit ();
5186 return true; // do it again, till we stop
5190 Editor::scroll_press (Direction dir)
5192 ++_scroll_callbacks;
5194 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5195 /* delay the first auto-repeat */
5201 scroll_backward (1);
5209 scroll_tracks_up_line ();
5213 scroll_tracks_down_line ();
5217 /* do hacky auto-repeat */
5218 if (!_scroll_connection.connected ()) {
5220 _scroll_connection = Glib::signal_timeout().connect (
5221 sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), dir), 100
5224 _scroll_callbacks = 0;
5231 Editor::scroll_release ()
5233 _scroll_connection.disconnect ();
5236 /** Queue a change for the Editor viewport x origin to follow the playhead */
5238 Editor::reset_x_origin_to_follow_playhead ()
5240 framepos_t const frame = playhead_cursor->current_frame;
5242 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5244 if (_session->transport_speed() < 0) {
5246 if (frame > (current_page_frames() / 2)) {
5247 center_screen (frame-(current_page_frames()/2));
5249 center_screen (current_page_frames()/2);
5254 if (frame < leftmost_frame) {
5257 if (_session->transport_rolling()) {
5258 /* rolling; end up with the playhead at the right of the page */
5259 l = frame - current_page_frames ();
5261 /* not rolling: end up with the playhead 3/4 of the way along the page */
5262 l = frame - (3 * current_page_frames() / 4);
5269 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5272 if (_session->transport_rolling()) {
5273 /* rolling: end up with the playhead on the left of the page */
5274 center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5276 /* not rolling: end up with the playhead 1/4 of the way along the page */
5277 center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5285 Editor::super_rapid_screen_update ()
5287 if (!_session || !_session->engine().running()) {
5291 /* METERING / MIXER STRIPS */
5293 /* update track meters, if required */
5294 if (is_mapped() && meters_running) {
5295 RouteTimeAxisView* rtv;
5296 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5297 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5298 rtv->fast_update ();
5303 /* and any current mixer strip */
5304 if (current_mixer_strip) {
5305 current_mixer_strip->fast_update ();
5308 /* PLAYHEAD AND VIEWPORT */
5310 framepos_t const frame = _session->audible_frame();
5312 /* There are a few reasons why we might not update the playhead / viewport stuff:
5314 * 1. we don't update things when there's a pending locate request, otherwise
5315 * when the editor requests a locate there is a chance that this method
5316 * will move the playhead before the locate request is processed, causing
5318 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5319 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5322 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5324 last_update_frame = frame;
5326 if (!_dragging_playhead) {
5327 playhead_cursor->set_position (frame);
5330 if (!_stationary_playhead) {
5332 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5333 reset_x_origin_to_follow_playhead ();
5338 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5342 // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5343 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5344 if (target <= 0.0) {
5347 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5348 target = (target * 0.15) + (current * 0.85);
5354 set_horizontal_position (current);
5363 Editor::session_going_away ()
5365 _have_idled = false;
5367 _session_connections.drop_connections ();
5369 super_rapid_screen_update_connection.disconnect ();
5371 selection->clear ();
5372 cut_buffer->clear ();
5374 clicked_regionview = 0;
5375 clicked_axisview = 0;
5376 clicked_routeview = 0;
5377 clicked_crossfadeview = 0;
5378 entered_regionview = 0;
5380 last_update_frame = 0;
5383 playhead_cursor->canvas_item.hide ();
5385 /* rip everything out of the list displays */
5389 _route_groups->clear ();
5391 /* do this first so that deleting a track doesn't reset cms to null
5392 and thus cause a leak.
5395 if (current_mixer_strip) {
5396 if (current_mixer_strip->get_parent() != 0) {
5397 global_hpacker.remove (*current_mixer_strip);
5399 delete current_mixer_strip;
5400 current_mixer_strip = 0;
5403 /* delete all trackviews */
5405 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5408 track_views.clear ();
5410 zoom_range_clock.set_session (0);
5411 nudge_clock.set_session (0);
5413 editor_list_button.set_active(false);
5414 editor_list_button.set_sensitive(false);
5416 /* clear tempo/meter rulers */
5417 remove_metric_marks ();
5419 clear_marker_display ();
5421 delete current_bbt_points;
5422 current_bbt_points = 0;
5424 /* get rid of any existing editor mixer strip */
5426 WindowTitle title(Glib::get_application_name());
5427 title += _("Editor");
5429 set_title (title.get_string());
5431 SessionHandlePtr::session_going_away ();
5436 Editor::show_editor_list (bool yn)
5439 _the_notebook.show ();
5441 _the_notebook.hide ();
5446 Editor::change_region_layering_order ()
5448 framepos_t const position = get_preferred_edit_position ();
5450 if (!clicked_routeview) {
5451 if (layering_order_editor) {
5452 layering_order_editor->hide ();
5457 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
5463 boost::shared_ptr<Playlist> pl = track->playlist();
5469 if (layering_order_editor == 0) {
5470 layering_order_editor = new RegionLayeringOrderEditor(*this);
5473 layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position);
5474 layering_order_editor->maybe_present ();
5478 Editor::update_region_layering_order_editor ()
5480 if (layering_order_editor && layering_order_editor->is_visible ()) {
5481 change_region_layering_order ();
5486 Editor::setup_fade_images ()
5488 _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear")));
5489 _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut")));
5490 _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut")));
5491 _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut")));
5492 _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut")));
5494 _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear")));
5495 _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut")));
5496 _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut")));
5497 _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut")));
5498 _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut")));
5502 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
5504 Editor::action_menu_item (std::string const & name)
5506 Glib::RefPtr<Action> a = editor_actions->get_action (name);
5509 return *manage (a->create_menu_item ());
5513 Editor::resize_text_widgets ()
5515 set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_FUDGE+10, 15);
5516 set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_FUDGE+10, 15);
5517 set_size_request_to_display_given_text (snap_type_selector, snap_type_strings, COMBO_FUDGE+10, 15);
5518 set_size_request_to_display_given_text (snap_mode_selector, snap_mode_strings, COMBO_FUDGE+10, 15);
5519 set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_FUDGE+10, 15);
5523 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
5525 EventBox* b = manage (new EventBox);
5526 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
5527 Label* l = manage (new Label (name));
5531 _the_notebook.append_page (widget, *b);
5535 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
5537 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
5538 _the_notebook.set_current_page (_the_notebook.page_num (*page));
5541 if (ev->type == GDK_2BUTTON_PRESS) {
5543 /* double-click on a notebook tab shrinks or expands the notebook */
5545 if (_notebook_shrunk) {
5546 edit_pane.set_position (pre_maximal_horizontal_pane_position);
5547 _notebook_shrunk = false;
5549 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
5550 edit_pane.set_position (edit_pane.get_position() + page->get_width());
5551 _notebook_shrunk = true;