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/grouped_buttons.h>
53 #include <gtkmm2ext/gtk_ui.h>
54 #include <gtkmm2ext/tearoff.h>
55 #include <gtkmm2ext/utils.h>
56 #include <gtkmm2ext/window_title.h>
57 #include <gtkmm2ext/choice.h>
58 #include <gtkmm2ext/cell_renderer_pixbuf_toggle.h>
60 #include "ardour/audio_diskstream.h"
61 #include "ardour/audio_track.h"
62 #include "ardour/audioplaylist.h"
63 #include "ardour/audioregion.h"
64 #include "ardour/location.h"
65 #include "ardour/midi_region.h"
66 #include "ardour/plugin_manager.h"
67 #include "ardour/profile.h"
68 #include "ardour/route_group.h"
69 #include "ardour/session_directory.h"
70 #include "ardour/session_route.h"
71 #include "ardour/session_state_utils.h"
72 #include "ardour/tempo.h"
73 #include "ardour/utils.h"
74 #include "ardour/session_playlists.h"
75 #include "ardour/audioengine.h"
77 #include "control_protocol/control_protocol.h"
83 #include "playlist_selector.h"
84 #include "audio_region_view.h"
85 #include "rgb_macros.h"
86 #include "selection.h"
87 #include "audio_streamview.h"
88 #include "time_axis_view.h"
89 #include "audio_time_axis.h"
91 #include "crossfade_view.h"
92 #include "canvas-noevent-text.h"
94 #include "public_editor.h"
95 #include "crossfade_edit.h"
96 #include "canvas_impl.h"
99 #include "gui_thread.h"
100 #include "simpleline.h"
101 #include "rhythm_ferret.h"
103 #include "tempo_lines.h"
104 #include "analysis_window.h"
105 #include "bundle_manager.h"
106 #include "global_port_matrix.h"
107 #include "editor_drag.h"
108 #include "editor_group_tabs.h"
109 #include "automation_time_axis.h"
110 #include "editor_routes.h"
111 #include "midi_time_axis.h"
112 #include "mixer_strip.h"
113 #include "editor_route_groups.h"
114 #include "editor_regions.h"
115 #include "editor_locations.h"
116 #include "editor_snapshots.h"
117 #include "editor_summary.h"
118 #include "region_layering_order_editor.h"
119 #include "mouse_cursors.h"
120 #include "editor_cursors.h"
125 #include "imageframe_socket_handler.h"
129 using namespace ARDOUR;
132 using namespace Glib;
133 using namespace Gtkmm2ext;
134 using namespace Editing;
136 using PBD::internationalize;
138 using Gtkmm2ext::Keyboard;
140 const double Editor::timebar_height = 15.0;
142 static const gchar *_snap_type_strings[] = {
144 N_("Timecode Frames"),
145 N_("Timecode Seconds"),
146 N_("Timecode Minutes"),
174 static const gchar *_snap_mode_strings[] = {
181 static const gchar *_edit_point_strings[] = {
188 static const gchar *_zoom_focus_strings[] = {
198 #ifdef USE_RUBBERBAND
199 static const gchar *_rb_opt_strings[] = {
202 N_("Balanced multitimbral mixture"),
203 N_("Unpitched percussion with stable notes"),
204 N_("Crisp monophonic instrumental"),
205 N_("Unpitched solo percussion"),
206 N_("Resample without preserving pitch"),
212 show_me_the_size (Requisition* r, const char* what)
214 cerr << "size of " << what << " = " << r->width << " x " << r->height << endl;
219 pane_size_watcher (Paned* pane)
221 /* if the handle of a pane vanishes into (at least) the tabs of a notebook,
222 it is no longer accessible. so stop that. this doesn't happen on X11,
223 just the quartz backend.
228 int max_width_of_lhs = GTK_WIDGET(pane->gobj())->allocation.width - 25;
230 gint pos = pane->get_position ();
232 if (pos > max_width_of_lhs) {
233 pane->set_position (max_width_of_lhs);
239 : _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
241 /* time display buttons */
242 , minsec_label (_("Mins:Secs"))
243 , bbt_label (_("Bars:Beats"))
244 , timecode_label (_("Timecode"))
245 , samples_label (_("Samples"))
246 , tempo_label (_("Tempo"))
247 , meter_label (_("Meter"))
248 , mark_label (_("Location Markers"))
249 , range_mark_label (_("Range Markers"))
250 , transport_mark_label (_("Loop/Punch Ranges"))
251 , cd_mark_label (_("CD Markers"))
252 , edit_packer (4, 4, true)
254 /* the values here don't matter: layout widgets
255 reset them as needed.
258 , vertical_adjustment (0.0, 0.0, 10.0, 400.0)
260 /* tool bar related */
262 , zoom_range_clock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, false, true)
264 , toolbar_selection_clock_table (2,3)
266 , automation_mode_button (_("mode"))
267 , global_automation_button (_("automation"))
269 , _toolbar_viewport (*manage (new Gtk::Adjustment (0, 0, 1e10)), *manage (new Gtk::Adjustment (0, 0, 1e10)))
270 , midi_panic_button (_("Panic"))
273 , image_socket_listener(0)
278 , nudge_clock (X_("nudge"), false, X_("NudgeClock"), true, false, true)
279 , meters_running(false)
280 , _pending_locate_request (false)
281 , _pending_initial_locate (false)
282 , _last_cut_copy_source_track (0)
284 , _region_selection_change_updates_region_list (true)
288 /* we are a singleton */
290 PublicEditor::_instance = this;
294 selection = new Selection (this);
295 cut_buffer = new Selection (this);
297 clicked_regionview = 0;
298 clicked_axisview = 0;
299 clicked_routeview = 0;
300 clicked_crossfadeview = 0;
301 clicked_control_point = 0;
302 last_update_frame = 0;
303 pre_press_cursor = 0;
304 _drags = new DragManager (this);
305 current_mixer_strip = 0;
306 current_bbt_points = 0;
309 snap_type_strings = I18N (_snap_type_strings);
310 snap_mode_strings = I18N (_snap_mode_strings);
311 zoom_focus_strings = I18N (_zoom_focus_strings);
312 edit_point_strings = I18N (_edit_point_strings);
313 #ifdef USE_RUBBERBAND
314 rb_opt_strings = I18N (_rb_opt_strings);
318 snap_threshold = 5.0;
319 bbt_beat_subdivision = 4;
322 last_autoscroll_x = 0;
323 last_autoscroll_y = 0;
324 autoscroll_active = false;
325 autoscroll_timeout_tag = -1;
330 current_interthread_info = 0;
331 _show_measures = true;
332 show_gain_after_trim = false;
333 verbose_cursor_on = true;
334 last_item_entered = 0;
336 have_pending_keyboard_selection = false;
337 _follow_playhead = true;
338 _stationary_playhead = false;
339 _xfade_visibility = true;
340 editor_ruler_menu = 0;
341 no_ruler_shown_update = false;
343 range_marker_menu = 0;
344 marker_menu_item = 0;
345 tempo_or_meter_marker_menu = 0;
346 transport_marker_menu = 0;
347 new_transport_marker_menu = 0;
348 editor_mixer_strip_width = Wide;
349 show_editor_mixer_when_tracks_arrive = false;
350 region_edit_menu_split_multichannel_item = 0;
351 region_edit_menu_split_item = 0;
354 current_stepping_trackview = 0;
356 entered_regionview = 0;
358 clear_entered_track = false;
361 button_release_can_deselect = true;
362 _dragging_playhead = false;
363 _dragging_edit_point = false;
364 select_new_marker = false;
366 layering_order_editor = 0;
368 no_save_visual = false;
371 scrubbing_direction = 0;
375 location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get();
376 location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get();
377 location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get();
378 location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get();
379 location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get();
381 _edit_point = EditAtMouse;
382 _internal_editing = false;
383 current_canvas_cursor = 0;
385 frames_per_unit = 2048; /* too early to use reset_zoom () */
387 _scroll_callbacks = 0;
389 zoom_focus = ZoomFocusLeft;
390 set_zoom_focus (ZoomFocusLeft);
391 zoom_range_clock.ValueChanged.connect (sigc::mem_fun(*this, &Editor::zoom_adjustment_changed));
393 bbt_label.set_name ("EditorTimeButton");
394 bbt_label.set_size_request (-1, (int)timebar_height);
395 bbt_label.set_alignment (1.0, 0.5);
396 bbt_label.set_padding (5,0);
398 bbt_label.set_no_show_all();
399 minsec_label.set_name ("EditorTimeButton");
400 minsec_label.set_size_request (-1, (int)timebar_height);
401 minsec_label.set_alignment (1.0, 0.5);
402 minsec_label.set_padding (5,0);
403 minsec_label.hide ();
404 minsec_label.set_no_show_all();
405 timecode_label.set_name ("EditorTimeButton");
406 timecode_label.set_size_request (-1, (int)timebar_height);
407 timecode_label.set_alignment (1.0, 0.5);
408 timecode_label.set_padding (5,0);
409 timecode_label.hide ();
410 timecode_label.set_no_show_all();
411 samples_label.set_name ("EditorTimeButton");
412 samples_label.set_size_request (-1, (int)timebar_height);
413 samples_label.set_alignment (1.0, 0.5);
414 samples_label.set_padding (5,0);
415 samples_label.hide ();
416 samples_label.set_no_show_all();
418 tempo_label.set_name ("EditorTimeButton");
419 tempo_label.set_size_request (-1, (int)timebar_height);
420 tempo_label.set_alignment (1.0, 0.5);
421 tempo_label.set_padding (5,0);
423 tempo_label.set_no_show_all();
425 meter_label.set_name ("EditorTimeButton");
426 meter_label.set_size_request (-1, (int)timebar_height);
427 meter_label.set_alignment (1.0, 0.5);
428 meter_label.set_padding (5,0);
430 meter_label.set_no_show_all();
432 mark_label.set_name ("EditorTimeButton");
433 mark_label.set_size_request (-1, (int)timebar_height);
434 mark_label.set_alignment (1.0, 0.5);
435 mark_label.set_padding (5,0);
437 mark_label.set_no_show_all();
439 cd_mark_label.set_name ("EditorTimeButton");
440 cd_mark_label.set_size_request (-1, (int)timebar_height);
441 cd_mark_label.set_alignment (1.0, 0.5);
442 cd_mark_label.set_padding (5,0);
443 cd_mark_label.hide();
444 cd_mark_label.set_no_show_all();
446 range_mark_label.set_name ("EditorTimeButton");
447 range_mark_label.set_size_request (-1, (int)timebar_height);
448 range_mark_label.set_alignment (1.0, 0.5);
449 range_mark_label.set_padding (5,0);
450 range_mark_label.hide();
451 range_mark_label.set_no_show_all();
453 transport_mark_label.set_name ("EditorTimeButton");
454 transport_mark_label.set_size_request (-1, (int)timebar_height);
455 transport_mark_label.set_alignment (1.0, 0.5);
456 transport_mark_label.set_padding (5,0);
457 transport_mark_label.hide();
458 transport_mark_label.set_no_show_all();
460 initialize_rulers ();
461 initialize_canvas ();
463 _summary = new EditorSummary (this);
465 selection->TimeChanged.connect (sigc::mem_fun(*this, &Editor::time_selection_changed));
466 selection->TracksChanged.connect (sigc::mem_fun(*this, &Editor::track_selection_changed));
468 editor_regions_selection_changed_connection = selection->RegionsChanged.connect (sigc::mem_fun(*this, &Editor::region_selection_changed));
470 selection->PointsChanged.connect (sigc::mem_fun(*this, &Editor::point_selection_changed));
471 selection->MarkersChanged.connect (sigc::mem_fun(*this, &Editor::marker_selection_changed));
473 edit_controls_vbox.set_spacing (0);
474 vertical_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &Editor::tie_vertical_scrolling), true);
475 track_canvas->signal_map_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_map_handler));
477 HBox* h = manage (new HBox);
478 _group_tabs = new EditorGroupTabs (this);
479 h->pack_start (*_group_tabs, PACK_SHRINK);
480 h->pack_start (edit_controls_vbox);
481 controls_layout.add (*h);
483 controls_layout.set_name ("EditControlsBase");
484 controls_layout.add_events (Gdk::SCROLL_MASK);
485 controls_layout.signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::control_layout_scroll), false);
487 controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
488 controls_layout.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_release));
490 _cursors = new MouseCursors;
492 ArdourCanvas::Canvas* time_pad = manage(new ArdourCanvas::Canvas());
493 ArdourCanvas::SimpleLine* pad_line_1 = manage(new ArdourCanvas::SimpleLine(*time_pad->root(),
494 0.0, 1.0, 100.0, 1.0));
496 pad_line_1->property_color_rgba() = 0xFF0000FF;
501 time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2);
502 time_canvas_vbox.set_size_request (-1, -1);
504 ruler_label_event_box.add (ruler_label_vbox);
505 ruler_label_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
506 ruler_label_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
508 time_button_event_box.add (time_button_vbox);
509 time_button_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
510 time_button_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
512 /* these enable us to have a dedicated window (for cursor setting, etc.)
513 for the canvas areas.
516 track_canvas_event_box.add (*track_canvas);
518 time_canvas_event_box.add (time_canvas_vbox);
519 time_canvas_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
521 edit_packer.set_col_spacings (0);
522 edit_packer.set_row_spacings (0);
523 edit_packer.set_homogeneous (false);
524 edit_packer.set_border_width (0);
525 edit_packer.set_name ("EditorWindow");
527 /* labels for the rulers */
528 edit_packer.attach (ruler_label_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0);
529 /* labels for the marker "tracks" */
530 edit_packer.attach (time_button_event_box, 1, 2, 1, 2, FILL, SHRINK, 0, 0);
532 edit_packer.attach (time_canvas_event_box, 2, 3, 0, 1, FILL|EXPAND, FILL, 0, 0);
534 edit_packer.attach (controls_layout, 0, 2, 2, 3, FILL, FILL|EXPAND, 0, 0);
536 edit_packer.attach (track_canvas_event_box, 2, 3, 1, 3, FILL|EXPAND, FILL|EXPAND, 0, 0);
538 bottom_hbox.set_border_width (2);
539 bottom_hbox.set_spacing (3);
541 _route_groups = new EditorRouteGroups (this);
542 _routes = new EditorRoutes (this);
543 _regions = new EditorRegions (this);
544 _snapshots = new EditorSnapshots (this);
545 _locations = new EditorLocations (this);
547 add_notebook_page (_("Regions"), _regions->widget ());
548 add_notebook_page (_("Tracks & Busses"), _routes->widget ());
549 add_notebook_page (_("Snapshots"), _snapshots->widget ());
550 add_notebook_page (_("Route Groups"), _route_groups->widget ());
551 add_notebook_page (_("Ranges & Marks"), _locations->widget ());
553 _the_notebook.set_show_tabs (true);
554 _the_notebook.set_scrollable (true);
555 _the_notebook.popup_disable ();
556 _the_notebook.set_tab_pos (Gtk::POS_RIGHT);
557 _the_notebook.show_all ();
559 post_maximal_editor_width = 0;
560 post_maximal_horizontal_pane_position = 0;
561 post_maximal_editor_height = 0;
562 post_maximal_vertical_pane_position = 0;
563 _notebook_shrunk = false;
565 editor_summary_pane.pack1(edit_packer);
567 Button* summary_arrows_left_left = manage (new Button);
568 summary_arrows_left_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
569 summary_arrows_left_left->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press)));
570 summary_arrows_left_left->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_release));
572 Button* summary_arrows_left_right = manage (new Button);
573 summary_arrows_left_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
574 summary_arrows_left_right->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press)));
575 summary_arrows_left_right->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_release));
577 VBox* summary_arrows_left = manage (new VBox);
578 summary_arrows_left->pack_start (*summary_arrows_left_left);
579 summary_arrows_left->pack_start (*summary_arrows_left_right);
581 Button* summary_arrows_right_left = manage (new Button);
582 summary_arrows_right_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
583 summary_arrows_right_left->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press)));
584 summary_arrows_right_left->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_release));
586 Button* summary_arrows_right_right = manage (new Button);
587 summary_arrows_right_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
588 summary_arrows_right_right->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press)));
589 summary_arrows_right_right->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_release));
591 VBox* summary_arrows_right = manage (new VBox);
592 summary_arrows_right->pack_start (*summary_arrows_right_left);
593 summary_arrows_right->pack_start (*summary_arrows_right_right);
595 Frame* summary_frame = manage (new Frame);
596 summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN);
598 summary_frame->add (*_summary);
599 summary_frame->show ();
601 _summary_hbox.pack_start (*summary_arrows_left, false, false);
602 _summary_hbox.pack_start (*summary_frame, true, true);
603 _summary_hbox.pack_start (*summary_arrows_right, false, false);
605 editor_summary_pane.pack2 (_summary_hbox);
607 edit_pane.pack1 (editor_summary_pane, true, true);
608 edit_pane.pack2 (_the_notebook, false, true);
610 editor_summary_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun (*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&editor_summary_pane)));
612 /* XXX: editor_summary_pane might need similar special OS X treatment to the edit_pane */
614 edit_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&edit_pane)));
616 Glib::PropertyProxy<int> proxy = edit_pane.property_position();
617 proxy.signal_changed().connect (bind (sigc::ptr_fun (pane_size_watcher), static_cast<Paned*> (&edit_pane)));
619 top_hbox.pack_start (toolbar_frame);
621 HBox *hbox = manage (new HBox);
622 hbox->pack_start (edit_pane, true, true);
624 global_vpacker.pack_start (top_hbox, false, false);
625 global_vpacker.pack_start (*hbox, true, true);
627 global_hpacker.pack_start (global_vpacker, true, true);
629 set_name ("EditorWindow");
630 add_accel_group (ActionManager::ui_manager->get_accel_group());
632 status_bar_hpacker.show ();
634 vpacker.pack_end (status_bar_hpacker, false, false);
635 vpacker.pack_end (global_hpacker, true, true);
637 /* register actions now so that set_state() can find them and set toggles/checks etc */
642 setup_midi_toolbar ();
644 _snap_type = SnapToBeat;
645 set_snap_to (_snap_type);
646 _snap_mode = SnapOff;
647 set_snap_mode (_snap_mode);
648 set_mouse_mode (MouseObject, true);
649 set_edit_point_preference (EditAtMouse, true);
651 _playlist_selector = new PlaylistSelector();
652 _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
654 RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), ui_bind (&Editor::catch_vanishing_regionview, this, _1), gui_context());
658 nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
659 nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
661 nudge_forward_button.set_name ("TransportButton");
662 nudge_backward_button.set_name ("TransportButton");
664 fade_context_menu.set_name ("ArdourContextMenu");
666 /* icons, titles, WM stuff */
668 list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
669 Glib::RefPtr<Gdk::Pixbuf> icon;
671 if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
672 window_icons.push_back (icon);
674 if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
675 window_icons.push_back (icon);
677 if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
678 window_icons.push_back (icon);
680 if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
681 window_icons.push_back (icon);
683 if (!window_icons.empty()) {
684 set_icon_list (window_icons);
685 set_default_icon_list (window_icons);
688 WindowTitle title(Glib::get_application_name());
689 title += _("Editor");
690 set_title (title.get_string());
691 set_wmclass (X_("ardour_editor"), PROGRAM_NAME);
694 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
696 signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
697 signal_delete_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
699 /* allow external control surfaces/protocols to do various things */
701 ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context());
702 ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context());
703 ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context());
704 ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), ui_bind (&Editor::control_scroll, this, _1), gui_context());
705 BasicUI::AccessAction.connect (*this, invalidator (*this), ui_bind (&Editor::access_action, this, _1, _2), gui_context());
707 /* problematic: has to return a value and thus cannot be x-thread */
709 Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
711 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
713 TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
715 _ignore_region_action = false;
716 _last_region_menu_was_main = false;
717 _popup_region_menu_item = 0;
719 _show_marker_lines = false;
720 _over_region_trim_target = false;
725 setup_fade_images ();
731 if(image_socket_listener) {
732 if(image_socket_listener->is_connected())
734 image_socket_listener->close_connection() ;
737 delete image_socket_listener ;
738 image_socket_listener = 0 ;
743 delete _route_groups;
749 Editor::add_toplevel_controls (Container& cont)
751 vpacker.pack_start (cont, false, false);
756 Editor::catch_vanishing_regionview (RegionView *rv)
758 /* note: the selection will take care of the vanishing
759 audioregionview by itself.
762 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
766 if (clicked_regionview == rv) {
767 clicked_regionview = 0;
770 if (entered_regionview == rv) {
771 set_entered_regionview (0);
774 if (!_all_region_actions_sensitized) {
775 sensitize_all_region_actions (true);
780 Editor::set_entered_regionview (RegionView* rv)
782 if (rv == entered_regionview) {
786 if (entered_regionview) {
787 entered_regionview->exited ();
790 if ((entered_regionview = rv) != 0) {
791 entered_regionview->entered (internal_editing ());
794 if (!_all_region_actions_sensitized && _last_region_menu_was_main) {
795 /* This RegionView entry might have changed what region actions
796 are allowed, so sensitize them all in case a key is pressed.
798 sensitize_all_region_actions (true);
803 Editor::set_entered_track (TimeAxisView* tav)
806 entered_track->exited ();
809 if ((entered_track = tav) != 0) {
810 entered_track->entered ();
815 Editor::show_window ()
817 if (!is_visible ()) {
820 /* XXX: this is a bit unfortunate; it would probably
821 be nicer if we could just call show () above rather
822 than needing the show_all ()
825 /* re-hide stuff if necessary */
826 editor_list_button_toggled ();
827 parameter_changed ("show-summary");
828 parameter_changed ("show-group-tabs");
829 parameter_changed ("show-zoom-tools");
831 /* now reset all audio_time_axis heights, because widgets might need
837 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
838 tv = (static_cast<TimeAxisView*>(*i));
842 if (current_mixer_strip) {
843 current_mixer_strip->hide_things ();
851 Editor::instant_save ()
853 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
858 _session->add_instant_xml(get_state());
860 Config->add_instant_xml(get_state());
865 Editor::zoom_adjustment_changed ()
871 double fpu = zoom_range_clock.current_duration() / _canvas_width;
875 zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width));
876 } else if (fpu > _session->current_end_frame() / _canvas_width) {
877 fpu = _session->current_end_frame() / _canvas_width;
878 zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width));
885 Editor::control_scroll (float fraction)
887 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
893 double step = fraction * current_page_frames();
896 _control_scroll_target is an optional<T>
898 it acts like a pointer to an framepos_t, with
899 a operator conversion to boolean to check
900 that it has a value could possibly use
901 playhead_cursor->current_frame to store the
902 value and a boolean in the class to know
903 when it's out of date
906 if (!_control_scroll_target) {
907 _control_scroll_target = _session->transport_frame();
908 _dragging_playhead = true;
911 if ((fraction < 0.0f) && (*_control_scroll_target < (framepos_t) fabs(step))) {
912 *_control_scroll_target = 0;
913 } else if ((fraction > 0.0f) && (max_framepos - *_control_scroll_target < step)) {
914 *_control_scroll_target = max_framepos - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
916 *_control_scroll_target += (framepos_t) floor (step);
919 /* move visuals, we'll catch up with it later */
921 playhead_cursor->set_position (*_control_scroll_target);
922 UpdateAllTransportClocks (*_control_scroll_target);
924 if (*_control_scroll_target > (current_page_frames() / 2)) {
925 /* try to center PH in window */
926 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
932 Now we do a timeout to actually bring the session to the right place
933 according to the playhead. This is to avoid reading disk buffers on every
934 call to control_scroll, which is driven by ScrollTimeline and therefore
935 probably by a control surface wheel which can generate lots of events.
937 /* cancel the existing timeout */
939 control_scroll_connection.disconnect ();
941 /* add the next timeout */
943 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
947 Editor::deferred_control_scroll (framepos_t /*target*/)
949 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
950 // reset for next stream
951 _control_scroll_target = boost::none;
952 _dragging_playhead = false;
957 Editor::access_action (std::string action_group, std::string action_item)
963 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
966 act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
974 Editor::on_realize ()
976 Window::on_realize ();
981 Editor::map_position_change (framepos_t frame)
983 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, frame)
989 if (_follow_playhead) {
990 center_screen (frame);
993 playhead_cursor->set_position (frame);
997 Editor::center_screen (framepos_t frame)
999 double page = _canvas_width * frames_per_unit;
1001 /* if we're off the page, then scroll.
1004 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
1005 center_screen_internal (frame, page);
1010 Editor::center_screen_internal (framepos_t frame, float page)
1015 frame -= (framepos_t) page;
1020 reset_x_origin (frame);
1025 Editor::update_title ()
1027 ENSURE_GUI_THREAD (*this, &Editor::update_title)
1030 bool dirty = _session->dirty();
1032 string session_name;
1034 if (_session->snap_name() != _session->name()) {
1035 session_name = _session->snap_name();
1037 session_name = _session->name();
1041 session_name = "*" + session_name;
1044 WindowTitle title(session_name);
1045 title += Glib::get_application_name();
1046 set_title (title.get_string());
1051 Editor::set_session (Session *t)
1053 SessionHandlePtr::set_session (t);
1059 zoom_range_clock.set_session (_session);
1060 _playlist_selector->set_session (_session);
1061 nudge_clock.set_session (_session);
1062 _summary->set_session (_session);
1063 _group_tabs->set_session (_session);
1064 _route_groups->set_session (_session);
1065 _regions->set_session (_session);
1066 _snapshots->set_session (_session);
1067 _routes->set_session (_session);
1068 _locations->set_session (_session);
1070 if (rhythm_ferret) {
1071 rhythm_ferret->set_session (_session);
1074 if (analysis_window) {
1075 analysis_window->set_session (_session);
1079 sfbrowser->set_session (_session);
1082 compute_fixed_ruler_scale ();
1084 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1085 set_state (*node, Stateful::loading_state_version);
1087 /* catch up with the playhead */
1089 _session->request_locate (playhead_cursor->current_frame);
1090 _pending_initial_locate = true;
1094 /* These signals can all be emitted by a non-GUI thread. Therefore the
1095 handlers for them must not attempt to directly interact with the GUI,
1096 but use Gtkmm2ext::UI::instance()->call_slot();
1099 _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), ui_bind(&Editor::step_edit_status_change, this, _1), gui_context());
1100 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1101 _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context());
1102 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context());
1103 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1104 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context());
1105 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1106 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
1107 _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Editor::session_state_saved, this, _1), gui_context());
1108 _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context());
1109 _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&Editor::location_gone, this, _1), gui_context());
1110 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1111 _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display_s, this, _1), gui_context());
1112 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1114 if (Profile->get_sae()) {
1115 Timecode::BBT_Time bbt;
1119 framepos_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1);
1120 nudge_clock.set_mode(AudioClock::BBT);
1121 nudge_clock.set (pos, true, 0, AudioClock::BBT);
1124 nudge_clock.set (_session->frame_rate() * 5, true, 0, AudioClock::Timecode); // default of 5 seconds
1127 playhead_cursor->canvas_item.show ();
1129 Location* loc = _session->locations()->auto_loop_location();
1131 loc = new Location (*_session, 0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1133 if (loc->start() == loc->end()) {
1134 loc->set_end (loc->start() + 1);
1137 _session->locations()->add (loc, false);
1138 _session->set_auto_loop_location (loc);
1141 loc->set_name (_("Loop"));
1144 loc = _session->locations()->auto_punch_location();
1147 loc = new Location (*_session, 0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1149 if (loc->start() == loc->end()) {
1150 loc->set_end (loc->start() + 1);
1153 _session->locations()->add (loc, false);
1154 _session->set_auto_punch_location (loc);
1157 loc->set_name (_("Punch"));
1160 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1161 Config->map_parameters (pc);
1162 _session->config.map_parameters (pc);
1164 refresh_location_display ();
1166 restore_ruler_visibility ();
1167 //tempo_map_changed (PropertyChange (0));
1168 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1170 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1171 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1174 super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
1175 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1178 switch (_snap_type) {
1179 case SnapToRegionStart:
1180 case SnapToRegionEnd:
1181 case SnapToRegionSync:
1182 case SnapToRegionBoundary:
1183 build_region_boundary_cache ();
1190 /* register for undo history */
1191 _session->register_with_memento_command_factory(_id, this);
1193 ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated));
1195 start_updating_meters ();
1199 Editor::action_pre_activated (Glib::RefPtr<Action> const & a)
1201 if (a->get_name() == "RegionMenu") {
1202 /* When the main menu's region menu is opened, we setup the actions so that they look right
1203 in the menu. I can't find a way of getting a signal when this menu is subsequently closed,
1204 so we resensitize all region actions when the entered regionview or the region selection
1205 changes. HOWEVER we can't always resensitize on entered_regionview change because that
1206 happens after the region context menu is opened. So we set a flag here, too.
1210 sensitize_the_right_region_actions ();
1211 _last_region_menu_was_main = true;
1215 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1217 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1219 using namespace Menu_Helpers;
1220 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1223 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1227 MenuList& items (fade_context_menu.items());
1231 switch (item_type) {
1233 case FadeInHandleItem:
1234 if (arv->audio_region()->fade_in_active()) {
1235 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1237 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1240 items.push_back (SeparatorElem());
1242 if (Profile->get_sae()) {
1244 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)));
1245 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)));
1252 *_fade_in_images[FadeLinear],
1253 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)
1257 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1262 *_fade_in_images[FadeFast],
1263 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)
1266 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1271 *_fade_in_images[FadeLogB],
1272 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogB)
1275 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1280 *_fade_in_images[FadeLogA],
1281 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogA)
1284 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1289 *_fade_in_images[FadeSlow],
1290 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow)
1293 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1299 case FadeOutHandleItem:
1300 if (arv->audio_region()->fade_out_active()) {
1301 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1303 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1306 items.push_back (SeparatorElem());
1308 if (Profile->get_sae()) {
1309 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)));
1310 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)));
1316 *_fade_out_images[FadeLinear],
1317 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)
1321 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1326 *_fade_out_images[FadeFast],
1327 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)
1330 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1335 *_fade_out_images[FadeLogB],
1336 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogA)
1339 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1344 *_fade_out_images[FadeLogA],
1345 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogB)
1348 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1353 *_fade_out_images[FadeSlow],
1354 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast)
1357 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1363 fatal << _("programming error: ")
1364 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1369 fade_context_menu.popup (button, time);
1373 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1375 using namespace Menu_Helpers;
1376 Menu* (Editor::*build_menu_function)();
1379 switch (item_type) {
1381 case RegionViewName:
1382 case RegionViewNameHighlight:
1383 case LeftFrameHandle:
1384 case RightFrameHandle:
1385 if (with_selection) {
1386 build_menu_function = &Editor::build_track_selection_context_menu;
1388 build_menu_function = &Editor::build_track_region_context_menu;
1393 if (with_selection) {
1394 build_menu_function = &Editor::build_track_selection_context_menu;
1396 build_menu_function = &Editor::build_track_context_menu;
1400 case CrossfadeViewItem:
1401 build_menu_function = &Editor::build_track_crossfade_context_menu;
1405 if (clicked_routeview->track()) {
1406 build_menu_function = &Editor::build_track_context_menu;
1408 build_menu_function = &Editor::build_track_bus_context_menu;
1413 /* probably shouldn't happen but if it does, we don't care */
1417 menu = (this->*build_menu_function)();
1418 menu->set_name ("ArdourContextMenu");
1420 /* now handle specific situations */
1422 switch (item_type) {
1424 case RegionViewName:
1425 case RegionViewNameHighlight:
1426 case LeftFrameHandle:
1427 case RightFrameHandle:
1428 if (!with_selection) {
1429 if (region_edit_menu_split_item) {
1430 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1431 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1433 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1436 if (region_edit_menu_split_multichannel_item) {
1437 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1438 region_edit_menu_split_multichannel_item->set_sensitive (true);
1440 region_edit_menu_split_multichannel_item->set_sensitive (false);
1449 case CrossfadeViewItem:
1456 /* probably shouldn't happen but if it does, we don't care */
1460 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1462 /* Bounce to disk */
1464 using namespace Menu_Helpers;
1465 MenuList& edit_items = menu->items();
1467 edit_items.push_back (SeparatorElem());
1469 switch (clicked_routeview->audio_track()->freeze_state()) {
1470 case AudioTrack::NoFreeze:
1471 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1474 case AudioTrack::Frozen:
1475 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1478 case AudioTrack::UnFrozen:
1479 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1485 if (item_type == StreamItem && clicked_routeview) {
1486 clicked_routeview->build_underlay_menu(menu);
1489 /* When the region menu is opened, we setup the actions so that they look right
1492 sensitize_the_right_region_actions ();
1493 _last_region_menu_was_main = false;
1495 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1496 menu->popup (button, time);
1500 Editor::build_track_context_menu ()
1502 using namespace Menu_Helpers;
1504 MenuList& edit_items = track_context_menu.items();
1507 add_dstream_context_items (edit_items);
1508 return &track_context_menu;
1512 Editor::build_track_bus_context_menu ()
1514 using namespace Menu_Helpers;
1516 MenuList& edit_items = track_context_menu.items();
1519 add_bus_context_items (edit_items);
1520 return &track_context_menu;
1524 Editor::build_track_region_context_menu ()
1526 using namespace Menu_Helpers;
1527 MenuList& edit_items = track_region_context_menu.items();
1530 /* we've just cleared the track region context menu, so the menu that these
1531 two items were on will have disappeared; stop them dangling.
1533 region_edit_menu_split_item = 0;
1534 region_edit_menu_split_multichannel_item = 0;
1536 /* we might try to use items that are currently attached to a crossfade menu,
1539 track_crossfade_context_menu.items().clear ();
1541 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1544 boost::shared_ptr<Track> tr;
1545 boost::shared_ptr<Playlist> pl;
1547 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
1548 framepos_t const framepos = (framepos_t) floor ((double) get_preferred_edit_position() * tr->speed());
1549 uint32_t regions_at = pl->count_regions_at (framepos);
1550 add_region_context_items (edit_items, regions_at > 1);
1554 add_dstream_context_items (edit_items);
1556 return &track_region_context_menu;
1560 Editor::build_track_crossfade_context_menu ()
1562 using namespace Menu_Helpers;
1563 MenuList& edit_items = track_crossfade_context_menu.items();
1564 edit_items.clear ();
1566 /* we might try to use items that are currently attached to a crossfade menu,
1569 track_region_context_menu.items().clear ();
1571 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1574 boost::shared_ptr<Track> tr;
1575 boost::shared_ptr<Playlist> pl;
1576 boost::shared_ptr<AudioPlaylist> apl;
1578 if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1580 AudioPlaylist::Crossfades xfades;
1582 apl->crossfades_at (get_preferred_edit_position (), xfades);
1584 bool many = xfades.size() > 1;
1586 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1587 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1590 framepos_t framepos = (framepos_t) floor ((double) get_preferred_edit_position() * tr->speed());
1591 uint32_t regions_at = pl->count_regions_at (framepos);
1592 add_region_context_items (edit_items, regions_at > 1);
1596 add_dstream_context_items (edit_items);
1598 return &track_crossfade_context_menu;
1602 Editor::analyze_region_selection ()
1604 if (analysis_window == 0) {
1605 analysis_window = new AnalysisWindow();
1608 analysis_window->set_session(_session);
1610 analysis_window->show_all();
1613 analysis_window->set_regionmode();
1614 analysis_window->analyze();
1616 analysis_window->present();
1620 Editor::analyze_range_selection()
1622 if (analysis_window == 0) {
1623 analysis_window = new AnalysisWindow();
1626 analysis_window->set_session(_session);
1628 analysis_window->show_all();
1631 analysis_window->set_rangemode();
1632 analysis_window->analyze();
1634 analysis_window->present();
1638 Editor::build_track_selection_context_menu ()
1640 using namespace Menu_Helpers;
1641 MenuList& edit_items = track_selection_context_menu.items();
1642 edit_items.clear ();
1644 add_selection_context_items (edit_items);
1645 // edit_items.push_back (SeparatorElem());
1646 // add_dstream_context_items (edit_items);
1648 return &track_selection_context_menu;
1651 /** Add context menu items relevant to crossfades.
1652 * @param edit_items List to add the items to.
1655 Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1657 using namespace Menu_Helpers;
1658 Menu *xfade_menu = manage (new Menu);
1659 MenuList& items = xfade_menu->items();
1660 xfade_menu->set_name ("ArdourContextMenu");
1663 if (xfade->active()) {
1669 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1670 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1672 if (xfade->can_follow_overlap()) {
1674 if (xfade->following_overlap()) {
1675 str = _("Convert to Short");
1677 str = _("Convert to Full");
1680 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1684 str = xfade->out()->name();
1686 str += xfade->in()->name();
1688 str = _("Crossfade");
1691 edit_items.push_back (MenuElem (str, *xfade_menu));
1692 edit_items.push_back (SeparatorElem());
1696 Editor::xfade_edit_left_region ()
1698 if (clicked_crossfadeview) {
1699 clicked_crossfadeview->left_view.show_region_editor ();
1704 Editor::xfade_edit_right_region ()
1706 if (clicked_crossfadeview) {
1707 clicked_crossfadeview->right_view.show_region_editor ();
1712 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, bool multiple_regions_at_position)
1714 using namespace Menu_Helpers;
1716 /* OK, stick the region submenu at the top of the list, and then add
1720 /* we have to hack up the region name because "_" has a special
1721 meaning for menu titles.
1724 RegionSelection rs = get_regions_from_selection_and_entered ();
1726 string::size_type pos = 0;
1727 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1729 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1730 menu_item_name.replace (pos, 1, "__");
1734 if (_popup_region_menu_item == 0) {
1735 _popup_region_menu_item = new MenuItem (menu_item_name);
1736 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1737 _popup_region_menu_item->show ();
1739 _popup_region_menu_item->set_label (menu_item_name);
1742 edit_items.push_back (*_popup_region_menu_item);
1743 if (multiple_regions_at_position && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1744 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region")->create_menu_item ()));
1746 edit_items.push_back (SeparatorElem());
1749 /** Add context menu items relevant to selection ranges.
1750 * @param edit_items List to add the items to.
1753 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1755 using namespace Menu_Helpers;
1757 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1758 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1760 edit_items.push_back (SeparatorElem());
1761 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1763 if (!selection->regions.empty()) {
1764 edit_items.push_back (SeparatorElem());
1765 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)));
1766 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)));
1769 edit_items.push_back (SeparatorElem());
1770 edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection)));
1771 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1773 edit_items.push_back (SeparatorElem());
1774 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1776 edit_items.push_back (SeparatorElem());
1777 edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1778 edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1780 edit_items.push_back (SeparatorElem());
1781 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1783 edit_items.push_back (SeparatorElem());
1784 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1785 edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1786 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1788 edit_items.push_back (SeparatorElem());
1789 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1790 edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1791 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1792 edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1793 edit_items.push_back (MenuElem (_("Export Range"), sigc::mem_fun(*this, &Editor::export_selection)));
1798 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1800 using namespace Menu_Helpers;
1804 Menu *play_menu = manage (new Menu);
1805 MenuList& play_items = play_menu->items();
1806 play_menu->set_name ("ArdourContextMenu");
1808 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1809 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1810 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1811 play_items.push_back (SeparatorElem());
1812 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
1814 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1818 Menu *select_menu = manage (new Menu);
1819 MenuList& select_items = select_menu->items();
1820 select_menu->set_name ("ArdourContextMenu");
1822 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1823 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1824 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1825 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1826 select_items.push_back (SeparatorElem());
1827 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
1828 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
1829 select_items.push_back (SeparatorElem());
1830 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1831 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1832 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1833 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1834 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
1835 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
1836 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
1838 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1842 Menu *cutnpaste_menu = manage (new Menu);
1843 MenuList& cutnpaste_items = cutnpaste_menu->items();
1844 cutnpaste_menu->set_name ("ArdourContextMenu");
1846 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1847 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1848 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1850 cutnpaste_items.push_back (SeparatorElem());
1852 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
1853 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
1855 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
1857 /* Adding new material */
1859 edit_items.push_back (SeparatorElem());
1860 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
1861 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
1865 Menu *nudge_menu = manage (new Menu());
1866 MenuList& nudge_items = nudge_menu->items();
1867 nudge_menu->set_name ("ArdourContextMenu");
1869 edit_items.push_back (SeparatorElem());
1870 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1871 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1872 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1873 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1875 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1879 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
1881 using namespace Menu_Helpers;
1885 Menu *play_menu = manage (new Menu);
1886 MenuList& play_items = play_menu->items();
1887 play_menu->set_name ("ArdourContextMenu");
1889 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1890 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1891 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1895 Menu *select_menu = manage (new Menu);
1896 MenuList& select_items = select_menu->items();
1897 select_menu->set_name ("ArdourContextMenu");
1899 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1900 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1901 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1902 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1903 select_items.push_back (SeparatorElem());
1904 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1905 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1906 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1907 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1909 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1913 Menu *cutnpaste_menu = manage (new Menu);
1914 MenuList& cutnpaste_items = cutnpaste_menu->items();
1915 cutnpaste_menu->set_name ("ArdourContextMenu");
1917 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1918 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1919 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1921 Menu *nudge_menu = manage (new Menu());
1922 MenuList& nudge_items = nudge_menu->items();
1923 nudge_menu->set_name ("ArdourContextMenu");
1925 edit_items.push_back (SeparatorElem());
1926 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1927 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1928 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1929 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1931 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1935 Editor::snap_type() const
1941 Editor::snap_mode() const
1947 Editor::set_snap_to (SnapType st)
1949 unsigned int snap_ind = (unsigned int)st;
1953 if (snap_ind > snap_type_strings.size() - 1) {
1955 _snap_type = (SnapType)snap_ind;
1958 string str = snap_type_strings[snap_ind];
1960 if (str != snap_type_selector.get_active_text()) {
1961 snap_type_selector.set_active_text (str);
1966 switch (_snap_type) {
1967 case SnapToBeatDiv32:
1968 case SnapToBeatDiv28:
1969 case SnapToBeatDiv24:
1970 case SnapToBeatDiv20:
1971 case SnapToBeatDiv16:
1972 case SnapToBeatDiv14:
1973 case SnapToBeatDiv12:
1974 case SnapToBeatDiv10:
1975 case SnapToBeatDiv8:
1976 case SnapToBeatDiv7:
1977 case SnapToBeatDiv6:
1978 case SnapToBeatDiv5:
1979 case SnapToBeatDiv4:
1980 case SnapToBeatDiv3:
1981 case SnapToBeatDiv2:
1982 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
1983 update_tempo_based_rulers ();
1986 case SnapToRegionStart:
1987 case SnapToRegionEnd:
1988 case SnapToRegionSync:
1989 case SnapToRegionBoundary:
1990 build_region_boundary_cache ();
1998 SnapChanged (); /* EMIT SIGNAL */
2002 Editor::set_snap_mode (SnapMode mode)
2005 string str = snap_mode_strings[(int)mode];
2007 if (str != snap_mode_selector.get_active_text ()) {
2008 snap_mode_selector.set_active_text (str);
2014 Editor::set_edit_point_preference (EditPoint ep, bool force)
2016 bool changed = (_edit_point != ep);
2019 string str = edit_point_strings[(int)ep];
2021 if (str != edit_point_selector.get_active_text ()) {
2022 edit_point_selector.set_active_text (str);
2025 set_canvas_cursor ();
2027 if (!force && !changed) {
2031 const char* action=NULL;
2033 switch (_edit_point) {
2034 case EditAtPlayhead:
2035 action = "edit-at-playhead";
2037 case EditAtSelectedMarker:
2038 action = "edit-at-marker";
2041 action = "edit-at-mouse";
2045 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2047 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2051 bool in_track_canvas;
2053 if (!mouse_frame (foo, in_track_canvas)) {
2054 in_track_canvas = false;
2057 reset_canvas_action_sensitivity (in_track_canvas);
2063 Editor::set_state (const XMLNode& node, int /*version*/)
2065 const XMLProperty* prop;
2067 int x, y, xoff, yoff;
2070 if ((prop = node.property ("id")) != 0) {
2071 _id = prop->value ();
2074 g.base_width = default_width;
2075 g.base_height = default_height;
2081 if ((geometry = find_named_node (node, "geometry")) != 0) {
2085 if ((prop = geometry->property("x_size")) == 0) {
2086 prop = geometry->property ("x-size");
2089 g.base_width = atoi(prop->value());
2091 if ((prop = geometry->property("y_size")) == 0) {
2092 prop = geometry->property ("y-size");
2095 g.base_height = atoi(prop->value());
2098 if ((prop = geometry->property ("x_pos")) == 0) {
2099 prop = geometry->property ("x-pos");
2102 x = atoi (prop->value());
2105 if ((prop = geometry->property ("y_pos")) == 0) {
2106 prop = geometry->property ("y-pos");
2109 y = atoi (prop->value());
2112 if ((prop = geometry->property ("x_off")) == 0) {
2113 prop = geometry->property ("x-off");
2116 xoff = atoi (prop->value());
2118 if ((prop = geometry->property ("y_off")) == 0) {
2119 prop = geometry->property ("y-off");
2122 yoff = atoi (prop->value());
2126 //set_default_size (g.base_width, g.base_height);
2129 if (_session && (prop = node.property ("playhead"))) {
2131 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2132 playhead_cursor->set_position (pos);
2134 playhead_cursor->set_position (0);
2137 if ((prop = node.property ("mixer-width"))) {
2138 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2141 if ((prop = node.property ("zoom-focus"))) {
2142 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2145 if ((prop = node.property ("zoom"))) {
2146 reset_zoom (PBD::atof (prop->value()));
2148 reset_zoom (frames_per_unit);
2151 if ((prop = node.property ("snap-to"))) {
2152 set_snap_to ((SnapType) atoi (prop->value()));
2155 if ((prop = node.property ("snap-mode"))) {
2156 set_snap_mode ((SnapMode) atoi (prop->value()));
2159 if ((prop = node.property ("mouse-mode"))) {
2160 MouseMode m = str2mousemode(prop->value());
2161 set_mouse_mode (m, true);
2163 set_mouse_mode (MouseObject, true);
2166 if ((prop = node.property ("left-frame")) != 0) {
2168 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2169 reset_x_origin (pos);
2173 if ((prop = node.property ("y-origin")) != 0) {
2174 reset_y_origin (atof (prop->value ()));
2177 if ((prop = node.property ("internal-edit"))) {
2178 bool yn = string_is_affirmative (prop->value());
2179 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2181 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2182 tact->set_active (!yn);
2183 tact->set_active (yn);
2187 if ((prop = node.property ("join-object-range"))) {
2188 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2191 if ((prop = node.property ("edit-point"))) {
2192 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2195 if ((prop = node.property ("show-measures"))) {
2196 bool yn = string_is_affirmative (prop->value());
2197 _show_measures = yn;
2198 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2200 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2201 /* do it twice to force the change */
2202 tact->set_active (!yn);
2203 tact->set_active (yn);
2207 if ((prop = node.property ("follow-playhead"))) {
2208 bool yn = string_is_affirmative (prop->value());
2209 set_follow_playhead (yn);
2210 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2212 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2213 if (tact->get_active() != yn) {
2214 tact->set_active (yn);
2219 if ((prop = node.property ("stationary-playhead"))) {
2220 bool yn = (prop->value() == "yes");
2221 set_stationary_playhead (yn);
2222 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2224 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2225 if (tact->get_active() != yn) {
2226 tact->set_active (yn);
2231 if ((prop = node.property ("region-list-sort-type"))) {
2232 RegionListSortType st;
2233 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2236 if ((prop = node.property ("xfades-visible"))) {
2237 bool yn = string_is_affirmative (prop->value());
2238 _xfade_visibility = !yn;
2239 // set_xfade_visibility (yn);
2242 if ((prop = node.property ("show-editor-mixer"))) {
2244 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2247 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2248 bool yn = string_is_affirmative (prop->value());
2250 /* do it twice to force the change */
2252 tact->set_active (!yn);
2253 tact->set_active (yn);
2256 if ((prop = node.property ("show-editor-list"))) {
2258 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2261 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2262 bool yn = string_is_affirmative (prop->value());
2264 /* do it twice to force the change */
2266 tact->set_active (!yn);
2267 tact->set_active (yn);
2270 if ((prop = node.property (X_("editor-list-page")))) {
2271 _the_notebook.set_current_page (atoi (prop->value ()));
2274 if ((prop = node.property (X_("show-marker-lines")))) {
2275 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2277 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2278 bool yn = string_is_affirmative (prop->value ());
2280 tact->set_active (!yn);
2281 tact->set_active (yn);
2284 XMLNodeList children = node.children ();
2285 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2286 selection->set_state (**i, Stateful::current_state_version);
2287 _regions->set_state (**i);
2294 Editor::get_state ()
2296 XMLNode* node = new XMLNode ("Editor");
2299 _id.print (buf, sizeof (buf));
2300 node->add_property ("id", buf);
2302 if (is_realized()) {
2303 Glib::RefPtr<Gdk::Window> win = get_window();
2305 int x, y, xoff, yoff, width, height;
2306 win->get_root_origin(x, y);
2307 win->get_position(xoff, yoff);
2308 win->get_size(width, height);
2310 XMLNode* geometry = new XMLNode ("geometry");
2312 snprintf(buf, sizeof(buf), "%d", width);
2313 geometry->add_property("x-size", string(buf));
2314 snprintf(buf, sizeof(buf), "%d", height);
2315 geometry->add_property("y-size", string(buf));
2316 snprintf(buf, sizeof(buf), "%d", x);
2317 geometry->add_property("x-pos", string(buf));
2318 snprintf(buf, sizeof(buf), "%d", y);
2319 geometry->add_property("y-pos", string(buf));
2320 snprintf(buf, sizeof(buf), "%d", xoff);
2321 geometry->add_property("x-off", string(buf));
2322 snprintf(buf, sizeof(buf), "%d", yoff);
2323 geometry->add_property("y-off", string(buf));
2324 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2325 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2326 geometry->add_property("notebook-shrunk", _notebook_shrunk ? "1" : "0");
2327 snprintf(buf,sizeof(buf), "%d",pre_maximal_horizontal_pane_position);
2328 geometry->add_property("pre-maximal-horizontal-pane-position", string(buf));
2329 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2330 geometry->add_property("edit-vertical-pane-pos", string(buf));
2332 node->add_child_nocopy (*geometry);
2335 maybe_add_mixer_strip_width (*node);
2337 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2338 node->add_property ("zoom-focus", buf);
2339 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2340 node->add_property ("zoom", buf);
2341 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2342 node->add_property ("snap-to", buf);
2343 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2344 node->add_property ("snap-mode", buf);
2346 node->add_property ("edit-point", enum_2_string (_edit_point));
2348 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2349 node->add_property ("playhead", buf);
2350 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2351 node->add_property ("left-frame", buf);
2352 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2353 node->add_property ("y-origin", buf);
2355 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2356 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2357 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2358 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2359 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2360 node->add_property ("mouse-mode", enum2str(mouse_mode));
2361 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2362 node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2364 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2366 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2367 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2370 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2372 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2373 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2376 snprintf (buf, sizeof (buf), "%d", _the_notebook.get_current_page ());
2377 node->add_property (X_("editor-list-page"), buf);
2379 node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no");
2381 node->add_child_nocopy (selection->get_state ());
2382 node->add_child_nocopy (_regions->get_state ());
2389 /** @param y y offset from the top of all trackviews.
2390 * @return pair: TimeAxisView that y is over, layer index.
2391 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2392 * in stacked region display mode, otherwise 0.
2394 std::pair<TimeAxisView *, layer_t>
2395 Editor::trackview_by_y_position (double y)
2397 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2399 std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
2405 return std::make_pair ( (TimeAxisView *) 0, 0);
2408 /** Snap a position to the grid, if appropriate, taking into account current
2409 * grid settings and also the state of any snap modifier keys that may be pressed.
2410 * @param start Position to snap.
2411 * @param event Event to get current key modifier information from, or 0.
2414 Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2416 if (!_session || !event) {
2420 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2421 if (_snap_mode == SnapOff) {
2422 snap_to_internal (start, direction, for_mark);
2425 if (_snap_mode != SnapOff) {
2426 snap_to_internal (start, direction, for_mark);
2432 Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark)
2434 if (!_session || _snap_mode == SnapOff) {
2438 snap_to_internal (start, direction, for_mark);
2442 Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /*for_mark*/)
2444 const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2445 framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2447 switch (_snap_type) {
2448 case SnapToTimecodeFrame:
2449 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2450 start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2452 start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2456 case SnapToTimecodeSeconds:
2457 if (_session->config.get_timecode_offset_negative()) {
2458 start += _session->config.get_timecode_offset ();
2460 start -= _session->config.get_timecode_offset ();
2462 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2463 start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2465 start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2468 if (_session->config.get_timecode_offset_negative()) {
2469 start -= _session->config.get_timecode_offset ();
2471 start += _session->config.get_timecode_offset ();
2475 case SnapToTimecodeMinutes:
2476 if (_session->config.get_timecode_offset_negative()) {
2477 start += _session->config.get_timecode_offset ();
2479 start -= _session->config.get_timecode_offset ();
2481 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2482 start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2484 start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2486 if (_session->config.get_timecode_offset_negative()) {
2487 start -= _session->config.get_timecode_offset ();
2489 start += _session->config.get_timecode_offset ();
2493 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2499 Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark)
2501 const framepos_t one_second = _session->frame_rate();
2502 const framepos_t one_minute = _session->frame_rate() * 60;
2503 framepos_t presnap = start;
2507 switch (_snap_type) {
2508 case SnapToTimecodeFrame:
2509 case SnapToTimecodeSeconds:
2510 case SnapToTimecodeMinutes:
2511 return timecode_snap_to_internal (start, direction, for_mark);
2514 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2515 start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2517 start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2522 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2523 start = (framepos_t) ceil ((double) start / one_second) * one_second;
2525 start = (framepos_t) floor ((double) start / one_second) * one_second;
2530 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2531 start = (framepos_t) ceil ((double) start / one_minute) * one_minute;
2533 start = (framepos_t) floor ((double) start / one_minute) * one_minute;
2538 start = _session->tempo_map().round_to_bar (start, direction);
2542 start = _session->tempo_map().round_to_beat (start, direction);
2545 case SnapToBeatDiv32:
2546 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2548 case SnapToBeatDiv28:
2549 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2551 case SnapToBeatDiv24:
2552 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2554 case SnapToBeatDiv20:
2555 start = _session->tempo_map().round_to_beat_subdivision (start, 20, direction);
2557 case SnapToBeatDiv16:
2558 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2560 case SnapToBeatDiv14:
2561 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2563 case SnapToBeatDiv12:
2564 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2566 case SnapToBeatDiv10:
2567 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2569 case SnapToBeatDiv8:
2570 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2572 case SnapToBeatDiv7:
2573 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2575 case SnapToBeatDiv6:
2576 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2578 case SnapToBeatDiv5:
2579 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2581 case SnapToBeatDiv4:
2582 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2584 case SnapToBeatDiv3:
2585 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2587 case SnapToBeatDiv2:
2588 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2596 _session->locations()->marks_either_side (start, before, after);
2598 if (before == max_framepos) {
2600 } else if (after == max_framepos) {
2602 } else if (before != max_framepos && after != max_framepos) {
2603 /* have before and after */
2604 if ((start - before) < (after - start)) {
2613 case SnapToRegionStart:
2614 case SnapToRegionEnd:
2615 case SnapToRegionSync:
2616 case SnapToRegionBoundary:
2617 if (!region_boundary_cache.empty()) {
2619 vector<framepos_t>::iterator prev = region_boundary_cache.end ();
2620 vector<framepos_t>::iterator next = region_boundary_cache.end ();
2622 if (direction > 0) {
2623 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2625 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2628 if (next != region_boundary_cache.begin ()) {
2633 framepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2634 framepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2636 if (start > (p + n) / 2) {
2645 switch (_snap_mode) {
2651 if (presnap > start) {
2652 if (presnap > (start + unit_to_frame(snap_threshold))) {
2656 } else if (presnap < start) {
2657 if (presnap < (start - unit_to_frame(snap_threshold))) {
2663 /* handled at entry */
2671 Editor::setup_toolbar ()
2675 /* Mode Buttons (tool selection) */
2677 mouse_move_button.set_relief(Gtk::RELIEF_NONE);
2678 mouse_select_button.set_relief(Gtk::RELIEF_NONE);
2679 mouse_gain_button.set_relief(Gtk::RELIEF_NONE);
2680 mouse_zoom_button.set_relief(Gtk::RELIEF_NONE);
2681 mouse_timefx_button.set_relief(Gtk::RELIEF_NONE);
2682 mouse_audition_button.set_relief(Gtk::RELIEF_NONE);
2683 // internal_edit_button.set_relief(Gtk::RELIEF_NONE);
2684 join_object_range_button.set_relief(Gtk::RELIEF_NONE);
2686 HBox* mode_box = manage(new HBox);
2687 mode_box->set_border_width (2);
2688 mode_box->set_spacing(4);
2690 /* table containing mode buttons */
2692 HBox* mouse_mode_button_box = manage (new HBox ());
2694 if (Profile->get_sae()) {
2695 mouse_mode_button_box->pack_start (mouse_move_button);
2697 mouse_mode_button_box->pack_start (mouse_move_button);
2698 mouse_mode_button_box->pack_start (join_object_range_button);
2699 mouse_mode_button_box->pack_start (mouse_select_button);
2702 mouse_mode_button_box->pack_start (mouse_zoom_button);
2704 if (!Profile->get_sae()) {
2705 mouse_mode_button_box->pack_start (mouse_gain_button);
2708 mouse_mode_button_box->pack_start (mouse_timefx_button);
2709 mouse_mode_button_box->pack_start (mouse_audition_button);
2710 mouse_mode_button_box->pack_start (internal_edit_button);
2712 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2713 if (!Profile->get_sae()) {
2714 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2716 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2718 edit_mode_selector.set_name ("EditModeSelector");
2719 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2720 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2722 mode_box->pack_start (edit_mode_selector);
2723 mode_box->pack_start (*mouse_mode_button_box);
2725 _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2726 _mouse_mode_tearoff->set_name ("MouseModeBase");
2727 _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2729 if (Profile->get_sae()) {
2730 _mouse_mode_tearoff->set_can_be_torn_off (false);
2733 _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2734 &_mouse_mode_tearoff->tearoff_window()));
2735 _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2736 &_mouse_mode_tearoff->tearoff_window(), 1));
2737 _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2738 &_mouse_mode_tearoff->tearoff_window()));
2739 _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2740 &_mouse_mode_tearoff->tearoff_window(), 1));
2742 mouse_move_button.set_mode (false);
2743 mouse_select_button.set_mode (false);
2744 mouse_gain_button.set_mode (false);
2745 mouse_zoom_button.set_mode (false);
2746 mouse_timefx_button.set_mode (false);
2747 mouse_audition_button.set_mode (false);
2748 join_object_range_button.set_mode (false);
2750 mouse_move_button.set_name ("MouseModeButton");
2751 mouse_select_button.set_name ("MouseModeButton");
2752 mouse_gain_button.set_name ("MouseModeButton");
2753 mouse_zoom_button.set_name ("MouseModeButton");
2754 mouse_timefx_button.set_name ("MouseModeButton");
2755 mouse_audition_button.set_name ("MouseModeButton");
2756 internal_edit_button.set_name ("MouseModeButton");
2757 join_object_range_button.set_name ("MouseModeButton");
2759 mouse_move_button.unset_flags (CAN_FOCUS);
2760 mouse_select_button.unset_flags (CAN_FOCUS);
2761 mouse_gain_button.unset_flags (CAN_FOCUS);
2762 mouse_zoom_button.unset_flags (CAN_FOCUS);
2763 mouse_timefx_button.unset_flags (CAN_FOCUS);
2764 mouse_audition_button.unset_flags (CAN_FOCUS);
2765 internal_edit_button.unset_flags (CAN_FOCUS);
2766 join_object_range_button.unset_flags (CAN_FOCUS);
2770 _zoom_box.set_spacing (1);
2771 _zoom_box.set_border_width (0);
2773 zoom_in_button.set_name ("EditorTimeButton");
2774 zoom_in_button.set_image (*(manage (new Image (Stock::ZOOM_IN, Gtk::ICON_SIZE_MENU))));
2775 zoom_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), false));
2777 zoom_out_button.set_name ("EditorTimeButton");
2778 zoom_out_button.set_image (*(manage (new Image (Stock::ZOOM_OUT, Gtk::ICON_SIZE_MENU))));
2779 zoom_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), true));
2781 zoom_out_full_button.set_name ("EditorTimeButton");
2782 zoom_out_full_button.set_image (*(manage (new Image (Stock::ZOOM_100, Gtk::ICON_SIZE_MENU))));
2783 zoom_out_full_button.signal_clicked().connect (sigc::mem_fun(*this, &Editor::temporal_zoom_session));
2785 zoom_focus_selector.set_name ("ZoomFocusSelector");
2786 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
2787 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2789 _zoom_box.pack_start (zoom_out_button, false, false);
2790 _zoom_box.pack_start (zoom_in_button, false, false);
2791 _zoom_box.pack_start (zoom_out_full_button, false, false);
2793 _zoom_box.pack_start (zoom_focus_selector);
2795 /* Track zoom buttons */
2796 tav_expand_button.set_name ("TrackHeightButton");
2797 tav_expand_button.set_size_request(-1,20);
2798 tav_expand_button.add (*(manage (new Image (::get_icon("tav_exp")))));
2799 tav_expand_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), false));
2801 tav_shrink_button.set_name ("TrackHeightButton");
2802 tav_shrink_button.set_size_request(-1,20);
2803 tav_shrink_button.add (*(manage (new Image (::get_icon("tav_shrink")))));
2804 tav_shrink_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), true));
2806 _zoom_box.pack_start (tav_shrink_button);
2807 _zoom_box.pack_start (tav_expand_button);
2809 _zoom_tearoff = manage (new TearOff (_zoom_box));
2811 _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2812 &_zoom_tearoff->tearoff_window()));
2813 _zoom_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2814 &_zoom_tearoff->tearoff_window(), 0));
2815 _zoom_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2816 &_zoom_tearoff->tearoff_window()));
2817 _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2818 &_zoom_tearoff->tearoff_window(), 0));
2820 snap_box.set_spacing (1);
2821 snap_box.set_border_width (2);
2823 snap_type_selector.set_name ("SnapTypeSelector");
2824 set_popdown_strings (snap_type_selector, snap_type_strings, true);
2825 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2827 snap_mode_selector.set_name ("SnapModeSelector");
2828 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
2829 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2831 edit_point_selector.set_name ("EditPointSelector");
2832 set_popdown_strings (edit_point_selector, edit_point_strings, true);
2833 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2835 snap_box.pack_start (snap_mode_selector, false, false);
2836 snap_box.pack_start (snap_type_selector, false, false);
2837 snap_box.pack_start (edit_point_selector, false, false);
2841 HBox *nudge_box = manage (new HBox);
2842 nudge_box->set_spacing(1);
2843 nudge_box->set_border_width (2);
2845 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2846 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2848 nudge_box->pack_start (nudge_backward_button, false, false);
2849 nudge_box->pack_start (nudge_forward_button, false, false);
2850 nudge_box->pack_start (nudge_clock, false, false);
2853 /* Pack everything in... */
2855 HBox* hbox = manage (new HBox);
2856 hbox->set_spacing(10);
2858 _tools_tearoff = manage (new TearOff (*hbox));
2859 _tools_tearoff->set_name ("MouseModeBase");
2860 _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
2862 if (Profile->get_sae()) {
2863 _tools_tearoff->set_can_be_torn_off (false);
2866 _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2867 &_tools_tearoff->tearoff_window()));
2868 _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2869 &_tools_tearoff->tearoff_window(), 0));
2870 _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2871 &_tools_tearoff->tearoff_window()));
2872 _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2873 &_tools_tearoff->tearoff_window(), 0));
2875 toolbar_hbox.set_spacing (10);
2876 toolbar_hbox.set_border_width (1);
2878 toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
2879 toolbar_hbox.pack_start (*_zoom_tearoff, false, false);
2880 toolbar_hbox.pack_start (*_tools_tearoff, false, false);
2882 hbox->pack_start (snap_box, false, false);
2883 hbox->pack_start (*nudge_box, false, false);
2884 hbox->pack_start (panic_box, false, false);
2888 toolbar_base.set_name ("ToolBarBase");
2889 toolbar_base.add (toolbar_hbox);
2891 _toolbar_viewport.add (toolbar_base);
2892 /* stick to the required height but allow width to vary if there's not enough room */
2893 _toolbar_viewport.set_size_request (1, -1);
2895 toolbar_frame.set_shadow_type (SHADOW_OUT);
2896 toolbar_frame.set_name ("BaseFrame");
2897 toolbar_frame.add (_toolbar_viewport);
2899 DPIReset.connect (sigc::mem_fun (*this, &Editor::resize_text_widgets));
2903 Editor::setup_tooltips ()
2905 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
2906 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain"));
2907 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
2908 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
2909 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2910 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
2911 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
2912 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
2913 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
2914 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
2915 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
2916 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
2917 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
2918 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
2919 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
2920 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
2921 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
2922 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
2923 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
2924 ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
2925 ARDOUR_UI::instance()->set_tip (midi_panic_button, _("Send note off and reset controller messages on all MIDI channels"));
2926 ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode"));
2930 Editor::midi_panic ()
2932 cerr << "MIDI panic\n";
2935 _session->midi_panic();
2940 Editor::setup_midi_toolbar ()
2944 /* Midi sound notes */
2945 midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
2946 midi_sound_notes.set_relief(Gtk::RELIEF_NONE);
2947 midi_sound_notes.unset_flags (CAN_FOCUS);
2951 act = ActionManager::get_action (X_("MIDI"), X_("panic"));
2952 midi_panic_button.set_name("MidiPanicButton");
2953 act->connect_proxy (midi_panic_button);
2955 panic_box.pack_start (midi_sound_notes , true, true);
2956 panic_box.pack_start (midi_panic_button, true, true);
2960 Editor::convert_drop_to_paths (
2961 vector<string>& paths,
2962 const RefPtr<Gdk::DragContext>& /*context*/,
2965 const SelectionData& data,
2969 if (_session == 0) {
2973 vector<string> uris = data.get_uris();
2977 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
2978 are actually URI lists. So do it by hand.
2981 if (data.get_target() != "text/plain") {
2985 /* Parse the "uri-list" format that Nautilus provides,
2986 where each pathname is delimited by \r\n.
2988 THERE MAY BE NO NULL TERMINATING CHAR!!!
2991 string txt = data.get_text();
2995 p = (const char *) malloc (txt.length() + 1);
2996 txt.copy ((char *) p, txt.length(), 0);
2997 ((char*)p)[txt.length()] = '\0';
3003 while (g_ascii_isspace (*p))
3007 while (*q && (*q != '\n') && (*q != '\r')) {
3014 while (q > p && g_ascii_isspace (*q))
3019 uris.push_back (string (p, q - p + 1));
3023 p = strchr (p, '\n');
3035 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3037 if ((*i).substr (0,7) == "file://") {
3040 PBD::url_decode (p);
3042 // scan forward past three slashes
3044 string::size_type slashcnt = 0;
3045 string::size_type n = 0;
3046 string::iterator x = p.begin();
3048 while (slashcnt < 3 && x != p.end()) {
3051 } else if (slashcnt == 3) {
3058 if (slashcnt != 3 || x == p.end()) {
3059 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3063 paths.push_back (p.substr (n - 1));
3071 Editor::new_tempo_section ()
3077 Editor::map_transport_state ()
3079 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3081 if (_session && _session->transport_stopped()) {
3082 have_pending_keyboard_selection = false;
3085 update_loop_range_view (true);
3090 Editor::State::State (PublicEditor const * e)
3092 selection = new Selection (e);
3095 Editor::State::~State ()
3101 Editor::begin_reversible_command (string name)
3104 _session->begin_reversible_command (name);
3109 Editor::begin_reversible_command (GQuark q)
3112 _session->begin_reversible_command (q);
3117 Editor::commit_reversible_command ()
3120 _session->commit_reversible_command ();
3125 Editor::history_changed ()
3129 if (undo_action && _session) {
3130 if (_session->undo_depth() == 0) {
3133 label = string_compose(_("Undo (%1)"), _session->next_undo());
3135 undo_action->property_label() = label;
3138 if (redo_action && _session) {
3139 if (_session->redo_depth() == 0) {
3142 label = string_compose(_("Redo (%1)"), _session->next_redo());
3144 redo_action->property_label() = label;
3149 Editor::duplicate_dialog (bool with_dialog)
3153 if (mouse_mode == MouseRange) {
3154 if (selection->time.length() == 0) {
3159 RegionSelection rs = get_regions_from_selection_and_entered ();
3161 if (mouse_mode != MouseRange && rs.empty()) {
3167 ArdourDialog win (_("Duplicate"));
3168 Label label (_("Number of duplications:"));
3169 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3170 SpinButton spinner (adjustment, 0.0, 1);
3173 win.get_vbox()->set_spacing (12);
3174 win.get_vbox()->pack_start (hbox);
3175 hbox.set_border_width (6);
3176 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3178 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3179 place, visually. so do this by hand.
3182 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3183 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3184 spinner.grab_focus();
3190 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3191 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3192 win.set_default_response (RESPONSE_ACCEPT);
3194 win.set_position (WIN_POS_MOUSE);
3196 spinner.grab_focus ();
3198 switch (win.run ()) {
3199 case RESPONSE_ACCEPT:
3205 times = adjustment.get_value();
3208 if (mouse_mode == MouseRange) {
3209 duplicate_selection (times);
3211 duplicate_some_regions (rs, times);
3216 Editor::show_verbose_canvas_cursor ()
3218 verbose_canvas_cursor->raise_to_top();
3219 verbose_canvas_cursor->show();
3220 verbose_cursor_visible = true;
3224 Editor::hide_verbose_canvas_cursor ()
3226 verbose_canvas_cursor->hide();
3227 verbose_cursor_visible = false;
3231 Editor::clamp_verbose_cursor_x (double x)
3236 x = min (_canvas_width - 200.0, x);
3242 Editor::clamp_verbose_cursor_y (double y)
3244 if (y < canvas_timebars_vsize) {
3245 y = canvas_timebars_vsize;
3247 y = min (_canvas_height - 50, y);
3253 Editor::show_verbose_canvas_cursor_with (const string & txt, int32_t xoffset, int32_t yoffset)
3255 verbose_canvas_cursor->property_text() = txt.c_str();
3260 track_canvas->get_pointer (x, y);
3261 track_canvas->window_to_world (x, y, wx, wy);
3266 /* don't get too close to the edge */
3267 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (wx);
3268 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (wy);
3270 show_verbose_canvas_cursor ();
3274 Editor::set_verbose_canvas_cursor (const string & txt, double x, double y)
3276 verbose_canvas_cursor->property_text() = txt.c_str();
3277 /* don't get too close to the edge */
3278 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (x);
3279 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (y);
3283 Editor::set_verbose_canvas_cursor_text (const string & txt)
3285 verbose_canvas_cursor->property_text() = txt.c_str();
3289 Editor::set_edit_mode (EditMode m)
3291 Config->set_edit_mode (m);
3295 Editor::cycle_edit_mode ()
3297 switch (Config->get_edit_mode()) {
3299 if (Profile->get_sae()) {
3300 Config->set_edit_mode (Lock);
3302 Config->set_edit_mode (Splice);
3306 Config->set_edit_mode (Lock);
3309 Config->set_edit_mode (Slide);
3315 Editor::edit_mode_selection_done ()
3317 string s = edit_mode_selector.get_active_text ();
3320 Config->set_edit_mode (string_to_edit_mode (s));
3325 Editor::snap_type_selection_done ()
3327 string choice = snap_type_selector.get_active_text();
3328 SnapType snaptype = SnapToBeat;
3330 if (choice == _("Beats/2")) {
3331 snaptype = SnapToBeatDiv2;
3332 } else if (choice == _("Beats/3")) {
3333 snaptype = SnapToBeatDiv3;
3334 } else if (choice == _("Beats/4")) {
3335 snaptype = SnapToBeatDiv4;
3336 } else if (choice == _("Beats/5")) {
3337 snaptype = SnapToBeatDiv5;
3338 } else if (choice == _("Beats/6")) {
3339 snaptype = SnapToBeatDiv6;
3340 } else if (choice == _("Beats/7")) {
3341 snaptype = SnapToBeatDiv7;
3342 } else if (choice == _("Beats/8")) {
3343 snaptype = SnapToBeatDiv8;
3344 } else if (choice == _("Beats/10")) {
3345 snaptype = SnapToBeatDiv10;
3346 } else if (choice == _("Beats/12")) {
3347 snaptype = SnapToBeatDiv12;
3348 } else if (choice == _("Beats/14")) {
3349 snaptype = SnapToBeatDiv14;
3350 } else if (choice == _("Beats/16")) {
3351 snaptype = SnapToBeatDiv16;
3352 } else if (choice == _("Beats/20")) {
3353 snaptype = SnapToBeatDiv20;
3354 } else if (choice == _("Beats/24")) {
3355 snaptype = SnapToBeatDiv24;
3356 } else if (choice == _("Beats/28")) {
3357 snaptype = SnapToBeatDiv28;
3358 } else if (choice == _("Beats/32")) {
3359 snaptype = SnapToBeatDiv32;
3360 } else if (choice == _("Beats")) {
3361 snaptype = SnapToBeat;
3362 } else if (choice == _("Bars")) {
3363 snaptype = SnapToBar;
3364 } else if (choice == _("Marks")) {
3365 snaptype = SnapToMark;
3366 } else if (choice == _("Region starts")) {
3367 snaptype = SnapToRegionStart;
3368 } else if (choice == _("Region ends")) {
3369 snaptype = SnapToRegionEnd;
3370 } else if (choice == _("Region bounds")) {
3371 snaptype = SnapToRegionBoundary;
3372 } else if (choice == _("Region syncs")) {
3373 snaptype = SnapToRegionSync;
3374 } else if (choice == _("CD Frames")) {
3375 snaptype = SnapToCDFrame;
3376 } else if (choice == _("Timecode Frames")) {
3377 snaptype = SnapToTimecodeFrame;
3378 } else if (choice == _("Timecode Seconds")) {
3379 snaptype = SnapToTimecodeSeconds;
3380 } else if (choice == _("Timecode Minutes")) {
3381 snaptype = SnapToTimecodeMinutes;
3382 } else if (choice == _("Seconds")) {
3383 snaptype = SnapToSeconds;
3384 } else if (choice == _("Minutes")) {
3385 snaptype = SnapToMinutes;
3388 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3390 ract->set_active ();
3395 Editor::snap_mode_selection_done ()
3397 string choice = snap_mode_selector.get_active_text();
3398 SnapMode mode = SnapNormal;
3400 if (choice == _("No Grid")) {
3402 } else if (choice == _("Grid")) {
3404 } else if (choice == _("Magnetic")) {
3405 mode = SnapMagnetic;
3408 RefPtr<RadioAction> ract = snap_mode_action (mode);
3411 ract->set_active (true);
3416 Editor::cycle_edit_point (bool with_marker)
3418 switch (_edit_point) {
3420 set_edit_point_preference (EditAtPlayhead);
3422 case EditAtPlayhead:
3424 set_edit_point_preference (EditAtSelectedMarker);
3426 set_edit_point_preference (EditAtMouse);
3429 case EditAtSelectedMarker:
3430 set_edit_point_preference (EditAtMouse);
3436 Editor::edit_point_selection_done ()
3438 string choice = edit_point_selector.get_active_text();
3439 EditPoint ep = EditAtSelectedMarker;
3441 if (choice == _("Marker")) {
3442 set_edit_point_preference (EditAtSelectedMarker);
3443 } else if (choice == _("Playhead")) {
3444 set_edit_point_preference (EditAtPlayhead);
3446 set_edit_point_preference (EditAtMouse);
3449 RefPtr<RadioAction> ract = edit_point_action (ep);
3452 ract->set_active (true);
3457 Editor::zoom_focus_selection_done ()
3459 string choice = zoom_focus_selector.get_active_text();
3460 ZoomFocus focus_type = ZoomFocusLeft;
3462 if (choice == _("Left")) {
3463 focus_type = ZoomFocusLeft;
3464 } else if (choice == _("Right")) {
3465 focus_type = ZoomFocusRight;
3466 } else if (choice == _("Center")) {
3467 focus_type = ZoomFocusCenter;
3468 } else if (choice == _("Playhead")) {
3469 focus_type = ZoomFocusPlayhead;
3470 } else if (choice == _("Mouse")) {
3471 focus_type = ZoomFocusMouse;
3472 } else if (choice == _("Edit point")) {
3473 focus_type = ZoomFocusEdit;
3476 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3479 ract->set_active ();
3484 Editor::edit_controls_button_release (GdkEventButton* ev)
3486 if (Keyboard::is_context_menu_event (ev)) {
3487 ARDOUR_UI::instance()->add_route (this);
3493 Editor::mouse_select_button_release (GdkEventButton* ev)
3495 /* this handles just right-clicks */
3497 if (ev->button != 3) {
3505 Editor::set_zoom_focus (ZoomFocus f)
3507 string str = zoom_focus_strings[(int)f];
3509 if (str != zoom_focus_selector.get_active_text()) {
3510 zoom_focus_selector.set_active_text (str);
3513 if (zoom_focus != f) {
3516 ZoomFocusChanged (); /* EMIT_SIGNAL */
3523 Editor::ensure_float (Window& win)
3525 win.set_transient_for (*this);
3529 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3531 /* recover or initialize pane positions. do this here rather than earlier because
3532 we don't want the positions to change the child allocations, which they seem to do.
3538 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3550 width = default_width;
3551 height = default_height;
3553 if ((geometry = find_named_node (*node, "geometry")) != 0) {
3555 prop = geometry->property ("x-size");
3557 width = atoi (prop->value());
3559 prop = geometry->property ("y-size");
3561 height = atoi (prop->value());
3565 if (which == static_cast<Paned*> (&edit_pane)) {
3567 if (done & Horizontal) {
3571 if (geometry && (prop = geometry->property ("notebook-shrunk"))) {
3572 _notebook_shrunk = string_is_affirmative (prop->value ());
3575 if (geometry && (prop = geometry->property ("pre-maximal-horizontal-pane-position"))) {
3576 pre_maximal_horizontal_pane_position = atoi (prop->value ());
3579 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3580 /* initial allocation is 90% to canvas, 10% to notebook */
3581 pos = (int) floor (alloc.get_width() * 0.90f);
3582 snprintf (buf, sizeof(buf), "%d", pos);
3584 pos = atoi (prop->value());
3587 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3588 edit_pane.set_position (pos);
3589 if (pre_maximal_horizontal_pane_position == 0) {
3590 pre_maximal_horizontal_pane_position = pos;
3594 done = (Pane) (done | Horizontal);
3596 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3598 if (done & Vertical) {
3602 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3603 /* initial allocation is 90% to canvas, 10% to summary */
3604 pos = (int) floor (alloc.get_height() * 0.90f);
3605 snprintf (buf, sizeof(buf), "%d", pos);
3607 pos = atoi (prop->value());
3610 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3611 editor_summary_pane.set_position (pos);
3612 pre_maximal_vertical_pane_position = pos;
3615 done = (Pane) (done | Vertical);
3620 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3622 if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) {
3623 top_hbox.remove (toolbar_frame);
3628 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3630 if (toolbar_frame.get_parent() == 0) {
3631 top_hbox.pack_end (toolbar_frame);
3636 Editor::set_show_measures (bool yn)
3638 if (_show_measures != yn) {
3641 if ((_show_measures = yn) == true) {
3643 tempo_lines->show();
3651 Editor::toggle_follow_playhead ()
3653 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3655 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3656 set_follow_playhead (tact->get_active());
3660 /** @param yn true to follow playhead, otherwise false.
3661 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
3664 Editor::set_follow_playhead (bool yn, bool catch_up)
3666 if (_follow_playhead != yn) {
3667 if ((_follow_playhead = yn) == true && catch_up) {
3669 reset_x_origin_to_follow_playhead ();
3676 Editor::toggle_stationary_playhead ()
3678 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3680 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3681 set_stationary_playhead (tact->get_active());
3686 Editor::set_stationary_playhead (bool yn)
3688 if (_stationary_playhead != yn) {
3689 if ((_stationary_playhead = yn) == true) {
3691 // FIXME need a 3.0 equivalent of this 2.X call
3692 // update_current_screen ();
3699 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3701 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3703 xfade->set_active (!xfade->active());
3708 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3710 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3712 xfade->set_follow_overlap (!xfade->following_overlap());
3717 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3719 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3725 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3729 switch (cew.run ()) {
3730 case RESPONSE_ACCEPT:
3737 PropertyChange all_crossfade_properties;
3738 all_crossfade_properties.add (ARDOUR::Properties::active);
3739 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3740 xfade->PropertyChanged (all_crossfade_properties);
3744 Editor::playlist_selector () const
3746 return *_playlist_selector;
3750 Editor::get_grid_type_as_beats (bool& success, framepos_t position)
3754 switch (_snap_type) {
3759 case SnapToBeatDiv32:
3762 case SnapToBeatDiv28:
3765 case SnapToBeatDiv24:
3768 case SnapToBeatDiv20:
3771 case SnapToBeatDiv16:
3774 case SnapToBeatDiv14:
3777 case SnapToBeatDiv12:
3780 case SnapToBeatDiv10:
3783 case SnapToBeatDiv8:
3786 case SnapToBeatDiv7:
3789 case SnapToBeatDiv6:
3792 case SnapToBeatDiv5:
3795 case SnapToBeatDiv4:
3798 case SnapToBeatDiv3:
3801 case SnapToBeatDiv2:
3807 return _session->tempo_map().meter_at (position).beats_per_bar();
3812 case SnapToTimecodeFrame:
3813 case SnapToTimecodeSeconds:
3814 case SnapToTimecodeMinutes:
3817 case SnapToRegionStart:
3818 case SnapToRegionEnd:
3819 case SnapToRegionSync:
3820 case SnapToRegionBoundary:
3830 Editor::get_nudge_distance (framepos_t pos, framecnt_t& next)
3834 ret = nudge_clock.current_duration (pos);
3835 next = ret + 1; /* XXXX fix me */
3841 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3843 ArdourDialog dialog (_("Playlist Deletion"));
3844 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3845 "If left alone, no audio files used by it will be cleaned.\n"
3846 "If deleted, audio files used by it alone by will cleaned."),
3849 dialog.set_position (WIN_POS_CENTER);
3850 dialog.get_vbox()->pack_start (label);
3854 dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT);
3855 dialog.add_button (_("Keep playlist"), RESPONSE_REJECT);
3856 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3858 switch (dialog.run ()) {
3859 case RESPONSE_ACCEPT:
3860 /* delete the playlist */
3864 case RESPONSE_REJECT:
3865 /* keep the playlist */
3877 Editor::audio_region_selection_covers (framepos_t where)
3879 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3880 if ((*a)->region()->covers (where)) {
3889 Editor::prepare_for_cleanup ()
3891 cut_buffer->clear_regions ();
3892 cut_buffer->clear_playlists ();
3894 selection->clear_regions ();
3895 selection->clear_playlists ();
3897 _regions->suspend_redisplay ();
3901 Editor::finish_cleanup ()
3903 _regions->resume_redisplay ();
3907 Editor::transport_loop_location()
3910 return _session->locations()->auto_loop_location();
3917 Editor::transport_punch_location()
3920 return _session->locations()->auto_punch_location();
3927 Editor::control_layout_scroll (GdkEventScroll* ev)
3929 if (Keyboard::some_magic_widget_has_focus()) {
3933 switch (ev->direction) {
3935 scroll_tracks_up_line ();
3939 case GDK_SCROLL_DOWN:
3940 scroll_tracks_down_line ();
3944 /* no left/right handling yet */
3952 Editor::session_state_saved (string)
3955 _snapshots->redisplay ();
3959 Editor::maximise_editing_space ()
3961 _mouse_mode_tearoff->set_visible (false);
3962 _tools_tearoff->set_visible (false);
3963 _zoom_tearoff->set_visible (false);
3965 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
3966 pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
3967 pre_maximal_editor_width = this->get_width ();
3968 pre_maximal_editor_height = this->get_height ();
3970 if (post_maximal_horizontal_pane_position == 0) {
3971 post_maximal_horizontal_pane_position = edit_pane.get_width();
3974 if (post_maximal_vertical_pane_position == 0) {
3975 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
3980 if (post_maximal_editor_width) {
3981 edit_pane.set_position (post_maximal_horizontal_pane_position -
3982 abs(post_maximal_editor_width - pre_maximal_editor_width));
3984 edit_pane.set_position (post_maximal_horizontal_pane_position);
3987 if (post_maximal_editor_height) {
3988 editor_summary_pane.set_position (post_maximal_vertical_pane_position -
3989 abs(post_maximal_editor_height - pre_maximal_editor_height));
3991 editor_summary_pane.set_position (post_maximal_vertical_pane_position);
3994 if (Config->get_keep_tearoffs()) {
3995 _mouse_mode_tearoff->set_visible (true);
3996 _tools_tearoff->set_visible (true);
3997 if (Config->get_show_zoom_tools ()) {
3998 _zoom_tearoff->set_visible (true);
4005 Editor::restore_editing_space ()
4007 // user changed width/height of panes during fullscreen
4009 if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
4010 post_maximal_horizontal_pane_position = edit_pane.get_position();
4013 if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
4014 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
4019 _mouse_mode_tearoff->set_visible (true);
4020 _tools_tearoff->set_visible (true);
4021 if (Config->get_show_zoom_tools ()) {
4022 _zoom_tearoff->set_visible (true);
4024 post_maximal_editor_width = this->get_width();
4025 post_maximal_editor_height = this->get_height();
4027 edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
4028 editor_summary_pane.set_position (pre_maximal_vertical_pane_position + abs(this->get_height() - pre_maximal_editor_height));
4032 * Make new playlists for a given track and also any others that belong
4033 * to the same active route group with the `edit' property.
4038 Editor::new_playlists (TimeAxisView* v)
4040 begin_reversible_command (_("new playlists"));
4041 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4042 _session->playlists->get (playlists);
4043 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4044 commit_reversible_command ();
4048 * Use a copy of the current playlist for a given track and also any others that belong
4049 * to the same active route group with the `edit' property.
4054 Editor::copy_playlists (TimeAxisView* v)
4056 begin_reversible_command (_("copy playlists"));
4057 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4058 _session->playlists->get (playlists);
4059 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4060 commit_reversible_command ();
4063 /** Clear the current playlist for a given track and also any others that belong
4064 * to the same active route group with the `edit' property.
4069 Editor::clear_playlists (TimeAxisView* v)
4071 begin_reversible_command (_("clear playlists"));
4072 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4073 _session->playlists->get (playlists);
4074 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4075 commit_reversible_command ();
4079 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4081 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4085 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4087 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4091 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4093 atv.clear_playlist ();
4097 Editor::on_key_press_event (GdkEventKey* ev)
4099 return key_press_focus_accelerator_handler (*this, ev);
4103 Editor::on_key_release_event (GdkEventKey* ev)
4105 return Gtk::Window::on_key_release_event (ev);
4106 // return key_press_focus_accelerator_handler (*this, ev);
4109 /** Queue up a change to the viewport x origin.
4110 * @param frame New x origin.
4113 Editor::reset_x_origin (framepos_t frame)
4115 queue_visual_change (frame);
4119 Editor::reset_y_origin (double y)
4121 queue_visual_change_y (y);
4125 Editor::reset_zoom (double fpu)
4127 queue_visual_change (fpu);
4131 Editor::reposition_and_zoom (framepos_t frame, double fpu)
4133 reset_x_origin (frame);
4136 if (!no_save_visual) {
4137 undo_visual_stack.push_back (current_visual_state(false));
4141 Editor::VisualState*
4142 Editor::current_visual_state (bool with_tracks)
4144 VisualState* vs = new VisualState;
4145 vs->y_position = vertical_adjustment.get_value();
4146 vs->frames_per_unit = frames_per_unit;
4147 vs->leftmost_frame = leftmost_frame;
4148 vs->zoom_focus = zoom_focus;
4151 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4152 vs->track_states.push_back (TAVState ((*i), &(*i)->get_state()));
4160 Editor::undo_visual_state ()
4162 if (undo_visual_stack.empty()) {
4166 redo_visual_stack.push_back (current_visual_state());
4168 VisualState* vs = undo_visual_stack.back();
4169 undo_visual_stack.pop_back();
4170 use_visual_state (*vs);
4174 Editor::redo_visual_state ()
4176 if (redo_visual_stack.empty()) {
4180 undo_visual_stack.push_back (current_visual_state());
4182 VisualState* vs = redo_visual_stack.back();
4183 redo_visual_stack.pop_back();
4184 use_visual_state (*vs);
4188 Editor::swap_visual_state ()
4190 if (undo_visual_stack.empty()) {
4191 redo_visual_state ();
4193 undo_visual_state ();
4198 Editor::use_visual_state (VisualState& vs)
4200 no_save_visual = true;
4202 _routes->suspend_redisplay ();
4204 vertical_adjustment.set_value (vs.y_position);
4206 set_zoom_focus (vs.zoom_focus);
4207 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4209 for (list<TAVState>::iterator i = vs.track_states.begin(); i != vs.track_states.end(); ++i) {
4210 TrackViewList::iterator t;
4212 /* check if the track still exists - it could have been deleted */
4214 if ((t = find (track_views.begin(), track_views.end(), i->first)) != track_views.end()) {
4215 (*t)->set_state (*(i->second), Stateful::loading_state_version);
4220 if (!vs.track_states.empty()) {
4221 _routes->update_visibility ();
4224 _routes->resume_redisplay ();
4226 no_save_visual = false;
4230 Editor::set_frames_per_unit (double fpu)
4232 /* this is the core function that controls the zoom level of the canvas. it is called
4233 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4236 if (fpu == frames_per_unit) {
4245 /* don't allow zooms that fit more than the maximum number
4246 of frames into an 800 pixel wide space.
4249 if (max_framepos / fpu < 800.0) {
4254 tempo_lines->tempo_map_changed();
4256 frames_per_unit = fpu;
4261 Editor::post_zoom ()
4263 // convert fpu to frame count
4265 framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width);
4267 if (frames_per_unit != zoom_range_clock.current_duration()) {
4268 zoom_range_clock.set (frames);
4271 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4272 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4273 (*i)->reshow_selection (selection->time);
4277 ZoomChanged (); /* EMIT_SIGNAL */
4279 //reset_scrolling_region ();
4281 if (playhead_cursor) {
4282 playhead_cursor->set_position (playhead_cursor->current_frame);
4285 refresh_location_display();
4286 _summary->set_overlays_dirty ();
4288 update_marker_labels ();
4294 Editor::queue_visual_change (framepos_t where)
4296 pending_visual_change.add (VisualChange::TimeOrigin);
4297 pending_visual_change.time_origin = where;
4298 ensure_visual_change_idle_handler ();
4302 Editor::queue_visual_change (double fpu)
4304 pending_visual_change.add (VisualChange::ZoomLevel);
4305 pending_visual_change.frames_per_unit = fpu;
4307 ensure_visual_change_idle_handler ();
4311 Editor::queue_visual_change_y (double y)
4313 pending_visual_change.add (VisualChange::YOrigin);
4314 pending_visual_change.y_origin = y;
4316 ensure_visual_change_idle_handler ();
4320 Editor::ensure_visual_change_idle_handler ()
4322 if (pending_visual_change.idle_handler_id < 0) {
4323 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4328 Editor::_idle_visual_changer (void* arg)
4330 return static_cast<Editor*>(arg)->idle_visual_changer ();
4334 Editor::idle_visual_changer ()
4336 VisualChange::Type p = pending_visual_change.pending;
4337 pending_visual_change.pending = (VisualChange::Type) 0;
4339 double const last_time_origin = horizontal_position ();
4341 if (p & VisualChange::TimeOrigin) {
4342 /* This is a bit of a hack, but set_frames_per_unit
4343 below will (if called) end up with the
4344 CrossfadeViews looking at Editor::leftmost_frame,
4345 and if we're changing origin and zoom in the same
4346 operation it will be the wrong value unless we
4350 leftmost_frame = pending_visual_change.time_origin;
4353 if (p & VisualChange::ZoomLevel) {
4354 set_frames_per_unit (pending_visual_change.frames_per_unit);
4356 compute_fixed_ruler_scale ();
4357 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4358 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4359 update_tempo_based_rulers ();
4361 if (p & VisualChange::TimeOrigin) {
4362 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4364 if (p & VisualChange::YOrigin) {
4365 vertical_adjustment.set_value (pending_visual_change.y_origin);
4368 if (last_time_origin == horizontal_position ()) {
4369 /* changed signal not emitted */
4370 update_fixed_rulers ();
4371 redisplay_tempo (true);
4374 _summary->set_overlays_dirty ();
4376 pending_visual_change.idle_handler_id = -1;
4377 return 0; /* this is always a one-shot call */
4380 struct EditorOrderTimeAxisSorter {
4381 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4382 return a->order () < b->order ();
4387 Editor::sort_track_selection (TrackViewList* sel)
4389 EditorOrderTimeAxisSorter cmp;
4394 selection->tracks.sort (cmp);
4399 Editor::get_preferred_edit_position (bool ignore_playhead)
4402 framepos_t where = 0;
4403 EditPoint ep = _edit_point;
4405 if (entered_marker) {
4406 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position()));
4407 return entered_marker->position();
4410 if (ignore_playhead && ep == EditAtPlayhead) {
4411 ep = EditAtSelectedMarker;
4415 case EditAtPlayhead:
4416 where = _session->audible_frame();
4417 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where));
4420 case EditAtSelectedMarker:
4421 if (!selection->markers.empty()) {
4423 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4426 where = loc->start();
4430 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where));
4438 if (!mouse_frame (where, ignored)) {
4439 /* XXX not right but what can we do ? */
4443 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where));
4451 Editor::set_loop_range (framepos_t start, framepos_t end, string cmd)
4453 if (!_session) return;
4455 begin_reversible_command (cmd);
4459 if ((tll = transport_loop_location()) == 0) {
4460 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop);
4461 XMLNode &before = _session->locations()->get_state();
4462 _session->locations()->add (loc, true);
4463 _session->set_auto_loop_location (loc);
4464 XMLNode &after = _session->locations()->get_state();
4465 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4467 XMLNode &before = tll->get_state();
4468 tll->set_hidden (false, this);
4469 tll->set (start, end);
4470 XMLNode &after = tll->get_state();
4471 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4474 commit_reversible_command ();
4478 Editor::set_punch_range (framepos_t start, framepos_t end, string cmd)
4480 if (!_session) return;
4482 begin_reversible_command (cmd);
4486 if ((tpl = transport_punch_location()) == 0) {
4487 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoPunch);
4488 XMLNode &before = _session->locations()->get_state();
4489 _session->locations()->add (loc, true);
4490 _session->set_auto_loop_location (loc);
4491 XMLNode &after = _session->locations()->get_state();
4492 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4495 XMLNode &before = tpl->get_state();
4496 tpl->set_hidden (false, this);
4497 tpl->set (start, end);
4498 XMLNode &after = tpl->get_state();
4499 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4502 commit_reversible_command ();
4505 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4506 * @param rs List to which found regions are added.
4507 * @param where Time to look at.
4508 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4511 Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4513 const TrackViewList* tracks;
4516 tracks = &track_views;
4521 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4523 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4526 boost::shared_ptr<Track> tr;
4527 boost::shared_ptr<Playlist> pl;
4529 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4531 Playlist::RegionList* regions = pl->regions_at (
4532 (framepos_t) floor ( (double) where * tr->speed()));
4534 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4535 RegionView* rv = rtv->view()->find_view (*i);
4548 Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4550 const TrackViewList* tracks;
4553 tracks = &track_views;
4558 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4559 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4561 boost::shared_ptr<Track> tr;
4562 boost::shared_ptr<Playlist> pl;
4564 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4566 Playlist::RegionList* regions = pl->regions_touched (
4567 (framepos_t) floor ( (double)where * tr->speed()), max_framepos);
4569 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4571 RegionView* rv = rtv->view()->find_view (*i);
4584 /** Start with regions that are selected. Then add equivalent regions
4585 * on tracks in the same active edit-enabled route group as any of
4586 * the regions that we started with.
4590 Editor::get_regions_from_selection ()
4592 return get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
4595 /** Get regions using the following method:
4597 * Make an initial region list using the selected regions, unless
4598 * the edit point is `mouse' and the mouse is over an unselected
4599 * region. In this case, start with just that region.
4601 * Then, make an initial track list of the tracks that these
4602 * regions are on, and if the edit point is not `mouse', add the
4605 * Look at this track list and add any other tracks that are on the
4606 * same active edit-enabled route group as one of the initial tracks.
4608 * Finally take the initial region list and add any regions that are
4609 * under the edit point on one of the tracks on the track list to get
4610 * the returned region list.
4612 * The rationale here is that the mouse edit point is special in that
4613 * its position describes both a time and a track; the other edit
4614 * modes only describe a time. Hence if the edit point is `mouse' we
4615 * ignore selected tracks, as we assume the user means something by
4616 * pointing at a particular track. Also in this case we take note of
4617 * the region directly under the edit point, as there is always just one
4618 * (rather than possibly several with non-mouse edit points).
4622 Editor::get_regions_from_selection_and_edit_point ()
4624 RegionSelection regions;
4626 if (_edit_point == EditAtMouse && entered_regionview && !selection->regions.contains (entered_regionview)) {
4627 regions.add (entered_regionview);
4629 regions = selection->regions;
4632 TrackViewList tracks;
4634 if (_edit_point != EditAtMouse) {
4635 tracks = selection->tracks;
4638 /* Add any other tracks that have regions that are in the same
4639 edit-activated route group as one of our regions.
4641 for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) {
4643 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4645 if (g && g->is_active() && g->is_edit()) {
4646 tracks.add (axis_views_from_routes (g->route_list()));
4650 if (!tracks.empty()) {
4651 /* now find regions that are at the edit position on those tracks */
4652 framepos_t const where = get_preferred_edit_position ();
4653 get_regions_at (regions, where, tracks);
4659 /** Start with regions that are selected, or the entered regionview if none are selected.
4660 * Then add equivalent regions on tracks in the same active edit-enabled route group as any
4661 * of the regions that we started with.
4665 Editor::get_regions_from_selection_and_entered ()
4667 RegionSelection regions = selection->regions;
4669 if (regions.empty() && entered_regionview) {
4670 regions.add (entered_regionview);
4673 return get_equivalent_regions (regions, ARDOUR::Properties::edit.property_id);
4677 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4679 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4681 RouteTimeAxisView* tatv;
4683 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4685 boost::shared_ptr<Playlist> pl;
4686 vector<boost::shared_ptr<Region> > results;
4688 boost::shared_ptr<Track> tr;
4690 if ((tr = tatv->track()) == 0) {
4695 if ((pl = (tr->playlist())) != 0) {
4696 pl->get_region_list_equivalent_regions (region, results);
4699 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4700 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4701 regions.push_back (marv);
4710 Editor::show_rhythm_ferret ()
4712 if (rhythm_ferret == 0) {
4713 rhythm_ferret = new RhythmFerret(*this);
4716 rhythm_ferret->set_session (_session);
4717 rhythm_ferret->show ();
4718 rhythm_ferret->present ();
4722 Editor::first_idle ()
4724 MessageDialog* dialog = 0;
4726 if (track_views.size() > 1) {
4727 dialog = new MessageDialog (*this,
4728 string_compose (_("Please wait while %1 loads visual data"), PROGRAM_NAME),
4733 ARDOUR_UI::instance()->flush_pending ();
4736 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4740 // first idle adds route children (automation tracks), so we need to redisplay here
4741 _routes->redisplay ();
4749 Editor::_idle_resize (gpointer arg)
4751 return ((Editor*)arg)->idle_resize ();
4755 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4757 if (resize_idle_id < 0) {
4758 resize_idle_id = g_idle_add (_idle_resize, this);
4759 _pending_resize_amount = 0;
4762 /* make a note of the smallest resulting height, so that we can clamp the
4763 lower limit at TimeAxisView::hSmall */
4765 int32_t min_resulting = INT32_MAX;
4767 _pending_resize_amount += h;
4768 _pending_resize_view = view;
4770 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4772 if (selection->tracks.contains (_pending_resize_view)) {
4773 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4774 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4778 if (min_resulting < 0) {
4783 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4784 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4788 /** Handle pending resizing of tracks */
4790 Editor::idle_resize ()
4792 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4794 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4795 selection->tracks.contains (_pending_resize_view)) {
4797 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4798 if (*i != _pending_resize_view) {
4799 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4804 _pending_resize_amount = 0;
4806 _group_tabs->set_dirty ();
4807 resize_idle_id = -1;
4815 ENSURE_GUI_THREAD (*this, &Editor::located);
4817 playhead_cursor->set_position (_session->audible_frame ());
4818 if (_follow_playhead && !_pending_initial_locate) {
4819 reset_x_origin_to_follow_playhead ();
4822 _pending_locate_request = false;
4823 _pending_initial_locate = false;
4827 Editor::region_view_added (RegionView *)
4829 _summary->set_dirty ();
4833 Editor::region_view_removed ()
4835 _summary->set_dirty ();
4839 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4841 TrackViewList::const_iterator j = track_views.begin ();
4842 while (j != track_views.end()) {
4843 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4844 if (rtv && rtv->route() == r) {
4855 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4859 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4860 TimeAxisView* tv = axis_view_from_route (*i);
4871 Editor::handle_new_route (RouteList& routes)
4873 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4875 RouteTimeAxisView *rtv;
4876 list<RouteTimeAxisView*> new_views;
4878 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4879 boost::shared_ptr<Route> route = (*x);
4881 if (route->is_hidden() || route->is_monitor()) {
4885 DataType dt = route->input()->default_type();
4887 if (dt == ARDOUR::DataType::AUDIO) {
4888 rtv = new AudioTimeAxisView (*this, _session, route, *track_canvas);
4889 } else if (dt == ARDOUR::DataType::MIDI) {
4890 rtv = new MidiTimeAxisView (*this, _session, route, *track_canvas);
4892 throw unknown_type();
4895 new_views.push_back (rtv);
4896 track_views.push_back (rtv);
4898 rtv->effective_gain_display ();
4900 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4901 rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed));
4904 _routes->routes_added (new_views);
4905 _summary->routes_added (new_views);
4907 if (show_editor_mixer_when_tracks_arrive) {
4908 show_editor_mixer (true);
4911 editor_list_button.set_sensitive (true);
4915 Editor::timeaxisview_deleted (TimeAxisView *tv)
4917 if (_session && _session->deletion_in_progress()) {
4918 /* the situation is under control */
4922 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
4924 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
4926 _routes->route_removed (tv);
4928 if (tv == entered_track) {
4932 TimeAxisView::Children c = tv->get_child_list ();
4933 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
4934 if (entered_track == i->get()) {
4939 /* remove it from the list of track views */
4941 TrackViewList::iterator i;
4943 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
4944 i = track_views.erase (i);
4947 /* update whatever the current mixer strip is displaying, if revelant */
4949 boost::shared_ptr<Route> route;
4952 route = rtav->route ();
4955 if (current_mixer_strip && current_mixer_strip->route() == route) {
4957 TimeAxisView* next_tv;
4959 if (track_views.empty()) {
4961 } else if (i == track_views.end()) {
4962 next_tv = track_views.front();
4969 set_selected_mixer_strip (*next_tv);
4971 /* make the editor mixer strip go away setting the
4972 * button to inactive (which also unticks the menu option)
4975 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
4981 Editor::hide_track_in_display (TimeAxisView* tv, bool /*temponly*/)
4983 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
4985 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
4986 // this will hide the mixer strip
4987 set_selected_mixer_strip (*tv);
4990 _routes->hide_track_in_display (*tv);
4994 Editor::sync_track_view_list_and_routes ()
4996 track_views = TrackViewList (_routes->views ());
4998 _summary->set_dirty ();
4999 _group_tabs->set_dirty ();
5001 return false; // do not call again (until needed)
5005 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
5007 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5012 /** Find a RouteTimeAxisView by the ID of its route */
5014 Editor::get_route_view_by_route_id (PBD::ID& id) const
5016 RouteTimeAxisView* v;
5018 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5019 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5020 if(v->route()->id() == id) {
5030 Editor::fit_route_group (RouteGroup *g)
5032 TrackViewList ts = axis_views_from_routes (g->route_list ());
5037 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5039 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5042 _session->cancel_audition ();
5046 if (_session->is_auditioning()) {
5047 _session->cancel_audition ();
5048 if (r == last_audition_region) {
5053 _session->audition_region (r);
5054 last_audition_region = r;
5059 Editor::hide_a_region (boost::shared_ptr<Region> r)
5061 r->set_hidden (true);
5065 Editor::show_a_region (boost::shared_ptr<Region> r)
5067 r->set_hidden (false);
5071 Editor::audition_region_from_region_list ()
5073 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5077 Editor::hide_region_from_region_list ()
5079 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5083 Editor::show_region_in_region_list ()
5085 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5089 Editor::step_edit_status_change (bool yn)
5092 start_step_editing ();
5094 stop_step_editing ();
5099 Editor::start_step_editing ()
5101 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5105 Editor::stop_step_editing ()
5107 step_edit_connection.disconnect ();
5111 Editor::check_step_edit ()
5113 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5114 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5116 mtv->check_step_edit ();
5120 return true; // do it again, till we stop
5124 Editor::horizontal_scroll_left_press ()
5126 ++_scroll_callbacks;
5128 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5129 /* delay the first auto-repeat */
5133 double x = leftmost_position() - current_page_frames() / 5;
5140 /* do hacky auto-repeat */
5141 if (!_scroll_connection.connected ()) {
5142 _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press), 100);
5143 _scroll_callbacks = 0;
5150 Editor::horizontal_scroll_left_release ()
5152 _scroll_connection.disconnect ();
5156 Editor::horizontal_scroll_right_press ()
5158 ++_scroll_callbacks;
5160 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5161 /* delay the first auto-repeat */
5165 reset_x_origin (leftmost_position() + current_page_frames() / 5);
5167 /* do hacky auto-repeat */
5168 if (!_scroll_connection.connected ()) {
5169 _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press), 100);
5170 _scroll_callbacks = 0;
5177 Editor::horizontal_scroll_right_release ()
5179 _scroll_connection.disconnect ();
5182 /** Queue a change for the Editor viewport x origin to follow the playhead */
5184 Editor::reset_x_origin_to_follow_playhead ()
5186 framepos_t const frame = playhead_cursor->current_frame;
5188 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5190 if (_session->transport_speed() < 0) {
5192 if (frame > (current_page_frames() / 2)) {
5193 center_screen (frame-(current_page_frames()/2));
5195 center_screen (current_page_frames()/2);
5200 if (frame < leftmost_frame) {
5203 if (_session->transport_rolling()) {
5204 /* rolling; end up with the playhead at the right of the page */
5205 l = frame - current_page_frames ();
5207 /* not rolling: end up with the playhead 3/4 of the way along the page */
5208 l = frame - (3 * current_page_frames() / 4);
5215 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5218 if (_session->transport_rolling()) {
5219 /* rolling: end up with the playhead on the left of the page */
5220 center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5222 /* not rolling: end up with the playhead 1/4 of the way along the page */
5223 center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5231 Editor::super_rapid_screen_update ()
5233 if (!_session || !_session->engine().running()) {
5237 /* METERING / MIXER STRIPS */
5239 /* update track meters, if required */
5240 if (is_mapped() && meters_running) {
5241 RouteTimeAxisView* rtv;
5242 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5243 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5244 rtv->fast_update ();
5249 /* and any current mixer strip */
5250 if (current_mixer_strip) {
5251 current_mixer_strip->fast_update ();
5254 /* PLAYHEAD AND VIEWPORT */
5256 framepos_t const frame = _session->audible_frame();
5258 /* There are a few reasons why we might not update the playhead / viewport stuff:
5260 * 1. we don't update things when there's a pending locate request, otherwise
5261 * when the editor requests a locate there is a chance that this method
5262 * will move the playhead before the locate request is processed, causing
5264 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5265 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5268 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5270 last_update_frame = frame;
5272 if (!_dragging_playhead) {
5273 playhead_cursor->set_position (frame);
5276 if (!_stationary_playhead) {
5278 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5279 reset_x_origin_to_follow_playhead ();
5284 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5288 // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5289 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5290 if (target <= 0.0) {
5293 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5294 target = (target * 0.15) + (current * 0.85);
5300 set_horizontal_position (current);
5309 Editor::session_going_away ()
5311 _have_idled = false;
5313 _session_connections.drop_connections ();
5315 super_rapid_screen_update_connection.disconnect ();
5317 selection->clear ();
5318 cut_buffer->clear ();
5320 clicked_regionview = 0;
5321 clicked_axisview = 0;
5322 clicked_routeview = 0;
5323 clicked_crossfadeview = 0;
5324 entered_regionview = 0;
5326 last_update_frame = 0;
5329 playhead_cursor->canvas_item.hide ();
5331 /* rip everything out of the list displays */
5335 _route_groups->clear ();
5337 /* do this first so that deleting a track doesn't reset cms to null
5338 and thus cause a leak.
5341 if (current_mixer_strip) {
5342 if (current_mixer_strip->get_parent() != 0) {
5343 global_hpacker.remove (*current_mixer_strip);
5345 delete current_mixer_strip;
5346 current_mixer_strip = 0;
5349 /* delete all trackviews */
5351 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5354 track_views.clear ();
5356 zoom_range_clock.set_session (0);
5357 nudge_clock.set_session (0);
5359 editor_list_button.set_active(false);
5360 editor_list_button.set_sensitive(false);
5362 /* clear tempo/meter rulers */
5363 remove_metric_marks ();
5365 clear_marker_display ();
5367 delete current_bbt_points;
5368 current_bbt_points = 0;
5370 /* get rid of any existing editor mixer strip */
5372 WindowTitle title(Glib::get_application_name());
5373 title += _("Editor");
5375 set_title (title.get_string());
5377 SessionHandlePtr::session_going_away ();
5382 Editor::show_editor_list (bool yn)
5385 _the_notebook.show ();
5387 _the_notebook.hide ();
5392 Editor::change_region_layering_order ()
5394 framepos_t const position = get_preferred_edit_position ();
5396 if (!clicked_routeview) {
5397 if (layering_order_editor) {
5398 layering_order_editor->hide ();
5403 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
5409 boost::shared_ptr<Playlist> pl = track->playlist();
5415 if (layering_order_editor == 0) {
5416 layering_order_editor = new RegionLayeringOrderEditor(*this);
5419 layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position);
5420 layering_order_editor->maybe_present ();
5424 Editor::update_region_layering_order_editor ()
5426 if (layering_order_editor && layering_order_editor->is_visible ()) {
5427 change_region_layering_order ();
5432 Editor::setup_fade_images ()
5434 _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear")));
5435 _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut")));
5436 _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut")));
5437 _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut")));
5438 _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut")));
5440 _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear")));
5441 _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut")));
5442 _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut")));
5443 _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut")));
5444 _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut")));
5448 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
5450 Editor::action_menu_item (std::string const & name)
5452 Glib::RefPtr<Action> a = editor_actions->get_action (name);
5455 return *manage (a->create_menu_item ());
5459 Editor::resize_text_widgets ()
5461 set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_FUDGE+10, 15);
5462 set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_FUDGE+10, 15);
5463 set_size_request_to_display_given_text (snap_type_selector, snap_type_strings, COMBO_FUDGE+10, 15);
5464 set_size_request_to_display_given_text (snap_mode_selector, snap_mode_strings, COMBO_FUDGE+10, 15);
5465 set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_FUDGE+10, 15);
5469 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
5471 EventBox* b = manage (new EventBox);
5472 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
5473 Label* l = manage (new Label (name));
5477 _the_notebook.append_page (widget, *b);
5481 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
5483 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
5484 _the_notebook.set_current_page (_the_notebook.page_num (*page));
5487 if (ev->type == GDK_2BUTTON_PRESS) {
5489 /* double-click on a notebook tab shrinks or expands the notebook */
5491 if (_notebook_shrunk) {
5492 edit_pane.set_position (pre_maximal_horizontal_pane_position);
5493 _notebook_shrunk = false;
5495 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
5496 edit_pane.set_position (edit_pane.get_position() + page->get_width());
5497 _notebook_shrunk = true;