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::bind (sigc::mem_fun (*this, &Editor::scroll_press), LEFT)));
570 summary_arrows_left_left->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_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::bind (sigc::mem_fun (*this, &Editor::scroll_press), RIGHT)));
575 summary_arrows_left_right->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_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_up = manage (new Button);
582 summary_arrows_right_up->add (*manage (new Arrow (ARROW_UP, SHADOW_NONE)));
583 summary_arrows_right_up->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), UP)));
584 summary_arrows_right_up->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
586 Button* summary_arrows_right_down = manage (new Button);
587 summary_arrows_right_down->add (*manage (new Arrow (ARROW_DOWN, SHADOW_NONE)));
588 summary_arrows_right_down->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), DOWN)));
589 summary_arrows_right_down->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
591 VBox* summary_arrows_right = manage (new VBox);
592 summary_arrows_right->pack_start (*summary_arrows_right_up);
593 summary_arrows_right->pack_start (*summary_arrows_right_down);
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())) {
1548 add_region_context_items (edit_items, tr);
1552 add_dstream_context_items (edit_items);
1554 return &track_region_context_menu;
1558 Editor::build_track_crossfade_context_menu ()
1560 using namespace Menu_Helpers;
1561 MenuList& edit_items = track_crossfade_context_menu.items();
1562 edit_items.clear ();
1564 /* we might try to use items that are currently attached to a crossfade menu,
1567 track_region_context_menu.items().clear ();
1569 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1572 boost::shared_ptr<Track> tr;
1573 boost::shared_ptr<Playlist> pl;
1574 boost::shared_ptr<AudioPlaylist> apl;
1576 if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1578 AudioPlaylist::Crossfades xfades;
1582 /* The xfade menu is a bit of a special case, as we always use the mouse position
1583 to decide whether or not to display it (rather than the edit point). No particularly
1584 strong reasons for this, other than it is a bit surprising to right-click on a xfade
1587 mouse_frame (where, ignored);
1588 apl->crossfades_at (where, xfades);
1590 bool const many = xfades.size() > 1;
1592 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1593 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1596 add_region_context_items (edit_items, tr);
1600 add_dstream_context_items (edit_items);
1602 return &track_crossfade_context_menu;
1606 Editor::analyze_region_selection ()
1608 if (analysis_window == 0) {
1609 analysis_window = new AnalysisWindow();
1612 analysis_window->set_session(_session);
1614 analysis_window->show_all();
1617 analysis_window->set_regionmode();
1618 analysis_window->analyze();
1620 analysis_window->present();
1624 Editor::analyze_range_selection()
1626 if (analysis_window == 0) {
1627 analysis_window = new AnalysisWindow();
1630 analysis_window->set_session(_session);
1632 analysis_window->show_all();
1635 analysis_window->set_rangemode();
1636 analysis_window->analyze();
1638 analysis_window->present();
1642 Editor::build_track_selection_context_menu ()
1644 using namespace Menu_Helpers;
1645 MenuList& edit_items = track_selection_context_menu.items();
1646 edit_items.clear ();
1648 add_selection_context_items (edit_items);
1649 // edit_items.push_back (SeparatorElem());
1650 // add_dstream_context_items (edit_items);
1652 return &track_selection_context_menu;
1655 /** Add context menu items relevant to crossfades.
1656 * @param edit_items List to add the items to.
1659 Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1661 using namespace Menu_Helpers;
1662 Menu *xfade_menu = manage (new Menu);
1663 MenuList& items = xfade_menu->items();
1664 xfade_menu->set_name ("ArdourContextMenu");
1667 if (xfade->active()) {
1673 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1674 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1676 if (xfade->can_follow_overlap()) {
1678 if (xfade->following_overlap()) {
1679 str = _("Convert to Short");
1681 str = _("Convert to Full");
1684 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1688 str = xfade->out()->name();
1690 str += xfade->in()->name();
1692 str = _("Crossfade");
1695 edit_items.push_back (MenuElem (str, *xfade_menu));
1696 edit_items.push_back (SeparatorElem());
1700 Editor::xfade_edit_left_region ()
1702 if (clicked_crossfadeview) {
1703 clicked_crossfadeview->left_view.show_region_editor ();
1708 Editor::xfade_edit_right_region ()
1710 if (clicked_crossfadeview) {
1711 clicked_crossfadeview->right_view.show_region_editor ();
1716 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::shared_ptr<Track> track)
1718 using namespace Menu_Helpers;
1720 /* OK, stick the region submenu at the top of the list, and then add
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 /* we have to hack up the region name because "_" has a special
1730 meaning for menu titles.
1733 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1734 menu_item_name.replace (pos, 1, "__");
1738 if (_popup_region_menu_item == 0) {
1739 _popup_region_menu_item = new MenuItem (menu_item_name);
1740 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1741 _popup_region_menu_item->show ();
1743 _popup_region_menu_item->set_label (menu_item_name);
1746 /* Use the mouse position rather than the edit point to decide whether to show the `choose top region'
1747 dialogue. If we use the edit point it gets a bit messy because the user still has to click over
1748 *some* region in order to get the region context menu stuff to be displayed at all.
1753 mouse_frame (mouse, ignored);
1755 edit_items.push_back (*_popup_region_menu_item);
1756 if (track->playlist()->count_regions_at (mouse) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1757 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region")->create_menu_item ()));
1759 edit_items.push_back (SeparatorElem());
1762 /** Add context menu items relevant to selection ranges.
1763 * @param edit_items List to add the items to.
1766 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1768 using namespace Menu_Helpers;
1770 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1771 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1773 edit_items.push_back (SeparatorElem());
1774 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1776 if (!selection->regions.empty()) {
1777 edit_items.push_back (SeparatorElem());
1778 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)));
1779 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)));
1782 edit_items.push_back (SeparatorElem());
1783 edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection)));
1784 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1786 edit_items.push_back (SeparatorElem());
1787 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1789 edit_items.push_back (SeparatorElem());
1790 edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1791 edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1793 edit_items.push_back (SeparatorElem());
1794 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1796 edit_items.push_back (SeparatorElem());
1797 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1798 edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1799 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1801 edit_items.push_back (SeparatorElem());
1802 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1803 edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1804 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1805 edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1806 edit_items.push_back (MenuElem (_("Export Range"), sigc::mem_fun(*this, &Editor::export_selection)));
1811 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1813 using namespace Menu_Helpers;
1817 Menu *play_menu = manage (new Menu);
1818 MenuList& play_items = play_menu->items();
1819 play_menu->set_name ("ArdourContextMenu");
1821 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1822 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1823 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1824 play_items.push_back (SeparatorElem());
1825 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
1827 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1831 Menu *select_menu = manage (new Menu);
1832 MenuList& select_items = select_menu->items();
1833 select_menu->set_name ("ArdourContextMenu");
1835 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1836 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1837 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1838 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1839 select_items.push_back (SeparatorElem());
1840 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
1841 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
1842 select_items.push_back (SeparatorElem());
1843 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1844 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1845 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1846 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1847 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
1848 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
1849 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
1851 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1855 Menu *cutnpaste_menu = manage (new Menu);
1856 MenuList& cutnpaste_items = cutnpaste_menu->items();
1857 cutnpaste_menu->set_name ("ArdourContextMenu");
1859 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1860 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1861 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1863 cutnpaste_items.push_back (SeparatorElem());
1865 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
1866 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
1868 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
1870 /* Adding new material */
1872 edit_items.push_back (SeparatorElem());
1873 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
1874 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
1878 Menu *nudge_menu = manage (new Menu());
1879 MenuList& nudge_items = nudge_menu->items();
1880 nudge_menu->set_name ("ArdourContextMenu");
1882 edit_items.push_back (SeparatorElem());
1883 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1884 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1885 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1886 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1888 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1892 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
1894 using namespace Menu_Helpers;
1898 Menu *play_menu = manage (new Menu);
1899 MenuList& play_items = play_menu->items();
1900 play_menu->set_name ("ArdourContextMenu");
1902 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1903 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1904 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1908 Menu *select_menu = manage (new Menu);
1909 MenuList& select_items = select_menu->items();
1910 select_menu->set_name ("ArdourContextMenu");
1912 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1913 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1914 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1915 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1916 select_items.push_back (SeparatorElem());
1917 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1918 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1919 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1920 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1922 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1926 Menu *cutnpaste_menu = manage (new Menu);
1927 MenuList& cutnpaste_items = cutnpaste_menu->items();
1928 cutnpaste_menu->set_name ("ArdourContextMenu");
1930 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1931 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1932 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1934 Menu *nudge_menu = manage (new Menu());
1935 MenuList& nudge_items = nudge_menu->items();
1936 nudge_menu->set_name ("ArdourContextMenu");
1938 edit_items.push_back (SeparatorElem());
1939 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1940 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1941 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1942 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1944 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1948 Editor::snap_type() const
1954 Editor::snap_mode() const
1960 Editor::set_snap_to (SnapType st)
1962 unsigned int snap_ind = (unsigned int)st;
1966 if (snap_ind > snap_type_strings.size() - 1) {
1968 _snap_type = (SnapType)snap_ind;
1971 string str = snap_type_strings[snap_ind];
1973 if (str != snap_type_selector.get_active_text()) {
1974 snap_type_selector.set_active_text (str);
1979 switch (_snap_type) {
1980 case SnapToBeatDiv32:
1981 case SnapToBeatDiv28:
1982 case SnapToBeatDiv24:
1983 case SnapToBeatDiv20:
1984 case SnapToBeatDiv16:
1985 case SnapToBeatDiv14:
1986 case SnapToBeatDiv12:
1987 case SnapToBeatDiv10:
1988 case SnapToBeatDiv8:
1989 case SnapToBeatDiv7:
1990 case SnapToBeatDiv6:
1991 case SnapToBeatDiv5:
1992 case SnapToBeatDiv4:
1993 case SnapToBeatDiv3:
1994 case SnapToBeatDiv2:
1995 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
1996 update_tempo_based_rulers ();
1999 case SnapToRegionStart:
2000 case SnapToRegionEnd:
2001 case SnapToRegionSync:
2002 case SnapToRegionBoundary:
2003 build_region_boundary_cache ();
2011 SnapChanged (); /* EMIT SIGNAL */
2015 Editor::set_snap_mode (SnapMode mode)
2018 string str = snap_mode_strings[(int)mode];
2020 if (str != snap_mode_selector.get_active_text ()) {
2021 snap_mode_selector.set_active_text (str);
2027 Editor::set_edit_point_preference (EditPoint ep, bool force)
2029 bool changed = (_edit_point != ep);
2032 string str = edit_point_strings[(int)ep];
2034 if (str != edit_point_selector.get_active_text ()) {
2035 edit_point_selector.set_active_text (str);
2038 set_canvas_cursor ();
2040 if (!force && !changed) {
2044 const char* action=NULL;
2046 switch (_edit_point) {
2047 case EditAtPlayhead:
2048 action = "edit-at-playhead";
2050 case EditAtSelectedMarker:
2051 action = "edit-at-marker";
2054 action = "edit-at-mouse";
2058 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2060 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2064 bool in_track_canvas;
2066 if (!mouse_frame (foo, in_track_canvas)) {
2067 in_track_canvas = false;
2070 reset_canvas_action_sensitivity (in_track_canvas);
2076 Editor::set_state (const XMLNode& node, int /*version*/)
2078 const XMLProperty* prop;
2080 int x, y, xoff, yoff;
2083 if ((prop = node.property ("id")) != 0) {
2084 _id = prop->value ();
2087 g.base_width = default_width;
2088 g.base_height = default_height;
2094 if ((geometry = find_named_node (node, "geometry")) != 0) {
2098 if ((prop = geometry->property("x_size")) == 0) {
2099 prop = geometry->property ("x-size");
2102 g.base_width = atoi(prop->value());
2104 if ((prop = geometry->property("y_size")) == 0) {
2105 prop = geometry->property ("y-size");
2108 g.base_height = atoi(prop->value());
2111 if ((prop = geometry->property ("x_pos")) == 0) {
2112 prop = geometry->property ("x-pos");
2115 x = atoi (prop->value());
2118 if ((prop = geometry->property ("y_pos")) == 0) {
2119 prop = geometry->property ("y-pos");
2122 y = atoi (prop->value());
2125 if ((prop = geometry->property ("x_off")) == 0) {
2126 prop = geometry->property ("x-off");
2129 xoff = atoi (prop->value());
2131 if ((prop = geometry->property ("y_off")) == 0) {
2132 prop = geometry->property ("y-off");
2135 yoff = atoi (prop->value());
2139 //set_default_size (g.base_width, g.base_height);
2142 if (_session && (prop = node.property ("playhead"))) {
2144 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2145 playhead_cursor->set_position (pos);
2147 playhead_cursor->set_position (0);
2150 if ((prop = node.property ("mixer-width"))) {
2151 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2154 if ((prop = node.property ("zoom-focus"))) {
2155 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2158 if ((prop = node.property ("zoom"))) {
2159 reset_zoom (PBD::atof (prop->value()));
2161 reset_zoom (frames_per_unit);
2164 if ((prop = node.property ("snap-to"))) {
2165 set_snap_to ((SnapType) atoi (prop->value()));
2168 if ((prop = node.property ("snap-mode"))) {
2169 set_snap_mode ((SnapMode) atoi (prop->value()));
2172 if ((prop = node.property ("mouse-mode"))) {
2173 MouseMode m = str2mousemode(prop->value());
2174 set_mouse_mode (m, true);
2176 set_mouse_mode (MouseObject, true);
2179 if ((prop = node.property ("left-frame")) != 0) {
2181 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2182 reset_x_origin (pos);
2186 if ((prop = node.property ("y-origin")) != 0) {
2187 reset_y_origin (atof (prop->value ()));
2190 if ((prop = node.property ("internal-edit"))) {
2191 bool yn = string_is_affirmative (prop->value());
2192 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2194 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2195 tact->set_active (!yn);
2196 tact->set_active (yn);
2200 if ((prop = node.property ("join-object-range"))) {
2201 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2204 if ((prop = node.property ("edit-point"))) {
2205 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2208 if ((prop = node.property ("show-measures"))) {
2209 bool yn = string_is_affirmative (prop->value());
2210 _show_measures = yn;
2211 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2213 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2214 /* do it twice to force the change */
2215 tact->set_active (!yn);
2216 tact->set_active (yn);
2220 if ((prop = node.property ("follow-playhead"))) {
2221 bool yn = string_is_affirmative (prop->value());
2222 set_follow_playhead (yn);
2223 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2225 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2226 if (tact->get_active() != yn) {
2227 tact->set_active (yn);
2232 if ((prop = node.property ("stationary-playhead"))) {
2233 bool yn = (prop->value() == "yes");
2234 set_stationary_playhead (yn);
2235 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2237 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2238 if (tact->get_active() != yn) {
2239 tact->set_active (yn);
2244 if ((prop = node.property ("region-list-sort-type"))) {
2245 RegionListSortType st;
2246 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2249 if ((prop = node.property ("xfades-visible"))) {
2250 bool yn = string_is_affirmative (prop->value());
2251 _xfade_visibility = !yn;
2252 // set_xfade_visibility (yn);
2255 if ((prop = node.property ("show-editor-mixer"))) {
2257 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2260 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2261 bool yn = string_is_affirmative (prop->value());
2263 /* do it twice to force the change */
2265 tact->set_active (!yn);
2266 tact->set_active (yn);
2269 if ((prop = node.property ("show-editor-list"))) {
2271 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2274 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2275 bool yn = string_is_affirmative (prop->value());
2277 /* do it twice to force the change */
2279 tact->set_active (!yn);
2280 tact->set_active (yn);
2283 if ((prop = node.property (X_("editor-list-page")))) {
2284 _the_notebook.set_current_page (atoi (prop->value ()));
2287 if ((prop = node.property (X_("show-marker-lines")))) {
2288 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2290 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2291 bool yn = string_is_affirmative (prop->value ());
2293 tact->set_active (!yn);
2294 tact->set_active (yn);
2297 XMLNodeList children = node.children ();
2298 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2299 selection->set_state (**i, Stateful::current_state_version);
2300 _regions->set_state (**i);
2307 Editor::get_state ()
2309 XMLNode* node = new XMLNode ("Editor");
2312 _id.print (buf, sizeof (buf));
2313 node->add_property ("id", buf);
2315 if (is_realized()) {
2316 Glib::RefPtr<Gdk::Window> win = get_window();
2318 int x, y, xoff, yoff, width, height;
2319 win->get_root_origin(x, y);
2320 win->get_position(xoff, yoff);
2321 win->get_size(width, height);
2323 XMLNode* geometry = new XMLNode ("geometry");
2325 snprintf(buf, sizeof(buf), "%d", width);
2326 geometry->add_property("x-size", string(buf));
2327 snprintf(buf, sizeof(buf), "%d", height);
2328 geometry->add_property("y-size", string(buf));
2329 snprintf(buf, sizeof(buf), "%d", x);
2330 geometry->add_property("x-pos", string(buf));
2331 snprintf(buf, sizeof(buf), "%d", y);
2332 geometry->add_property("y-pos", string(buf));
2333 snprintf(buf, sizeof(buf), "%d", xoff);
2334 geometry->add_property("x-off", string(buf));
2335 snprintf(buf, sizeof(buf), "%d", yoff);
2336 geometry->add_property("y-off", string(buf));
2337 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2338 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2339 geometry->add_property("notebook-shrunk", _notebook_shrunk ? "1" : "0");
2340 snprintf(buf,sizeof(buf), "%d",pre_maximal_horizontal_pane_position);
2341 geometry->add_property("pre-maximal-horizontal-pane-position", string(buf));
2342 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2343 geometry->add_property("edit-vertical-pane-pos", string(buf));
2345 node->add_child_nocopy (*geometry);
2348 maybe_add_mixer_strip_width (*node);
2350 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2351 node->add_property ("zoom-focus", buf);
2352 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2353 node->add_property ("zoom", buf);
2354 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2355 node->add_property ("snap-to", buf);
2356 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2357 node->add_property ("snap-mode", buf);
2359 node->add_property ("edit-point", enum_2_string (_edit_point));
2361 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2362 node->add_property ("playhead", buf);
2363 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2364 node->add_property ("left-frame", buf);
2365 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2366 node->add_property ("y-origin", buf);
2368 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2369 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2370 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2371 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2372 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2373 node->add_property ("mouse-mode", enum2str(mouse_mode));
2374 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2375 node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2377 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2379 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2380 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2383 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2385 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2386 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2389 snprintf (buf, sizeof (buf), "%d", _the_notebook.get_current_page ());
2390 node->add_property (X_("editor-list-page"), buf);
2392 node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no");
2394 node->add_child_nocopy (selection->get_state ());
2395 node->add_child_nocopy (_regions->get_state ());
2402 /** @param y y offset from the top of all trackviews.
2403 * @return pair: TimeAxisView that y is over, layer index.
2404 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2405 * in stacked region display mode, otherwise 0.
2407 std::pair<TimeAxisView *, layer_t>
2408 Editor::trackview_by_y_position (double y)
2410 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2412 std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
2418 return std::make_pair ( (TimeAxisView *) 0, 0);
2421 /** Snap a position to the grid, if appropriate, taking into account current
2422 * grid settings and also the state of any snap modifier keys that may be pressed.
2423 * @param start Position to snap.
2424 * @param event Event to get current key modifier information from, or 0.
2427 Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2429 if (!_session || !event) {
2433 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2434 if (_snap_mode == SnapOff) {
2435 snap_to_internal (start, direction, for_mark);
2438 if (_snap_mode != SnapOff) {
2439 snap_to_internal (start, direction, for_mark);
2445 Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark)
2447 if (!_session || _snap_mode == SnapOff) {
2451 snap_to_internal (start, direction, for_mark);
2455 Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /*for_mark*/)
2457 const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2458 framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2460 switch (_snap_type) {
2461 case SnapToTimecodeFrame:
2462 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2463 start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2465 start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2469 case SnapToTimecodeSeconds:
2470 if (_session->config.get_timecode_offset_negative()) {
2471 start += _session->config.get_timecode_offset ();
2473 start -= _session->config.get_timecode_offset ();
2475 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2476 start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2478 start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2481 if (_session->config.get_timecode_offset_negative()) {
2482 start -= _session->config.get_timecode_offset ();
2484 start += _session->config.get_timecode_offset ();
2488 case SnapToTimecodeMinutes:
2489 if (_session->config.get_timecode_offset_negative()) {
2490 start += _session->config.get_timecode_offset ();
2492 start -= _session->config.get_timecode_offset ();
2494 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2495 start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2497 start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2499 if (_session->config.get_timecode_offset_negative()) {
2500 start -= _session->config.get_timecode_offset ();
2502 start += _session->config.get_timecode_offset ();
2506 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2512 Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark)
2514 const framepos_t one_second = _session->frame_rate();
2515 const framepos_t one_minute = _session->frame_rate() * 60;
2516 framepos_t presnap = start;
2520 switch (_snap_type) {
2521 case SnapToTimecodeFrame:
2522 case SnapToTimecodeSeconds:
2523 case SnapToTimecodeMinutes:
2524 return timecode_snap_to_internal (start, direction, for_mark);
2527 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2528 start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2530 start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2535 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2536 start = (framepos_t) ceil ((double) start / one_second) * one_second;
2538 start = (framepos_t) floor ((double) start / one_second) * one_second;
2543 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2544 start = (framepos_t) ceil ((double) start / one_minute) * one_minute;
2546 start = (framepos_t) floor ((double) start / one_minute) * one_minute;
2551 start = _session->tempo_map().round_to_bar (start, direction);
2555 start = _session->tempo_map().round_to_beat (start, direction);
2558 case SnapToBeatDiv32:
2559 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2561 case SnapToBeatDiv28:
2562 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2564 case SnapToBeatDiv24:
2565 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2567 case SnapToBeatDiv20:
2568 start = _session->tempo_map().round_to_beat_subdivision (start, 20, direction);
2570 case SnapToBeatDiv16:
2571 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2573 case SnapToBeatDiv14:
2574 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2576 case SnapToBeatDiv12:
2577 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2579 case SnapToBeatDiv10:
2580 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2582 case SnapToBeatDiv8:
2583 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2585 case SnapToBeatDiv7:
2586 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2588 case SnapToBeatDiv6:
2589 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2591 case SnapToBeatDiv5:
2592 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2594 case SnapToBeatDiv4:
2595 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2597 case SnapToBeatDiv3:
2598 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2600 case SnapToBeatDiv2:
2601 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2609 _session->locations()->marks_either_side (start, before, after);
2611 if (before == max_framepos) {
2613 } else if (after == max_framepos) {
2615 } else if (before != max_framepos && after != max_framepos) {
2616 /* have before and after */
2617 if ((start - before) < (after - start)) {
2626 case SnapToRegionStart:
2627 case SnapToRegionEnd:
2628 case SnapToRegionSync:
2629 case SnapToRegionBoundary:
2630 if (!region_boundary_cache.empty()) {
2632 vector<framepos_t>::iterator prev = region_boundary_cache.end ();
2633 vector<framepos_t>::iterator next = region_boundary_cache.end ();
2635 if (direction > 0) {
2636 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2638 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2641 if (next != region_boundary_cache.begin ()) {
2646 framepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2647 framepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2649 if (start > (p + n) / 2) {
2658 switch (_snap_mode) {
2664 if (presnap > start) {
2665 if (presnap > (start + unit_to_frame(snap_threshold))) {
2669 } else if (presnap < start) {
2670 if (presnap < (start - unit_to_frame(snap_threshold))) {
2676 /* handled at entry */
2684 Editor::setup_toolbar ()
2688 /* Mode Buttons (tool selection) */
2690 mouse_move_button.set_relief(Gtk::RELIEF_NONE);
2691 mouse_select_button.set_relief(Gtk::RELIEF_NONE);
2692 mouse_gain_button.set_relief(Gtk::RELIEF_NONE);
2693 mouse_zoom_button.set_relief(Gtk::RELIEF_NONE);
2694 mouse_timefx_button.set_relief(Gtk::RELIEF_NONE);
2695 mouse_audition_button.set_relief(Gtk::RELIEF_NONE);
2696 // internal_edit_button.set_relief(Gtk::RELIEF_NONE);
2697 join_object_range_button.set_relief(Gtk::RELIEF_NONE);
2699 HBox* mode_box = manage(new HBox);
2700 mode_box->set_border_width (2);
2701 mode_box->set_spacing(4);
2703 /* table containing mode buttons */
2705 HBox* mouse_mode_button_box = manage (new HBox ());
2707 if (Profile->get_sae()) {
2708 mouse_mode_button_box->pack_start (mouse_move_button);
2710 mouse_mode_button_box->pack_start (mouse_move_button);
2711 mouse_mode_button_box->pack_start (join_object_range_button);
2712 mouse_mode_button_box->pack_start (mouse_select_button);
2715 mouse_mode_button_box->pack_start (mouse_zoom_button);
2717 if (!Profile->get_sae()) {
2718 mouse_mode_button_box->pack_start (mouse_gain_button);
2721 mouse_mode_button_box->pack_start (mouse_timefx_button);
2722 mouse_mode_button_box->pack_start (mouse_audition_button);
2723 mouse_mode_button_box->pack_start (internal_edit_button);
2725 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2726 if (!Profile->get_sae()) {
2727 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2729 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2731 edit_mode_selector.set_name ("EditModeSelector");
2732 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2733 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2735 mode_box->pack_start (edit_mode_selector);
2736 mode_box->pack_start (*mouse_mode_button_box);
2738 _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2739 _mouse_mode_tearoff->set_name ("MouseModeBase");
2740 _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2742 if (Profile->get_sae()) {
2743 _mouse_mode_tearoff->set_can_be_torn_off (false);
2746 _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2747 &_mouse_mode_tearoff->tearoff_window()));
2748 _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2749 &_mouse_mode_tearoff->tearoff_window(), 1));
2750 _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2751 &_mouse_mode_tearoff->tearoff_window()));
2752 _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2753 &_mouse_mode_tearoff->tearoff_window(), 1));
2755 mouse_move_button.set_mode (false);
2756 mouse_select_button.set_mode (false);
2757 mouse_gain_button.set_mode (false);
2758 mouse_zoom_button.set_mode (false);
2759 mouse_timefx_button.set_mode (false);
2760 mouse_audition_button.set_mode (false);
2761 join_object_range_button.set_mode (false);
2763 mouse_move_button.set_name ("MouseModeButton");
2764 mouse_select_button.set_name ("MouseModeButton");
2765 mouse_gain_button.set_name ("MouseModeButton");
2766 mouse_zoom_button.set_name ("MouseModeButton");
2767 mouse_timefx_button.set_name ("MouseModeButton");
2768 mouse_audition_button.set_name ("MouseModeButton");
2769 internal_edit_button.set_name ("MouseModeButton");
2770 join_object_range_button.set_name ("MouseModeButton");
2772 mouse_move_button.unset_flags (CAN_FOCUS);
2773 mouse_select_button.unset_flags (CAN_FOCUS);
2774 mouse_gain_button.unset_flags (CAN_FOCUS);
2775 mouse_zoom_button.unset_flags (CAN_FOCUS);
2776 mouse_timefx_button.unset_flags (CAN_FOCUS);
2777 mouse_audition_button.unset_flags (CAN_FOCUS);
2778 internal_edit_button.unset_flags (CAN_FOCUS);
2779 join_object_range_button.unset_flags (CAN_FOCUS);
2783 _zoom_box.set_spacing (1);
2784 _zoom_box.set_border_width (0);
2786 zoom_in_button.set_name ("EditorTimeButton");
2787 zoom_in_button.set_image (*(manage (new Image (Stock::ZOOM_IN, Gtk::ICON_SIZE_MENU))));
2788 zoom_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), false));
2790 zoom_out_button.set_name ("EditorTimeButton");
2791 zoom_out_button.set_image (*(manage (new Image (Stock::ZOOM_OUT, Gtk::ICON_SIZE_MENU))));
2792 zoom_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), true));
2794 zoom_out_full_button.set_name ("EditorTimeButton");
2795 zoom_out_full_button.set_image (*(manage (new Image (Stock::ZOOM_100, Gtk::ICON_SIZE_MENU))));
2796 zoom_out_full_button.signal_clicked().connect (sigc::mem_fun(*this, &Editor::temporal_zoom_session));
2798 zoom_focus_selector.set_name ("ZoomFocusSelector");
2799 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
2800 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2802 _zoom_box.pack_start (zoom_out_button, false, false);
2803 _zoom_box.pack_start (zoom_in_button, false, false);
2804 _zoom_box.pack_start (zoom_out_full_button, false, false);
2806 _zoom_box.pack_start (zoom_focus_selector);
2808 /* Track zoom buttons */
2809 tav_expand_button.set_name ("TrackHeightButton");
2810 tav_expand_button.set_size_request (-1, 20);
2811 tav_expand_button.add (*(manage (new Image (::get_icon ("tav_exp")))));
2812 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("expand-tracks"));
2813 act->connect_proxy (tav_expand_button);
2815 tav_shrink_button.set_name ("TrackHeightButton");
2816 tav_shrink_button.set_size_request (-1, 20);
2817 tav_shrink_button.add (*(manage (new Image (::get_icon ("tav_shrink")))));
2818 act = ActionManager::get_action (X_("Editor"), X_("shrink-tracks"));
2819 act->connect_proxy (tav_shrink_button);
2821 _zoom_box.pack_start (tav_shrink_button);
2822 _zoom_box.pack_start (tav_expand_button);
2824 _zoom_tearoff = manage (new TearOff (_zoom_box));
2826 _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2827 &_zoom_tearoff->tearoff_window()));
2828 _zoom_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2829 &_zoom_tearoff->tearoff_window(), 0));
2830 _zoom_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2831 &_zoom_tearoff->tearoff_window()));
2832 _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2833 &_zoom_tearoff->tearoff_window(), 0));
2835 snap_box.set_spacing (1);
2836 snap_box.set_border_width (2);
2838 snap_type_selector.set_name ("SnapTypeSelector");
2839 set_popdown_strings (snap_type_selector, snap_type_strings, true);
2840 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2842 snap_mode_selector.set_name ("SnapModeSelector");
2843 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
2844 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2846 edit_point_selector.set_name ("EditPointSelector");
2847 set_popdown_strings (edit_point_selector, edit_point_strings, true);
2848 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2850 snap_box.pack_start (snap_mode_selector, false, false);
2851 snap_box.pack_start (snap_type_selector, false, false);
2852 snap_box.pack_start (edit_point_selector, false, false);
2856 HBox *nudge_box = manage (new HBox);
2857 nudge_box->set_spacing(1);
2858 nudge_box->set_border_width (2);
2860 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2861 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2863 nudge_box->pack_start (nudge_backward_button, false, false);
2864 nudge_box->pack_start (nudge_forward_button, false, false);
2865 nudge_box->pack_start (nudge_clock, false, false);
2868 /* Pack everything in... */
2870 HBox* hbox = manage (new HBox);
2871 hbox->set_spacing(10);
2873 _tools_tearoff = manage (new TearOff (*hbox));
2874 _tools_tearoff->set_name ("MouseModeBase");
2875 _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
2877 if (Profile->get_sae()) {
2878 _tools_tearoff->set_can_be_torn_off (false);
2881 _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2882 &_tools_tearoff->tearoff_window()));
2883 _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2884 &_tools_tearoff->tearoff_window(), 0));
2885 _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2886 &_tools_tearoff->tearoff_window()));
2887 _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2888 &_tools_tearoff->tearoff_window(), 0));
2890 toolbar_hbox.set_spacing (10);
2891 toolbar_hbox.set_border_width (1);
2893 toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
2894 toolbar_hbox.pack_start (*_zoom_tearoff, false, false);
2895 toolbar_hbox.pack_start (*_tools_tearoff, false, false);
2897 hbox->pack_start (snap_box, false, false);
2898 hbox->pack_start (*nudge_box, false, false);
2899 hbox->pack_start (panic_box, false, false);
2903 toolbar_base.set_name ("ToolBarBase");
2904 toolbar_base.add (toolbar_hbox);
2906 _toolbar_viewport.add (toolbar_base);
2907 /* stick to the required height but allow width to vary if there's not enough room */
2908 _toolbar_viewport.set_size_request (1, -1);
2910 toolbar_frame.set_shadow_type (SHADOW_OUT);
2911 toolbar_frame.set_name ("BaseFrame");
2912 toolbar_frame.add (_toolbar_viewport);
2914 DPIReset.connect (sigc::mem_fun (*this, &Editor::resize_text_widgets));
2918 Editor::setup_tooltips ()
2920 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
2921 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain"));
2922 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
2923 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
2924 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2925 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
2926 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
2927 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
2928 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
2929 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
2930 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
2931 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
2932 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
2933 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
2934 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
2935 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
2936 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
2937 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
2938 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
2939 ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
2940 ARDOUR_UI::instance()->set_tip (midi_panic_button, _("Send note off and reset controller messages on all MIDI channels"));
2941 ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode"));
2945 Editor::midi_panic ()
2947 cerr << "MIDI panic\n";
2950 _session->midi_panic();
2955 Editor::setup_midi_toolbar ()
2959 /* Midi sound notes */
2960 midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
2961 midi_sound_notes.set_relief(Gtk::RELIEF_NONE);
2962 midi_sound_notes.unset_flags (CAN_FOCUS);
2966 act = ActionManager::get_action (X_("MIDI"), X_("panic"));
2967 midi_panic_button.set_name("MidiPanicButton");
2968 act->connect_proxy (midi_panic_button);
2970 panic_box.pack_start (midi_sound_notes , true, true);
2971 panic_box.pack_start (midi_panic_button, true, true);
2975 Editor::convert_drop_to_paths (
2976 vector<string>& paths,
2977 const RefPtr<Gdk::DragContext>& /*context*/,
2980 const SelectionData& data,
2984 if (_session == 0) {
2988 vector<string> uris = data.get_uris();
2992 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
2993 are actually URI lists. So do it by hand.
2996 if (data.get_target() != "text/plain") {
3000 /* Parse the "uri-list" format that Nautilus provides,
3001 where each pathname is delimited by \r\n.
3003 THERE MAY BE NO NULL TERMINATING CHAR!!!
3006 string txt = data.get_text();
3010 p = (const char *) malloc (txt.length() + 1);
3011 txt.copy ((char *) p, txt.length(), 0);
3012 ((char*)p)[txt.length()] = '\0';
3018 while (g_ascii_isspace (*p))
3022 while (*q && (*q != '\n') && (*q != '\r')) {
3029 while (q > p && g_ascii_isspace (*q))
3034 uris.push_back (string (p, q - p + 1));
3038 p = strchr (p, '\n');
3050 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3052 if ((*i).substr (0,7) == "file://") {
3055 PBD::url_decode (p);
3057 // scan forward past three slashes
3059 string::size_type slashcnt = 0;
3060 string::size_type n = 0;
3061 string::iterator x = p.begin();
3063 while (slashcnt < 3 && x != p.end()) {
3066 } else if (slashcnt == 3) {
3073 if (slashcnt != 3 || x == p.end()) {
3074 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3078 paths.push_back (p.substr (n - 1));
3086 Editor::new_tempo_section ()
3092 Editor::map_transport_state ()
3094 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3096 if (_session && _session->transport_stopped()) {
3097 have_pending_keyboard_selection = false;
3100 update_loop_range_view (true);
3105 Editor::State::State (PublicEditor const * e)
3107 selection = new Selection (e);
3110 Editor::State::~State ()
3116 Editor::begin_reversible_command (string name)
3119 _session->begin_reversible_command (name);
3124 Editor::begin_reversible_command (GQuark q)
3127 _session->begin_reversible_command (q);
3132 Editor::commit_reversible_command ()
3135 _session->commit_reversible_command ();
3140 Editor::history_changed ()
3144 if (undo_action && _session) {
3145 if (_session->undo_depth() == 0) {
3148 label = string_compose(_("Undo (%1)"), _session->next_undo());
3150 undo_action->property_label() = label;
3153 if (redo_action && _session) {
3154 if (_session->redo_depth() == 0) {
3157 label = string_compose(_("Redo (%1)"), _session->next_redo());
3159 redo_action->property_label() = label;
3164 Editor::duplicate_dialog (bool with_dialog)
3168 if (mouse_mode == MouseRange) {
3169 if (selection->time.length() == 0) {
3174 RegionSelection rs = get_regions_from_selection_and_entered ();
3176 if (mouse_mode != MouseRange && rs.empty()) {
3182 ArdourDialog win (_("Duplicate"));
3183 Label label (_("Number of duplications:"));
3184 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3185 SpinButton spinner (adjustment, 0.0, 1);
3188 win.get_vbox()->set_spacing (12);
3189 win.get_vbox()->pack_start (hbox);
3190 hbox.set_border_width (6);
3191 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3193 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3194 place, visually. so do this by hand.
3197 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3198 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3199 spinner.grab_focus();
3205 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3206 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3207 win.set_default_response (RESPONSE_ACCEPT);
3209 win.set_position (WIN_POS_MOUSE);
3211 spinner.grab_focus ();
3213 switch (win.run ()) {
3214 case RESPONSE_ACCEPT:
3220 times = adjustment.get_value();
3223 if (mouse_mode == MouseRange) {
3224 duplicate_selection (times);
3226 duplicate_some_regions (rs, times);
3231 Editor::show_verbose_canvas_cursor ()
3233 verbose_canvas_cursor->raise_to_top();
3234 verbose_canvas_cursor->show();
3235 verbose_cursor_visible = true;
3239 Editor::hide_verbose_canvas_cursor ()
3241 verbose_canvas_cursor->hide();
3242 verbose_cursor_visible = false;
3246 Editor::clamp_verbose_cursor_x (double x)
3251 x = min (_canvas_width - 200.0, x);
3257 Editor::clamp_verbose_cursor_y (double y)
3259 if (y < canvas_timebars_vsize) {
3260 y = canvas_timebars_vsize;
3262 y = min (_canvas_height - 50, y);
3268 Editor::show_verbose_canvas_cursor_with (const string & txt, int32_t xoffset, int32_t yoffset)
3270 verbose_canvas_cursor->property_text() = txt.c_str();
3275 track_canvas->get_pointer (x, y);
3276 track_canvas->window_to_world (x, y, wx, wy);
3281 /* don't get too close to the edge */
3282 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (wx);
3283 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (wy);
3285 show_verbose_canvas_cursor ();
3289 Editor::set_verbose_canvas_cursor (const string & txt, double x, double y)
3291 verbose_canvas_cursor->property_text() = txt.c_str();
3292 /* don't get too close to the edge */
3293 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (x);
3294 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (y);
3298 Editor::set_verbose_canvas_cursor_text (const string & txt)
3300 verbose_canvas_cursor->property_text() = txt.c_str();
3304 Editor::set_edit_mode (EditMode m)
3306 Config->set_edit_mode (m);
3310 Editor::cycle_edit_mode ()
3312 switch (Config->get_edit_mode()) {
3314 if (Profile->get_sae()) {
3315 Config->set_edit_mode (Lock);
3317 Config->set_edit_mode (Splice);
3321 Config->set_edit_mode (Lock);
3324 Config->set_edit_mode (Slide);
3330 Editor::edit_mode_selection_done ()
3332 string s = edit_mode_selector.get_active_text ();
3335 Config->set_edit_mode (string_to_edit_mode (s));
3340 Editor::snap_type_selection_done ()
3342 string choice = snap_type_selector.get_active_text();
3343 SnapType snaptype = SnapToBeat;
3345 if (choice == _("Beats/2")) {
3346 snaptype = SnapToBeatDiv2;
3347 } else if (choice == _("Beats/3")) {
3348 snaptype = SnapToBeatDiv3;
3349 } else if (choice == _("Beats/4")) {
3350 snaptype = SnapToBeatDiv4;
3351 } else if (choice == _("Beats/5")) {
3352 snaptype = SnapToBeatDiv5;
3353 } else if (choice == _("Beats/6")) {
3354 snaptype = SnapToBeatDiv6;
3355 } else if (choice == _("Beats/7")) {
3356 snaptype = SnapToBeatDiv7;
3357 } else if (choice == _("Beats/8")) {
3358 snaptype = SnapToBeatDiv8;
3359 } else if (choice == _("Beats/10")) {
3360 snaptype = SnapToBeatDiv10;
3361 } else if (choice == _("Beats/12")) {
3362 snaptype = SnapToBeatDiv12;
3363 } else if (choice == _("Beats/14")) {
3364 snaptype = SnapToBeatDiv14;
3365 } else if (choice == _("Beats/16")) {
3366 snaptype = SnapToBeatDiv16;
3367 } else if (choice == _("Beats/20")) {
3368 snaptype = SnapToBeatDiv20;
3369 } else if (choice == _("Beats/24")) {
3370 snaptype = SnapToBeatDiv24;
3371 } else if (choice == _("Beats/28")) {
3372 snaptype = SnapToBeatDiv28;
3373 } else if (choice == _("Beats/32")) {
3374 snaptype = SnapToBeatDiv32;
3375 } else if (choice == _("Beats")) {
3376 snaptype = SnapToBeat;
3377 } else if (choice == _("Bars")) {
3378 snaptype = SnapToBar;
3379 } else if (choice == _("Marks")) {
3380 snaptype = SnapToMark;
3381 } else if (choice == _("Region starts")) {
3382 snaptype = SnapToRegionStart;
3383 } else if (choice == _("Region ends")) {
3384 snaptype = SnapToRegionEnd;
3385 } else if (choice == _("Region bounds")) {
3386 snaptype = SnapToRegionBoundary;
3387 } else if (choice == _("Region syncs")) {
3388 snaptype = SnapToRegionSync;
3389 } else if (choice == _("CD Frames")) {
3390 snaptype = SnapToCDFrame;
3391 } else if (choice == _("Timecode Frames")) {
3392 snaptype = SnapToTimecodeFrame;
3393 } else if (choice == _("Timecode Seconds")) {
3394 snaptype = SnapToTimecodeSeconds;
3395 } else if (choice == _("Timecode Minutes")) {
3396 snaptype = SnapToTimecodeMinutes;
3397 } else if (choice == _("Seconds")) {
3398 snaptype = SnapToSeconds;
3399 } else if (choice == _("Minutes")) {
3400 snaptype = SnapToMinutes;
3403 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3405 ract->set_active ();
3410 Editor::snap_mode_selection_done ()
3412 string choice = snap_mode_selector.get_active_text();
3413 SnapMode mode = SnapNormal;
3415 if (choice == _("No Grid")) {
3417 } else if (choice == _("Grid")) {
3419 } else if (choice == _("Magnetic")) {
3420 mode = SnapMagnetic;
3423 RefPtr<RadioAction> ract = snap_mode_action (mode);
3426 ract->set_active (true);
3431 Editor::cycle_edit_point (bool with_marker)
3433 switch (_edit_point) {
3435 set_edit_point_preference (EditAtPlayhead);
3437 case EditAtPlayhead:
3439 set_edit_point_preference (EditAtSelectedMarker);
3441 set_edit_point_preference (EditAtMouse);
3444 case EditAtSelectedMarker:
3445 set_edit_point_preference (EditAtMouse);
3451 Editor::edit_point_selection_done ()
3453 string choice = edit_point_selector.get_active_text();
3454 EditPoint ep = EditAtSelectedMarker;
3456 if (choice == _("Marker")) {
3457 set_edit_point_preference (EditAtSelectedMarker);
3458 } else if (choice == _("Playhead")) {
3459 set_edit_point_preference (EditAtPlayhead);
3461 set_edit_point_preference (EditAtMouse);
3464 RefPtr<RadioAction> ract = edit_point_action (ep);
3467 ract->set_active (true);
3472 Editor::zoom_focus_selection_done ()
3474 string choice = zoom_focus_selector.get_active_text();
3475 ZoomFocus focus_type = ZoomFocusLeft;
3477 if (choice == _("Left")) {
3478 focus_type = ZoomFocusLeft;
3479 } else if (choice == _("Right")) {
3480 focus_type = ZoomFocusRight;
3481 } else if (choice == _("Center")) {
3482 focus_type = ZoomFocusCenter;
3483 } else if (choice == _("Playhead")) {
3484 focus_type = ZoomFocusPlayhead;
3485 } else if (choice == _("Mouse")) {
3486 focus_type = ZoomFocusMouse;
3487 } else if (choice == _("Edit point")) {
3488 focus_type = ZoomFocusEdit;
3491 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3494 ract->set_active ();
3499 Editor::edit_controls_button_release (GdkEventButton* ev)
3501 if (Keyboard::is_context_menu_event (ev)) {
3502 ARDOUR_UI::instance()->add_route (this);
3508 Editor::mouse_select_button_release (GdkEventButton* ev)
3510 /* this handles just right-clicks */
3512 if (ev->button != 3) {
3520 Editor::set_zoom_focus (ZoomFocus f)
3522 string str = zoom_focus_strings[(int)f];
3524 if (str != zoom_focus_selector.get_active_text()) {
3525 zoom_focus_selector.set_active_text (str);
3528 if (zoom_focus != f) {
3531 ZoomFocusChanged (); /* EMIT_SIGNAL */
3538 Editor::ensure_float (Window& win)
3540 win.set_transient_for (*this);
3544 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3546 /* recover or initialize pane positions. do this here rather than earlier because
3547 we don't want the positions to change the child allocations, which they seem to do.
3553 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3565 width = default_width;
3566 height = default_height;
3568 if ((geometry = find_named_node (*node, "geometry")) != 0) {
3570 prop = geometry->property ("x-size");
3572 width = atoi (prop->value());
3574 prop = geometry->property ("y-size");
3576 height = atoi (prop->value());
3580 if (which == static_cast<Paned*> (&edit_pane)) {
3582 if (done & Horizontal) {
3586 if (geometry && (prop = geometry->property ("notebook-shrunk"))) {
3587 _notebook_shrunk = string_is_affirmative (prop->value ());
3590 if (geometry && (prop = geometry->property ("pre-maximal-horizontal-pane-position"))) {
3591 pre_maximal_horizontal_pane_position = atoi (prop->value ());
3594 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3595 /* initial allocation is 90% to canvas, 10% to notebook */
3596 pos = (int) floor (alloc.get_width() * 0.90f);
3597 snprintf (buf, sizeof(buf), "%d", pos);
3599 pos = atoi (prop->value());
3602 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3603 edit_pane.set_position (pos);
3604 if (pre_maximal_horizontal_pane_position == 0) {
3605 pre_maximal_horizontal_pane_position = pos;
3609 done = (Pane) (done | Horizontal);
3611 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3613 if (done & Vertical) {
3617 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3618 /* initial allocation is 90% to canvas, 10% to summary */
3619 pos = (int) floor (alloc.get_height() * 0.90f);
3620 snprintf (buf, sizeof(buf), "%d", pos);
3622 pos = atoi (prop->value());
3625 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3626 editor_summary_pane.set_position (pos);
3627 pre_maximal_vertical_pane_position = pos;
3630 done = (Pane) (done | Vertical);
3635 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3637 if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) {
3638 top_hbox.remove (toolbar_frame);
3643 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3645 if (toolbar_frame.get_parent() == 0) {
3646 top_hbox.pack_end (toolbar_frame);
3651 Editor::set_show_measures (bool yn)
3653 if (_show_measures != yn) {
3656 if ((_show_measures = yn) == true) {
3658 tempo_lines->show();
3666 Editor::toggle_follow_playhead ()
3668 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3670 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3671 set_follow_playhead (tact->get_active());
3675 /** @param yn true to follow playhead, otherwise false.
3676 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
3679 Editor::set_follow_playhead (bool yn, bool catch_up)
3681 if (_follow_playhead != yn) {
3682 if ((_follow_playhead = yn) == true && catch_up) {
3684 reset_x_origin_to_follow_playhead ();
3691 Editor::toggle_stationary_playhead ()
3693 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3695 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3696 set_stationary_playhead (tact->get_active());
3701 Editor::set_stationary_playhead (bool yn)
3703 if (_stationary_playhead != yn) {
3704 if ((_stationary_playhead = yn) == true) {
3706 // FIXME need a 3.0 equivalent of this 2.X call
3707 // update_current_screen ();
3714 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3716 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3718 xfade->set_active (!xfade->active());
3723 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3725 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3727 xfade->set_follow_overlap (!xfade->following_overlap());
3732 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3734 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3740 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3744 switch (cew.run ()) {
3745 case RESPONSE_ACCEPT:
3752 PropertyChange all_crossfade_properties;
3753 all_crossfade_properties.add (ARDOUR::Properties::active);
3754 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3755 xfade->PropertyChanged (all_crossfade_properties);
3759 Editor::playlist_selector () const
3761 return *_playlist_selector;
3765 Editor::get_grid_type_as_beats (bool& success, framepos_t position)
3769 switch (_snap_type) {
3774 case SnapToBeatDiv32:
3777 case SnapToBeatDiv28:
3780 case SnapToBeatDiv24:
3783 case SnapToBeatDiv20:
3786 case SnapToBeatDiv16:
3789 case SnapToBeatDiv14:
3792 case SnapToBeatDiv12:
3795 case SnapToBeatDiv10:
3798 case SnapToBeatDiv8:
3801 case SnapToBeatDiv7:
3804 case SnapToBeatDiv6:
3807 case SnapToBeatDiv5:
3810 case SnapToBeatDiv4:
3813 case SnapToBeatDiv3:
3816 case SnapToBeatDiv2:
3822 return _session->tempo_map().meter_at (position).beats_per_bar();
3827 case SnapToTimecodeFrame:
3828 case SnapToTimecodeSeconds:
3829 case SnapToTimecodeMinutes:
3832 case SnapToRegionStart:
3833 case SnapToRegionEnd:
3834 case SnapToRegionSync:
3835 case SnapToRegionBoundary:
3845 Editor::get_nudge_distance (framepos_t pos, framecnt_t& next)
3849 ret = nudge_clock.current_duration (pos);
3850 next = ret + 1; /* XXXX fix me */
3856 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3858 ArdourDialog dialog (_("Playlist Deletion"));
3859 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3860 "If left alone, no audio files used by it will be cleaned.\n"
3861 "If deleted, audio files used by it alone by will cleaned."),
3864 dialog.set_position (WIN_POS_CENTER);
3865 dialog.get_vbox()->pack_start (label);
3869 dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT);
3870 dialog.add_button (_("Keep playlist"), RESPONSE_REJECT);
3871 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3873 switch (dialog.run ()) {
3874 case RESPONSE_ACCEPT:
3875 /* delete the playlist */
3879 case RESPONSE_REJECT:
3880 /* keep the playlist */
3892 Editor::audio_region_selection_covers (framepos_t where)
3894 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3895 if ((*a)->region()->covers (where)) {
3904 Editor::prepare_for_cleanup ()
3906 cut_buffer->clear_regions ();
3907 cut_buffer->clear_playlists ();
3909 selection->clear_regions ();
3910 selection->clear_playlists ();
3912 _regions->suspend_redisplay ();
3916 Editor::finish_cleanup ()
3918 _regions->resume_redisplay ();
3922 Editor::transport_loop_location()
3925 return _session->locations()->auto_loop_location();
3932 Editor::transport_punch_location()
3935 return _session->locations()->auto_punch_location();
3942 Editor::control_layout_scroll (GdkEventScroll* ev)
3944 if (Keyboard::some_magic_widget_has_focus()) {
3948 switch (ev->direction) {
3950 scroll_tracks_up_line ();
3954 case GDK_SCROLL_DOWN:
3955 scroll_tracks_down_line ();
3959 /* no left/right handling yet */
3967 Editor::session_state_saved (string)
3970 _snapshots->redisplay ();
3974 Editor::maximise_editing_space ()
3976 _mouse_mode_tearoff->set_visible (false);
3977 _tools_tearoff->set_visible (false);
3978 _zoom_tearoff->set_visible (false);
3980 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
3981 pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
3982 pre_maximal_editor_width = this->get_width ();
3983 pre_maximal_editor_height = this->get_height ();
3985 if (post_maximal_horizontal_pane_position == 0) {
3986 post_maximal_horizontal_pane_position = edit_pane.get_width();
3989 if (post_maximal_vertical_pane_position == 0) {
3990 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
3995 if (post_maximal_editor_width) {
3996 edit_pane.set_position (post_maximal_horizontal_pane_position -
3997 abs(post_maximal_editor_width - pre_maximal_editor_width));
3999 edit_pane.set_position (post_maximal_horizontal_pane_position);
4002 if (post_maximal_editor_height) {
4003 editor_summary_pane.set_position (post_maximal_vertical_pane_position -
4004 abs(post_maximal_editor_height - pre_maximal_editor_height));
4006 editor_summary_pane.set_position (post_maximal_vertical_pane_position);
4009 if (Config->get_keep_tearoffs()) {
4010 _mouse_mode_tearoff->set_visible (true);
4011 _tools_tearoff->set_visible (true);
4012 if (Config->get_show_zoom_tools ()) {
4013 _zoom_tearoff->set_visible (true);
4020 Editor::restore_editing_space ()
4022 // user changed width/height of panes during fullscreen
4024 if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
4025 post_maximal_horizontal_pane_position = edit_pane.get_position();
4028 if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
4029 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
4034 _mouse_mode_tearoff->set_visible (true);
4035 _tools_tearoff->set_visible (true);
4036 if (Config->get_show_zoom_tools ()) {
4037 _zoom_tearoff->set_visible (true);
4039 post_maximal_editor_width = this->get_width();
4040 post_maximal_editor_height = this->get_height();
4042 edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
4043 editor_summary_pane.set_position (pre_maximal_vertical_pane_position + abs(this->get_height() - pre_maximal_editor_height));
4047 * Make new playlists for a given track and also any others that belong
4048 * to the same active route group with the `edit' property.
4053 Editor::new_playlists (TimeAxisView* v)
4055 begin_reversible_command (_("new playlists"));
4056 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4057 _session->playlists->get (playlists);
4058 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4059 commit_reversible_command ();
4063 * Use a copy of 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::copy_playlists (TimeAxisView* v)
4071 begin_reversible_command (_("copy playlists"));
4072 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4073 _session->playlists->get (playlists);
4074 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4075 commit_reversible_command ();
4078 /** Clear the current playlist for a given track and also any others that belong
4079 * to the same active route group with the `edit' property.
4084 Editor::clear_playlists (TimeAxisView* v)
4086 begin_reversible_command (_("clear playlists"));
4087 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4088 _session->playlists->get (playlists);
4089 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4090 commit_reversible_command ();
4094 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4096 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4100 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4102 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4106 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4108 atv.clear_playlist ();
4112 Editor::on_key_press_event (GdkEventKey* ev)
4114 return key_press_focus_accelerator_handler (*this, ev);
4118 Editor::on_key_release_event (GdkEventKey* ev)
4120 return Gtk::Window::on_key_release_event (ev);
4121 // return key_press_focus_accelerator_handler (*this, ev);
4124 /** Queue up a change to the viewport x origin.
4125 * @param frame New x origin.
4128 Editor::reset_x_origin (framepos_t frame)
4130 queue_visual_change (frame);
4134 Editor::reset_y_origin (double y)
4136 queue_visual_change_y (y);
4140 Editor::reset_zoom (double fpu)
4142 queue_visual_change (fpu);
4146 Editor::reposition_and_zoom (framepos_t frame, double fpu)
4148 reset_x_origin (frame);
4151 if (!no_save_visual) {
4152 undo_visual_stack.push_back (current_visual_state(false));
4156 Editor::VisualState*
4157 Editor::current_visual_state (bool with_tracks)
4159 VisualState* vs = new VisualState;
4160 vs->y_position = vertical_adjustment.get_value();
4161 vs->frames_per_unit = frames_per_unit;
4162 vs->leftmost_frame = leftmost_frame;
4163 vs->zoom_focus = zoom_focus;
4166 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4167 vs->track_states.push_back (TAVState ((*i), &(*i)->get_state()));
4175 Editor::undo_visual_state ()
4177 if (undo_visual_stack.empty()) {
4181 redo_visual_stack.push_back (current_visual_state());
4183 VisualState* vs = undo_visual_stack.back();
4184 undo_visual_stack.pop_back();
4185 use_visual_state (*vs);
4189 Editor::redo_visual_state ()
4191 if (redo_visual_stack.empty()) {
4195 undo_visual_stack.push_back (current_visual_state());
4197 VisualState* vs = redo_visual_stack.back();
4198 redo_visual_stack.pop_back();
4199 use_visual_state (*vs);
4203 Editor::swap_visual_state ()
4205 if (undo_visual_stack.empty()) {
4206 redo_visual_state ();
4208 undo_visual_state ();
4213 Editor::use_visual_state (VisualState& vs)
4215 no_save_visual = true;
4217 _routes->suspend_redisplay ();
4219 vertical_adjustment.set_value (vs.y_position);
4221 set_zoom_focus (vs.zoom_focus);
4222 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4224 for (list<TAVState>::iterator i = vs.track_states.begin(); i != vs.track_states.end(); ++i) {
4225 TrackViewList::iterator t;
4227 /* check if the track still exists - it could have been deleted */
4229 if ((t = find (track_views.begin(), track_views.end(), i->first)) != track_views.end()) {
4230 (*t)->set_state (*(i->second), Stateful::loading_state_version);
4235 if (!vs.track_states.empty()) {
4236 _routes->update_visibility ();
4239 _routes->resume_redisplay ();
4241 no_save_visual = false;
4245 Editor::set_frames_per_unit (double fpu)
4247 /* this is the core function that controls the zoom level of the canvas. it is called
4248 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4251 if (fpu == frames_per_unit) {
4260 /* don't allow zooms that fit more than the maximum number
4261 of frames into an 800 pixel wide space.
4264 if (max_framepos / fpu < 800.0) {
4269 tempo_lines->tempo_map_changed();
4271 frames_per_unit = fpu;
4276 Editor::post_zoom ()
4278 // convert fpu to frame count
4280 framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width);
4282 if (frames_per_unit != zoom_range_clock.current_duration()) {
4283 zoom_range_clock.set (frames);
4286 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4287 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4288 (*i)->reshow_selection (selection->time);
4292 ZoomChanged (); /* EMIT_SIGNAL */
4294 //reset_scrolling_region ();
4296 if (playhead_cursor) {
4297 playhead_cursor->set_position (playhead_cursor->current_frame);
4300 refresh_location_display();
4301 _summary->set_overlays_dirty ();
4303 update_marker_labels ();
4309 Editor::queue_visual_change (framepos_t where)
4311 pending_visual_change.add (VisualChange::TimeOrigin);
4312 pending_visual_change.time_origin = where;
4313 ensure_visual_change_idle_handler ();
4317 Editor::queue_visual_change (double fpu)
4319 pending_visual_change.add (VisualChange::ZoomLevel);
4320 pending_visual_change.frames_per_unit = fpu;
4322 ensure_visual_change_idle_handler ();
4326 Editor::queue_visual_change_y (double y)
4328 pending_visual_change.add (VisualChange::YOrigin);
4329 pending_visual_change.y_origin = y;
4331 ensure_visual_change_idle_handler ();
4335 Editor::ensure_visual_change_idle_handler ()
4337 if (pending_visual_change.idle_handler_id < 0) {
4338 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4343 Editor::_idle_visual_changer (void* arg)
4345 return static_cast<Editor*>(arg)->idle_visual_changer ();
4349 Editor::idle_visual_changer ()
4351 VisualChange::Type p = pending_visual_change.pending;
4352 pending_visual_change.pending = (VisualChange::Type) 0;
4354 double const last_time_origin = horizontal_position ();
4356 if (p & VisualChange::TimeOrigin) {
4357 /* This is a bit of a hack, but set_frames_per_unit
4358 below will (if called) end up with the
4359 CrossfadeViews looking at Editor::leftmost_frame,
4360 and if we're changing origin and zoom in the same
4361 operation it will be the wrong value unless we
4365 leftmost_frame = pending_visual_change.time_origin;
4368 if (p & VisualChange::ZoomLevel) {
4369 set_frames_per_unit (pending_visual_change.frames_per_unit);
4371 compute_fixed_ruler_scale ();
4372 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4373 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4374 update_tempo_based_rulers ();
4376 if (p & VisualChange::TimeOrigin) {
4377 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4379 if (p & VisualChange::YOrigin) {
4380 vertical_adjustment.set_value (pending_visual_change.y_origin);
4383 if (last_time_origin == horizontal_position ()) {
4384 /* changed signal not emitted */
4385 update_fixed_rulers ();
4386 redisplay_tempo (true);
4389 _summary->set_overlays_dirty ();
4391 pending_visual_change.idle_handler_id = -1;
4392 return 0; /* this is always a one-shot call */
4395 struct EditorOrderTimeAxisSorter {
4396 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4397 return a->order () < b->order ();
4402 Editor::sort_track_selection (TrackViewList* sel)
4404 EditorOrderTimeAxisSorter cmp;
4409 selection->tracks.sort (cmp);
4414 Editor::get_preferred_edit_position (bool ignore_playhead)
4417 framepos_t where = 0;
4418 EditPoint ep = _edit_point;
4420 if (entered_marker) {
4421 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position()));
4422 return entered_marker->position();
4425 if (ignore_playhead && ep == EditAtPlayhead) {
4426 ep = EditAtSelectedMarker;
4430 case EditAtPlayhead:
4431 where = _session->audible_frame();
4432 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where));
4435 case EditAtSelectedMarker:
4436 if (!selection->markers.empty()) {
4438 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4441 where = loc->start();
4445 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where));
4453 if (!mouse_frame (where, ignored)) {
4454 /* XXX not right but what can we do ? */
4458 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where));
4466 Editor::set_loop_range (framepos_t start, framepos_t end, string cmd)
4468 if (!_session) return;
4470 begin_reversible_command (cmd);
4474 if ((tll = transport_loop_location()) == 0) {
4475 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop);
4476 XMLNode &before = _session->locations()->get_state();
4477 _session->locations()->add (loc, true);
4478 _session->set_auto_loop_location (loc);
4479 XMLNode &after = _session->locations()->get_state();
4480 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4482 XMLNode &before = tll->get_state();
4483 tll->set_hidden (false, this);
4484 tll->set (start, end);
4485 XMLNode &after = tll->get_state();
4486 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4489 commit_reversible_command ();
4493 Editor::set_punch_range (framepos_t start, framepos_t end, string cmd)
4495 if (!_session) return;
4497 begin_reversible_command (cmd);
4501 if ((tpl = transport_punch_location()) == 0) {
4502 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoPunch);
4503 XMLNode &before = _session->locations()->get_state();
4504 _session->locations()->add (loc, true);
4505 _session->set_auto_loop_location (loc);
4506 XMLNode &after = _session->locations()->get_state();
4507 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4510 XMLNode &before = tpl->get_state();
4511 tpl->set_hidden (false, this);
4512 tpl->set (start, end);
4513 XMLNode &after = tpl->get_state();
4514 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4517 commit_reversible_command ();
4520 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4521 * @param rs List to which found regions are added.
4522 * @param where Time to look at.
4523 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4526 Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4528 const TrackViewList* tracks;
4531 tracks = &track_views;
4536 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4538 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4541 boost::shared_ptr<Track> tr;
4542 boost::shared_ptr<Playlist> pl;
4544 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4546 Playlist::RegionList* regions = pl->regions_at (
4547 (framepos_t) floor ( (double) where * tr->speed()));
4549 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4550 RegionView* rv = rtv->view()->find_view (*i);
4563 Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4565 const TrackViewList* tracks;
4568 tracks = &track_views;
4573 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4574 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4576 boost::shared_ptr<Track> tr;
4577 boost::shared_ptr<Playlist> pl;
4579 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4581 Playlist::RegionList* regions = pl->regions_touched (
4582 (framepos_t) floor ( (double)where * tr->speed()), max_framepos);
4584 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4586 RegionView* rv = rtv->view()->find_view (*i);
4599 /** Start with regions that are selected. Then add equivalent regions
4600 * on tracks in the same active edit-enabled route group as any of
4601 * the regions that we started with.
4605 Editor::get_regions_from_selection ()
4607 return get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
4610 /** Get regions using the following method:
4612 * Make an initial region list using the selected regions, unless
4613 * the edit point is `mouse' and the mouse is over an unselected
4614 * region. In this case, start with just that region.
4616 * Then, make an initial track list of the tracks that these
4617 * regions are on, and if the edit point is not `mouse', add the
4620 * Look at this track list and add any other tracks that are on the
4621 * same active edit-enabled route group as one of the initial tracks.
4623 * Finally take the initial region list and add any regions that are
4624 * under the edit point on one of the tracks on the track list to get
4625 * the returned region list.
4627 * The rationale here is that the mouse edit point is special in that
4628 * its position describes both a time and a track; the other edit
4629 * modes only describe a time. Hence if the edit point is `mouse' we
4630 * ignore selected tracks, as we assume the user means something by
4631 * pointing at a particular track. Also in this case we take note of
4632 * the region directly under the edit point, as there is always just one
4633 * (rather than possibly several with non-mouse edit points).
4637 Editor::get_regions_from_selection_and_edit_point ()
4639 RegionSelection regions;
4641 if (_edit_point == EditAtMouse && entered_regionview && !selection->regions.contains (entered_regionview)) {
4642 regions.add (entered_regionview);
4644 regions = selection->regions;
4647 TrackViewList tracks;
4649 if (_edit_point != EditAtMouse) {
4650 tracks = selection->tracks;
4653 /* Add any other tracks that have regions that are in the same
4654 edit-activated route group as one of our regions.
4656 for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) {
4658 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4660 if (g && g->is_active() && g->is_edit()) {
4661 tracks.add (axis_views_from_routes (g->route_list()));
4665 if (!tracks.empty()) {
4666 /* now find regions that are at the edit position on those tracks */
4667 framepos_t const where = get_preferred_edit_position ();
4668 get_regions_at (regions, where, tracks);
4674 /** Start with regions that are selected, or the entered regionview if none are selected.
4675 * Then add equivalent regions on tracks in the same active edit-enabled route group as any
4676 * of the regions that we started with.
4680 Editor::get_regions_from_selection_and_entered ()
4682 RegionSelection regions = selection->regions;
4684 if (regions.empty() && entered_regionview) {
4685 regions.add (entered_regionview);
4688 return get_equivalent_regions (regions, ARDOUR::Properties::edit.property_id);
4692 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4694 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4696 RouteTimeAxisView* tatv;
4698 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4700 boost::shared_ptr<Playlist> pl;
4701 vector<boost::shared_ptr<Region> > results;
4703 boost::shared_ptr<Track> tr;
4705 if ((tr = tatv->track()) == 0) {
4710 if ((pl = (tr->playlist())) != 0) {
4711 pl->get_region_list_equivalent_regions (region, results);
4714 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4715 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4716 regions.push_back (marv);
4725 Editor::show_rhythm_ferret ()
4727 if (rhythm_ferret == 0) {
4728 rhythm_ferret = new RhythmFerret(*this);
4731 rhythm_ferret->set_session (_session);
4732 rhythm_ferret->show ();
4733 rhythm_ferret->present ();
4737 Editor::first_idle ()
4739 MessageDialog* dialog = 0;
4741 if (track_views.size() > 1) {
4742 dialog = new MessageDialog (*this,
4743 string_compose (_("Please wait while %1 loads visual data"), PROGRAM_NAME),
4748 ARDOUR_UI::instance()->flush_pending ();
4751 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4755 // first idle adds route children (automation tracks), so we need to redisplay here
4756 _routes->redisplay ();
4764 Editor::_idle_resize (gpointer arg)
4766 return ((Editor*)arg)->idle_resize ();
4770 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4772 if (resize_idle_id < 0) {
4773 resize_idle_id = g_idle_add (_idle_resize, this);
4774 _pending_resize_amount = 0;
4777 /* make a note of the smallest resulting height, so that we can clamp the
4778 lower limit at TimeAxisView::hSmall */
4780 int32_t min_resulting = INT32_MAX;
4782 _pending_resize_amount += h;
4783 _pending_resize_view = view;
4785 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4787 if (selection->tracks.contains (_pending_resize_view)) {
4788 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4789 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4793 if (min_resulting < 0) {
4798 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4799 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4803 /** Handle pending resizing of tracks */
4805 Editor::idle_resize ()
4807 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4809 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4810 selection->tracks.contains (_pending_resize_view)) {
4812 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4813 if (*i != _pending_resize_view) {
4814 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4819 _pending_resize_amount = 0;
4821 _group_tabs->set_dirty ();
4822 resize_idle_id = -1;
4830 ENSURE_GUI_THREAD (*this, &Editor::located);
4832 playhead_cursor->set_position (_session->audible_frame ());
4833 if (_follow_playhead && !_pending_initial_locate) {
4834 reset_x_origin_to_follow_playhead ();
4837 _pending_locate_request = false;
4838 _pending_initial_locate = false;
4842 Editor::region_view_added (RegionView *)
4844 _summary->set_dirty ();
4848 Editor::region_view_removed ()
4850 _summary->set_dirty ();
4854 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4856 TrackViewList::const_iterator j = track_views.begin ();
4857 while (j != track_views.end()) {
4858 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4859 if (rtv && rtv->route() == r) {
4870 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4874 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4875 TimeAxisView* tv = axis_view_from_route (*i);
4886 Editor::handle_new_route (RouteList& routes)
4888 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4890 RouteTimeAxisView *rtv;
4891 list<RouteTimeAxisView*> new_views;
4893 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4894 boost::shared_ptr<Route> route = (*x);
4896 if (route->is_hidden() || route->is_monitor()) {
4900 DataType dt = route->input()->default_type();
4902 if (dt == ARDOUR::DataType::AUDIO) {
4903 rtv = new AudioTimeAxisView (*this, _session, route, *track_canvas);
4904 } else if (dt == ARDOUR::DataType::MIDI) {
4905 rtv = new MidiTimeAxisView (*this, _session, route, *track_canvas);
4907 throw unknown_type();
4910 new_views.push_back (rtv);
4911 track_views.push_back (rtv);
4913 rtv->effective_gain_display ();
4915 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4916 rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed));
4919 _routes->routes_added (new_views);
4920 _summary->routes_added (new_views);
4922 if (show_editor_mixer_when_tracks_arrive) {
4923 show_editor_mixer (true);
4926 editor_list_button.set_sensitive (true);
4930 Editor::timeaxisview_deleted (TimeAxisView *tv)
4932 if (_session && _session->deletion_in_progress()) {
4933 /* the situation is under control */
4937 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
4939 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
4941 _routes->route_removed (tv);
4943 if (tv == entered_track) {
4947 TimeAxisView::Children c = tv->get_child_list ();
4948 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
4949 if (entered_track == i->get()) {
4954 /* remove it from the list of track views */
4956 TrackViewList::iterator i;
4958 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
4959 i = track_views.erase (i);
4962 /* update whatever the current mixer strip is displaying, if revelant */
4964 boost::shared_ptr<Route> route;
4967 route = rtav->route ();
4970 if (current_mixer_strip && current_mixer_strip->route() == route) {
4972 TimeAxisView* next_tv;
4974 if (track_views.empty()) {
4976 } else if (i == track_views.end()) {
4977 next_tv = track_views.front();
4984 set_selected_mixer_strip (*next_tv);
4986 /* make the editor mixer strip go away setting the
4987 * button to inactive (which also unticks the menu option)
4990 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
4996 Editor::hide_track_in_display (TimeAxisView* tv, bool /*temponly*/)
4998 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
5000 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
5001 // this will hide the mixer strip
5002 set_selected_mixer_strip (*tv);
5005 _routes->hide_track_in_display (*tv);
5009 Editor::sync_track_view_list_and_routes ()
5011 track_views = TrackViewList (_routes->views ());
5013 _summary->set_dirty ();
5014 _group_tabs->set_dirty ();
5016 return false; // do not call again (until needed)
5020 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
5022 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5027 /** Find a RouteTimeAxisView by the ID of its route */
5029 Editor::get_route_view_by_route_id (PBD::ID& id) const
5031 RouteTimeAxisView* v;
5033 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5034 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5035 if(v->route()->id() == id) {
5045 Editor::fit_route_group (RouteGroup *g)
5047 TrackViewList ts = axis_views_from_routes (g->route_list ());
5052 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5054 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5057 _session->cancel_audition ();
5061 if (_session->is_auditioning()) {
5062 _session->cancel_audition ();
5063 if (r == last_audition_region) {
5068 _session->audition_region (r);
5069 last_audition_region = r;
5074 Editor::hide_a_region (boost::shared_ptr<Region> r)
5076 r->set_hidden (true);
5080 Editor::show_a_region (boost::shared_ptr<Region> r)
5082 r->set_hidden (false);
5086 Editor::audition_region_from_region_list ()
5088 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5092 Editor::hide_region_from_region_list ()
5094 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5098 Editor::show_region_in_region_list ()
5100 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5104 Editor::step_edit_status_change (bool yn)
5107 start_step_editing ();
5109 stop_step_editing ();
5114 Editor::start_step_editing ()
5116 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5120 Editor::stop_step_editing ()
5122 step_edit_connection.disconnect ();
5126 Editor::check_step_edit ()
5128 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5129 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5131 mtv->check_step_edit ();
5135 return true; // do it again, till we stop
5139 Editor::scroll_press (Direction dir)
5141 ++_scroll_callbacks;
5143 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5144 /* delay the first auto-repeat */
5150 scroll_backward (1);
5158 scroll_tracks_up_line ();
5162 scroll_tracks_down_line ();
5166 /* do hacky auto-repeat */
5167 if (!_scroll_connection.connected ()) {
5169 _scroll_connection = Glib::signal_timeout().connect (
5170 sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), dir), 100
5173 _scroll_callbacks = 0;
5180 Editor::scroll_release ()
5182 _scroll_connection.disconnect ();
5185 /** Queue a change for the Editor viewport x origin to follow the playhead */
5187 Editor::reset_x_origin_to_follow_playhead ()
5189 framepos_t const frame = playhead_cursor->current_frame;
5191 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5193 if (_session->transport_speed() < 0) {
5195 if (frame > (current_page_frames() / 2)) {
5196 center_screen (frame-(current_page_frames()/2));
5198 center_screen (current_page_frames()/2);
5203 if (frame < leftmost_frame) {
5206 if (_session->transport_rolling()) {
5207 /* rolling; end up with the playhead at the right of the page */
5208 l = frame - current_page_frames ();
5210 /* not rolling: end up with the playhead 3/4 of the way along the page */
5211 l = frame - (3 * current_page_frames() / 4);
5218 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5221 if (_session->transport_rolling()) {
5222 /* rolling: end up with the playhead on the left of the page */
5223 center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5225 /* not rolling: end up with the playhead 1/4 of the way along the page */
5226 center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5234 Editor::super_rapid_screen_update ()
5236 if (!_session || !_session->engine().running()) {
5240 /* METERING / MIXER STRIPS */
5242 /* update track meters, if required */
5243 if (is_mapped() && meters_running) {
5244 RouteTimeAxisView* rtv;
5245 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5246 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5247 rtv->fast_update ();
5252 /* and any current mixer strip */
5253 if (current_mixer_strip) {
5254 current_mixer_strip->fast_update ();
5257 /* PLAYHEAD AND VIEWPORT */
5259 framepos_t const frame = _session->audible_frame();
5261 /* There are a few reasons why we might not update the playhead / viewport stuff:
5263 * 1. we don't update things when there's a pending locate request, otherwise
5264 * when the editor requests a locate there is a chance that this method
5265 * will move the playhead before the locate request is processed, causing
5267 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5268 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5271 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5273 last_update_frame = frame;
5275 if (!_dragging_playhead) {
5276 playhead_cursor->set_position (frame);
5279 if (!_stationary_playhead) {
5281 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5282 reset_x_origin_to_follow_playhead ();
5287 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5291 // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5292 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5293 if (target <= 0.0) {
5296 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5297 target = (target * 0.15) + (current * 0.85);
5303 set_horizontal_position (current);
5312 Editor::session_going_away ()
5314 _have_idled = false;
5316 _session_connections.drop_connections ();
5318 super_rapid_screen_update_connection.disconnect ();
5320 selection->clear ();
5321 cut_buffer->clear ();
5323 clicked_regionview = 0;
5324 clicked_axisview = 0;
5325 clicked_routeview = 0;
5326 clicked_crossfadeview = 0;
5327 entered_regionview = 0;
5329 last_update_frame = 0;
5332 playhead_cursor->canvas_item.hide ();
5334 /* rip everything out of the list displays */
5338 _route_groups->clear ();
5340 /* do this first so that deleting a track doesn't reset cms to null
5341 and thus cause a leak.
5344 if (current_mixer_strip) {
5345 if (current_mixer_strip->get_parent() != 0) {
5346 global_hpacker.remove (*current_mixer_strip);
5348 delete current_mixer_strip;
5349 current_mixer_strip = 0;
5352 /* delete all trackviews */
5354 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5357 track_views.clear ();
5359 zoom_range_clock.set_session (0);
5360 nudge_clock.set_session (0);
5362 editor_list_button.set_active(false);
5363 editor_list_button.set_sensitive(false);
5365 /* clear tempo/meter rulers */
5366 remove_metric_marks ();
5368 clear_marker_display ();
5370 delete current_bbt_points;
5371 current_bbt_points = 0;
5373 /* get rid of any existing editor mixer strip */
5375 WindowTitle title(Glib::get_application_name());
5376 title += _("Editor");
5378 set_title (title.get_string());
5380 SessionHandlePtr::session_going_away ();
5385 Editor::show_editor_list (bool yn)
5388 _the_notebook.show ();
5390 _the_notebook.hide ();
5395 Editor::change_region_layering_order ()
5397 framepos_t const position = get_preferred_edit_position ();
5399 if (!clicked_routeview) {
5400 if (layering_order_editor) {
5401 layering_order_editor->hide ();
5406 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
5412 boost::shared_ptr<Playlist> pl = track->playlist();
5418 if (layering_order_editor == 0) {
5419 layering_order_editor = new RegionLayeringOrderEditor(*this);
5422 layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position);
5423 layering_order_editor->maybe_present ();
5427 Editor::update_region_layering_order_editor ()
5429 if (layering_order_editor && layering_order_editor->is_visible ()) {
5430 change_region_layering_order ();
5435 Editor::setup_fade_images ()
5437 _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear")));
5438 _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut")));
5439 _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut")));
5440 _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut")));
5441 _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut")));
5443 _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear")));
5444 _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut")));
5445 _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut")));
5446 _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut")));
5447 _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut")));
5451 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
5453 Editor::action_menu_item (std::string const & name)
5455 Glib::RefPtr<Action> a = editor_actions->get_action (name);
5458 return *manage (a->create_menu_item ());
5462 Editor::resize_text_widgets ()
5464 set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_FUDGE+10, 15);
5465 set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_FUDGE+10, 15);
5466 set_size_request_to_display_given_text (snap_type_selector, snap_type_strings, COMBO_FUDGE+10, 15);
5467 set_size_request_to_display_given_text (snap_mode_selector, snap_mode_strings, COMBO_FUDGE+10, 15);
5468 set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_FUDGE+10, 15);
5472 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
5474 EventBox* b = manage (new EventBox);
5475 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
5476 Label* l = manage (new Label (name));
5480 _the_notebook.append_page (widget, *b);
5484 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
5486 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
5487 _the_notebook.set_current_page (_the_notebook.page_num (*page));
5490 if (ev->type == GDK_2BUTTON_PRESS) {
5492 /* double-click on a notebook tab shrinks or expands the notebook */
5494 if (_notebook_shrunk) {
5495 edit_pane.set_position (pre_maximal_horizontal_pane_position);
5496 _notebook_shrunk = false;
5498 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
5499 edit_pane.set_position (edit_pane.get_position() + page->get_width());
5500 _notebook_shrunk = true;