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"
82 #include "playlist_selector.h"
83 #include "audio_region_view.h"
84 #include "rgb_macros.h"
85 #include "selection.h"
86 #include "audio_streamview.h"
87 #include "time_axis_view.h"
88 #include "audio_time_axis.h"
90 #include "crossfade_view.h"
91 #include "canvas-noevent-text.h"
93 #include "public_editor.h"
94 #include "crossfade_edit.h"
95 #include "canvas_impl.h"
98 #include "gui_thread.h"
99 #include "simpleline.h"
100 #include "rhythm_ferret.h"
102 #include "tempo_lines.h"
103 #include "analysis_window.h"
104 #include "bundle_manager.h"
105 #include "global_port_matrix.h"
106 #include "editor_drag.h"
107 #include "editor_group_tabs.h"
108 #include "automation_time_axis.h"
109 #include "editor_routes.h"
110 #include "midi_time_axis.h"
111 #include "mixer_strip.h"
112 #include "editor_route_groups.h"
113 #include "editor_regions.h"
114 #include "editor_locations.h"
115 #include "editor_snapshots.h"
116 #include "editor_summary.h"
117 #include "region_layering_order_editor.h"
118 #include "mouse_cursors.h"
119 #include "editor_cursors.h"
124 #include "imageframe_socket_handler.h"
128 using namespace ARDOUR;
131 using namespace Glib;
132 using namespace Gtkmm2ext;
133 using namespace Editing;
135 using PBD::internationalize;
137 using Gtkmm2ext::Keyboard;
139 const double Editor::timebar_height = 15.0;
141 static const gchar *_snap_type_strings[] = {
143 N_("Timecode Frames"),
144 N_("Timecode Seconds"),
145 N_("Timecode Minutes"),
173 static const gchar *_snap_mode_strings[] = {
180 static const gchar *_edit_point_strings[] = {
187 static const gchar *_zoom_focus_strings[] = {
197 #ifdef USE_RUBBERBAND
198 static const gchar *_rb_opt_strings[] = {
201 N_("Balanced multitimbral mixture"),
202 N_("Unpitched percussion with stable notes"),
203 N_("Crisp monophonic instrumental"),
204 N_("Unpitched solo percussion"),
205 N_("Resample without preserving pitch"),
211 show_me_the_size (Requisition* r, const char* what)
213 cerr << "size of " << what << " = " << r->width << " x " << r->height << endl;
218 pane_size_watcher (Paned* pane)
220 /* if the handle of a pane vanishes into (at least) the tabs of a notebook,
221 it is no longer accessible. so stop that. this doesn't happen on X11,
222 just the quartz backend.
227 int max_width_of_lhs = GTK_WIDGET(pane->gobj())->allocation.width - 25;
229 gint pos = pane->get_position ();
231 if (pos > max_width_of_lhs) {
232 pane->set_position (max_width_of_lhs);
238 : _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
240 /* time display buttons */
241 , minsec_label (_("Mins:Secs"))
242 , bbt_label (_("Bars:Beats"))
243 , timecode_label (_("Timecode"))
244 , samples_label (_("Samples"))
245 , tempo_label (_("Tempo"))
246 , meter_label (_("Meter"))
247 , mark_label (_("Location Markers"))
248 , range_mark_label (_("Range Markers"))
249 , transport_mark_label (_("Loop/Punch Ranges"))
250 , cd_mark_label (_("CD Markers"))
251 , edit_packer (4, 4, true)
253 /* the values here don't matter: layout widgets
254 reset them as needed.
257 , vertical_adjustment (0.0, 0.0, 10.0, 400.0)
259 /* tool bar related */
261 , zoom_range_clock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, false, true)
263 , toolbar_selection_clock_table (2,3)
265 , automation_mode_button (_("mode"))
266 , global_automation_button (_("automation"))
268 , _toolbar_viewport (*manage (new Gtk::Adjustment (0, 0, 1e10)), *manage (new Gtk::Adjustment (0, 0, 1e10)))
269 , midi_panic_button (_("Panic"))
272 , image_socket_listener(0)
277 , nudge_clock (X_("nudge"), false, X_("NudgeClock"), true, false, true)
278 , meters_running(false)
279 , _pending_locate_request (false)
280 , _pending_initial_locate (false)
281 , _last_cut_copy_source_track (0)
283 , _region_selection_change_updates_region_list (true)
287 /* we are a singleton */
289 PublicEditor::_instance = this;
293 selection = new Selection (this);
294 cut_buffer = new Selection (this);
296 clicked_regionview = 0;
297 clicked_axisview = 0;
298 clicked_routeview = 0;
299 clicked_crossfadeview = 0;
300 clicked_control_point = 0;
301 last_update_frame = 0;
302 pre_press_cursor = 0;
303 _drags = new DragManager (this);
304 current_mixer_strip = 0;
305 current_bbt_points = 0;
308 snap_type_strings = I18N (_snap_type_strings);
309 snap_mode_strings = I18N (_snap_mode_strings);
310 zoom_focus_strings = I18N (_zoom_focus_strings);
311 edit_point_strings = I18N (_edit_point_strings);
312 #ifdef USE_RUBBERBAND
313 rb_opt_strings = I18N (_rb_opt_strings);
317 snap_threshold = 5.0;
318 bbt_beat_subdivision = 4;
321 last_autoscroll_x = 0;
322 last_autoscroll_y = 0;
323 autoscroll_active = false;
324 autoscroll_timeout_tag = -1;
329 current_interthread_info = 0;
330 _show_measures = true;
331 show_gain_after_trim = false;
332 verbose_cursor_on = true;
333 last_item_entered = 0;
335 have_pending_keyboard_selection = false;
336 _follow_playhead = true;
337 _stationary_playhead = false;
338 _xfade_visibility = true;
339 editor_ruler_menu = 0;
340 no_ruler_shown_update = false;
342 range_marker_menu = 0;
343 marker_menu_item = 0;
344 tempo_or_meter_marker_menu = 0;
345 transport_marker_menu = 0;
346 new_transport_marker_menu = 0;
347 editor_mixer_strip_width = Wide;
348 show_editor_mixer_when_tracks_arrive = false;
349 region_edit_menu_split_multichannel_item = 0;
350 region_edit_menu_split_item = 0;
353 current_stepping_trackview = 0;
355 entered_regionview = 0;
357 clear_entered_track = false;
360 button_release_can_deselect = true;
361 _dragging_playhead = false;
362 _dragging_edit_point = false;
363 select_new_marker = false;
365 layering_order_editor = 0;
367 no_save_visual = false;
370 scrubbing_direction = 0;
374 location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get();
375 location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get();
376 location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get();
377 location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get();
378 location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get();
380 _edit_point = EditAtMouse;
381 _internal_editing = false;
382 current_canvas_cursor = 0;
384 frames_per_unit = 2048; /* too early to use reset_zoom () */
386 _scroll_callbacks = 0;
388 zoom_focus = ZoomFocusLeft;
389 set_zoom_focus (ZoomFocusLeft);
390 zoom_range_clock.ValueChanged.connect (sigc::mem_fun(*this, &Editor::zoom_adjustment_changed));
392 bbt_label.set_name ("EditorTimeButton");
393 bbt_label.set_size_request (-1, (int)timebar_height);
394 bbt_label.set_alignment (1.0, 0.5);
395 bbt_label.set_padding (5,0);
397 bbt_label.set_no_show_all();
398 minsec_label.set_name ("EditorTimeButton");
399 minsec_label.set_size_request (-1, (int)timebar_height);
400 minsec_label.set_alignment (1.0, 0.5);
401 minsec_label.set_padding (5,0);
402 minsec_label.hide ();
403 minsec_label.set_no_show_all();
404 timecode_label.set_name ("EditorTimeButton");
405 timecode_label.set_size_request (-1, (int)timebar_height);
406 timecode_label.set_alignment (1.0, 0.5);
407 timecode_label.set_padding (5,0);
408 timecode_label.hide ();
409 timecode_label.set_no_show_all();
410 samples_label.set_name ("EditorTimeButton");
411 samples_label.set_size_request (-1, (int)timebar_height);
412 samples_label.set_alignment (1.0, 0.5);
413 samples_label.set_padding (5,0);
414 samples_label.hide ();
415 samples_label.set_no_show_all();
417 tempo_label.set_name ("EditorTimeButton");
418 tempo_label.set_size_request (-1, (int)timebar_height);
419 tempo_label.set_alignment (1.0, 0.5);
420 tempo_label.set_padding (5,0);
422 tempo_label.set_no_show_all();
424 meter_label.set_name ("EditorTimeButton");
425 meter_label.set_size_request (-1, (int)timebar_height);
426 meter_label.set_alignment (1.0, 0.5);
427 meter_label.set_padding (5,0);
429 meter_label.set_no_show_all();
431 mark_label.set_name ("EditorTimeButton");
432 mark_label.set_size_request (-1, (int)timebar_height);
433 mark_label.set_alignment (1.0, 0.5);
434 mark_label.set_padding (5,0);
436 mark_label.set_no_show_all();
438 cd_mark_label.set_name ("EditorTimeButton");
439 cd_mark_label.set_size_request (-1, (int)timebar_height);
440 cd_mark_label.set_alignment (1.0, 0.5);
441 cd_mark_label.set_padding (5,0);
442 cd_mark_label.hide();
443 cd_mark_label.set_no_show_all();
445 range_mark_label.set_name ("EditorTimeButton");
446 range_mark_label.set_size_request (-1, (int)timebar_height);
447 range_mark_label.set_alignment (1.0, 0.5);
448 range_mark_label.set_padding (5,0);
449 range_mark_label.hide();
450 range_mark_label.set_no_show_all();
452 transport_mark_label.set_name ("EditorTimeButton");
453 transport_mark_label.set_size_request (-1, (int)timebar_height);
454 transport_mark_label.set_alignment (1.0, 0.5);
455 transport_mark_label.set_padding (5,0);
456 transport_mark_label.hide();
457 transport_mark_label.set_no_show_all();
459 initialize_rulers ();
460 initialize_canvas ();
462 _summary = new EditorSummary (this);
464 selection->TimeChanged.connect (sigc::mem_fun(*this, &Editor::time_selection_changed));
465 selection->TracksChanged.connect (sigc::mem_fun(*this, &Editor::track_selection_changed));
467 editor_regions_selection_changed_connection = selection->RegionsChanged.connect (sigc::mem_fun(*this, &Editor::region_selection_changed));
469 selection->PointsChanged.connect (sigc::mem_fun(*this, &Editor::point_selection_changed));
470 selection->MarkersChanged.connect (sigc::mem_fun(*this, &Editor::marker_selection_changed));
472 edit_controls_vbox.set_spacing (0);
473 vertical_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &Editor::tie_vertical_scrolling), true);
474 track_canvas->signal_map_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_map_handler));
476 HBox* h = manage (new HBox);
477 _group_tabs = new EditorGroupTabs (this);
478 h->pack_start (*_group_tabs, PACK_SHRINK);
479 h->pack_start (edit_controls_vbox);
480 controls_layout.add (*h);
482 controls_layout.set_name ("EditControlsBase");
483 controls_layout.add_events (Gdk::SCROLL_MASK);
484 controls_layout.signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::control_layout_scroll), false);
486 controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
487 controls_layout.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_release));
489 _cursors = new MouseCursors;
491 ArdourCanvas::Canvas* time_pad = manage(new ArdourCanvas::Canvas());
492 ArdourCanvas::SimpleLine* pad_line_1 = manage(new ArdourCanvas::SimpleLine(*time_pad->root(),
493 0.0, 1.0, 100.0, 1.0));
495 pad_line_1->property_color_rgba() = 0xFF0000FF;
500 time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2);
501 time_canvas_vbox.set_size_request (-1, -1);
503 ruler_label_event_box.add (ruler_label_vbox);
504 ruler_label_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
505 ruler_label_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
507 time_button_event_box.add (time_button_vbox);
508 time_button_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
509 time_button_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
511 /* these enable us to have a dedicated window (for cursor setting, etc.)
512 for the canvas areas.
515 track_canvas_event_box.add (*track_canvas);
517 time_canvas_event_box.add (time_canvas_vbox);
518 time_canvas_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
520 edit_packer.set_col_spacings (0);
521 edit_packer.set_row_spacings (0);
522 edit_packer.set_homogeneous (false);
523 edit_packer.set_border_width (0);
524 edit_packer.set_name ("EditorWindow");
526 /* labels for the rulers */
527 edit_packer.attach (ruler_label_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0);
528 /* labels for the marker "tracks" */
529 edit_packer.attach (time_button_event_box, 1, 2, 1, 2, FILL, SHRINK, 0, 0);
531 edit_packer.attach (time_canvas_event_box, 2, 3, 0, 1, FILL|EXPAND, FILL, 0, 0);
533 edit_packer.attach (controls_layout, 0, 2, 2, 3, FILL, FILL|EXPAND, 0, 0);
535 edit_packer.attach (track_canvas_event_box, 2, 3, 1, 3, FILL|EXPAND, FILL|EXPAND, 0, 0);
537 bottom_hbox.set_border_width (2);
538 bottom_hbox.set_spacing (3);
540 _route_groups = new EditorRouteGroups (this);
541 _routes = new EditorRoutes (this);
542 _regions = new EditorRegions (this);
543 _snapshots = new EditorSnapshots (this);
544 _locations = new EditorLocations (this);
546 add_notebook_page (_("Regions"), _regions->widget ());
547 add_notebook_page (_("Tracks & Busses"), _routes->widget ());
548 add_notebook_page (_("Snapshots"), _snapshots->widget ());
549 add_notebook_page (_("Route Groups"), _route_groups->widget ());
550 add_notebook_page (_("Ranges & Marks"), _locations->widget ());
552 _the_notebook.set_show_tabs (true);
553 _the_notebook.set_scrollable (true);
554 _the_notebook.popup_disable ();
555 _the_notebook.set_tab_pos (Gtk::POS_RIGHT);
556 _the_notebook.show_all ();
558 post_maximal_editor_width = 0;
559 post_maximal_horizontal_pane_position = 0;
560 post_maximal_editor_height = 0;
561 post_maximal_vertical_pane_position = 0;
562 _notebook_shrunk = false;
564 editor_summary_pane.pack1(edit_packer);
566 Button* summary_arrows_left_left = manage (new Button);
567 summary_arrows_left_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
568 summary_arrows_left_left->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press)));
569 summary_arrows_left_left->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_release));
571 Button* summary_arrows_left_right = manage (new Button);
572 summary_arrows_left_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
573 summary_arrows_left_right->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press)));
574 summary_arrows_left_right->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_release));
576 VBox* summary_arrows_left = manage (new VBox);
577 summary_arrows_left->pack_start (*summary_arrows_left_left);
578 summary_arrows_left->pack_start (*summary_arrows_left_right);
580 Button* summary_arrows_right_left = manage (new Button);
581 summary_arrows_right_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
582 summary_arrows_right_left->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press)));
583 summary_arrows_right_left->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_release));
585 Button* summary_arrows_right_right = manage (new Button);
586 summary_arrows_right_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
587 summary_arrows_right_right->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press)));
588 summary_arrows_right_right->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_release));
590 VBox* summary_arrows_right = manage (new VBox);
591 summary_arrows_right->pack_start (*summary_arrows_right_left);
592 summary_arrows_right->pack_start (*summary_arrows_right_right);
594 Frame* summary_frame = manage (new Frame);
595 summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN);
597 summary_frame->add (*_summary);
598 summary_frame->show ();
600 _summary_hbox.pack_start (*summary_arrows_left, false, false);
601 _summary_hbox.pack_start (*summary_frame, true, true);
602 _summary_hbox.pack_start (*summary_arrows_right, false, false);
604 editor_summary_pane.pack2 (_summary_hbox);
606 edit_pane.pack1 (editor_summary_pane, true, true);
607 edit_pane.pack2 (_the_notebook, false, true);
609 editor_summary_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun (*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&editor_summary_pane)));
611 /* XXX: editor_summary_pane might need similar special OS X treatment to the edit_pane */
613 edit_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&edit_pane)));
615 Glib::PropertyProxy<int> proxy = edit_pane.property_position();
616 proxy.signal_changed().connect (bind (sigc::ptr_fun (pane_size_watcher), static_cast<Paned*> (&edit_pane)));
618 top_hbox.pack_start (toolbar_frame);
620 HBox *hbox = manage (new HBox);
621 hbox->pack_start (edit_pane, true, true);
623 global_vpacker.pack_start (top_hbox, false, false);
624 global_vpacker.pack_start (*hbox, true, true);
626 global_hpacker.pack_start (global_vpacker, true, true);
628 set_name ("EditorWindow");
629 add_accel_group (ActionManager::ui_manager->get_accel_group());
631 status_bar_hpacker.show ();
633 vpacker.pack_end (status_bar_hpacker, false, false);
634 vpacker.pack_end (global_hpacker, true, true);
636 /* register actions now so that set_state() can find them and set toggles/checks etc */
641 setup_midi_toolbar ();
643 _snap_type = SnapToBeat;
644 set_snap_to (_snap_type);
645 _snap_mode = SnapOff;
646 set_snap_mode (_snap_mode);
647 set_mouse_mode (MouseObject, true);
648 set_edit_point_preference (EditAtMouse, true);
650 _playlist_selector = new PlaylistSelector();
651 _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
653 RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), ui_bind (&Editor::catch_vanishing_regionview, this, _1), gui_context());
657 nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
658 nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
660 nudge_forward_button.set_name ("TransportButton");
661 nudge_backward_button.set_name ("TransportButton");
663 fade_context_menu.set_name ("ArdourContextMenu");
665 /* icons, titles, WM stuff */
667 list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
668 Glib::RefPtr<Gdk::Pixbuf> icon;
670 if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
671 window_icons.push_back (icon);
673 if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
674 window_icons.push_back (icon);
676 if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
677 window_icons.push_back (icon);
679 if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
680 window_icons.push_back (icon);
682 if (!window_icons.empty()) {
683 set_icon_list (window_icons);
684 set_default_icon_list (window_icons);
687 WindowTitle title(Glib::get_application_name());
688 title += _("Editor");
689 set_title (title.get_string());
690 set_wmclass (X_("ardour_editor"), PROGRAM_NAME);
693 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
695 signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
696 signal_delete_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
698 /* allow external control surfaces/protocols to do various things */
700 ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context());
701 ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context());
702 ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context());
703 ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), ui_bind (&Editor::control_scroll, this, _1), gui_context());
704 BasicUI::AccessAction.connect (*this, invalidator (*this), ui_bind (&Editor::access_action, this, _1, _2), gui_context());
706 /* problematic: has to return a value and thus cannot be x-thread */
708 Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
710 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
712 TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
714 _ignore_region_action = false;
715 _last_region_menu_was_main = false;
716 _popup_region_menu_item = 0;
718 _show_marker_lines = false;
719 _over_region_trim_target = false;
724 setup_fade_images ();
730 if(image_socket_listener) {
731 if(image_socket_listener->is_connected())
733 image_socket_listener->close_connection() ;
736 delete image_socket_listener ;
737 image_socket_listener = 0 ;
742 delete _route_groups;
748 Editor::add_toplevel_controls (Container& cont)
750 vpacker.pack_start (cont, false, false);
755 Editor::catch_vanishing_regionview (RegionView *rv)
757 /* note: the selection will take care of the vanishing
758 audioregionview by itself.
761 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
765 if (clicked_regionview == rv) {
766 clicked_regionview = 0;
769 if (entered_regionview == rv) {
770 set_entered_regionview (0);
773 if (!_all_region_actions_sensitized) {
774 sensitize_all_region_actions (true);
779 Editor::set_entered_regionview (RegionView* rv)
781 if (rv == entered_regionview) {
785 if (entered_regionview) {
786 entered_regionview->exited ();
789 if ((entered_regionview = rv) != 0) {
790 entered_regionview->entered (internal_editing ());
793 if (!_all_region_actions_sensitized && _last_region_menu_was_main) {
794 /* This RegionView entry might have changed what region actions
795 are allowed, so sensitize them all in case a key is pressed.
797 sensitize_all_region_actions (true);
802 Editor::set_entered_track (TimeAxisView* tav)
805 entered_track->exited ();
808 if ((entered_track = tav) != 0) {
809 entered_track->entered ();
814 Editor::show_window ()
816 if (!is_visible ()) {
819 /* re-hide stuff if necessary */
820 editor_list_button_toggled ();
821 parameter_changed ("show-summary");
822 parameter_changed ("show-edit-group-tabs");
823 parameter_changed ("show-zoom-tools");
825 /* now reset all audio_time_axis heights, because widgets might need
831 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
832 tv = (static_cast<TimeAxisView*>(*i));
841 Editor::instant_save ()
843 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
848 _session->add_instant_xml(get_state());
850 Config->add_instant_xml(get_state());
855 Editor::zoom_adjustment_changed ()
861 double fpu = zoom_range_clock.current_duration() / _canvas_width;
865 zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width));
866 } else if (fpu > _session->current_end_frame() / _canvas_width) {
867 fpu = _session->current_end_frame() / _canvas_width;
868 zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width));
875 Editor::control_scroll (float fraction)
877 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
883 double step = fraction * current_page_frames();
886 _control_scroll_target is an optional<T>
888 it acts like a pointer to an framepos_t, with
889 a operator conversion to boolean to check
890 that it has a value could possibly use
891 playhead_cursor->current_frame to store the
892 value and a boolean in the class to know
893 when it's out of date
896 if (!_control_scroll_target) {
897 _control_scroll_target = _session->transport_frame();
898 _dragging_playhead = true;
901 if ((fraction < 0.0f) && (*_control_scroll_target < (framepos_t) fabs(step))) {
902 *_control_scroll_target = 0;
903 } else if ((fraction > 0.0f) && (max_framepos - *_control_scroll_target < step)) {
904 *_control_scroll_target = max_framepos - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
906 *_control_scroll_target += (framepos_t) floor (step);
909 /* move visuals, we'll catch up with it later */
911 playhead_cursor->set_position (*_control_scroll_target);
912 UpdateAllTransportClocks (*_control_scroll_target);
914 if (*_control_scroll_target > (current_page_frames() / 2)) {
915 /* try to center PH in window */
916 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
922 Now we do a timeout to actually bring the session to the right place
923 according to the playhead. This is to avoid reading disk buffers on every
924 call to control_scroll, which is driven by ScrollTimeline and therefore
925 probably by a control surface wheel which can generate lots of events.
927 /* cancel the existing timeout */
929 control_scroll_connection.disconnect ();
931 /* add the next timeout */
933 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
937 Editor::deferred_control_scroll (framepos_t /*target*/)
939 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
940 // reset for next stream
941 _control_scroll_target = boost::none;
942 _dragging_playhead = false;
947 Editor::access_action (std::string action_group, std::string action_item)
953 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
956 act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
964 Editor::on_realize ()
966 Window::on_realize ();
971 Editor::map_position_change (framepos_t frame)
973 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, frame)
979 if (_follow_playhead) {
980 center_screen (frame);
983 playhead_cursor->set_position (frame);
987 Editor::center_screen (framepos_t frame)
989 double page = _canvas_width * frames_per_unit;
991 /* if we're off the page, then scroll.
994 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
995 center_screen_internal (frame, page);
1000 Editor::center_screen_internal (framepos_t frame, float page)
1005 frame -= (framepos_t) page;
1010 reset_x_origin (frame);
1015 Editor::update_title ()
1017 ENSURE_GUI_THREAD (*this, &Editor::update_title)
1020 bool dirty = _session->dirty();
1022 string session_name;
1024 if (_session->snap_name() != _session->name()) {
1025 session_name = _session->snap_name();
1027 session_name = _session->name();
1031 session_name = "*" + session_name;
1034 WindowTitle title(session_name);
1035 title += Glib::get_application_name();
1036 set_title (title.get_string());
1041 Editor::set_session (Session *t)
1043 SessionHandlePtr::set_session (t);
1049 zoom_range_clock.set_session (_session);
1050 _playlist_selector->set_session (_session);
1051 nudge_clock.set_session (_session);
1052 _summary->set_session (_session);
1053 _group_tabs->set_session (_session);
1054 _route_groups->set_session (_session);
1055 _regions->set_session (_session);
1056 _snapshots->set_session (_session);
1057 _routes->set_session (_session);
1058 _locations->set_session (_session);
1060 if (rhythm_ferret) {
1061 rhythm_ferret->set_session (_session);
1064 if (analysis_window) {
1065 analysis_window->set_session (_session);
1069 sfbrowser->set_session (_session);
1072 compute_fixed_ruler_scale ();
1074 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1075 set_state (*node, Stateful::loading_state_version);
1077 /* catch up with the playhead */
1079 _session->request_locate (playhead_cursor->current_frame);
1080 _pending_initial_locate = true;
1084 /* These signals can all be emitted by a non-GUI thread. Therefore the
1085 handlers for them must not attempt to directly interact with the GUI,
1086 but use Gtkmm2ext::UI::instance()->call_slot();
1089 _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), ui_bind(&Editor::step_edit_status_change, this, _1), gui_context());
1090 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1091 _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context());
1092 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context());
1093 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1094 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context());
1095 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1096 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
1097 _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Editor::session_state_saved, this, _1), gui_context());
1098 _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context());
1099 _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&Editor::location_gone, this, _1), gui_context());
1100 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1101 _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display_s, this, _1), gui_context());
1102 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1104 if (Profile->get_sae()) {
1105 Timecode::BBT_Time bbt;
1109 framepos_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1);
1110 nudge_clock.set_mode(AudioClock::BBT);
1111 nudge_clock.set (pos, true, 0, AudioClock::BBT);
1114 nudge_clock.set (_session->frame_rate() * 5, true, 0, AudioClock::Timecode); // default of 5 seconds
1117 playhead_cursor->canvas_item.show ();
1119 Location* loc = _session->locations()->auto_loop_location();
1121 loc = new Location (*_session, 0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1123 if (loc->start() == loc->end()) {
1124 loc->set_end (loc->start() + 1);
1127 _session->locations()->add (loc, false);
1128 _session->set_auto_loop_location (loc);
1131 loc->set_name (_("Loop"));
1134 loc = _session->locations()->auto_punch_location();
1137 loc = new Location (*_session, 0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1139 if (loc->start() == loc->end()) {
1140 loc->set_end (loc->start() + 1);
1143 _session->locations()->add (loc, false);
1144 _session->set_auto_punch_location (loc);
1147 loc->set_name (_("Punch"));
1150 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1151 Config->map_parameters (pc);
1152 _session->config.map_parameters (pc);
1154 refresh_location_display ();
1156 restore_ruler_visibility ();
1157 //tempo_map_changed (PropertyChange (0));
1158 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1160 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1161 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1164 super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
1165 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1168 switch (_snap_type) {
1169 case SnapToRegionStart:
1170 case SnapToRegionEnd:
1171 case SnapToRegionSync:
1172 case SnapToRegionBoundary:
1173 build_region_boundary_cache ();
1180 /* register for undo history */
1181 _session->register_with_memento_command_factory(_id, this);
1183 ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated));
1185 start_updating_meters ();
1189 Editor::action_pre_activated (Glib::RefPtr<Action> const & a)
1191 if (a->get_name() == "RegionMenu") {
1192 /* When the main menu's region menu is opened, we setup the actions so that they look right
1193 in the menu. I can't find a way of getting a signal when this menu is subsequently closed,
1194 so we resensitize all region actions when the entered regionview or the region selection
1195 changes. HOWEVER we can't always resensitize on entered_regionview change because that
1196 happens after the region context menu is opened. So we set a flag here, too.
1200 sensitize_the_right_region_actions ();
1201 _last_region_menu_was_main = true;
1205 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1207 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1209 using namespace Menu_Helpers;
1210 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1213 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1217 MenuList& items (fade_context_menu.items());
1221 switch (item_type) {
1223 case FadeInHandleItem:
1224 if (arv->audio_region()->fade_in_active()) {
1225 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1227 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1230 items.push_back (SeparatorElem());
1232 if (Profile->get_sae()) {
1234 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)));
1235 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)));
1242 *_fade_in_images[FadeLinear],
1243 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)
1247 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1252 *_fade_in_images[FadeFast],
1253 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)
1256 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1261 *_fade_in_images[FadeLogB],
1262 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogB)
1265 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1270 *_fade_in_images[FadeLogA],
1271 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogA)
1274 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1279 *_fade_in_images[FadeSlow],
1280 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow)
1283 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1289 case FadeOutHandleItem:
1290 if (arv->audio_region()->fade_out_active()) {
1291 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1293 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1296 items.push_back (SeparatorElem());
1298 if (Profile->get_sae()) {
1299 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)));
1300 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)));
1306 *_fade_out_images[FadeLinear],
1307 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)
1311 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1316 *_fade_out_images[FadeFast],
1317 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)
1320 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1325 *_fade_out_images[FadeLogB],
1326 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogA)
1329 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1334 *_fade_out_images[FadeLogA],
1335 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogB)
1338 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1343 *_fade_out_images[FadeSlow],
1344 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast)
1347 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1353 fatal << _("programming error: ")
1354 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1359 fade_context_menu.popup (button, time);
1363 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1365 using namespace Menu_Helpers;
1366 Menu* (Editor::*build_menu_function)();
1369 switch (item_type) {
1371 case RegionViewName:
1372 case RegionViewNameHighlight:
1373 case LeftFrameHandle:
1374 case RightFrameHandle:
1375 if (with_selection) {
1376 build_menu_function = &Editor::build_track_selection_context_menu;
1378 build_menu_function = &Editor::build_track_region_context_menu;
1383 if (with_selection) {
1384 build_menu_function = &Editor::build_track_selection_context_menu;
1386 build_menu_function = &Editor::build_track_context_menu;
1390 case CrossfadeViewItem:
1391 build_menu_function = &Editor::build_track_crossfade_context_menu;
1395 if (clicked_routeview->track()) {
1396 build_menu_function = &Editor::build_track_context_menu;
1398 build_menu_function = &Editor::build_track_bus_context_menu;
1403 /* probably shouldn't happen but if it does, we don't care */
1407 menu = (this->*build_menu_function)();
1408 menu->set_name ("ArdourContextMenu");
1410 /* now handle specific situations */
1412 switch (item_type) {
1414 case RegionViewName:
1415 case RegionViewNameHighlight:
1416 case LeftFrameHandle:
1417 case RightFrameHandle:
1418 if (!with_selection) {
1419 if (region_edit_menu_split_item) {
1420 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1421 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1423 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1426 if (region_edit_menu_split_multichannel_item) {
1427 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1428 region_edit_menu_split_multichannel_item->set_sensitive (true);
1430 region_edit_menu_split_multichannel_item->set_sensitive (false);
1439 case CrossfadeViewItem:
1446 /* probably shouldn't happen but if it does, we don't care */
1450 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1452 /* Bounce to disk */
1454 using namespace Menu_Helpers;
1455 MenuList& edit_items = menu->items();
1457 edit_items.push_back (SeparatorElem());
1459 switch (clicked_routeview->audio_track()->freeze_state()) {
1460 case AudioTrack::NoFreeze:
1461 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1464 case AudioTrack::Frozen:
1465 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1468 case AudioTrack::UnFrozen:
1469 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1475 if (item_type == StreamItem && clicked_routeview) {
1476 clicked_routeview->build_underlay_menu(menu);
1479 /* When the region menu is opened, we setup the actions so that they look right
1482 sensitize_the_right_region_actions ();
1483 _last_region_menu_was_main = false;
1485 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1486 menu->popup (button, time);
1490 Editor::build_track_context_menu ()
1492 using namespace Menu_Helpers;
1494 MenuList& edit_items = track_context_menu.items();
1497 add_dstream_context_items (edit_items);
1498 return &track_context_menu;
1502 Editor::build_track_bus_context_menu ()
1504 using namespace Menu_Helpers;
1506 MenuList& edit_items = track_context_menu.items();
1509 add_bus_context_items (edit_items);
1510 return &track_context_menu;
1514 Editor::build_track_region_context_menu ()
1516 using namespace Menu_Helpers;
1517 MenuList& edit_items = track_region_context_menu.items();
1520 /* we've just cleared the track region context menu, so the menu that these
1521 two items were on will have disappeared; stop them dangling.
1523 region_edit_menu_split_item = 0;
1524 region_edit_menu_split_multichannel_item = 0;
1526 /* we might try to use items that are currently attached to a crossfade menu,
1529 track_crossfade_context_menu.items().clear ();
1531 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1534 boost::shared_ptr<Track> tr;
1535 boost::shared_ptr<Playlist> pl;
1537 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
1538 framepos_t const framepos = (framepos_t) floor ((double) get_preferred_edit_position() * tr->speed());
1539 uint32_t regions_at = pl->count_regions_at (framepos);
1540 add_region_context_items (edit_items, regions_at > 1);
1544 add_dstream_context_items (edit_items);
1546 return &track_region_context_menu;
1550 Editor::build_track_crossfade_context_menu ()
1552 using namespace Menu_Helpers;
1553 MenuList& edit_items = track_crossfade_context_menu.items();
1554 edit_items.clear ();
1556 /* we might try to use items that are currently attached to a crossfade menu,
1559 track_region_context_menu.items().clear ();
1561 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1564 boost::shared_ptr<Track> tr;
1565 boost::shared_ptr<Playlist> pl;
1566 boost::shared_ptr<AudioPlaylist> apl;
1568 if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1570 AudioPlaylist::Crossfades xfades;
1572 apl->crossfades_at (get_preferred_edit_position (), xfades);
1574 bool many = xfades.size() > 1;
1576 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1577 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1580 framepos_t framepos = (framepos_t) floor ((double) get_preferred_edit_position() * tr->speed());
1581 uint32_t regions_at = pl->count_regions_at (framepos);
1582 add_region_context_items (edit_items, regions_at > 1);
1586 add_dstream_context_items (edit_items);
1588 return &track_crossfade_context_menu;
1592 Editor::analyze_region_selection ()
1594 if (analysis_window == 0) {
1595 analysis_window = new AnalysisWindow();
1598 analysis_window->set_session(_session);
1600 analysis_window->show_all();
1603 analysis_window->set_regionmode();
1604 analysis_window->analyze();
1606 analysis_window->present();
1610 Editor::analyze_range_selection()
1612 if (analysis_window == 0) {
1613 analysis_window = new AnalysisWindow();
1616 analysis_window->set_session(_session);
1618 analysis_window->show_all();
1621 analysis_window->set_rangemode();
1622 analysis_window->analyze();
1624 analysis_window->present();
1628 Editor::build_track_selection_context_menu ()
1630 using namespace Menu_Helpers;
1631 MenuList& edit_items = track_selection_context_menu.items();
1632 edit_items.clear ();
1634 add_selection_context_items (edit_items);
1635 // edit_items.push_back (SeparatorElem());
1636 // add_dstream_context_items (edit_items);
1638 return &track_selection_context_menu;
1641 /** Add context menu items relevant to crossfades.
1642 * @param edit_items List to add the items to.
1645 Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1647 using namespace Menu_Helpers;
1648 Menu *xfade_menu = manage (new Menu);
1649 MenuList& items = xfade_menu->items();
1650 xfade_menu->set_name ("ArdourContextMenu");
1653 if (xfade->active()) {
1659 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1660 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1662 if (xfade->can_follow_overlap()) {
1664 if (xfade->following_overlap()) {
1665 str = _("Convert to Short");
1667 str = _("Convert to Full");
1670 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1674 str = xfade->out()->name();
1676 str += xfade->in()->name();
1678 str = _("Crossfade");
1681 edit_items.push_back (MenuElem (str, *xfade_menu));
1682 edit_items.push_back (SeparatorElem());
1686 Editor::xfade_edit_left_region ()
1688 if (clicked_crossfadeview) {
1689 clicked_crossfadeview->left_view.show_region_editor ();
1694 Editor::xfade_edit_right_region ()
1696 if (clicked_crossfadeview) {
1697 clicked_crossfadeview->right_view.show_region_editor ();
1702 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, bool multiple_regions_at_position)
1704 using namespace Menu_Helpers;
1706 /* OK, stick the region submenu at the top of the list, and then add
1710 /* we have to hack up the region name because "_" has a special
1711 meaning for menu titles.
1714 RegionSelection rs = get_regions_from_selection_and_entered ();
1716 string::size_type pos = 0;
1717 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1719 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1720 menu_item_name.replace (pos, 1, "__");
1724 if (_popup_region_menu_item == 0) {
1725 _popup_region_menu_item = manage (new MenuItem (menu_item_name));
1726 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1727 _popup_region_menu_item->show ();
1729 _popup_region_menu_item->set_label (menu_item_name);
1732 edit_items.push_back (*_popup_region_menu_item);
1733 if (multiple_regions_at_position && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1734 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region")->create_menu_item ()));
1736 edit_items.push_back (SeparatorElem());
1739 /** Add context menu items relevant to selection ranges.
1740 * @param edit_items List to add the items to.
1743 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1745 using namespace Menu_Helpers;
1747 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1748 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1750 edit_items.push_back (SeparatorElem());
1751 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1753 if (!selection->regions.empty()) {
1754 edit_items.push_back (SeparatorElem());
1755 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)));
1756 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)));
1759 edit_items.push_back (SeparatorElem());
1760 edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection)));
1761 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1763 edit_items.push_back (SeparatorElem());
1764 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1766 edit_items.push_back (SeparatorElem());
1767 edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1768 edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1770 edit_items.push_back (SeparatorElem());
1771 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1773 edit_items.push_back (SeparatorElem());
1774 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1775 edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1776 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1778 edit_items.push_back (SeparatorElem());
1779 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1780 edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1781 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1782 edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1783 edit_items.push_back (MenuElem (_("Export Range"), sigc::mem_fun(*this, &Editor::export_selection)));
1788 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1790 using namespace Menu_Helpers;
1794 Menu *play_menu = manage (new Menu);
1795 MenuList& play_items = play_menu->items();
1796 play_menu->set_name ("ArdourContextMenu");
1798 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1799 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1800 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1801 play_items.push_back (SeparatorElem());
1802 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
1804 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1808 Menu *select_menu = manage (new Menu);
1809 MenuList& select_items = select_menu->items();
1810 select_menu->set_name ("ArdourContextMenu");
1812 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1813 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1814 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1815 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1816 select_items.push_back (SeparatorElem());
1817 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
1818 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
1819 select_items.push_back (SeparatorElem());
1820 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1821 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1822 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1823 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1824 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
1825 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
1826 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
1828 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1832 Menu *cutnpaste_menu = manage (new Menu);
1833 MenuList& cutnpaste_items = cutnpaste_menu->items();
1834 cutnpaste_menu->set_name ("ArdourContextMenu");
1836 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1837 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1838 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1840 cutnpaste_items.push_back (SeparatorElem());
1842 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
1843 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
1845 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
1847 /* Adding new material */
1849 edit_items.push_back (SeparatorElem());
1850 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
1851 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
1855 Menu *nudge_menu = manage (new Menu());
1856 MenuList& nudge_items = nudge_menu->items();
1857 nudge_menu->set_name ("ArdourContextMenu");
1859 edit_items.push_back (SeparatorElem());
1860 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1861 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1862 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1863 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1865 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1869 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
1871 using namespace Menu_Helpers;
1875 Menu *play_menu = manage (new Menu);
1876 MenuList& play_items = play_menu->items();
1877 play_menu->set_name ("ArdourContextMenu");
1879 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1880 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1881 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1885 Menu *select_menu = manage (new Menu);
1886 MenuList& select_items = select_menu->items();
1887 select_menu->set_name ("ArdourContextMenu");
1889 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1890 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1891 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1892 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1893 select_items.push_back (SeparatorElem());
1894 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1895 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1896 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1897 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1899 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1903 Menu *cutnpaste_menu = manage (new Menu);
1904 MenuList& cutnpaste_items = cutnpaste_menu->items();
1905 cutnpaste_menu->set_name ("ArdourContextMenu");
1907 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1908 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1909 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1911 Menu *nudge_menu = manage (new Menu());
1912 MenuList& nudge_items = nudge_menu->items();
1913 nudge_menu->set_name ("ArdourContextMenu");
1915 edit_items.push_back (SeparatorElem());
1916 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1917 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1918 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1919 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1921 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1925 Editor::snap_type() const
1931 Editor::snap_mode() const
1937 Editor::set_snap_to (SnapType st)
1939 unsigned int snap_ind = (unsigned int)st;
1943 if (snap_ind > snap_type_strings.size() - 1) {
1945 _snap_type = (SnapType)snap_ind;
1948 string str = snap_type_strings[snap_ind];
1950 if (str != snap_type_selector.get_active_text()) {
1951 snap_type_selector.set_active_text (str);
1956 switch (_snap_type) {
1957 case SnapToBeatDiv32:
1958 case SnapToBeatDiv28:
1959 case SnapToBeatDiv24:
1960 case SnapToBeatDiv20:
1961 case SnapToBeatDiv16:
1962 case SnapToBeatDiv14:
1963 case SnapToBeatDiv12:
1964 case SnapToBeatDiv10:
1965 case SnapToBeatDiv8:
1966 case SnapToBeatDiv7:
1967 case SnapToBeatDiv6:
1968 case SnapToBeatDiv5:
1969 case SnapToBeatDiv4:
1970 case SnapToBeatDiv3:
1971 case SnapToBeatDiv2:
1972 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
1973 update_tempo_based_rulers ();
1976 case SnapToRegionStart:
1977 case SnapToRegionEnd:
1978 case SnapToRegionSync:
1979 case SnapToRegionBoundary:
1980 build_region_boundary_cache ();
1988 SnapChanged (); /* EMIT SIGNAL */
1992 Editor::set_snap_mode (SnapMode mode)
1995 string str = snap_mode_strings[(int)mode];
1997 if (str != snap_mode_selector.get_active_text ()) {
1998 snap_mode_selector.set_active_text (str);
2004 Editor::set_edit_point_preference (EditPoint ep, bool force)
2006 bool changed = (_edit_point != ep);
2009 string str = edit_point_strings[(int)ep];
2011 if (str != edit_point_selector.get_active_text ()) {
2012 edit_point_selector.set_active_text (str);
2015 set_canvas_cursor ();
2017 if (!force && !changed) {
2021 const char* action=NULL;
2023 switch (_edit_point) {
2024 case EditAtPlayhead:
2025 action = "edit-at-playhead";
2027 case EditAtSelectedMarker:
2028 action = "edit-at-marker";
2031 action = "edit-at-mouse";
2035 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2037 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2041 bool in_track_canvas;
2043 if (!mouse_frame (foo, in_track_canvas)) {
2044 in_track_canvas = false;
2047 reset_canvas_action_sensitivity (in_track_canvas);
2053 Editor::set_state (const XMLNode& node, int /*version*/)
2055 const XMLProperty* prop;
2057 int x, y, xoff, yoff;
2060 if ((prop = node.property ("id")) != 0) {
2061 _id = prop->value ();
2064 g.base_width = default_width;
2065 g.base_height = default_height;
2071 if ((geometry = find_named_node (node, "geometry")) != 0) {
2075 if ((prop = geometry->property("x_size")) == 0) {
2076 prop = geometry->property ("x-size");
2079 g.base_width = atoi(prop->value());
2081 if ((prop = geometry->property("y_size")) == 0) {
2082 prop = geometry->property ("y-size");
2085 g.base_height = atoi(prop->value());
2088 if ((prop = geometry->property ("x_pos")) == 0) {
2089 prop = geometry->property ("x-pos");
2092 x = atoi (prop->value());
2095 if ((prop = geometry->property ("y_pos")) == 0) {
2096 prop = geometry->property ("y-pos");
2099 y = atoi (prop->value());
2102 if ((prop = geometry->property ("x_off")) == 0) {
2103 prop = geometry->property ("x-off");
2106 xoff = atoi (prop->value());
2108 if ((prop = geometry->property ("y_off")) == 0) {
2109 prop = geometry->property ("y-off");
2112 yoff = atoi (prop->value());
2116 //set_default_size (g.base_width, g.base_height);
2119 if (_session && (prop = node.property ("playhead"))) {
2121 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2122 playhead_cursor->set_position (pos);
2124 playhead_cursor->set_position (0);
2127 if ((prop = node.property ("mixer-width"))) {
2128 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2131 if ((prop = node.property ("zoom-focus"))) {
2132 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2135 if ((prop = node.property ("zoom"))) {
2136 reset_zoom (PBD::atof (prop->value()));
2138 reset_zoom (frames_per_unit);
2141 if ((prop = node.property ("snap-to"))) {
2142 set_snap_to ((SnapType) atoi (prop->value()));
2145 if ((prop = node.property ("snap-mode"))) {
2146 set_snap_mode ((SnapMode) atoi (prop->value()));
2149 if ((prop = node.property ("mouse-mode"))) {
2150 MouseMode m = str2mousemode(prop->value());
2151 set_mouse_mode (m, true);
2153 set_mouse_mode (MouseObject, true);
2156 if ((prop = node.property ("left-frame")) != 0) {
2158 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2159 reset_x_origin (pos);
2163 if ((prop = node.property ("y-origin")) != 0) {
2164 reset_y_origin (atof (prop->value ()));
2167 if ((prop = node.property ("internal-edit"))) {
2168 bool yn = string_is_affirmative (prop->value());
2169 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2171 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2172 tact->set_active (!yn);
2173 tact->set_active (yn);
2177 if ((prop = node.property ("join-object-range"))) {
2178 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2181 if ((prop = node.property ("edit-point"))) {
2182 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2185 if ((prop = node.property ("show-measures"))) {
2186 bool yn = string_is_affirmative (prop->value());
2187 _show_measures = yn;
2188 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2190 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2191 /* do it twice to force the change */
2192 tact->set_active (!yn);
2193 tact->set_active (yn);
2197 if ((prop = node.property ("follow-playhead"))) {
2198 bool yn = string_is_affirmative (prop->value());
2199 set_follow_playhead (yn);
2200 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2202 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2203 if (tact->get_active() != yn) {
2204 tact->set_active (yn);
2209 if ((prop = node.property ("stationary-playhead"))) {
2210 bool yn = (prop->value() == "yes");
2211 set_stationary_playhead (yn);
2212 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2214 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2215 if (tact->get_active() != yn) {
2216 tact->set_active (yn);
2221 if ((prop = node.property ("region-list-sort-type"))) {
2222 RegionListSortType st;
2223 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2226 if ((prop = node.property ("xfades-visible"))) {
2227 bool yn = string_is_affirmative (prop->value());
2228 _xfade_visibility = !yn;
2229 // set_xfade_visibility (yn);
2232 if ((prop = node.property ("show-editor-mixer"))) {
2234 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2237 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2238 bool yn = string_is_affirmative (prop->value());
2240 /* do it twice to force the change */
2242 tact->set_active (!yn);
2243 tact->set_active (yn);
2246 if ((prop = node.property ("show-editor-list"))) {
2248 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2251 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2252 bool yn = string_is_affirmative (prop->value());
2254 /* do it twice to force the change */
2256 tact->set_active (!yn);
2257 tact->set_active (yn);
2260 if ((prop = node.property (X_("editor-list-page")))) {
2261 _the_notebook.set_current_page (atoi (prop->value ()));
2264 if ((prop = node.property (X_("show-marker-lines")))) {
2265 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2267 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2268 bool yn = string_is_affirmative (prop->value ());
2270 tact->set_active (!yn);
2271 tact->set_active (yn);
2274 XMLNodeList children = node.children ();
2275 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2276 selection->set_state (**i, Stateful::current_state_version);
2277 _regions->set_state (**i);
2284 Editor::get_state ()
2286 XMLNode* node = new XMLNode ("Editor");
2289 _id.print (buf, sizeof (buf));
2290 node->add_property ("id", buf);
2292 if (is_realized()) {
2293 Glib::RefPtr<Gdk::Window> win = get_window();
2295 int x, y, xoff, yoff, width, height;
2296 win->get_root_origin(x, y);
2297 win->get_position(xoff, yoff);
2298 win->get_size(width, height);
2300 XMLNode* geometry = new XMLNode ("geometry");
2302 snprintf(buf, sizeof(buf), "%d", width);
2303 geometry->add_property("x-size", string(buf));
2304 snprintf(buf, sizeof(buf), "%d", height);
2305 geometry->add_property("y-size", string(buf));
2306 snprintf(buf, sizeof(buf), "%d", x);
2307 geometry->add_property("x-pos", string(buf));
2308 snprintf(buf, sizeof(buf), "%d", y);
2309 geometry->add_property("y-pos", string(buf));
2310 snprintf(buf, sizeof(buf), "%d", xoff);
2311 geometry->add_property("x-off", string(buf));
2312 snprintf(buf, sizeof(buf), "%d", yoff);
2313 geometry->add_property("y-off", string(buf));
2314 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2315 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2316 geometry->add_property("notebook-shrunk", _notebook_shrunk ? "1" : "0");
2317 snprintf(buf,sizeof(buf), "%d",pre_maximal_horizontal_pane_position);
2318 geometry->add_property("pre-maximal-horizontal-pane-position", string(buf));
2319 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2320 geometry->add_property("edit-vertical-pane-pos", string(buf));
2322 node->add_child_nocopy (*geometry);
2325 maybe_add_mixer_strip_width (*node);
2327 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2328 node->add_property ("zoom-focus", buf);
2329 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2330 node->add_property ("zoom", buf);
2331 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2332 node->add_property ("snap-to", buf);
2333 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2334 node->add_property ("snap-mode", buf);
2336 node->add_property ("edit-point", enum_2_string (_edit_point));
2338 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2339 node->add_property ("playhead", buf);
2340 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2341 node->add_property ("left-frame", buf);
2342 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2343 node->add_property ("y-origin", buf);
2345 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2346 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2347 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2348 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2349 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2350 node->add_property ("mouse-mode", enum2str(mouse_mode));
2351 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2352 node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2354 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2356 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2357 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2360 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2362 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2363 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2366 snprintf (buf, sizeof (buf), "%d", _the_notebook.get_current_page ());
2367 node->add_property (X_("editor-list-page"), buf);
2369 node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no");
2371 node->add_child_nocopy (selection->get_state ());
2372 node->add_child_nocopy (_regions->get_state ());
2379 /** @param y y offset from the top of all trackviews.
2380 * @return pair: TimeAxisView that y is over, layer index.
2381 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2382 * in stacked region display mode, otherwise 0.
2384 std::pair<TimeAxisView *, layer_t>
2385 Editor::trackview_by_y_position (double y)
2387 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2389 std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
2395 return std::make_pair ( (TimeAxisView *) 0, 0);
2398 /** Snap a position to the grid, if appropriate, taking into account current
2399 * grid settings and also the state of any snap modifier keys that may be pressed.
2400 * @param start Position to snap.
2401 * @param event Event to get current key modifier information from, or 0.
2404 Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2406 if (!_session || !event) {
2410 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2411 if (_snap_mode == SnapOff) {
2412 snap_to_internal (start, direction, for_mark);
2415 if (_snap_mode != SnapOff) {
2416 snap_to_internal (start, direction, for_mark);
2422 Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark)
2424 if (!_session || _snap_mode == SnapOff) {
2428 snap_to_internal (start, direction, for_mark);
2432 Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /*for_mark*/)
2434 const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2435 framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2437 switch (_snap_type) {
2438 case SnapToTimecodeFrame:
2439 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2440 start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2442 start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2446 case SnapToTimecodeSeconds:
2447 if (_session->config.get_timecode_offset_negative()) {
2448 start += _session->config.get_timecode_offset ();
2450 start -= _session->config.get_timecode_offset ();
2452 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2453 start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2455 start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2458 if (_session->config.get_timecode_offset_negative()) {
2459 start -= _session->config.get_timecode_offset ();
2461 start += _session->config.get_timecode_offset ();
2465 case SnapToTimecodeMinutes:
2466 if (_session->config.get_timecode_offset_negative()) {
2467 start += _session->config.get_timecode_offset ();
2469 start -= _session->config.get_timecode_offset ();
2471 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2472 start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2474 start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2476 if (_session->config.get_timecode_offset_negative()) {
2477 start -= _session->config.get_timecode_offset ();
2479 start += _session->config.get_timecode_offset ();
2483 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2489 Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark)
2491 const framepos_t one_second = _session->frame_rate();
2492 const framepos_t one_minute = _session->frame_rate() * 60;
2493 framepos_t presnap = start;
2497 switch (_snap_type) {
2498 case SnapToTimecodeFrame:
2499 case SnapToTimecodeSeconds:
2500 case SnapToTimecodeMinutes:
2501 return timecode_snap_to_internal (start, direction, for_mark);
2504 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2505 start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2507 start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2512 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2513 start = (framepos_t) ceil ((double) start / one_second) * one_second;
2515 start = (framepos_t) floor ((double) start / one_second) * one_second;
2520 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2521 start = (framepos_t) ceil ((double) start / one_minute) * one_minute;
2523 start = (framepos_t) floor ((double) start / one_minute) * one_minute;
2528 start = _session->tempo_map().round_to_bar (start, direction);
2532 start = _session->tempo_map().round_to_beat (start, direction);
2535 case SnapToBeatDiv32:
2536 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2538 case SnapToBeatDiv28:
2539 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2541 case SnapToBeatDiv24:
2542 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2544 case SnapToBeatDiv20:
2545 start = _session->tempo_map().round_to_beat_subdivision (start, 20, direction);
2547 case SnapToBeatDiv16:
2548 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2550 case SnapToBeatDiv14:
2551 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2553 case SnapToBeatDiv12:
2554 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2556 case SnapToBeatDiv10:
2557 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2559 case SnapToBeatDiv8:
2560 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2562 case SnapToBeatDiv7:
2563 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2565 case SnapToBeatDiv6:
2566 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2568 case SnapToBeatDiv5:
2569 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2571 case SnapToBeatDiv4:
2572 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2574 case SnapToBeatDiv3:
2575 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2577 case SnapToBeatDiv2:
2578 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2586 _session->locations()->marks_either_side (start, before, after);
2588 if (before == max_framepos) {
2590 } else if (after == max_framepos) {
2592 } else if (before != max_framepos && after != max_framepos) {
2593 /* have before and after */
2594 if ((start - before) < (after - start)) {
2603 case SnapToRegionStart:
2604 case SnapToRegionEnd:
2605 case SnapToRegionSync:
2606 case SnapToRegionBoundary:
2607 if (!region_boundary_cache.empty()) {
2609 vector<framepos_t>::iterator prev = region_boundary_cache.end ();
2610 vector<framepos_t>::iterator next = region_boundary_cache.end ();
2612 if (direction > 0) {
2613 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2615 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2618 if (next != region_boundary_cache.begin ()) {
2623 framepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2624 framepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2626 if (start > (p + n) / 2) {
2635 switch (_snap_mode) {
2641 if (presnap > start) {
2642 if (presnap > (start + unit_to_frame(snap_threshold))) {
2646 } else if (presnap < start) {
2647 if (presnap < (start - unit_to_frame(snap_threshold))) {
2653 /* handled at entry */
2661 Editor::setup_toolbar ()
2665 /* Mode Buttons (tool selection) */
2667 mouse_move_button.set_relief(Gtk::RELIEF_NONE);
2668 mouse_select_button.set_relief(Gtk::RELIEF_NONE);
2669 mouse_gain_button.set_relief(Gtk::RELIEF_NONE);
2670 mouse_zoom_button.set_relief(Gtk::RELIEF_NONE);
2671 mouse_timefx_button.set_relief(Gtk::RELIEF_NONE);
2672 mouse_audition_button.set_relief(Gtk::RELIEF_NONE);
2673 // internal_edit_button.set_relief(Gtk::RELIEF_NONE);
2674 join_object_range_button.set_relief(Gtk::RELIEF_NONE);
2676 HBox* mode_box = manage(new HBox);
2677 mode_box->set_border_width (2);
2678 mode_box->set_spacing(4);
2680 /* table containing mode buttons */
2682 HBox* mouse_mode_button_box = manage (new HBox ());
2684 if (Profile->get_sae()) {
2685 mouse_mode_button_box->pack_start (mouse_move_button);
2687 mouse_mode_button_box->pack_start (mouse_move_button);
2688 mouse_mode_button_box->pack_start (join_object_range_button);
2689 mouse_mode_button_box->pack_start (mouse_select_button);
2692 mouse_mode_button_box->pack_start (mouse_zoom_button);
2694 if (!Profile->get_sae()) {
2695 mouse_mode_button_box->pack_start (mouse_gain_button);
2698 mouse_mode_button_box->pack_start (mouse_timefx_button);
2699 mouse_mode_button_box->pack_start (mouse_audition_button);
2700 mouse_mode_button_box->pack_start (internal_edit_button);
2702 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2703 if (!Profile->get_sae()) {
2704 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2706 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2708 edit_mode_selector.set_name ("EditModeSelector");
2709 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2710 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2712 mode_box->pack_start (edit_mode_selector);
2713 mode_box->pack_start (*mouse_mode_button_box);
2715 _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2716 _mouse_mode_tearoff->set_name ("MouseModeBase");
2717 _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2719 if (Profile->get_sae()) {
2720 _mouse_mode_tearoff->set_can_be_torn_off (false);
2723 _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2724 &_mouse_mode_tearoff->tearoff_window()));
2725 _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2726 &_mouse_mode_tearoff->tearoff_window(), 1));
2727 _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2728 &_mouse_mode_tearoff->tearoff_window()));
2729 _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2730 &_mouse_mode_tearoff->tearoff_window(), 1));
2732 mouse_move_button.set_mode (false);
2733 mouse_select_button.set_mode (false);
2734 mouse_gain_button.set_mode (false);
2735 mouse_zoom_button.set_mode (false);
2736 mouse_timefx_button.set_mode (false);
2737 mouse_audition_button.set_mode (false);
2738 join_object_range_button.set_mode (false);
2740 mouse_move_button.set_name ("MouseModeButton");
2741 mouse_select_button.set_name ("MouseModeButton");
2742 mouse_gain_button.set_name ("MouseModeButton");
2743 mouse_zoom_button.set_name ("MouseModeButton");
2744 mouse_timefx_button.set_name ("MouseModeButton");
2745 mouse_audition_button.set_name ("MouseModeButton");
2746 internal_edit_button.set_name ("MouseModeButton");
2747 join_object_range_button.set_name ("MouseModeButton");
2749 mouse_move_button.unset_flags (CAN_FOCUS);
2750 mouse_select_button.unset_flags (CAN_FOCUS);
2751 mouse_gain_button.unset_flags (CAN_FOCUS);
2752 mouse_zoom_button.unset_flags (CAN_FOCUS);
2753 mouse_timefx_button.unset_flags (CAN_FOCUS);
2754 mouse_audition_button.unset_flags (CAN_FOCUS);
2755 internal_edit_button.unset_flags (CAN_FOCUS);
2756 join_object_range_button.unset_flags (CAN_FOCUS);
2760 _zoom_box.set_spacing (1);
2761 _zoom_box.set_border_width (0);
2763 zoom_in_button.set_name ("EditorTimeButton");
2764 zoom_in_button.set_image (*(manage (new Image (Stock::ZOOM_IN, Gtk::ICON_SIZE_MENU))));
2765 zoom_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), false));
2767 zoom_out_button.set_name ("EditorTimeButton");
2768 zoom_out_button.set_image (*(manage (new Image (Stock::ZOOM_OUT, Gtk::ICON_SIZE_MENU))));
2769 zoom_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), true));
2771 zoom_out_full_button.set_name ("EditorTimeButton");
2772 zoom_out_full_button.set_image (*(manage (new Image (Stock::ZOOM_100, Gtk::ICON_SIZE_MENU))));
2773 zoom_out_full_button.signal_clicked().connect (sigc::mem_fun(*this, &Editor::temporal_zoom_session));
2775 zoom_focus_selector.set_name ("ZoomFocusSelector");
2776 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
2777 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2779 _zoom_box.pack_start (zoom_out_button, false, false);
2780 _zoom_box.pack_start (zoom_in_button, false, false);
2781 _zoom_box.pack_start (zoom_out_full_button, false, false);
2783 _zoom_box.pack_start (zoom_focus_selector);
2785 /* Track zoom buttons */
2786 tav_expand_button.set_name ("TrackHeightButton");
2787 tav_expand_button.set_size_request(-1,20);
2788 tav_expand_button.add (*(manage (new Image (::get_icon("tav_exp")))));
2789 tav_expand_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), true));
2791 tav_shrink_button.set_name ("TrackHeightButton");
2792 tav_shrink_button.set_size_request(-1,20);
2793 tav_shrink_button.add (*(manage (new Image (::get_icon("tav_shrink")))));
2794 tav_shrink_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), false));
2796 _zoom_box.pack_start (tav_shrink_button);
2797 _zoom_box.pack_start (tav_expand_button);
2799 _zoom_tearoff = manage (new TearOff (_zoom_box));
2801 _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2802 &_zoom_tearoff->tearoff_window()));
2803 _zoom_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2804 &_zoom_tearoff->tearoff_window(), 0));
2805 _zoom_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2806 &_zoom_tearoff->tearoff_window()));
2807 _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2808 &_zoom_tearoff->tearoff_window(), 0));
2810 snap_box.set_spacing (1);
2811 snap_box.set_border_width (2);
2813 snap_type_selector.set_name ("SnapTypeSelector");
2814 set_popdown_strings (snap_type_selector, snap_type_strings, true);
2815 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2817 snap_mode_selector.set_name ("SnapModeSelector");
2818 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
2819 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2821 edit_point_selector.set_name ("EditPointSelector");
2822 set_popdown_strings (edit_point_selector, edit_point_strings, true);
2823 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2825 snap_box.pack_start (snap_mode_selector, false, false);
2826 snap_box.pack_start (snap_type_selector, false, false);
2827 snap_box.pack_start (edit_point_selector, false, false);
2831 HBox *nudge_box = manage (new HBox);
2832 nudge_box->set_spacing(1);
2833 nudge_box->set_border_width (2);
2835 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2836 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2838 nudge_box->pack_start (nudge_backward_button, false, false);
2839 nudge_box->pack_start (nudge_forward_button, false, false);
2840 nudge_box->pack_start (nudge_clock, false, false);
2843 /* Pack everything in... */
2845 HBox* hbox = manage (new HBox);
2846 hbox->set_spacing(10);
2848 _tools_tearoff = manage (new TearOff (*hbox));
2849 _tools_tearoff->set_name ("MouseModeBase");
2850 _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
2852 if (Profile->get_sae()) {
2853 _tools_tearoff->set_can_be_torn_off (false);
2856 _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2857 &_tools_tearoff->tearoff_window()));
2858 _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2859 &_tools_tearoff->tearoff_window(), 0));
2860 _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2861 &_tools_tearoff->tearoff_window()));
2862 _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2863 &_tools_tearoff->tearoff_window(), 0));
2865 toolbar_hbox.set_spacing (10);
2866 toolbar_hbox.set_border_width (1);
2868 toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
2869 toolbar_hbox.pack_start (*_zoom_tearoff, false, false);
2870 toolbar_hbox.pack_start (*_tools_tearoff, false, false);
2872 hbox->pack_start (snap_box, false, false);
2873 hbox->pack_start (*nudge_box, false, false);
2874 hbox->pack_start (panic_box, false, false);
2878 toolbar_base.set_name ("ToolBarBase");
2879 toolbar_base.add (toolbar_hbox);
2881 _toolbar_viewport.add (toolbar_base);
2882 /* stick to the required height but allow width to vary if there's not enough room */
2883 _toolbar_viewport.set_size_request (1, -1);
2885 toolbar_frame.set_shadow_type (SHADOW_OUT);
2886 toolbar_frame.set_name ("BaseFrame");
2887 toolbar_frame.add (_toolbar_viewport);
2889 DPIReset.connect (sigc::mem_fun (*this, &Editor::resize_text_widgets));
2893 Editor::setup_tooltips ()
2895 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
2896 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain"));
2897 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
2898 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
2899 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2900 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
2901 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
2902 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
2903 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
2904 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
2905 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
2906 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
2907 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
2908 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
2909 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
2910 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
2911 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
2912 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
2913 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
2914 ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
2915 ARDOUR_UI::instance()->set_tip (midi_panic_button, _("Send note off and reset controller messages on all MIDI channels"));
2916 ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode"));
2920 Editor::midi_panic ()
2922 cerr << "MIDI panic\n";
2925 _session->midi_panic();
2930 Editor::setup_midi_toolbar ()
2934 /* Midi sound notes */
2935 midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
2936 midi_sound_notes.set_relief(Gtk::RELIEF_NONE);
2937 midi_sound_notes.unset_flags (CAN_FOCUS);
2941 act = ActionManager::get_action (X_("MIDI"), X_("panic"));
2942 midi_panic_button.set_name("MidiPanicButton");
2943 act->connect_proxy (midi_panic_button);
2945 panic_box.pack_start (midi_sound_notes , true, true);
2946 panic_box.pack_start (midi_panic_button, true, true);
2950 Editor::convert_drop_to_paths (
2951 vector<string>& paths,
2952 const RefPtr<Gdk::DragContext>& /*context*/,
2955 const SelectionData& data,
2959 if (_session == 0) {
2963 vector<string> uris = data.get_uris();
2967 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
2968 are actually URI lists. So do it by hand.
2971 if (data.get_target() != "text/plain") {
2975 /* Parse the "uri-list" format that Nautilus provides,
2976 where each pathname is delimited by \r\n.
2978 THERE MAY BE NO NULL TERMINATING CHAR!!!
2981 string txt = data.get_text();
2985 p = (const char *) malloc (txt.length() + 1);
2986 txt.copy ((char *) p, txt.length(), 0);
2987 ((char*)p)[txt.length()] = '\0';
2993 while (g_ascii_isspace (*p))
2997 while (*q && (*q != '\n') && (*q != '\r')) {
3004 while (q > p && g_ascii_isspace (*q))
3009 uris.push_back (string (p, q - p + 1));
3013 p = strchr (p, '\n');
3025 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3027 if ((*i).substr (0,7) == "file://") {
3030 PBD::url_decode (p);
3032 // scan forward past three slashes
3034 string::size_type slashcnt = 0;
3035 string::size_type n = 0;
3036 string::iterator x = p.begin();
3038 while (slashcnt < 3 && x != p.end()) {
3041 } else if (slashcnt == 3) {
3048 if (slashcnt != 3 || x == p.end()) {
3049 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3053 paths.push_back (p.substr (n - 1));
3061 Editor::new_tempo_section ()
3067 Editor::map_transport_state ()
3069 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3071 if (_session && _session->transport_stopped()) {
3072 have_pending_keyboard_selection = false;
3075 update_loop_range_view (true);
3080 Editor::State::State (PublicEditor const * e)
3082 selection = new Selection (e);
3085 Editor::State::~State ()
3091 Editor::begin_reversible_command (string name)
3094 _session->begin_reversible_command (name);
3099 Editor::begin_reversible_command (GQuark q)
3102 _session->begin_reversible_command (q);
3107 Editor::commit_reversible_command ()
3110 _session->commit_reversible_command ();
3115 Editor::history_changed ()
3119 if (undo_action && _session) {
3120 if (_session->undo_depth() == 0) {
3123 label = string_compose(_("Undo (%1)"), _session->next_undo());
3125 undo_action->property_label() = label;
3128 if (redo_action && _session) {
3129 if (_session->redo_depth() == 0) {
3132 label = string_compose(_("Redo (%1)"), _session->next_redo());
3134 redo_action->property_label() = label;
3139 Editor::duplicate_dialog (bool with_dialog)
3143 if (mouse_mode == MouseRange) {
3144 if (selection->time.length() == 0) {
3149 RegionSelection rs = get_regions_from_selection_and_entered ();
3151 if (mouse_mode != MouseRange && rs.empty()) {
3157 ArdourDialog win (_("Duplicate"));
3158 Label label (_("Number of duplications:"));
3159 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3160 SpinButton spinner (adjustment, 0.0, 1);
3163 win.get_vbox()->set_spacing (12);
3164 win.get_vbox()->pack_start (hbox);
3165 hbox.set_border_width (6);
3166 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3168 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3169 place, visually. so do this by hand.
3172 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3173 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3174 spinner.grab_focus();
3180 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3181 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3182 win.set_default_response (RESPONSE_ACCEPT);
3184 win.set_position (WIN_POS_MOUSE);
3186 spinner.grab_focus ();
3188 switch (win.run ()) {
3189 case RESPONSE_ACCEPT:
3195 times = adjustment.get_value();
3198 if (mouse_mode == MouseRange) {
3199 duplicate_selection (times);
3201 duplicate_some_regions (rs, times);
3206 Editor::show_verbose_canvas_cursor ()
3208 verbose_canvas_cursor->raise_to_top();
3209 verbose_canvas_cursor->show();
3210 verbose_cursor_visible = true;
3214 Editor::hide_verbose_canvas_cursor ()
3216 verbose_canvas_cursor->hide();
3217 verbose_cursor_visible = false;
3221 Editor::clamp_verbose_cursor_x (double x)
3226 x = min (_canvas_width - 200.0, x);
3232 Editor::clamp_verbose_cursor_y (double y)
3234 if (y < canvas_timebars_vsize) {
3235 y = canvas_timebars_vsize;
3237 y = min (_canvas_height - 50, y);
3243 Editor::show_verbose_canvas_cursor_with (const string & txt, int32_t xoffset, int32_t yoffset)
3245 verbose_canvas_cursor->property_text() = txt.c_str();
3250 track_canvas->get_pointer (x, y);
3251 track_canvas->window_to_world (x, y, wx, wy);
3256 /* don't get too close to the edge */
3257 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (wx);
3258 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (wy);
3260 show_verbose_canvas_cursor ();
3264 Editor::set_verbose_canvas_cursor (const string & txt, double x, double y)
3266 verbose_canvas_cursor->property_text() = txt.c_str();
3267 /* don't get too close to the edge */
3268 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (x);
3269 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (y);
3273 Editor::set_verbose_canvas_cursor_text (const string & txt)
3275 verbose_canvas_cursor->property_text() = txt.c_str();
3279 Editor::set_edit_mode (EditMode m)
3281 Config->set_edit_mode (m);
3285 Editor::cycle_edit_mode ()
3287 switch (Config->get_edit_mode()) {
3289 if (Profile->get_sae()) {
3290 Config->set_edit_mode (Lock);
3292 Config->set_edit_mode (Splice);
3296 Config->set_edit_mode (Lock);
3299 Config->set_edit_mode (Slide);
3305 Editor::edit_mode_selection_done ()
3307 string s = edit_mode_selector.get_active_text ();
3310 Config->set_edit_mode (string_to_edit_mode (s));
3315 Editor::snap_type_selection_done ()
3317 string choice = snap_type_selector.get_active_text();
3318 SnapType snaptype = SnapToBeat;
3320 if (choice == _("Beats/2")) {
3321 snaptype = SnapToBeatDiv2;
3322 } else if (choice == _("Beats/3")) {
3323 snaptype = SnapToBeatDiv3;
3324 } else if (choice == _("Beats/4")) {
3325 snaptype = SnapToBeatDiv4;
3326 } else if (choice == _("Beats/5")) {
3327 snaptype = SnapToBeatDiv5;
3328 } else if (choice == _("Beats/6")) {
3329 snaptype = SnapToBeatDiv6;
3330 } else if (choice == _("Beats/7")) {
3331 snaptype = SnapToBeatDiv7;
3332 } else if (choice == _("Beats/8")) {
3333 snaptype = SnapToBeatDiv8;
3334 } else if (choice == _("Beats/10")) {
3335 snaptype = SnapToBeatDiv10;
3336 } else if (choice == _("Beats/12")) {
3337 snaptype = SnapToBeatDiv12;
3338 } else if (choice == _("Beats/14")) {
3339 snaptype = SnapToBeatDiv14;
3340 } else if (choice == _("Beats/16")) {
3341 snaptype = SnapToBeatDiv16;
3342 } else if (choice == _("Beats/20")) {
3343 snaptype = SnapToBeatDiv20;
3344 } else if (choice == _("Beats/24")) {
3345 snaptype = SnapToBeatDiv24;
3346 } else if (choice == _("Beats/28")) {
3347 snaptype = SnapToBeatDiv28;
3348 } else if (choice == _("Beats/32")) {
3349 snaptype = SnapToBeatDiv32;
3350 } else if (choice == _("Beats")) {
3351 snaptype = SnapToBeat;
3352 } else if (choice == _("Bars")) {
3353 snaptype = SnapToBar;
3354 } else if (choice == _("Marks")) {
3355 snaptype = SnapToMark;
3356 } else if (choice == _("Region starts")) {
3357 snaptype = SnapToRegionStart;
3358 } else if (choice == _("Region ends")) {
3359 snaptype = SnapToRegionEnd;
3360 } else if (choice == _("Region bounds")) {
3361 snaptype = SnapToRegionBoundary;
3362 } else if (choice == _("Region syncs")) {
3363 snaptype = SnapToRegionSync;
3364 } else if (choice == _("CD Frames")) {
3365 snaptype = SnapToCDFrame;
3366 } else if (choice == _("Timecode Frames")) {
3367 snaptype = SnapToTimecodeFrame;
3368 } else if (choice == _("Timecode Seconds")) {
3369 snaptype = SnapToTimecodeSeconds;
3370 } else if (choice == _("Timecode Minutes")) {
3371 snaptype = SnapToTimecodeMinutes;
3372 } else if (choice == _("Seconds")) {
3373 snaptype = SnapToSeconds;
3374 } else if (choice == _("Minutes")) {
3375 snaptype = SnapToMinutes;
3378 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3380 ract->set_active ();
3385 Editor::snap_mode_selection_done ()
3387 string choice = snap_mode_selector.get_active_text();
3388 SnapMode mode = SnapNormal;
3390 if (choice == _("No Grid")) {
3392 } else if (choice == _("Grid")) {
3394 } else if (choice == _("Magnetic")) {
3395 mode = SnapMagnetic;
3398 RefPtr<RadioAction> ract = snap_mode_action (mode);
3401 ract->set_active (true);
3406 Editor::cycle_edit_point (bool with_marker)
3408 switch (_edit_point) {
3410 set_edit_point_preference (EditAtPlayhead);
3412 case EditAtPlayhead:
3414 set_edit_point_preference (EditAtSelectedMarker);
3416 set_edit_point_preference (EditAtMouse);
3419 case EditAtSelectedMarker:
3420 set_edit_point_preference (EditAtMouse);
3426 Editor::edit_point_selection_done ()
3428 string choice = edit_point_selector.get_active_text();
3429 EditPoint ep = EditAtSelectedMarker;
3431 if (choice == _("Marker")) {
3432 set_edit_point_preference (EditAtSelectedMarker);
3433 } else if (choice == _("Playhead")) {
3434 set_edit_point_preference (EditAtPlayhead);
3436 set_edit_point_preference (EditAtMouse);
3439 RefPtr<RadioAction> ract = edit_point_action (ep);
3442 ract->set_active (true);
3447 Editor::zoom_focus_selection_done ()
3449 string choice = zoom_focus_selector.get_active_text();
3450 ZoomFocus focus_type = ZoomFocusLeft;
3452 if (choice == _("Left")) {
3453 focus_type = ZoomFocusLeft;
3454 } else if (choice == _("Right")) {
3455 focus_type = ZoomFocusRight;
3456 } else if (choice == _("Center")) {
3457 focus_type = ZoomFocusCenter;
3458 } else if (choice == _("Playhead")) {
3459 focus_type = ZoomFocusPlayhead;
3460 } else if (choice == _("Mouse")) {
3461 focus_type = ZoomFocusMouse;
3462 } else if (choice == _("Edit point")) {
3463 focus_type = ZoomFocusEdit;
3466 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3469 ract->set_active ();
3474 Editor::edit_controls_button_release (GdkEventButton* ev)
3476 if (Keyboard::is_context_menu_event (ev)) {
3477 ARDOUR_UI::instance()->add_route (this);
3483 Editor::mouse_select_button_release (GdkEventButton* ev)
3485 /* this handles just right-clicks */
3487 if (ev->button != 3) {
3495 Editor::set_zoom_focus (ZoomFocus f)
3497 string str = zoom_focus_strings[(int)f];
3499 if (str != zoom_focus_selector.get_active_text()) {
3500 zoom_focus_selector.set_active_text (str);
3503 if (zoom_focus != f) {
3506 ZoomFocusChanged (); /* EMIT_SIGNAL */
3513 Editor::ensure_float (Window& win)
3515 win.set_transient_for (*this);
3519 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3521 /* recover or initialize pane positions. do this here rather than earlier because
3522 we don't want the positions to change the child allocations, which they seem to do.
3528 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3540 width = default_width;
3541 height = default_height;
3543 if ((geometry = find_named_node (*node, "geometry")) != 0) {
3545 prop = geometry->property ("x-size");
3547 width = atoi (prop->value());
3549 prop = geometry->property ("y-size");
3551 height = atoi (prop->value());
3555 if (which == static_cast<Paned*> (&edit_pane)) {
3557 if (done & Horizontal) {
3561 if (geometry && (prop = geometry->property ("notebook-shrunk"))) {
3562 _notebook_shrunk = string_is_affirmative (prop->value ());
3565 if (geometry && (prop = geometry->property ("pre-maximal-horizontal-pane-position"))) {
3566 pre_maximal_horizontal_pane_position = atoi (prop->value ());
3569 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3570 /* initial allocation is 90% to canvas, 10% to notebook */
3571 pos = (int) floor (alloc.get_width() * 0.90f);
3572 snprintf (buf, sizeof(buf), "%d", pos);
3574 pos = atoi (prop->value());
3577 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3578 edit_pane.set_position (pos);
3579 if (pre_maximal_horizontal_pane_position == 0) {
3580 pre_maximal_horizontal_pane_position = pos;
3584 done = (Pane) (done | Horizontal);
3586 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3588 if (done & Vertical) {
3592 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3593 /* initial allocation is 90% to canvas, 10% to summary */
3594 pos = (int) floor (alloc.get_height() * 0.90f);
3595 snprintf (buf, sizeof(buf), "%d", pos);
3597 pos = atoi (prop->value());
3600 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3601 editor_summary_pane.set_position (pos);
3602 pre_maximal_vertical_pane_position = pos;
3605 done = (Pane) (done | Vertical);
3610 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3612 if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) {
3613 top_hbox.remove (toolbar_frame);
3618 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3620 if (toolbar_frame.get_parent() == 0) {
3621 top_hbox.pack_end (toolbar_frame);
3626 Editor::set_show_measures (bool yn)
3628 if (_show_measures != yn) {
3631 if ((_show_measures = yn) == true) {
3633 tempo_lines->show();
3641 Editor::toggle_follow_playhead ()
3643 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3645 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3646 set_follow_playhead (tact->get_active());
3650 /** @param yn true to follow playhead, otherwise false.
3651 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
3654 Editor::set_follow_playhead (bool yn, bool catch_up)
3656 if (_follow_playhead != yn) {
3657 if ((_follow_playhead = yn) == true && catch_up) {
3659 reset_x_origin_to_follow_playhead ();
3666 Editor::toggle_stationary_playhead ()
3668 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3670 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3671 set_stationary_playhead (tact->get_active());
3676 Editor::set_stationary_playhead (bool yn)
3678 if (_stationary_playhead != yn) {
3679 if ((_stationary_playhead = yn) == true) {
3681 // FIXME need a 3.0 equivalent of this 2.X call
3682 // update_current_screen ();
3689 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3691 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3693 xfade->set_active (!xfade->active());
3698 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3700 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3702 xfade->set_follow_overlap (!xfade->following_overlap());
3707 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3709 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3715 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3719 switch (cew.run ()) {
3720 case RESPONSE_ACCEPT:
3727 PropertyChange all_crossfade_properties;
3728 all_crossfade_properties.add (ARDOUR::Properties::active);
3729 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3730 xfade->PropertyChanged (all_crossfade_properties);
3734 Editor::playlist_selector () const
3736 return *_playlist_selector;
3740 Editor::get_grid_type_as_beats (bool& success, framepos_t position)
3744 switch (_snap_type) {
3749 case SnapToBeatDiv32:
3752 case SnapToBeatDiv28:
3755 case SnapToBeatDiv24:
3758 case SnapToBeatDiv20:
3761 case SnapToBeatDiv16:
3764 case SnapToBeatDiv14:
3767 case SnapToBeatDiv12:
3770 case SnapToBeatDiv10:
3773 case SnapToBeatDiv8:
3776 case SnapToBeatDiv7:
3779 case SnapToBeatDiv6:
3782 case SnapToBeatDiv5:
3785 case SnapToBeatDiv4:
3788 case SnapToBeatDiv3:
3791 case SnapToBeatDiv2:
3797 return _session->tempo_map().meter_at (position).beats_per_bar();
3802 case SnapToTimecodeFrame:
3803 case SnapToTimecodeSeconds:
3804 case SnapToTimecodeMinutes:
3807 case SnapToRegionStart:
3808 case SnapToRegionEnd:
3809 case SnapToRegionSync:
3810 case SnapToRegionBoundary:
3820 Editor::get_nudge_distance (framepos_t pos, framecnt_t& next)
3824 ret = nudge_clock.current_duration (pos);
3825 next = ret + 1; /* XXXX fix me */
3831 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3833 ArdourDialog dialog (_("Playlist Deletion"));
3834 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3835 "If left alone, no audio files used by it will be cleaned.\n"
3836 "If deleted, audio files used by it alone by will cleaned."),
3839 dialog.set_position (WIN_POS_CENTER);
3840 dialog.get_vbox()->pack_start (label);
3844 dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT);
3845 dialog.add_button (_("Keep playlist"), RESPONSE_REJECT);
3846 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3848 switch (dialog.run ()) {
3849 case RESPONSE_ACCEPT:
3850 /* delete the playlist */
3854 case RESPONSE_REJECT:
3855 /* keep the playlist */
3867 Editor::audio_region_selection_covers (framepos_t where)
3869 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3870 if ((*a)->region()->covers (where)) {
3879 Editor::prepare_for_cleanup ()
3881 cut_buffer->clear_regions ();
3882 cut_buffer->clear_playlists ();
3884 selection->clear_regions ();
3885 selection->clear_playlists ();
3887 _regions->suspend_redisplay ();
3891 Editor::finish_cleanup ()
3893 _regions->resume_redisplay ();
3897 Editor::transport_loop_location()
3900 return _session->locations()->auto_loop_location();
3907 Editor::transport_punch_location()
3910 return _session->locations()->auto_punch_location();
3917 Editor::control_layout_scroll (GdkEventScroll* ev)
3919 if (Keyboard::some_magic_widget_has_focus()) {
3923 switch (ev->direction) {
3925 scroll_tracks_up_line ();
3929 case GDK_SCROLL_DOWN:
3930 scroll_tracks_down_line ();
3934 /* no left/right handling yet */
3942 Editor::session_state_saved (string)
3945 _snapshots->redisplay ();
3949 Editor::maximise_editing_space ()
3951 _mouse_mode_tearoff->set_visible (false);
3952 _tools_tearoff->set_visible (false);
3953 _zoom_tearoff->set_visible (false);
3955 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
3956 pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
3957 pre_maximal_editor_width = this->get_width ();
3958 pre_maximal_editor_height = this->get_height ();
3960 if (post_maximal_horizontal_pane_position == 0) {
3961 post_maximal_horizontal_pane_position = edit_pane.get_width();
3964 if (post_maximal_vertical_pane_position == 0) {
3965 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
3970 if (post_maximal_editor_width) {
3971 edit_pane.set_position (post_maximal_horizontal_pane_position -
3972 abs(post_maximal_editor_width - pre_maximal_editor_width));
3974 edit_pane.set_position (post_maximal_horizontal_pane_position);
3977 if (post_maximal_editor_height) {
3978 editor_summary_pane.set_position (post_maximal_vertical_pane_position -
3979 abs(post_maximal_editor_height - pre_maximal_editor_height));
3981 editor_summary_pane.set_position (post_maximal_vertical_pane_position);
3984 if (Config->get_keep_tearoffs()) {
3985 _mouse_mode_tearoff->set_visible (true);
3986 _tools_tearoff->set_visible (true);
3987 if (Config->get_show_zoom_tools ()) {
3988 _zoom_tearoff->set_visible (true);
3995 Editor::restore_editing_space ()
3997 // user changed width/height of panes during fullscreen
3999 if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
4000 post_maximal_horizontal_pane_position = edit_pane.get_position();
4003 if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
4004 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
4009 _mouse_mode_tearoff->set_visible (true);
4010 _tools_tearoff->set_visible (true);
4011 if (Config->get_show_zoom_tools ()) {
4012 _zoom_tearoff->set_visible (true);
4014 post_maximal_editor_width = this->get_width();
4015 post_maximal_editor_height = this->get_height();
4017 edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
4018 editor_summary_pane.set_position (pre_maximal_vertical_pane_position + abs(this->get_height() - pre_maximal_editor_height));
4022 * Make new playlists for a given track and also any others that belong
4023 * to the same active route group with the `edit' property.
4028 Editor::new_playlists (TimeAxisView* v)
4030 begin_reversible_command (_("new playlists"));
4031 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4032 _session->playlists->get (playlists);
4033 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4034 commit_reversible_command ();
4038 * Use a copy of the current playlist for a given track and also any others that belong
4039 * to the same active route group with the `edit' property.
4044 Editor::copy_playlists (TimeAxisView* v)
4046 begin_reversible_command (_("copy playlists"));
4047 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4048 _session->playlists->get (playlists);
4049 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4050 commit_reversible_command ();
4053 /** Clear the current playlist for a given track and also any others that belong
4054 * to the same active route group with the `edit' property.
4059 Editor::clear_playlists (TimeAxisView* v)
4061 begin_reversible_command (_("clear playlists"));
4062 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4063 _session->playlists->get (playlists);
4064 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4065 commit_reversible_command ();
4069 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4071 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4075 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4077 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4081 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4083 atv.clear_playlist ();
4087 Editor::on_key_press_event (GdkEventKey* ev)
4089 return key_press_focus_accelerator_handler (*this, ev);
4093 Editor::on_key_release_event (GdkEventKey* ev)
4095 return Gtk::Window::on_key_release_event (ev);
4096 // return key_press_focus_accelerator_handler (*this, ev);
4099 /** Queue up a change to the viewport x origin.
4100 * @param frame New x origin.
4103 Editor::reset_x_origin (framepos_t frame)
4105 queue_visual_change (frame);
4109 Editor::reset_y_origin (double y)
4111 queue_visual_change_y (y);
4115 Editor::reset_zoom (double fpu)
4117 queue_visual_change (fpu);
4121 Editor::reposition_and_zoom (framepos_t frame, double fpu)
4123 reset_x_origin (frame);
4126 if (!no_save_visual) {
4127 undo_visual_stack.push_back (current_visual_state(false));
4131 Editor::VisualState*
4132 Editor::current_visual_state (bool with_tracks)
4134 VisualState* vs = new VisualState;
4135 vs->y_position = vertical_adjustment.get_value();
4136 vs->frames_per_unit = frames_per_unit;
4137 vs->leftmost_frame = leftmost_frame;
4138 vs->zoom_focus = zoom_focus;
4141 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4142 vs->track_states.push_back (TAVState ((*i), &(*i)->get_state()));
4150 Editor::undo_visual_state ()
4152 if (undo_visual_stack.empty()) {
4156 redo_visual_stack.push_back (current_visual_state());
4158 VisualState* vs = undo_visual_stack.back();
4159 undo_visual_stack.pop_back();
4160 use_visual_state (*vs);
4164 Editor::redo_visual_state ()
4166 if (redo_visual_stack.empty()) {
4170 undo_visual_stack.push_back (current_visual_state());
4172 VisualState* vs = redo_visual_stack.back();
4173 redo_visual_stack.pop_back();
4174 use_visual_state (*vs);
4178 Editor::swap_visual_state ()
4180 if (undo_visual_stack.empty()) {
4181 redo_visual_state ();
4183 undo_visual_state ();
4188 Editor::use_visual_state (VisualState& vs)
4190 no_save_visual = true;
4192 _routes->suspend_redisplay ();
4194 vertical_adjustment.set_value (vs.y_position);
4196 set_zoom_focus (vs.zoom_focus);
4197 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4199 for (list<TAVState>::iterator i = vs.track_states.begin(); i != vs.track_states.end(); ++i) {
4200 TrackViewList::iterator t;
4202 /* check if the track still exists - it could have been deleted */
4204 if ((t = find (track_views.begin(), track_views.end(), i->first)) != track_views.end()) {
4205 (*t)->set_state (*(i->second), Stateful::loading_state_version);
4210 if (!vs.track_states.empty()) {
4211 _routes->update_visibility ();
4214 _routes->resume_redisplay ();
4216 no_save_visual = false;
4220 Editor::set_frames_per_unit (double fpu)
4222 /* this is the core function that controls the zoom level of the canvas. it is called
4223 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4226 if (fpu == frames_per_unit) {
4235 /* don't allow zooms that fit more than the maximum number
4236 of frames into an 800 pixel wide space.
4239 if (max_framepos / fpu < 800.0) {
4244 tempo_lines->tempo_map_changed();
4246 frames_per_unit = fpu;
4251 Editor::post_zoom ()
4253 // convert fpu to frame count
4255 framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width);
4257 if (frames_per_unit != zoom_range_clock.current_duration()) {
4258 zoom_range_clock.set (frames);
4261 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4262 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4263 (*i)->reshow_selection (selection->time);
4267 ZoomChanged (); /* EMIT_SIGNAL */
4269 //reset_scrolling_region ();
4271 if (playhead_cursor) {
4272 playhead_cursor->set_position (playhead_cursor->current_frame);
4275 refresh_location_display();
4276 _summary->set_overlays_dirty ();
4278 update_marker_labels ();
4284 Editor::queue_visual_change (framepos_t where)
4286 pending_visual_change.add (VisualChange::TimeOrigin);
4287 pending_visual_change.time_origin = where;
4288 ensure_visual_change_idle_handler ();
4292 Editor::queue_visual_change (double fpu)
4294 pending_visual_change.add (VisualChange::ZoomLevel);
4295 pending_visual_change.frames_per_unit = fpu;
4297 ensure_visual_change_idle_handler ();
4301 Editor::queue_visual_change_y (double y)
4303 pending_visual_change.add (VisualChange::YOrigin);
4304 pending_visual_change.y_origin = y;
4306 ensure_visual_change_idle_handler ();
4310 Editor::ensure_visual_change_idle_handler ()
4312 if (pending_visual_change.idle_handler_id < 0) {
4313 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4318 Editor::_idle_visual_changer (void* arg)
4320 return static_cast<Editor*>(arg)->idle_visual_changer ();
4324 Editor::idle_visual_changer ()
4326 VisualChange::Type p = pending_visual_change.pending;
4327 pending_visual_change.pending = (VisualChange::Type) 0;
4329 double const last_time_origin = horizontal_position ();
4331 if (p & VisualChange::TimeOrigin) {
4332 /* This is a bit of a hack, but set_frames_per_unit
4333 below will (if called) end up with the
4334 CrossfadeViews looking at Editor::leftmost_frame,
4335 and if we're changing origin and zoom in the same
4336 operation it will be the wrong value unless we
4340 leftmost_frame = pending_visual_change.time_origin;
4343 if (p & VisualChange::ZoomLevel) {
4344 set_frames_per_unit (pending_visual_change.frames_per_unit);
4346 compute_fixed_ruler_scale ();
4347 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4348 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4349 update_tempo_based_rulers ();
4351 if (p & VisualChange::TimeOrigin) {
4352 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4354 if (p & VisualChange::YOrigin) {
4355 vertical_adjustment.set_value (pending_visual_change.y_origin);
4358 if (last_time_origin == horizontal_position ()) {
4359 /* changed signal not emitted */
4360 update_fixed_rulers ();
4361 redisplay_tempo (true);
4364 _summary->set_overlays_dirty ();
4366 pending_visual_change.idle_handler_id = -1;
4367 return 0; /* this is always a one-shot call */
4370 struct EditorOrderTimeAxisSorter {
4371 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4372 return a->order () < b->order ();
4377 Editor::sort_track_selection (TrackViewList* sel)
4379 EditorOrderTimeAxisSorter cmp;
4384 selection->tracks.sort (cmp);
4389 Editor::get_preferred_edit_position (bool ignore_playhead)
4392 framepos_t where = 0;
4393 EditPoint ep = _edit_point;
4395 if (entered_marker) {
4396 return entered_marker->position();
4399 if (ignore_playhead && ep == EditAtPlayhead) {
4400 ep = EditAtSelectedMarker;
4404 case EditAtPlayhead:
4405 where = _session->audible_frame();
4408 case EditAtSelectedMarker:
4409 if (!selection->markers.empty()) {
4411 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4414 where = loc->start();
4425 if (!mouse_frame (where, ignored)) {
4426 /* XXX not right but what can we do ? */
4437 Editor::set_loop_range (framepos_t start, framepos_t end, string cmd)
4439 if (!_session) return;
4441 begin_reversible_command (cmd);
4445 if ((tll = transport_loop_location()) == 0) {
4446 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop);
4447 XMLNode &before = _session->locations()->get_state();
4448 _session->locations()->add (loc, true);
4449 _session->set_auto_loop_location (loc);
4450 XMLNode &after = _session->locations()->get_state();
4451 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4453 XMLNode &before = tll->get_state();
4454 tll->set_hidden (false, this);
4455 tll->set (start, end);
4456 XMLNode &after = tll->get_state();
4457 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4460 commit_reversible_command ();
4464 Editor::set_punch_range (framepos_t start, framepos_t end, string cmd)
4466 if (!_session) return;
4468 begin_reversible_command (cmd);
4472 if ((tpl = transport_punch_location()) == 0) {
4473 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoPunch);
4474 XMLNode &before = _session->locations()->get_state();
4475 _session->locations()->add (loc, true);
4476 _session->set_auto_loop_location (loc);
4477 XMLNode &after = _session->locations()->get_state();
4478 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4481 XMLNode &before = tpl->get_state();
4482 tpl->set_hidden (false, this);
4483 tpl->set (start, end);
4484 XMLNode &after = tpl->get_state();
4485 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4488 commit_reversible_command ();
4491 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4492 * @param rs List to which found regions are added.
4493 * @param where Time to look at.
4494 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4497 Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4499 const TrackViewList* tracks;
4502 tracks = &track_views;
4507 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4509 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4512 boost::shared_ptr<Track> tr;
4513 boost::shared_ptr<Playlist> pl;
4515 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4517 Playlist::RegionList* regions = pl->regions_at (
4518 (framepos_t) floor ( (double) where * tr->speed()));
4520 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4521 RegionView* rv = rtv->view()->find_view (*i);
4534 Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4536 const TrackViewList* tracks;
4539 tracks = &track_views;
4544 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4545 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4547 boost::shared_ptr<Track> tr;
4548 boost::shared_ptr<Playlist> pl;
4550 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4552 Playlist::RegionList* regions = pl->regions_touched (
4553 (framepos_t) floor ( (double)where * tr->speed()), max_framepos);
4555 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4557 RegionView* rv = rtv->view()->find_view (*i);
4570 /** Start with regions that are selected. Then add equivalent regions
4571 * on tracks in the same active edit-enabled route group as any of
4572 * the regions that we started with.
4576 Editor::get_regions_from_selection ()
4578 return get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
4581 /** Get regions using the following method:
4583 * Make an initial region list using the selected regions, unless
4584 * the edit point is `mouse' and the mouse is over an unselected
4585 * region. In this case, start with just that region.
4587 * Then, make an initial track list of the tracks that these
4588 * regions are on, and if the edit point is not `mouse', add the
4591 * Look at this track list and add any other tracks that are on the
4592 * same active edit-enabled route group as one of the initial tracks.
4594 * Finally take the initial region list and add any regions that are
4595 * under the edit point on one of the tracks on the track list to get
4596 * the returned region list.
4598 * The rationale here is that the mouse edit point is special in that
4599 * its position describes both a time and a track; the other edit
4600 * modes only describe a time. Hence if the edit point is `mouse' we
4601 * ignore selected tracks, as we assume the user means something by
4602 * pointing at a particular track. Also in this case we take note of
4603 * the region directly under the edit point, as there is always just one
4604 * (rather than possibly several with non-mouse edit points).
4608 Editor::get_regions_from_selection_and_edit_point ()
4610 RegionSelection regions;
4612 if (_edit_point == EditAtMouse && entered_regionview && !selection->regions.contains (entered_regionview)) {
4613 regions.add (entered_regionview);
4615 regions = selection->regions;
4618 TrackViewList tracks;
4620 if (_edit_point != EditAtMouse) {
4621 tracks = selection->tracks;
4624 /* Add any other tracks that have regions that are in the same
4625 edit-activated route group as one of our regions.
4627 for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) {
4629 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4631 if (g && g->is_active() && g->is_edit()) {
4632 tracks.add (axis_views_from_routes (g->route_list()));
4636 if (!tracks.empty()) {
4637 /* now find regions that are at the edit position on those tracks */
4638 framepos_t const where = get_preferred_edit_position ();
4639 get_regions_at (regions, where, tracks);
4645 /** Start with regions that are selected, or the entered regionview if none are selected.
4646 * Then add equivalent regions on tracks in the same active edit-enabled route group as any
4647 * of the regions that we started with.
4651 Editor::get_regions_from_selection_and_entered ()
4653 RegionSelection regions = selection->regions;
4655 if (regions.empty() && entered_regionview) {
4656 regions.add (entered_regionview);
4659 return get_equivalent_regions (regions, ARDOUR::Properties::edit.property_id);
4663 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4665 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4667 RouteTimeAxisView* tatv;
4669 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4671 boost::shared_ptr<Playlist> pl;
4672 vector<boost::shared_ptr<Region> > results;
4674 boost::shared_ptr<Track> tr;
4676 if ((tr = tatv->track()) == 0) {
4681 if ((pl = (tr->playlist())) != 0) {
4682 pl->get_region_list_equivalent_regions (region, results);
4685 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4686 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4687 regions.push_back (marv);
4696 Editor::show_rhythm_ferret ()
4698 if (rhythm_ferret == 0) {
4699 rhythm_ferret = new RhythmFerret(*this);
4702 rhythm_ferret->set_session (_session);
4703 rhythm_ferret->show ();
4704 rhythm_ferret->present ();
4708 Editor::first_idle ()
4710 MessageDialog* dialog = 0;
4712 if (track_views.size() > 1) {
4713 dialog = new MessageDialog (*this,
4714 string_compose (_("Please wait while %1 loads visual data"), PROGRAM_NAME),
4719 ARDOUR_UI::instance()->flush_pending ();
4722 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4726 // first idle adds route children (automation tracks), so we need to redisplay here
4727 _routes->redisplay ();
4735 Editor::_idle_resize (gpointer arg)
4737 return ((Editor*)arg)->idle_resize ();
4741 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4743 if (resize_idle_id < 0) {
4744 resize_idle_id = g_idle_add (_idle_resize, this);
4745 _pending_resize_amount = 0;
4748 /* make a note of the smallest resulting height, so that we can clamp the
4749 lower limit at TimeAxisView::hSmall */
4751 int32_t min_resulting = INT32_MAX;
4753 _pending_resize_amount += h;
4754 _pending_resize_view = view;
4756 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4758 if (selection->tracks.contains (_pending_resize_view)) {
4759 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4760 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4764 if (min_resulting < 0) {
4769 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4770 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4774 /** Handle pending resizing of tracks */
4776 Editor::idle_resize ()
4778 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4780 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4781 selection->tracks.contains (_pending_resize_view)) {
4783 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4784 if (*i != _pending_resize_view) {
4785 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4790 _pending_resize_amount = 0;
4792 _group_tabs->set_dirty ();
4793 resize_idle_id = -1;
4801 ENSURE_GUI_THREAD (*this, &Editor::located);
4803 playhead_cursor->set_position (_session->audible_frame ());
4804 if (_follow_playhead && !_pending_initial_locate) {
4805 reset_x_origin_to_follow_playhead ();
4808 _pending_locate_request = false;
4809 _pending_initial_locate = false;
4813 Editor::region_view_added (RegionView *)
4815 _summary->set_dirty ();
4819 Editor::region_view_removed ()
4821 _summary->set_dirty ();
4825 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4827 TrackViewList::const_iterator j = track_views.begin ();
4828 while (j != track_views.end()) {
4829 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4830 if (rtv && rtv->route() == r) {
4841 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4845 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4846 TimeAxisView* tv = axis_view_from_route (*i);
4857 Editor::handle_new_route (RouteList& routes)
4859 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4861 RouteTimeAxisView *rtv;
4862 list<RouteTimeAxisView*> new_views;
4864 cerr << "Handle new route\n";
4866 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4867 boost::shared_ptr<Route> route = (*x);
4869 if (route->is_hidden() || route->is_monitor()) {
4873 DataType dt = route->input()->default_type();
4875 if (dt == ARDOUR::DataType::AUDIO) {
4876 rtv = new AudioTimeAxisView (*this, _session, route, *track_canvas);
4877 } else if (dt == ARDOUR::DataType::MIDI) {
4878 rtv = new MidiTimeAxisView (*this, _session, route, *track_canvas);
4880 throw unknown_type();
4883 new_views.push_back (rtv);
4884 track_views.push_back (rtv);
4886 rtv->effective_gain_display ();
4888 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4889 rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed));
4892 _routes->routes_added (new_views);
4893 _summary->routes_added (new_views);
4895 if (show_editor_mixer_when_tracks_arrive) {
4896 show_editor_mixer (true);
4899 editor_list_button.set_sensitive (true);
4903 Editor::timeaxisview_deleted (TimeAxisView *tv)
4905 if (_session && _session->deletion_in_progress()) {
4906 /* the situation is under control */
4910 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
4912 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
4914 _routes->route_removed (tv);
4916 if (tv == entered_track) {
4920 TimeAxisView::Children c = tv->get_child_list ();
4921 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
4922 if (entered_track == i->get()) {
4927 /* remove it from the list of track views */
4929 TrackViewList::iterator i;
4931 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
4932 i = track_views.erase (i);
4935 /* update whatever the current mixer strip is displaying, if revelant */
4937 boost::shared_ptr<Route> route;
4940 route = rtav->route ();
4943 if (current_mixer_strip && current_mixer_strip->route() == route) {
4945 TimeAxisView* next_tv;
4947 if (track_views.empty()) {
4949 } else if (i == track_views.end()) {
4950 next_tv = track_views.front();
4957 set_selected_mixer_strip (*next_tv);
4959 /* make the editor mixer strip go away setting the
4960 * button to inactive (which also unticks the menu option)
4963 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
4969 Editor::hide_track_in_display (TimeAxisView* tv, bool /*temponly*/)
4971 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
4973 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
4974 // this will hide the mixer strip
4975 set_selected_mixer_strip (*tv);
4978 _routes->hide_track_in_display (*tv);
4982 Editor::sync_track_view_list_and_routes ()
4984 track_views = TrackViewList (_routes->views ());
4986 _summary->set_dirty ();
4987 _group_tabs->set_dirty ();
4989 return false; // do not call again (until needed)
4993 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
4995 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5000 /** Find a RouteTimeAxisView by the ID of its route */
5002 Editor::get_route_view_by_route_id (PBD::ID& id) const
5004 RouteTimeAxisView* v;
5006 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5007 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5008 if(v->route()->id() == id) {
5018 Editor::fit_route_group (RouteGroup *g)
5020 TrackViewList ts = axis_views_from_routes (g->route_list ());
5025 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5027 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5030 _session->cancel_audition ();
5034 if (_session->is_auditioning()) {
5035 _session->cancel_audition ();
5036 if (r == last_audition_region) {
5041 _session->audition_region (r);
5042 last_audition_region = r;
5047 Editor::hide_a_region (boost::shared_ptr<Region> r)
5049 r->set_hidden (true);
5053 Editor::show_a_region (boost::shared_ptr<Region> r)
5055 r->set_hidden (false);
5059 Editor::audition_region_from_region_list ()
5061 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5065 Editor::hide_region_from_region_list ()
5067 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5071 Editor::show_region_in_region_list ()
5073 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5077 Editor::step_edit_status_change (bool yn)
5080 start_step_editing ();
5082 stop_step_editing ();
5087 Editor::start_step_editing ()
5089 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5093 Editor::stop_step_editing ()
5095 step_edit_connection.disconnect ();
5099 Editor::check_step_edit ()
5101 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5102 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5104 mtv->check_step_edit ();
5108 return true; // do it again, till we stop
5112 Editor::horizontal_scroll_left_press ()
5114 ++_scroll_callbacks;
5116 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5117 /* delay the first auto-repeat */
5121 double x = leftmost_position() - current_page_frames() / 5;
5128 /* do hacky auto-repeat */
5129 if (!_scroll_connection.connected ()) {
5130 _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press), 100);
5131 _scroll_callbacks = 0;
5138 Editor::horizontal_scroll_left_release ()
5140 _scroll_connection.disconnect ();
5144 Editor::horizontal_scroll_right_press ()
5146 ++_scroll_callbacks;
5148 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5149 /* delay the first auto-repeat */
5153 reset_x_origin (leftmost_position() + current_page_frames() / 5);
5155 /* do hacky auto-repeat */
5156 if (!_scroll_connection.connected ()) {
5157 _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press), 100);
5158 _scroll_callbacks = 0;
5165 Editor::horizontal_scroll_right_release ()
5167 _scroll_connection.disconnect ();
5170 /** Queue a change for the Editor viewport x origin to follow the playhead */
5172 Editor::reset_x_origin_to_follow_playhead ()
5174 framepos_t const frame = playhead_cursor->current_frame;
5176 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5178 if (_session->transport_speed() < 0) {
5180 if (frame > (current_page_frames() / 2)) {
5181 center_screen (frame-(current_page_frames()/2));
5183 center_screen (current_page_frames()/2);
5188 if (frame < leftmost_frame) {
5191 if (_session->transport_rolling()) {
5192 /* rolling; end up with the playhead at the right of the page */
5193 l = frame - current_page_frames ();
5195 /* not rolling: end up with the playhead 3/4 of the way along the page */
5196 l = frame - (3 * current_page_frames() / 4);
5203 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5206 if (_session->transport_rolling()) {
5207 /* rolling: end up with the playhead on the left of the page */
5208 center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5210 /* not rolling: end up with the playhead 1/4 of the way along the page */
5211 center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5219 Editor::super_rapid_screen_update ()
5221 if (!_session || !_session->engine().running()) {
5225 /* METERING / MIXER STRIPS */
5227 /* update track meters, if required */
5228 if (is_mapped() && meters_running) {
5229 RouteTimeAxisView* rtv;
5230 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5231 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5232 rtv->fast_update ();
5237 /* and any current mixer strip */
5238 if (current_mixer_strip) {
5239 current_mixer_strip->fast_update ();
5242 /* PLAYHEAD AND VIEWPORT */
5244 framepos_t const frame = _session->audible_frame();
5246 /* There are a few reasons why we might not update the playhead / viewport stuff:
5248 * 1. we don't update things when there's a pending locate request, otherwise
5249 * when the editor requests a locate there is a chance that this method
5250 * will move the playhead before the locate request is processed, causing
5252 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5253 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5256 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5258 last_update_frame = frame;
5260 if (!_dragging_playhead) {
5261 playhead_cursor->set_position (frame);
5264 if (!_stationary_playhead) {
5266 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5267 reset_x_origin_to_follow_playhead ();
5272 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5276 // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5277 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5278 if (target <= 0.0) {
5281 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5282 target = (target * 0.15) + (current * 0.85);
5288 set_horizontal_position (current);
5297 Editor::session_going_away ()
5299 _have_idled = false;
5301 _session_connections.drop_connections ();
5303 super_rapid_screen_update_connection.disconnect ();
5305 selection->clear ();
5306 cut_buffer->clear ();
5308 clicked_regionview = 0;
5309 clicked_axisview = 0;
5310 clicked_routeview = 0;
5311 clicked_crossfadeview = 0;
5312 entered_regionview = 0;
5314 last_update_frame = 0;
5317 playhead_cursor->canvas_item.hide ();
5319 /* rip everything out of the list displays */
5323 _route_groups->clear ();
5325 /* do this first so that deleting a track doesn't reset cms to null
5326 and thus cause a leak.
5329 if (current_mixer_strip) {
5330 if (current_mixer_strip->get_parent() != 0) {
5331 global_hpacker.remove (*current_mixer_strip);
5333 delete current_mixer_strip;
5334 current_mixer_strip = 0;
5337 /* delete all trackviews */
5339 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5342 track_views.clear ();
5344 zoom_range_clock.set_session (0);
5345 nudge_clock.set_session (0);
5347 editor_list_button.set_active(false);
5348 editor_list_button.set_sensitive(false);
5350 /* clear tempo/meter rulers */
5351 remove_metric_marks ();
5353 clear_marker_display ();
5355 delete current_bbt_points;
5356 current_bbt_points = 0;
5358 /* get rid of any existing editor mixer strip */
5360 WindowTitle title(Glib::get_application_name());
5361 title += _("Editor");
5363 set_title (title.get_string());
5365 SessionHandlePtr::session_going_away ();
5370 Editor::show_editor_list (bool yn)
5373 _the_notebook.show ();
5375 _the_notebook.hide ();
5380 Editor::change_region_layering_order ()
5382 framepos_t const position = get_preferred_edit_position ();
5384 if (!clicked_routeview) {
5385 if (layering_order_editor) {
5386 layering_order_editor->hide ();
5391 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
5397 boost::shared_ptr<Playlist> pl = track->playlist();
5403 if (layering_order_editor == 0) {
5404 layering_order_editor = new RegionLayeringOrderEditor(*this);
5407 layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position);
5408 layering_order_editor->maybe_present ();
5412 Editor::update_region_layering_order_editor ()
5414 if (layering_order_editor && layering_order_editor->is_visible ()) {
5415 change_region_layering_order ();
5420 Editor::setup_fade_images ()
5422 _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear")));
5423 _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut")));
5424 _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut")));
5425 _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut")));
5426 _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut")));
5428 _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear")));
5429 _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut")));
5430 _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut")));
5431 _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut")));
5432 _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut")));
5436 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
5438 Editor::action_menu_item (std::string const & name)
5440 Glib::RefPtr<Action> a = editor_actions->get_action (name);
5443 return *manage (a->create_menu_item ());
5447 Editor::resize_text_widgets ()
5449 set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_FUDGE+10, 15);
5450 set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_FUDGE+10, 15);
5451 set_size_request_to_display_given_text (snap_type_selector, snap_type_strings, COMBO_FUDGE+10, 15);
5452 set_size_request_to_display_given_text (snap_mode_selector, snap_mode_strings, COMBO_FUDGE+10, 15);
5453 set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_FUDGE+10, 15);
5457 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
5459 EventBox* b = manage (new EventBox);
5460 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
5461 Label* l = manage (new Label (name));
5465 _the_notebook.append_page (widget, *b);
5469 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
5471 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
5472 _the_notebook.set_current_page (_the_notebook.page_num (*page));
5475 if (ev->type == GDK_2BUTTON_PRESS) {
5477 /* double-click on a notebook tab shrinks or expands the notebook */
5479 if (_notebook_shrunk) {
5480 edit_pane.set_position (pre_maximal_horizontal_pane_position);
5481 _notebook_shrunk = false;
5483 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
5484 edit_pane.set_position (edit_pane.get_position() + page->get_width());
5485 _notebook_shrunk = true;