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 /* XXX: this is a bit unfortunate; it would probably
820 be nicer if we could just call show () above rather
821 than needing the show_all ()
824 /* re-hide stuff if necessary */
825 editor_list_button_toggled ();
826 parameter_changed ("show-summary");
827 parameter_changed ("show-edit-group-tabs");
828 parameter_changed ("show-zoom-tools");
830 /* now reset all audio_time_axis heights, because widgets might need
836 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
837 tv = (static_cast<TimeAxisView*>(*i));
841 if (current_mixer_strip) {
842 current_mixer_strip->hide_things ();
850 Editor::instant_save ()
852 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
857 _session->add_instant_xml(get_state());
859 Config->add_instant_xml(get_state());
864 Editor::zoom_adjustment_changed ()
870 double fpu = zoom_range_clock.current_duration() / _canvas_width;
874 zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width));
875 } else if (fpu > _session->current_end_frame() / _canvas_width) {
876 fpu = _session->current_end_frame() / _canvas_width;
877 zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width));
884 Editor::control_scroll (float fraction)
886 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
892 double step = fraction * current_page_frames();
895 _control_scroll_target is an optional<T>
897 it acts like a pointer to an framepos_t, with
898 a operator conversion to boolean to check
899 that it has a value could possibly use
900 playhead_cursor->current_frame to store the
901 value and a boolean in the class to know
902 when it's out of date
905 if (!_control_scroll_target) {
906 _control_scroll_target = _session->transport_frame();
907 _dragging_playhead = true;
910 if ((fraction < 0.0f) && (*_control_scroll_target < (framepos_t) fabs(step))) {
911 *_control_scroll_target = 0;
912 } else if ((fraction > 0.0f) && (max_framepos - *_control_scroll_target < step)) {
913 *_control_scroll_target = max_framepos - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
915 *_control_scroll_target += (framepos_t) floor (step);
918 /* move visuals, we'll catch up with it later */
920 playhead_cursor->set_position (*_control_scroll_target);
921 UpdateAllTransportClocks (*_control_scroll_target);
923 if (*_control_scroll_target > (current_page_frames() / 2)) {
924 /* try to center PH in window */
925 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
931 Now we do a timeout to actually bring the session to the right place
932 according to the playhead. This is to avoid reading disk buffers on every
933 call to control_scroll, which is driven by ScrollTimeline and therefore
934 probably by a control surface wheel which can generate lots of events.
936 /* cancel the existing timeout */
938 control_scroll_connection.disconnect ();
940 /* add the next timeout */
942 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
946 Editor::deferred_control_scroll (framepos_t /*target*/)
948 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
949 // reset for next stream
950 _control_scroll_target = boost::none;
951 _dragging_playhead = false;
956 Editor::access_action (std::string action_group, std::string action_item)
962 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
965 act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
973 Editor::on_realize ()
975 Window::on_realize ();
980 Editor::map_position_change (framepos_t frame)
982 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, frame)
988 if (_follow_playhead) {
989 center_screen (frame);
992 playhead_cursor->set_position (frame);
996 Editor::center_screen (framepos_t frame)
998 double page = _canvas_width * frames_per_unit;
1000 /* if we're off the page, then scroll.
1003 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
1004 center_screen_internal (frame, page);
1009 Editor::center_screen_internal (framepos_t frame, float page)
1014 frame -= (framepos_t) page;
1019 reset_x_origin (frame);
1024 Editor::update_title ()
1026 ENSURE_GUI_THREAD (*this, &Editor::update_title)
1029 bool dirty = _session->dirty();
1031 string session_name;
1033 if (_session->snap_name() != _session->name()) {
1034 session_name = _session->snap_name();
1036 session_name = _session->name();
1040 session_name = "*" + session_name;
1043 WindowTitle title(session_name);
1044 title += Glib::get_application_name();
1045 set_title (title.get_string());
1050 Editor::set_session (Session *t)
1052 SessionHandlePtr::set_session (t);
1058 zoom_range_clock.set_session (_session);
1059 _playlist_selector->set_session (_session);
1060 nudge_clock.set_session (_session);
1061 _summary->set_session (_session);
1062 _group_tabs->set_session (_session);
1063 _route_groups->set_session (_session);
1064 _regions->set_session (_session);
1065 _snapshots->set_session (_session);
1066 _routes->set_session (_session);
1067 _locations->set_session (_session);
1069 if (rhythm_ferret) {
1070 rhythm_ferret->set_session (_session);
1073 if (analysis_window) {
1074 analysis_window->set_session (_session);
1078 sfbrowser->set_session (_session);
1081 compute_fixed_ruler_scale ();
1083 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1084 set_state (*node, Stateful::loading_state_version);
1086 /* catch up with the playhead */
1088 _session->request_locate (playhead_cursor->current_frame);
1089 _pending_initial_locate = true;
1093 /* These signals can all be emitted by a non-GUI thread. Therefore the
1094 handlers for them must not attempt to directly interact with the GUI,
1095 but use Gtkmm2ext::UI::instance()->call_slot();
1098 _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), ui_bind(&Editor::step_edit_status_change, this, _1), gui_context());
1099 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1100 _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context());
1101 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context());
1102 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1103 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context());
1104 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1105 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
1106 _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Editor::session_state_saved, this, _1), gui_context());
1107 _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context());
1108 _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&Editor::location_gone, this, _1), gui_context());
1109 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1110 _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display_s, this, _1), gui_context());
1111 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1113 if (Profile->get_sae()) {
1114 Timecode::BBT_Time bbt;
1118 framepos_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1);
1119 nudge_clock.set_mode(AudioClock::BBT);
1120 nudge_clock.set (pos, true, 0, AudioClock::BBT);
1123 nudge_clock.set (_session->frame_rate() * 5, true, 0, AudioClock::Timecode); // default of 5 seconds
1126 playhead_cursor->canvas_item.show ();
1128 Location* loc = _session->locations()->auto_loop_location();
1130 loc = new Location (*_session, 0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1132 if (loc->start() == loc->end()) {
1133 loc->set_end (loc->start() + 1);
1136 _session->locations()->add (loc, false);
1137 _session->set_auto_loop_location (loc);
1140 loc->set_name (_("Loop"));
1143 loc = _session->locations()->auto_punch_location();
1146 loc = new Location (*_session, 0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1148 if (loc->start() == loc->end()) {
1149 loc->set_end (loc->start() + 1);
1152 _session->locations()->add (loc, false);
1153 _session->set_auto_punch_location (loc);
1156 loc->set_name (_("Punch"));
1159 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1160 Config->map_parameters (pc);
1161 _session->config.map_parameters (pc);
1163 refresh_location_display ();
1165 restore_ruler_visibility ();
1166 //tempo_map_changed (PropertyChange (0));
1167 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1169 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1170 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1173 super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
1174 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1177 switch (_snap_type) {
1178 case SnapToRegionStart:
1179 case SnapToRegionEnd:
1180 case SnapToRegionSync:
1181 case SnapToRegionBoundary:
1182 build_region_boundary_cache ();
1189 /* register for undo history */
1190 _session->register_with_memento_command_factory(_id, this);
1192 ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated));
1194 start_updating_meters ();
1198 Editor::action_pre_activated (Glib::RefPtr<Action> const & a)
1200 if (a->get_name() == "RegionMenu") {
1201 /* When the main menu's region menu is opened, we setup the actions so that they look right
1202 in the menu. I can't find a way of getting a signal when this menu is subsequently closed,
1203 so we resensitize all region actions when the entered regionview or the region selection
1204 changes. HOWEVER we can't always resensitize on entered_regionview change because that
1205 happens after the region context menu is opened. So we set a flag here, too.
1209 sensitize_the_right_region_actions ();
1210 _last_region_menu_was_main = true;
1214 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1216 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1218 using namespace Menu_Helpers;
1219 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1222 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1226 MenuList& items (fade_context_menu.items());
1230 switch (item_type) {
1232 case FadeInHandleItem:
1233 if (arv->audio_region()->fade_in_active()) {
1234 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1236 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1239 items.push_back (SeparatorElem());
1241 if (Profile->get_sae()) {
1243 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)));
1244 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)));
1251 *_fade_in_images[FadeLinear],
1252 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)
1256 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1261 *_fade_in_images[FadeFast],
1262 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)
1265 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1270 *_fade_in_images[FadeLogB],
1271 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogB)
1274 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1279 *_fade_in_images[FadeLogA],
1280 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogA)
1283 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1288 *_fade_in_images[FadeSlow],
1289 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow)
1292 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1298 case FadeOutHandleItem:
1299 if (arv->audio_region()->fade_out_active()) {
1300 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1302 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1305 items.push_back (SeparatorElem());
1307 if (Profile->get_sae()) {
1308 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)));
1309 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)));
1315 *_fade_out_images[FadeLinear],
1316 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)
1320 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1325 *_fade_out_images[FadeFast],
1326 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)
1329 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1334 *_fade_out_images[FadeLogB],
1335 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogA)
1338 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1343 *_fade_out_images[FadeLogA],
1344 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogB)
1347 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1352 *_fade_out_images[FadeSlow],
1353 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast)
1356 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1362 fatal << _("programming error: ")
1363 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1368 fade_context_menu.popup (button, time);
1372 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1374 using namespace Menu_Helpers;
1375 Menu* (Editor::*build_menu_function)();
1378 switch (item_type) {
1380 case RegionViewName:
1381 case RegionViewNameHighlight:
1382 case LeftFrameHandle:
1383 case RightFrameHandle:
1384 if (with_selection) {
1385 build_menu_function = &Editor::build_track_selection_context_menu;
1387 build_menu_function = &Editor::build_track_region_context_menu;
1392 if (with_selection) {
1393 build_menu_function = &Editor::build_track_selection_context_menu;
1395 build_menu_function = &Editor::build_track_context_menu;
1399 case CrossfadeViewItem:
1400 build_menu_function = &Editor::build_track_crossfade_context_menu;
1404 if (clicked_routeview->track()) {
1405 build_menu_function = &Editor::build_track_context_menu;
1407 build_menu_function = &Editor::build_track_bus_context_menu;
1412 /* probably shouldn't happen but if it does, we don't care */
1416 menu = (this->*build_menu_function)();
1417 menu->set_name ("ArdourContextMenu");
1419 /* now handle specific situations */
1421 switch (item_type) {
1423 case RegionViewName:
1424 case RegionViewNameHighlight:
1425 case LeftFrameHandle:
1426 case RightFrameHandle:
1427 if (!with_selection) {
1428 if (region_edit_menu_split_item) {
1429 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1430 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1432 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1435 if (region_edit_menu_split_multichannel_item) {
1436 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1437 region_edit_menu_split_multichannel_item->set_sensitive (true);
1439 region_edit_menu_split_multichannel_item->set_sensitive (false);
1448 case CrossfadeViewItem:
1455 /* probably shouldn't happen but if it does, we don't care */
1459 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1461 /* Bounce to disk */
1463 using namespace Menu_Helpers;
1464 MenuList& edit_items = menu->items();
1466 edit_items.push_back (SeparatorElem());
1468 switch (clicked_routeview->audio_track()->freeze_state()) {
1469 case AudioTrack::NoFreeze:
1470 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1473 case AudioTrack::Frozen:
1474 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1477 case AudioTrack::UnFrozen:
1478 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1484 if (item_type == StreamItem && clicked_routeview) {
1485 clicked_routeview->build_underlay_menu(menu);
1488 /* When the region menu is opened, we setup the actions so that they look right
1491 sensitize_the_right_region_actions ();
1492 _last_region_menu_was_main = false;
1494 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1495 menu->popup (button, time);
1499 Editor::build_track_context_menu ()
1501 using namespace Menu_Helpers;
1503 MenuList& edit_items = track_context_menu.items();
1506 add_dstream_context_items (edit_items);
1507 return &track_context_menu;
1511 Editor::build_track_bus_context_menu ()
1513 using namespace Menu_Helpers;
1515 MenuList& edit_items = track_context_menu.items();
1518 add_bus_context_items (edit_items);
1519 return &track_context_menu;
1523 Editor::build_track_region_context_menu ()
1525 using namespace Menu_Helpers;
1526 MenuList& edit_items = track_region_context_menu.items();
1529 /* we've just cleared the track region context menu, so the menu that these
1530 two items were on will have disappeared; stop them dangling.
1532 region_edit_menu_split_item = 0;
1533 region_edit_menu_split_multichannel_item = 0;
1535 /* we might try to use items that are currently attached to a crossfade menu,
1538 track_crossfade_context_menu.items().clear ();
1540 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1543 boost::shared_ptr<Track> tr;
1544 boost::shared_ptr<Playlist> pl;
1546 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
1547 framepos_t const framepos = (framepos_t) floor ((double) get_preferred_edit_position() * tr->speed());
1548 uint32_t regions_at = pl->count_regions_at (framepos);
1549 add_region_context_items (edit_items, regions_at > 1);
1553 add_dstream_context_items (edit_items);
1555 return &track_region_context_menu;
1559 Editor::build_track_crossfade_context_menu ()
1561 using namespace Menu_Helpers;
1562 MenuList& edit_items = track_crossfade_context_menu.items();
1563 edit_items.clear ();
1565 /* we might try to use items that are currently attached to a crossfade menu,
1568 track_region_context_menu.items().clear ();
1570 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1573 boost::shared_ptr<Track> tr;
1574 boost::shared_ptr<Playlist> pl;
1575 boost::shared_ptr<AudioPlaylist> apl;
1577 if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1579 AudioPlaylist::Crossfades xfades;
1581 apl->crossfades_at (get_preferred_edit_position (), xfades);
1583 bool many = xfades.size() > 1;
1585 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1586 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1589 framepos_t framepos = (framepos_t) floor ((double) get_preferred_edit_position() * tr->speed());
1590 uint32_t regions_at = pl->count_regions_at (framepos);
1591 add_region_context_items (edit_items, regions_at > 1);
1595 add_dstream_context_items (edit_items);
1597 return &track_crossfade_context_menu;
1601 Editor::analyze_region_selection ()
1603 if (analysis_window == 0) {
1604 analysis_window = new AnalysisWindow();
1607 analysis_window->set_session(_session);
1609 analysis_window->show_all();
1612 analysis_window->set_regionmode();
1613 analysis_window->analyze();
1615 analysis_window->present();
1619 Editor::analyze_range_selection()
1621 if (analysis_window == 0) {
1622 analysis_window = new AnalysisWindow();
1625 analysis_window->set_session(_session);
1627 analysis_window->show_all();
1630 analysis_window->set_rangemode();
1631 analysis_window->analyze();
1633 analysis_window->present();
1637 Editor::build_track_selection_context_menu ()
1639 using namespace Menu_Helpers;
1640 MenuList& edit_items = track_selection_context_menu.items();
1641 edit_items.clear ();
1643 add_selection_context_items (edit_items);
1644 // edit_items.push_back (SeparatorElem());
1645 // add_dstream_context_items (edit_items);
1647 return &track_selection_context_menu;
1650 /** Add context menu items relevant to crossfades.
1651 * @param edit_items List to add the items to.
1654 Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1656 using namespace Menu_Helpers;
1657 Menu *xfade_menu = manage (new Menu);
1658 MenuList& items = xfade_menu->items();
1659 xfade_menu->set_name ("ArdourContextMenu");
1662 if (xfade->active()) {
1668 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1669 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1671 if (xfade->can_follow_overlap()) {
1673 if (xfade->following_overlap()) {
1674 str = _("Convert to Short");
1676 str = _("Convert to Full");
1679 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1683 str = xfade->out()->name();
1685 str += xfade->in()->name();
1687 str = _("Crossfade");
1690 edit_items.push_back (MenuElem (str, *xfade_menu));
1691 edit_items.push_back (SeparatorElem());
1695 Editor::xfade_edit_left_region ()
1697 if (clicked_crossfadeview) {
1698 clicked_crossfadeview->left_view.show_region_editor ();
1703 Editor::xfade_edit_right_region ()
1705 if (clicked_crossfadeview) {
1706 clicked_crossfadeview->right_view.show_region_editor ();
1711 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, bool multiple_regions_at_position)
1713 using namespace Menu_Helpers;
1715 /* OK, stick the region submenu at the top of the list, and then add
1719 /* we have to hack up the region name because "_" has a special
1720 meaning for menu titles.
1723 RegionSelection rs = get_regions_from_selection_and_entered ();
1725 string::size_type pos = 0;
1726 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1728 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1729 menu_item_name.replace (pos, 1, "__");
1733 if (_popup_region_menu_item == 0) {
1734 _popup_region_menu_item = new MenuItem (menu_item_name);
1735 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1736 _popup_region_menu_item->show ();
1738 _popup_region_menu_item->set_label (menu_item_name);
1741 edit_items.push_back (*_popup_region_menu_item);
1742 if (multiple_regions_at_position && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1743 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region")->create_menu_item ()));
1745 edit_items.push_back (SeparatorElem());
1748 /** Add context menu items relevant to selection ranges.
1749 * @param edit_items List to add the items to.
1752 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1754 using namespace Menu_Helpers;
1756 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1757 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1759 edit_items.push_back (SeparatorElem());
1760 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1762 if (!selection->regions.empty()) {
1763 edit_items.push_back (SeparatorElem());
1764 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)));
1765 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)));
1768 edit_items.push_back (SeparatorElem());
1769 edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection)));
1770 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1772 edit_items.push_back (SeparatorElem());
1773 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1775 edit_items.push_back (SeparatorElem());
1776 edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1777 edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1779 edit_items.push_back (SeparatorElem());
1780 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1782 edit_items.push_back (SeparatorElem());
1783 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1784 edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1785 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1787 edit_items.push_back (SeparatorElem());
1788 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1789 edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1790 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1791 edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1792 edit_items.push_back (MenuElem (_("Export Range"), sigc::mem_fun(*this, &Editor::export_selection)));
1797 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1799 using namespace Menu_Helpers;
1803 Menu *play_menu = manage (new Menu);
1804 MenuList& play_items = play_menu->items();
1805 play_menu->set_name ("ArdourContextMenu");
1807 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1808 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1809 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1810 play_items.push_back (SeparatorElem());
1811 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
1813 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1817 Menu *select_menu = manage (new Menu);
1818 MenuList& select_items = select_menu->items();
1819 select_menu->set_name ("ArdourContextMenu");
1821 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1822 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1823 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1824 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1825 select_items.push_back (SeparatorElem());
1826 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
1827 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
1828 select_items.push_back (SeparatorElem());
1829 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1830 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1831 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1832 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1833 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
1834 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
1835 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
1837 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1841 Menu *cutnpaste_menu = manage (new Menu);
1842 MenuList& cutnpaste_items = cutnpaste_menu->items();
1843 cutnpaste_menu->set_name ("ArdourContextMenu");
1845 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1846 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1847 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1849 cutnpaste_items.push_back (SeparatorElem());
1851 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
1852 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
1854 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
1856 /* Adding new material */
1858 edit_items.push_back (SeparatorElem());
1859 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
1860 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
1864 Menu *nudge_menu = manage (new Menu());
1865 MenuList& nudge_items = nudge_menu->items();
1866 nudge_menu->set_name ("ArdourContextMenu");
1868 edit_items.push_back (SeparatorElem());
1869 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1870 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1871 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1872 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1874 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1878 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
1880 using namespace Menu_Helpers;
1884 Menu *play_menu = manage (new Menu);
1885 MenuList& play_items = play_menu->items();
1886 play_menu->set_name ("ArdourContextMenu");
1888 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1889 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1890 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1894 Menu *select_menu = manage (new Menu);
1895 MenuList& select_items = select_menu->items();
1896 select_menu->set_name ("ArdourContextMenu");
1898 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1899 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1900 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1901 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1902 select_items.push_back (SeparatorElem());
1903 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1904 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1905 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1906 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1908 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1912 Menu *cutnpaste_menu = manage (new Menu);
1913 MenuList& cutnpaste_items = cutnpaste_menu->items();
1914 cutnpaste_menu->set_name ("ArdourContextMenu");
1916 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1917 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1918 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1920 Menu *nudge_menu = manage (new Menu());
1921 MenuList& nudge_items = nudge_menu->items();
1922 nudge_menu->set_name ("ArdourContextMenu");
1924 edit_items.push_back (SeparatorElem());
1925 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1926 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1927 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1928 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1930 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1934 Editor::snap_type() const
1940 Editor::snap_mode() const
1946 Editor::set_snap_to (SnapType st)
1948 unsigned int snap_ind = (unsigned int)st;
1952 if (snap_ind > snap_type_strings.size() - 1) {
1954 _snap_type = (SnapType)snap_ind;
1957 string str = snap_type_strings[snap_ind];
1959 if (str != snap_type_selector.get_active_text()) {
1960 snap_type_selector.set_active_text (str);
1965 switch (_snap_type) {
1966 case SnapToBeatDiv32:
1967 case SnapToBeatDiv28:
1968 case SnapToBeatDiv24:
1969 case SnapToBeatDiv20:
1970 case SnapToBeatDiv16:
1971 case SnapToBeatDiv14:
1972 case SnapToBeatDiv12:
1973 case SnapToBeatDiv10:
1974 case SnapToBeatDiv8:
1975 case SnapToBeatDiv7:
1976 case SnapToBeatDiv6:
1977 case SnapToBeatDiv5:
1978 case SnapToBeatDiv4:
1979 case SnapToBeatDiv3:
1980 case SnapToBeatDiv2:
1981 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
1982 update_tempo_based_rulers ();
1985 case SnapToRegionStart:
1986 case SnapToRegionEnd:
1987 case SnapToRegionSync:
1988 case SnapToRegionBoundary:
1989 build_region_boundary_cache ();
1997 SnapChanged (); /* EMIT SIGNAL */
2001 Editor::set_snap_mode (SnapMode mode)
2004 string str = snap_mode_strings[(int)mode];
2006 if (str != snap_mode_selector.get_active_text ()) {
2007 snap_mode_selector.set_active_text (str);
2013 Editor::set_edit_point_preference (EditPoint ep, bool force)
2015 bool changed = (_edit_point != ep);
2018 string str = edit_point_strings[(int)ep];
2020 if (str != edit_point_selector.get_active_text ()) {
2021 edit_point_selector.set_active_text (str);
2024 set_canvas_cursor ();
2026 if (!force && !changed) {
2030 const char* action=NULL;
2032 switch (_edit_point) {
2033 case EditAtPlayhead:
2034 action = "edit-at-playhead";
2036 case EditAtSelectedMarker:
2037 action = "edit-at-marker";
2040 action = "edit-at-mouse";
2044 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2046 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2050 bool in_track_canvas;
2052 if (!mouse_frame (foo, in_track_canvas)) {
2053 in_track_canvas = false;
2056 reset_canvas_action_sensitivity (in_track_canvas);
2062 Editor::set_state (const XMLNode& node, int /*version*/)
2064 const XMLProperty* prop;
2066 int x, y, xoff, yoff;
2069 if ((prop = node.property ("id")) != 0) {
2070 _id = prop->value ();
2073 g.base_width = default_width;
2074 g.base_height = default_height;
2080 if ((geometry = find_named_node (node, "geometry")) != 0) {
2084 if ((prop = geometry->property("x_size")) == 0) {
2085 prop = geometry->property ("x-size");
2088 g.base_width = atoi(prop->value());
2090 if ((prop = geometry->property("y_size")) == 0) {
2091 prop = geometry->property ("y-size");
2094 g.base_height = atoi(prop->value());
2097 if ((prop = geometry->property ("x_pos")) == 0) {
2098 prop = geometry->property ("x-pos");
2101 x = atoi (prop->value());
2104 if ((prop = geometry->property ("y_pos")) == 0) {
2105 prop = geometry->property ("y-pos");
2108 y = atoi (prop->value());
2111 if ((prop = geometry->property ("x_off")) == 0) {
2112 prop = geometry->property ("x-off");
2115 xoff = atoi (prop->value());
2117 if ((prop = geometry->property ("y_off")) == 0) {
2118 prop = geometry->property ("y-off");
2121 yoff = atoi (prop->value());
2125 //set_default_size (g.base_width, g.base_height);
2128 if (_session && (prop = node.property ("playhead"))) {
2130 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2131 playhead_cursor->set_position (pos);
2133 playhead_cursor->set_position (0);
2136 if ((prop = node.property ("mixer-width"))) {
2137 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2140 if ((prop = node.property ("zoom-focus"))) {
2141 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2144 if ((prop = node.property ("zoom"))) {
2145 reset_zoom (PBD::atof (prop->value()));
2147 reset_zoom (frames_per_unit);
2150 if ((prop = node.property ("snap-to"))) {
2151 set_snap_to ((SnapType) atoi (prop->value()));
2154 if ((prop = node.property ("snap-mode"))) {
2155 set_snap_mode ((SnapMode) atoi (prop->value()));
2158 if ((prop = node.property ("mouse-mode"))) {
2159 MouseMode m = str2mousemode(prop->value());
2160 set_mouse_mode (m, true);
2162 set_mouse_mode (MouseObject, true);
2165 if ((prop = node.property ("left-frame")) != 0) {
2167 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2168 reset_x_origin (pos);
2172 if ((prop = node.property ("y-origin")) != 0) {
2173 reset_y_origin (atof (prop->value ()));
2176 if ((prop = node.property ("internal-edit"))) {
2177 bool yn = string_is_affirmative (prop->value());
2178 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2180 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2181 tact->set_active (!yn);
2182 tact->set_active (yn);
2186 if ((prop = node.property ("join-object-range"))) {
2187 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2190 if ((prop = node.property ("edit-point"))) {
2191 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2194 if ((prop = node.property ("show-measures"))) {
2195 bool yn = string_is_affirmative (prop->value());
2196 _show_measures = yn;
2197 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2199 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2200 /* do it twice to force the change */
2201 tact->set_active (!yn);
2202 tact->set_active (yn);
2206 if ((prop = node.property ("follow-playhead"))) {
2207 bool yn = string_is_affirmative (prop->value());
2208 set_follow_playhead (yn);
2209 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2211 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2212 if (tact->get_active() != yn) {
2213 tact->set_active (yn);
2218 if ((prop = node.property ("stationary-playhead"))) {
2219 bool yn = (prop->value() == "yes");
2220 set_stationary_playhead (yn);
2221 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2223 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2224 if (tact->get_active() != yn) {
2225 tact->set_active (yn);
2230 if ((prop = node.property ("region-list-sort-type"))) {
2231 RegionListSortType st;
2232 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2235 if ((prop = node.property ("xfades-visible"))) {
2236 bool yn = string_is_affirmative (prop->value());
2237 _xfade_visibility = !yn;
2238 // set_xfade_visibility (yn);
2241 if ((prop = node.property ("show-editor-mixer"))) {
2243 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2246 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2247 bool yn = string_is_affirmative (prop->value());
2249 /* do it twice to force the change */
2251 tact->set_active (!yn);
2252 tact->set_active (yn);
2255 if ((prop = node.property ("show-editor-list"))) {
2257 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2260 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2261 bool yn = string_is_affirmative (prop->value());
2263 /* do it twice to force the change */
2265 tact->set_active (!yn);
2266 tact->set_active (yn);
2269 if ((prop = node.property (X_("editor-list-page")))) {
2270 _the_notebook.set_current_page (atoi (prop->value ()));
2273 if ((prop = node.property (X_("show-marker-lines")))) {
2274 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2276 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2277 bool yn = string_is_affirmative (prop->value ());
2279 tact->set_active (!yn);
2280 tact->set_active (yn);
2283 XMLNodeList children = node.children ();
2284 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2285 selection->set_state (**i, Stateful::current_state_version);
2286 _regions->set_state (**i);
2293 Editor::get_state ()
2295 XMLNode* node = new XMLNode ("Editor");
2298 _id.print (buf, sizeof (buf));
2299 node->add_property ("id", buf);
2301 if (is_realized()) {
2302 Glib::RefPtr<Gdk::Window> win = get_window();
2304 int x, y, xoff, yoff, width, height;
2305 win->get_root_origin(x, y);
2306 win->get_position(xoff, yoff);
2307 win->get_size(width, height);
2309 XMLNode* geometry = new XMLNode ("geometry");
2311 snprintf(buf, sizeof(buf), "%d", width);
2312 geometry->add_property("x-size", string(buf));
2313 snprintf(buf, sizeof(buf), "%d", height);
2314 geometry->add_property("y-size", string(buf));
2315 snprintf(buf, sizeof(buf), "%d", x);
2316 geometry->add_property("x-pos", string(buf));
2317 snprintf(buf, sizeof(buf), "%d", y);
2318 geometry->add_property("y-pos", string(buf));
2319 snprintf(buf, sizeof(buf), "%d", xoff);
2320 geometry->add_property("x-off", string(buf));
2321 snprintf(buf, sizeof(buf), "%d", yoff);
2322 geometry->add_property("y-off", string(buf));
2323 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2324 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2325 geometry->add_property("notebook-shrunk", _notebook_shrunk ? "1" : "0");
2326 snprintf(buf,sizeof(buf), "%d",pre_maximal_horizontal_pane_position);
2327 geometry->add_property("pre-maximal-horizontal-pane-position", string(buf));
2328 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2329 geometry->add_property("edit-vertical-pane-pos", string(buf));
2331 node->add_child_nocopy (*geometry);
2334 maybe_add_mixer_strip_width (*node);
2336 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2337 node->add_property ("zoom-focus", buf);
2338 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2339 node->add_property ("zoom", buf);
2340 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2341 node->add_property ("snap-to", buf);
2342 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2343 node->add_property ("snap-mode", buf);
2345 node->add_property ("edit-point", enum_2_string (_edit_point));
2347 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2348 node->add_property ("playhead", buf);
2349 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2350 node->add_property ("left-frame", buf);
2351 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2352 node->add_property ("y-origin", buf);
2354 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2355 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2356 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2357 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2358 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2359 node->add_property ("mouse-mode", enum2str(mouse_mode));
2360 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2361 node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2363 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2365 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2366 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2369 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2371 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2372 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2375 snprintf (buf, sizeof (buf), "%d", _the_notebook.get_current_page ());
2376 node->add_property (X_("editor-list-page"), buf);
2378 node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no");
2380 node->add_child_nocopy (selection->get_state ());
2381 node->add_child_nocopy (_regions->get_state ());
2388 /** @param y y offset from the top of all trackviews.
2389 * @return pair: TimeAxisView that y is over, layer index.
2390 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2391 * in stacked region display mode, otherwise 0.
2393 std::pair<TimeAxisView *, layer_t>
2394 Editor::trackview_by_y_position (double y)
2396 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2398 std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
2404 return std::make_pair ( (TimeAxisView *) 0, 0);
2407 /** Snap a position to the grid, if appropriate, taking into account current
2408 * grid settings and also the state of any snap modifier keys that may be pressed.
2409 * @param start Position to snap.
2410 * @param event Event to get current key modifier information from, or 0.
2413 Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2415 if (!_session || !event) {
2419 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2420 if (_snap_mode == SnapOff) {
2421 snap_to_internal (start, direction, for_mark);
2424 if (_snap_mode != SnapOff) {
2425 snap_to_internal (start, direction, for_mark);
2431 Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark)
2433 if (!_session || _snap_mode == SnapOff) {
2437 snap_to_internal (start, direction, for_mark);
2441 Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /*for_mark*/)
2443 const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2444 framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2446 switch (_snap_type) {
2447 case SnapToTimecodeFrame:
2448 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2449 start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2451 start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2455 case SnapToTimecodeSeconds:
2456 if (_session->config.get_timecode_offset_negative()) {
2457 start += _session->config.get_timecode_offset ();
2459 start -= _session->config.get_timecode_offset ();
2461 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2462 start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2464 start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2467 if (_session->config.get_timecode_offset_negative()) {
2468 start -= _session->config.get_timecode_offset ();
2470 start += _session->config.get_timecode_offset ();
2474 case SnapToTimecodeMinutes:
2475 if (_session->config.get_timecode_offset_negative()) {
2476 start += _session->config.get_timecode_offset ();
2478 start -= _session->config.get_timecode_offset ();
2480 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2481 start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2483 start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2485 if (_session->config.get_timecode_offset_negative()) {
2486 start -= _session->config.get_timecode_offset ();
2488 start += _session->config.get_timecode_offset ();
2492 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2498 Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark)
2500 const framepos_t one_second = _session->frame_rate();
2501 const framepos_t one_minute = _session->frame_rate() * 60;
2502 framepos_t presnap = start;
2506 switch (_snap_type) {
2507 case SnapToTimecodeFrame:
2508 case SnapToTimecodeSeconds:
2509 case SnapToTimecodeMinutes:
2510 return timecode_snap_to_internal (start, direction, for_mark);
2513 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2514 start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2516 start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2521 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2522 start = (framepos_t) ceil ((double) start / one_second) * one_second;
2524 start = (framepos_t) floor ((double) start / one_second) * one_second;
2529 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2530 start = (framepos_t) ceil ((double) start / one_minute) * one_minute;
2532 start = (framepos_t) floor ((double) start / one_minute) * one_minute;
2537 start = _session->tempo_map().round_to_bar (start, direction);
2541 start = _session->tempo_map().round_to_beat (start, direction);
2544 case SnapToBeatDiv32:
2545 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2547 case SnapToBeatDiv28:
2548 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2550 case SnapToBeatDiv24:
2551 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2553 case SnapToBeatDiv20:
2554 start = _session->tempo_map().round_to_beat_subdivision (start, 20, direction);
2556 case SnapToBeatDiv16:
2557 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2559 case SnapToBeatDiv14:
2560 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2562 case SnapToBeatDiv12:
2563 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2565 case SnapToBeatDiv10:
2566 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2568 case SnapToBeatDiv8:
2569 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2571 case SnapToBeatDiv7:
2572 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2574 case SnapToBeatDiv6:
2575 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2577 case SnapToBeatDiv5:
2578 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2580 case SnapToBeatDiv4:
2581 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2583 case SnapToBeatDiv3:
2584 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2586 case SnapToBeatDiv2:
2587 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2595 _session->locations()->marks_either_side (start, before, after);
2597 if (before == max_framepos) {
2599 } else if (after == max_framepos) {
2601 } else if (before != max_framepos && after != max_framepos) {
2602 /* have before and after */
2603 if ((start - before) < (after - start)) {
2612 case SnapToRegionStart:
2613 case SnapToRegionEnd:
2614 case SnapToRegionSync:
2615 case SnapToRegionBoundary:
2616 if (!region_boundary_cache.empty()) {
2618 vector<framepos_t>::iterator prev = region_boundary_cache.end ();
2619 vector<framepos_t>::iterator next = region_boundary_cache.end ();
2621 if (direction > 0) {
2622 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2624 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2627 if (next != region_boundary_cache.begin ()) {
2632 framepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2633 framepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2635 if (start > (p + n) / 2) {
2644 switch (_snap_mode) {
2650 if (presnap > start) {
2651 if (presnap > (start + unit_to_frame(snap_threshold))) {
2655 } else if (presnap < start) {
2656 if (presnap < (start - unit_to_frame(snap_threshold))) {
2662 /* handled at entry */
2670 Editor::setup_toolbar ()
2674 /* Mode Buttons (tool selection) */
2676 mouse_move_button.set_relief(Gtk::RELIEF_NONE);
2677 mouse_select_button.set_relief(Gtk::RELIEF_NONE);
2678 mouse_gain_button.set_relief(Gtk::RELIEF_NONE);
2679 mouse_zoom_button.set_relief(Gtk::RELIEF_NONE);
2680 mouse_timefx_button.set_relief(Gtk::RELIEF_NONE);
2681 mouse_audition_button.set_relief(Gtk::RELIEF_NONE);
2682 // internal_edit_button.set_relief(Gtk::RELIEF_NONE);
2683 join_object_range_button.set_relief(Gtk::RELIEF_NONE);
2685 HBox* mode_box = manage(new HBox);
2686 mode_box->set_border_width (2);
2687 mode_box->set_spacing(4);
2689 /* table containing mode buttons */
2691 HBox* mouse_mode_button_box = manage (new HBox ());
2693 if (Profile->get_sae()) {
2694 mouse_mode_button_box->pack_start (mouse_move_button);
2696 mouse_mode_button_box->pack_start (mouse_move_button);
2697 mouse_mode_button_box->pack_start (join_object_range_button);
2698 mouse_mode_button_box->pack_start (mouse_select_button);
2701 mouse_mode_button_box->pack_start (mouse_zoom_button);
2703 if (!Profile->get_sae()) {
2704 mouse_mode_button_box->pack_start (mouse_gain_button);
2707 mouse_mode_button_box->pack_start (mouse_timefx_button);
2708 mouse_mode_button_box->pack_start (mouse_audition_button);
2709 mouse_mode_button_box->pack_start (internal_edit_button);
2711 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2712 if (!Profile->get_sae()) {
2713 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2715 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2717 edit_mode_selector.set_name ("EditModeSelector");
2718 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2719 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2721 mode_box->pack_start (edit_mode_selector);
2722 mode_box->pack_start (*mouse_mode_button_box);
2724 _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2725 _mouse_mode_tearoff->set_name ("MouseModeBase");
2726 _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2728 if (Profile->get_sae()) {
2729 _mouse_mode_tearoff->set_can_be_torn_off (false);
2732 _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2733 &_mouse_mode_tearoff->tearoff_window()));
2734 _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2735 &_mouse_mode_tearoff->tearoff_window(), 1));
2736 _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2737 &_mouse_mode_tearoff->tearoff_window()));
2738 _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2739 &_mouse_mode_tearoff->tearoff_window(), 1));
2741 mouse_move_button.set_mode (false);
2742 mouse_select_button.set_mode (false);
2743 mouse_gain_button.set_mode (false);
2744 mouse_zoom_button.set_mode (false);
2745 mouse_timefx_button.set_mode (false);
2746 mouse_audition_button.set_mode (false);
2747 join_object_range_button.set_mode (false);
2749 mouse_move_button.set_name ("MouseModeButton");
2750 mouse_select_button.set_name ("MouseModeButton");
2751 mouse_gain_button.set_name ("MouseModeButton");
2752 mouse_zoom_button.set_name ("MouseModeButton");
2753 mouse_timefx_button.set_name ("MouseModeButton");
2754 mouse_audition_button.set_name ("MouseModeButton");
2755 internal_edit_button.set_name ("MouseModeButton");
2756 join_object_range_button.set_name ("MouseModeButton");
2758 mouse_move_button.unset_flags (CAN_FOCUS);
2759 mouse_select_button.unset_flags (CAN_FOCUS);
2760 mouse_gain_button.unset_flags (CAN_FOCUS);
2761 mouse_zoom_button.unset_flags (CAN_FOCUS);
2762 mouse_timefx_button.unset_flags (CAN_FOCUS);
2763 mouse_audition_button.unset_flags (CAN_FOCUS);
2764 internal_edit_button.unset_flags (CAN_FOCUS);
2765 join_object_range_button.unset_flags (CAN_FOCUS);
2769 _zoom_box.set_spacing (1);
2770 _zoom_box.set_border_width (0);
2772 zoom_in_button.set_name ("EditorTimeButton");
2773 zoom_in_button.set_image (*(manage (new Image (Stock::ZOOM_IN, Gtk::ICON_SIZE_MENU))));
2774 zoom_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), false));
2776 zoom_out_button.set_name ("EditorTimeButton");
2777 zoom_out_button.set_image (*(manage (new Image (Stock::ZOOM_OUT, Gtk::ICON_SIZE_MENU))));
2778 zoom_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), true));
2780 zoom_out_full_button.set_name ("EditorTimeButton");
2781 zoom_out_full_button.set_image (*(manage (new Image (Stock::ZOOM_100, Gtk::ICON_SIZE_MENU))));
2782 zoom_out_full_button.signal_clicked().connect (sigc::mem_fun(*this, &Editor::temporal_zoom_session));
2784 zoom_focus_selector.set_name ("ZoomFocusSelector");
2785 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
2786 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2788 _zoom_box.pack_start (zoom_out_button, false, false);
2789 _zoom_box.pack_start (zoom_in_button, false, false);
2790 _zoom_box.pack_start (zoom_out_full_button, false, false);
2792 _zoom_box.pack_start (zoom_focus_selector);
2794 /* Track zoom buttons */
2795 tav_expand_button.set_name ("TrackHeightButton");
2796 tav_expand_button.set_size_request(-1,20);
2797 tav_expand_button.add (*(manage (new Image (::get_icon("tav_exp")))));
2798 tav_expand_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), true));
2800 tav_shrink_button.set_name ("TrackHeightButton");
2801 tav_shrink_button.set_size_request(-1,20);
2802 tav_shrink_button.add (*(manage (new Image (::get_icon("tav_shrink")))));
2803 tav_shrink_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), false));
2805 _zoom_box.pack_start (tav_shrink_button);
2806 _zoom_box.pack_start (tav_expand_button);
2808 _zoom_tearoff = manage (new TearOff (_zoom_box));
2810 _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2811 &_zoom_tearoff->tearoff_window()));
2812 _zoom_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2813 &_zoom_tearoff->tearoff_window(), 0));
2814 _zoom_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2815 &_zoom_tearoff->tearoff_window()));
2816 _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2817 &_zoom_tearoff->tearoff_window(), 0));
2819 snap_box.set_spacing (1);
2820 snap_box.set_border_width (2);
2822 snap_type_selector.set_name ("SnapTypeSelector");
2823 set_popdown_strings (snap_type_selector, snap_type_strings, true);
2824 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2826 snap_mode_selector.set_name ("SnapModeSelector");
2827 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
2828 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2830 edit_point_selector.set_name ("EditPointSelector");
2831 set_popdown_strings (edit_point_selector, edit_point_strings, true);
2832 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2834 snap_box.pack_start (snap_mode_selector, false, false);
2835 snap_box.pack_start (snap_type_selector, false, false);
2836 snap_box.pack_start (edit_point_selector, false, false);
2840 HBox *nudge_box = manage (new HBox);
2841 nudge_box->set_spacing(1);
2842 nudge_box->set_border_width (2);
2844 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2845 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2847 nudge_box->pack_start (nudge_backward_button, false, false);
2848 nudge_box->pack_start (nudge_forward_button, false, false);
2849 nudge_box->pack_start (nudge_clock, false, false);
2852 /* Pack everything in... */
2854 HBox* hbox = manage (new HBox);
2855 hbox->set_spacing(10);
2857 _tools_tearoff = manage (new TearOff (*hbox));
2858 _tools_tearoff->set_name ("MouseModeBase");
2859 _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
2861 if (Profile->get_sae()) {
2862 _tools_tearoff->set_can_be_torn_off (false);
2865 _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2866 &_tools_tearoff->tearoff_window()));
2867 _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2868 &_tools_tearoff->tearoff_window(), 0));
2869 _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2870 &_tools_tearoff->tearoff_window()));
2871 _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2872 &_tools_tearoff->tearoff_window(), 0));
2874 toolbar_hbox.set_spacing (10);
2875 toolbar_hbox.set_border_width (1);
2877 toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
2878 toolbar_hbox.pack_start (*_zoom_tearoff, false, false);
2879 toolbar_hbox.pack_start (*_tools_tearoff, false, false);
2881 hbox->pack_start (snap_box, false, false);
2882 hbox->pack_start (*nudge_box, false, false);
2883 hbox->pack_start (panic_box, false, false);
2887 toolbar_base.set_name ("ToolBarBase");
2888 toolbar_base.add (toolbar_hbox);
2890 _toolbar_viewport.add (toolbar_base);
2891 /* stick to the required height but allow width to vary if there's not enough room */
2892 _toolbar_viewport.set_size_request (1, -1);
2894 toolbar_frame.set_shadow_type (SHADOW_OUT);
2895 toolbar_frame.set_name ("BaseFrame");
2896 toolbar_frame.add (_toolbar_viewport);
2898 DPIReset.connect (sigc::mem_fun (*this, &Editor::resize_text_widgets));
2902 Editor::setup_tooltips ()
2904 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
2905 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain"));
2906 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
2907 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
2908 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2909 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
2910 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
2911 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
2912 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
2913 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
2914 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
2915 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
2916 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
2917 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
2918 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
2919 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
2920 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
2921 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
2922 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
2923 ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
2924 ARDOUR_UI::instance()->set_tip (midi_panic_button, _("Send note off and reset controller messages on all MIDI channels"));
2925 ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode"));
2929 Editor::midi_panic ()
2931 cerr << "MIDI panic\n";
2934 _session->midi_panic();
2939 Editor::setup_midi_toolbar ()
2943 /* Midi sound notes */
2944 midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
2945 midi_sound_notes.set_relief(Gtk::RELIEF_NONE);
2946 midi_sound_notes.unset_flags (CAN_FOCUS);
2950 act = ActionManager::get_action (X_("MIDI"), X_("panic"));
2951 midi_panic_button.set_name("MidiPanicButton");
2952 act->connect_proxy (midi_panic_button);
2954 panic_box.pack_start (midi_sound_notes , true, true);
2955 panic_box.pack_start (midi_panic_button, true, true);
2959 Editor::convert_drop_to_paths (
2960 vector<string>& paths,
2961 const RefPtr<Gdk::DragContext>& /*context*/,
2964 const SelectionData& data,
2968 if (_session == 0) {
2972 vector<string> uris = data.get_uris();
2976 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
2977 are actually URI lists. So do it by hand.
2980 if (data.get_target() != "text/plain") {
2984 /* Parse the "uri-list" format that Nautilus provides,
2985 where each pathname is delimited by \r\n.
2987 THERE MAY BE NO NULL TERMINATING CHAR!!!
2990 string txt = data.get_text();
2994 p = (const char *) malloc (txt.length() + 1);
2995 txt.copy ((char *) p, txt.length(), 0);
2996 ((char*)p)[txt.length()] = '\0';
3002 while (g_ascii_isspace (*p))
3006 while (*q && (*q != '\n') && (*q != '\r')) {
3013 while (q > p && g_ascii_isspace (*q))
3018 uris.push_back (string (p, q - p + 1));
3022 p = strchr (p, '\n');
3034 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3036 if ((*i).substr (0,7) == "file://") {
3039 PBD::url_decode (p);
3041 // scan forward past three slashes
3043 string::size_type slashcnt = 0;
3044 string::size_type n = 0;
3045 string::iterator x = p.begin();
3047 while (slashcnt < 3 && x != p.end()) {
3050 } else if (slashcnt == 3) {
3057 if (slashcnt != 3 || x == p.end()) {
3058 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3062 paths.push_back (p.substr (n - 1));
3070 Editor::new_tempo_section ()
3076 Editor::map_transport_state ()
3078 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3080 if (_session && _session->transport_stopped()) {
3081 have_pending_keyboard_selection = false;
3084 update_loop_range_view (true);
3089 Editor::State::State (PublicEditor const * e)
3091 selection = new Selection (e);
3094 Editor::State::~State ()
3100 Editor::begin_reversible_command (string name)
3103 _session->begin_reversible_command (name);
3108 Editor::begin_reversible_command (GQuark q)
3111 _session->begin_reversible_command (q);
3116 Editor::commit_reversible_command ()
3119 _session->commit_reversible_command ();
3124 Editor::history_changed ()
3128 if (undo_action && _session) {
3129 if (_session->undo_depth() == 0) {
3132 label = string_compose(_("Undo (%1)"), _session->next_undo());
3134 undo_action->property_label() = label;
3137 if (redo_action && _session) {
3138 if (_session->redo_depth() == 0) {
3141 label = string_compose(_("Redo (%1)"), _session->next_redo());
3143 redo_action->property_label() = label;
3148 Editor::duplicate_dialog (bool with_dialog)
3152 if (mouse_mode == MouseRange) {
3153 if (selection->time.length() == 0) {
3158 RegionSelection rs = get_regions_from_selection_and_entered ();
3160 if (mouse_mode != MouseRange && rs.empty()) {
3166 ArdourDialog win (_("Duplicate"));
3167 Label label (_("Number of duplications:"));
3168 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3169 SpinButton spinner (adjustment, 0.0, 1);
3172 win.get_vbox()->set_spacing (12);
3173 win.get_vbox()->pack_start (hbox);
3174 hbox.set_border_width (6);
3175 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3177 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3178 place, visually. so do this by hand.
3181 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3182 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3183 spinner.grab_focus();
3189 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3190 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3191 win.set_default_response (RESPONSE_ACCEPT);
3193 win.set_position (WIN_POS_MOUSE);
3195 spinner.grab_focus ();
3197 switch (win.run ()) {
3198 case RESPONSE_ACCEPT:
3204 times = adjustment.get_value();
3207 if (mouse_mode == MouseRange) {
3208 duplicate_selection (times);
3210 duplicate_some_regions (rs, times);
3215 Editor::show_verbose_canvas_cursor ()
3217 verbose_canvas_cursor->raise_to_top();
3218 verbose_canvas_cursor->show();
3219 verbose_cursor_visible = true;
3223 Editor::hide_verbose_canvas_cursor ()
3225 verbose_canvas_cursor->hide();
3226 verbose_cursor_visible = false;
3230 Editor::clamp_verbose_cursor_x (double x)
3235 x = min (_canvas_width - 200.0, x);
3241 Editor::clamp_verbose_cursor_y (double y)
3243 if (y < canvas_timebars_vsize) {
3244 y = canvas_timebars_vsize;
3246 y = min (_canvas_height - 50, y);
3252 Editor::show_verbose_canvas_cursor_with (const string & txt, int32_t xoffset, int32_t yoffset)
3254 verbose_canvas_cursor->property_text() = txt.c_str();
3259 track_canvas->get_pointer (x, y);
3260 track_canvas->window_to_world (x, y, wx, wy);
3265 /* don't get too close to the edge */
3266 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (wx);
3267 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (wy);
3269 show_verbose_canvas_cursor ();
3273 Editor::set_verbose_canvas_cursor (const string & txt, double x, double y)
3275 verbose_canvas_cursor->property_text() = txt.c_str();
3276 /* don't get too close to the edge */
3277 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (x);
3278 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (y);
3282 Editor::set_verbose_canvas_cursor_text (const string & txt)
3284 verbose_canvas_cursor->property_text() = txt.c_str();
3288 Editor::set_edit_mode (EditMode m)
3290 Config->set_edit_mode (m);
3294 Editor::cycle_edit_mode ()
3296 switch (Config->get_edit_mode()) {
3298 if (Profile->get_sae()) {
3299 Config->set_edit_mode (Lock);
3301 Config->set_edit_mode (Splice);
3305 Config->set_edit_mode (Lock);
3308 Config->set_edit_mode (Slide);
3314 Editor::edit_mode_selection_done ()
3316 string s = edit_mode_selector.get_active_text ();
3319 Config->set_edit_mode (string_to_edit_mode (s));
3324 Editor::snap_type_selection_done ()
3326 string choice = snap_type_selector.get_active_text();
3327 SnapType snaptype = SnapToBeat;
3329 if (choice == _("Beats/2")) {
3330 snaptype = SnapToBeatDiv2;
3331 } else if (choice == _("Beats/3")) {
3332 snaptype = SnapToBeatDiv3;
3333 } else if (choice == _("Beats/4")) {
3334 snaptype = SnapToBeatDiv4;
3335 } else if (choice == _("Beats/5")) {
3336 snaptype = SnapToBeatDiv5;
3337 } else if (choice == _("Beats/6")) {
3338 snaptype = SnapToBeatDiv6;
3339 } else if (choice == _("Beats/7")) {
3340 snaptype = SnapToBeatDiv7;
3341 } else if (choice == _("Beats/8")) {
3342 snaptype = SnapToBeatDiv8;
3343 } else if (choice == _("Beats/10")) {
3344 snaptype = SnapToBeatDiv10;
3345 } else if (choice == _("Beats/12")) {
3346 snaptype = SnapToBeatDiv12;
3347 } else if (choice == _("Beats/14")) {
3348 snaptype = SnapToBeatDiv14;
3349 } else if (choice == _("Beats/16")) {
3350 snaptype = SnapToBeatDiv16;
3351 } else if (choice == _("Beats/20")) {
3352 snaptype = SnapToBeatDiv20;
3353 } else if (choice == _("Beats/24")) {
3354 snaptype = SnapToBeatDiv24;
3355 } else if (choice == _("Beats/28")) {
3356 snaptype = SnapToBeatDiv28;
3357 } else if (choice == _("Beats/32")) {
3358 snaptype = SnapToBeatDiv32;
3359 } else if (choice == _("Beats")) {
3360 snaptype = SnapToBeat;
3361 } else if (choice == _("Bars")) {
3362 snaptype = SnapToBar;
3363 } else if (choice == _("Marks")) {
3364 snaptype = SnapToMark;
3365 } else if (choice == _("Region starts")) {
3366 snaptype = SnapToRegionStart;
3367 } else if (choice == _("Region ends")) {
3368 snaptype = SnapToRegionEnd;
3369 } else if (choice == _("Region bounds")) {
3370 snaptype = SnapToRegionBoundary;
3371 } else if (choice == _("Region syncs")) {
3372 snaptype = SnapToRegionSync;
3373 } else if (choice == _("CD Frames")) {
3374 snaptype = SnapToCDFrame;
3375 } else if (choice == _("Timecode Frames")) {
3376 snaptype = SnapToTimecodeFrame;
3377 } else if (choice == _("Timecode Seconds")) {
3378 snaptype = SnapToTimecodeSeconds;
3379 } else if (choice == _("Timecode Minutes")) {
3380 snaptype = SnapToTimecodeMinutes;
3381 } else if (choice == _("Seconds")) {
3382 snaptype = SnapToSeconds;
3383 } else if (choice == _("Minutes")) {
3384 snaptype = SnapToMinutes;
3387 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3389 ract->set_active ();
3394 Editor::snap_mode_selection_done ()
3396 string choice = snap_mode_selector.get_active_text();
3397 SnapMode mode = SnapNormal;
3399 if (choice == _("No Grid")) {
3401 } else if (choice == _("Grid")) {
3403 } else if (choice == _("Magnetic")) {
3404 mode = SnapMagnetic;
3407 RefPtr<RadioAction> ract = snap_mode_action (mode);
3410 ract->set_active (true);
3415 Editor::cycle_edit_point (bool with_marker)
3417 switch (_edit_point) {
3419 set_edit_point_preference (EditAtPlayhead);
3421 case EditAtPlayhead:
3423 set_edit_point_preference (EditAtSelectedMarker);
3425 set_edit_point_preference (EditAtMouse);
3428 case EditAtSelectedMarker:
3429 set_edit_point_preference (EditAtMouse);
3435 Editor::edit_point_selection_done ()
3437 string choice = edit_point_selector.get_active_text();
3438 EditPoint ep = EditAtSelectedMarker;
3440 if (choice == _("Marker")) {
3441 set_edit_point_preference (EditAtSelectedMarker);
3442 } else if (choice == _("Playhead")) {
3443 set_edit_point_preference (EditAtPlayhead);
3445 set_edit_point_preference (EditAtMouse);
3448 RefPtr<RadioAction> ract = edit_point_action (ep);
3451 ract->set_active (true);
3456 Editor::zoom_focus_selection_done ()
3458 string choice = zoom_focus_selector.get_active_text();
3459 ZoomFocus focus_type = ZoomFocusLeft;
3461 if (choice == _("Left")) {
3462 focus_type = ZoomFocusLeft;
3463 } else if (choice == _("Right")) {
3464 focus_type = ZoomFocusRight;
3465 } else if (choice == _("Center")) {
3466 focus_type = ZoomFocusCenter;
3467 } else if (choice == _("Playhead")) {
3468 focus_type = ZoomFocusPlayhead;
3469 } else if (choice == _("Mouse")) {
3470 focus_type = ZoomFocusMouse;
3471 } else if (choice == _("Edit point")) {
3472 focus_type = ZoomFocusEdit;
3475 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3478 ract->set_active ();
3483 Editor::edit_controls_button_release (GdkEventButton* ev)
3485 if (Keyboard::is_context_menu_event (ev)) {
3486 ARDOUR_UI::instance()->add_route (this);
3492 Editor::mouse_select_button_release (GdkEventButton* ev)
3494 /* this handles just right-clicks */
3496 if (ev->button != 3) {
3504 Editor::set_zoom_focus (ZoomFocus f)
3506 string str = zoom_focus_strings[(int)f];
3508 if (str != zoom_focus_selector.get_active_text()) {
3509 zoom_focus_selector.set_active_text (str);
3512 if (zoom_focus != f) {
3515 ZoomFocusChanged (); /* EMIT_SIGNAL */
3522 Editor::ensure_float (Window& win)
3524 win.set_transient_for (*this);
3528 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3530 /* recover or initialize pane positions. do this here rather than earlier because
3531 we don't want the positions to change the child allocations, which they seem to do.
3537 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3549 width = default_width;
3550 height = default_height;
3552 if ((geometry = find_named_node (*node, "geometry")) != 0) {
3554 prop = geometry->property ("x-size");
3556 width = atoi (prop->value());
3558 prop = geometry->property ("y-size");
3560 height = atoi (prop->value());
3564 if (which == static_cast<Paned*> (&edit_pane)) {
3566 if (done & Horizontal) {
3570 if (geometry && (prop = geometry->property ("notebook-shrunk"))) {
3571 _notebook_shrunk = string_is_affirmative (prop->value ());
3574 if (geometry && (prop = geometry->property ("pre-maximal-horizontal-pane-position"))) {
3575 pre_maximal_horizontal_pane_position = atoi (prop->value ());
3578 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3579 /* initial allocation is 90% to canvas, 10% to notebook */
3580 pos = (int) floor (alloc.get_width() * 0.90f);
3581 snprintf (buf, sizeof(buf), "%d", pos);
3583 pos = atoi (prop->value());
3586 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3587 edit_pane.set_position (pos);
3588 if (pre_maximal_horizontal_pane_position == 0) {
3589 pre_maximal_horizontal_pane_position = pos;
3593 done = (Pane) (done | Horizontal);
3595 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3597 if (done & Vertical) {
3601 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3602 /* initial allocation is 90% to canvas, 10% to summary */
3603 pos = (int) floor (alloc.get_height() * 0.90f);
3604 snprintf (buf, sizeof(buf), "%d", pos);
3606 pos = atoi (prop->value());
3609 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3610 editor_summary_pane.set_position (pos);
3611 pre_maximal_vertical_pane_position = pos;
3614 done = (Pane) (done | Vertical);
3619 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3621 if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) {
3622 top_hbox.remove (toolbar_frame);
3627 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3629 if (toolbar_frame.get_parent() == 0) {
3630 top_hbox.pack_end (toolbar_frame);
3635 Editor::set_show_measures (bool yn)
3637 if (_show_measures != yn) {
3640 if ((_show_measures = yn) == true) {
3642 tempo_lines->show();
3650 Editor::toggle_follow_playhead ()
3652 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3654 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3655 set_follow_playhead (tact->get_active());
3659 /** @param yn true to follow playhead, otherwise false.
3660 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
3663 Editor::set_follow_playhead (bool yn, bool catch_up)
3665 if (_follow_playhead != yn) {
3666 if ((_follow_playhead = yn) == true && catch_up) {
3668 reset_x_origin_to_follow_playhead ();
3675 Editor::toggle_stationary_playhead ()
3677 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3679 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3680 set_stationary_playhead (tact->get_active());
3685 Editor::set_stationary_playhead (bool yn)
3687 if (_stationary_playhead != yn) {
3688 if ((_stationary_playhead = yn) == true) {
3690 // FIXME need a 3.0 equivalent of this 2.X call
3691 // update_current_screen ();
3698 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3700 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3702 xfade->set_active (!xfade->active());
3707 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3709 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3711 xfade->set_follow_overlap (!xfade->following_overlap());
3716 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3718 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3724 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3728 switch (cew.run ()) {
3729 case RESPONSE_ACCEPT:
3736 PropertyChange all_crossfade_properties;
3737 all_crossfade_properties.add (ARDOUR::Properties::active);
3738 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3739 xfade->PropertyChanged (all_crossfade_properties);
3743 Editor::playlist_selector () const
3745 return *_playlist_selector;
3749 Editor::get_grid_type_as_beats (bool& success, framepos_t position)
3753 switch (_snap_type) {
3758 case SnapToBeatDiv32:
3761 case SnapToBeatDiv28:
3764 case SnapToBeatDiv24:
3767 case SnapToBeatDiv20:
3770 case SnapToBeatDiv16:
3773 case SnapToBeatDiv14:
3776 case SnapToBeatDiv12:
3779 case SnapToBeatDiv10:
3782 case SnapToBeatDiv8:
3785 case SnapToBeatDiv7:
3788 case SnapToBeatDiv6:
3791 case SnapToBeatDiv5:
3794 case SnapToBeatDiv4:
3797 case SnapToBeatDiv3:
3800 case SnapToBeatDiv2:
3806 return _session->tempo_map().meter_at (position).beats_per_bar();
3811 case SnapToTimecodeFrame:
3812 case SnapToTimecodeSeconds:
3813 case SnapToTimecodeMinutes:
3816 case SnapToRegionStart:
3817 case SnapToRegionEnd:
3818 case SnapToRegionSync:
3819 case SnapToRegionBoundary:
3829 Editor::get_nudge_distance (framepos_t pos, framecnt_t& next)
3833 ret = nudge_clock.current_duration (pos);
3834 next = ret + 1; /* XXXX fix me */
3840 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3842 ArdourDialog dialog (_("Playlist Deletion"));
3843 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3844 "If left alone, no audio files used by it will be cleaned.\n"
3845 "If deleted, audio files used by it alone by will cleaned."),
3848 dialog.set_position (WIN_POS_CENTER);
3849 dialog.get_vbox()->pack_start (label);
3853 dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT);
3854 dialog.add_button (_("Keep playlist"), RESPONSE_REJECT);
3855 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3857 switch (dialog.run ()) {
3858 case RESPONSE_ACCEPT:
3859 /* delete the playlist */
3863 case RESPONSE_REJECT:
3864 /* keep the playlist */
3876 Editor::audio_region_selection_covers (framepos_t where)
3878 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3879 if ((*a)->region()->covers (where)) {
3888 Editor::prepare_for_cleanup ()
3890 cut_buffer->clear_regions ();
3891 cut_buffer->clear_playlists ();
3893 selection->clear_regions ();
3894 selection->clear_playlists ();
3896 _regions->suspend_redisplay ();
3900 Editor::finish_cleanup ()
3902 _regions->resume_redisplay ();
3906 Editor::transport_loop_location()
3909 return _session->locations()->auto_loop_location();
3916 Editor::transport_punch_location()
3919 return _session->locations()->auto_punch_location();
3926 Editor::control_layout_scroll (GdkEventScroll* ev)
3928 if (Keyboard::some_magic_widget_has_focus()) {
3932 switch (ev->direction) {
3934 scroll_tracks_up_line ();
3938 case GDK_SCROLL_DOWN:
3939 scroll_tracks_down_line ();
3943 /* no left/right handling yet */
3951 Editor::session_state_saved (string)
3954 _snapshots->redisplay ();
3958 Editor::maximise_editing_space ()
3960 _mouse_mode_tearoff->set_visible (false);
3961 _tools_tearoff->set_visible (false);
3962 _zoom_tearoff->set_visible (false);
3964 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
3965 pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
3966 pre_maximal_editor_width = this->get_width ();
3967 pre_maximal_editor_height = this->get_height ();
3969 if (post_maximal_horizontal_pane_position == 0) {
3970 post_maximal_horizontal_pane_position = edit_pane.get_width();
3973 if (post_maximal_vertical_pane_position == 0) {
3974 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
3979 if (post_maximal_editor_width) {
3980 edit_pane.set_position (post_maximal_horizontal_pane_position -
3981 abs(post_maximal_editor_width - pre_maximal_editor_width));
3983 edit_pane.set_position (post_maximal_horizontal_pane_position);
3986 if (post_maximal_editor_height) {
3987 editor_summary_pane.set_position (post_maximal_vertical_pane_position -
3988 abs(post_maximal_editor_height - pre_maximal_editor_height));
3990 editor_summary_pane.set_position (post_maximal_vertical_pane_position);
3993 if (Config->get_keep_tearoffs()) {
3994 _mouse_mode_tearoff->set_visible (true);
3995 _tools_tearoff->set_visible (true);
3996 if (Config->get_show_zoom_tools ()) {
3997 _zoom_tearoff->set_visible (true);
4004 Editor::restore_editing_space ()
4006 // user changed width/height of panes during fullscreen
4008 if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
4009 post_maximal_horizontal_pane_position = edit_pane.get_position();
4012 if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
4013 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
4018 _mouse_mode_tearoff->set_visible (true);
4019 _tools_tearoff->set_visible (true);
4020 if (Config->get_show_zoom_tools ()) {
4021 _zoom_tearoff->set_visible (true);
4023 post_maximal_editor_width = this->get_width();
4024 post_maximal_editor_height = this->get_height();
4026 edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
4027 editor_summary_pane.set_position (pre_maximal_vertical_pane_position + abs(this->get_height() - pre_maximal_editor_height));
4031 * Make new playlists for a given track and also any others that belong
4032 * to the same active route group with the `edit' property.
4037 Editor::new_playlists (TimeAxisView* v)
4039 begin_reversible_command (_("new playlists"));
4040 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4041 _session->playlists->get (playlists);
4042 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4043 commit_reversible_command ();
4047 * Use a copy of the current playlist for a given track and also any others that belong
4048 * to the same active route group with the `edit' property.
4053 Editor::copy_playlists (TimeAxisView* v)
4055 begin_reversible_command (_("copy playlists"));
4056 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4057 _session->playlists->get (playlists);
4058 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4059 commit_reversible_command ();
4062 /** Clear the current playlist for a given track and also any others that belong
4063 * to the same active route group with the `edit' property.
4068 Editor::clear_playlists (TimeAxisView* v)
4070 begin_reversible_command (_("clear playlists"));
4071 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4072 _session->playlists->get (playlists);
4073 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4074 commit_reversible_command ();
4078 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4080 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4084 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4086 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4090 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4092 atv.clear_playlist ();
4096 Editor::on_key_press_event (GdkEventKey* ev)
4098 return key_press_focus_accelerator_handler (*this, ev);
4102 Editor::on_key_release_event (GdkEventKey* ev)
4104 return Gtk::Window::on_key_release_event (ev);
4105 // return key_press_focus_accelerator_handler (*this, ev);
4108 /** Queue up a change to the viewport x origin.
4109 * @param frame New x origin.
4112 Editor::reset_x_origin (framepos_t frame)
4114 queue_visual_change (frame);
4118 Editor::reset_y_origin (double y)
4120 queue_visual_change_y (y);
4124 Editor::reset_zoom (double fpu)
4126 queue_visual_change (fpu);
4130 Editor::reposition_and_zoom (framepos_t frame, double fpu)
4132 reset_x_origin (frame);
4135 if (!no_save_visual) {
4136 undo_visual_stack.push_back (current_visual_state(false));
4140 Editor::VisualState*
4141 Editor::current_visual_state (bool with_tracks)
4143 VisualState* vs = new VisualState;
4144 vs->y_position = vertical_adjustment.get_value();
4145 vs->frames_per_unit = frames_per_unit;
4146 vs->leftmost_frame = leftmost_frame;
4147 vs->zoom_focus = zoom_focus;
4150 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4151 vs->track_states.push_back (TAVState ((*i), &(*i)->get_state()));
4159 Editor::undo_visual_state ()
4161 if (undo_visual_stack.empty()) {
4165 redo_visual_stack.push_back (current_visual_state());
4167 VisualState* vs = undo_visual_stack.back();
4168 undo_visual_stack.pop_back();
4169 use_visual_state (*vs);
4173 Editor::redo_visual_state ()
4175 if (redo_visual_stack.empty()) {
4179 undo_visual_stack.push_back (current_visual_state());
4181 VisualState* vs = redo_visual_stack.back();
4182 redo_visual_stack.pop_back();
4183 use_visual_state (*vs);
4187 Editor::swap_visual_state ()
4189 if (undo_visual_stack.empty()) {
4190 redo_visual_state ();
4192 undo_visual_state ();
4197 Editor::use_visual_state (VisualState& vs)
4199 no_save_visual = true;
4201 _routes->suspend_redisplay ();
4203 vertical_adjustment.set_value (vs.y_position);
4205 set_zoom_focus (vs.zoom_focus);
4206 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4208 for (list<TAVState>::iterator i = vs.track_states.begin(); i != vs.track_states.end(); ++i) {
4209 TrackViewList::iterator t;
4211 /* check if the track still exists - it could have been deleted */
4213 if ((t = find (track_views.begin(), track_views.end(), i->first)) != track_views.end()) {
4214 (*t)->set_state (*(i->second), Stateful::loading_state_version);
4219 if (!vs.track_states.empty()) {
4220 _routes->update_visibility ();
4223 _routes->resume_redisplay ();
4225 no_save_visual = false;
4229 Editor::set_frames_per_unit (double fpu)
4231 /* this is the core function that controls the zoom level of the canvas. it is called
4232 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4235 if (fpu == frames_per_unit) {
4244 /* don't allow zooms that fit more than the maximum number
4245 of frames into an 800 pixel wide space.
4248 if (max_framepos / fpu < 800.0) {
4253 tempo_lines->tempo_map_changed();
4255 frames_per_unit = fpu;
4260 Editor::post_zoom ()
4262 // convert fpu to frame count
4264 framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width);
4266 if (frames_per_unit != zoom_range_clock.current_duration()) {
4267 zoom_range_clock.set (frames);
4270 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4271 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4272 (*i)->reshow_selection (selection->time);
4276 ZoomChanged (); /* EMIT_SIGNAL */
4278 //reset_scrolling_region ();
4280 if (playhead_cursor) {
4281 playhead_cursor->set_position (playhead_cursor->current_frame);
4284 refresh_location_display();
4285 _summary->set_overlays_dirty ();
4287 update_marker_labels ();
4293 Editor::queue_visual_change (framepos_t where)
4295 pending_visual_change.add (VisualChange::TimeOrigin);
4296 pending_visual_change.time_origin = where;
4297 ensure_visual_change_idle_handler ();
4301 Editor::queue_visual_change (double fpu)
4303 pending_visual_change.add (VisualChange::ZoomLevel);
4304 pending_visual_change.frames_per_unit = fpu;
4306 ensure_visual_change_idle_handler ();
4310 Editor::queue_visual_change_y (double y)
4312 pending_visual_change.add (VisualChange::YOrigin);
4313 pending_visual_change.y_origin = y;
4315 ensure_visual_change_idle_handler ();
4319 Editor::ensure_visual_change_idle_handler ()
4321 if (pending_visual_change.idle_handler_id < 0) {
4322 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4327 Editor::_idle_visual_changer (void* arg)
4329 return static_cast<Editor*>(arg)->idle_visual_changer ();
4333 Editor::idle_visual_changer ()
4335 VisualChange::Type p = pending_visual_change.pending;
4336 pending_visual_change.pending = (VisualChange::Type) 0;
4338 double const last_time_origin = horizontal_position ();
4340 if (p & VisualChange::TimeOrigin) {
4341 /* This is a bit of a hack, but set_frames_per_unit
4342 below will (if called) end up with the
4343 CrossfadeViews looking at Editor::leftmost_frame,
4344 and if we're changing origin and zoom in the same
4345 operation it will be the wrong value unless we
4349 leftmost_frame = pending_visual_change.time_origin;
4352 if (p & VisualChange::ZoomLevel) {
4353 set_frames_per_unit (pending_visual_change.frames_per_unit);
4355 compute_fixed_ruler_scale ();
4356 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4357 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4358 update_tempo_based_rulers ();
4360 if (p & VisualChange::TimeOrigin) {
4361 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4363 if (p & VisualChange::YOrigin) {
4364 vertical_adjustment.set_value (pending_visual_change.y_origin);
4367 if (last_time_origin == horizontal_position ()) {
4368 /* changed signal not emitted */
4369 update_fixed_rulers ();
4370 redisplay_tempo (true);
4373 _summary->set_overlays_dirty ();
4375 pending_visual_change.idle_handler_id = -1;
4376 return 0; /* this is always a one-shot call */
4379 struct EditorOrderTimeAxisSorter {
4380 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4381 return a->order () < b->order ();
4386 Editor::sort_track_selection (TrackViewList* sel)
4388 EditorOrderTimeAxisSorter cmp;
4393 selection->tracks.sort (cmp);
4398 Editor::get_preferred_edit_position (bool ignore_playhead)
4401 framepos_t where = 0;
4402 EditPoint ep = _edit_point;
4404 if (entered_marker) {
4405 return entered_marker->position();
4408 if (ignore_playhead && ep == EditAtPlayhead) {
4409 ep = EditAtSelectedMarker;
4413 case EditAtPlayhead:
4414 where = _session->audible_frame();
4417 case EditAtSelectedMarker:
4418 if (!selection->markers.empty()) {
4420 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4423 where = loc->start();
4434 if (!mouse_frame (where, ignored)) {
4435 /* XXX not right but what can we do ? */
4446 Editor::set_loop_range (framepos_t start, framepos_t end, string cmd)
4448 if (!_session) return;
4450 begin_reversible_command (cmd);
4454 if ((tll = transport_loop_location()) == 0) {
4455 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop);
4456 XMLNode &before = _session->locations()->get_state();
4457 _session->locations()->add (loc, true);
4458 _session->set_auto_loop_location (loc);
4459 XMLNode &after = _session->locations()->get_state();
4460 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4462 XMLNode &before = tll->get_state();
4463 tll->set_hidden (false, this);
4464 tll->set (start, end);
4465 XMLNode &after = tll->get_state();
4466 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4469 commit_reversible_command ();
4473 Editor::set_punch_range (framepos_t start, framepos_t end, string cmd)
4475 if (!_session) return;
4477 begin_reversible_command (cmd);
4481 if ((tpl = transport_punch_location()) == 0) {
4482 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoPunch);
4483 XMLNode &before = _session->locations()->get_state();
4484 _session->locations()->add (loc, true);
4485 _session->set_auto_loop_location (loc);
4486 XMLNode &after = _session->locations()->get_state();
4487 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4490 XMLNode &before = tpl->get_state();
4491 tpl->set_hidden (false, this);
4492 tpl->set (start, end);
4493 XMLNode &after = tpl->get_state();
4494 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4497 commit_reversible_command ();
4500 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4501 * @param rs List to which found regions are added.
4502 * @param where Time to look at.
4503 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4506 Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4508 const TrackViewList* tracks;
4511 tracks = &track_views;
4516 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4518 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4521 boost::shared_ptr<Track> tr;
4522 boost::shared_ptr<Playlist> pl;
4524 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4526 Playlist::RegionList* regions = pl->regions_at (
4527 (framepos_t) floor ( (double) where * tr->speed()));
4529 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4530 RegionView* rv = rtv->view()->find_view (*i);
4543 Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4545 const TrackViewList* tracks;
4548 tracks = &track_views;
4553 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4554 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4556 boost::shared_ptr<Track> tr;
4557 boost::shared_ptr<Playlist> pl;
4559 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4561 Playlist::RegionList* regions = pl->regions_touched (
4562 (framepos_t) floor ( (double)where * tr->speed()), max_framepos);
4564 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4566 RegionView* rv = rtv->view()->find_view (*i);
4579 /** Start with regions that are selected. Then add equivalent regions
4580 * on tracks in the same active edit-enabled route group as any of
4581 * the regions that we started with.
4585 Editor::get_regions_from_selection ()
4587 return get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
4590 /** Get regions using the following method:
4592 * Make an initial region list using the selected regions, unless
4593 * the edit point is `mouse' and the mouse is over an unselected
4594 * region. In this case, start with just that region.
4596 * Then, make an initial track list of the tracks that these
4597 * regions are on, and if the edit point is not `mouse', add the
4600 * Look at this track list and add any other tracks that are on the
4601 * same active edit-enabled route group as one of the initial tracks.
4603 * Finally take the initial region list and add any regions that are
4604 * under the edit point on one of the tracks on the track list to get
4605 * the returned region list.
4607 * The rationale here is that the mouse edit point is special in that
4608 * its position describes both a time and a track; the other edit
4609 * modes only describe a time. Hence if the edit point is `mouse' we
4610 * ignore selected tracks, as we assume the user means something by
4611 * pointing at a particular track. Also in this case we take note of
4612 * the region directly under the edit point, as there is always just one
4613 * (rather than possibly several with non-mouse edit points).
4617 Editor::get_regions_from_selection_and_edit_point ()
4619 RegionSelection regions;
4621 if (_edit_point == EditAtMouse && entered_regionview && !selection->regions.contains (entered_regionview)) {
4622 regions.add (entered_regionview);
4624 regions = selection->regions;
4627 TrackViewList tracks;
4629 if (_edit_point != EditAtMouse) {
4630 tracks = selection->tracks;
4633 /* Add any other tracks that have regions that are in the same
4634 edit-activated route group as one of our regions.
4636 for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) {
4638 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4640 if (g && g->is_active() && g->is_edit()) {
4641 tracks.add (axis_views_from_routes (g->route_list()));
4645 if (!tracks.empty()) {
4646 /* now find regions that are at the edit position on those tracks */
4647 framepos_t const where = get_preferred_edit_position ();
4648 get_regions_at (regions, where, tracks);
4654 /** Start with regions that are selected, or the entered regionview if none are selected.
4655 * Then add equivalent regions on tracks in the same active edit-enabled route group as any
4656 * of the regions that we started with.
4660 Editor::get_regions_from_selection_and_entered ()
4662 RegionSelection regions = selection->regions;
4664 if (regions.empty() && entered_regionview) {
4665 regions.add (entered_regionview);
4668 return get_equivalent_regions (regions, ARDOUR::Properties::edit.property_id);
4672 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4674 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4676 RouteTimeAxisView* tatv;
4678 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4680 boost::shared_ptr<Playlist> pl;
4681 vector<boost::shared_ptr<Region> > results;
4683 boost::shared_ptr<Track> tr;
4685 if ((tr = tatv->track()) == 0) {
4690 if ((pl = (tr->playlist())) != 0) {
4691 pl->get_region_list_equivalent_regions (region, results);
4694 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4695 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4696 regions.push_back (marv);
4705 Editor::show_rhythm_ferret ()
4707 if (rhythm_ferret == 0) {
4708 rhythm_ferret = new RhythmFerret(*this);
4711 rhythm_ferret->set_session (_session);
4712 rhythm_ferret->show ();
4713 rhythm_ferret->present ();
4717 Editor::first_idle ()
4719 MessageDialog* dialog = 0;
4721 if (track_views.size() > 1) {
4722 dialog = new MessageDialog (*this,
4723 string_compose (_("Please wait while %1 loads visual data"), PROGRAM_NAME),
4728 ARDOUR_UI::instance()->flush_pending ();
4731 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4735 // first idle adds route children (automation tracks), so we need to redisplay here
4736 _routes->redisplay ();
4744 Editor::_idle_resize (gpointer arg)
4746 return ((Editor*)arg)->idle_resize ();
4750 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4752 if (resize_idle_id < 0) {
4753 resize_idle_id = g_idle_add (_idle_resize, this);
4754 _pending_resize_amount = 0;
4757 /* make a note of the smallest resulting height, so that we can clamp the
4758 lower limit at TimeAxisView::hSmall */
4760 int32_t min_resulting = INT32_MAX;
4762 _pending_resize_amount += h;
4763 _pending_resize_view = view;
4765 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4767 if (selection->tracks.contains (_pending_resize_view)) {
4768 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4769 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4773 if (min_resulting < 0) {
4778 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4779 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4783 /** Handle pending resizing of tracks */
4785 Editor::idle_resize ()
4787 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4789 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4790 selection->tracks.contains (_pending_resize_view)) {
4792 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4793 if (*i != _pending_resize_view) {
4794 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4799 _pending_resize_amount = 0;
4801 _group_tabs->set_dirty ();
4802 resize_idle_id = -1;
4810 ENSURE_GUI_THREAD (*this, &Editor::located);
4812 playhead_cursor->set_position (_session->audible_frame ());
4813 if (_follow_playhead && !_pending_initial_locate) {
4814 reset_x_origin_to_follow_playhead ();
4817 _pending_locate_request = false;
4818 _pending_initial_locate = false;
4822 Editor::region_view_added (RegionView *)
4824 _summary->set_dirty ();
4828 Editor::region_view_removed ()
4830 _summary->set_dirty ();
4834 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4836 TrackViewList::const_iterator j = track_views.begin ();
4837 while (j != track_views.end()) {
4838 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4839 if (rtv && rtv->route() == r) {
4850 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4854 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4855 TimeAxisView* tv = axis_view_from_route (*i);
4866 Editor::handle_new_route (RouteList& routes)
4868 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4870 RouteTimeAxisView *rtv;
4871 list<RouteTimeAxisView*> new_views;
4873 cerr << "Handle new route\n";
4875 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4876 boost::shared_ptr<Route> route = (*x);
4878 if (route->is_hidden() || route->is_monitor()) {
4882 DataType dt = route->input()->default_type();
4884 if (dt == ARDOUR::DataType::AUDIO) {
4885 rtv = new AudioTimeAxisView (*this, _session, route, *track_canvas);
4886 } else if (dt == ARDOUR::DataType::MIDI) {
4887 rtv = new MidiTimeAxisView (*this, _session, route, *track_canvas);
4889 throw unknown_type();
4892 new_views.push_back (rtv);
4893 track_views.push_back (rtv);
4895 rtv->effective_gain_display ();
4897 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4898 rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed));
4901 _routes->routes_added (new_views);
4902 _summary->routes_added (new_views);
4904 if (show_editor_mixer_when_tracks_arrive) {
4905 show_editor_mixer (true);
4908 editor_list_button.set_sensitive (true);
4912 Editor::timeaxisview_deleted (TimeAxisView *tv)
4914 if (_session && _session->deletion_in_progress()) {
4915 /* the situation is under control */
4919 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
4921 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
4923 _routes->route_removed (tv);
4925 if (tv == entered_track) {
4929 TimeAxisView::Children c = tv->get_child_list ();
4930 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
4931 if (entered_track == i->get()) {
4936 /* remove it from the list of track views */
4938 TrackViewList::iterator i;
4940 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
4941 i = track_views.erase (i);
4944 /* update whatever the current mixer strip is displaying, if revelant */
4946 boost::shared_ptr<Route> route;
4949 route = rtav->route ();
4952 if (current_mixer_strip && current_mixer_strip->route() == route) {
4954 TimeAxisView* next_tv;
4956 if (track_views.empty()) {
4958 } else if (i == track_views.end()) {
4959 next_tv = track_views.front();
4966 set_selected_mixer_strip (*next_tv);
4968 /* make the editor mixer strip go away setting the
4969 * button to inactive (which also unticks the menu option)
4972 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
4978 Editor::hide_track_in_display (TimeAxisView* tv, bool /*temponly*/)
4980 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
4982 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
4983 // this will hide the mixer strip
4984 set_selected_mixer_strip (*tv);
4987 _routes->hide_track_in_display (*tv);
4991 Editor::sync_track_view_list_and_routes ()
4993 track_views = TrackViewList (_routes->views ());
4995 _summary->set_dirty ();
4996 _group_tabs->set_dirty ();
4998 return false; // do not call again (until needed)
5002 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
5004 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5009 /** Find a RouteTimeAxisView by the ID of its route */
5011 Editor::get_route_view_by_route_id (PBD::ID& id) const
5013 RouteTimeAxisView* v;
5015 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5016 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5017 if(v->route()->id() == id) {
5027 Editor::fit_route_group (RouteGroup *g)
5029 TrackViewList ts = axis_views_from_routes (g->route_list ());
5034 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5036 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5039 _session->cancel_audition ();
5043 if (_session->is_auditioning()) {
5044 _session->cancel_audition ();
5045 if (r == last_audition_region) {
5050 _session->audition_region (r);
5051 last_audition_region = r;
5056 Editor::hide_a_region (boost::shared_ptr<Region> r)
5058 r->set_hidden (true);
5062 Editor::show_a_region (boost::shared_ptr<Region> r)
5064 r->set_hidden (false);
5068 Editor::audition_region_from_region_list ()
5070 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5074 Editor::hide_region_from_region_list ()
5076 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5080 Editor::show_region_in_region_list ()
5082 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5086 Editor::step_edit_status_change (bool yn)
5089 start_step_editing ();
5091 stop_step_editing ();
5096 Editor::start_step_editing ()
5098 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5102 Editor::stop_step_editing ()
5104 step_edit_connection.disconnect ();
5108 Editor::check_step_edit ()
5110 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5111 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5113 mtv->check_step_edit ();
5117 return true; // do it again, till we stop
5121 Editor::horizontal_scroll_left_press ()
5123 ++_scroll_callbacks;
5125 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5126 /* delay the first auto-repeat */
5130 double x = leftmost_position() - current_page_frames() / 5;
5137 /* do hacky auto-repeat */
5138 if (!_scroll_connection.connected ()) {
5139 _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press), 100);
5140 _scroll_callbacks = 0;
5147 Editor::horizontal_scroll_left_release ()
5149 _scroll_connection.disconnect ();
5153 Editor::horizontal_scroll_right_press ()
5155 ++_scroll_callbacks;
5157 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5158 /* delay the first auto-repeat */
5162 reset_x_origin (leftmost_position() + current_page_frames() / 5);
5164 /* do hacky auto-repeat */
5165 if (!_scroll_connection.connected ()) {
5166 _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press), 100);
5167 _scroll_callbacks = 0;
5174 Editor::horizontal_scroll_right_release ()
5176 _scroll_connection.disconnect ();
5179 /** Queue a change for the Editor viewport x origin to follow the playhead */
5181 Editor::reset_x_origin_to_follow_playhead ()
5183 framepos_t const frame = playhead_cursor->current_frame;
5185 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5187 if (_session->transport_speed() < 0) {
5189 if (frame > (current_page_frames() / 2)) {
5190 center_screen (frame-(current_page_frames()/2));
5192 center_screen (current_page_frames()/2);
5197 if (frame < leftmost_frame) {
5200 if (_session->transport_rolling()) {
5201 /* rolling; end up with the playhead at the right of the page */
5202 l = frame - current_page_frames ();
5204 /* not rolling: end up with the playhead 3/4 of the way along the page */
5205 l = frame - (3 * current_page_frames() / 4);
5212 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5215 if (_session->transport_rolling()) {
5216 /* rolling: end up with the playhead on the left of the page */
5217 center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5219 /* not rolling: end up with the playhead 1/4 of the way along the page */
5220 center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5228 Editor::super_rapid_screen_update ()
5230 if (!_session || !_session->engine().running()) {
5234 /* METERING / MIXER STRIPS */
5236 /* update track meters, if required */
5237 if (is_mapped() && meters_running) {
5238 RouteTimeAxisView* rtv;
5239 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5240 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5241 rtv->fast_update ();
5246 /* and any current mixer strip */
5247 if (current_mixer_strip) {
5248 current_mixer_strip->fast_update ();
5251 /* PLAYHEAD AND VIEWPORT */
5253 framepos_t const frame = _session->audible_frame();
5255 /* There are a few reasons why we might not update the playhead / viewport stuff:
5257 * 1. we don't update things when there's a pending locate request, otherwise
5258 * when the editor requests a locate there is a chance that this method
5259 * will move the playhead before the locate request is processed, causing
5261 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5262 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5265 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5267 last_update_frame = frame;
5269 if (!_dragging_playhead) {
5270 playhead_cursor->set_position (frame);
5273 if (!_stationary_playhead) {
5275 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5276 reset_x_origin_to_follow_playhead ();
5281 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5285 // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5286 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5287 if (target <= 0.0) {
5290 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5291 target = (target * 0.15) + (current * 0.85);
5297 set_horizontal_position (current);
5306 Editor::session_going_away ()
5308 _have_idled = false;
5310 _session_connections.drop_connections ();
5312 super_rapid_screen_update_connection.disconnect ();
5314 selection->clear ();
5315 cut_buffer->clear ();
5317 clicked_regionview = 0;
5318 clicked_axisview = 0;
5319 clicked_routeview = 0;
5320 clicked_crossfadeview = 0;
5321 entered_regionview = 0;
5323 last_update_frame = 0;
5326 playhead_cursor->canvas_item.hide ();
5328 /* rip everything out of the list displays */
5332 _route_groups->clear ();
5334 /* do this first so that deleting a track doesn't reset cms to null
5335 and thus cause a leak.
5338 if (current_mixer_strip) {
5339 if (current_mixer_strip->get_parent() != 0) {
5340 global_hpacker.remove (*current_mixer_strip);
5342 delete current_mixer_strip;
5343 current_mixer_strip = 0;
5346 /* delete all trackviews */
5348 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5351 track_views.clear ();
5353 zoom_range_clock.set_session (0);
5354 nudge_clock.set_session (0);
5356 editor_list_button.set_active(false);
5357 editor_list_button.set_sensitive(false);
5359 /* clear tempo/meter rulers */
5360 remove_metric_marks ();
5362 clear_marker_display ();
5364 delete current_bbt_points;
5365 current_bbt_points = 0;
5367 /* get rid of any existing editor mixer strip */
5369 WindowTitle title(Glib::get_application_name());
5370 title += _("Editor");
5372 set_title (title.get_string());
5374 SessionHandlePtr::session_going_away ();
5379 Editor::show_editor_list (bool yn)
5382 _the_notebook.show ();
5384 _the_notebook.hide ();
5389 Editor::change_region_layering_order ()
5391 framepos_t const position = get_preferred_edit_position ();
5393 if (!clicked_routeview) {
5394 if (layering_order_editor) {
5395 layering_order_editor->hide ();
5400 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
5406 boost::shared_ptr<Playlist> pl = track->playlist();
5412 if (layering_order_editor == 0) {
5413 layering_order_editor = new RegionLayeringOrderEditor(*this);
5416 layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position);
5417 layering_order_editor->maybe_present ();
5421 Editor::update_region_layering_order_editor ()
5423 if (layering_order_editor && layering_order_editor->is_visible ()) {
5424 change_region_layering_order ();
5429 Editor::setup_fade_images ()
5431 _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear")));
5432 _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut")));
5433 _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut")));
5434 _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut")));
5435 _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut")));
5437 _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear")));
5438 _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut")));
5439 _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut")));
5440 _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut")));
5441 _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut")));
5445 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
5447 Editor::action_menu_item (std::string const & name)
5449 Glib::RefPtr<Action> a = editor_actions->get_action (name);
5452 return *manage (a->create_menu_item ());
5456 Editor::resize_text_widgets ()
5458 set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_FUDGE+10, 15);
5459 set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_FUDGE+10, 15);
5460 set_size_request_to_display_given_text (snap_type_selector, snap_type_strings, COMBO_FUDGE+10, 15);
5461 set_size_request_to_display_given_text (snap_mode_selector, snap_mode_strings, COMBO_FUDGE+10, 15);
5462 set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_FUDGE+10, 15);
5466 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
5468 EventBox* b = manage (new EventBox);
5469 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
5470 Label* l = manage (new Label (name));
5474 _the_notebook.append_page (widget, *b);
5478 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
5480 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
5481 _the_notebook.set_current_page (_the_notebook.page_num (*page));
5484 if (ev->type == GDK_2BUTTON_PRESS) {
5486 /* double-click on a notebook tab shrinks or expands the notebook */
5488 if (_notebook_shrunk) {
5489 edit_pane.set_position (pre_maximal_horizontal_pane_position);
5490 _notebook_shrunk = false;
5492 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
5493 edit_pane.set_position (edit_pane.get_position() + page->get_width());
5494 _notebook_shrunk = true;