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"
46 #include "pbd/stacktrace.h"
48 #include <glibmm/miscutils.h>
49 #include <gtkmm/image.h>
50 #include <gdkmm/color.h>
51 #include <gdkmm/bitmap.h>
53 #include "gtkmm2ext/bindings.h"
54 #include "gtkmm2ext/grouped_buttons.h"
55 #include "gtkmm2ext/gtk_ui.h"
56 #include "gtkmm2ext/tearoff.h"
57 #include "gtkmm2ext/utils.h"
58 #include "gtkmm2ext/window_title.h"
59 #include "gtkmm2ext/choice.h"
60 #include "gtkmm2ext/cell_renderer_pixbuf_toggle.h"
62 #include "ardour/audio_track.h"
63 #include "ardour/audioplaylist.h"
64 #include "ardour/audioregion.h"
65 #include "ardour/location.h"
66 #include "ardour/midi_region.h"
67 #include "ardour/plugin_manager.h"
68 #include "ardour/profile.h"
69 #include "ardour/route_group.h"
70 #include "ardour/session_directory.h"
71 #include "ardour/session_route.h"
72 #include "ardour/session_state_utils.h"
73 #include "ardour/tempo.h"
74 #include "ardour/utils.h"
75 #include "ardour/session_playlists.h"
76 #include "ardour/audioengine.h"
78 #include "control_protocol/control_protocol.h"
82 #include "analysis_window.h"
83 #include "audio_clock.h"
84 #include "audio_region_view.h"
85 #include "audio_streamview.h"
86 #include "audio_time_axis.h"
87 #include "automation_time_axis.h"
88 #include "bundle_manager.h"
89 #include "button_joiner.h"
90 #include "canvas-noevent-text.h"
91 #include "canvas_impl.h"
92 #include "crossfade_edit.h"
93 #include "crossfade_view.h"
97 #include "editor_cursors.h"
98 #include "editor_drag.h"
99 #include "editor_group_tabs.h"
100 #include "editor_locations.h"
101 #include "editor_regions.h"
102 #include "editor_route_groups.h"
103 #include "editor_routes.h"
104 #include "editor_snapshots.h"
105 #include "editor_summary.h"
106 #include "global_port_matrix.h"
107 #include "gui_object.h"
108 #include "gui_thread.h"
109 #include "keyboard.h"
111 #include "midi_time_axis.h"
112 #include "mixer_strip.h"
113 #include "mixer_ui.h"
114 #include "mouse_cursors.h"
115 #include "playlist_selector.h"
116 #include "public_editor.h"
117 #include "region_layering_order_editor.h"
118 #include "rgb_macros.h"
119 #include "rhythm_ferret.h"
120 #include "selection.h"
122 #include "simpleline.h"
123 #include "tempo_lines.h"
124 #include "time_axis_view.h"
130 #include "imageframe_socket_handler.h"
134 using namespace ARDOUR;
137 using namespace Glib;
138 using namespace Gtkmm2ext;
139 using namespace Editing;
141 using PBD::internationalize;
143 using Gtkmm2ext::Keyboard;
145 const double Editor::timebar_height = 15.0;
147 static const gchar *_snap_type_strings[] = {
149 N_("Timecode Frames"),
150 N_("Timecode Seconds"),
151 N_("Timecode Minutes"),
179 static const gchar *_snap_mode_strings[] = {
186 static const gchar *_edit_point_strings[] = {
193 static const gchar *_zoom_focus_strings[] = {
203 #ifdef USE_RUBBERBAND
204 static const gchar *_rb_opt_strings[] = {
207 N_("Balanced multitimbral mixture"),
208 N_("Unpitched percussion with stable notes"),
209 N_("Crisp monophonic instrumental"),
210 N_("Unpitched solo percussion"),
211 N_("Resample without preserving pitch"),
217 show_me_the_size (Requisition* r, const char* what)
219 cerr << "size of " << what << " = " << r->width << " x " << r->height << endl;
224 pane_size_watcher (Paned* pane)
226 /* if the handle of a pane vanishes into (at least) the tabs of a notebook,
227 it is no longer accessible. so stop that. this doesn't happen on X11,
228 just the quartz backend.
233 int max_width_of_lhs = GTK_WIDGET(pane->gobj())->allocation.width - 25;
235 gint pos = pane->get_position ();
237 if (pos > max_width_of_lhs) {
238 pane->set_position (max_width_of_lhs);
244 : _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
246 /* time display buttons */
247 , minsec_label (_("Mins:Secs"))
248 , bbt_label (_("Bars:Beats"))
249 , timecode_label (_("Timecode"))
250 , samples_label (_("Samples"))
251 , tempo_label (_("Tempo"))
252 , meter_label (_("Meter"))
253 , mark_label (_("Location Markers"))
254 , range_mark_label (_("Range Markers"))
255 , transport_mark_label (_("Loop/Punch Ranges"))
256 , cd_mark_label (_("CD Markers"))
257 , edit_packer (4, 4, true)
259 /* the values here don't matter: layout widgets
260 reset them as needed.
263 , vertical_adjustment (0.0, 0.0, 10.0, 400.0)
265 /* tool bar related */
267 , zoom_range_clock (new AudioClock (X_("zoomrange"), false, X_("zoom range"), true, false, true))
269 , toolbar_selection_clock_table (2,3)
271 , automation_mode_button (_("mode"))
273 , _toolbar_viewport (*manage (new Gtk::Adjustment (0, 0, 1e10)), *manage (new Gtk::Adjustment (0, 0, 1e10)))
276 , image_socket_listener(0)
281 , nudge_clock (new AudioClock (X_("nudge"), false, X_("nudge"), true, false, true))
282 , meters_running(false)
283 , _pending_locate_request (false)
284 , _pending_initial_locate (false)
285 , _last_cut_copy_source_track (0)
287 , _region_selection_change_updates_region_list (true)
288 , _following_mixer_selection (false)
292 /* we are a singleton */
294 PublicEditor::_instance = this;
298 selection = new Selection (this);
299 cut_buffer = new Selection (this);
301 clicked_regionview = 0;
302 clicked_axisview = 0;
303 clicked_routeview = 0;
304 clicked_crossfadeview = 0;
305 clicked_control_point = 0;
306 last_update_frame = 0;
307 pre_press_cursor = 0;
308 _drags = new DragManager (this);
309 current_mixer_strip = 0;
312 snap_type_strings = I18N (_snap_type_strings);
313 snap_mode_strings = I18N (_snap_mode_strings);
314 zoom_focus_strings = I18N (_zoom_focus_strings);
315 edit_point_strings = I18N (_edit_point_strings);
316 #ifdef USE_RUBBERBAND
317 rb_opt_strings = I18N (_rb_opt_strings);
321 snap_threshold = 5.0;
322 bbt_beat_subdivision = 4;
325 last_autoscroll_x = 0;
326 last_autoscroll_y = 0;
327 autoscroll_active = false;
328 autoscroll_timeout_tag = -1;
333 current_interthread_info = 0;
334 _show_measures = true;
336 show_gain_after_trim = false;
338 have_pending_keyboard_selection = false;
339 _follow_playhead = true;
340 _stationary_playhead = false;
341 _xfade_visibility = true;
342 editor_ruler_menu = 0;
343 no_ruler_shown_update = false;
345 range_marker_menu = 0;
346 marker_menu_item = 0;
347 tempo_or_meter_marker_menu = 0;
348 transport_marker_menu = 0;
349 new_transport_marker_menu = 0;
350 editor_mixer_strip_width = Wide;
351 show_editor_mixer_when_tracks_arrive = false;
352 region_edit_menu_split_multichannel_item = 0;
353 region_edit_menu_split_item = 0;
356 current_stepping_trackview = 0;
358 entered_regionview = 0;
360 clear_entered_track = false;
363 button_release_can_deselect = true;
364 _dragging_playhead = false;
365 _dragging_edit_point = false;
366 select_new_marker = false;
368 layering_order_editor = 0;
369 no_save_visual = false;
372 scrubbing_direction = 0;
376 location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get();
377 location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get();
378 location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get();
379 location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get();
380 location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get();
382 _edit_point = EditAtMouse;
383 _internal_editing = false;
384 current_canvas_cursor = 0;
386 frames_per_unit = 2048; /* too early to use reset_zoom () */
388 _scroll_callbacks = 0;
390 zoom_focus = ZoomFocusLeft;
391 set_zoom_focus (ZoomFocusLeft);
392 zoom_range_clock->ValueChanged.connect (sigc::mem_fun(*this, &Editor::zoom_adjustment_changed));
394 bbt_label.set_name ("EditorTimeButton");
395 bbt_label.set_size_request (-1, (int)timebar_height);
396 bbt_label.set_alignment (1.0, 0.5);
397 bbt_label.set_padding (5,0);
399 bbt_label.set_no_show_all();
400 minsec_label.set_name ("EditorTimeButton");
401 minsec_label.set_size_request (-1, (int)timebar_height);
402 minsec_label.set_alignment (1.0, 0.5);
403 minsec_label.set_padding (5,0);
404 minsec_label.hide ();
405 minsec_label.set_no_show_all();
406 timecode_label.set_name ("EditorTimeButton");
407 timecode_label.set_size_request (-1, (int)timebar_height);
408 timecode_label.set_alignment (1.0, 0.5);
409 timecode_label.set_padding (5,0);
410 timecode_label.hide ();
411 timecode_label.set_no_show_all();
412 samples_label.set_name ("EditorTimeButton");
413 samples_label.set_size_request (-1, (int)timebar_height);
414 samples_label.set_alignment (1.0, 0.5);
415 samples_label.set_padding (5,0);
416 samples_label.hide ();
417 samples_label.set_no_show_all();
419 tempo_label.set_name ("EditorTimeButton");
420 tempo_label.set_size_request (-1, (int)timebar_height);
421 tempo_label.set_alignment (1.0, 0.5);
422 tempo_label.set_padding (5,0);
424 tempo_label.set_no_show_all();
426 meter_label.set_name ("EditorTimeButton");
427 meter_label.set_size_request (-1, (int)timebar_height);
428 meter_label.set_alignment (1.0, 0.5);
429 meter_label.set_padding (5,0);
431 meter_label.set_no_show_all();
433 mark_label.set_name ("EditorTimeButton");
434 mark_label.set_size_request (-1, (int)timebar_height);
435 mark_label.set_alignment (1.0, 0.5);
436 mark_label.set_padding (5,0);
438 mark_label.set_no_show_all();
440 cd_mark_label.set_name ("EditorTimeButton");
441 cd_mark_label.set_size_request (-1, (int)timebar_height);
442 cd_mark_label.set_alignment (1.0, 0.5);
443 cd_mark_label.set_padding (5,0);
444 cd_mark_label.hide();
445 cd_mark_label.set_no_show_all();
447 range_mark_label.set_name ("EditorTimeButton");
448 range_mark_label.set_size_request (-1, (int)timebar_height);
449 range_mark_label.set_alignment (1.0, 0.5);
450 range_mark_label.set_padding (5,0);
451 range_mark_label.hide();
452 range_mark_label.set_no_show_all();
454 transport_mark_label.set_name ("EditorTimeButton");
455 transport_mark_label.set_size_request (-1, (int)timebar_height);
456 transport_mark_label.set_alignment (1.0, 0.5);
457 transport_mark_label.set_padding (5,0);
458 transport_mark_label.hide();
459 transport_mark_label.set_no_show_all();
461 initialize_rulers ();
462 initialize_canvas ();
464 _summary = new EditorSummary (this);
466 selection->TimeChanged.connect (sigc::mem_fun(*this, &Editor::time_selection_changed));
467 selection->TracksChanged.connect (sigc::mem_fun(*this, &Editor::track_selection_changed));
469 editor_regions_selection_changed_connection = selection->RegionsChanged.connect (sigc::mem_fun(*this, &Editor::region_selection_changed));
471 selection->PointsChanged.connect (sigc::mem_fun(*this, &Editor::point_selection_changed));
472 selection->MarkersChanged.connect (sigc::mem_fun(*this, &Editor::marker_selection_changed));
474 edit_controls_vbox.set_spacing (0);
475 vertical_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &Editor::tie_vertical_scrolling), true);
476 track_canvas->signal_map_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_map_handler));
478 HBox* h = manage (new HBox);
479 _group_tabs = new EditorGroupTabs (this);
480 h->pack_start (*_group_tabs, PACK_SHRINK);
481 h->pack_start (edit_controls_vbox);
482 controls_layout.add (*h);
484 controls_layout.set_name ("EditControlsBase");
485 controls_layout.add_events (Gdk::SCROLL_MASK);
486 controls_layout.signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::control_layout_scroll), false);
488 controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
489 controls_layout.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_release));
491 _cursors = new MouseCursors;
493 ArdourCanvas::Canvas* time_pad = manage(new ArdourCanvas::Canvas());
494 ArdourCanvas::SimpleLine* pad_line_1 = manage(new ArdourCanvas::SimpleLine(*time_pad->root(),
495 0.0, 1.0, 100.0, 1.0));
497 pad_line_1->property_color_rgba() = 0xFF0000FF;
502 time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2);
503 time_canvas_vbox.set_size_request (-1, -1);
505 ruler_label_event_box.add (ruler_label_vbox);
506 ruler_label_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
507 ruler_label_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
509 time_button_event_box.add (time_button_vbox);
510 time_button_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
511 time_button_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
513 /* these enable us to have a dedicated window (for cursor setting, etc.)
514 for the canvas areas.
517 track_canvas_event_box.add (*track_canvas);
519 time_canvas_event_box.add (time_canvas_vbox);
520 time_canvas_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
522 edit_packer.set_col_spacings (0);
523 edit_packer.set_row_spacings (0);
524 edit_packer.set_homogeneous (false);
525 edit_packer.set_border_width (0);
526 edit_packer.set_name ("EditorWindow");
528 /* labels for the rulers */
529 edit_packer.attach (ruler_label_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0);
530 /* labels for the marker "tracks" */
531 edit_packer.attach (time_button_event_box, 1, 2, 1, 2, FILL, SHRINK, 0, 0);
533 edit_packer.attach (time_canvas_event_box, 2, 3, 0, 1, FILL|EXPAND, FILL, 0, 0);
535 edit_packer.attach (controls_layout, 0, 2, 2, 3, FILL, FILL|EXPAND, 0, 0);
537 edit_packer.attach (track_canvas_event_box, 2, 3, 1, 3, FILL|EXPAND, FILL|EXPAND, 0, 0);
539 bottom_hbox.set_border_width (2);
540 bottom_hbox.set_spacing (3);
542 _route_groups = new EditorRouteGroups (this);
543 _routes = new EditorRoutes (this);
544 _regions = new EditorRegions (this);
545 _snapshots = new EditorSnapshots (this);
546 _locations = new EditorLocations (this);
548 add_notebook_page (_("Regions"), _regions->widget ());
549 add_notebook_page (_("Tracks & Busses"), _routes->widget ());
550 add_notebook_page (_("Snapshots"), _snapshots->widget ());
551 add_notebook_page (_("Route Groups"), _route_groups->widget ());
552 add_notebook_page (_("Ranges & Marks"), _locations->widget ());
554 _the_notebook.set_show_tabs (true);
555 _the_notebook.set_scrollable (true);
556 _the_notebook.popup_disable ();
557 _the_notebook.set_tab_pos (Gtk::POS_RIGHT);
558 _the_notebook.show_all ();
560 _notebook_shrunk = false;
562 editor_summary_pane.pack1(edit_packer);
564 Button* summary_arrows_left_left = manage (new Button);
565 summary_arrows_left_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
566 summary_arrows_left_left->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), LEFT)));
567 summary_arrows_left_left->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
569 Button* summary_arrows_left_right = manage (new Button);
570 summary_arrows_left_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
571 summary_arrows_left_right->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), RIGHT)));
572 summary_arrows_left_right->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
574 VBox* summary_arrows_left = manage (new VBox);
575 summary_arrows_left->pack_start (*summary_arrows_left_left);
576 summary_arrows_left->pack_start (*summary_arrows_left_right);
578 Button* summary_arrows_right_up = manage (new Button);
579 summary_arrows_right_up->add (*manage (new Arrow (ARROW_UP, SHADOW_NONE)));
580 summary_arrows_right_up->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), UP)));
581 summary_arrows_right_up->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
583 Button* summary_arrows_right_down = manage (new Button);
584 summary_arrows_right_down->add (*manage (new Arrow (ARROW_DOWN, SHADOW_NONE)));
585 summary_arrows_right_down->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), DOWN)));
586 summary_arrows_right_down->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
588 VBox* summary_arrows_right = manage (new VBox);
589 summary_arrows_right->pack_start (*summary_arrows_right_up);
590 summary_arrows_right->pack_start (*summary_arrows_right_down);
592 Frame* summary_frame = manage (new Frame);
593 summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN);
595 summary_frame->add (*_summary);
596 summary_frame->show ();
598 _summary_hbox.pack_start (*summary_arrows_left, false, false);
599 _summary_hbox.pack_start (*summary_frame, true, true);
600 _summary_hbox.pack_start (*summary_arrows_right, false, false);
602 editor_summary_pane.pack2 (_summary_hbox);
604 edit_pane.pack1 (editor_summary_pane, true, true);
605 edit_pane.pack2 (_the_notebook, false, true);
607 editor_summary_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun (*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&editor_summary_pane)));
609 /* XXX: editor_summary_pane might need similar special OS X treatment to the edit_pane */
611 edit_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&edit_pane)));
613 Glib::PropertyProxy<int> proxy = edit_pane.property_position();
614 proxy.signal_changed().connect (bind (sigc::ptr_fun (pane_size_watcher), static_cast<Paned*> (&edit_pane)));
616 top_hbox.pack_start (toolbar_frame);
618 HBox *hbox = manage (new HBox);
619 hbox->pack_start (edit_pane, true, true);
621 global_vpacker.pack_start (top_hbox, false, false);
622 global_vpacker.pack_start (*hbox, true, true);
624 global_hpacker.pack_start (global_vpacker, true, true);
626 set_name ("EditorWindow");
627 add_accel_group (ActionManager::ui_manager->get_accel_group());
629 status_bar_hpacker.show ();
631 vpacker.pack_end (status_bar_hpacker, false, false);
632 vpacker.pack_end (global_hpacker, true, true);
634 /* register actions now so that set_state() can find them and set toggles/checks etc */
637 /* when we start using our own keybinding system for the editor, this
638 * will be uncommented
644 _snap_type = SnapToBeat;
645 set_snap_to (_snap_type);
646 _snap_mode = SnapOff;
647 set_snap_mode (_snap_mode);
648 set_mouse_mode (MouseObject, true);
649 pre_internal_mouse_mode = MouseObject;
650 set_edit_point_preference (EditAtMouse, true);
652 _playlist_selector = new PlaylistSelector();
653 _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
655 RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), ui_bind (&Editor::catch_vanishing_regionview, this, _1), gui_context());
659 nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
660 nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
662 nudge_forward_button.set_name ("TransportButton");
663 nudge_backward_button.set_name ("TransportButton");
665 fade_context_menu.set_name ("ArdourContextMenu");
667 /* icons, titles, WM stuff */
669 list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
670 Glib::RefPtr<Gdk::Pixbuf> icon;
672 if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
673 window_icons.push_back (icon);
675 if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
676 window_icons.push_back (icon);
678 if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
679 window_icons.push_back (icon);
681 if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
682 window_icons.push_back (icon);
684 if (!window_icons.empty()) {
685 // set_icon_list (window_icons);
686 set_default_icon_list (window_icons);
689 WindowTitle title(Glib::get_application_name());
690 title += _("Editor");
691 set_title (title.get_string());
692 set_wmclass (X_("ardour_editor"), PROGRAM_NAME);
695 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
697 signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
698 signal_delete_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
700 /* allow external control surfaces/protocols to do various things */
702 ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context());
703 ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context());
704 ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context());
705 ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), ui_bind (&Editor::control_scroll, this, _1), gui_context());
706 ControlProtocol::SelectByRID.connect (*this, invalidator (*this), ui_bind (&Editor::control_select, this, _1), gui_context());
707 BasicUI::AccessAction.connect (*this, invalidator (*this), ui_bind (&Editor::access_action, this, _1, _2), gui_context());
709 /* problematic: has to return a value and thus cannot be x-thread */
711 Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
713 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
715 TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
717 _ignore_region_action = false;
718 _last_region_menu_was_main = false;
719 _popup_region_menu_item = 0;
721 _show_marker_lines = false;
722 _over_region_trim_target = false;
724 /* Button bindings */
726 button_bindings = new Bindings;
728 XMLNode* node = button_settings();
730 for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) {
731 button_bindings->load (**i);
738 setup_fade_images ();
744 if(image_socket_listener) {
745 if(image_socket_listener->is_connected())
747 image_socket_listener->close_connection() ;
750 delete image_socket_listener ;
751 image_socket_listener = 0 ;
755 delete button_bindings;
757 delete _route_groups;
763 Editor::button_settings () const
765 XMLNode* settings = ARDOUR_UI::instance()->editor_settings();
766 XMLNode* node = find_named_node (*settings, X_("Buttons"));
769 cerr << "new empty Button node\n";
770 node = new XMLNode (X_("Buttons"));
777 Editor::add_toplevel_controls (Container& cont)
779 vpacker.pack_start (cont, false, false);
784 Editor::catch_vanishing_regionview (RegionView *rv)
786 /* note: the selection will take care of the vanishing
787 audioregionview by itself.
790 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
794 if (clicked_regionview == rv) {
795 clicked_regionview = 0;
798 if (entered_regionview == rv) {
799 set_entered_regionview (0);
802 if (!_all_region_actions_sensitized) {
803 sensitize_all_region_actions (true);
806 _over_region_trim_target = false;
810 Editor::set_entered_regionview (RegionView* rv)
812 if (rv == entered_regionview) {
816 if (entered_regionview) {
817 entered_regionview->exited ();
820 if ((entered_regionview = rv) != 0) {
821 entered_regionview->entered (internal_editing ());
824 if (!_all_region_actions_sensitized && _last_region_menu_was_main) {
825 /* This RegionView entry might have changed what region actions
826 are allowed, so sensitize them all in case a key is pressed.
828 sensitize_all_region_actions (true);
833 Editor::set_entered_track (TimeAxisView* tav)
836 entered_track->exited ();
839 if ((entered_track = tav) != 0) {
840 entered_track->entered ();
845 Editor::show_window ()
847 if (!is_visible ()) {
850 /* XXX: this is a bit unfortunate; it would probably
851 be nicer if we could just call show () above rather
852 than needing the show_all ()
855 /* re-hide stuff if necessary */
856 editor_list_button_toggled ();
857 parameter_changed ("show-summary");
858 parameter_changed ("show-group-tabs");
859 parameter_changed ("show-zoom-tools");
861 /* now reset all audio_time_axis heights, because widgets might need
867 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
868 tv = (static_cast<TimeAxisView*>(*i));
872 if (current_mixer_strip) {
873 current_mixer_strip->hide_things ();
874 current_mixer_strip->parameter_changed ("mixer-strip-visibility");
882 Editor::instant_save ()
884 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
889 _session->add_instant_xml(get_state());
891 Config->add_instant_xml(get_state());
896 Editor::zoom_adjustment_changed ()
902 double fpu = zoom_range_clock->current_duration() / _canvas_width;
906 zoom_range_clock->set ((framepos_t) floor (fpu * _canvas_width));
907 } else if (fpu > _session->current_end_frame() / _canvas_width) {
908 fpu = _session->current_end_frame() / _canvas_width;
909 zoom_range_clock->set ((framepos_t) floor (fpu * _canvas_width));
916 Editor::control_select (uint32_t rid)
918 /* handles the (static) signal from the ControlProtocol class that
919 * requests setting the selected track to a given RID
926 boost::shared_ptr<Route> r = _session->route_by_remote_id (rid);
932 TimeAxisView* tav = axis_view_from_route (r);
935 selection->set (tav);
937 selection->clear_tracks ();
942 Editor::control_scroll (float fraction)
944 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
950 double step = fraction * current_page_frames();
953 _control_scroll_target is an optional<T>
955 it acts like a pointer to an framepos_t, with
956 a operator conversion to boolean to check
957 that it has a value could possibly use
958 playhead_cursor->current_frame to store the
959 value and a boolean in the class to know
960 when it's out of date
963 if (!_control_scroll_target) {
964 _control_scroll_target = _session->transport_frame();
965 _dragging_playhead = true;
968 if ((fraction < 0.0f) && (*_control_scroll_target < (framepos_t) fabs(step))) {
969 *_control_scroll_target = 0;
970 } else if ((fraction > 0.0f) && (max_framepos - *_control_scroll_target < step)) {
971 *_control_scroll_target = max_framepos - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
973 *_control_scroll_target += (framepos_t) floor (step);
976 /* move visuals, we'll catch up with it later */
978 playhead_cursor->set_position (*_control_scroll_target);
979 UpdateAllTransportClocks (*_control_scroll_target);
981 if (*_control_scroll_target > (current_page_frames() / 2)) {
982 /* try to center PH in window */
983 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
989 Now we do a timeout to actually bring the session to the right place
990 according to the playhead. This is to avoid reading disk buffers on every
991 call to control_scroll, which is driven by ScrollTimeline and therefore
992 probably by a control surface wheel which can generate lots of events.
994 /* cancel the existing timeout */
996 control_scroll_connection.disconnect ();
998 /* add the next timeout */
1000 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
1004 Editor::deferred_control_scroll (framepos_t /*target*/)
1006 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
1007 // reset for next stream
1008 _control_scroll_target = boost::none;
1009 _dragging_playhead = false;
1014 Editor::access_action (std::string action_group, std::string action_item)
1020 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
1023 act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
1031 Editor::on_realize ()
1033 Window::on_realize ();
1038 Editor::map_position_change (framepos_t frame)
1040 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, frame)
1042 if (_session == 0) {
1046 if (_follow_playhead) {
1047 center_screen (frame);
1050 playhead_cursor->set_position (frame);
1054 Editor::center_screen (framepos_t frame)
1056 double page = _canvas_width * frames_per_unit;
1058 /* if we're off the page, then scroll.
1061 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
1062 center_screen_internal (frame, page);
1067 Editor::center_screen_internal (framepos_t frame, float page)
1072 frame -= (framepos_t) page;
1077 reset_x_origin (frame);
1082 Editor::update_title ()
1084 ENSURE_GUI_THREAD (*this, &Editor::update_title)
1087 bool dirty = _session->dirty();
1089 string session_name;
1091 if (_session->snap_name() != _session->name()) {
1092 session_name = _session->snap_name();
1094 session_name = _session->name();
1098 session_name = "*" + session_name;
1101 WindowTitle title(session_name);
1102 title += Glib::get_application_name();
1103 set_title (title.get_string());
1108 Editor::set_session (Session *t)
1110 SessionHandlePtr::set_session (t);
1116 zoom_range_clock->set_session (_session);
1117 _playlist_selector->set_session (_session);
1118 nudge_clock->set_session (_session);
1119 _summary->set_session (_session);
1120 _group_tabs->set_session (_session);
1121 _route_groups->set_session (_session);
1122 _regions->set_session (_session);
1123 _snapshots->set_session (_session);
1124 _routes->set_session (_session);
1125 _locations->set_session (_session);
1127 if (rhythm_ferret) {
1128 rhythm_ferret->set_session (_session);
1131 if (analysis_window) {
1132 analysis_window->set_session (_session);
1136 sfbrowser->set_session (_session);
1139 compute_fixed_ruler_scale ();
1141 /* Make sure we have auto loop and auto punch ranges */
1143 Location* loc = _session->locations()->auto_loop_location();
1145 loc = new Location (*_session, 0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1147 if (loc->start() == loc->end()) {
1148 loc->set_end (loc->start() + 1);
1151 _session->locations()->add (loc, false);
1152 _session->set_auto_loop_location (loc);
1155 loc->set_name (_("Loop"));
1158 loc = _session->locations()->auto_punch_location();
1161 loc = new Location (*_session, 0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1163 if (loc->start() == loc->end()) {
1164 loc->set_end (loc->start() + 1);
1167 _session->locations()->add (loc, false);
1168 _session->set_auto_punch_location (loc);
1171 loc->set_name (_("Punch"));
1174 refresh_location_display ();
1176 /* This must happen after refresh_location_display(), as (amongst other things) we restore
1177 the selected Marker; this needs the LocationMarker list to be available.
1179 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1180 set_state (*node, Stateful::loading_state_version);
1182 /* catch up with the playhead */
1184 _session->request_locate (playhead_cursor->current_frame);
1185 _pending_initial_locate = true;
1189 /* These signals can all be emitted by a non-GUI thread. Therefore the
1190 handlers for them must not attempt to directly interact with the GUI,
1191 but use Gtkmm2ext::UI::instance()->call_slot();
1194 _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), ui_bind(&Editor::step_edit_status_change, this, _1), gui_context());
1195 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1196 _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context());
1197 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context());
1198 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1199 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context());
1200 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1201 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
1202 _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Editor::session_state_saved, this, _1), gui_context());
1203 _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context());
1204 _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&Editor::location_gone, this, _1), gui_context());
1205 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1206 _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display, this), gui_context());
1207 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1209 if (Profile->get_sae()) {
1210 Timecode::BBT_Time bbt;
1214 framepos_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1);
1215 nudge_clock->set_mode(AudioClock::BBT);
1216 nudge_clock->set (pos, true);
1219 nudge_clock->set_mode (AudioClock::Timecode);
1220 nudge_clock->set (_session->frame_rate() * 5, true);
1223 playhead_cursor->canvas_item.show ();
1225 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1226 Config->map_parameters (pc);
1227 _session->config.map_parameters (pc);
1229 restore_ruler_visibility ();
1230 //tempo_map_changed (PropertyChange (0));
1231 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1233 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1234 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1237 super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
1238 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1241 switch (_snap_type) {
1242 case SnapToRegionStart:
1243 case SnapToRegionEnd:
1244 case SnapToRegionSync:
1245 case SnapToRegionBoundary:
1246 build_region_boundary_cache ();
1253 /* register for undo history */
1254 _session->register_with_memento_command_factory(id(), this);
1256 ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated));
1258 start_updating_meters ();
1262 Editor::action_pre_activated (Glib::RefPtr<Action> const & a)
1264 if (a->get_name() == "RegionMenu") {
1265 /* When the main menu's region menu is opened, we setup the actions so that they look right
1266 in the menu. I can't find a way of getting a signal when this menu is subsequently closed,
1267 so we resensitize all region actions when the entered regionview or the region selection
1268 changes. HOWEVER we can't always resensitize on entered_regionview change because that
1269 happens after the region context menu is opened. So we set a flag here, too.
1273 sensitize_the_right_region_actions ();
1274 _last_region_menu_was_main = true;
1278 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1280 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1282 using namespace Menu_Helpers;
1283 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1286 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1290 MenuList& items (fade_context_menu.items());
1294 switch (item_type) {
1296 case FadeInHandleItem:
1297 if (arv->audio_region()->fade_in_active()) {
1298 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1300 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1303 items.push_back (SeparatorElem());
1305 if (Profile->get_sae()) {
1307 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)));
1308 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)));
1315 *_fade_in_images[FadeLinear],
1316 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)
1320 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1325 *_fade_in_images[FadeFast],
1326 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)
1329 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1334 *_fade_in_images[FadeLogB],
1335 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogB)
1338 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1343 *_fade_in_images[FadeLogA],
1344 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogA)
1347 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1352 *_fade_in_images[FadeSlow],
1353 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow)
1356 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1362 case FadeOutHandleItem:
1363 if (arv->audio_region()->fade_out_active()) {
1364 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1366 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1369 items.push_back (SeparatorElem());
1371 if (Profile->get_sae()) {
1372 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)));
1373 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)));
1379 *_fade_out_images[FadeLinear],
1380 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)
1384 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1389 *_fade_out_images[FadeFast],
1390 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)
1393 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1398 *_fade_out_images[FadeLogB],
1399 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogA)
1402 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1407 *_fade_out_images[FadeLogA],
1408 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogB)
1411 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1416 *_fade_out_images[FadeSlow],
1417 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast)
1420 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1426 fatal << _("programming error: ")
1427 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1432 fade_context_menu.popup (button, time);
1436 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1438 using namespace Menu_Helpers;
1439 Menu* (Editor::*build_menu_function)();
1442 switch (item_type) {
1444 case RegionViewName:
1445 case RegionViewNameHighlight:
1446 case LeftFrameHandle:
1447 case RightFrameHandle:
1448 if (with_selection) {
1449 build_menu_function = &Editor::build_track_selection_context_menu;
1451 build_menu_function = &Editor::build_track_region_context_menu;
1456 if (with_selection) {
1457 build_menu_function = &Editor::build_track_selection_context_menu;
1459 build_menu_function = &Editor::build_track_context_menu;
1463 case CrossfadeViewItem:
1464 build_menu_function = &Editor::build_track_crossfade_context_menu;
1468 if (clicked_routeview->track()) {
1469 build_menu_function = &Editor::build_track_context_menu;
1471 build_menu_function = &Editor::build_track_bus_context_menu;
1476 /* probably shouldn't happen but if it does, we don't care */
1480 menu = (this->*build_menu_function)();
1481 menu->set_name ("ArdourContextMenu");
1483 /* now handle specific situations */
1485 switch (item_type) {
1487 case RegionViewName:
1488 case RegionViewNameHighlight:
1489 case LeftFrameHandle:
1490 case RightFrameHandle:
1491 if (!with_selection) {
1492 if (region_edit_menu_split_item) {
1493 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1494 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1496 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1499 if (region_edit_menu_split_multichannel_item) {
1500 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1501 region_edit_menu_split_multichannel_item->set_sensitive (true);
1503 region_edit_menu_split_multichannel_item->set_sensitive (false);
1512 case CrossfadeViewItem:
1519 /* probably shouldn't happen but if it does, we don't care */
1523 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1525 /* Bounce to disk */
1527 using namespace Menu_Helpers;
1528 MenuList& edit_items = menu->items();
1530 edit_items.push_back (SeparatorElem());
1532 switch (clicked_routeview->audio_track()->freeze_state()) {
1533 case AudioTrack::NoFreeze:
1534 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1537 case AudioTrack::Frozen:
1538 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1541 case AudioTrack::UnFrozen:
1542 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1548 if (item_type == StreamItem && clicked_routeview) {
1549 clicked_routeview->build_underlay_menu(menu);
1552 /* When the region menu is opened, we setup the actions so that they look right
1555 sensitize_the_right_region_actions ();
1556 _last_region_menu_was_main = false;
1558 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1559 menu->popup (button, time);
1563 Editor::build_track_context_menu ()
1565 using namespace Menu_Helpers;
1567 MenuList& edit_items = track_context_menu.items();
1570 add_dstream_context_items (edit_items);
1571 return &track_context_menu;
1575 Editor::build_track_bus_context_menu ()
1577 using namespace Menu_Helpers;
1579 MenuList& edit_items = track_context_menu.items();
1582 add_bus_context_items (edit_items);
1583 return &track_context_menu;
1587 Editor::build_track_region_context_menu ()
1589 using namespace Menu_Helpers;
1590 MenuList& edit_items = track_region_context_menu.items();
1593 /* we've just cleared the track region context menu, so the menu that these
1594 two items were on will have disappeared; stop them dangling.
1596 region_edit_menu_split_item = 0;
1597 region_edit_menu_split_multichannel_item = 0;
1599 /* we might try to use items that are currently attached to a crossfade menu,
1602 track_crossfade_context_menu.items().clear ();
1604 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1607 boost::shared_ptr<Track> tr;
1608 boost::shared_ptr<Playlist> pl;
1610 if ((tr = rtv->track())) {
1611 add_region_context_items (edit_items, tr);
1615 add_dstream_context_items (edit_items);
1617 return &track_region_context_menu;
1621 Editor::build_track_crossfade_context_menu ()
1623 using namespace Menu_Helpers;
1624 MenuList& edit_items = track_crossfade_context_menu.items();
1625 edit_items.clear ();
1627 /* we might try to use items that are currently attached to a crossfade menu,
1630 track_region_context_menu.items().clear ();
1632 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1635 boost::shared_ptr<Track> tr;
1636 boost::shared_ptr<Playlist> pl;
1637 boost::shared_ptr<AudioPlaylist> apl;
1639 if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1641 AudioPlaylist::Crossfades xfades;
1645 /* The xfade menu is a bit of a special case, as we always use the mouse position
1646 to decide whether or not to display it (rather than the edit point). No particularly
1647 strong reasons for this, other than it is a bit surprising to right-click on a xfade
1650 mouse_frame (where, ignored);
1651 apl->crossfades_at (where, xfades);
1653 bool const many = xfades.size() > 1;
1655 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1656 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1659 add_region_context_items (edit_items, tr);
1663 add_dstream_context_items (edit_items);
1665 return &track_crossfade_context_menu;
1669 Editor::analyze_region_selection ()
1671 if (analysis_window == 0) {
1672 analysis_window = new AnalysisWindow();
1675 analysis_window->set_session(_session);
1677 analysis_window->show_all();
1680 analysis_window->set_regionmode();
1681 analysis_window->analyze();
1683 analysis_window->present();
1687 Editor::analyze_range_selection()
1689 if (analysis_window == 0) {
1690 analysis_window = new AnalysisWindow();
1693 analysis_window->set_session(_session);
1695 analysis_window->show_all();
1698 analysis_window->set_rangemode();
1699 analysis_window->analyze();
1701 analysis_window->present();
1705 Editor::build_track_selection_context_menu ()
1707 using namespace Menu_Helpers;
1708 MenuList& edit_items = track_selection_context_menu.items();
1709 edit_items.clear ();
1711 add_selection_context_items (edit_items);
1712 // edit_items.push_back (SeparatorElem());
1713 // add_dstream_context_items (edit_items);
1715 return &track_selection_context_menu;
1718 /** Add context menu items relevant to crossfades.
1719 * @param edit_items List to add the items to.
1722 Editor::add_crossfade_context_items (AudioStreamView* view, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1724 using namespace Menu_Helpers;
1725 Menu *xfade_menu = manage (new Menu);
1726 MenuList& items = xfade_menu->items();
1727 xfade_menu->set_name ("ArdourContextMenu");
1730 if (xfade->active()) {
1737 MenuElem (str, sigc::bind (sigc::mem_fun (*this, &Editor::toggle_xfade_active), &view->trackview(), boost::weak_ptr<Crossfade> (xfade)))
1741 MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun (*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade)))
1744 if (xfade->can_follow_overlap()) {
1746 if (xfade->following_overlap()) {
1747 str = _("Convert to Short");
1749 str = _("Convert to Full");
1753 MenuElem (str, sigc::bind (sigc::mem_fun (*this, &Editor::toggle_xfade_length), &view->trackview(), xfade))
1758 str = xfade->out()->name();
1760 str += xfade->in()->name();
1762 str = _("Crossfade");
1765 edit_items.push_back (MenuElem (str, *xfade_menu));
1766 edit_items.push_back (SeparatorElem());
1770 Editor::xfade_edit_left_region ()
1772 if (clicked_crossfadeview) {
1773 clicked_crossfadeview->left_view.show_region_editor ();
1778 Editor::xfade_edit_right_region ()
1780 if (clicked_crossfadeview) {
1781 clicked_crossfadeview->right_view.show_region_editor ();
1786 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::shared_ptr<Track> track)
1788 using namespace Menu_Helpers;
1790 /* OK, stick the region submenu at the top of the list, and then add
1794 RegionSelection rs = get_regions_from_selection_and_entered ();
1796 string::size_type pos = 0;
1797 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1799 /* we have to hack up the region name because "_" has a special
1800 meaning for menu titles.
1803 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1804 menu_item_name.replace (pos, 1, "__");
1808 if (_popup_region_menu_item == 0) {
1809 _popup_region_menu_item = new MenuItem (menu_item_name);
1810 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1811 _popup_region_menu_item->show ();
1813 _popup_region_menu_item->set_label (menu_item_name);
1816 const framepos_t position = get_preferred_edit_position (false, true);
1818 edit_items.push_back (*_popup_region_menu_item);
1819 if (track->playlist()->count_regions_at (position) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1820 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region-context-menu")->create_menu_item ()));
1822 edit_items.push_back (SeparatorElem());
1825 /** Add context menu items relevant to selection ranges.
1826 * @param edit_items List to add the items to.
1829 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1831 using namespace Menu_Helpers;
1833 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1834 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1836 edit_items.push_back (SeparatorElem());
1837 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1839 edit_items.push_back (SeparatorElem());
1841 edit_items.push_back (
1843 _("Move Range Start to Previous Region Boundary"),
1844 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, false)
1848 edit_items.push_back (
1850 _("Move Range Start to Next Region Boundary"),
1851 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, true)
1855 edit_items.push_back (
1857 _("Move Range End to Previous Region Boundary"),
1858 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, false)
1862 edit_items.push_back (
1864 _("Move Range End to Next Region Boundary"),
1865 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, true)
1869 edit_items.push_back (SeparatorElem());
1870 edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection)));
1871 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1873 edit_items.push_back (SeparatorElem());
1874 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1876 edit_items.push_back (SeparatorElem());
1877 edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1878 edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1880 edit_items.push_back (SeparatorElem());
1881 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1883 edit_items.push_back (SeparatorElem());
1884 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1885 edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1886 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1888 edit_items.push_back (SeparatorElem());
1889 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1890 edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1891 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1892 edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1893 edit_items.push_back (MenuElem (_("Export Range..."), sigc::mem_fun(*this, &Editor::export_selection)));
1898 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1900 using namespace Menu_Helpers;
1904 Menu *play_menu = manage (new Menu);
1905 MenuList& play_items = play_menu->items();
1906 play_menu->set_name ("ArdourContextMenu");
1908 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1909 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1910 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1911 play_items.push_back (SeparatorElem());
1912 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
1914 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1918 Menu *select_menu = manage (new Menu);
1919 MenuList& select_items = select_menu->items();
1920 select_menu->set_name ("ArdourContextMenu");
1922 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1923 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1924 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1925 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1926 select_items.push_back (SeparatorElem());
1927 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
1928 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
1929 select_items.push_back (SeparatorElem());
1930 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1931 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1932 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1933 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1934 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
1935 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
1936 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
1938 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1942 Menu *cutnpaste_menu = manage (new Menu);
1943 MenuList& cutnpaste_items = cutnpaste_menu->items();
1944 cutnpaste_menu->set_name ("ArdourContextMenu");
1946 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1947 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1948 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f, true)));
1950 cutnpaste_items.push_back (SeparatorElem());
1952 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
1953 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
1955 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
1957 /* Adding new material */
1959 edit_items.push_back (SeparatorElem());
1960 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
1961 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
1965 Menu *nudge_menu = manage (new Menu());
1966 MenuList& nudge_items = nudge_menu->items();
1967 nudge_menu->set_name ("ArdourContextMenu");
1969 edit_items.push_back (SeparatorElem());
1970 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1971 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1972 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1973 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1975 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1979 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
1981 using namespace Menu_Helpers;
1985 Menu *play_menu = manage (new Menu);
1986 MenuList& play_items = play_menu->items();
1987 play_menu->set_name ("ArdourContextMenu");
1989 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1990 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1991 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1995 Menu *select_menu = manage (new Menu);
1996 MenuList& select_items = select_menu->items();
1997 select_menu->set_name ("ArdourContextMenu");
1999 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2000 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
2001 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
2002 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
2003 select_items.push_back (SeparatorElem());
2004 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
2005 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
2006 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2007 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2009 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2013 Menu *cutnpaste_menu = manage (new Menu);
2014 MenuList& cutnpaste_items = cutnpaste_menu->items();
2015 cutnpaste_menu->set_name ("ArdourContextMenu");
2017 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2018 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2019 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f, true)));
2021 Menu *nudge_menu = manage (new Menu());
2022 MenuList& nudge_items = nudge_menu->items();
2023 nudge_menu->set_name ("ArdourContextMenu");
2025 edit_items.push_back (SeparatorElem());
2026 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2027 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2028 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2029 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2031 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2035 Editor::snap_type() const
2041 Editor::snap_mode() const
2047 Editor::set_snap_to (SnapType st)
2049 unsigned int snap_ind = (unsigned int)st;
2053 if (snap_ind > snap_type_strings.size() - 1) {
2055 _snap_type = (SnapType)snap_ind;
2058 string str = snap_type_strings[snap_ind];
2060 if (str != snap_type_selector.get_active_text()) {
2061 snap_type_selector.set_active_text (str);
2066 switch (_snap_type) {
2067 case SnapToBeatDiv32:
2068 case SnapToBeatDiv28:
2069 case SnapToBeatDiv24:
2070 case SnapToBeatDiv20:
2071 case SnapToBeatDiv16:
2072 case SnapToBeatDiv14:
2073 case SnapToBeatDiv12:
2074 case SnapToBeatDiv10:
2075 case SnapToBeatDiv8:
2076 case SnapToBeatDiv7:
2077 case SnapToBeatDiv6:
2078 case SnapToBeatDiv5:
2079 case SnapToBeatDiv4:
2080 case SnapToBeatDiv3:
2081 case SnapToBeatDiv2:
2082 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
2083 update_tempo_based_rulers ();
2086 case SnapToRegionStart:
2087 case SnapToRegionEnd:
2088 case SnapToRegionSync:
2089 case SnapToRegionBoundary:
2090 build_region_boundary_cache ();
2098 SnapChanged (); /* EMIT SIGNAL */
2102 Editor::set_snap_mode (SnapMode mode)
2105 string str = snap_mode_strings[(int)mode];
2107 if (str != snap_mode_selector.get_active_text ()) {
2108 snap_mode_selector.set_active_text (str);
2114 Editor::set_edit_point_preference (EditPoint ep, bool force)
2116 bool changed = (_edit_point != ep);
2119 string str = edit_point_strings[(int)ep];
2121 if (str != edit_point_selector.get_active_text ()) {
2122 edit_point_selector.set_active_text (str);
2125 set_canvas_cursor ();
2127 if (!force && !changed) {
2131 const char* action=NULL;
2133 switch (_edit_point) {
2134 case EditAtPlayhead:
2135 action = "edit-at-playhead";
2137 case EditAtSelectedMarker:
2138 action = "edit-at-marker";
2141 action = "edit-at-mouse";
2145 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2147 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2151 bool in_track_canvas;
2153 if (!mouse_frame (foo, in_track_canvas)) {
2154 in_track_canvas = false;
2157 reset_canvas_action_sensitivity (in_track_canvas);
2163 Editor::set_state (const XMLNode& node, int /*version*/)
2165 const XMLProperty* prop;
2172 g.base_width = default_width;
2173 g.base_height = default_height;
2177 if ((geometry = find_named_node (node, "geometry")) != 0) {
2181 if ((prop = geometry->property("x_size")) == 0) {
2182 prop = geometry->property ("x-size");
2185 g.base_width = atoi(prop->value());
2187 if ((prop = geometry->property("y_size")) == 0) {
2188 prop = geometry->property ("y-size");
2191 g.base_height = atoi(prop->value());
2194 if ((prop = geometry->property ("x_pos")) == 0) {
2195 prop = geometry->property ("x-pos");
2198 x = atoi (prop->value());
2201 if ((prop = geometry->property ("y_pos")) == 0) {
2202 prop = geometry->property ("y-pos");
2205 y = atoi (prop->value());
2209 set_default_size (g.base_width, g.base_height);
2212 if (_session && (prop = node.property ("playhead"))) {
2214 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2215 playhead_cursor->set_position (pos);
2217 playhead_cursor->set_position (0);
2220 if ((prop = node.property ("mixer-width"))) {
2221 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2224 if ((prop = node.property ("zoom-focus"))) {
2225 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2228 if ((prop = node.property ("zoom"))) {
2229 reset_zoom (PBD::atof (prop->value()));
2231 reset_zoom (frames_per_unit);
2234 if ((prop = node.property ("snap-to"))) {
2235 set_snap_to ((SnapType) atoi (prop->value()));
2238 if ((prop = node.property ("snap-mode"))) {
2239 set_snap_mode ((SnapMode) atoi (prop->value()));
2242 if ((prop = node.property ("mouse-mode"))) {
2243 MouseMode m = str2mousemode(prop->value());
2244 set_mouse_mode (m, true);
2246 set_mouse_mode (MouseObject, true);
2249 if ((prop = node.property ("left-frame")) != 0) {
2251 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2255 reset_x_origin (pos);
2259 if ((prop = node.property ("y-origin")) != 0) {
2260 reset_y_origin (atof (prop->value ()));
2263 if ((prop = node.property ("internal-edit"))) {
2264 bool yn = string_is_affirmative (prop->value());
2265 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2267 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2268 tact->set_active (!yn);
2269 tact->set_active (yn);
2273 if ((prop = node.property ("join-object-range"))) {
2274 ActionManager::set_toggle_action ("MouseMode", "set-mouse-mode-object-range", string_is_affirmative (prop->value ()));
2277 if ((prop = node.property ("edit-point"))) {
2278 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2281 if ((prop = node.property ("show-measures"))) {
2282 bool yn = string_is_affirmative (prop->value());
2283 _show_measures = yn;
2284 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2286 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2287 /* do it twice to force the change */
2288 tact->set_active (!yn);
2289 tact->set_active (yn);
2293 if ((prop = node.property ("follow-playhead"))) {
2294 bool yn = string_is_affirmative (prop->value());
2295 set_follow_playhead (yn);
2296 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2298 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2299 if (tact->get_active() != yn) {
2300 tact->set_active (yn);
2305 if ((prop = node.property ("stationary-playhead"))) {
2306 bool yn = string_is_affirmative (prop->value());
2307 set_stationary_playhead (yn);
2308 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2310 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2311 if (tact->get_active() != yn) {
2312 tact->set_active (yn);
2317 if ((prop = node.property ("region-list-sort-type"))) {
2318 RegionListSortType st;
2319 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2322 if ((prop = node.property ("xfades-visible"))) {
2323 bool yn = string_is_affirmative (prop->value());
2324 _xfade_visibility = !yn;
2325 // set_xfade_visibility (yn);
2328 if ((prop = node.property ("show-editor-mixer"))) {
2330 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2333 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2334 bool yn = string_is_affirmative (prop->value());
2336 /* do it twice to force the change */
2338 tact->set_active (!yn);
2339 tact->set_active (yn);
2342 if ((prop = node.property ("show-editor-list"))) {
2344 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2347 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2348 bool yn = string_is_affirmative (prop->value());
2350 /* do it twice to force the change */
2352 tact->set_active (!yn);
2353 tact->set_active (yn);
2356 if ((prop = node.property (X_("editor-list-page")))) {
2357 _the_notebook.set_current_page (atoi (prop->value ()));
2360 if ((prop = node.property (X_("show-marker-lines")))) {
2361 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2363 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2364 bool yn = string_is_affirmative (prop->value ());
2366 tact->set_active (!yn);
2367 tact->set_active (yn);
2370 XMLNodeList children = node.children ();
2371 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2372 selection->set_state (**i, Stateful::current_state_version);
2373 _regions->set_state (**i);
2376 if ((prop = node.property ("maximised"))) {
2377 bool yn = string_is_affirmative (prop->value());
2379 ActionManager::do_action ("Common", "ToggleMaximalEditor");
2387 Editor::get_state ()
2389 XMLNode* node = new XMLNode ("Editor");
2392 id().print (buf, sizeof (buf));
2393 node->add_property ("id", buf);
2395 if (is_realized()) {
2396 Glib::RefPtr<Gdk::Window> win = get_window();
2398 int x, y, width, height;
2399 win->get_root_origin(x, y);
2400 win->get_size(width, height);
2402 XMLNode* geometry = new XMLNode ("geometry");
2404 snprintf(buf, sizeof(buf), "%d", width);
2405 geometry->add_property("x-size", string(buf));
2406 snprintf(buf, sizeof(buf), "%d", height);
2407 geometry->add_property("y-size", string(buf));
2408 snprintf(buf, sizeof(buf), "%d", x);
2409 geometry->add_property("x-pos", string(buf));
2410 snprintf(buf, sizeof(buf), "%d", y);
2411 geometry->add_property("y-pos", string(buf));
2412 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2413 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2414 geometry->add_property("notebook-shrunk", _notebook_shrunk ? "1" : "0");
2415 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2416 geometry->add_property("edit-vertical-pane-pos", string(buf));
2418 node->add_child_nocopy (*geometry);
2421 maybe_add_mixer_strip_width (*node);
2423 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2424 node->add_property ("zoom-focus", buf);
2425 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2426 node->add_property ("zoom", buf);
2427 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2428 node->add_property ("snap-to", buf);
2429 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2430 node->add_property ("snap-mode", buf);
2432 node->add_property ("edit-point", enum_2_string (_edit_point));
2434 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2435 node->add_property ("playhead", buf);
2436 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2437 node->add_property ("left-frame", buf);
2438 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2439 node->add_property ("y-origin", buf);
2441 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2442 node->add_property ("maximised", _maximised ? "yes" : "no");
2443 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2444 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2445 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2446 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2447 node->add_property ("mouse-mode", enum2str(mouse_mode));
2448 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2449 node->add_property ("join-object-range", smart_mode_action->get_active () ? "yes" : "no");
2451 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2453 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2454 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2457 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2459 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2460 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2463 snprintf (buf, sizeof (buf), "%d", _the_notebook.get_current_page ());
2464 node->add_property (X_("editor-list-page"), buf);
2466 if (button_bindings) {
2467 XMLNode* bb = new XMLNode (X_("Buttons"));
2468 button_bindings->save (*bb);
2469 node->add_child_nocopy (*bb);
2472 node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no");
2474 node->add_child_nocopy (selection->get_state ());
2475 node->add_child_nocopy (_regions->get_state ());
2482 /** @param y y offset from the top of all trackviews.
2483 * @return pair: TimeAxisView that y is over, layer index.
2484 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2485 * in stacked or expanded region display mode, otherwise 0.
2487 std::pair<TimeAxisView *, double>
2488 Editor::trackview_by_y_position (double y)
2490 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2492 std::pair<TimeAxisView*, double> const r = (*iter)->covers_y_position (y);
2498 return std::make_pair ( (TimeAxisView *) 0, 0);
2501 /** Snap a position to the grid, if appropriate, taking into account current
2502 * grid settings and also the state of any snap modifier keys that may be pressed.
2503 * @param start Position to snap.
2504 * @param event Event to get current key modifier information from, or 0.
2507 Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2509 if (!_session || !event) {
2513 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2514 if (_snap_mode == SnapOff) {
2515 snap_to_internal (start, direction, for_mark);
2518 if (_snap_mode != SnapOff) {
2519 snap_to_internal (start, direction, for_mark);
2525 Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark)
2527 if (!_session || _snap_mode == SnapOff) {
2531 snap_to_internal (start, direction, for_mark);
2535 Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /*for_mark*/)
2537 const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2538 framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2540 switch (_snap_type) {
2541 case SnapToTimecodeFrame:
2542 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2543 start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2545 start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2549 case SnapToTimecodeSeconds:
2550 if (_session->config.get_timecode_offset_negative()) {
2551 start += _session->config.get_timecode_offset ();
2553 start -= _session->config.get_timecode_offset ();
2555 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2556 start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2558 start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2561 if (_session->config.get_timecode_offset_negative()) {
2562 start -= _session->config.get_timecode_offset ();
2564 start += _session->config.get_timecode_offset ();
2568 case SnapToTimecodeMinutes:
2569 if (_session->config.get_timecode_offset_negative()) {
2570 start += _session->config.get_timecode_offset ();
2572 start -= _session->config.get_timecode_offset ();
2574 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2575 start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2577 start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2579 if (_session->config.get_timecode_offset_negative()) {
2580 start -= _session->config.get_timecode_offset ();
2582 start += _session->config.get_timecode_offset ();
2586 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2592 Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark)
2594 const framepos_t one_second = _session->frame_rate();
2595 const framepos_t one_minute = _session->frame_rate() * 60;
2596 framepos_t presnap = start;
2600 switch (_snap_type) {
2601 case SnapToTimecodeFrame:
2602 case SnapToTimecodeSeconds:
2603 case SnapToTimecodeMinutes:
2604 return timecode_snap_to_internal (start, direction, for_mark);
2607 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2608 start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2610 start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2615 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2616 start = (framepos_t) ceil ((double) start / one_second) * one_second;
2618 start = (framepos_t) floor ((double) start / one_second) * one_second;
2623 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2624 start = (framepos_t) ceil ((double) start / one_minute) * one_minute;
2626 start = (framepos_t) floor ((double) start / one_minute) * one_minute;
2631 start = _session->tempo_map().round_to_bar (start, direction);
2635 start = _session->tempo_map().round_to_beat (start, direction);
2638 case SnapToBeatDiv32:
2639 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2641 case SnapToBeatDiv28:
2642 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2644 case SnapToBeatDiv24:
2645 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2647 case SnapToBeatDiv20:
2648 start = _session->tempo_map().round_to_beat_subdivision (start, 20, direction);
2650 case SnapToBeatDiv16:
2651 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2653 case SnapToBeatDiv14:
2654 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2656 case SnapToBeatDiv12:
2657 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2659 case SnapToBeatDiv10:
2660 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2662 case SnapToBeatDiv8:
2663 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2665 case SnapToBeatDiv7:
2666 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2668 case SnapToBeatDiv6:
2669 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2671 case SnapToBeatDiv5:
2672 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2674 case SnapToBeatDiv4:
2675 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2677 case SnapToBeatDiv3:
2678 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2680 case SnapToBeatDiv2:
2681 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2689 _session->locations()->marks_either_side (start, before, after);
2691 if (before == max_framepos) {
2693 } else if (after == max_framepos) {
2695 } else if (before != max_framepos && after != max_framepos) {
2696 /* have before and after */
2697 if ((start - before) < (after - start)) {
2706 case SnapToRegionStart:
2707 case SnapToRegionEnd:
2708 case SnapToRegionSync:
2709 case SnapToRegionBoundary:
2710 if (!region_boundary_cache.empty()) {
2712 vector<framepos_t>::iterator prev = region_boundary_cache.end ();
2713 vector<framepos_t>::iterator next = region_boundary_cache.end ();
2715 if (direction > 0) {
2716 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2718 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2721 if (next != region_boundary_cache.begin ()) {
2726 framepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2727 framepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2729 if (start > (p + n) / 2) {
2738 switch (_snap_mode) {
2744 if (presnap > start) {
2745 if (presnap > (start + unit_to_frame(snap_threshold))) {
2749 } else if (presnap < start) {
2750 if (presnap < (start - unit_to_frame(snap_threshold))) {
2756 /* handled at entry */
2764 Editor::setup_toolbar ()
2766 HBox* mode_box = manage(new HBox);
2767 mode_box->set_border_width (2);
2768 mode_box->set_spacing(4);
2770 HBox* mouse_mode_box = manage (new HBox);
2771 HBox* mouse_mode_hbox1 = manage (new HBox);
2772 HBox* mouse_mode_hbox2 = manage (new HBox);
2773 VBox* mouse_mode_vbox1 = manage (new VBox);
2774 VBox* mouse_mode_vbox2 = manage (new VBox);
2775 Alignment* mouse_mode_align1 = manage (new Alignment);
2776 Alignment* mouse_mode_align2 = manage (new Alignment);
2778 Glib::RefPtr<SizeGroup> mouse_mode_size_group = SizeGroup::create (SIZE_GROUP_BOTH);
2779 mouse_mode_size_group->add_widget (mouse_move_button);
2780 mouse_mode_size_group->add_widget (mouse_select_button);
2781 mouse_mode_size_group->add_widget (mouse_zoom_button);
2782 mouse_mode_size_group->add_widget (mouse_gain_button);
2783 mouse_mode_size_group->add_widget (mouse_timefx_button);
2784 mouse_mode_size_group->add_widget (mouse_audition_button);
2785 mouse_mode_size_group->add_widget (mouse_draw_button);
2786 mouse_mode_size_group->add_widget (internal_edit_button);
2788 /* make them just a bit bigger */
2789 mouse_move_button.set_size_request (-1, 25);
2791 smart_mode_joiner = manage (new ButtonJoiner ("mouse mode button", mouse_move_button, mouse_select_button));
2792 smart_mode_joiner->set_related_action (smart_mode_action);
2794 mouse_move_button.set_elements (ArdourButton::Element (ArdourButton::Body|ArdourButton::Text));
2795 mouse_select_button.set_elements (ArdourButton::Element (ArdourButton::Body|ArdourButton::Text));
2797 mouse_move_button.set_rounded_corner_mask (0x1); // upper left only
2798 mouse_select_button.set_rounded_corner_mask (0x2); // upper right only
2800 mouse_mode_hbox2->set_spacing (2);
2801 mouse_mode_box->set_spacing (2);
2803 mouse_mode_hbox1->pack_start (*smart_mode_joiner, false, false);
2804 mouse_mode_hbox2->pack_start (mouse_zoom_button, false, false);
2805 mouse_mode_hbox2->pack_start (mouse_gain_button, false, false);
2806 mouse_mode_hbox2->pack_start (mouse_timefx_button, false, false);
2807 mouse_mode_hbox2->pack_start (mouse_audition_button, false, false);
2808 mouse_mode_hbox2->pack_start (mouse_draw_button, false, false);
2809 mouse_mode_hbox2->pack_start (internal_edit_button, false, false);
2811 mouse_mode_vbox1->pack_start (*mouse_mode_hbox1, false, false);
2812 mouse_mode_vbox2->pack_start (*mouse_mode_hbox2, false, false);
2814 mouse_mode_align1->add (*mouse_mode_vbox1);
2815 mouse_mode_align1->set (0.5, 1.0, 0.0, 0.0);
2816 mouse_mode_align2->add (*mouse_mode_vbox2);
2817 mouse_mode_align2->set (0.5, 1.0, 0.0, 0.0);
2819 mouse_mode_box->pack_start (*mouse_mode_align1, false, false);
2820 mouse_mode_box->pack_start (*mouse_mode_align2, false, false);
2822 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2823 if (!Profile->get_sae()) {
2824 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2826 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2828 edit_mode_selector.set_name ("EditModeSelector");
2829 set_popdown_strings (edit_mode_selector, edit_mode_strings);
2830 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2832 mode_box->pack_start (edit_mode_selector, false, false);
2833 mode_box->pack_start (*mouse_mode_box, false, false);
2835 _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2836 _mouse_mode_tearoff->set_name ("MouseModeBase");
2837 _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2839 if (Profile->get_sae()) {
2840 _mouse_mode_tearoff->set_can_be_torn_off (false);
2843 _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2844 &_mouse_mode_tearoff->tearoff_window()));
2845 _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2846 &_mouse_mode_tearoff->tearoff_window(), 1));
2847 _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2848 &_mouse_mode_tearoff->tearoff_window()));
2849 _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2850 &_mouse_mode_tearoff->tearoff_window(), 1));
2854 _zoom_box.set_spacing (2);
2855 _zoom_box.set_border_width (2);
2859 zoom_in_button.set_name ("zoom button");
2860 zoom_in_button.set_image (::get_icon ("zoom_in"));
2861 zoom_in_button.set_tweaks (ArdourButton::ShowClick);
2862 act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-in"));
2863 zoom_in_button.set_related_action (act);
2865 zoom_out_button.set_name ("zoom button");
2866 zoom_out_button.set_image (::get_icon ("zoom_out"));
2867 zoom_out_button.set_tweaks (ArdourButton::ShowClick);
2868 act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-out"));
2869 zoom_out_button.set_related_action (act);
2871 zoom_out_full_button.set_name ("zoom button");
2872 zoom_out_full_button.set_image (::get_icon ("zoom_full"));
2873 zoom_out_full_button.set_tweaks (ArdourButton::ShowClick);
2874 act = ActionManager::get_action (X_("Editor"), X_("zoom-to-session"));
2875 zoom_out_full_button.set_related_action (act);
2877 zoom_focus_selector.set_name ("ZoomFocusSelector");
2878 set_popdown_strings (zoom_focus_selector, zoom_focus_strings);
2879 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2881 _zoom_box.pack_start (zoom_out_button, false, false);
2882 _zoom_box.pack_start (zoom_in_button, false, false);
2883 _zoom_box.pack_start (zoom_out_full_button, false, false);
2885 _zoom_box.pack_start (zoom_focus_selector, false, false);
2887 /* Track zoom buttons */
2888 tav_expand_button.set_name ("TrackHeightButton");
2889 tav_expand_button.set_size_request (-1, 20);
2890 tav_expand_button.add (*(manage (new Image (::get_icon ("tav_exp")))));
2891 act = ActionManager::get_action (X_("Editor"), X_("expand-tracks"));
2892 act->connect_proxy (tav_expand_button);
2894 tav_shrink_button.set_name ("TrackHeightButton");
2895 tav_shrink_button.set_size_request (-1, 20);
2896 tav_shrink_button.add (*(manage (new Image (::get_icon ("tav_shrink")))));
2897 act = ActionManager::get_action (X_("Editor"), X_("shrink-tracks"));
2898 act->connect_proxy (tav_shrink_button);
2900 _zoom_box.pack_start (tav_shrink_button);
2901 _zoom_box.pack_start (tav_expand_button);
2903 _zoom_tearoff = manage (new TearOff (_zoom_box));
2905 _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2906 &_zoom_tearoff->tearoff_window()));
2907 _zoom_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2908 &_zoom_tearoff->tearoff_window(), 0));
2909 _zoom_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2910 &_zoom_tearoff->tearoff_window()));
2911 _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2912 &_zoom_tearoff->tearoff_window(), 0));
2914 snap_box.set_spacing (1);
2915 snap_box.set_border_width (2);
2917 snap_type_selector.set_name ("SnapTypeSelector");
2918 set_popdown_strings (snap_type_selector, snap_type_strings);
2919 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2921 snap_mode_selector.set_name ("SnapModeSelector");
2922 set_popdown_strings (snap_mode_selector, snap_mode_strings);
2923 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2925 edit_point_selector.set_name ("EditPointSelector");
2926 set_popdown_strings (edit_point_selector, edit_point_strings);
2927 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2929 snap_box.pack_start (snap_mode_selector, false, false);
2930 snap_box.pack_start (snap_type_selector, false, false);
2931 snap_box.pack_start (edit_point_selector, false, false);
2935 HBox *nudge_box = manage (new HBox);
2936 nudge_box->set_spacing (2);
2937 nudge_box->set_border_width (2);
2939 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2940 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2942 nudge_box->pack_start (nudge_backward_button, false, false);
2943 nudge_box->pack_start (nudge_forward_button, false, false);
2944 nudge_box->pack_start (*nudge_clock, false, false);
2947 /* Pack everything in... */
2949 HBox* hbox = manage (new HBox);
2950 hbox->set_spacing(10);
2952 _tools_tearoff = manage (new TearOff (*hbox));
2953 _tools_tearoff->set_name ("MouseModeBase");
2954 _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
2956 if (Profile->get_sae()) {
2957 _tools_tearoff->set_can_be_torn_off (false);
2960 _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2961 &_tools_tearoff->tearoff_window()));
2962 _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2963 &_tools_tearoff->tearoff_window(), 0));
2964 _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2965 &_tools_tearoff->tearoff_window()));
2966 _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2967 &_tools_tearoff->tearoff_window(), 0));
2969 toolbar_hbox.set_spacing (10);
2970 toolbar_hbox.set_border_width (1);
2972 toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
2973 toolbar_hbox.pack_start (*_zoom_tearoff, false, false);
2974 toolbar_hbox.pack_start (*_tools_tearoff, false, false);
2976 hbox->pack_start (snap_box, false, false);
2977 if (!Profile->get_small_screen()) {
2978 hbox->pack_start (*nudge_box, false, false);
2980 ARDOUR_UI::instance()->editor_transport_box().pack_start (*nudge_box, false, false);
2982 hbox->pack_start (panic_box, false, false);
2986 toolbar_base.set_name ("ToolBarBase");
2987 toolbar_base.add (toolbar_hbox);
2989 _toolbar_viewport.add (toolbar_base);
2990 /* stick to the required height but allow width to vary if there's not enough room */
2991 _toolbar_viewport.set_size_request (1, -1);
2993 toolbar_frame.set_shadow_type (SHADOW_OUT);
2994 toolbar_frame.set_name ("BaseFrame");
2995 toolbar_frame.add (_toolbar_viewport);
2999 Editor::setup_tooltips ()
3001 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
3002 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Select/Move Ranges"));
3003 ARDOUR_UI::instance()->set_tip (mouse_draw_button, _("Draw/Edit MIDI Notes"));
3004 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain"));
3005 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
3006 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
3007 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
3008 ARDOUR_UI::instance()->set_tip (smart_mode_joiner, _("Smart Mode (Select/Move Objects + Ranges)"));
3009 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
3010 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
3011 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
3012 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
3013 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
3014 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
3015 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
3016 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
3017 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
3018 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
3019 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
3020 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
3021 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
3022 ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode"));
3023 ARDOUR_UI::instance()->set_tip (nudge_clock, _("Nudge Clock\n(controls distance used to nudge regions and selections)"));
3027 Editor::convert_drop_to_paths (
3028 vector<string>& paths,
3029 const RefPtr<Gdk::DragContext>& /*context*/,
3032 const SelectionData& data,
3036 if (_session == 0) {
3040 vector<string> uris = data.get_uris();
3044 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3045 are actually URI lists. So do it by hand.
3048 if (data.get_target() != "text/plain") {
3052 /* Parse the "uri-list" format that Nautilus provides,
3053 where each pathname is delimited by \r\n.
3055 THERE MAY BE NO NULL TERMINATING CHAR!!!
3058 string txt = data.get_text();
3062 p = (const char *) malloc (txt.length() + 1);
3063 txt.copy ((char *) p, txt.length(), 0);
3064 ((char*)p)[txt.length()] = '\0';
3070 while (g_ascii_isspace (*p))
3074 while (*q && (*q != '\n') && (*q != '\r')) {
3081 while (q > p && g_ascii_isspace (*q))
3086 uris.push_back (string (p, q - p + 1));
3090 p = strchr (p, '\n');
3102 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3104 if ((*i).substr (0,7) == "file://") {
3107 PBD::url_decode (p);
3109 // scan forward past three slashes
3111 string::size_type slashcnt = 0;
3112 string::size_type n = 0;
3113 string::iterator x = p.begin();
3115 while (slashcnt < 3 && x != p.end()) {
3118 } else if (slashcnt == 3) {
3125 if (slashcnt != 3 || x == p.end()) {
3126 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3130 paths.push_back (p.substr (n - 1));
3138 Editor::new_tempo_section ()
3144 Editor::map_transport_state ()
3146 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3148 if (_session && _session->transport_stopped()) {
3149 have_pending_keyboard_selection = false;
3152 update_loop_range_view (true);
3157 Editor::State::State (PublicEditor const * e)
3159 selection = new Selection (e);
3162 Editor::State::~State ()
3168 Editor::begin_reversible_command (string name)
3171 _session->begin_reversible_command (name);
3176 Editor::begin_reversible_command (GQuark q)
3179 _session->begin_reversible_command (q);
3184 Editor::commit_reversible_command ()
3187 _session->commit_reversible_command ();
3192 Editor::history_changed ()
3196 if (undo_action && _session) {
3197 if (_session->undo_depth() == 0) {
3198 label = S_("Command|Undo");
3200 label = string_compose(S_("Command|Undo (%1)"), _session->next_undo());
3202 undo_action->property_label() = label;
3205 if (redo_action && _session) {
3206 if (_session->redo_depth() == 0) {
3209 label = string_compose(_("Redo (%1)"), _session->next_redo());
3211 redo_action->property_label() = label;
3216 Editor::duplicate_dialog (bool with_dialog)
3220 if (mouse_mode == MouseRange) {
3221 if (selection->time.length() == 0) {
3226 RegionSelection rs = get_regions_from_selection_and_entered ();
3228 if (mouse_mode != MouseRange && rs.empty()) {
3234 ArdourDialog win (_("Duplicate"));
3235 Label label (_("Number of duplications:"));
3236 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3237 SpinButton spinner (adjustment, 0.0, 1);
3240 win.get_vbox()->set_spacing (12);
3241 win.get_vbox()->pack_start (hbox);
3242 hbox.set_border_width (6);
3243 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3245 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3246 place, visually. so do this by hand.
3249 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3250 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3251 spinner.grab_focus();
3257 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3258 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3259 win.set_default_response (RESPONSE_ACCEPT);
3261 win.set_position (WIN_POS_MOUSE);
3263 spinner.grab_focus ();
3265 switch (win.run ()) {
3266 case RESPONSE_ACCEPT:
3272 times = adjustment.get_value();
3275 if (mouse_mode == MouseRange) {
3276 duplicate_selection (times);
3278 duplicate_some_regions (rs, times);
3283 Editor::set_edit_mode (EditMode m)
3285 Config->set_edit_mode (m);
3289 Editor::cycle_edit_mode ()
3291 switch (Config->get_edit_mode()) {
3293 if (Profile->get_sae()) {
3294 Config->set_edit_mode (Lock);
3296 Config->set_edit_mode (Splice);
3300 Config->set_edit_mode (Lock);
3303 Config->set_edit_mode (Slide);
3309 Editor::edit_mode_selection_done ()
3311 string s = edit_mode_selector.get_active_text ();
3314 Config->set_edit_mode (string_to_edit_mode (s));
3319 Editor::snap_type_selection_done ()
3321 string choice = snap_type_selector.get_active_text();
3322 SnapType snaptype = SnapToBeat;
3324 if (choice == _("Beats/2")) {
3325 snaptype = SnapToBeatDiv2;
3326 } else if (choice == _("Beats/3")) {
3327 snaptype = SnapToBeatDiv3;
3328 } else if (choice == _("Beats/4")) {
3329 snaptype = SnapToBeatDiv4;
3330 } else if (choice == _("Beats/5")) {
3331 snaptype = SnapToBeatDiv5;
3332 } else if (choice == _("Beats/6")) {
3333 snaptype = SnapToBeatDiv6;
3334 } else if (choice == _("Beats/7")) {
3335 snaptype = SnapToBeatDiv7;
3336 } else if (choice == _("Beats/8")) {
3337 snaptype = SnapToBeatDiv8;
3338 } else if (choice == _("Beats/10")) {
3339 snaptype = SnapToBeatDiv10;
3340 } else if (choice == _("Beats/12")) {
3341 snaptype = SnapToBeatDiv12;
3342 } else if (choice == _("Beats/14")) {
3343 snaptype = SnapToBeatDiv14;
3344 } else if (choice == _("Beats/16")) {
3345 snaptype = SnapToBeatDiv16;
3346 } else if (choice == _("Beats/20")) {
3347 snaptype = SnapToBeatDiv20;
3348 } else if (choice == _("Beats/24")) {
3349 snaptype = SnapToBeatDiv24;
3350 } else if (choice == _("Beats/28")) {
3351 snaptype = SnapToBeatDiv28;
3352 } else if (choice == _("Beats/32")) {
3353 snaptype = SnapToBeatDiv32;
3354 } else if (choice == _("Beats")) {
3355 snaptype = SnapToBeat;
3356 } else if (choice == _("Bars")) {
3357 snaptype = SnapToBar;
3358 } else if (choice == _("Marks")) {
3359 snaptype = SnapToMark;
3360 } else if (choice == _("Region starts")) {
3361 snaptype = SnapToRegionStart;
3362 } else if (choice == _("Region ends")) {
3363 snaptype = SnapToRegionEnd;
3364 } else if (choice == _("Region bounds")) {
3365 snaptype = SnapToRegionBoundary;
3366 } else if (choice == _("Region syncs")) {
3367 snaptype = SnapToRegionSync;
3368 } else if (choice == _("CD Frames")) {
3369 snaptype = SnapToCDFrame;
3370 } else if (choice == _("Timecode Frames")) {
3371 snaptype = SnapToTimecodeFrame;
3372 } else if (choice == _("Timecode Seconds")) {
3373 snaptype = SnapToTimecodeSeconds;
3374 } else if (choice == _("Timecode Minutes")) {
3375 snaptype = SnapToTimecodeMinutes;
3376 } else if (choice == _("Seconds")) {
3377 snaptype = SnapToSeconds;
3378 } else if (choice == _("Minutes")) {
3379 snaptype = SnapToMinutes;
3382 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3384 ract->set_active ();
3389 Editor::snap_mode_selection_done ()
3391 string choice = snap_mode_selector.get_active_text();
3392 SnapMode mode = SnapNormal;
3394 if (choice == _("No Grid")) {
3396 } else if (choice == _("Grid")) {
3398 } else if (choice == _("Magnetic")) {
3399 mode = SnapMagnetic;
3402 RefPtr<RadioAction> ract = snap_mode_action (mode);
3405 ract->set_active (true);
3410 Editor::cycle_edit_point (bool with_marker)
3412 switch (_edit_point) {
3414 set_edit_point_preference (EditAtPlayhead);
3416 case EditAtPlayhead:
3418 set_edit_point_preference (EditAtSelectedMarker);
3420 set_edit_point_preference (EditAtMouse);
3423 case EditAtSelectedMarker:
3424 set_edit_point_preference (EditAtMouse);
3430 Editor::edit_point_selection_done ()
3432 string choice = edit_point_selector.get_active_text();
3433 EditPoint ep = EditAtSelectedMarker;
3435 if (choice == _("Marker")) {
3436 set_edit_point_preference (EditAtSelectedMarker);
3437 } else if (choice == _("Playhead")) {
3438 set_edit_point_preference (EditAtPlayhead);
3440 set_edit_point_preference (EditAtMouse);
3443 RefPtr<RadioAction> ract = edit_point_action (ep);
3446 ract->set_active (true);
3451 Editor::zoom_focus_selection_done ()
3453 string choice = zoom_focus_selector.get_active_text();
3454 ZoomFocus focus_type = ZoomFocusLeft;
3456 if (choice == _("Left")) {
3457 focus_type = ZoomFocusLeft;
3458 } else if (choice == _("Right")) {
3459 focus_type = ZoomFocusRight;
3460 } else if (choice == _("Center")) {
3461 focus_type = ZoomFocusCenter;
3462 } else if (choice == _("Playhead")) {
3463 focus_type = ZoomFocusPlayhead;
3464 } else if (choice == _("Mouse")) {
3465 focus_type = ZoomFocusMouse;
3466 } else if (choice == _("Edit point")) {
3467 focus_type = ZoomFocusEdit;
3470 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3473 ract->set_active ();
3478 Editor::edit_controls_button_release (GdkEventButton* ev)
3480 if (Keyboard::is_context_menu_event (ev)) {
3481 ARDOUR_UI::instance()->add_route (this);
3482 } else if (ev->button == 1) {
3483 selection->clear_tracks ();
3490 Editor::mouse_select_button_release (GdkEventButton* ev)
3492 /* this handles just right-clicks */
3494 if (ev->button != 3) {
3502 Editor::set_zoom_focus (ZoomFocus f)
3504 string str = zoom_focus_strings[(int)f];
3506 if (str != zoom_focus_selector.get_active_text()) {
3507 zoom_focus_selector.set_active_text (str);
3510 if (zoom_focus != f) {
3517 Editor::ensure_float (Window& win)
3519 win.set_transient_for (*this);
3523 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3525 /* recover or initialize pane positions. do this here rather than earlier because
3526 we don't want the positions to change the child allocations, which they seem to do.
3532 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3541 XMLNode* geometry = find_named_node (*node, "geometry");
3543 if (which == static_cast<Paned*> (&edit_pane)) {
3545 if (done & Horizontal) {
3549 if (geometry && (prop = geometry->property ("notebook-shrunk"))) {
3550 _notebook_shrunk = string_is_affirmative (prop->value ());
3553 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3554 /* initial allocation is 90% to canvas, 10% to notebook */
3555 pos = (int) floor (alloc.get_width() * 0.90f);
3556 snprintf (buf, sizeof(buf), "%d", pos);
3558 pos = atoi (prop->value());
3561 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3562 edit_pane.set_position (pos);
3565 done = (Pane) (done | Horizontal);
3567 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3569 if (done & Vertical) {
3573 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3574 /* initial allocation is 90% to canvas, 10% to summary */
3575 pos = (int) floor (alloc.get_height() * 0.90f);
3576 snprintf (buf, sizeof(buf), "%d", pos);
3579 pos = atoi (prop->value());
3582 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3583 editor_summary_pane.set_position (pos);
3586 done = (Pane) (done | Vertical);
3591 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3593 if ((_tools_tearoff->torn_off() || !_tools_tearoff->visible()) &&
3594 (_mouse_mode_tearoff->torn_off() || !_mouse_mode_tearoff->visible()) &&
3595 (_zoom_tearoff->torn_off() || !_zoom_tearoff->visible())) {
3596 top_hbox.remove (toolbar_frame);
3601 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3603 if (toolbar_frame.get_parent() == 0) {
3604 top_hbox.pack_end (toolbar_frame);
3609 Editor::set_show_measures (bool yn)
3611 if (_show_measures != yn) {
3614 if ((_show_measures = yn) == true) {
3616 tempo_lines->show();
3624 Editor::toggle_follow_playhead ()
3626 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3628 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3629 set_follow_playhead (tact->get_active());
3633 /** @param yn true to follow playhead, otherwise false.
3634 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
3637 Editor::set_follow_playhead (bool yn, bool catch_up)
3639 if (_follow_playhead != yn) {
3640 if ((_follow_playhead = yn) == true && catch_up) {
3642 reset_x_origin_to_follow_playhead ();
3649 Editor::toggle_stationary_playhead ()
3651 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3653 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3654 set_stationary_playhead (tact->get_active());
3659 Editor::set_stationary_playhead (bool yn)
3661 if (_stationary_playhead != yn) {
3662 if ((_stationary_playhead = yn) == true) {
3664 // FIXME need a 3.0 equivalent of this 2.X call
3665 // update_current_screen ();
3672 Editor::toggle_xfade_active (RouteTimeAxisView* tv, boost::weak_ptr<Crossfade> wxfade)
3674 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3679 vector<boost::shared_ptr<Crossfade> > all = get_equivalent_crossfades (*tv, xfade, ARDOUR::Properties::edit.property_id);
3681 _session->begin_reversible_command (_("Change crossfade active state"));
3683 for (vector<boost::shared_ptr<Crossfade> >::iterator i = all.begin(); i != all.end(); ++i) {
3684 (*i)->clear_changes ();
3685 (*i)->set_active (!(*i)->active());
3686 _session->add_command (new StatefulDiffCommand (*i));
3689 _session->commit_reversible_command ();
3693 Editor::toggle_xfade_length (RouteTimeAxisView* tv, boost::weak_ptr<Crossfade> wxfade)
3695 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3700 vector<boost::shared_ptr<Crossfade> > all = get_equivalent_crossfades (*tv, xfade, ARDOUR::Properties::edit.property_id);
3702 /* This can't be a StatefulDiffCommand as the fade shapes are not
3703 managed by the Stateful properties system.
3705 _session->begin_reversible_command (_("Change crossfade length"));
3707 for (vector<boost::shared_ptr<Crossfade> >::iterator i = all.begin(); i != all.end(); ++i) {
3708 XMLNode& before = (*i)->get_state ();
3709 (*i)->set_follow_overlap (!(*i)->following_overlap());
3710 XMLNode& after = (*i)->get_state ();
3712 _session->add_command (new MementoCommand<Crossfade> (*i->get(), &before, &after));
3715 _session->commit_reversible_command ();
3719 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3721 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3727 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3731 switch (cew.run ()) {
3732 case RESPONSE_ACCEPT:
3739 PropertyChange all_crossfade_properties;
3740 all_crossfade_properties.add (ARDOUR::Properties::active);
3741 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3742 xfade->PropertyChanged (all_crossfade_properties);
3746 Editor::playlist_selector () const
3748 return *_playlist_selector;
3752 Editor::get_grid_type_as_beats (bool& success, framepos_t position)
3756 switch (_snap_type) {
3761 case SnapToBeatDiv32:
3764 case SnapToBeatDiv28:
3767 case SnapToBeatDiv24:
3770 case SnapToBeatDiv20:
3773 case SnapToBeatDiv16:
3776 case SnapToBeatDiv14:
3779 case SnapToBeatDiv12:
3782 case SnapToBeatDiv10:
3785 case SnapToBeatDiv8:
3788 case SnapToBeatDiv7:
3791 case SnapToBeatDiv6:
3794 case SnapToBeatDiv5:
3797 case SnapToBeatDiv4:
3800 case SnapToBeatDiv3:
3803 case SnapToBeatDiv2:
3809 return _session->tempo_map().meter_at (position).divisions_per_bar();
3814 case SnapToTimecodeFrame:
3815 case SnapToTimecodeSeconds:
3816 case SnapToTimecodeMinutes:
3819 case SnapToRegionStart:
3820 case SnapToRegionEnd:
3821 case SnapToRegionSync:
3822 case SnapToRegionBoundary:
3832 Editor::get_nudge_distance (framepos_t pos, framecnt_t& next)
3836 ret = nudge_clock->current_duration (pos);
3837 next = ret + 1; /* XXXX fix me */
3843 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3845 ArdourDialog dialog (_("Playlist Deletion"));
3846 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3847 "If it is kept, its audio files will not be cleaned.\n"
3848 "If it is deleted, audio files used by it alone will be cleaned."),
3851 dialog.set_position (WIN_POS_CENTER);
3852 dialog.get_vbox()->pack_start (label);
3856 dialog.add_button (_("Delete Playlist"), RESPONSE_ACCEPT);
3857 dialog.add_button (_("Keep Playlist"), RESPONSE_REJECT);
3858 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3860 switch (dialog.run ()) {
3861 case RESPONSE_ACCEPT:
3862 /* delete the playlist */
3866 case RESPONSE_REJECT:
3867 /* keep the playlist */
3879 Editor::audio_region_selection_covers (framepos_t where)
3881 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3882 if ((*a)->region()->covers (where)) {
3891 Editor::prepare_for_cleanup ()
3893 cut_buffer->clear_regions ();
3894 cut_buffer->clear_playlists ();
3896 selection->clear_regions ();
3897 selection->clear_playlists ();
3899 _regions->suspend_redisplay ();
3903 Editor::finish_cleanup ()
3905 _regions->resume_redisplay ();
3909 Editor::transport_loop_location()
3912 return _session->locations()->auto_loop_location();
3919 Editor::transport_punch_location()
3922 return _session->locations()->auto_punch_location();
3929 Editor::control_layout_scroll (GdkEventScroll* ev)
3931 if (Keyboard::some_magic_widget_has_focus()) {
3935 switch (ev->direction) {
3937 scroll_tracks_up_line ();
3941 case GDK_SCROLL_DOWN:
3942 scroll_tracks_down_line ();
3946 /* no left/right handling yet */
3954 Editor::session_state_saved (string)
3957 _snapshots->redisplay ();
3961 Editor::maximise_editing_space ()
3969 if (!Config->get_keep_tearoffs()) {
3970 /* these calls will leave each tearoff visible *if* it is torn off,
3971 but invisible otherwise.
3973 _mouse_mode_tearoff->set_visible (false);
3974 _tools_tearoff->set_visible (false);
3975 _zoom_tearoff->set_visible (false);
3982 Editor::restore_editing_space ()
3990 if (!Config->get_keep_tearoffs()) {
3991 _mouse_mode_tearoff->set_visible (true);
3992 _tools_tearoff->set_visible (true);
3993 _zoom_tearoff->set_visible (true);
4000 * Make new playlists for a given track and also any others that belong
4001 * to the same active route group with the `edit' property.
4006 Editor::new_playlists (TimeAxisView* v)
4008 begin_reversible_command (_("new playlists"));
4009 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4010 _session->playlists->get (playlists);
4011 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4012 commit_reversible_command ();
4016 * Use a copy of the current playlist for a given track and also any others that belong
4017 * to the same active route group with the `edit' property.
4022 Editor::copy_playlists (TimeAxisView* v)
4024 begin_reversible_command (_("copy playlists"));
4025 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4026 _session->playlists->get (playlists);
4027 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4028 commit_reversible_command ();
4031 /** Clear the current playlist for a given track and also any others that belong
4032 * to the same active route group with the `edit' property.
4037 Editor::clear_playlists (TimeAxisView* v)
4039 begin_reversible_command (_("clear playlists"));
4040 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4041 _session->playlists->get (playlists);
4042 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4043 commit_reversible_command ();
4047 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4049 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4053 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4055 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4059 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4061 atv.clear_playlist ();
4065 Editor::on_key_press_event (GdkEventKey* ev)
4067 return key_press_focus_accelerator_handler (*this, ev);
4071 Editor::on_key_release_event (GdkEventKey* ev)
4073 return Gtk::Window::on_key_release_event (ev);
4074 // return key_press_focus_accelerator_handler (*this, ev);
4077 /** Queue up a change to the viewport x origin.
4078 * @param frame New x origin.
4081 Editor::reset_x_origin (framepos_t frame)
4083 queue_visual_change (frame);
4087 Editor::reset_y_origin (double y)
4089 queue_visual_change_y (y);
4093 Editor::reset_zoom (double fpu)
4095 queue_visual_change (fpu);
4099 Editor::reposition_and_zoom (framepos_t frame, double fpu)
4101 reset_x_origin (frame);
4104 if (!no_save_visual) {
4105 undo_visual_stack.push_back (current_visual_state(false));
4109 Editor::VisualState::VisualState ()
4110 : gui_state (new GUIObjectState)
4114 Editor::VisualState::~VisualState ()
4119 Editor::VisualState*
4120 Editor::current_visual_state (bool with_tracks)
4122 VisualState* vs = new VisualState;
4123 vs->y_position = vertical_adjustment.get_value();
4124 vs->frames_per_unit = frames_per_unit;
4125 vs->leftmost_frame = leftmost_frame;
4126 vs->zoom_focus = zoom_focus;
4129 *(vs->gui_state) = *ARDOUR_UI::instance()->gui_object_state;
4136 Editor::undo_visual_state ()
4138 if (undo_visual_stack.empty()) {
4142 redo_visual_stack.push_back (current_visual_state());
4144 VisualState* vs = undo_visual_stack.back();
4145 undo_visual_stack.pop_back();
4146 use_visual_state (*vs);
4150 Editor::redo_visual_state ()
4152 if (redo_visual_stack.empty()) {
4156 undo_visual_stack.push_back (current_visual_state());
4158 VisualState* vs = redo_visual_stack.back();
4159 redo_visual_stack.pop_back();
4160 use_visual_state (*vs);
4164 Editor::swap_visual_state ()
4166 if (undo_visual_stack.empty()) {
4167 redo_visual_state ();
4169 undo_visual_state ();
4174 Editor::use_visual_state (VisualState& vs)
4176 no_save_visual = true;
4178 _routes->suspend_redisplay ();
4180 vertical_adjustment.set_value (vs.y_position);
4182 set_zoom_focus (vs.zoom_focus);
4183 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4185 *ARDOUR_UI::instance()->gui_object_state = *vs.gui_state;
4187 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4188 (*i)->reset_visual_state ();
4191 _routes->update_visibility ();
4192 _routes->resume_redisplay ();
4194 no_save_visual = false;
4198 Editor::set_frames_per_unit (double fpu)
4200 /* this is the core function that controls the zoom level of the canvas. it is called
4201 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4204 if (fpu == frames_per_unit) {
4213 /* don't allow zooms that fit more than the maximum number
4214 of frames into an 800 pixel wide space.
4217 if (max_framepos / fpu < 800.0) {
4222 tempo_lines->tempo_map_changed();
4224 frames_per_unit = fpu;
4229 Editor::post_zoom ()
4231 // convert fpu to frame count
4233 framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width);
4235 if (frames_per_unit != zoom_range_clock->current_duration()) {
4236 zoom_range_clock->set (frames);
4239 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4240 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4241 (*i)->reshow_selection (selection->time);
4245 ZoomChanged (); /* EMIT_SIGNAL */
4247 //reset_scrolling_region ();
4249 if (playhead_cursor) {
4250 playhead_cursor->set_position (playhead_cursor->current_frame);
4253 refresh_location_display();
4254 _summary->set_overlays_dirty ();
4256 update_marker_labels ();
4262 Editor::queue_visual_change (framepos_t where)
4264 pending_visual_change.add (VisualChange::TimeOrigin);
4265 pending_visual_change.time_origin = where;
4266 ensure_visual_change_idle_handler ();
4270 Editor::queue_visual_change (double fpu)
4272 pending_visual_change.add (VisualChange::ZoomLevel);
4273 pending_visual_change.frames_per_unit = fpu;
4275 ensure_visual_change_idle_handler ();
4279 Editor::queue_visual_change_y (double y)
4281 pending_visual_change.add (VisualChange::YOrigin);
4282 pending_visual_change.y_origin = y;
4284 ensure_visual_change_idle_handler ();
4288 Editor::ensure_visual_change_idle_handler ()
4290 if (pending_visual_change.idle_handler_id < 0) {
4291 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4296 Editor::_idle_visual_changer (void* arg)
4298 return static_cast<Editor*>(arg)->idle_visual_changer ();
4302 Editor::idle_visual_changer ()
4304 VisualChange::Type p = pending_visual_change.pending;
4305 pending_visual_change.pending = (VisualChange::Type) 0;
4307 double const last_time_origin = horizontal_position ();
4309 if (p & VisualChange::TimeOrigin) {
4310 /* This is a bit of a hack, but set_frames_per_unit
4311 below will (if called) end up with the
4312 CrossfadeViews looking at Editor::leftmost_frame,
4313 and if we're changing origin and zoom in the same
4314 operation it will be the wrong value unless we
4318 leftmost_frame = pending_visual_change.time_origin;
4319 assert (leftmost_frame >= 0);
4322 if (p & VisualChange::ZoomLevel) {
4323 set_frames_per_unit (pending_visual_change.frames_per_unit);
4325 compute_fixed_ruler_scale ();
4326 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4327 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4328 update_tempo_based_rulers ();
4330 if (p & VisualChange::TimeOrigin) {
4331 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4333 if (p & VisualChange::YOrigin) {
4334 vertical_adjustment.set_value (pending_visual_change.y_origin);
4337 if (last_time_origin == horizontal_position ()) {
4338 /* changed signal not emitted */
4339 update_fixed_rulers ();
4340 redisplay_tempo (true);
4343 _summary->set_overlays_dirty ();
4345 pending_visual_change.idle_handler_id = -1;
4346 return 0; /* this is always a one-shot call */
4349 struct EditorOrderTimeAxisSorter {
4350 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4351 return a->order () < b->order ();
4356 Editor::sort_track_selection (TrackViewList& sel)
4358 EditorOrderTimeAxisSorter cmp;
4363 Editor::get_preferred_edit_position (bool ignore_playhead, bool from_context_menu)
4366 framepos_t where = 0;
4367 EditPoint ep = _edit_point;
4369 if (from_context_menu && (ep == EditAtMouse)) {
4370 return event_frame (&context_click_event, 0, 0);
4373 if (entered_marker) {
4374 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position()));
4375 return entered_marker->position();
4378 if (ignore_playhead && ep == EditAtPlayhead) {
4379 ep = EditAtSelectedMarker;
4383 case EditAtPlayhead:
4384 where = _session->audible_frame();
4385 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where));
4388 case EditAtSelectedMarker:
4389 if (!selection->markers.empty()) {
4391 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4394 where = loc->start();
4398 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where));
4406 if (!mouse_frame (where, ignored)) {
4407 /* XXX not right but what can we do ? */
4411 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where));
4419 Editor::set_loop_range (framepos_t start, framepos_t end, string cmd)
4421 if (!_session) return;
4423 begin_reversible_command (cmd);
4427 if ((tll = transport_loop_location()) == 0) {
4428 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop);
4429 XMLNode &before = _session->locations()->get_state();
4430 _session->locations()->add (loc, true);
4431 _session->set_auto_loop_location (loc);
4432 XMLNode &after = _session->locations()->get_state();
4433 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4435 XMLNode &before = tll->get_state();
4436 tll->set_hidden (false, this);
4437 tll->set (start, end);
4438 XMLNode &after = tll->get_state();
4439 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4442 commit_reversible_command ();
4446 Editor::set_punch_range (framepos_t start, framepos_t end, string cmd)
4448 if (!_session) return;
4450 begin_reversible_command (cmd);
4454 if ((tpl = transport_punch_location()) == 0) {
4455 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoPunch);
4456 XMLNode &before = _session->locations()->get_state();
4457 _session->locations()->add (loc, true);
4458 _session->set_auto_loop_location (loc);
4459 XMLNode &after = _session->locations()->get_state();
4460 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4463 XMLNode &before = tpl->get_state();
4464 tpl->set_hidden (false, this);
4465 tpl->set (start, end);
4466 XMLNode &after = tpl->get_state();
4467 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4470 commit_reversible_command ();
4473 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4474 * @param rs List to which found regions are added.
4475 * @param where Time to look at.
4476 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4479 Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4481 const TrackViewList* tracks;
4484 tracks = &track_views;
4489 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4491 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4494 boost::shared_ptr<Track> tr;
4495 boost::shared_ptr<Playlist> pl;
4497 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4499 boost::shared_ptr<RegionList> regions = pl->regions_at (
4500 (framepos_t) floor ( (double) where * tr->speed()));
4502 for (RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4503 RegionView* rv = rtv->view()->find_view (*i);
4514 Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4516 const TrackViewList* tracks;
4519 tracks = &track_views;
4524 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4525 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4527 boost::shared_ptr<Track> tr;
4528 boost::shared_ptr<Playlist> pl;
4530 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4532 boost::shared_ptr<RegionList> regions = pl->regions_touched (
4533 (framepos_t) floor ( (double)where * tr->speed()), max_framepos);
4535 for (RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4537 RegionView* rv = rtv->view()->find_view (*i);
4548 /** Start with regions that are selected. Then add equivalent regions
4549 * on tracks in the same active edit-enabled route group as any of
4550 * the regions that we started with.
4554 Editor::get_regions_from_selection ()
4556 return get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
4559 /** Get regions using the following method:
4561 * Make an initial region list using the selected regions, unless
4562 * the edit point is `mouse' and the mouse is over an unselected
4563 * region. In this case, start with just that region.
4565 * Then, make an initial track list of the tracks that these
4566 * regions are on, and if the edit point is not `mouse', add the
4569 * Look at this track list and add any other tracks that are on the
4570 * same active edit-enabled route group as one of the initial tracks.
4572 * Finally take the initial region list and add any regions that are
4573 * under the edit point on one of the tracks on the track list to get
4574 * the returned region list.
4576 * The rationale here is that the mouse edit point is special in that
4577 * its position describes both a time and a track; the other edit
4578 * modes only describe a time. Hence if the edit point is `mouse' we
4579 * ignore selected tracks, as we assume the user means something by
4580 * pointing at a particular track. Also in this case we take note of
4581 * the region directly under the edit point, as there is always just one
4582 * (rather than possibly several with non-mouse edit points).
4586 Editor::get_regions_from_selection_and_edit_point ()
4588 RegionSelection regions;
4590 if (_edit_point == EditAtMouse && entered_regionview && !selection->regions.contains (entered_regionview)) {
4591 regions.add (entered_regionview);
4593 regions = selection->regions;
4596 TrackViewList tracks;
4598 if (_edit_point != EditAtMouse) {
4599 tracks = selection->tracks;
4602 /* Add any other tracks that have regions that are in the same
4603 edit-activated route group as one of our regions.
4605 for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) {
4607 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4609 if (g && g->is_active() && g->is_edit()) {
4610 tracks.add (axis_views_from_routes (g->route_list()));
4614 if (!tracks.empty()) {
4615 /* now find regions that are at the edit position on those tracks */
4616 framepos_t const where = get_preferred_edit_position ();
4617 get_regions_at (regions, where, tracks);
4623 /** Start with regions that are selected, or the entered regionview if none are selected.
4624 * Then add equivalent regions on tracks in the same active edit-enabled route group as any
4625 * of the regions that we started with.
4629 Editor::get_regions_from_selection_and_entered ()
4631 RegionSelection regions = selection->regions;
4633 if (regions.empty() && entered_regionview) {
4634 regions.add (entered_regionview);
4637 return get_equivalent_regions (regions, ARDOUR::Properties::edit.property_id);
4641 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4643 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4645 RouteTimeAxisView* tatv;
4647 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4649 boost::shared_ptr<Playlist> pl;
4650 vector<boost::shared_ptr<Region> > results;
4652 boost::shared_ptr<Track> tr;
4654 if ((tr = tatv->track()) == 0) {
4659 if ((pl = (tr->playlist())) != 0) {
4660 pl->get_region_list_equivalent_regions (region, results);
4663 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4664 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4665 regions.push_back (marv);
4674 Editor::show_rhythm_ferret ()
4676 if (rhythm_ferret == 0) {
4677 rhythm_ferret = new RhythmFerret(*this);
4680 rhythm_ferret->set_session (_session);
4681 rhythm_ferret->show ();
4682 rhythm_ferret->present ();
4686 Editor::first_idle ()
4688 MessageDialog* dialog = 0;
4690 if (track_views.size() > 1) {
4691 dialog = new MessageDialog (
4693 string_compose (_("Please wait while %1 loads visual data."), PROGRAM_NAME),
4697 ARDOUR_UI::instance()->flush_pending ();
4700 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4704 // first idle adds route children (automation tracks), so we need to redisplay here
4705 _routes->redisplay ();
4712 Editor::_idle_resize (gpointer arg)
4714 return ((Editor*)arg)->idle_resize ();
4718 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4720 if (resize_idle_id < 0) {
4721 resize_idle_id = g_idle_add (_idle_resize, this);
4722 _pending_resize_amount = 0;
4725 /* make a note of the smallest resulting height, so that we can clamp the
4726 lower limit at TimeAxisView::hSmall */
4728 int32_t min_resulting = INT32_MAX;
4730 _pending_resize_amount += h;
4731 _pending_resize_view = view;
4733 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4735 if (selection->tracks.contains (_pending_resize_view)) {
4736 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4737 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4741 if (min_resulting < 0) {
4746 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4747 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4751 /** Handle pending resizing of tracks */
4753 Editor::idle_resize ()
4755 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4757 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4758 selection->tracks.contains (_pending_resize_view)) {
4760 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4761 if (*i != _pending_resize_view) {
4762 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4767 _pending_resize_amount = 0;
4769 _group_tabs->set_dirty ();
4770 resize_idle_id = -1;
4778 ENSURE_GUI_THREAD (*this, &Editor::located);
4780 playhead_cursor->set_position (_session->audible_frame ());
4781 if (_follow_playhead && !_pending_initial_locate) {
4782 reset_x_origin_to_follow_playhead ();
4785 _pending_locate_request = false;
4786 _pending_initial_locate = false;
4790 Editor::region_view_added (RegionView *)
4792 _summary->set_dirty ();
4796 Editor::region_view_removed ()
4798 _summary->set_dirty ();
4802 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4804 TrackViewList::const_iterator j = track_views.begin ();
4805 while (j != track_views.end()) {
4806 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4807 if (rtv && rtv->route() == r) {
4818 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4822 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4823 TimeAxisView* tv = axis_view_from_route (*i);
4834 Editor::handle_new_route (RouteList& routes)
4836 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4838 RouteTimeAxisView *rtv;
4839 list<RouteTimeAxisView*> new_views;
4841 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4842 boost::shared_ptr<Route> route = (*x);
4844 if (route->is_hidden() || route->is_monitor()) {
4848 DataType dt = route->input()->default_type();
4850 if (dt == ARDOUR::DataType::AUDIO) {
4851 rtv = new AudioTimeAxisView (*this, _session, *track_canvas);
4852 rtv->set_route (route);
4853 } else if (dt == ARDOUR::DataType::MIDI) {
4854 rtv = new MidiTimeAxisView (*this, _session, *track_canvas);
4855 rtv->set_route (route);
4857 throw unknown_type();
4860 new_views.push_back (rtv);
4861 track_views.push_back (rtv);
4863 rtv->effective_gain_display ();
4865 if (internal_editing()) {
4866 rtv->enter_internal_edit_mode ();
4868 rtv->leave_internal_edit_mode ();
4871 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4872 rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed));
4875 _routes->routes_added (new_views);
4876 _summary->routes_added (new_views);
4878 if (show_editor_mixer_when_tracks_arrive) {
4879 show_editor_mixer (true);
4882 editor_list_button.set_sensitive (true);
4886 Editor::timeaxisview_deleted (TimeAxisView *tv)
4888 if (_session && _session->deletion_in_progress()) {
4889 /* the situation is under control */
4893 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
4895 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
4897 _routes->route_removed (tv);
4899 if (tv == entered_track) {
4903 TimeAxisView::Children c = tv->get_child_list ();
4904 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
4905 if (entered_track == i->get()) {
4910 /* remove it from the list of track views */
4912 TrackViewList::iterator i;
4914 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
4915 i = track_views.erase (i);
4918 /* update whatever the current mixer strip is displaying, if revelant */
4920 boost::shared_ptr<Route> route;
4923 route = rtav->route ();
4926 if (current_mixer_strip && current_mixer_strip->route() == route) {
4928 TimeAxisView* next_tv;
4930 if (track_views.empty()) {
4932 } else if (i == track_views.end()) {
4933 next_tv = track_views.front();
4940 set_selected_mixer_strip (*next_tv);
4942 /* make the editor mixer strip go away setting the
4943 * button to inactive (which also unticks the menu option)
4946 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
4952 Editor::hide_track_in_display (TimeAxisView* tv, bool apply_to_selection)
4954 if (apply_to_selection) {
4955 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ) {
4957 TrackSelection::iterator j = i;
4960 hide_track_in_display (*i, false);
4965 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
4967 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
4968 // this will hide the mixer strip
4969 set_selected_mixer_strip (*tv);
4972 _routes->hide_track_in_display (*tv);
4977 Editor::sync_track_view_list_and_routes ()
4979 track_views = TrackViewList (_routes->views ());
4981 _summary->set_dirty ();
4982 _group_tabs->set_dirty ();
4984 return false; // do not call again (until needed)
4988 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
4990 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4995 /** Find a RouteTimeAxisView by the ID of its route */
4997 Editor::get_route_view_by_route_id (const PBD::ID& id) const
4999 RouteTimeAxisView* v;
5001 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5002 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5003 if(v->route()->id() == id) {
5013 Editor::fit_route_group (RouteGroup *g)
5015 TrackViewList ts = axis_views_from_routes (g->route_list ());
5020 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5022 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5025 _session->cancel_audition ();
5029 if (_session->is_auditioning()) {
5030 _session->cancel_audition ();
5031 if (r == last_audition_region) {
5036 _session->audition_region (r);
5037 last_audition_region = r;
5042 Editor::hide_a_region (boost::shared_ptr<Region> r)
5044 r->set_hidden (true);
5048 Editor::show_a_region (boost::shared_ptr<Region> r)
5050 r->set_hidden (false);
5054 Editor::audition_region_from_region_list ()
5056 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5060 Editor::hide_region_from_region_list ()
5062 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5066 Editor::show_region_in_region_list ()
5068 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5072 Editor::step_edit_status_change (bool yn)
5075 start_step_editing ();
5077 stop_step_editing ();
5082 Editor::start_step_editing ()
5084 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5088 Editor::stop_step_editing ()
5090 step_edit_connection.disconnect ();
5094 Editor::check_step_edit ()
5096 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5097 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5099 mtv->check_step_edit ();
5103 return true; // do it again, till we stop
5107 Editor::scroll_press (Direction dir)
5109 ++_scroll_callbacks;
5111 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5112 /* delay the first auto-repeat */
5118 scroll_backward (1);
5126 scroll_tracks_up_line ();
5130 scroll_tracks_down_line ();
5134 /* do hacky auto-repeat */
5135 if (!_scroll_connection.connected ()) {
5137 _scroll_connection = Glib::signal_timeout().connect (
5138 sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), dir), 100
5141 _scroll_callbacks = 0;
5148 Editor::scroll_release ()
5150 _scroll_connection.disconnect ();
5153 /** Queue a change for the Editor viewport x origin to follow the playhead */
5155 Editor::reset_x_origin_to_follow_playhead ()
5157 framepos_t const frame = playhead_cursor->current_frame;
5159 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5161 if (_session->transport_speed() < 0) {
5163 if (frame > (current_page_frames() / 2)) {
5164 center_screen (frame-(current_page_frames()/2));
5166 center_screen (current_page_frames()/2);
5173 if (frame < leftmost_frame) {
5175 if (_session->transport_rolling()) {
5176 /* rolling; end up with the playhead at the right of the page */
5177 l = frame - current_page_frames ();
5179 /* not rolling: end up with the playhead 1/4 of the way along the page */
5180 l = frame - current_page_frames() / 4;
5184 if (_session->transport_rolling()) {
5185 /* rolling: end up with the playhead on the left of the page */
5188 /* not rolling: end up with the playhead 3/4 of the way along the page */
5189 l = frame - 3 * current_page_frames() / 4;
5197 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5203 Editor::super_rapid_screen_update ()
5205 if (!_session || !_session->engine().running()) {
5209 /* METERING / MIXER STRIPS */
5211 /* update track meters, if required */
5212 if (is_mapped() && meters_running) {
5213 RouteTimeAxisView* rtv;
5214 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5215 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5216 rtv->fast_update ();
5221 /* and any current mixer strip */
5222 if (current_mixer_strip) {
5223 current_mixer_strip->fast_update ();
5226 /* PLAYHEAD AND VIEWPORT */
5228 framepos_t const frame = _session->audible_frame();
5230 /* There are a few reasons why we might not update the playhead / viewport stuff:
5232 * 1. we don't update things when there's a pending locate request, otherwise
5233 * when the editor requests a locate there is a chance that this method
5234 * will move the playhead before the locate request is processed, causing
5236 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5237 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5240 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5242 last_update_frame = frame;
5244 if (!_dragging_playhead) {
5245 playhead_cursor->set_position (frame);
5248 if (!_stationary_playhead) {
5250 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5251 reset_x_origin_to_follow_playhead ();
5256 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5260 // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5261 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5262 if (target <= 0.0) {
5265 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5266 target = (target * 0.15) + (current * 0.85);
5272 set_horizontal_position (current);
5281 Editor::session_going_away ()
5283 _have_idled = false;
5285 _session_connections.drop_connections ();
5287 super_rapid_screen_update_connection.disconnect ();
5289 selection->clear ();
5290 cut_buffer->clear ();
5292 clicked_regionview = 0;
5293 clicked_axisview = 0;
5294 clicked_routeview = 0;
5295 clicked_crossfadeview = 0;
5296 entered_regionview = 0;
5298 last_update_frame = 0;
5301 playhead_cursor->canvas_item.hide ();
5303 /* rip everything out of the list displays */
5307 _route_groups->clear ();
5309 /* do this first so that deleting a track doesn't reset cms to null
5310 and thus cause a leak.
5313 if (current_mixer_strip) {
5314 if (current_mixer_strip->get_parent() != 0) {
5315 global_hpacker.remove (*current_mixer_strip);
5317 delete current_mixer_strip;
5318 current_mixer_strip = 0;
5321 /* delete all trackviews */
5323 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5326 track_views.clear ();
5328 zoom_range_clock->set_session (0);
5329 nudge_clock->set_session (0);
5331 editor_list_button.set_active(false);
5332 editor_list_button.set_sensitive(false);
5334 /* clear tempo/meter rulers */
5335 remove_metric_marks ();
5337 clear_marker_display ();
5339 current_bbt_points_begin = current_bbt_points_end;
5341 /* get rid of any existing editor mixer strip */
5343 WindowTitle title(Glib::get_application_name());
5344 title += _("Editor");
5346 set_title (title.get_string());
5348 SessionHandlePtr::session_going_away ();
5353 Editor::show_editor_list (bool yn)
5356 _the_notebook.show ();
5358 _the_notebook.hide ();
5363 Editor::change_region_layering_order (bool from_context_menu)
5365 const framepos_t position = get_preferred_edit_position (false, from_context_menu);
5367 if (!clicked_routeview) {
5368 if (layering_order_editor) {
5369 layering_order_editor->hide ();
5374 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
5380 boost::shared_ptr<Playlist> pl = track->playlist();
5386 if (layering_order_editor == 0) {
5387 layering_order_editor = new RegionLayeringOrderEditor (*this);
5388 layering_order_editor->set_position (WIN_POS_MOUSE);
5391 layering_order_editor->set_context (clicked_routeview->name(), _session, clicked_routeview, pl, position);
5392 layering_order_editor->maybe_present ();
5396 Editor::update_region_layering_order_editor ()
5398 if (layering_order_editor && layering_order_editor->is_visible ()) {
5399 change_region_layering_order (true);
5404 Editor::setup_fade_images ()
5406 _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear")));
5407 _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut")));
5408 _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut")));
5409 _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut")));
5410 _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut")));
5412 _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear")));
5413 _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut")));
5414 _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut")));
5415 _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut")));
5416 _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut")));
5420 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
5422 Editor::action_menu_item (std::string const & name)
5424 Glib::RefPtr<Action> a = editor_actions->get_action (name);
5427 return *manage (a->create_menu_item ());
5431 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
5433 EventBox* b = manage (new EventBox);
5434 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
5435 Label* l = manage (new Label (name));
5439 _the_notebook.append_page (widget, *b);
5443 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
5445 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
5446 _the_notebook.set_current_page (_the_notebook.page_num (*page));
5449 if (ev->type == GDK_2BUTTON_PRESS) {
5451 /* double-click on a notebook tab shrinks or expands the notebook */
5453 if (_notebook_shrunk) {
5454 if (pre_notebook_shrink_pane_width) {
5455 edit_pane.set_position (*pre_notebook_shrink_pane_width);
5457 _notebook_shrunk = false;
5459 pre_notebook_shrink_pane_width = edit_pane.get_position();
5461 /* this expands the LHS of the edit pane to cover the notebook
5462 PAGE but leaves the tabs visible.
5464 edit_pane.set_position (edit_pane.get_position() + page->get_width());
5465 _notebook_shrunk = true;
5473 Editor::popup_control_point_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
5475 using namespace Menu_Helpers;
5477 MenuList& items = _control_point_context_menu.items ();
5480 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun (*this, &Editor::edit_control_point), item)));
5481 items.push_back (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun (*this, &Editor::remove_control_point), item)));
5482 if (!can_remove_control_point (item)) {
5483 items.back().set_sensitive (false);
5486 _control_point_context_menu.popup (event->button.button, event->button.time);