2 Copyright (C) 2000-2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /* Note: public Editor methods are documented in public_editor.h */
30 #include "ardour_ui.h"
32 * ardour_ui.h include was moved to the top of the list
33 * due to a conflicting definition of 'Style' between
34 * Apple's MacTypes.h and BarController.
37 #include <boost/none.hpp>
39 #include <sigc++/bind.h>
41 #include "pbd/convert.h"
42 #include "pbd/error.h"
43 #include "pbd/enumwriter.h"
44 #include "pbd/memento_command.h"
45 #include "pbd/unknown_type.h"
47 #include <glibmm/miscutils.h>
48 #include <gtkmm/image.h>
49 #include <gdkmm/color.h>
50 #include <gdkmm/bitmap.h>
52 #include <gtkmm2ext/grouped_buttons.h>
53 #include <gtkmm2ext/gtk_ui.h>
54 #include <gtkmm2ext/tearoff.h>
55 #include <gtkmm2ext/utils.h>
56 #include <gtkmm2ext/window_title.h>
57 #include <gtkmm2ext/choice.h>
58 #include <gtkmm2ext/cell_renderer_pixbuf_toggle.h>
60 #include "ardour/audio_diskstream.h"
61 #include "ardour/audio_track.h"
62 #include "ardour/audioplaylist.h"
63 #include "ardour/audioregion.h"
64 #include "ardour/location.h"
65 #include "ardour/midi_region.h"
66 #include "ardour/plugin_manager.h"
67 #include "ardour/profile.h"
68 #include "ardour/route_group.h"
69 #include "ardour/session_directory.h"
70 #include "ardour/session_route.h"
71 #include "ardour/session_state_utils.h"
72 #include "ardour/tempo.h"
73 #include "ardour/utils.h"
74 #include "ardour/session_playlists.h"
75 #include "ardour/audioengine.h"
77 #include "control_protocol/control_protocol.h"
82 #include "playlist_selector.h"
83 #include "audio_region_view.h"
84 #include "rgb_macros.h"
85 #include "selection.h"
86 #include "audio_streamview.h"
87 #include "time_axis_view.h"
88 #include "audio_time_axis.h"
90 #include "crossfade_view.h"
91 #include "canvas-noevent-text.h"
93 #include "public_editor.h"
94 #include "crossfade_edit.h"
95 #include "canvas_impl.h"
98 #include "gui_thread.h"
99 #include "simpleline.h"
100 #include "rhythm_ferret.h"
102 #include "tempo_lines.h"
103 #include "analysis_window.h"
104 #include "bundle_manager.h"
105 #include "global_port_matrix.h"
106 #include "editor_drag.h"
107 #include "editor_group_tabs.h"
108 #include "automation_time_axis.h"
109 #include "editor_routes.h"
110 #include "midi_time_axis.h"
111 #include "mixer_strip.h"
112 #include "editor_route_groups.h"
113 #include "editor_regions.h"
114 #include "editor_locations.h"
115 #include "editor_snapshots.h"
116 #include "editor_summary.h"
117 #include "region_layering_order_editor.h"
118 #include "mouse_cursors.h"
119 #include "editor_cursors.h"
124 #include "imageframe_socket_handler.h"
128 using namespace ARDOUR;
131 using namespace Glib;
132 using namespace Gtkmm2ext;
133 using namespace Editing;
135 using PBD::internationalize;
137 using Gtkmm2ext::Keyboard;
139 const double Editor::timebar_height = 15.0;
141 static const gchar *_snap_type_strings[] = {
143 N_("Timecode Frames"),
144 N_("Timecode Seconds"),
145 N_("Timecode Minutes"),
173 static const gchar *_snap_mode_strings[] = {
180 static const gchar *_edit_point_strings[] = {
187 static const gchar *_zoom_focus_strings[] = {
197 #ifdef USE_RUBBERBAND
198 static const gchar *_rb_opt_strings[] = {
201 N_("Balanced multitimbral mixture"),
202 N_("Unpitched percussion with stable notes"),
203 N_("Crisp monophonic instrumental"),
204 N_("Unpitched solo percussion"),
205 N_("Resample without preserving pitch"),
211 show_me_the_size (Requisition* r, const char* what)
213 cerr << "size of " << what << " = " << r->width << " x " << r->height << endl;
218 pane_size_watcher (Paned* pane)
220 /* if the handle of a pane vanishes into (at least) the tabs of a notebook,
221 it is no longer accessible. so stop that. this doesn't happen on X11,
222 just the quartz backend.
227 int max_width_of_lhs = GTK_WIDGET(pane->gobj())->allocation.width - 25;
229 gint pos = pane->get_position ();
231 if (pos > max_width_of_lhs) {
232 pane->set_position (max_width_of_lhs);
238 : _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
240 /* time display buttons */
241 , minsec_label (_("Mins:Secs"))
242 , bbt_label (_("Bars:Beats"))
243 , timecode_label (_("Timecode"))
244 , samples_label (_("Samples"))
245 , tempo_label (_("Tempo"))
246 , meter_label (_("Meter"))
247 , mark_label (_("Location Markers"))
248 , range_mark_label (_("Range Markers"))
249 , transport_mark_label (_("Loop/Punch Ranges"))
250 , cd_mark_label (_("CD Markers"))
251 , edit_packer (4, 4, true)
253 /* the values here don't matter: layout widgets
254 reset them as needed.
257 , vertical_adjustment (0.0, 0.0, 10.0, 400.0)
259 /* tool bar related */
261 , zoom_range_clock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, false, true)
263 , toolbar_selection_clock_table (2,3)
265 , automation_mode_button (_("mode"))
266 , global_automation_button (_("automation"))
268 , _toolbar_viewport (*manage (new Gtk::Adjustment (0, 0, 1e10)), *manage (new Gtk::Adjustment (0, 0, 1e10)))
269 , midi_panic_button (_("Panic"))
272 , image_socket_listener(0)
277 , nudge_clock (X_("nudge"), false, X_("NudgeClock"), true, false, true)
278 , meters_running(false)
279 , _pending_locate_request (false)
280 , _pending_initial_locate (false)
281 , _last_cut_copy_source_track (0)
283 , _region_selection_change_updates_region_list (true)
287 /* we are a singleton */
289 PublicEditor::_instance = this;
293 selection = new Selection (this);
294 cut_buffer = new Selection (this);
296 clicked_regionview = 0;
297 clicked_axisview = 0;
298 clicked_routeview = 0;
299 clicked_crossfadeview = 0;
300 clicked_control_point = 0;
301 last_update_frame = 0;
302 pre_press_cursor = 0;
303 _drags = new DragManager (this);
304 current_mixer_strip = 0;
305 current_bbt_points = 0;
308 snap_type_strings = I18N (_snap_type_strings);
309 snap_mode_strings = I18N (_snap_mode_strings);
310 zoom_focus_strings = I18N (_zoom_focus_strings);
311 edit_point_strings = I18N (_edit_point_strings);
312 #ifdef USE_RUBBERBAND
313 rb_opt_strings = I18N (_rb_opt_strings);
317 snap_threshold = 5.0;
318 bbt_beat_subdivision = 4;
321 last_autoscroll_x = 0;
322 last_autoscroll_y = 0;
323 autoscroll_active = false;
324 autoscroll_timeout_tag = -1;
329 current_interthread_info = 0;
330 _show_measures = true;
331 show_gain_after_trim = false;
332 verbose_cursor_on = true;
333 last_item_entered = 0;
335 have_pending_keyboard_selection = false;
336 _follow_playhead = true;
337 _stationary_playhead = false;
338 _xfade_visibility = true;
339 editor_ruler_menu = 0;
340 no_ruler_shown_update = false;
342 range_marker_menu = 0;
343 marker_menu_item = 0;
344 tempo_or_meter_marker_menu = 0;
345 transport_marker_menu = 0;
346 new_transport_marker_menu = 0;
347 editor_mixer_strip_width = Wide;
348 show_editor_mixer_when_tracks_arrive = false;
349 region_edit_menu_split_multichannel_item = 0;
350 region_edit_menu_split_item = 0;
353 current_stepping_trackview = 0;
355 entered_regionview = 0;
357 clear_entered_track = false;
360 button_release_can_deselect = true;
361 _dragging_playhead = false;
362 _dragging_edit_point = false;
363 select_new_marker = false;
365 layering_order_editor = 0;
367 no_save_visual = false;
370 scrubbing_direction = 0;
374 location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get();
375 location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get();
376 location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get();
377 location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get();
378 location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get();
380 _edit_point = EditAtMouse;
381 _internal_editing = false;
382 current_canvas_cursor = 0;
384 frames_per_unit = 2048; /* too early to use reset_zoom () */
386 _scroll_callbacks = 0;
388 zoom_focus = ZoomFocusLeft;
389 set_zoom_focus (ZoomFocusLeft);
390 zoom_range_clock.ValueChanged.connect (sigc::mem_fun(*this, &Editor::zoom_adjustment_changed));
392 bbt_label.set_name ("EditorTimeButton");
393 bbt_label.set_size_request (-1, (int)timebar_height);
394 bbt_label.set_alignment (1.0, 0.5);
395 bbt_label.set_padding (5,0);
397 bbt_label.set_no_show_all();
398 minsec_label.set_name ("EditorTimeButton");
399 minsec_label.set_size_request (-1, (int)timebar_height);
400 minsec_label.set_alignment (1.0, 0.5);
401 minsec_label.set_padding (5,0);
402 minsec_label.hide ();
403 minsec_label.set_no_show_all();
404 timecode_label.set_name ("EditorTimeButton");
405 timecode_label.set_size_request (-1, (int)timebar_height);
406 timecode_label.set_alignment (1.0, 0.5);
407 timecode_label.set_padding (5,0);
408 timecode_label.hide ();
409 timecode_label.set_no_show_all();
410 samples_label.set_name ("EditorTimeButton");
411 samples_label.set_size_request (-1, (int)timebar_height);
412 samples_label.set_alignment (1.0, 0.5);
413 samples_label.set_padding (5,0);
414 samples_label.hide ();
415 samples_label.set_no_show_all();
417 tempo_label.set_name ("EditorTimeButton");
418 tempo_label.set_size_request (-1, (int)timebar_height);
419 tempo_label.set_alignment (1.0, 0.5);
420 tempo_label.set_padding (5,0);
422 tempo_label.set_no_show_all();
424 meter_label.set_name ("EditorTimeButton");
425 meter_label.set_size_request (-1, (int)timebar_height);
426 meter_label.set_alignment (1.0, 0.5);
427 meter_label.set_padding (5,0);
429 meter_label.set_no_show_all();
431 mark_label.set_name ("EditorTimeButton");
432 mark_label.set_size_request (-1, (int)timebar_height);
433 mark_label.set_alignment (1.0, 0.5);
434 mark_label.set_padding (5,0);
436 mark_label.set_no_show_all();
438 cd_mark_label.set_name ("EditorTimeButton");
439 cd_mark_label.set_size_request (-1, (int)timebar_height);
440 cd_mark_label.set_alignment (1.0, 0.5);
441 cd_mark_label.set_padding (5,0);
442 cd_mark_label.hide();
443 cd_mark_label.set_no_show_all();
445 range_mark_label.set_name ("EditorTimeButton");
446 range_mark_label.set_size_request (-1, (int)timebar_height);
447 range_mark_label.set_alignment (1.0, 0.5);
448 range_mark_label.set_padding (5,0);
449 range_mark_label.hide();
450 range_mark_label.set_no_show_all();
452 transport_mark_label.set_name ("EditorTimeButton");
453 transport_mark_label.set_size_request (-1, (int)timebar_height);
454 transport_mark_label.set_alignment (1.0, 0.5);
455 transport_mark_label.set_padding (5,0);
456 transport_mark_label.hide();
457 transport_mark_label.set_no_show_all();
459 initialize_rulers ();
460 initialize_canvas ();
462 _summary = new EditorSummary (this);
464 selection->TimeChanged.connect (sigc::mem_fun(*this, &Editor::time_selection_changed));
465 selection->TracksChanged.connect (sigc::mem_fun(*this, &Editor::track_selection_changed));
467 editor_regions_selection_changed_connection = selection->RegionsChanged.connect (sigc::mem_fun(*this, &Editor::region_selection_changed));
469 selection->PointsChanged.connect (sigc::mem_fun(*this, &Editor::point_selection_changed));
470 selection->MarkersChanged.connect (sigc::mem_fun(*this, &Editor::marker_selection_changed));
472 edit_controls_vbox.set_spacing (0);
473 vertical_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &Editor::tie_vertical_scrolling), true);
474 track_canvas->signal_map_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_map_handler));
476 HBox* h = manage (new HBox);
477 _group_tabs = new EditorGroupTabs (this);
478 h->pack_start (*_group_tabs, PACK_SHRINK);
479 h->pack_start (edit_controls_vbox);
480 controls_layout.add (*h);
482 controls_layout.set_name ("EditControlsBase");
483 controls_layout.add_events (Gdk::SCROLL_MASK);
484 controls_layout.signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::control_layout_scroll), false);
486 controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
487 controls_layout.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_release));
489 _cursors = new MouseCursors;
491 ArdourCanvas::Canvas* time_pad = manage(new ArdourCanvas::Canvas());
492 ArdourCanvas::SimpleLine* pad_line_1 = manage(new ArdourCanvas::SimpleLine(*time_pad->root(),
493 0.0, 1.0, 100.0, 1.0));
495 pad_line_1->property_color_rgba() = 0xFF0000FF;
500 time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2);
501 time_canvas_vbox.set_size_request (-1, -1);
503 ruler_label_event_box.add (ruler_label_vbox);
504 ruler_label_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
505 ruler_label_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
507 time_button_event_box.add (time_button_vbox);
508 time_button_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
509 time_button_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
511 /* these enable us to have a dedicated window (for cursor setting, etc.)
512 for the canvas areas.
515 track_canvas_event_box.add (*track_canvas);
517 time_canvas_event_box.add (time_canvas_vbox);
518 time_canvas_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
520 edit_packer.set_col_spacings (0);
521 edit_packer.set_row_spacings (0);
522 edit_packer.set_homogeneous (false);
523 edit_packer.set_border_width (0);
524 edit_packer.set_name ("EditorWindow");
526 /* labels for the rulers */
527 edit_packer.attach (ruler_label_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0);
528 /* labels for the marker "tracks" */
529 edit_packer.attach (time_button_event_box, 1, 2, 1, 2, FILL, SHRINK, 0, 0);
531 edit_packer.attach (time_canvas_event_box, 2, 3, 0, 1, FILL|EXPAND, FILL, 0, 0);
533 edit_packer.attach (controls_layout, 0, 2, 2, 3, FILL, FILL|EXPAND, 0, 0);
535 edit_packer.attach (track_canvas_event_box, 2, 3, 1, 3, FILL|EXPAND, FILL|EXPAND, 0, 0);
537 bottom_hbox.set_border_width (2);
538 bottom_hbox.set_spacing (3);
540 _route_groups = new EditorRouteGroups (this);
541 _routes = new EditorRoutes (this);
542 _regions = new EditorRegions (this);
543 _snapshots = new EditorSnapshots (this);
544 _locations = new EditorLocations (this);
546 add_notebook_page (_("Regions"), _regions->widget ());
547 add_notebook_page (_("Tracks & Busses"), _routes->widget ());
548 add_notebook_page (_("Snapshots"), _snapshots->widget ());
549 add_notebook_page (_("Route Groups"), _route_groups->widget ());
550 add_notebook_page (_("Ranges & Marks"), _locations->widget ());
552 _the_notebook.set_show_tabs (true);
553 _the_notebook.set_scrollable (true);
554 _the_notebook.popup_disable ();
555 _the_notebook.set_tab_pos (Gtk::POS_RIGHT);
556 _the_notebook.show_all ();
558 post_maximal_editor_width = 0;
559 post_maximal_horizontal_pane_position = 0;
560 post_maximal_editor_height = 0;
561 post_maximal_vertical_pane_position = 0;
562 _notebook_shrunk = false;
564 editor_summary_pane.pack1(edit_packer);
566 Button* summary_arrows_left_left = manage (new Button);
567 summary_arrows_left_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
568 summary_arrows_left_left->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press)));
569 summary_arrows_left_left->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_release));
571 Button* summary_arrows_left_right = manage (new Button);
572 summary_arrows_left_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
573 summary_arrows_left_right->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press)));
574 summary_arrows_left_right->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_release));
576 VBox* summary_arrows_left = manage (new VBox);
577 summary_arrows_left->pack_start (*summary_arrows_left_left);
578 summary_arrows_left->pack_start (*summary_arrows_left_right);
580 Button* summary_arrows_right_left = manage (new Button);
581 summary_arrows_right_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
582 summary_arrows_right_left->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press)));
583 summary_arrows_right_left->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_release));
585 Button* summary_arrows_right_right = manage (new Button);
586 summary_arrows_right_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
587 summary_arrows_right_right->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press)));
588 summary_arrows_right_right->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_release));
590 VBox* summary_arrows_right = manage (new VBox);
591 summary_arrows_right->pack_start (*summary_arrows_right_left);
592 summary_arrows_right->pack_start (*summary_arrows_right_right);
594 Frame* summary_frame = manage (new Frame);
595 summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN);
597 summary_frame->add (*_summary);
598 summary_frame->show ();
600 _summary_hbox.pack_start (*summary_arrows_left, false, false);
601 _summary_hbox.pack_start (*summary_frame, true, true);
602 _summary_hbox.pack_start (*summary_arrows_right, false, false);
604 editor_summary_pane.pack2 (_summary_hbox);
606 edit_pane.pack1 (editor_summary_pane, true, true);
607 edit_pane.pack2 (_the_notebook, false, true);
609 editor_summary_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun (*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&editor_summary_pane)));
611 /* XXX: editor_summary_pane might need similar special OS X treatment to the edit_pane */
613 edit_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&edit_pane)));
615 Glib::PropertyProxy<int> proxy = edit_pane.property_position();
616 proxy.signal_changed().connect (bind (sigc::ptr_fun (pane_size_watcher), static_cast<Paned*> (&edit_pane)));
618 top_hbox.pack_start (toolbar_frame);
620 HBox *hbox = manage (new HBox);
621 hbox->pack_start (edit_pane, true, true);
623 global_vpacker.pack_start (top_hbox, false, false);
624 global_vpacker.pack_start (*hbox, true, true);
626 global_hpacker.pack_start (global_vpacker, true, true);
628 set_name ("EditorWindow");
629 add_accel_group (ActionManager::ui_manager->get_accel_group());
631 status_bar_hpacker.show ();
633 vpacker.pack_end (status_bar_hpacker, false, false);
634 vpacker.pack_end (global_hpacker, true, true);
636 /* register actions now so that set_state() can find them and set toggles/checks etc */
641 setup_midi_toolbar ();
643 _snap_type = SnapToBeat;
644 set_snap_to (_snap_type);
645 _snap_mode = SnapOff;
646 set_snap_mode (_snap_mode);
647 set_mouse_mode (MouseObject, true);
648 set_edit_point_preference (EditAtMouse, true);
650 _playlist_selector = new PlaylistSelector();
651 _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
653 RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), ui_bind (&Editor::catch_vanishing_regionview, this, _1), gui_context());
657 nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
658 nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
660 nudge_forward_button.set_name ("TransportButton");
661 nudge_backward_button.set_name ("TransportButton");
663 fade_context_menu.set_name ("ArdourContextMenu");
665 /* icons, titles, WM stuff */
667 list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
668 Glib::RefPtr<Gdk::Pixbuf> icon;
670 if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
671 window_icons.push_back (icon);
673 if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
674 window_icons.push_back (icon);
676 if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
677 window_icons.push_back (icon);
679 if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
680 window_icons.push_back (icon);
682 if (!window_icons.empty()) {
683 set_icon_list (window_icons);
684 set_default_icon_list (window_icons);
687 WindowTitle title(Glib::get_application_name());
688 title += _("Editor");
689 set_title (title.get_string());
690 set_wmclass (X_("ardour_editor"), PROGRAM_NAME);
693 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
695 signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
696 signal_delete_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
698 /* allow external control surfaces/protocols to do various things */
700 ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context());
701 ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context());
702 ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context());
703 ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), ui_bind (&Editor::control_scroll, this, _1), gui_context());
704 BasicUI::AccessAction.connect (*this, invalidator (*this), ui_bind (&Editor::access_action, this, _1, _2), gui_context());
706 /* problematic: has to return a value and thus cannot be x-thread */
708 Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
710 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
712 TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
714 _ignore_region_action = false;
715 _last_region_menu_was_main = false;
716 _popup_region_menu_item = 0;
718 _show_marker_lines = false;
719 _over_region_trim_target = false;
724 setup_fade_images ();
730 if(image_socket_listener) {
731 if(image_socket_listener->is_connected())
733 image_socket_listener->close_connection() ;
736 delete image_socket_listener ;
737 image_socket_listener = 0 ;
742 delete _route_groups;
748 Editor::add_toplevel_controls (Container& cont)
750 vpacker.pack_start (cont, false, false);
755 Editor::catch_vanishing_regionview (RegionView *rv)
757 /* note: the selection will take care of the vanishing
758 audioregionview by itself.
761 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
765 if (clicked_regionview == rv) {
766 clicked_regionview = 0;
769 if (entered_regionview == rv) {
770 set_entered_regionview (0);
773 if (!_all_region_actions_sensitized) {
774 sensitize_all_region_actions (true);
779 Editor::set_entered_regionview (RegionView* rv)
781 if (rv == entered_regionview) {
785 if (entered_regionview) {
786 entered_regionview->exited ();
789 if ((entered_regionview = rv) != 0) {
790 entered_regionview->entered (internal_editing ());
793 if (!_all_region_actions_sensitized && _last_region_menu_was_main) {
794 /* This RegionView entry might have changed what region actions
795 are allowed, so sensitize them all in case a key is pressed.
797 sensitize_all_region_actions (true);
802 Editor::set_entered_track (TimeAxisView* tav)
805 entered_track->exited ();
808 if ((entered_track = tav) != 0) {
809 entered_track->entered ();
814 Editor::show_window ()
816 if (!is_visible ()) {
819 /* re-hide stuff if necessary */
820 editor_list_button_toggled ();
821 parameter_changed ("show-summary");
822 parameter_changed ("show-edit-group-tabs");
823 parameter_changed ("show-zoom-tools");
825 /* now reset all audio_time_axis heights, because widgets might need
831 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
832 tv = (static_cast<TimeAxisView*>(*i));
841 Editor::instant_save ()
843 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
848 _session->add_instant_xml(get_state());
850 Config->add_instant_xml(get_state());
855 Editor::zoom_adjustment_changed ()
861 double fpu = zoom_range_clock.current_duration() / _canvas_width;
865 zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width));
866 } else if (fpu > _session->current_end_frame() / _canvas_width) {
867 fpu = _session->current_end_frame() / _canvas_width;
868 zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width));
875 Editor::control_scroll (float fraction)
877 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
883 double step = fraction * current_page_frames();
886 _control_scroll_target is an optional<T>
888 it acts like a pointer to an framepos_t, with
889 a operator conversion to boolean to check
890 that it has a value could possibly use
891 playhead_cursor->current_frame to store the
892 value and a boolean in the class to know
893 when it's out of date
896 if (!_control_scroll_target) {
897 _control_scroll_target = _session->transport_frame();
898 _dragging_playhead = true;
901 if ((fraction < 0.0f) && (*_control_scroll_target < (framepos_t) fabs(step))) {
902 *_control_scroll_target = 0;
903 } else if ((fraction > 0.0f) && (max_framepos - *_control_scroll_target < step)) {
904 *_control_scroll_target = max_framepos - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
906 *_control_scroll_target += (framepos_t) floor (step);
909 /* move visuals, we'll catch up with it later */
911 playhead_cursor->set_position (*_control_scroll_target);
912 UpdateAllTransportClocks (*_control_scroll_target);
914 if (*_control_scroll_target > (current_page_frames() / 2)) {
915 /* try to center PH in window */
916 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
922 Now we do a timeout to actually bring the session to the right place
923 according to the playhead. This is to avoid reading disk buffers on every
924 call to control_scroll, which is driven by ScrollTimeline and therefore
925 probably by a control surface wheel which can generate lots of events.
927 /* cancel the existing timeout */
929 control_scroll_connection.disconnect ();
931 /* add the next timeout */
933 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
937 Editor::deferred_control_scroll (framepos_t /*target*/)
939 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
940 // reset for next stream
941 _control_scroll_target = boost::none;
942 _dragging_playhead = false;
947 Editor::access_action (std::string action_group, std::string action_item)
953 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
956 act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
964 Editor::on_realize ()
966 Window::on_realize ();
971 Editor::map_position_change (framepos_t frame)
973 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, frame)
979 if (_follow_playhead) {
980 center_screen (frame);
983 playhead_cursor->set_position (frame);
987 Editor::center_screen (framepos_t frame)
989 double page = _canvas_width * frames_per_unit;
991 /* if we're off the page, then scroll.
994 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
995 center_screen_internal (frame, page);
1000 Editor::center_screen_internal (framepos_t frame, float page)
1005 frame -= (framepos_t) page;
1010 reset_x_origin (frame);
1015 Editor::update_title ()
1017 ENSURE_GUI_THREAD (*this, &Editor::update_title)
1020 bool dirty = _session->dirty();
1022 string session_name;
1024 if (_session->snap_name() != _session->name()) {
1025 session_name = _session->snap_name();
1027 session_name = _session->name();
1031 session_name = "*" + session_name;
1034 WindowTitle title(session_name);
1035 title += Glib::get_application_name();
1036 set_title (title.get_string());
1041 Editor::set_session (Session *t)
1043 SessionHandlePtr::set_session (t);
1049 zoom_range_clock.set_session (_session);
1050 _playlist_selector->set_session (_session);
1051 nudge_clock.set_session (_session);
1052 _summary->set_session (_session);
1053 _group_tabs->set_session (_session);
1054 _route_groups->set_session (_session);
1055 _regions->set_session (_session);
1056 _snapshots->set_session (_session);
1057 _routes->set_session (_session);
1058 _locations->set_session (_session);
1060 if (rhythm_ferret) {
1061 rhythm_ferret->set_session (_session);
1064 if (analysis_window) {
1065 analysis_window->set_session (_session);
1069 sfbrowser->set_session (_session);
1072 compute_fixed_ruler_scale ();
1074 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1075 set_state (*node, Stateful::loading_state_version);
1077 /* catch up with the playhead */
1079 _session->request_locate (playhead_cursor->current_frame);
1080 _pending_initial_locate = true;
1084 /* These signals can all be emitted by a non-GUI thread. Therefore the
1085 handlers for them must not attempt to directly interact with the GUI,
1086 but use Gtkmm2ext::UI::instance()->call_slot();
1089 _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), ui_bind(&Editor::step_edit_status_change, this, _1), gui_context());
1090 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1091 _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context());
1092 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context());
1093 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1094 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context());
1095 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1096 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
1097 _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Editor::session_state_saved, this, _1), gui_context());
1098 _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context());
1099 _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&Editor::location_gone, this, _1), gui_context());
1100 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1101 _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display_s, this, _1), gui_context());
1102 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1104 if (Profile->get_sae()) {
1105 Timecode::BBT_Time bbt;
1109 framepos_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1);
1110 nudge_clock.set_mode(AudioClock::BBT);
1111 nudge_clock.set (pos, true, 0, AudioClock::BBT);
1114 nudge_clock.set (_session->frame_rate() * 5, true, 0, AudioClock::Timecode); // default of 5 seconds
1117 playhead_cursor->canvas_item.show ();
1119 Location* loc = _session->locations()->auto_loop_location();
1121 loc = new Location (*_session, 0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1123 if (loc->start() == loc->end()) {
1124 loc->set_end (loc->start() + 1);
1127 _session->locations()->add (loc, false);
1128 _session->set_auto_loop_location (loc);
1131 loc->set_name (_("Loop"));
1134 loc = _session->locations()->auto_punch_location();
1137 loc = new Location (*_session, 0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1139 if (loc->start() == loc->end()) {
1140 loc->set_end (loc->start() + 1);
1143 _session->locations()->add (loc, false);
1144 _session->set_auto_punch_location (loc);
1147 loc->set_name (_("Punch"));
1150 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1151 Config->map_parameters (pc);
1152 _session->config.map_parameters (pc);
1154 refresh_location_display ();
1156 restore_ruler_visibility ();
1157 //tempo_map_changed (PropertyChange (0));
1158 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1160 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1161 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1164 super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
1165 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1168 switch (_snap_type) {
1169 case SnapToRegionStart:
1170 case SnapToRegionEnd:
1171 case SnapToRegionSync:
1172 case SnapToRegionBoundary:
1173 build_region_boundary_cache ();
1180 /* register for undo history */
1181 _session->register_with_memento_command_factory(_id, this);
1183 ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated));
1185 start_updating_meters ();
1189 Editor::action_pre_activated (Glib::RefPtr<Action> const & a)
1191 if (a->get_name() == "RegionMenu") {
1192 /* When the main menu's region menu is opened, we setup the actions so that they look right
1193 in the menu. I can't find a way of getting a signal when this menu is subsequently closed,
1194 so we resensitize all region actions when the entered regionview or the region selection
1195 changes. HOWEVER we can't always resensitize on entered_regionview change because that
1196 happens after the region context menu is opened. So we set a flag here, too.
1200 sensitize_the_right_region_actions ();
1201 _last_region_menu_was_main = true;
1205 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1207 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1209 using namespace Menu_Helpers;
1210 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1213 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1217 MenuList& items (fade_context_menu.items());
1221 switch (item_type) {
1223 case FadeInHandleItem:
1224 if (arv->audio_region()->fade_in_active()) {
1225 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1227 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1230 items.push_back (SeparatorElem());
1232 if (Profile->get_sae()) {
1234 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)));
1235 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)));
1242 *_fade_in_images[FadeLinear],
1243 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)
1247 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1252 *_fade_in_images[FadeFast],
1253 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)
1256 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1261 *_fade_in_images[FadeLogB],
1262 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogB)
1265 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1270 *_fade_in_images[FadeLogA],
1271 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogA)
1274 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1279 *_fade_in_images[FadeSlow],
1280 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow)
1283 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1289 case FadeOutHandleItem:
1290 if (arv->audio_region()->fade_out_active()) {
1291 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1293 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1296 items.push_back (SeparatorElem());
1298 if (Profile->get_sae()) {
1299 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)));
1300 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)));
1306 *_fade_out_images[FadeLinear],
1307 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)
1311 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1316 *_fade_out_images[FadeFast],
1317 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)
1320 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1325 *_fade_out_images[FadeLogB],
1326 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogA)
1329 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1334 *_fade_out_images[FadeLogA],
1335 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogB)
1338 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1343 *_fade_out_images[FadeSlow],
1344 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast)
1347 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1353 fatal << _("programming error: ")
1354 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1359 fade_context_menu.popup (button, time);
1363 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1365 using namespace Menu_Helpers;
1366 Menu* (Editor::*build_menu_function)();
1369 switch (item_type) {
1371 case RegionViewName:
1372 case RegionViewNameHighlight:
1373 case LeftFrameHandle:
1374 case RightFrameHandle:
1375 if (with_selection) {
1376 build_menu_function = &Editor::build_track_selection_context_menu;
1378 build_menu_function = &Editor::build_track_region_context_menu;
1383 if (with_selection) {
1384 build_menu_function = &Editor::build_track_selection_context_menu;
1386 build_menu_function = &Editor::build_track_context_menu;
1390 case CrossfadeViewItem:
1391 build_menu_function = &Editor::build_track_crossfade_context_menu;
1395 if (clicked_routeview->track()) {
1396 build_menu_function = &Editor::build_track_context_menu;
1398 build_menu_function = &Editor::build_track_bus_context_menu;
1403 /* probably shouldn't happen but if it does, we don't care */
1407 menu = (this->*build_menu_function)();
1408 menu->set_name ("ArdourContextMenu");
1410 /* now handle specific situations */
1412 switch (item_type) {
1414 case RegionViewName:
1415 case RegionViewNameHighlight:
1416 case LeftFrameHandle:
1417 case RightFrameHandle:
1418 if (!with_selection) {
1419 if (region_edit_menu_split_item) {
1420 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1421 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1423 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1426 if (region_edit_menu_split_multichannel_item) {
1427 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1428 region_edit_menu_split_multichannel_item->set_sensitive (true);
1430 region_edit_menu_split_multichannel_item->set_sensitive (false);
1439 case CrossfadeViewItem:
1446 /* probably shouldn't happen but if it does, we don't care */
1450 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1452 /* Bounce to disk */
1454 using namespace Menu_Helpers;
1455 MenuList& edit_items = menu->items();
1457 edit_items.push_back (SeparatorElem());
1459 switch (clicked_routeview->audio_track()->freeze_state()) {
1460 case AudioTrack::NoFreeze:
1461 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1464 case AudioTrack::Frozen:
1465 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1468 case AudioTrack::UnFrozen:
1469 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1475 if (item_type == StreamItem && clicked_routeview) {
1476 clicked_routeview->build_underlay_menu(menu);
1479 /* When the region menu is opened, we setup the actions so that they look right
1482 sensitize_the_right_region_actions ();
1483 _last_region_menu_was_main = false;
1485 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1486 menu->popup (button, time);
1490 Editor::build_track_context_menu ()
1492 using namespace Menu_Helpers;
1494 MenuList& edit_items = track_context_menu.items();
1497 add_dstream_context_items (edit_items);
1498 return &track_context_menu;
1502 Editor::build_track_bus_context_menu ()
1504 using namespace Menu_Helpers;
1506 MenuList& edit_items = track_context_menu.items();
1509 add_bus_context_items (edit_items);
1510 return &track_context_menu;
1514 Editor::build_track_region_context_menu ()
1516 using namespace Menu_Helpers;
1517 MenuList& edit_items = track_region_context_menu.items();
1520 /* we've just cleared the track region context menu, so the menu that these
1521 two items were on will have disappeared; stop them dangling.
1523 region_edit_menu_split_item = 0;
1524 region_edit_menu_split_multichannel_item = 0;
1526 /* we might try to use items that are currently attached to a crossfade menu,
1529 track_crossfade_context_menu.items().clear ();
1531 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1534 boost::shared_ptr<Track> tr;
1535 boost::shared_ptr<Playlist> pl;
1537 /* Don't offer a region submenu if we are in internal edit mode, as we don't select regions in this
1538 mode and so offering region context is somewhat confusing.
1540 if ((tr = rtv->track()) && ((pl = tr->playlist())) && !internal_editing()) {
1541 framepos_t const framepos = (framepos_t) floor ((double) get_preferred_edit_position() * tr->speed());
1542 uint32_t regions_at = pl->count_regions_at (framepos);
1543 add_region_context_items (edit_items, regions_at > 1);
1547 add_dstream_context_items (edit_items);
1549 return &track_region_context_menu;
1553 Editor::build_track_crossfade_context_menu ()
1555 using namespace Menu_Helpers;
1556 MenuList& edit_items = track_crossfade_context_menu.items();
1557 edit_items.clear ();
1559 /* we might try to use items that are currently attached to a crossfade menu,
1562 track_region_context_menu.items().clear ();
1564 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1567 boost::shared_ptr<Track> tr;
1568 boost::shared_ptr<Playlist> pl;
1569 boost::shared_ptr<AudioPlaylist> apl;
1571 if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1573 AudioPlaylist::Crossfades xfades;
1575 apl->crossfades_at (get_preferred_edit_position (), xfades);
1577 bool many = xfades.size() > 1;
1579 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1580 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1583 framepos_t framepos = (framepos_t) floor ((double) get_preferred_edit_position() * tr->speed());
1584 uint32_t regions_at = pl->count_regions_at (framepos);
1585 add_region_context_items (edit_items, regions_at > 1);
1589 add_dstream_context_items (edit_items);
1591 return &track_crossfade_context_menu;
1595 Editor::analyze_region_selection ()
1597 if (analysis_window == 0) {
1598 analysis_window = new AnalysisWindow();
1601 analysis_window->set_session(_session);
1603 analysis_window->show_all();
1606 analysis_window->set_regionmode();
1607 analysis_window->analyze();
1609 analysis_window->present();
1613 Editor::analyze_range_selection()
1615 if (analysis_window == 0) {
1616 analysis_window = new AnalysisWindow();
1619 analysis_window->set_session(_session);
1621 analysis_window->show_all();
1624 analysis_window->set_rangemode();
1625 analysis_window->analyze();
1627 analysis_window->present();
1631 Editor::build_track_selection_context_menu ()
1633 using namespace Menu_Helpers;
1634 MenuList& edit_items = track_selection_context_menu.items();
1635 edit_items.clear ();
1637 add_selection_context_items (edit_items);
1638 // edit_items.push_back (SeparatorElem());
1639 // add_dstream_context_items (edit_items);
1641 return &track_selection_context_menu;
1644 /** Add context menu items relevant to crossfades.
1645 * @param edit_items List to add the items to.
1648 Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1650 using namespace Menu_Helpers;
1651 Menu *xfade_menu = manage (new Menu);
1652 MenuList& items = xfade_menu->items();
1653 xfade_menu->set_name ("ArdourContextMenu");
1656 if (xfade->active()) {
1662 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1663 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1665 if (xfade->can_follow_overlap()) {
1667 if (xfade->following_overlap()) {
1668 str = _("Convert to Short");
1670 str = _("Convert to Full");
1673 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1677 str = xfade->out()->name();
1679 str += xfade->in()->name();
1681 str = _("Crossfade");
1684 edit_items.push_back (MenuElem (str, *xfade_menu));
1685 edit_items.push_back (SeparatorElem());
1689 Editor::xfade_edit_left_region ()
1691 if (clicked_crossfadeview) {
1692 clicked_crossfadeview->left_view.show_region_editor ();
1697 Editor::xfade_edit_right_region ()
1699 if (clicked_crossfadeview) {
1700 clicked_crossfadeview->right_view.show_region_editor ();
1705 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, bool multiple_regions_at_position)
1707 using namespace Menu_Helpers;
1709 /* OK, stick the region submenu at the top of the list, and then add
1713 /* we have to hack up the region name because "_" has a special
1714 meaning for menu titles.
1717 RegionSelection rs = get_regions_from_selection_and_entered ();
1719 string::size_type pos = 0;
1720 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1722 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1723 menu_item_name.replace (pos, 1, "__");
1727 if (_popup_region_menu_item == 0) {
1728 _popup_region_menu_item = manage (new MenuItem (menu_item_name));
1729 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1730 _popup_region_menu_item->show ();
1732 _popup_region_menu_item->set_label (menu_item_name);
1735 edit_items.push_back (*_popup_region_menu_item);
1736 if (multiple_regions_at_position && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1737 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region")->create_menu_item ()));
1739 edit_items.push_back (SeparatorElem());
1742 /** Add context menu items relevant to selection ranges.
1743 * @param edit_items List to add the items to.
1746 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1748 using namespace Menu_Helpers;
1750 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1751 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1753 edit_items.push_back (SeparatorElem());
1754 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1756 if (!selection->regions.empty()) {
1757 edit_items.push_back (SeparatorElem());
1758 edit_items.push_back (MenuElem (_("Extend Range to End of Region"), sigc::bind (sigc::mem_fun(*this, &Editor::extend_selection_to_end_of_region), false)));
1759 edit_items.push_back (MenuElem (_("Extend Range to Start of Region"), sigc::bind (sigc::mem_fun(*this, &Editor::extend_selection_to_start_of_region), false)));
1762 edit_items.push_back (SeparatorElem());
1763 edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection)));
1764 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1766 edit_items.push_back (SeparatorElem());
1767 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1769 edit_items.push_back (SeparatorElem());
1770 edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1771 edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1773 edit_items.push_back (SeparatorElem());
1774 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1776 edit_items.push_back (SeparatorElem());
1777 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1778 edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1779 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1781 edit_items.push_back (SeparatorElem());
1782 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1783 edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1784 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1785 edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1786 edit_items.push_back (MenuElem (_("Export Range"), sigc::mem_fun(*this, &Editor::export_selection)));
1791 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1793 using namespace Menu_Helpers;
1797 Menu *play_menu = manage (new Menu);
1798 MenuList& play_items = play_menu->items();
1799 play_menu->set_name ("ArdourContextMenu");
1801 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1802 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1803 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1804 play_items.push_back (SeparatorElem());
1805 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
1807 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1811 Menu *select_menu = manage (new Menu);
1812 MenuList& select_items = select_menu->items();
1813 select_menu->set_name ("ArdourContextMenu");
1815 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1816 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1817 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1818 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1819 select_items.push_back (SeparatorElem());
1820 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
1821 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
1822 select_items.push_back (SeparatorElem());
1823 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1824 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1825 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1826 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1827 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
1828 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
1829 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
1831 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1835 Menu *cutnpaste_menu = manage (new Menu);
1836 MenuList& cutnpaste_items = cutnpaste_menu->items();
1837 cutnpaste_menu->set_name ("ArdourContextMenu");
1839 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1840 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1841 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1843 cutnpaste_items.push_back (SeparatorElem());
1845 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
1846 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
1848 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
1850 /* Adding new material */
1852 edit_items.push_back (SeparatorElem());
1853 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
1854 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
1858 Menu *nudge_menu = manage (new Menu());
1859 MenuList& nudge_items = nudge_menu->items();
1860 nudge_menu->set_name ("ArdourContextMenu");
1862 edit_items.push_back (SeparatorElem());
1863 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1864 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1865 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1866 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1868 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1872 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
1874 using namespace Menu_Helpers;
1878 Menu *play_menu = manage (new Menu);
1879 MenuList& play_items = play_menu->items();
1880 play_menu->set_name ("ArdourContextMenu");
1882 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1883 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1884 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1888 Menu *select_menu = manage (new Menu);
1889 MenuList& select_items = select_menu->items();
1890 select_menu->set_name ("ArdourContextMenu");
1892 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1893 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1894 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1895 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1896 select_items.push_back (SeparatorElem());
1897 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1898 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1899 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1900 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1902 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1906 Menu *cutnpaste_menu = manage (new Menu);
1907 MenuList& cutnpaste_items = cutnpaste_menu->items();
1908 cutnpaste_menu->set_name ("ArdourContextMenu");
1910 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1911 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1912 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1914 Menu *nudge_menu = manage (new Menu());
1915 MenuList& nudge_items = nudge_menu->items();
1916 nudge_menu->set_name ("ArdourContextMenu");
1918 edit_items.push_back (SeparatorElem());
1919 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1920 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1921 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1922 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1924 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1928 Editor::snap_type() const
1934 Editor::snap_mode() const
1940 Editor::set_snap_to (SnapType st)
1942 unsigned int snap_ind = (unsigned int)st;
1946 if (snap_ind > snap_type_strings.size() - 1) {
1948 _snap_type = (SnapType)snap_ind;
1951 string str = snap_type_strings[snap_ind];
1953 if (str != snap_type_selector.get_active_text()) {
1954 snap_type_selector.set_active_text (str);
1959 switch (_snap_type) {
1960 case SnapToBeatDiv32:
1961 case SnapToBeatDiv28:
1962 case SnapToBeatDiv24:
1963 case SnapToBeatDiv20:
1964 case SnapToBeatDiv16:
1965 case SnapToBeatDiv14:
1966 case SnapToBeatDiv12:
1967 case SnapToBeatDiv10:
1968 case SnapToBeatDiv8:
1969 case SnapToBeatDiv7:
1970 case SnapToBeatDiv6:
1971 case SnapToBeatDiv5:
1972 case SnapToBeatDiv4:
1973 case SnapToBeatDiv3:
1974 case SnapToBeatDiv2:
1975 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
1976 update_tempo_based_rulers ();
1979 case SnapToRegionStart:
1980 case SnapToRegionEnd:
1981 case SnapToRegionSync:
1982 case SnapToRegionBoundary:
1983 build_region_boundary_cache ();
1991 SnapChanged (); /* EMIT SIGNAL */
1995 Editor::set_snap_mode (SnapMode mode)
1998 string str = snap_mode_strings[(int)mode];
2000 if (str != snap_mode_selector.get_active_text ()) {
2001 snap_mode_selector.set_active_text (str);
2007 Editor::set_edit_point_preference (EditPoint ep, bool force)
2009 bool changed = (_edit_point != ep);
2012 string str = edit_point_strings[(int)ep];
2014 if (str != edit_point_selector.get_active_text ()) {
2015 edit_point_selector.set_active_text (str);
2018 set_canvas_cursor ();
2020 if (!force && !changed) {
2024 const char* action=NULL;
2026 switch (_edit_point) {
2027 case EditAtPlayhead:
2028 action = "edit-at-playhead";
2030 case EditAtSelectedMarker:
2031 action = "edit-at-marker";
2034 action = "edit-at-mouse";
2038 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2040 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2044 bool in_track_canvas;
2046 if (!mouse_frame (foo, in_track_canvas)) {
2047 in_track_canvas = false;
2050 reset_canvas_action_sensitivity (in_track_canvas);
2056 Editor::set_state (const XMLNode& node, int /*version*/)
2058 const XMLProperty* prop;
2060 int x, y, xoff, yoff;
2063 if ((prop = node.property ("id")) != 0) {
2064 _id = prop->value ();
2067 g.base_width = default_width;
2068 g.base_height = default_height;
2074 if ((geometry = find_named_node (node, "geometry")) != 0) {
2078 if ((prop = geometry->property("x_size")) == 0) {
2079 prop = geometry->property ("x-size");
2082 g.base_width = atoi(prop->value());
2084 if ((prop = geometry->property("y_size")) == 0) {
2085 prop = geometry->property ("y-size");
2088 g.base_height = atoi(prop->value());
2091 if ((prop = geometry->property ("x_pos")) == 0) {
2092 prop = geometry->property ("x-pos");
2095 x = atoi (prop->value());
2098 if ((prop = geometry->property ("y_pos")) == 0) {
2099 prop = geometry->property ("y-pos");
2102 y = atoi (prop->value());
2105 if ((prop = geometry->property ("x_off")) == 0) {
2106 prop = geometry->property ("x-off");
2109 xoff = atoi (prop->value());
2111 if ((prop = geometry->property ("y_off")) == 0) {
2112 prop = geometry->property ("y-off");
2115 yoff = atoi (prop->value());
2119 //set_default_size (g.base_width, g.base_height);
2122 if (_session && (prop = node.property ("playhead"))) {
2124 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2125 playhead_cursor->set_position (pos);
2127 playhead_cursor->set_position (0);
2130 if ((prop = node.property ("mixer-width"))) {
2131 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2134 if ((prop = node.property ("zoom-focus"))) {
2135 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2138 if ((prop = node.property ("zoom"))) {
2139 reset_zoom (PBD::atof (prop->value()));
2141 reset_zoom (frames_per_unit);
2144 if ((prop = node.property ("snap-to"))) {
2145 set_snap_to ((SnapType) atoi (prop->value()));
2148 if ((prop = node.property ("snap-mode"))) {
2149 set_snap_mode ((SnapMode) atoi (prop->value()));
2152 if ((prop = node.property ("mouse-mode"))) {
2153 MouseMode m = str2mousemode(prop->value());
2154 set_mouse_mode (m, true);
2156 set_mouse_mode (MouseObject, true);
2159 if ((prop = node.property ("left-frame")) != 0) {
2161 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2162 reset_x_origin (pos);
2166 if ((prop = node.property ("y-origin")) != 0) {
2167 reset_y_origin (atof (prop->value ()));
2170 if ((prop = node.property ("internal-edit"))) {
2171 bool yn = string_is_affirmative (prop->value());
2172 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2174 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2175 tact->set_active (!yn);
2176 tact->set_active (yn);
2180 if ((prop = node.property ("join-object-range"))) {
2181 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2184 if ((prop = node.property ("edit-point"))) {
2185 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2188 if ((prop = node.property ("show-measures"))) {
2189 bool yn = string_is_affirmative (prop->value());
2190 _show_measures = yn;
2191 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2193 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2194 /* do it twice to force the change */
2195 tact->set_active (!yn);
2196 tact->set_active (yn);
2200 if ((prop = node.property ("follow-playhead"))) {
2201 bool yn = string_is_affirmative (prop->value());
2202 set_follow_playhead (yn);
2203 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2205 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2206 if (tact->get_active() != yn) {
2207 tact->set_active (yn);
2212 if ((prop = node.property ("stationary-playhead"))) {
2213 bool yn = (prop->value() == "yes");
2214 set_stationary_playhead (yn);
2215 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2217 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2218 if (tact->get_active() != yn) {
2219 tact->set_active (yn);
2224 if ((prop = node.property ("region-list-sort-type"))) {
2225 RegionListSortType st;
2226 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2229 if ((prop = node.property ("xfades-visible"))) {
2230 bool yn = string_is_affirmative (prop->value());
2231 _xfade_visibility = !yn;
2232 // set_xfade_visibility (yn);
2235 if ((prop = node.property ("show-editor-mixer"))) {
2237 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2240 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2241 bool yn = string_is_affirmative (prop->value());
2243 /* do it twice to force the change */
2245 tact->set_active (!yn);
2246 tact->set_active (yn);
2249 if ((prop = node.property ("show-editor-list"))) {
2251 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2254 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2255 bool yn = string_is_affirmative (prop->value());
2257 /* do it twice to force the change */
2259 tact->set_active (!yn);
2260 tact->set_active (yn);
2263 if ((prop = node.property (X_("editor-list-page")))) {
2264 _the_notebook.set_current_page (atoi (prop->value ()));
2267 if ((prop = node.property (X_("show-marker-lines")))) {
2268 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2270 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2271 bool yn = string_is_affirmative (prop->value ());
2273 tact->set_active (!yn);
2274 tact->set_active (yn);
2277 XMLNodeList children = node.children ();
2278 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2279 selection->set_state (**i, Stateful::current_state_version);
2280 _regions->set_state (**i);
2287 Editor::get_state ()
2289 XMLNode* node = new XMLNode ("Editor");
2292 _id.print (buf, sizeof (buf));
2293 node->add_property ("id", buf);
2295 if (is_realized()) {
2296 Glib::RefPtr<Gdk::Window> win = get_window();
2298 int x, y, xoff, yoff, width, height;
2299 win->get_root_origin(x, y);
2300 win->get_position(xoff, yoff);
2301 win->get_size(width, height);
2303 XMLNode* geometry = new XMLNode ("geometry");
2305 snprintf(buf, sizeof(buf), "%d", width);
2306 geometry->add_property("x-size", string(buf));
2307 snprintf(buf, sizeof(buf), "%d", height);
2308 geometry->add_property("y-size", string(buf));
2309 snprintf(buf, sizeof(buf), "%d", x);
2310 geometry->add_property("x-pos", string(buf));
2311 snprintf(buf, sizeof(buf), "%d", y);
2312 geometry->add_property("y-pos", string(buf));
2313 snprintf(buf, sizeof(buf), "%d", xoff);
2314 geometry->add_property("x-off", string(buf));
2315 snprintf(buf, sizeof(buf), "%d", yoff);
2316 geometry->add_property("y-off", string(buf));
2317 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2318 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2319 geometry->add_property("notebook-shrunk", _notebook_shrunk ? "1" : "0");
2320 snprintf(buf,sizeof(buf), "%d",pre_maximal_horizontal_pane_position);
2321 geometry->add_property("pre-maximal-horizontal-pane-position", string(buf));
2322 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2323 geometry->add_property("edit-vertical-pane-pos", string(buf));
2325 node->add_child_nocopy (*geometry);
2328 maybe_add_mixer_strip_width (*node);
2330 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2331 node->add_property ("zoom-focus", buf);
2332 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2333 node->add_property ("zoom", buf);
2334 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2335 node->add_property ("snap-to", buf);
2336 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2337 node->add_property ("snap-mode", buf);
2339 node->add_property ("edit-point", enum_2_string (_edit_point));
2341 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2342 node->add_property ("playhead", buf);
2343 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2344 node->add_property ("left-frame", buf);
2345 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2346 node->add_property ("y-origin", buf);
2348 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2349 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2350 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2351 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2352 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2353 node->add_property ("mouse-mode", enum2str(mouse_mode));
2354 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2355 node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2357 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2359 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2360 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2363 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2365 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2366 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2369 snprintf (buf, sizeof (buf), "%d", _the_notebook.get_current_page ());
2370 node->add_property (X_("editor-list-page"), buf);
2372 node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no");
2374 node->add_child_nocopy (selection->get_state ());
2375 node->add_child_nocopy (_regions->get_state ());
2382 /** @param y y offset from the top of all trackviews.
2383 * @return pair: TimeAxisView that y is over, layer index.
2384 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2385 * in stacked region display mode, otherwise 0.
2387 std::pair<TimeAxisView *, layer_t>
2388 Editor::trackview_by_y_position (double y)
2390 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2392 std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
2398 return std::make_pair ( (TimeAxisView *) 0, 0);
2401 /** Snap a position to the grid, if appropriate, taking into account current
2402 * grid settings and also the state of any snap modifier keys that may be pressed.
2403 * @param start Position to snap.
2404 * @param event Event to get current key modifier information from, or 0.
2407 Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2409 if (!_session || !event) {
2413 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2414 if (_snap_mode == SnapOff) {
2415 snap_to_internal (start, direction, for_mark);
2418 if (_snap_mode != SnapOff) {
2419 snap_to_internal (start, direction, for_mark);
2425 Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark)
2427 if (!_session || _snap_mode == SnapOff) {
2431 snap_to_internal (start, direction, for_mark);
2435 Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /*for_mark*/)
2437 const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2438 framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2440 switch (_snap_type) {
2441 case SnapToTimecodeFrame:
2442 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2443 start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2445 start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2449 case SnapToTimecodeSeconds:
2450 if (_session->config.get_timecode_offset_negative()) {
2451 start += _session->config.get_timecode_offset ();
2453 start -= _session->config.get_timecode_offset ();
2455 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2456 start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2458 start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2461 if (_session->config.get_timecode_offset_negative()) {
2462 start -= _session->config.get_timecode_offset ();
2464 start += _session->config.get_timecode_offset ();
2468 case SnapToTimecodeMinutes:
2469 if (_session->config.get_timecode_offset_negative()) {
2470 start += _session->config.get_timecode_offset ();
2472 start -= _session->config.get_timecode_offset ();
2474 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2475 start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2477 start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2479 if (_session->config.get_timecode_offset_negative()) {
2480 start -= _session->config.get_timecode_offset ();
2482 start += _session->config.get_timecode_offset ();
2486 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2492 Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark)
2494 const framepos_t one_second = _session->frame_rate();
2495 const framepos_t one_minute = _session->frame_rate() * 60;
2496 framepos_t presnap = start;
2500 switch (_snap_type) {
2501 case SnapToTimecodeFrame:
2502 case SnapToTimecodeSeconds:
2503 case SnapToTimecodeMinutes:
2504 return timecode_snap_to_internal (start, direction, for_mark);
2507 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2508 start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2510 start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2515 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2516 start = (framepos_t) ceil ((double) start / one_second) * one_second;
2518 start = (framepos_t) floor ((double) start / one_second) * one_second;
2523 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2524 start = (framepos_t) ceil ((double) start / one_minute) * one_minute;
2526 start = (framepos_t) floor ((double) start / one_minute) * one_minute;
2531 start = _session->tempo_map().round_to_bar (start, direction);
2535 start = _session->tempo_map().round_to_beat (start, direction);
2538 case SnapToBeatDiv32:
2539 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2541 case SnapToBeatDiv28:
2542 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2544 case SnapToBeatDiv24:
2545 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2547 case SnapToBeatDiv20:
2548 start = _session->tempo_map().round_to_beat_subdivision (start, 20, direction);
2550 case SnapToBeatDiv16:
2551 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2553 case SnapToBeatDiv14:
2554 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2556 case SnapToBeatDiv12:
2557 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2559 case SnapToBeatDiv10:
2560 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2562 case SnapToBeatDiv8:
2563 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2565 case SnapToBeatDiv7:
2566 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2568 case SnapToBeatDiv6:
2569 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2571 case SnapToBeatDiv5:
2572 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2574 case SnapToBeatDiv4:
2575 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2577 case SnapToBeatDiv3:
2578 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2580 case SnapToBeatDiv2:
2581 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2589 _session->locations()->marks_either_side (start, before, after);
2591 if (before == max_framepos) {
2593 } else if (after == max_framepos) {
2595 } else if (before != max_framepos && after != max_framepos) {
2596 /* have before and after */
2597 if ((start - before) < (after - start)) {
2606 case SnapToRegionStart:
2607 case SnapToRegionEnd:
2608 case SnapToRegionSync:
2609 case SnapToRegionBoundary:
2610 if (!region_boundary_cache.empty()) {
2612 vector<framepos_t>::iterator prev = region_boundary_cache.end ();
2613 vector<framepos_t>::iterator next = region_boundary_cache.end ();
2615 if (direction > 0) {
2616 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2618 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2621 if (next != region_boundary_cache.begin ()) {
2626 framepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2627 framepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2629 if (start > (p + n) / 2) {
2638 switch (_snap_mode) {
2644 if (presnap > start) {
2645 if (presnap > (start + unit_to_frame(snap_threshold))) {
2649 } else if (presnap < start) {
2650 if (presnap < (start - unit_to_frame(snap_threshold))) {
2656 /* handled at entry */
2664 Editor::setup_toolbar ()
2668 /* Mode Buttons (tool selection) */
2670 mouse_move_button.set_relief(Gtk::RELIEF_NONE);
2671 mouse_select_button.set_relief(Gtk::RELIEF_NONE);
2672 mouse_gain_button.set_relief(Gtk::RELIEF_NONE);
2673 mouse_zoom_button.set_relief(Gtk::RELIEF_NONE);
2674 mouse_timefx_button.set_relief(Gtk::RELIEF_NONE);
2675 mouse_audition_button.set_relief(Gtk::RELIEF_NONE);
2676 // internal_edit_button.set_relief(Gtk::RELIEF_NONE);
2677 join_object_range_button.set_relief(Gtk::RELIEF_NONE);
2679 HBox* mode_box = manage(new HBox);
2680 mode_box->set_border_width (2);
2681 mode_box->set_spacing(4);
2683 /* table containing mode buttons */
2685 HBox* mouse_mode_button_box = manage (new HBox ());
2687 if (Profile->get_sae()) {
2688 mouse_mode_button_box->pack_start (mouse_move_button);
2690 mouse_mode_button_box->pack_start (mouse_move_button);
2691 mouse_mode_button_box->pack_start (join_object_range_button);
2692 mouse_mode_button_box->pack_start (mouse_select_button);
2695 mouse_mode_button_box->pack_start (mouse_zoom_button);
2697 if (!Profile->get_sae()) {
2698 mouse_mode_button_box->pack_start (mouse_gain_button);
2701 mouse_mode_button_box->pack_start (mouse_timefx_button);
2702 mouse_mode_button_box->pack_start (mouse_audition_button);
2703 mouse_mode_button_box->pack_start (internal_edit_button);
2705 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2706 if (!Profile->get_sae()) {
2707 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2709 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2711 edit_mode_selector.set_name ("EditModeSelector");
2712 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2713 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2715 mode_box->pack_start (edit_mode_selector);
2716 mode_box->pack_start (*mouse_mode_button_box);
2718 _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2719 _mouse_mode_tearoff->set_name ("MouseModeBase");
2720 _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2722 if (Profile->get_sae()) {
2723 _mouse_mode_tearoff->set_can_be_torn_off (false);
2726 _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2727 &_mouse_mode_tearoff->tearoff_window()));
2728 _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2729 &_mouse_mode_tearoff->tearoff_window(), 1));
2730 _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2731 &_mouse_mode_tearoff->tearoff_window()));
2732 _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2733 &_mouse_mode_tearoff->tearoff_window(), 1));
2735 mouse_move_button.set_mode (false);
2736 mouse_select_button.set_mode (false);
2737 mouse_gain_button.set_mode (false);
2738 mouse_zoom_button.set_mode (false);
2739 mouse_timefx_button.set_mode (false);
2740 mouse_audition_button.set_mode (false);
2741 join_object_range_button.set_mode (false);
2743 mouse_move_button.set_name ("MouseModeButton");
2744 mouse_select_button.set_name ("MouseModeButton");
2745 mouse_gain_button.set_name ("MouseModeButton");
2746 mouse_zoom_button.set_name ("MouseModeButton");
2747 mouse_timefx_button.set_name ("MouseModeButton");
2748 mouse_audition_button.set_name ("MouseModeButton");
2749 internal_edit_button.set_name ("MouseModeButton");
2750 join_object_range_button.set_name ("MouseModeButton");
2752 mouse_move_button.unset_flags (CAN_FOCUS);
2753 mouse_select_button.unset_flags (CAN_FOCUS);
2754 mouse_gain_button.unset_flags (CAN_FOCUS);
2755 mouse_zoom_button.unset_flags (CAN_FOCUS);
2756 mouse_timefx_button.unset_flags (CAN_FOCUS);
2757 mouse_audition_button.unset_flags (CAN_FOCUS);
2758 internal_edit_button.unset_flags (CAN_FOCUS);
2759 join_object_range_button.unset_flags (CAN_FOCUS);
2763 _zoom_box.set_spacing (1);
2764 _zoom_box.set_border_width (0);
2766 zoom_in_button.set_name ("EditorTimeButton");
2767 zoom_in_button.set_image (*(manage (new Image (Stock::ZOOM_IN, Gtk::ICON_SIZE_MENU))));
2768 zoom_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), false));
2770 zoom_out_button.set_name ("EditorTimeButton");
2771 zoom_out_button.set_image (*(manage (new Image (Stock::ZOOM_OUT, Gtk::ICON_SIZE_MENU))));
2772 zoom_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), true));
2774 zoom_out_full_button.set_name ("EditorTimeButton");
2775 zoom_out_full_button.set_image (*(manage (new Image (Stock::ZOOM_100, Gtk::ICON_SIZE_MENU))));
2776 zoom_out_full_button.signal_clicked().connect (sigc::mem_fun(*this, &Editor::temporal_zoom_session));
2778 zoom_focus_selector.set_name ("ZoomFocusSelector");
2779 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
2780 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2782 _zoom_box.pack_start (zoom_out_button, false, false);
2783 _zoom_box.pack_start (zoom_in_button, false, false);
2784 _zoom_box.pack_start (zoom_out_full_button, false, false);
2786 _zoom_box.pack_start (zoom_focus_selector);
2788 /* Track zoom buttons */
2789 tav_expand_button.set_name ("TrackHeightButton");
2790 tav_expand_button.set_size_request(-1,20);
2791 tav_expand_button.add (*(manage (new Image (::get_icon("tav_exp")))));
2792 tav_expand_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), true));
2794 tav_shrink_button.set_name ("TrackHeightButton");
2795 tav_shrink_button.set_size_request(-1,20);
2796 tav_shrink_button.add (*(manage (new Image (::get_icon("tav_shrink")))));
2797 tav_shrink_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), false));
2799 _zoom_box.pack_start (tav_shrink_button);
2800 _zoom_box.pack_start (tav_expand_button);
2802 _zoom_tearoff = manage (new TearOff (_zoom_box));
2804 _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2805 &_zoom_tearoff->tearoff_window()));
2806 _zoom_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2807 &_zoom_tearoff->tearoff_window(), 0));
2808 _zoom_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2809 &_zoom_tearoff->tearoff_window()));
2810 _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2811 &_zoom_tearoff->tearoff_window(), 0));
2813 snap_box.set_spacing (1);
2814 snap_box.set_border_width (2);
2816 snap_type_selector.set_name ("SnapTypeSelector");
2817 set_popdown_strings (snap_type_selector, snap_type_strings, true);
2818 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2820 snap_mode_selector.set_name ("SnapModeSelector");
2821 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
2822 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2824 edit_point_selector.set_name ("EditPointSelector");
2825 set_popdown_strings (edit_point_selector, edit_point_strings, true);
2826 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2828 snap_box.pack_start (snap_mode_selector, false, false);
2829 snap_box.pack_start (snap_type_selector, false, false);
2830 snap_box.pack_start (edit_point_selector, false, false);
2834 HBox *nudge_box = manage (new HBox);
2835 nudge_box->set_spacing(1);
2836 nudge_box->set_border_width (2);
2838 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2839 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2841 nudge_box->pack_start (nudge_backward_button, false, false);
2842 nudge_box->pack_start (nudge_forward_button, false, false);
2843 nudge_box->pack_start (nudge_clock, false, false);
2846 /* Pack everything in... */
2848 HBox* hbox = manage (new HBox);
2849 hbox->set_spacing(10);
2851 _tools_tearoff = manage (new TearOff (*hbox));
2852 _tools_tearoff->set_name ("MouseModeBase");
2853 _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
2855 if (Profile->get_sae()) {
2856 _tools_tearoff->set_can_be_torn_off (false);
2859 _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2860 &_tools_tearoff->tearoff_window()));
2861 _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2862 &_tools_tearoff->tearoff_window(), 0));
2863 _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2864 &_tools_tearoff->tearoff_window()));
2865 _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2866 &_tools_tearoff->tearoff_window(), 0));
2868 toolbar_hbox.set_spacing (10);
2869 toolbar_hbox.set_border_width (1);
2871 toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
2872 toolbar_hbox.pack_start (*_zoom_tearoff, false, false);
2873 toolbar_hbox.pack_start (*_tools_tearoff, false, false);
2875 hbox->pack_start (snap_box, false, false);
2876 hbox->pack_start (*nudge_box, false, false);
2877 hbox->pack_start (panic_box, false, false);
2881 toolbar_base.set_name ("ToolBarBase");
2882 toolbar_base.add (toolbar_hbox);
2884 _toolbar_viewport.add (toolbar_base);
2885 /* stick to the required height but allow width to vary if there's not enough room */
2886 _toolbar_viewport.set_size_request (1, -1);
2888 toolbar_frame.set_shadow_type (SHADOW_OUT);
2889 toolbar_frame.set_name ("BaseFrame");
2890 toolbar_frame.add (_toolbar_viewport);
2892 DPIReset.connect (sigc::mem_fun (*this, &Editor::resize_text_widgets));
2896 Editor::setup_tooltips ()
2898 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
2899 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain"));
2900 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
2901 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
2902 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2903 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
2904 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
2905 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
2906 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
2907 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
2908 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
2909 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
2910 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
2911 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
2912 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
2913 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
2914 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
2915 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
2916 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
2917 ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
2918 ARDOUR_UI::instance()->set_tip (midi_panic_button, _("Send note off and reset controller messages on all MIDI channels"));
2919 ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode"));
2923 Editor::midi_panic ()
2925 cerr << "MIDI panic\n";
2928 _session->midi_panic();
2933 Editor::setup_midi_toolbar ()
2937 /* Midi sound notes */
2938 midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
2939 midi_sound_notes.set_relief(Gtk::RELIEF_NONE);
2940 midi_sound_notes.unset_flags (CAN_FOCUS);
2944 act = ActionManager::get_action (X_("MIDI"), X_("panic"));
2945 midi_panic_button.set_name("MidiPanicButton");
2946 act->connect_proxy (midi_panic_button);
2948 panic_box.pack_start (midi_sound_notes , true, true);
2949 panic_box.pack_start (midi_panic_button, true, true);
2953 Editor::convert_drop_to_paths (
2954 vector<string>& paths,
2955 const RefPtr<Gdk::DragContext>& /*context*/,
2958 const SelectionData& data,
2962 if (_session == 0) {
2966 vector<string> uris = data.get_uris();
2970 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
2971 are actually URI lists. So do it by hand.
2974 if (data.get_target() != "text/plain") {
2978 /* Parse the "uri-list" format that Nautilus provides,
2979 where each pathname is delimited by \r\n.
2981 THERE MAY BE NO NULL TERMINATING CHAR!!!
2984 string txt = data.get_text();
2988 p = (const char *) malloc (txt.length() + 1);
2989 txt.copy ((char *) p, txt.length(), 0);
2990 ((char*)p)[txt.length()] = '\0';
2996 while (g_ascii_isspace (*p))
3000 while (*q && (*q != '\n') && (*q != '\r')) {
3007 while (q > p && g_ascii_isspace (*q))
3012 uris.push_back (string (p, q - p + 1));
3016 p = strchr (p, '\n');
3028 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3030 if ((*i).substr (0,7) == "file://") {
3033 PBD::url_decode (p);
3035 // scan forward past three slashes
3037 string::size_type slashcnt = 0;
3038 string::size_type n = 0;
3039 string::iterator x = p.begin();
3041 while (slashcnt < 3 && x != p.end()) {
3044 } else if (slashcnt == 3) {
3051 if (slashcnt != 3 || x == p.end()) {
3052 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3056 paths.push_back (p.substr (n - 1));
3064 Editor::new_tempo_section ()
3070 Editor::map_transport_state ()
3072 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3074 if (_session && _session->transport_stopped()) {
3075 have_pending_keyboard_selection = false;
3078 update_loop_range_view (true);
3083 Editor::State::State (PublicEditor const * e)
3085 selection = new Selection (e);
3088 Editor::State::~State ()
3094 Editor::begin_reversible_command (string name)
3097 _session->begin_reversible_command (name);
3102 Editor::begin_reversible_command (GQuark q)
3105 _session->begin_reversible_command (q);
3110 Editor::commit_reversible_command ()
3113 _session->commit_reversible_command ();
3118 Editor::history_changed ()
3122 if (undo_action && _session) {
3123 if (_session->undo_depth() == 0) {
3126 label = string_compose(_("Undo (%1)"), _session->next_undo());
3128 undo_action->property_label() = label;
3131 if (redo_action && _session) {
3132 if (_session->redo_depth() == 0) {
3135 label = string_compose(_("Redo (%1)"), _session->next_redo());
3137 redo_action->property_label() = label;
3142 Editor::duplicate_dialog (bool with_dialog)
3146 if (mouse_mode == MouseRange) {
3147 if (selection->time.length() == 0) {
3152 RegionSelection rs = get_regions_from_selection_and_entered ();
3154 if (mouse_mode != MouseRange && rs.empty()) {
3160 ArdourDialog win (_("Duplicate"));
3161 Label label (_("Number of duplications:"));
3162 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3163 SpinButton spinner (adjustment, 0.0, 1);
3166 win.get_vbox()->set_spacing (12);
3167 win.get_vbox()->pack_start (hbox);
3168 hbox.set_border_width (6);
3169 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3171 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3172 place, visually. so do this by hand.
3175 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3176 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3177 spinner.grab_focus();
3183 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3184 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3185 win.set_default_response (RESPONSE_ACCEPT);
3187 win.set_position (WIN_POS_MOUSE);
3189 spinner.grab_focus ();
3191 switch (win.run ()) {
3192 case RESPONSE_ACCEPT:
3198 times = adjustment.get_value();
3201 if (mouse_mode == MouseRange) {
3202 duplicate_selection (times);
3204 duplicate_some_regions (rs, times);
3209 Editor::show_verbose_canvas_cursor ()
3211 verbose_canvas_cursor->raise_to_top();
3212 verbose_canvas_cursor->show();
3213 verbose_cursor_visible = true;
3217 Editor::hide_verbose_canvas_cursor ()
3219 verbose_canvas_cursor->hide();
3220 verbose_cursor_visible = false;
3224 Editor::clamp_verbose_cursor_x (double x)
3229 x = min (_canvas_width - 200.0, x);
3235 Editor::clamp_verbose_cursor_y (double y)
3237 if (y < canvas_timebars_vsize) {
3238 y = canvas_timebars_vsize;
3240 y = min (_canvas_height - 50, y);
3246 Editor::show_verbose_canvas_cursor_with (const string & txt, int32_t xoffset, int32_t yoffset)
3248 verbose_canvas_cursor->property_text() = txt.c_str();
3253 track_canvas->get_pointer (x, y);
3254 track_canvas->window_to_world (x, y, wx, wy);
3259 /* don't get too close to the edge */
3260 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (wx);
3261 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (wy);
3263 show_verbose_canvas_cursor ();
3267 Editor::set_verbose_canvas_cursor (const string & txt, double x, double y)
3269 verbose_canvas_cursor->property_text() = txt.c_str();
3270 /* don't get too close to the edge */
3271 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (x);
3272 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (y);
3276 Editor::set_verbose_canvas_cursor_text (const string & txt)
3278 verbose_canvas_cursor->property_text() = txt.c_str();
3282 Editor::set_edit_mode (EditMode m)
3284 Config->set_edit_mode (m);
3288 Editor::cycle_edit_mode ()
3290 switch (Config->get_edit_mode()) {
3292 if (Profile->get_sae()) {
3293 Config->set_edit_mode (Lock);
3295 Config->set_edit_mode (Splice);
3299 Config->set_edit_mode (Lock);
3302 Config->set_edit_mode (Slide);
3308 Editor::edit_mode_selection_done ()
3310 string s = edit_mode_selector.get_active_text ();
3313 Config->set_edit_mode (string_to_edit_mode (s));
3318 Editor::snap_type_selection_done ()
3320 string choice = snap_type_selector.get_active_text();
3321 SnapType snaptype = SnapToBeat;
3323 if (choice == _("Beats/2")) {
3324 snaptype = SnapToBeatDiv2;
3325 } else if (choice == _("Beats/3")) {
3326 snaptype = SnapToBeatDiv3;
3327 } else if (choice == _("Beats/4")) {
3328 snaptype = SnapToBeatDiv4;
3329 } else if (choice == _("Beats/5")) {
3330 snaptype = SnapToBeatDiv5;
3331 } else if (choice == _("Beats/6")) {
3332 snaptype = SnapToBeatDiv6;
3333 } else if (choice == _("Beats/7")) {
3334 snaptype = SnapToBeatDiv7;
3335 } else if (choice == _("Beats/8")) {
3336 snaptype = SnapToBeatDiv8;
3337 } else if (choice == _("Beats/10")) {
3338 snaptype = SnapToBeatDiv10;
3339 } else if (choice == _("Beats/12")) {
3340 snaptype = SnapToBeatDiv12;
3341 } else if (choice == _("Beats/14")) {
3342 snaptype = SnapToBeatDiv14;
3343 } else if (choice == _("Beats/16")) {
3344 snaptype = SnapToBeatDiv16;
3345 } else if (choice == _("Beats/20")) {
3346 snaptype = SnapToBeatDiv20;
3347 } else if (choice == _("Beats/24")) {
3348 snaptype = SnapToBeatDiv24;
3349 } else if (choice == _("Beats/28")) {
3350 snaptype = SnapToBeatDiv28;
3351 } else if (choice == _("Beats/32")) {
3352 snaptype = SnapToBeatDiv32;
3353 } else if (choice == _("Beats")) {
3354 snaptype = SnapToBeat;
3355 } else if (choice == _("Bars")) {
3356 snaptype = SnapToBar;
3357 } else if (choice == _("Marks")) {
3358 snaptype = SnapToMark;
3359 } else if (choice == _("Region starts")) {
3360 snaptype = SnapToRegionStart;
3361 } else if (choice == _("Region ends")) {
3362 snaptype = SnapToRegionEnd;
3363 } else if (choice == _("Region bounds")) {
3364 snaptype = SnapToRegionBoundary;
3365 } else if (choice == _("Region syncs")) {
3366 snaptype = SnapToRegionSync;
3367 } else if (choice == _("CD Frames")) {
3368 snaptype = SnapToCDFrame;
3369 } else if (choice == _("Timecode Frames")) {
3370 snaptype = SnapToTimecodeFrame;
3371 } else if (choice == _("Timecode Seconds")) {
3372 snaptype = SnapToTimecodeSeconds;
3373 } else if (choice == _("Timecode Minutes")) {
3374 snaptype = SnapToTimecodeMinutes;
3375 } else if (choice == _("Seconds")) {
3376 snaptype = SnapToSeconds;
3377 } else if (choice == _("Minutes")) {
3378 snaptype = SnapToMinutes;
3381 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3383 ract->set_active ();
3388 Editor::snap_mode_selection_done ()
3390 string choice = snap_mode_selector.get_active_text();
3391 SnapMode mode = SnapNormal;
3393 if (choice == _("No Grid")) {
3395 } else if (choice == _("Grid")) {
3397 } else if (choice == _("Magnetic")) {
3398 mode = SnapMagnetic;
3401 RefPtr<RadioAction> ract = snap_mode_action (mode);
3404 ract->set_active (true);
3409 Editor::cycle_edit_point (bool with_marker)
3411 switch (_edit_point) {
3413 set_edit_point_preference (EditAtPlayhead);
3415 case EditAtPlayhead:
3417 set_edit_point_preference (EditAtSelectedMarker);
3419 set_edit_point_preference (EditAtMouse);
3422 case EditAtSelectedMarker:
3423 set_edit_point_preference (EditAtMouse);
3429 Editor::edit_point_selection_done ()
3431 string choice = edit_point_selector.get_active_text();
3432 EditPoint ep = EditAtSelectedMarker;
3434 if (choice == _("Marker")) {
3435 set_edit_point_preference (EditAtSelectedMarker);
3436 } else if (choice == _("Playhead")) {
3437 set_edit_point_preference (EditAtPlayhead);
3439 set_edit_point_preference (EditAtMouse);
3442 RefPtr<RadioAction> ract = edit_point_action (ep);
3445 ract->set_active (true);
3450 Editor::zoom_focus_selection_done ()
3452 string choice = zoom_focus_selector.get_active_text();
3453 ZoomFocus focus_type = ZoomFocusLeft;
3455 if (choice == _("Left")) {
3456 focus_type = ZoomFocusLeft;
3457 } else if (choice == _("Right")) {
3458 focus_type = ZoomFocusRight;
3459 } else if (choice == _("Center")) {
3460 focus_type = ZoomFocusCenter;
3461 } else if (choice == _("Playhead")) {
3462 focus_type = ZoomFocusPlayhead;
3463 } else if (choice == _("Mouse")) {
3464 focus_type = ZoomFocusMouse;
3465 } else if (choice == _("Edit point")) {
3466 focus_type = ZoomFocusEdit;
3469 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3472 ract->set_active ();
3477 Editor::edit_controls_button_release (GdkEventButton* ev)
3479 if (Keyboard::is_context_menu_event (ev)) {
3480 ARDOUR_UI::instance()->add_route (this);
3486 Editor::mouse_select_button_release (GdkEventButton* ev)
3488 /* this handles just right-clicks */
3490 if (ev->button != 3) {
3498 Editor::set_zoom_focus (ZoomFocus f)
3500 string str = zoom_focus_strings[(int)f];
3502 if (str != zoom_focus_selector.get_active_text()) {
3503 zoom_focus_selector.set_active_text (str);
3506 if (zoom_focus != f) {
3509 ZoomFocusChanged (); /* EMIT_SIGNAL */
3516 Editor::ensure_float (Window& win)
3518 win.set_transient_for (*this);
3522 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3524 /* recover or initialize pane positions. do this here rather than earlier because
3525 we don't want the positions to change the child allocations, which they seem to do.
3531 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3543 width = default_width;
3544 height = default_height;
3546 if ((geometry = find_named_node (*node, "geometry")) != 0) {
3548 prop = geometry->property ("x-size");
3550 width = atoi (prop->value());
3552 prop = geometry->property ("y-size");
3554 height = atoi (prop->value());
3558 if (which == static_cast<Paned*> (&edit_pane)) {
3560 if (done & Horizontal) {
3564 if (geometry && (prop = geometry->property ("notebook-shrunk"))) {
3565 _notebook_shrunk = string_is_affirmative (prop->value ());
3568 if (geometry && (prop = geometry->property ("pre-maximal-horizontal-pane-position"))) {
3569 pre_maximal_horizontal_pane_position = atoi (prop->value ());
3572 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3573 /* initial allocation is 90% to canvas, 10% to notebook */
3574 pos = (int) floor (alloc.get_width() * 0.90f);
3575 snprintf (buf, sizeof(buf), "%d", pos);
3577 pos = atoi (prop->value());
3580 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3581 edit_pane.set_position (pos);
3582 if (pre_maximal_horizontal_pane_position == 0) {
3583 pre_maximal_horizontal_pane_position = pos;
3587 done = (Pane) (done | Horizontal);
3589 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3591 if (done & Vertical) {
3595 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3596 /* initial allocation is 90% to canvas, 10% to summary */
3597 pos = (int) floor (alloc.get_height() * 0.90f);
3598 snprintf (buf, sizeof(buf), "%d", pos);
3600 pos = atoi (prop->value());
3603 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3604 editor_summary_pane.set_position (pos);
3605 pre_maximal_vertical_pane_position = pos;
3608 done = (Pane) (done | Vertical);
3613 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3615 if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) {
3616 top_hbox.remove (toolbar_frame);
3621 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3623 if (toolbar_frame.get_parent() == 0) {
3624 top_hbox.pack_end (toolbar_frame);
3629 Editor::set_show_measures (bool yn)
3631 if (_show_measures != yn) {
3634 if ((_show_measures = yn) == true) {
3636 tempo_lines->show();
3644 Editor::toggle_follow_playhead ()
3646 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3648 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3649 set_follow_playhead (tact->get_active());
3653 /** @param yn true to follow playhead, otherwise false.
3654 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
3657 Editor::set_follow_playhead (bool yn, bool catch_up)
3659 if (_follow_playhead != yn) {
3660 if ((_follow_playhead = yn) == true && catch_up) {
3662 reset_x_origin_to_follow_playhead ();
3669 Editor::toggle_stationary_playhead ()
3671 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3673 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3674 set_stationary_playhead (tact->get_active());
3679 Editor::set_stationary_playhead (bool yn)
3681 if (_stationary_playhead != yn) {
3682 if ((_stationary_playhead = yn) == true) {
3684 // FIXME need a 3.0 equivalent of this 2.X call
3685 // update_current_screen ();
3692 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3694 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3696 xfade->set_active (!xfade->active());
3701 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3703 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3705 xfade->set_follow_overlap (!xfade->following_overlap());
3710 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3712 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3718 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3722 switch (cew.run ()) {
3723 case RESPONSE_ACCEPT:
3730 PropertyChange all_crossfade_properties;
3731 all_crossfade_properties.add (ARDOUR::Properties::active);
3732 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3733 xfade->PropertyChanged (all_crossfade_properties);
3737 Editor::playlist_selector () const
3739 return *_playlist_selector;
3743 Editor::get_grid_type_as_beats (bool& success, framepos_t position)
3747 switch (_snap_type) {
3752 case SnapToBeatDiv32:
3755 case SnapToBeatDiv28:
3758 case SnapToBeatDiv24:
3761 case SnapToBeatDiv20:
3764 case SnapToBeatDiv16:
3767 case SnapToBeatDiv14:
3770 case SnapToBeatDiv12:
3773 case SnapToBeatDiv10:
3776 case SnapToBeatDiv8:
3779 case SnapToBeatDiv7:
3782 case SnapToBeatDiv6:
3785 case SnapToBeatDiv5:
3788 case SnapToBeatDiv4:
3791 case SnapToBeatDiv3:
3794 case SnapToBeatDiv2:
3800 return _session->tempo_map().meter_at (position).beats_per_bar();
3805 case SnapToTimecodeFrame:
3806 case SnapToTimecodeSeconds:
3807 case SnapToTimecodeMinutes:
3810 case SnapToRegionStart:
3811 case SnapToRegionEnd:
3812 case SnapToRegionSync:
3813 case SnapToRegionBoundary:
3823 Editor::get_nudge_distance (framepos_t pos, framecnt_t& next)
3827 ret = nudge_clock.current_duration (pos);
3828 next = ret + 1; /* XXXX fix me */
3834 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3836 ArdourDialog dialog (_("Playlist Deletion"));
3837 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3838 "If left alone, no audio files used by it will be cleaned.\n"
3839 "If deleted, audio files used by it alone by will cleaned."),
3842 dialog.set_position (WIN_POS_CENTER);
3843 dialog.get_vbox()->pack_start (label);
3847 dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT);
3848 dialog.add_button (_("Keep playlist"), RESPONSE_REJECT);
3849 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3851 switch (dialog.run ()) {
3852 case RESPONSE_ACCEPT:
3853 /* delete the playlist */
3857 case RESPONSE_REJECT:
3858 /* keep the playlist */
3870 Editor::audio_region_selection_covers (framepos_t where)
3872 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3873 if ((*a)->region()->covers (where)) {
3882 Editor::prepare_for_cleanup ()
3884 cut_buffer->clear_regions ();
3885 cut_buffer->clear_playlists ();
3887 selection->clear_regions ();
3888 selection->clear_playlists ();
3890 _regions->suspend_redisplay ();
3894 Editor::finish_cleanup ()
3896 _regions->resume_redisplay ();
3900 Editor::transport_loop_location()
3903 return _session->locations()->auto_loop_location();
3910 Editor::transport_punch_location()
3913 return _session->locations()->auto_punch_location();
3920 Editor::control_layout_scroll (GdkEventScroll* ev)
3922 if (Keyboard::some_magic_widget_has_focus()) {
3926 switch (ev->direction) {
3928 scroll_tracks_up_line ();
3932 case GDK_SCROLL_DOWN:
3933 scroll_tracks_down_line ();
3937 /* no left/right handling yet */
3945 Editor::session_state_saved (string)
3948 _snapshots->redisplay ();
3952 Editor::maximise_editing_space ()
3954 _mouse_mode_tearoff->set_visible (false);
3955 _tools_tearoff->set_visible (false);
3956 _zoom_tearoff->set_visible (false);
3958 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
3959 pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
3960 pre_maximal_editor_width = this->get_width ();
3961 pre_maximal_editor_height = this->get_height ();
3963 if (post_maximal_horizontal_pane_position == 0) {
3964 post_maximal_horizontal_pane_position = edit_pane.get_width();
3967 if (post_maximal_vertical_pane_position == 0) {
3968 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
3973 if (post_maximal_editor_width) {
3974 edit_pane.set_position (post_maximal_horizontal_pane_position -
3975 abs(post_maximal_editor_width - pre_maximal_editor_width));
3977 edit_pane.set_position (post_maximal_horizontal_pane_position);
3980 if (post_maximal_editor_height) {
3981 editor_summary_pane.set_position (post_maximal_vertical_pane_position -
3982 abs(post_maximal_editor_height - pre_maximal_editor_height));
3984 editor_summary_pane.set_position (post_maximal_vertical_pane_position);
3987 if (Config->get_keep_tearoffs()) {
3988 _mouse_mode_tearoff->set_visible (true);
3989 _tools_tearoff->set_visible (true);
3990 if (Config->get_show_zoom_tools ()) {
3991 _zoom_tearoff->set_visible (true);
3998 Editor::restore_editing_space ()
4000 // user changed width/height of panes during fullscreen
4002 if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
4003 post_maximal_horizontal_pane_position = edit_pane.get_position();
4006 if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
4007 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
4012 _mouse_mode_tearoff->set_visible (true);
4013 _tools_tearoff->set_visible (true);
4014 if (Config->get_show_zoom_tools ()) {
4015 _zoom_tearoff->set_visible (true);
4017 post_maximal_editor_width = this->get_width();
4018 post_maximal_editor_height = this->get_height();
4020 edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
4021 editor_summary_pane.set_position (pre_maximal_vertical_pane_position + abs(this->get_height() - pre_maximal_editor_height));
4025 * Make new playlists for a given track and also any others that belong
4026 * to the same active route group with the `edit' property.
4031 Editor::new_playlists (TimeAxisView* v)
4033 begin_reversible_command (_("new playlists"));
4034 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4035 _session->playlists->get (playlists);
4036 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4037 commit_reversible_command ();
4041 * Use a copy of the current playlist for a given track and also any others that belong
4042 * to the same active route group with the `edit' property.
4047 Editor::copy_playlists (TimeAxisView* v)
4049 begin_reversible_command (_("copy playlists"));
4050 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4051 _session->playlists->get (playlists);
4052 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4053 commit_reversible_command ();
4056 /** Clear the current playlist for a given track and also any others that belong
4057 * to the same active route group with the `edit' property.
4062 Editor::clear_playlists (TimeAxisView* v)
4064 begin_reversible_command (_("clear playlists"));
4065 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4066 _session->playlists->get (playlists);
4067 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4068 commit_reversible_command ();
4072 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4074 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4078 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4080 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4084 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4086 atv.clear_playlist ();
4090 Editor::on_key_press_event (GdkEventKey* ev)
4092 return key_press_focus_accelerator_handler (*this, ev);
4096 Editor::on_key_release_event (GdkEventKey* ev)
4098 return Gtk::Window::on_key_release_event (ev);
4099 // return key_press_focus_accelerator_handler (*this, ev);
4102 /** Queue up a change to the viewport x origin.
4103 * @param frame New x origin.
4106 Editor::reset_x_origin (framepos_t frame)
4108 queue_visual_change (frame);
4112 Editor::reset_y_origin (double y)
4114 queue_visual_change_y (y);
4118 Editor::reset_zoom (double fpu)
4120 queue_visual_change (fpu);
4124 Editor::reposition_and_zoom (framepos_t frame, double fpu)
4126 reset_x_origin (frame);
4129 if (!no_save_visual) {
4130 undo_visual_stack.push_back (current_visual_state(false));
4134 Editor::VisualState*
4135 Editor::current_visual_state (bool with_tracks)
4137 VisualState* vs = new VisualState;
4138 vs->y_position = vertical_adjustment.get_value();
4139 vs->frames_per_unit = frames_per_unit;
4140 vs->leftmost_frame = leftmost_frame;
4141 vs->zoom_focus = zoom_focus;
4144 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4145 vs->track_states.push_back (TAVState ((*i), &(*i)->get_state()));
4153 Editor::undo_visual_state ()
4155 if (undo_visual_stack.empty()) {
4159 redo_visual_stack.push_back (current_visual_state());
4161 VisualState* vs = undo_visual_stack.back();
4162 undo_visual_stack.pop_back();
4163 use_visual_state (*vs);
4167 Editor::redo_visual_state ()
4169 if (redo_visual_stack.empty()) {
4173 undo_visual_stack.push_back (current_visual_state());
4175 VisualState* vs = redo_visual_stack.back();
4176 redo_visual_stack.pop_back();
4177 use_visual_state (*vs);
4181 Editor::swap_visual_state ()
4183 if (undo_visual_stack.empty()) {
4184 redo_visual_state ();
4186 undo_visual_state ();
4191 Editor::use_visual_state (VisualState& vs)
4193 no_save_visual = true;
4195 _routes->suspend_redisplay ();
4197 vertical_adjustment.set_value (vs.y_position);
4199 set_zoom_focus (vs.zoom_focus);
4200 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4202 for (list<TAVState>::iterator i = vs.track_states.begin(); i != vs.track_states.end(); ++i) {
4203 TrackViewList::iterator t;
4205 /* check if the track still exists - it could have been deleted */
4207 if ((t = find (track_views.begin(), track_views.end(), i->first)) != track_views.end()) {
4208 (*t)->set_state (*(i->second), Stateful::loading_state_version);
4213 if (!vs.track_states.empty()) {
4214 _routes->update_visibility ();
4217 _routes->resume_redisplay ();
4219 no_save_visual = false;
4223 Editor::set_frames_per_unit (double fpu)
4225 /* this is the core function that controls the zoom level of the canvas. it is called
4226 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4229 if (fpu == frames_per_unit) {
4238 /* don't allow zooms that fit more than the maximum number
4239 of frames into an 800 pixel wide space.
4242 if (max_framepos / fpu < 800.0) {
4247 tempo_lines->tempo_map_changed();
4249 frames_per_unit = fpu;
4254 Editor::post_zoom ()
4256 // convert fpu to frame count
4258 framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width);
4260 if (frames_per_unit != zoom_range_clock.current_duration()) {
4261 zoom_range_clock.set (frames);
4264 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4265 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4266 (*i)->reshow_selection (selection->time);
4270 ZoomChanged (); /* EMIT_SIGNAL */
4272 //reset_scrolling_region ();
4274 if (playhead_cursor) {
4275 playhead_cursor->set_position (playhead_cursor->current_frame);
4278 refresh_location_display();
4279 _summary->set_overlays_dirty ();
4281 update_marker_labels ();
4287 Editor::queue_visual_change (framepos_t where)
4289 pending_visual_change.add (VisualChange::TimeOrigin);
4290 pending_visual_change.time_origin = where;
4291 ensure_visual_change_idle_handler ();
4295 Editor::queue_visual_change (double fpu)
4297 pending_visual_change.add (VisualChange::ZoomLevel);
4298 pending_visual_change.frames_per_unit = fpu;
4300 ensure_visual_change_idle_handler ();
4304 Editor::queue_visual_change_y (double y)
4306 pending_visual_change.add (VisualChange::YOrigin);
4307 pending_visual_change.y_origin = y;
4309 ensure_visual_change_idle_handler ();
4313 Editor::ensure_visual_change_idle_handler ()
4315 if (pending_visual_change.idle_handler_id < 0) {
4316 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4321 Editor::_idle_visual_changer (void* arg)
4323 return static_cast<Editor*>(arg)->idle_visual_changer ();
4327 Editor::idle_visual_changer ()
4329 VisualChange::Type p = pending_visual_change.pending;
4330 pending_visual_change.pending = (VisualChange::Type) 0;
4332 double const last_time_origin = horizontal_position ();
4334 if (p & VisualChange::TimeOrigin) {
4335 /* This is a bit of a hack, but set_frames_per_unit
4336 below will (if called) end up with the
4337 CrossfadeViews looking at Editor::leftmost_frame,
4338 and if we're changing origin and zoom in the same
4339 operation it will be the wrong value unless we
4343 leftmost_frame = pending_visual_change.time_origin;
4346 if (p & VisualChange::ZoomLevel) {
4347 set_frames_per_unit (pending_visual_change.frames_per_unit);
4349 compute_fixed_ruler_scale ();
4350 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4351 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4352 update_tempo_based_rulers ();
4354 if (p & VisualChange::TimeOrigin) {
4355 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4357 if (p & VisualChange::YOrigin) {
4358 vertical_adjustment.set_value (pending_visual_change.y_origin);
4361 if (last_time_origin == horizontal_position ()) {
4362 /* changed signal not emitted */
4363 update_fixed_rulers ();
4364 redisplay_tempo (true);
4367 _summary->set_overlays_dirty ();
4369 pending_visual_change.idle_handler_id = -1;
4370 return 0; /* this is always a one-shot call */
4373 struct EditorOrderTimeAxisSorter {
4374 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4375 return a->order () < b->order ();
4380 Editor::sort_track_selection (TrackViewList* sel)
4382 EditorOrderTimeAxisSorter cmp;
4387 selection->tracks.sort (cmp);
4392 Editor::get_preferred_edit_position (bool ignore_playhead)
4395 framepos_t where = 0;
4396 EditPoint ep = _edit_point;
4398 if (entered_marker) {
4399 return entered_marker->position();
4402 if (ignore_playhead && ep == EditAtPlayhead) {
4403 ep = EditAtSelectedMarker;
4407 case EditAtPlayhead:
4408 where = _session->audible_frame();
4411 case EditAtSelectedMarker:
4412 if (!selection->markers.empty()) {
4414 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4417 where = loc->start();
4428 if (!mouse_frame (where, ignored)) {
4429 /* XXX not right but what can we do ? */
4440 Editor::set_loop_range (framepos_t start, framepos_t end, string cmd)
4442 if (!_session) return;
4444 begin_reversible_command (cmd);
4448 if ((tll = transport_loop_location()) == 0) {
4449 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop);
4450 XMLNode &before = _session->locations()->get_state();
4451 _session->locations()->add (loc, true);
4452 _session->set_auto_loop_location (loc);
4453 XMLNode &after = _session->locations()->get_state();
4454 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4456 XMLNode &before = tll->get_state();
4457 tll->set_hidden (false, this);
4458 tll->set (start, end);
4459 XMLNode &after = tll->get_state();
4460 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4463 commit_reversible_command ();
4467 Editor::set_punch_range (framepos_t start, framepos_t end, string cmd)
4469 if (!_session) return;
4471 begin_reversible_command (cmd);
4475 if ((tpl = transport_punch_location()) == 0) {
4476 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoPunch);
4477 XMLNode &before = _session->locations()->get_state();
4478 _session->locations()->add (loc, true);
4479 _session->set_auto_loop_location (loc);
4480 XMLNode &after = _session->locations()->get_state();
4481 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4484 XMLNode &before = tpl->get_state();
4485 tpl->set_hidden (false, this);
4486 tpl->set (start, end);
4487 XMLNode &after = tpl->get_state();
4488 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4491 commit_reversible_command ();
4494 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4495 * @param rs List to which found regions are added.
4496 * @param where Time to look at.
4497 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4500 Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4502 const TrackViewList* tracks;
4505 tracks = &track_views;
4510 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4512 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4515 boost::shared_ptr<Track> tr;
4516 boost::shared_ptr<Playlist> pl;
4518 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4520 Playlist::RegionList* regions = pl->regions_at (
4521 (framepos_t) floor ( (double) where * tr->speed()));
4523 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4524 RegionView* rv = rtv->view()->find_view (*i);
4537 Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4539 const TrackViewList* tracks;
4542 tracks = &track_views;
4547 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4548 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4550 boost::shared_ptr<Track> tr;
4551 boost::shared_ptr<Playlist> pl;
4553 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4555 Playlist::RegionList* regions = pl->regions_touched (
4556 (framepos_t) floor ( (double)where * tr->speed()), max_framepos);
4558 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4560 RegionView* rv = rtv->view()->find_view (*i);
4573 /** Start with regions that are selected. Then add equivalent regions
4574 * on tracks in the same active edit-enabled route group as any of
4575 * the regions that we started with.
4579 Editor::get_regions_from_selection ()
4581 return get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
4584 /** Get regions using the following method:
4586 * Make an initial region list using the selected regions, unless
4587 * the edit point is `mouse' and the mouse is over an unselected
4588 * region. In this case, start with just that region.
4590 * Then, make an initial track list of the tracks that these
4591 * regions are on, and if the edit point is not `mouse', add the
4594 * Look at this track list and add any other tracks that are on the
4595 * same active edit-enabled route group as one of the initial tracks.
4597 * Finally take the initial region list and add any regions that are
4598 * under the edit point on one of the tracks on the track list to get
4599 * the returned region list.
4601 * The rationale here is that the mouse edit point is special in that
4602 * its position describes both a time and a track; the other edit
4603 * modes only describe a time. Hence if the edit point is `mouse' we
4604 * ignore selected tracks, as we assume the user means something by
4605 * pointing at a particular track. Also in this case we take note of
4606 * the region directly under the edit point, as there is always just one
4607 * (rather than possibly several with non-mouse edit points).
4611 Editor::get_regions_from_selection_and_edit_point ()
4613 RegionSelection regions;
4615 if (_edit_point == EditAtMouse && entered_regionview && !selection->regions.contains (entered_regionview)) {
4616 regions.add (entered_regionview);
4618 regions = selection->regions;
4621 TrackViewList tracks;
4623 if (_edit_point != EditAtMouse) {
4624 tracks = selection->tracks;
4627 /* Add any other tracks that have regions that are in the same
4628 edit-activated route group as one of our regions.
4630 for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) {
4632 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4634 if (g && g->is_active() && g->is_edit()) {
4635 tracks.add (axis_views_from_routes (g->route_list()));
4639 if (!tracks.empty()) {
4640 /* now find regions that are at the edit position on those tracks */
4641 framepos_t const where = get_preferred_edit_position ();
4642 get_regions_at (regions, where, tracks);
4648 /** Start with regions that are selected, or the entered regionview if none are selected.
4649 * Then add equivalent regions on tracks in the same active edit-enabled route group as any
4650 * of the regions that we started with.
4654 Editor::get_regions_from_selection_and_entered ()
4656 RegionSelection regions = selection->regions;
4658 if (regions.empty() && entered_regionview) {
4659 regions.add (entered_regionview);
4662 return get_equivalent_regions (regions, ARDOUR::Properties::edit.property_id);
4666 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4668 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4670 RouteTimeAxisView* tatv;
4672 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4674 boost::shared_ptr<Playlist> pl;
4675 vector<boost::shared_ptr<Region> > results;
4677 boost::shared_ptr<Track> tr;
4679 if ((tr = tatv->track()) == 0) {
4684 if ((pl = (tr->playlist())) != 0) {
4685 pl->get_region_list_equivalent_regions (region, results);
4688 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4689 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4690 regions.push_back (marv);
4699 Editor::show_rhythm_ferret ()
4701 if (rhythm_ferret == 0) {
4702 rhythm_ferret = new RhythmFerret(*this);
4705 rhythm_ferret->set_session (_session);
4706 rhythm_ferret->show ();
4707 rhythm_ferret->present ();
4711 Editor::first_idle ()
4713 MessageDialog* dialog = 0;
4715 if (track_views.size() > 1) {
4716 dialog = new MessageDialog (*this,
4717 string_compose (_("Please wait while %1 loads visual data"), PROGRAM_NAME),
4722 ARDOUR_UI::instance()->flush_pending ();
4725 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4729 // first idle adds route children (automation tracks), so we need to redisplay here
4730 _routes->redisplay ();
4738 Editor::_idle_resize (gpointer arg)
4740 return ((Editor*)arg)->idle_resize ();
4744 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4746 if (resize_idle_id < 0) {
4747 resize_idle_id = g_idle_add (_idle_resize, this);
4748 _pending_resize_amount = 0;
4751 /* make a note of the smallest resulting height, so that we can clamp the
4752 lower limit at TimeAxisView::hSmall */
4754 int32_t min_resulting = INT32_MAX;
4756 _pending_resize_amount += h;
4757 _pending_resize_view = view;
4759 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4761 if (selection->tracks.contains (_pending_resize_view)) {
4762 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4763 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4767 if (min_resulting < 0) {
4772 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4773 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4777 /** Handle pending resizing of tracks */
4779 Editor::idle_resize ()
4781 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4783 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4784 selection->tracks.contains (_pending_resize_view)) {
4786 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4787 if (*i != _pending_resize_view) {
4788 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4793 _pending_resize_amount = 0;
4795 _group_tabs->set_dirty ();
4796 resize_idle_id = -1;
4804 ENSURE_GUI_THREAD (*this, &Editor::located);
4806 playhead_cursor->set_position (_session->audible_frame ());
4807 if (_follow_playhead && !_pending_initial_locate) {
4808 reset_x_origin_to_follow_playhead ();
4811 _pending_locate_request = false;
4812 _pending_initial_locate = false;
4816 Editor::region_view_added (RegionView *)
4818 _summary->set_dirty ();
4822 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4824 TrackViewList::const_iterator j = track_views.begin ();
4825 while (j != track_views.end()) {
4826 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4827 if (rtv && rtv->route() == r) {
4838 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4842 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4843 TimeAxisView* tv = axis_view_from_route (*i);
4854 Editor::handle_new_route (RouteList& routes)
4856 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4858 RouteTimeAxisView *rtv;
4859 list<RouteTimeAxisView*> new_views;
4861 cerr << "Handle new route\n";
4863 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4864 boost::shared_ptr<Route> route = (*x);
4866 if (route->is_hidden() || route->is_monitor()) {
4870 DataType dt = route->input()->default_type();
4872 if (dt == ARDOUR::DataType::AUDIO) {
4873 rtv = new AudioTimeAxisView (*this, _session, route, *track_canvas);
4874 } else if (dt == ARDOUR::DataType::MIDI) {
4875 rtv = new MidiTimeAxisView (*this, _session, route, *track_canvas);
4877 throw unknown_type();
4880 new_views.push_back (rtv);
4881 track_views.push_back (rtv);
4883 rtv->effective_gain_display ();
4885 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4888 _routes->routes_added (new_views);
4889 _summary->routes_added (new_views);
4891 if (show_editor_mixer_when_tracks_arrive) {
4892 show_editor_mixer (true);
4895 editor_list_button.set_sensitive (true);
4899 Editor::timeaxisview_deleted (TimeAxisView *tv)
4901 if (_session && _session->deletion_in_progress()) {
4902 /* the situation is under control */
4906 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
4908 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
4910 _routes->route_removed (tv);
4912 if (tv == entered_track) {
4916 TimeAxisView::Children c = tv->get_child_list ();
4917 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
4918 if (entered_track == i->get()) {
4923 /* remove it from the list of track views */
4925 TrackViewList::iterator i;
4927 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
4928 i = track_views.erase (i);
4931 /* update whatever the current mixer strip is displaying, if revelant */
4933 boost::shared_ptr<Route> route;
4936 route = rtav->route ();
4939 if (current_mixer_strip && current_mixer_strip->route() == route) {
4941 TimeAxisView* next_tv;
4943 if (track_views.empty()) {
4945 } else if (i == track_views.end()) {
4946 next_tv = track_views.front();
4953 set_selected_mixer_strip (*next_tv);
4955 /* make the editor mixer strip go away setting the
4956 * button to inactive (which also unticks the menu option)
4959 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
4965 Editor::hide_track_in_display (TimeAxisView* tv, bool /*temponly*/)
4967 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
4969 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
4970 // this will hide the mixer strip
4971 set_selected_mixer_strip (*tv);
4974 _routes->hide_track_in_display (*tv);
4978 Editor::sync_track_view_list_and_routes ()
4980 track_views = TrackViewList (_routes->views ());
4982 _summary->set_dirty ();
4983 _group_tabs->set_dirty ();
4985 return false; // do not call again (until needed)
4989 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
4991 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4996 /** Find a RouteTimeAxisView by the ID of its route */
4998 Editor::get_route_view_by_route_id (PBD::ID& id) const
5000 RouteTimeAxisView* v;
5002 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5003 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5004 if(v->route()->id() == id) {
5014 Editor::fit_route_group (RouteGroup *g)
5016 TrackViewList ts = axis_views_from_routes (g->route_list ());
5021 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5023 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5026 _session->cancel_audition ();
5030 if (_session->is_auditioning()) {
5031 _session->cancel_audition ();
5032 if (r == last_audition_region) {
5037 _session->audition_region (r);
5038 last_audition_region = r;
5043 Editor::hide_a_region (boost::shared_ptr<Region> r)
5045 r->set_hidden (true);
5049 Editor::show_a_region (boost::shared_ptr<Region> r)
5051 r->set_hidden (false);
5055 Editor::audition_region_from_region_list ()
5057 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5061 Editor::hide_region_from_region_list ()
5063 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5067 Editor::show_region_in_region_list ()
5069 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5073 Editor::step_edit_status_change (bool yn)
5076 start_step_editing ();
5078 stop_step_editing ();
5083 Editor::start_step_editing ()
5085 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5089 Editor::stop_step_editing ()
5091 step_edit_connection.disconnect ();
5095 Editor::check_step_edit ()
5097 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5098 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5100 mtv->check_step_edit ();
5104 return true; // do it again, till we stop
5108 Editor::horizontal_scroll_left_press ()
5110 ++_scroll_callbacks;
5112 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5113 /* delay the first auto-repeat */
5117 double x = leftmost_position() - current_page_frames() / 5;
5124 /* do hacky auto-repeat */
5125 if (!_scroll_connection.connected ()) {
5126 _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press), 100);
5127 _scroll_callbacks = 0;
5134 Editor::horizontal_scroll_left_release ()
5136 _scroll_connection.disconnect ();
5140 Editor::horizontal_scroll_right_press ()
5142 ++_scroll_callbacks;
5144 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5145 /* delay the first auto-repeat */
5149 reset_x_origin (leftmost_position() + current_page_frames() / 5);
5151 /* do hacky auto-repeat */
5152 if (!_scroll_connection.connected ()) {
5153 _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press), 100);
5154 _scroll_callbacks = 0;
5161 Editor::horizontal_scroll_right_release ()
5163 _scroll_connection.disconnect ();
5166 /** Queue a change for the Editor viewport x origin to follow the playhead */
5168 Editor::reset_x_origin_to_follow_playhead ()
5170 framepos_t const frame = playhead_cursor->current_frame;
5172 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5174 if (_session->transport_speed() < 0) {
5176 if (frame > (current_page_frames() / 2)) {
5177 center_screen (frame-(current_page_frames()/2));
5179 center_screen (current_page_frames()/2);
5184 if (frame < leftmost_frame) {
5187 if (_session->transport_rolling()) {
5188 /* rolling; end up with the playhead at the right of the page */
5189 l = frame - current_page_frames ();
5191 /* not rolling: end up with the playhead 3/4 of the way along the page */
5192 l = frame - (3 * current_page_frames() / 4);
5199 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5202 if (_session->transport_rolling()) {
5203 /* rolling: end up with the playhead on the left of the page */
5204 center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5206 /* not rolling: end up with the playhead 1/4 of the way along the page */
5207 center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5215 Editor::super_rapid_screen_update ()
5217 if (!_session || !_session->engine().running()) {
5221 /* METERING / MIXER STRIPS */
5223 /* update track meters, if required */
5224 if (is_mapped() && meters_running) {
5225 RouteTimeAxisView* rtv;
5226 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5227 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5228 rtv->fast_update ();
5233 /* and any current mixer strip */
5234 if (current_mixer_strip) {
5235 current_mixer_strip->fast_update ();
5238 /* PLAYHEAD AND VIEWPORT */
5240 framepos_t const frame = _session->audible_frame();
5242 /* There are a few reasons why we might not update the playhead / viewport stuff:
5244 * 1. we don't update things when there's a pending locate request, otherwise
5245 * when the editor requests a locate there is a chance that this method
5246 * will move the playhead before the locate request is processed, causing
5248 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5249 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5252 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5254 last_update_frame = frame;
5256 if (!_dragging_playhead) {
5257 playhead_cursor->set_position (frame);
5260 if (!_stationary_playhead) {
5262 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5263 reset_x_origin_to_follow_playhead ();
5268 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5272 // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5273 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5274 if (target <= 0.0) {
5277 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5278 target = (target * 0.15) + (current * 0.85);
5284 set_horizontal_position (current);
5293 Editor::session_going_away ()
5295 _have_idled = false;
5297 _session_connections.drop_connections ();
5299 super_rapid_screen_update_connection.disconnect ();
5301 selection->clear ();
5302 cut_buffer->clear ();
5304 clicked_regionview = 0;
5305 clicked_axisview = 0;
5306 clicked_routeview = 0;
5307 clicked_crossfadeview = 0;
5308 entered_regionview = 0;
5310 last_update_frame = 0;
5313 playhead_cursor->canvas_item.hide ();
5315 /* rip everything out of the list displays */
5319 _route_groups->clear ();
5321 /* do this first so that deleting a track doesn't reset cms to null
5322 and thus cause a leak.
5325 if (current_mixer_strip) {
5326 if (current_mixer_strip->get_parent() != 0) {
5327 global_hpacker.remove (*current_mixer_strip);
5329 delete current_mixer_strip;
5330 current_mixer_strip = 0;
5333 /* delete all trackviews */
5335 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5338 track_views.clear ();
5340 zoom_range_clock.set_session (0);
5341 nudge_clock.set_session (0);
5343 editor_list_button.set_active(false);
5344 editor_list_button.set_sensitive(false);
5346 /* clear tempo/meter rulers */
5347 remove_metric_marks ();
5349 clear_marker_display ();
5351 delete current_bbt_points;
5352 current_bbt_points = 0;
5354 /* get rid of any existing editor mixer strip */
5356 WindowTitle title(Glib::get_application_name());
5357 title += _("Editor");
5359 set_title (title.get_string());
5361 SessionHandlePtr::session_going_away ();
5366 Editor::show_editor_list (bool yn)
5369 _the_notebook.show ();
5371 _the_notebook.hide ();
5376 Editor::change_region_layering_order ()
5378 framepos_t const position = get_preferred_edit_position ();
5380 if (!clicked_routeview) {
5381 if (layering_order_editor) {
5382 layering_order_editor->hide ();
5387 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
5393 boost::shared_ptr<Playlist> pl = track->playlist();
5399 if (layering_order_editor == 0) {
5400 layering_order_editor = new RegionLayeringOrderEditor(*this);
5403 layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position);
5404 layering_order_editor->maybe_present ();
5408 Editor::update_region_layering_order_editor ()
5410 if (layering_order_editor && layering_order_editor->is_visible ()) {
5411 change_region_layering_order ();
5416 Editor::setup_fade_images ()
5418 _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear")));
5419 _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut")));
5420 _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut")));
5421 _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut")));
5422 _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut")));
5424 _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear")));
5425 _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut")));
5426 _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut")));
5427 _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut")));
5428 _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut")));
5432 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
5434 Editor::action_menu_item (std::string const & name)
5436 Glib::RefPtr<Action> a = editor_actions->get_action (name);
5439 return *manage (a->create_menu_item ());
5443 Editor::resize_text_widgets ()
5445 set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_FUDGE+10, 15);
5446 set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_FUDGE+10, 15);
5447 set_size_request_to_display_given_text (snap_type_selector, snap_type_strings, COMBO_FUDGE+10, 15);
5448 set_size_request_to_display_given_text (snap_mode_selector, snap_mode_strings, COMBO_FUDGE+10, 15);
5449 set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_FUDGE+10, 15);
5453 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
5455 EventBox* b = manage (new EventBox);
5456 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
5457 Label* l = manage (new Label (name));
5461 _the_notebook.append_page (widget, *b);
5465 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
5467 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
5468 _the_notebook.set_current_page (_the_notebook.page_num (*page));
5471 if (ev->type == GDK_2BUTTON_PRESS) {
5473 /* double-click on a notebook tab shrinks or expands the notebook */
5475 if (_notebook_shrunk) {
5476 edit_pane.set_position (pre_maximal_horizontal_pane_position);
5477 _notebook_shrunk = false;
5479 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
5480 edit_pane.set_position (edit_pane.get_position() + page->get_width());
5481 _notebook_shrunk = true;