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 , frame_label (_("Frames"))
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 frame_label.set_name ("EditorTimeButton");
411 frame_label.set_size_request (-1, (int)timebar_height);
412 frame_label.set_alignment (1.0, 0.5);
413 frame_label.set_padding (5,0);
415 frame_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();
423 meter_label.set_name ("EditorTimeButton");
424 meter_label.set_size_request (-1, (int)timebar_height);
425 meter_label.set_alignment (1.0, 0.5);
426 meter_label.set_padding (5,0);
428 meter_label.set_no_show_all();
429 mark_label.set_name ("EditorTimeButton");
430 mark_label.set_size_request (-1, (int)timebar_height);
431 mark_label.set_alignment (1.0, 0.5);
432 mark_label.set_padding (5,0);
434 mark_label.set_no_show_all();
435 cd_mark_label.set_name ("EditorTimeButton");
436 cd_mark_label.set_size_request (-1, (int)timebar_height);
437 cd_mark_label.set_alignment (1.0, 0.5);
438 cd_mark_label.set_padding (5,0);
439 cd_mark_label.hide();
440 cd_mark_label.set_no_show_all();
441 range_mark_label.set_name ("EditorTimeButton");
442 range_mark_label.set_size_request (-1, (int)timebar_height);
443 range_mark_label.set_alignment (1.0, 0.5);
444 range_mark_label.set_padding (5,0);
445 range_mark_label.hide();
446 range_mark_label.set_no_show_all();
447 transport_mark_label.set_name ("EditorTimeButton");
448 transport_mark_label.set_size_request (-1, (int)timebar_height);
449 transport_mark_label.set_alignment (1.0, 0.5);
450 transport_mark_label.set_padding (5,0);
451 transport_mark_label.hide();
452 transport_mark_label.set_no_show_all();
454 initialize_rulers ();
455 initialize_canvas ();
456 _summary = new EditorSummary (this);
458 selection->TimeChanged.connect (sigc::mem_fun(*this, &Editor::time_selection_changed));
459 selection->TracksChanged.connect (sigc::mem_fun(*this, &Editor::track_selection_changed));
460 editor_regions_selection_changed_connection = selection->RegionsChanged.connect (sigc::mem_fun(*this, &Editor::region_selection_changed));
461 selection->PointsChanged.connect (sigc::mem_fun(*this, &Editor::point_selection_changed));
462 selection->MarkersChanged.connect (sigc::mem_fun(*this, &Editor::marker_selection_changed));
464 edit_controls_vbox.set_spacing (0);
465 vertical_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &Editor::tie_vertical_scrolling), true);
466 track_canvas->signal_map_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_map_handler));
468 HBox* h = manage (new HBox);
469 _group_tabs = new EditorGroupTabs (this);
470 h->pack_start (*_group_tabs, PACK_SHRINK);
471 h->pack_start (edit_controls_vbox);
472 controls_layout.add (*h);
474 controls_layout.set_name ("EditControlsBase");
475 controls_layout.add_events (Gdk::SCROLL_MASK);
476 controls_layout.signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::control_layout_scroll), false);
478 controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
479 controls_layout.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_release));
480 controls_layout_size_request_connection = controls_layout.signal_size_request().connect (sigc::mem_fun (*this, &Editor::controls_layout_size_request));
482 _cursors = new MouseCursors;
484 ArdourCanvas::Canvas* time_pad = manage(new ArdourCanvas::Canvas());
485 ArdourCanvas::SimpleLine* pad_line_1 = manage(new ArdourCanvas::SimpleLine(*time_pad->root(),
486 0.0, 1.0, 100.0, 1.0));
487 pad_line_1->property_color_rgba() = 0xFF0000FF;
491 time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2);
492 time_canvas_vbox.set_size_request (-1, -1);
494 ruler_label_event_box.add (ruler_label_vbox);
495 ruler_label_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
496 ruler_label_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
498 time_button_event_box.add (time_button_vbox);
499 time_button_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
500 time_button_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
502 /* these enable us to have a dedicated window (for cursor setting, etc.)
503 for the canvas areas.
506 track_canvas_event_box.add (*track_canvas);
508 time_canvas_event_box.add (time_canvas_vbox);
509 time_canvas_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
511 edit_packer.set_col_spacings (0);
512 edit_packer.set_row_spacings (0);
513 edit_packer.set_homogeneous (false);
514 edit_packer.set_border_width (0);
515 edit_packer.set_name ("EditorWindow");
517 /* labels for the rulers */
518 edit_packer.attach (ruler_label_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0);
519 /* labels for the marker "tracks" */
520 edit_packer.attach (time_button_event_box, 1, 2, 1, 2, FILL, SHRINK, 0, 0);
522 edit_packer.attach (time_canvas_event_box, 2, 3, 0, 1, FILL|EXPAND, FILL, 0, 0);
524 edit_packer.attach (controls_layout, 0, 2, 2, 3, FILL, FILL|EXPAND, 0, 0);
526 edit_packer.attach (track_canvas_event_box, 2, 3, 1, 3, FILL|EXPAND, FILL|EXPAND, 0, 0);
528 bottom_hbox.set_border_width (2);
529 bottom_hbox.set_spacing (3);
531 _route_groups = new EditorRouteGroups (this);
532 _routes = new EditorRoutes (this);
533 _regions = new EditorRegions (this);
534 _snapshots = new EditorSnapshots (this);
535 _locations = new EditorLocations (this);
537 add_notebook_page (_("Regions"), _regions->widget ());
538 add_notebook_page (_("Tracks & Busses"), _routes->widget ());
539 add_notebook_page (_("Snapshots"), _snapshots->widget ());
540 add_notebook_page (_("Route Groups"), _route_groups->widget ());
541 add_notebook_page (_("Ranges & Marks"), _locations->widget ());
543 _the_notebook.set_show_tabs (true);
544 _the_notebook.set_scrollable (true);
545 _the_notebook.popup_disable ();
546 _the_notebook.set_tab_pos (Gtk::POS_RIGHT);
547 _the_notebook.show_all ();
549 post_maximal_editor_width = 0;
550 post_maximal_horizontal_pane_position = 0;
551 post_maximal_editor_height = 0;
552 post_maximal_vertical_pane_position = 0;
553 _notebook_shrunk = false;
555 editor_summary_pane.pack1(edit_packer);
557 Button* summary_arrows_left_left = manage (new Button);
558 summary_arrows_left_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
559 summary_arrows_left_left->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press)));
560 summary_arrows_left_left->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_release));
561 Button* summary_arrows_left_right = manage (new Button);
562 summary_arrows_left_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
563 summary_arrows_left_right->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press)));
564 summary_arrows_left_right->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_release));
565 VBox* summary_arrows_left = manage (new VBox);
566 summary_arrows_left->pack_start (*summary_arrows_left_left);
567 summary_arrows_left->pack_start (*summary_arrows_left_right);
569 Button* summary_arrows_right_left = manage (new Button);
570 summary_arrows_right_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
571 summary_arrows_right_left->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press)));
572 summary_arrows_right_left->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_release));
573 Button* summary_arrows_right_right = manage (new Button);
574 summary_arrows_right_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
575 summary_arrows_right_right->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press)));
576 summary_arrows_right_right->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_release));
577 VBox* summary_arrows_right = manage (new VBox);
578 summary_arrows_right->pack_start (*summary_arrows_right_left);
579 summary_arrows_right->pack_start (*summary_arrows_right_right);
581 Frame* summary_frame = manage (new Frame);
582 summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN);
583 summary_frame->add (*_summary);
584 summary_frame->show ();
586 _summary_hbox.pack_start (*summary_arrows_left, false, false);
587 _summary_hbox.pack_start (*summary_frame, true, true);
588 _summary_hbox.pack_start (*summary_arrows_right, false, false);
590 editor_summary_pane.pack2 (_summary_hbox);
592 edit_pane.pack1 (editor_summary_pane, true, true);
593 edit_pane.pack2 (_the_notebook, false, true);
595 editor_summary_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun (*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&editor_summary_pane)));
597 /* XXX: editor_summary_pane might need similar special OS X treatment to the edit_pane */
599 edit_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&edit_pane)));
601 Glib::PropertyProxy<int> proxy = edit_pane.property_position();
602 proxy.signal_changed().connect (bind (sigc::ptr_fun (pane_size_watcher), static_cast<Paned*> (&edit_pane)));
604 top_hbox.pack_start (toolbar_frame);
606 HBox *hbox = manage (new HBox);
607 hbox->pack_start (edit_pane, true, true);
609 global_vpacker.pack_start (top_hbox, false, false);
610 global_vpacker.pack_start (*hbox, true, true);
612 global_hpacker.pack_start (global_vpacker, true, true);
614 set_name ("EditorWindow");
615 add_accel_group (ActionManager::ui_manager->get_accel_group());
617 status_bar_hpacker.show ();
619 vpacker.pack_end (status_bar_hpacker, false, false);
620 vpacker.pack_end (global_hpacker, true, true);
622 /* register actions now so that set_state() can find them and set toggles/checks etc */
627 setup_midi_toolbar ();
629 _snap_type = SnapToBeat;
630 set_snap_to (_snap_type);
631 _snap_mode = SnapOff;
632 set_snap_mode (_snap_mode);
633 set_mouse_mode (MouseObject, true);
634 set_edit_point_preference (EditAtMouse, true);
636 _playlist_selector = new PlaylistSelector();
637 _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
639 RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), ui_bind (&Editor::catch_vanishing_regionview, this, _1), gui_context());
643 nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
644 nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
646 nudge_forward_button.set_name ("TransportButton");
647 nudge_backward_button.set_name ("TransportButton");
649 fade_context_menu.set_name ("ArdourContextMenu");
651 /* icons, titles, WM stuff */
653 list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
654 Glib::RefPtr<Gdk::Pixbuf> icon;
656 if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
657 window_icons.push_back (icon);
659 if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
660 window_icons.push_back (icon);
662 if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
663 window_icons.push_back (icon);
665 if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
666 window_icons.push_back (icon);
668 if (!window_icons.empty()) {
669 set_icon_list (window_icons);
670 set_default_icon_list (window_icons);
673 WindowTitle title(Glib::get_application_name());
674 title += _("Editor");
675 set_title (title.get_string());
676 set_wmclass (X_("ardour_editor"), PROGRAM_NAME);
679 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
681 signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
682 signal_delete_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
684 /* allow external control surfaces/protocols to do various things */
686 ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context());
687 ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context());
688 ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context());
689 ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), ui_bind (&Editor::control_scroll, this, _1), gui_context());
690 BasicUI::AccessAction.connect (*this, invalidator (*this), ui_bind (&Editor::access_action, this, _1, _2), gui_context());
692 /* problematic: has to return a value and thus cannot be x-thread */
694 Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
696 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
698 TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
700 _ignore_region_action = false;
701 _last_region_menu_was_main = false;
702 _popup_region_menu_item = 0;
704 _show_marker_lines = false;
705 _over_region_trim_target = false;
710 setup_fade_images ();
716 if(image_socket_listener) {
717 if(image_socket_listener->is_connected())
719 image_socket_listener->close_connection() ;
722 delete image_socket_listener ;
723 image_socket_listener = 0 ;
728 delete _route_groups;
734 Editor::add_toplevel_controls (Container& cont)
736 vpacker.pack_start (cont, false, false);
741 Editor::catch_vanishing_regionview (RegionView *rv)
743 /* note: the selection will take care of the vanishing
744 audioregionview by itself.
747 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
751 if (clicked_regionview == rv) {
752 clicked_regionview = 0;
755 if (entered_regionview == rv) {
756 set_entered_regionview (0);
759 if (!_all_region_actions_sensitized) {
760 sensitize_all_region_actions (true);
765 Editor::set_entered_regionview (RegionView* rv)
767 if (rv == entered_regionview) {
771 if (entered_regionview) {
772 entered_regionview->exited ();
775 if ((entered_regionview = rv) != 0) {
776 entered_regionview->entered (internal_editing ());
779 if (!_all_region_actions_sensitized && _last_region_menu_was_main) {
780 /* This RegionView entry might have changed what region actions
781 are allowed, so sensitize them all in case a key is pressed.
783 sensitize_all_region_actions (true);
788 Editor::set_entered_track (TimeAxisView* tav)
791 entered_track->exited ();
794 if ((entered_track = tav) != 0) {
795 entered_track->entered ();
800 Editor::show_window ()
802 if (!is_visible ()) {
805 /* re-hide stuff if necessary */
806 editor_list_button_toggled ();
807 parameter_changed ("show-summary");
808 parameter_changed ("show-edit-group-tabs");
809 parameter_changed ("show-zoom-tools");
811 /* now reset all audio_time_axis heights, because widgets might need
817 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
818 tv = (static_cast<TimeAxisView*>(*i));
827 Editor::instant_save ()
829 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
834 _session->add_instant_xml(get_state());
836 Config->add_instant_xml(get_state());
841 Editor::zoom_adjustment_changed ()
847 double fpu = zoom_range_clock.current_duration() / _canvas_width;
851 zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width));
852 } else if (fpu > _session->current_end_frame() / _canvas_width) {
853 fpu = _session->current_end_frame() / _canvas_width;
854 zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width));
861 Editor::control_scroll (float fraction)
863 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
869 double step = fraction * current_page_frames();
872 _control_scroll_target is an optional<T>
874 it acts like a pointer to an framepos_t, with
875 a operator conversion to boolean to check
876 that it has a value could possibly use
877 playhead_cursor->current_frame to store the
878 value and a boolean in the class to know
879 when it's out of date
882 if (!_control_scroll_target) {
883 _control_scroll_target = _session->transport_frame();
884 _dragging_playhead = true;
887 if ((fraction < 0.0f) && (*_control_scroll_target < (framepos_t) fabs(step))) {
888 *_control_scroll_target = 0;
889 } else if ((fraction > 0.0f) && (max_framepos - *_control_scroll_target < step)) {
890 *_control_scroll_target = max_framepos - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
892 *_control_scroll_target += (framepos_t) floor (step);
895 /* move visuals, we'll catch up with it later */
897 playhead_cursor->set_position (*_control_scroll_target);
898 UpdateAllTransportClocks (*_control_scroll_target);
900 if (*_control_scroll_target > (current_page_frames() / 2)) {
901 /* try to center PH in window */
902 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
908 Now we do a timeout to actually bring the session to the right place
909 according to the playhead. This is to avoid reading disk buffers on every
910 call to control_scroll, which is driven by ScrollTimeline and therefore
911 probably by a control surface wheel which can generate lots of events.
913 /* cancel the existing timeout */
915 control_scroll_connection.disconnect ();
917 /* add the next timeout */
919 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
923 Editor::deferred_control_scroll (framepos_t /*target*/)
925 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
926 // reset for next stream
927 _control_scroll_target = boost::none;
928 _dragging_playhead = false;
933 Editor::access_action (std::string action_group, std::string action_item)
939 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
942 act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
950 Editor::on_realize ()
952 Window::on_realize ();
957 Editor::map_position_change (framepos_t frame)
959 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, frame)
965 if (_follow_playhead) {
966 center_screen (frame);
969 playhead_cursor->set_position (frame);
973 Editor::center_screen (framepos_t frame)
975 double page = _canvas_width * frames_per_unit;
977 /* if we're off the page, then scroll.
980 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
981 center_screen_internal (frame, page);
986 Editor::center_screen_internal (framepos_t frame, float page)
991 frame -= (framepos_t) page;
996 reset_x_origin (frame);
1001 Editor::update_title ()
1003 ENSURE_GUI_THREAD (*this, &Editor::update_title)
1006 bool dirty = _session->dirty();
1008 string session_name;
1010 if (_session->snap_name() != _session->name()) {
1011 session_name = _session->snap_name();
1013 session_name = _session->name();
1017 session_name = "*" + session_name;
1020 WindowTitle title(session_name);
1021 title += Glib::get_application_name();
1022 set_title (title.get_string());
1027 Editor::set_session (Session *t)
1029 SessionHandlePtr::set_session (t);
1035 zoom_range_clock.set_session (_session);
1036 _playlist_selector->set_session (_session);
1037 nudge_clock.set_session (_session);
1038 _summary->set_session (_session);
1039 _group_tabs->set_session (_session);
1040 _route_groups->set_session (_session);
1041 _regions->set_session (_session);
1042 _snapshots->set_session (_session);
1043 _routes->set_session (_session);
1044 _locations->set_session (_session);
1046 if (rhythm_ferret) {
1047 rhythm_ferret->set_session (_session);
1050 if (analysis_window) {
1051 analysis_window->set_session (_session);
1055 sfbrowser->set_session (_session);
1058 compute_fixed_ruler_scale ();
1060 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1061 set_state (*node, Stateful::loading_state_version);
1063 /* catch up with the playhead */
1065 _session->request_locate (playhead_cursor->current_frame);
1066 _pending_initial_locate = true;
1070 /* These signals can all be emitted by a non-GUI thread. Therefore the
1071 handlers for them must not attempt to directly interact with the GUI,
1072 but use Gtkmm2ext::UI::instance()->call_slot();
1075 _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), ui_bind(&Editor::step_edit_status_change, this, _1), gui_context());
1076 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1077 _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context());
1078 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context());
1079 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1080 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context());
1081 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1082 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
1083 _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Editor::session_state_saved, this, _1), gui_context());
1084 _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context());
1085 _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&Editor::location_gone, this, _1), gui_context());
1086 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1087 _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display_s, this, _1), gui_context());
1088 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1090 if (Profile->get_sae()) {
1091 Timecode::BBT_Time bbt;
1095 framepos_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1);
1096 nudge_clock.set_mode(AudioClock::BBT);
1097 nudge_clock.set (pos, true, 0, AudioClock::BBT);
1100 nudge_clock.set (_session->frame_rate() * 5, true, 0, AudioClock::Timecode); // default of 5 seconds
1103 playhead_cursor->canvas_item.show ();
1105 Location* loc = _session->locations()->auto_loop_location();
1107 loc = new Location (*_session, 0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1108 if (loc->start() == loc->end()) {
1109 loc->set_end (loc->start() + 1);
1111 _session->locations()->add (loc, false);
1112 _session->set_auto_loop_location (loc);
1115 loc->set_name (_("Loop"));
1118 loc = _session->locations()->auto_punch_location();
1120 loc = new Location (*_session, 0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1121 if (loc->start() == loc->end()) {
1122 loc->set_end (loc->start() + 1);
1124 _session->locations()->add (loc, false);
1125 _session->set_auto_punch_location (loc);
1128 loc->set_name (_("Punch"));
1131 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1132 Config->map_parameters (pc);
1133 _session->config.map_parameters (pc);
1135 refresh_location_display ();
1137 restore_ruler_visibility ();
1138 //tempo_map_changed (PropertyChange (0));
1139 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1141 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1142 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1145 super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
1146 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1149 switch (_snap_type) {
1150 case SnapToRegionStart:
1151 case SnapToRegionEnd:
1152 case SnapToRegionSync:
1153 case SnapToRegionBoundary:
1154 build_region_boundary_cache ();
1161 /* register for undo history */
1162 _session->register_with_memento_command_factory(_id, this);
1164 ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated));
1166 start_updating_meters ();
1170 Editor::action_pre_activated (Glib::RefPtr<Action> const & a)
1172 if (a->get_name() == "RegionMenu") {
1173 /* When the main menu's region menu is opened, we setup the actions so that they look right
1174 in the menu. I can't find a way of getting a signal when this menu is subsequently closed,
1175 so we resensitize all region actions when the entered regionview or the region selection
1176 changes. HOWEVER we can't always resensitize on entered_regionview change because that
1177 happens after the region context menu is opened. So we set a flag here, too.
1181 sensitize_the_right_region_actions ();
1182 _last_region_menu_was_main = true;
1186 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1188 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1190 using namespace Menu_Helpers;
1191 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1194 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1198 MenuList& items (fade_context_menu.items());
1202 switch (item_type) {
1204 case FadeInHandleItem:
1205 if (arv->audio_region()->fade_in_active()) {
1206 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1208 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1211 items.push_back (SeparatorElem());
1213 if (Profile->get_sae()) {
1215 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)));
1216 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)));
1223 *_fade_in_images[FadeLinear],
1224 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)
1228 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1233 *_fade_in_images[FadeFast],
1234 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)
1237 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1242 *_fade_in_images[FadeLogB],
1243 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogB)
1246 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1251 *_fade_in_images[FadeLogA],
1252 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogA)
1255 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1260 *_fade_in_images[FadeSlow],
1261 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow)
1264 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1270 case FadeOutHandleItem:
1271 if (arv->audio_region()->fade_out_active()) {
1272 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1274 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1277 items.push_back (SeparatorElem());
1279 if (Profile->get_sae()) {
1280 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)));
1281 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)));
1287 *_fade_out_images[FadeLinear],
1288 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)
1292 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1297 *_fade_out_images[FadeFast],
1298 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)
1301 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1306 *_fade_out_images[FadeLogB],
1307 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogA)
1310 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1315 *_fade_out_images[FadeLogA],
1316 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogB)
1319 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1324 *_fade_out_images[FadeSlow],
1325 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast)
1328 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1334 fatal << _("programming error: ")
1335 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1340 fade_context_menu.popup (button, time);
1344 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1346 using namespace Menu_Helpers;
1347 Menu* (Editor::*build_menu_function)();
1350 switch (item_type) {
1352 case RegionViewName:
1353 case RegionViewNameHighlight:
1354 case LeftFrameHandle:
1355 case RightFrameHandle:
1356 if (with_selection) {
1357 build_menu_function = &Editor::build_track_selection_context_menu;
1359 build_menu_function = &Editor::build_track_region_context_menu;
1364 if (with_selection) {
1365 build_menu_function = &Editor::build_track_selection_context_menu;
1367 build_menu_function = &Editor::build_track_context_menu;
1371 case CrossfadeViewItem:
1372 build_menu_function = &Editor::build_track_crossfade_context_menu;
1376 if (clicked_routeview->track()) {
1377 build_menu_function = &Editor::build_track_context_menu;
1379 build_menu_function = &Editor::build_track_bus_context_menu;
1384 /* probably shouldn't happen but if it does, we don't care */
1388 menu = (this->*build_menu_function)();
1389 menu->set_name ("ArdourContextMenu");
1391 /* now handle specific situations */
1393 switch (item_type) {
1395 case RegionViewName:
1396 case RegionViewNameHighlight:
1397 case LeftFrameHandle:
1398 case RightFrameHandle:
1399 if (!with_selection) {
1400 if (region_edit_menu_split_item) {
1401 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1402 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1404 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1407 if (region_edit_menu_split_multichannel_item) {
1408 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1409 region_edit_menu_split_multichannel_item->set_sensitive (true);
1411 region_edit_menu_split_multichannel_item->set_sensitive (false);
1420 case CrossfadeViewItem:
1427 /* probably shouldn't happen but if it does, we don't care */
1431 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1433 /* Bounce to disk */
1435 using namespace Menu_Helpers;
1436 MenuList& edit_items = menu->items();
1438 edit_items.push_back (SeparatorElem());
1440 switch (clicked_routeview->audio_track()->freeze_state()) {
1441 case AudioTrack::NoFreeze:
1442 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1445 case AudioTrack::Frozen:
1446 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1449 case AudioTrack::UnFrozen:
1450 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1456 if (item_type == StreamItem && clicked_routeview) {
1457 clicked_routeview->build_underlay_menu(menu);
1460 /* When the region menu is opened, we setup the actions so that they look right
1463 sensitize_the_right_region_actions ();
1464 _last_region_menu_was_main = false;
1466 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1467 menu->popup (button, time);
1471 Editor::build_track_context_menu ()
1473 using namespace Menu_Helpers;
1475 MenuList& edit_items = track_context_menu.items();
1478 add_dstream_context_items (edit_items);
1479 return &track_context_menu;
1483 Editor::build_track_bus_context_menu ()
1485 using namespace Menu_Helpers;
1487 MenuList& edit_items = track_context_menu.items();
1490 add_bus_context_items (edit_items);
1491 return &track_context_menu;
1495 Editor::build_track_region_context_menu ()
1497 using namespace Menu_Helpers;
1498 MenuList& edit_items = track_region_context_menu.items();
1501 /* we've just cleared the track region context menu, so the menu that these
1502 two items were on will have disappeared; stop them dangling.
1504 region_edit_menu_split_item = 0;
1505 region_edit_menu_split_multichannel_item = 0;
1507 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1510 boost::shared_ptr<Track> tr;
1511 boost::shared_ptr<Playlist> pl;
1513 /* Don't offer a region submenu if we are in internal edit mode, as we don't select regions in this
1514 mode and so offering region context is somewhat confusing.
1516 if ((tr = rtv->track()) && ((pl = tr->playlist())) && !internal_editing()) {
1517 framepos_t const framepos = (framepos_t) floor ((double) get_preferred_edit_position() * tr->speed());
1518 uint32_t regions_at = pl->count_regions_at (framepos);
1519 add_region_context_items (edit_items, regions_at > 1);
1523 add_dstream_context_items (edit_items);
1525 return &track_region_context_menu;
1529 Editor::build_track_crossfade_context_menu ()
1531 using namespace Menu_Helpers;
1532 MenuList& edit_items = track_crossfade_context_menu.items();
1533 edit_items.clear ();
1535 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1538 boost::shared_ptr<Track> tr;
1539 boost::shared_ptr<Playlist> pl;
1540 boost::shared_ptr<AudioPlaylist> apl;
1542 if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1544 AudioPlaylist::Crossfades xfades;
1546 apl->crossfades_at (get_preferred_edit_position (), xfades);
1548 bool many = xfades.size() > 1;
1550 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1551 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1554 framepos_t framepos = (framepos_t) floor ((double) get_preferred_edit_position() * tr->speed());
1555 uint32_t regions_at = pl->count_regions_at (framepos);
1556 add_region_context_items (edit_items, regions_at > 1);
1560 add_dstream_context_items (edit_items);
1562 return &track_crossfade_context_menu;
1566 Editor::analyze_region_selection ()
1568 if (analysis_window == 0) {
1569 analysis_window = new AnalysisWindow();
1572 analysis_window->set_session(_session);
1574 analysis_window->show_all();
1577 analysis_window->set_regionmode();
1578 analysis_window->analyze();
1580 analysis_window->present();
1584 Editor::analyze_range_selection()
1586 if (analysis_window == 0) {
1587 analysis_window = new AnalysisWindow();
1590 analysis_window->set_session(_session);
1592 analysis_window->show_all();
1595 analysis_window->set_rangemode();
1596 analysis_window->analyze();
1598 analysis_window->present();
1602 Editor::build_track_selection_context_menu ()
1604 using namespace Menu_Helpers;
1605 MenuList& edit_items = track_selection_context_menu.items();
1606 edit_items.clear ();
1608 add_selection_context_items (edit_items);
1609 // edit_items.push_back (SeparatorElem());
1610 // add_dstream_context_items (edit_items);
1612 return &track_selection_context_menu;
1615 /** Add context menu items relevant to crossfades.
1616 * @param edit_items List to add the items to.
1619 Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1621 using namespace Menu_Helpers;
1622 Menu *xfade_menu = manage (new Menu);
1623 MenuList& items = xfade_menu->items();
1624 xfade_menu->set_name ("ArdourContextMenu");
1627 if (xfade->active()) {
1633 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1634 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1636 if (xfade->can_follow_overlap()) {
1638 if (xfade->following_overlap()) {
1639 str = _("Convert to Short");
1641 str = _("Convert to Full");
1644 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1648 str = xfade->out()->name();
1650 str += xfade->in()->name();
1652 str = _("Crossfade");
1655 edit_items.push_back (MenuElem (str, *xfade_menu));
1656 edit_items.push_back (SeparatorElem());
1660 Editor::xfade_edit_left_region ()
1662 if (clicked_crossfadeview) {
1663 clicked_crossfadeview->left_view.show_region_editor ();
1668 Editor::xfade_edit_right_region ()
1670 if (clicked_crossfadeview) {
1671 clicked_crossfadeview->right_view.show_region_editor ();
1676 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, bool multiple_regions_at_position)
1678 using namespace Menu_Helpers;
1680 /* OK, stick the region submenu at the top of the list, and then add
1684 /* we have to hack up the region name because "_" has a special
1685 meaning for menu titles.
1688 RegionSelection rs = get_regions_from_selection_and_entered ();
1690 string::size_type pos = 0;
1691 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1693 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1694 menu_item_name.replace (pos, 1, "__");
1698 if (_popup_region_menu_item == 0) {
1699 _popup_region_menu_item = new MenuItem (menu_item_name);
1700 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1701 _popup_region_menu_item->show ();
1703 _popup_region_menu_item->set_label (menu_item_name);
1706 edit_items.push_back (*_popup_region_menu_item);
1707 if (multiple_regions_at_position && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1708 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region")->create_menu_item ()));
1710 edit_items.push_back (SeparatorElem());
1713 /** Add context menu items relevant to selection ranges.
1714 * @param edit_items List to add the items to.
1717 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1719 using namespace Menu_Helpers;
1721 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1722 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1724 edit_items.push_back (SeparatorElem());
1725 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1727 if (!selection->regions.empty()) {
1728 edit_items.push_back (SeparatorElem());
1729 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)));
1730 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)));
1733 edit_items.push_back (SeparatorElem());
1734 edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection)));
1735 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1737 edit_items.push_back (SeparatorElem());
1738 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1740 edit_items.push_back (SeparatorElem());
1741 edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1742 edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1744 edit_items.push_back (SeparatorElem());
1745 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1747 edit_items.push_back (SeparatorElem());
1748 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1749 edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1750 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1752 edit_items.push_back (SeparatorElem());
1753 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1754 edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1755 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1756 edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1757 edit_items.push_back (MenuElem (_("Export Range"), sigc::mem_fun(*this, &Editor::export_selection)));
1762 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1764 using namespace Menu_Helpers;
1768 Menu *play_menu = manage (new Menu);
1769 MenuList& play_items = play_menu->items();
1770 play_menu->set_name ("ArdourContextMenu");
1772 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1773 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1774 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1775 play_items.push_back (SeparatorElem());
1776 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
1778 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1782 Menu *select_menu = manage (new Menu);
1783 MenuList& select_items = select_menu->items();
1784 select_menu->set_name ("ArdourContextMenu");
1786 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1787 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1788 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1789 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1790 select_items.push_back (SeparatorElem());
1791 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
1792 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
1793 select_items.push_back (SeparatorElem());
1794 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1795 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1796 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1797 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1798 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
1799 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
1800 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
1802 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1806 Menu *cutnpaste_menu = manage (new Menu);
1807 MenuList& cutnpaste_items = cutnpaste_menu->items();
1808 cutnpaste_menu->set_name ("ArdourContextMenu");
1810 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1811 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1812 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1814 cutnpaste_items.push_back (SeparatorElem());
1816 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
1817 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
1819 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
1821 /* Adding new material */
1823 edit_items.push_back (SeparatorElem());
1824 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
1825 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
1829 Menu *nudge_menu = manage (new Menu());
1830 MenuList& nudge_items = nudge_menu->items();
1831 nudge_menu->set_name ("ArdourContextMenu");
1833 edit_items.push_back (SeparatorElem());
1834 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1835 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1836 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1837 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1839 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1843 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
1845 using namespace Menu_Helpers;
1849 Menu *play_menu = manage (new Menu);
1850 MenuList& play_items = play_menu->items();
1851 play_menu->set_name ("ArdourContextMenu");
1853 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1854 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1855 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1859 Menu *select_menu = manage (new Menu);
1860 MenuList& select_items = select_menu->items();
1861 select_menu->set_name ("ArdourContextMenu");
1863 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1864 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1865 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1866 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1867 select_items.push_back (SeparatorElem());
1868 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1869 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1870 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1871 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1873 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1877 Menu *cutnpaste_menu = manage (new Menu);
1878 MenuList& cutnpaste_items = cutnpaste_menu->items();
1879 cutnpaste_menu->set_name ("ArdourContextMenu");
1881 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1882 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1883 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1885 Menu *nudge_menu = manage (new Menu());
1886 MenuList& nudge_items = nudge_menu->items();
1887 nudge_menu->set_name ("ArdourContextMenu");
1889 edit_items.push_back (SeparatorElem());
1890 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1891 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1892 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1893 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1895 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1899 Editor::snap_type() const
1905 Editor::snap_mode() const
1911 Editor::set_snap_to (SnapType st)
1913 unsigned int snap_ind = (unsigned int)st;
1917 if (snap_ind > snap_type_strings.size() - 1) {
1919 _snap_type = (SnapType)snap_ind;
1922 string str = snap_type_strings[snap_ind];
1924 if (str != snap_type_selector.get_active_text()) {
1925 snap_type_selector.set_active_text (str);
1930 switch (_snap_type) {
1931 case SnapToBeatDiv32:
1932 case SnapToBeatDiv28:
1933 case SnapToBeatDiv24:
1934 case SnapToBeatDiv20:
1935 case SnapToBeatDiv16:
1936 case SnapToBeatDiv14:
1937 case SnapToBeatDiv12:
1938 case SnapToBeatDiv10:
1939 case SnapToBeatDiv8:
1940 case SnapToBeatDiv7:
1941 case SnapToBeatDiv6:
1942 case SnapToBeatDiv5:
1943 case SnapToBeatDiv4:
1944 case SnapToBeatDiv3:
1945 case SnapToBeatDiv2:
1946 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
1947 update_tempo_based_rulers ();
1950 case SnapToRegionStart:
1951 case SnapToRegionEnd:
1952 case SnapToRegionSync:
1953 case SnapToRegionBoundary:
1954 build_region_boundary_cache ();
1962 SnapChanged (); /* EMIT SIGNAL */
1966 Editor::set_snap_mode (SnapMode mode)
1969 string str = snap_mode_strings[(int)mode];
1971 if (str != snap_mode_selector.get_active_text ()) {
1972 snap_mode_selector.set_active_text (str);
1978 Editor::set_edit_point_preference (EditPoint ep, bool force)
1980 bool changed = (_edit_point != ep);
1983 string str = edit_point_strings[(int)ep];
1985 if (str != edit_point_selector.get_active_text ()) {
1986 edit_point_selector.set_active_text (str);
1989 set_canvas_cursor ();
1991 if (!force && !changed) {
1995 const char* action=NULL;
1997 switch (_edit_point) {
1998 case EditAtPlayhead:
1999 action = "edit-at-playhead";
2001 case EditAtSelectedMarker:
2002 action = "edit-at-marker";
2005 action = "edit-at-mouse";
2009 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2011 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2015 bool in_track_canvas;
2017 if (!mouse_frame (foo, in_track_canvas)) {
2018 in_track_canvas = false;
2021 reset_canvas_action_sensitivity (in_track_canvas);
2027 Editor::set_state (const XMLNode& node, int /*version*/)
2029 const XMLProperty* prop;
2031 int x, y, xoff, yoff;
2034 if ((prop = node.property ("id")) != 0) {
2035 _id = prop->value ();
2038 g.base_width = default_width;
2039 g.base_height = default_height;
2045 if ((geometry = find_named_node (node, "geometry")) != 0) {
2049 if ((prop = geometry->property("x_size")) == 0) {
2050 prop = geometry->property ("x-size");
2053 g.base_width = atoi(prop->value());
2055 if ((prop = geometry->property("y_size")) == 0) {
2056 prop = geometry->property ("y-size");
2059 g.base_height = atoi(prop->value());
2062 if ((prop = geometry->property ("x_pos")) == 0) {
2063 prop = geometry->property ("x-pos");
2066 x = atoi (prop->value());
2069 if ((prop = geometry->property ("y_pos")) == 0) {
2070 prop = geometry->property ("y-pos");
2073 y = atoi (prop->value());
2076 if ((prop = geometry->property ("x_off")) == 0) {
2077 prop = geometry->property ("x-off");
2080 xoff = atoi (prop->value());
2082 if ((prop = geometry->property ("y_off")) == 0) {
2083 prop = geometry->property ("y-off");
2086 yoff = atoi (prop->value());
2090 set_default_size (g.base_width, g.base_height);
2093 if (_session && (prop = node.property ("playhead"))) {
2095 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2096 playhead_cursor->set_position (pos);
2098 playhead_cursor->set_position (0);
2101 if ((prop = node.property ("mixer-width"))) {
2102 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2105 if ((prop = node.property ("zoom-focus"))) {
2106 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2109 if ((prop = node.property ("zoom"))) {
2110 reset_zoom (PBD::atof (prop->value()));
2112 reset_zoom (frames_per_unit);
2115 if ((prop = node.property ("snap-to"))) {
2116 set_snap_to ((SnapType) atoi (prop->value()));
2119 if ((prop = node.property ("snap-mode"))) {
2120 set_snap_mode ((SnapMode) atoi (prop->value()));
2123 if ((prop = node.property ("mouse-mode"))) {
2124 MouseMode m = str2mousemode(prop->value());
2125 set_mouse_mode (m, true);
2127 set_mouse_mode (MouseObject, true);
2130 if ((prop = node.property ("left-frame")) != 0){
2132 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2133 reset_x_origin (pos);
2137 if ((prop = node.property ("y-origin")) != 0) {
2138 reset_y_origin (atof (prop->value ()));
2141 if ((prop = node.property ("internal-edit"))) {
2142 bool yn = string_is_affirmative (prop->value());
2143 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2145 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2146 tact->set_active (!yn);
2147 tact->set_active (yn);
2151 if ((prop = node.property ("join-object-range"))) {
2152 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2155 if ((prop = node.property ("edit-point"))) {
2156 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2159 if ((prop = node.property ("show-measures"))) {
2160 bool yn = string_is_affirmative (prop->value());
2161 _show_measures = yn;
2162 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2164 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2165 /* do it twice to force the change */
2166 tact->set_active (!yn);
2167 tact->set_active (yn);
2171 if ((prop = node.property ("follow-playhead"))) {
2172 bool yn = string_is_affirmative (prop->value());
2173 set_follow_playhead (yn);
2174 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2176 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2177 if (tact->get_active() != yn) {
2178 tact->set_active (yn);
2183 if ((prop = node.property ("stationary-playhead"))) {
2184 bool yn = (prop->value() == "yes");
2185 set_stationary_playhead (yn);
2186 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2188 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2189 if (tact->get_active() != yn) {
2190 tact->set_active (yn);
2195 if ((prop = node.property ("region-list-sort-type"))) {
2196 RegionListSortType st;
2197 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2200 if ((prop = node.property ("xfades-visible"))) {
2201 bool yn = string_is_affirmative (prop->value());
2202 _xfade_visibility = !yn;
2203 // set_xfade_visibility (yn);
2206 if ((prop = node.property ("show-editor-mixer"))) {
2208 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2211 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2212 bool yn = string_is_affirmative (prop->value());
2214 /* do it twice to force the change */
2216 tact->set_active (!yn);
2217 tact->set_active (yn);
2220 if ((prop = node.property ("show-editor-list"))) {
2222 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2225 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2226 bool yn = string_is_affirmative (prop->value());
2228 /* do it twice to force the change */
2230 tact->set_active (!yn);
2231 tact->set_active (yn);
2234 if ((prop = node.property (X_("editor-list-page")))) {
2235 _the_notebook.set_current_page (atoi (prop->value ()));
2238 if ((prop = node.property (X_("show-marker-lines")))) {
2239 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2241 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2242 bool yn = string_is_affirmative (prop->value ());
2244 tact->set_active (!yn);
2245 tact->set_active (yn);
2248 XMLNodeList children = node.children ();
2249 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2250 selection->set_state (**i, Stateful::current_state_version);
2251 _regions->set_state (**i);
2258 Editor::get_state ()
2260 XMLNode* node = new XMLNode ("Editor");
2263 _id.print (buf, sizeof (buf));
2264 node->add_property ("id", buf);
2266 if (is_realized()) {
2267 Glib::RefPtr<Gdk::Window> win = get_window();
2269 int x, y, xoff, yoff, width, height;
2270 win->get_root_origin(x, y);
2271 win->get_position(xoff, yoff);
2272 win->get_size(width, height);
2274 XMLNode* geometry = new XMLNode ("geometry");
2276 snprintf(buf, sizeof(buf), "%d", width);
2277 geometry->add_property("x-size", string(buf));
2278 snprintf(buf, sizeof(buf), "%d", height);
2279 geometry->add_property("y-size", string(buf));
2280 snprintf(buf, sizeof(buf), "%d", x);
2281 geometry->add_property("x-pos", string(buf));
2282 snprintf(buf, sizeof(buf), "%d", y);
2283 geometry->add_property("y-pos", string(buf));
2284 snprintf(buf, sizeof(buf), "%d", xoff);
2285 geometry->add_property("x-off", string(buf));
2286 snprintf(buf, sizeof(buf), "%d", yoff);
2287 geometry->add_property("y-off", string(buf));
2288 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2289 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2290 geometry->add_property("notebook-shrunk", _notebook_shrunk ? "1" : "0");
2291 snprintf(buf,sizeof(buf), "%d",pre_maximal_horizontal_pane_position);
2292 geometry->add_property("pre-maximal-horizontal-pane-position", string(buf));
2293 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2294 geometry->add_property("edit-vertical-pane-pos", string(buf));
2296 node->add_child_nocopy (*geometry);
2299 maybe_add_mixer_strip_width (*node);
2301 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2302 node->add_property ("zoom-focus", buf);
2303 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2304 node->add_property ("zoom", buf);
2305 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2306 node->add_property ("snap-to", buf);
2307 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2308 node->add_property ("snap-mode", buf);
2310 node->add_property ("edit-point", enum_2_string (_edit_point));
2312 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2313 node->add_property ("playhead", buf);
2314 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2315 node->add_property ("left-frame", buf);
2316 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2317 node->add_property ("y-origin", buf);
2319 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2320 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2321 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2322 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2323 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2324 node->add_property ("mouse-mode", enum2str(mouse_mode));
2325 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2326 node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2328 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2330 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2331 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2334 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2336 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2337 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2340 snprintf (buf, sizeof (buf), "%d", _the_notebook.get_current_page ());
2341 node->add_property (X_("editor-list-page"), buf);
2343 node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no");
2345 node->add_child_nocopy (selection->get_state ());
2346 node->add_child_nocopy (_regions->get_state ());
2353 /** @param y y offset from the top of all trackviews.
2354 * @return pair: TimeAxisView that y is over, layer index.
2355 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2356 * in stacked region display mode, otherwise 0.
2358 std::pair<TimeAxisView *, layer_t>
2359 Editor::trackview_by_y_position (double y)
2361 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2363 std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
2369 return std::make_pair ( (TimeAxisView *) 0, 0);
2372 /** Snap a position to the grid, if appropriate, taking into account current
2373 * grid settings and also the state of any snap modifier keys that may be pressed.
2374 * @param start Position to snap.
2375 * @param event Event to get current key modifier information from, or 0.
2378 Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2380 if (!_session || !event) {
2384 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2385 if (_snap_mode == SnapOff) {
2386 snap_to_internal (start, direction, for_mark);
2389 if (_snap_mode != SnapOff) {
2390 snap_to_internal (start, direction, for_mark);
2396 Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark)
2398 if (!_session || _snap_mode == SnapOff) {
2402 snap_to_internal (start, direction, for_mark);
2406 Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /*for_mark*/)
2408 const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2409 framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2411 switch (_snap_type) {
2412 case SnapToTimecodeFrame:
2413 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2414 start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2416 start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2420 case SnapToTimecodeSeconds:
2421 if (_session->config.get_timecode_offset_negative()) {
2422 start += _session->config.get_timecode_offset ();
2424 start -= _session->config.get_timecode_offset ();
2426 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2427 start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2429 start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2432 if (_session->config.get_timecode_offset_negative()) {
2433 start -= _session->config.get_timecode_offset ();
2435 start += _session->config.get_timecode_offset ();
2439 case SnapToTimecodeMinutes:
2440 if (_session->config.get_timecode_offset_negative()) {
2441 start += _session->config.get_timecode_offset ();
2443 start -= _session->config.get_timecode_offset ();
2445 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2446 start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2448 start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2450 if (_session->config.get_timecode_offset_negative()) {
2451 start -= _session->config.get_timecode_offset ();
2453 start += _session->config.get_timecode_offset ();
2457 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2463 Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark)
2465 const framepos_t one_second = _session->frame_rate();
2466 const framepos_t one_minute = _session->frame_rate() * 60;
2467 framepos_t presnap = start;
2471 switch (_snap_type) {
2472 case SnapToTimecodeFrame:
2473 case SnapToTimecodeSeconds:
2474 case SnapToTimecodeMinutes:
2475 return timecode_snap_to_internal (start, direction, for_mark);
2478 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2479 start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2481 start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2486 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2487 start = (framepos_t) ceil ((double) start / one_second) * one_second;
2489 start = (framepos_t) floor ((double) start / one_second) * one_second;
2494 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2495 start = (framepos_t) ceil ((double) start / one_minute) * one_minute;
2497 start = (framepos_t) floor ((double) start / one_minute) * one_minute;
2502 start = _session->tempo_map().round_to_bar (start, direction);
2506 start = _session->tempo_map().round_to_beat (start, direction);
2509 case SnapToBeatDiv32:
2510 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2512 case SnapToBeatDiv28:
2513 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2515 case SnapToBeatDiv24:
2516 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2518 case SnapToBeatDiv20:
2519 start = _session->tempo_map().round_to_beat_subdivision (start, 20, direction);
2521 case SnapToBeatDiv16:
2522 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2524 case SnapToBeatDiv14:
2525 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2527 case SnapToBeatDiv12:
2528 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2530 case SnapToBeatDiv10:
2531 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2533 case SnapToBeatDiv8:
2534 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2536 case SnapToBeatDiv7:
2537 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2539 case SnapToBeatDiv6:
2540 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2542 case SnapToBeatDiv5:
2543 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2545 case SnapToBeatDiv4:
2546 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2548 case SnapToBeatDiv3:
2549 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2551 case SnapToBeatDiv2:
2552 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2560 _session->locations()->marks_either_side (start, before, after);
2562 if (before == max_framepos) {
2564 } else if (after == max_framepos) {
2566 } else if (before != max_framepos && after != max_framepos) {
2567 /* have before and after */
2568 if ((start - before) < (after - start)) {
2577 case SnapToRegionStart:
2578 case SnapToRegionEnd:
2579 case SnapToRegionSync:
2580 case SnapToRegionBoundary:
2581 if (!region_boundary_cache.empty()) {
2583 vector<framepos_t>::iterator prev = region_boundary_cache.end ();
2584 vector<framepos_t>::iterator next = region_boundary_cache.end ();
2586 if (direction > 0) {
2587 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2589 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2592 if (next != region_boundary_cache.begin ()) {
2597 framepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2598 framepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2600 if (start > (p + n) / 2) {
2609 switch (_snap_mode) {
2615 if (presnap > start) {
2616 if (presnap > (start + unit_to_frame(snap_threshold))) {
2620 } else if (presnap < start) {
2621 if (presnap < (start - unit_to_frame(snap_threshold))) {
2627 /* handled at entry */
2635 Editor::setup_toolbar ()
2639 /* Mode Buttons (tool selection) */
2641 mouse_move_button.set_relief(Gtk::RELIEF_NONE);
2642 mouse_select_button.set_relief(Gtk::RELIEF_NONE);
2643 mouse_gain_button.set_relief(Gtk::RELIEF_NONE);
2644 mouse_zoom_button.set_relief(Gtk::RELIEF_NONE);
2645 mouse_timefx_button.set_relief(Gtk::RELIEF_NONE);
2646 mouse_audition_button.set_relief(Gtk::RELIEF_NONE);
2647 // internal_edit_button.set_relief(Gtk::RELIEF_NONE);
2648 join_object_range_button.set_relief(Gtk::RELIEF_NONE);
2650 HBox* mode_box = manage(new HBox);
2651 mode_box->set_border_width (2);
2652 mode_box->set_spacing(4);
2654 /* table containing mode buttons */
2656 HBox* mouse_mode_button_box = manage (new HBox ());
2658 if (Profile->get_sae()) {
2659 mouse_mode_button_box->pack_start (mouse_move_button);
2661 mouse_mode_button_box->pack_start (mouse_move_button);
2662 mouse_mode_button_box->pack_start (join_object_range_button);
2663 mouse_mode_button_box->pack_start (mouse_select_button);
2666 mouse_mode_button_box->pack_start (mouse_zoom_button);
2668 if (!Profile->get_sae()) {
2669 mouse_mode_button_box->pack_start (mouse_gain_button);
2672 mouse_mode_button_box->pack_start (mouse_timefx_button);
2673 mouse_mode_button_box->pack_start (mouse_audition_button);
2674 mouse_mode_button_box->pack_start (internal_edit_button);
2676 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2677 if (!Profile->get_sae()) {
2678 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2680 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2682 edit_mode_selector.set_name ("EditModeSelector");
2683 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2684 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2686 mode_box->pack_start (edit_mode_selector);
2687 mode_box->pack_start (*mouse_mode_button_box);
2689 _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2690 _mouse_mode_tearoff->set_name ("MouseModeBase");
2691 _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2693 if (Profile->get_sae()) {
2694 _mouse_mode_tearoff->set_can_be_torn_off (false);
2697 _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2698 &_mouse_mode_tearoff->tearoff_window()));
2699 _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2700 &_mouse_mode_tearoff->tearoff_window(), 1));
2701 _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2702 &_mouse_mode_tearoff->tearoff_window()));
2703 _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2704 &_mouse_mode_tearoff->tearoff_window(), 1));
2706 mouse_move_button.set_mode (false);
2707 mouse_select_button.set_mode (false);
2708 mouse_gain_button.set_mode (false);
2709 mouse_zoom_button.set_mode (false);
2710 mouse_timefx_button.set_mode (false);
2711 mouse_audition_button.set_mode (false);
2712 join_object_range_button.set_mode (false);
2714 mouse_move_button.set_name ("MouseModeButton");
2715 mouse_select_button.set_name ("MouseModeButton");
2716 mouse_gain_button.set_name ("MouseModeButton");
2717 mouse_zoom_button.set_name ("MouseModeButton");
2718 mouse_timefx_button.set_name ("MouseModeButton");
2719 mouse_audition_button.set_name ("MouseModeButton");
2720 internal_edit_button.set_name ("MouseModeButton");
2721 join_object_range_button.set_name ("MouseModeButton");
2723 mouse_move_button.unset_flags (CAN_FOCUS);
2724 mouse_select_button.unset_flags (CAN_FOCUS);
2725 mouse_gain_button.unset_flags (CAN_FOCUS);
2726 mouse_zoom_button.unset_flags (CAN_FOCUS);
2727 mouse_timefx_button.unset_flags (CAN_FOCUS);
2728 mouse_audition_button.unset_flags (CAN_FOCUS);
2729 internal_edit_button.unset_flags (CAN_FOCUS);
2730 join_object_range_button.unset_flags (CAN_FOCUS);
2734 _zoom_box.set_spacing (1);
2735 _zoom_box.set_border_width (0);
2737 zoom_in_button.set_name ("EditorTimeButton");
2738 zoom_in_button.set_image (*(manage (new Image (Stock::ZOOM_IN, Gtk::ICON_SIZE_MENU))));
2739 zoom_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), false));
2741 zoom_out_button.set_name ("EditorTimeButton");
2742 zoom_out_button.set_image (*(manage (new Image (Stock::ZOOM_OUT, Gtk::ICON_SIZE_MENU))));
2743 zoom_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), true));
2745 zoom_out_full_button.set_name ("EditorTimeButton");
2746 zoom_out_full_button.set_image (*(manage (new Image (Stock::ZOOM_100, Gtk::ICON_SIZE_MENU))));
2747 zoom_out_full_button.signal_clicked().connect (sigc::mem_fun(*this, &Editor::temporal_zoom_session));
2749 zoom_focus_selector.set_name ("ZoomFocusSelector");
2750 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
2751 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2753 _zoom_box.pack_start (zoom_out_button, false, false);
2754 _zoom_box.pack_start (zoom_in_button, false, false);
2755 _zoom_box.pack_start (zoom_out_full_button, false, false);
2757 _zoom_box.pack_start (zoom_focus_selector);
2759 /* Track zoom buttons */
2760 tav_expand_button.set_name ("TrackHeightButton");
2761 tav_expand_button.set_size_request(-1,20);
2762 tav_expand_button.add (*(manage (new Image (::get_icon("tav_exp")))));
2763 tav_expand_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), true));
2765 tav_shrink_button.set_name ("TrackHeightButton");
2766 tav_shrink_button.set_size_request(-1,20);
2767 tav_shrink_button.add (*(manage (new Image (::get_icon("tav_shrink")))));
2768 tav_shrink_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), false));
2770 _zoom_box.pack_start (tav_shrink_button);
2771 _zoom_box.pack_start (tav_expand_button);
2773 _zoom_tearoff = manage (new TearOff (_zoom_box));
2775 _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2776 &_zoom_tearoff->tearoff_window()));
2777 _zoom_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2778 &_zoom_tearoff->tearoff_window(), 0));
2779 _zoom_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2780 &_zoom_tearoff->tearoff_window()));
2781 _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2782 &_zoom_tearoff->tearoff_window(), 0));
2784 snap_box.set_spacing (1);
2785 snap_box.set_border_width (2);
2787 snap_type_selector.set_name ("SnapTypeSelector");
2788 set_popdown_strings (snap_type_selector, snap_type_strings, true);
2789 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2791 snap_mode_selector.set_name ("SnapModeSelector");
2792 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
2793 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2795 edit_point_selector.set_name ("EditPointSelector");
2796 set_popdown_strings (edit_point_selector, edit_point_strings, true);
2797 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2799 snap_box.pack_start (snap_mode_selector, false, false);
2800 snap_box.pack_start (snap_type_selector, false, false);
2801 snap_box.pack_start (edit_point_selector, false, false);
2805 HBox *nudge_box = manage (new HBox);
2806 nudge_box->set_spacing(1);
2807 nudge_box->set_border_width (2);
2809 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2810 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2812 nudge_box->pack_start (nudge_backward_button, false, false);
2813 nudge_box->pack_start (nudge_forward_button, false, false);
2814 nudge_box->pack_start (nudge_clock, false, false);
2817 /* Pack everything in... */
2819 HBox* hbox = manage (new HBox);
2820 hbox->set_spacing(10);
2822 _tools_tearoff = manage (new TearOff (*hbox));
2823 _tools_tearoff->set_name ("MouseModeBase");
2824 _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
2826 if (Profile->get_sae()) {
2827 _tools_tearoff->set_can_be_torn_off (false);
2830 _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2831 &_tools_tearoff->tearoff_window()));
2832 _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2833 &_tools_tearoff->tearoff_window(), 0));
2834 _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2835 &_tools_tearoff->tearoff_window()));
2836 _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2837 &_tools_tearoff->tearoff_window(), 0));
2839 toolbar_hbox.set_spacing (10);
2840 toolbar_hbox.set_border_width (1);
2842 toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
2843 toolbar_hbox.pack_start (*_zoom_tearoff, false, false);
2844 toolbar_hbox.pack_start (*_tools_tearoff, false, false);
2846 hbox->pack_start (snap_box, false, false);
2847 hbox->pack_start (*nudge_box, false, false);
2848 hbox->pack_start (panic_box, false, false);
2852 toolbar_base.set_name ("ToolBarBase");
2853 toolbar_base.add (toolbar_hbox);
2855 _toolbar_viewport.add (toolbar_base);
2856 /* stick to the required height but allow width to vary if there's not enough room */
2857 _toolbar_viewport.set_size_request (1, -1);
2859 toolbar_frame.set_shadow_type (SHADOW_OUT);
2860 toolbar_frame.set_name ("BaseFrame");
2861 toolbar_frame.add (_toolbar_viewport);
2863 DPIReset.connect (sigc::mem_fun (*this, &Editor::resize_text_widgets));
2867 Editor::setup_tooltips ()
2869 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
2870 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain"));
2871 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
2872 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
2873 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2874 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
2875 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
2876 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
2877 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
2878 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
2879 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
2880 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
2881 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
2882 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
2883 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
2884 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
2885 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
2886 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
2887 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
2888 ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
2889 ARDOUR_UI::instance()->set_tip (midi_panic_button, _("Send note off and reset controller messages on all MIDI channels"));
2890 ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode"));
2894 Editor::midi_panic ()
2896 cerr << "MIDI panic\n";
2899 _session->midi_panic();
2904 Editor::setup_midi_toolbar ()
2908 /* Midi sound notes */
2909 midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
2910 midi_sound_notes.set_relief(Gtk::RELIEF_NONE);
2911 midi_sound_notes.unset_flags (CAN_FOCUS);
2915 act = ActionManager::get_action (X_("MIDI"), X_("panic"));
2916 midi_panic_button.set_name("MidiPanicButton");
2917 act->connect_proxy (midi_panic_button);
2919 panic_box.pack_start (midi_sound_notes , true, true);
2920 panic_box.pack_start (midi_panic_button, true, true);
2924 Editor::convert_drop_to_paths (
2925 vector<string>& paths,
2926 const RefPtr<Gdk::DragContext>& /*context*/,
2929 const SelectionData& data,
2933 if (_session == 0) {
2937 vector<string> uris = data.get_uris();
2941 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
2942 are actually URI lists. So do it by hand.
2945 if (data.get_target() != "text/plain") {
2949 /* Parse the "uri-list" format that Nautilus provides,
2950 where each pathname is delimited by \r\n.
2952 THERE MAY BE NO NULL TERMINATING CHAR!!!
2955 string txt = data.get_text();
2959 p = (const char *) malloc (txt.length() + 1);
2960 txt.copy ((char *) p, txt.length(), 0);
2961 ((char*)p)[txt.length()] = '\0';
2967 while (g_ascii_isspace (*p))
2971 while (*q && (*q != '\n') && (*q != '\r')) {
2978 while (q > p && g_ascii_isspace (*q))
2983 uris.push_back (string (p, q - p + 1));
2987 p = strchr (p, '\n');
2999 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3001 if ((*i).substr (0,7) == "file://") {
3004 PBD::url_decode (p);
3006 // scan forward past three slashes
3008 string::size_type slashcnt = 0;
3009 string::size_type n = 0;
3010 string::iterator x = p.begin();
3012 while (slashcnt < 3 && x != p.end()) {
3015 } else if (slashcnt == 3) {
3022 if (slashcnt != 3 || x == p.end()) {
3023 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3027 paths.push_back (p.substr (n - 1));
3035 Editor::new_tempo_section ()
3041 Editor::map_transport_state ()
3043 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3045 if (_session && _session->transport_stopped()) {
3046 have_pending_keyboard_selection = false;
3049 update_loop_range_view (true);
3054 Editor::State::State (PublicEditor const * e)
3056 selection = new Selection (e);
3059 Editor::State::~State ()
3065 Editor::begin_reversible_command (string name)
3068 _session->begin_reversible_command (name);
3073 Editor::commit_reversible_command ()
3076 _session->commit_reversible_command ();
3081 Editor::history_changed ()
3085 if (undo_action && _session) {
3086 if (_session->undo_depth() == 0) {
3089 label = string_compose(_("Undo (%1)"), _session->next_undo());
3091 undo_action->property_label() = label;
3094 if (redo_action && _session) {
3095 if (_session->redo_depth() == 0) {
3098 label = string_compose(_("Redo (%1)"), _session->next_redo());
3100 redo_action->property_label() = label;
3105 Editor::duplicate_dialog (bool with_dialog)
3109 if (mouse_mode == MouseRange) {
3110 if (selection->time.length() == 0) {
3115 RegionSelection rs = get_regions_from_selection_and_entered ();
3117 if (mouse_mode != MouseRange && rs.empty()) {
3123 ArdourDialog win (_("Duplicate"));
3124 Label label (_("Number of duplications:"));
3125 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3126 SpinButton spinner (adjustment, 0.0, 1);
3129 win.get_vbox()->set_spacing (12);
3130 win.get_vbox()->pack_start (hbox);
3131 hbox.set_border_width (6);
3132 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3134 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3135 place, visually. so do this by hand.
3138 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3139 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3140 spinner.grab_focus();
3146 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3147 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3148 win.set_default_response (RESPONSE_ACCEPT);
3150 win.set_position (WIN_POS_MOUSE);
3152 spinner.grab_focus ();
3154 switch (win.run ()) {
3155 case RESPONSE_ACCEPT:
3161 times = adjustment.get_value();
3164 if (mouse_mode == MouseRange) {
3165 duplicate_selection (times);
3167 duplicate_some_regions (rs, times);
3172 Editor::show_verbose_canvas_cursor ()
3174 verbose_canvas_cursor->raise_to_top();
3175 verbose_canvas_cursor->show();
3176 verbose_cursor_visible = true;
3180 Editor::hide_verbose_canvas_cursor ()
3182 verbose_canvas_cursor->hide();
3183 verbose_cursor_visible = false;
3187 Editor::clamp_verbose_cursor_x (double x)
3192 x = min (_canvas_width - 200.0, x);
3198 Editor::clamp_verbose_cursor_y (double y)
3200 if (y < canvas_timebars_vsize) {
3201 y = canvas_timebars_vsize;
3203 y = min (_canvas_height - 50, y);
3209 Editor::show_verbose_canvas_cursor_with (const string & txt, int32_t xoffset, int32_t yoffset)
3211 verbose_canvas_cursor->property_text() = txt.c_str();
3216 track_canvas->get_pointer (x, y);
3217 track_canvas->window_to_world (x, y, wx, wy);
3222 /* don't get too close to the edge */
3223 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (wx);
3224 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (wy);
3226 show_verbose_canvas_cursor ();
3230 Editor::set_verbose_canvas_cursor (const string & txt, double x, double y)
3232 verbose_canvas_cursor->property_text() = txt.c_str();
3233 /* don't get too close to the edge */
3234 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (x);
3235 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (y);
3239 Editor::set_verbose_canvas_cursor_text (const string & txt)
3241 verbose_canvas_cursor->property_text() = txt.c_str();
3245 Editor::set_edit_mode (EditMode m)
3247 Config->set_edit_mode (m);
3251 Editor::cycle_edit_mode ()
3253 switch (Config->get_edit_mode()) {
3255 if (Profile->get_sae()) {
3256 Config->set_edit_mode (Lock);
3258 Config->set_edit_mode (Splice);
3262 Config->set_edit_mode (Lock);
3265 Config->set_edit_mode (Slide);
3271 Editor::edit_mode_selection_done ()
3273 string s = edit_mode_selector.get_active_text ();
3276 Config->set_edit_mode (string_to_edit_mode (s));
3281 Editor::snap_type_selection_done ()
3283 string choice = snap_type_selector.get_active_text();
3284 SnapType snaptype = SnapToBeat;
3286 if (choice == _("Beats/2")) {
3287 snaptype = SnapToBeatDiv2;
3288 } else if (choice == _("Beats/3")) {
3289 snaptype = SnapToBeatDiv3;
3290 } else if (choice == _("Beats/4")) {
3291 snaptype = SnapToBeatDiv4;
3292 } else if (choice == _("Beats/5")) {
3293 snaptype = SnapToBeatDiv5;
3294 } else if (choice == _("Beats/6")) {
3295 snaptype = SnapToBeatDiv6;
3296 } else if (choice == _("Beats/7")) {
3297 snaptype = SnapToBeatDiv7;
3298 } else if (choice == _("Beats/8")) {
3299 snaptype = SnapToBeatDiv8;
3300 } else if (choice == _("Beats/10")) {
3301 snaptype = SnapToBeatDiv10;
3302 } else if (choice == _("Beats/12")) {
3303 snaptype = SnapToBeatDiv12;
3304 } else if (choice == _("Beats/14")) {
3305 snaptype = SnapToBeatDiv14;
3306 } else if (choice == _("Beats/16")) {
3307 snaptype = SnapToBeatDiv16;
3308 } else if (choice == _("Beats/20")) {
3309 snaptype = SnapToBeatDiv20;
3310 } else if (choice == _("Beats/24")) {
3311 snaptype = SnapToBeatDiv24;
3312 } else if (choice == _("Beats/28")) {
3313 snaptype = SnapToBeatDiv28;
3314 } else if (choice == _("Beats/32")) {
3315 snaptype = SnapToBeatDiv32;
3316 } else if (choice == _("Beats")) {
3317 snaptype = SnapToBeat;
3318 } else if (choice == _("Bars")) {
3319 snaptype = SnapToBar;
3320 } else if (choice == _("Marks")) {
3321 snaptype = SnapToMark;
3322 } else if (choice == _("Region starts")) {
3323 snaptype = SnapToRegionStart;
3324 } else if (choice == _("Region ends")) {
3325 snaptype = SnapToRegionEnd;
3326 } else if (choice == _("Region bounds")) {
3327 snaptype = SnapToRegionBoundary;
3328 } else if (choice == _("Region syncs")) {
3329 snaptype = SnapToRegionSync;
3330 } else if (choice == _("CD Frames")) {
3331 snaptype = SnapToCDFrame;
3332 } else if (choice == _("Timecode Frames")) {
3333 snaptype = SnapToTimecodeFrame;
3334 } else if (choice == _("Timecode Seconds")) {
3335 snaptype = SnapToTimecodeSeconds;
3336 } else if (choice == _("Timecode Minutes")) {
3337 snaptype = SnapToTimecodeMinutes;
3338 } else if (choice == _("Seconds")) {
3339 snaptype = SnapToSeconds;
3340 } else if (choice == _("Minutes")) {
3341 snaptype = SnapToMinutes;
3344 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3346 ract->set_active ();
3351 Editor::snap_mode_selection_done ()
3353 string choice = snap_mode_selector.get_active_text();
3354 SnapMode mode = SnapNormal;
3356 if (choice == _("No Grid")) {
3358 } else if (choice == _("Grid")) {
3360 } else if (choice == _("Magnetic")) {
3361 mode = SnapMagnetic;
3364 RefPtr<RadioAction> ract = snap_mode_action (mode);
3367 ract->set_active (true);
3372 Editor::cycle_edit_point (bool with_marker)
3374 switch (_edit_point) {
3376 set_edit_point_preference (EditAtPlayhead);
3378 case EditAtPlayhead:
3380 set_edit_point_preference (EditAtSelectedMarker);
3382 set_edit_point_preference (EditAtMouse);
3385 case EditAtSelectedMarker:
3386 set_edit_point_preference (EditAtMouse);
3392 Editor::edit_point_selection_done ()
3394 string choice = edit_point_selector.get_active_text();
3395 EditPoint ep = EditAtSelectedMarker;
3397 if (choice == _("Marker")) {
3398 set_edit_point_preference (EditAtSelectedMarker);
3399 } else if (choice == _("Playhead")) {
3400 set_edit_point_preference (EditAtPlayhead);
3402 set_edit_point_preference (EditAtMouse);
3405 RefPtr<RadioAction> ract = edit_point_action (ep);
3408 ract->set_active (true);
3413 Editor::zoom_focus_selection_done ()
3415 string choice = zoom_focus_selector.get_active_text();
3416 ZoomFocus focus_type = ZoomFocusLeft;
3418 if (choice == _("Left")) {
3419 focus_type = ZoomFocusLeft;
3420 } else if (choice == _("Right")) {
3421 focus_type = ZoomFocusRight;
3422 } else if (choice == _("Center")) {
3423 focus_type = ZoomFocusCenter;
3424 } else if (choice == _("Playhead")) {
3425 focus_type = ZoomFocusPlayhead;
3426 } else if (choice == _("Mouse")) {
3427 focus_type = ZoomFocusMouse;
3428 } else if (choice == _("Edit point")) {
3429 focus_type = ZoomFocusEdit;
3432 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3435 ract->set_active ();
3440 Editor::edit_controls_button_release (GdkEventButton* ev)
3442 if (Keyboard::is_context_menu_event (ev)) {
3443 ARDOUR_UI::instance()->add_route (this);
3449 Editor::mouse_select_button_release (GdkEventButton* ev)
3451 /* this handles just right-clicks */
3453 if (ev->button != 3) {
3461 Editor::set_zoom_focus (ZoomFocus f)
3463 string str = zoom_focus_strings[(int)f];
3465 if (str != zoom_focus_selector.get_active_text()) {
3466 zoom_focus_selector.set_active_text (str);
3469 if (zoom_focus != f) {
3472 ZoomFocusChanged (); /* EMIT_SIGNAL */
3479 Editor::ensure_float (Window& win)
3481 win.set_transient_for (*this);
3485 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3487 /* recover or initialize pane positions. do this here rather than earlier because
3488 we don't want the positions to change the child allocations, which they seem to do.
3494 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3506 width = default_width;
3507 height = default_height;
3509 if ((geometry = find_named_node (*node, "geometry")) != 0) {
3511 prop = geometry->property ("x-size");
3513 width = atoi (prop->value());
3515 prop = geometry->property ("y-size");
3517 height = atoi (prop->value());
3521 if (which == static_cast<Paned*> (&edit_pane)) {
3523 if (done & Horizontal) {
3527 if (geometry && (prop = geometry->property ("notebook-shrunk"))) {
3528 _notebook_shrunk = string_is_affirmative (prop->value ());
3531 if (geometry && (prop = geometry->property ("pre-maximal-horizontal-pane-position"))) {
3532 pre_maximal_horizontal_pane_position = atoi (prop->value ());
3535 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3536 /* initial allocation is 90% to canvas, 10% to notebook */
3537 pos = (int) floor (alloc.get_width() * 0.90f);
3538 snprintf (buf, sizeof(buf), "%d", pos);
3540 pos = atoi (prop->value());
3543 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3544 edit_pane.set_position (pos);
3545 if (pre_maximal_horizontal_pane_position == 0) {
3546 pre_maximal_horizontal_pane_position = pos;
3550 done = (Pane) (done | Horizontal);
3552 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3554 if (done & Vertical) {
3558 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3559 /* initial allocation is 90% to canvas, 10% to summary */
3560 pos = (int) floor (alloc.get_height() * 0.90f);
3561 snprintf (buf, sizeof(buf), "%d", pos);
3563 pos = atoi (prop->value());
3566 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3567 editor_summary_pane.set_position (pos);
3568 pre_maximal_vertical_pane_position = pos;
3571 done = (Pane) (done | Vertical);
3576 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3578 if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) {
3579 top_hbox.remove (toolbar_frame);
3584 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3586 if (toolbar_frame.get_parent() == 0) {
3587 top_hbox.pack_end (toolbar_frame);
3592 Editor::set_show_measures (bool yn)
3594 if (_show_measures != yn) {
3597 if ((_show_measures = yn) == true) {
3599 tempo_lines->show();
3607 Editor::toggle_follow_playhead ()
3609 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3611 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3612 set_follow_playhead (tact->get_active());
3616 /** @param yn true to follow playhead, otherwise false.
3617 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
3620 Editor::set_follow_playhead (bool yn, bool catch_up)
3622 if (_follow_playhead != yn) {
3623 if ((_follow_playhead = yn) == true && catch_up) {
3625 reset_x_origin_to_follow_playhead ();
3632 Editor::toggle_stationary_playhead ()
3634 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3636 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3637 set_stationary_playhead (tact->get_active());
3642 Editor::set_stationary_playhead (bool yn)
3644 if (_stationary_playhead != yn) {
3645 if ((_stationary_playhead = yn) == true) {
3647 // FIXME need a 3.0 equivalent of this 2.X call
3648 // update_current_screen ();
3655 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3657 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3659 xfade->set_active (!xfade->active());
3664 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3666 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3668 xfade->set_follow_overlap (!xfade->following_overlap());
3673 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3675 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3681 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3685 switch (cew.run ()) {
3686 case RESPONSE_ACCEPT:
3693 PropertyChange all_crossfade_properties;
3694 all_crossfade_properties.add (ARDOUR::Properties::active);
3695 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3696 xfade->PropertyChanged (all_crossfade_properties);
3700 Editor::playlist_selector () const
3702 return *_playlist_selector;
3706 Editor::get_grid_type_as_beats (bool& success, framepos_t position)
3710 switch (_snap_type) {
3715 case SnapToBeatDiv32:
3718 case SnapToBeatDiv28:
3721 case SnapToBeatDiv24:
3724 case SnapToBeatDiv20:
3727 case SnapToBeatDiv16:
3730 case SnapToBeatDiv14:
3733 case SnapToBeatDiv12:
3736 case SnapToBeatDiv10:
3739 case SnapToBeatDiv8:
3742 case SnapToBeatDiv7:
3745 case SnapToBeatDiv6:
3748 case SnapToBeatDiv5:
3751 case SnapToBeatDiv4:
3754 case SnapToBeatDiv3:
3757 case SnapToBeatDiv2:
3763 return _session->tempo_map().meter_at (position).beats_per_bar();
3768 case SnapToTimecodeFrame:
3769 case SnapToTimecodeSeconds:
3770 case SnapToTimecodeMinutes:
3773 case SnapToRegionStart:
3774 case SnapToRegionEnd:
3775 case SnapToRegionSync:
3776 case SnapToRegionBoundary:
3786 Editor::get_nudge_distance (framepos_t pos, framecnt_t& next)
3790 ret = nudge_clock.current_duration (pos);
3791 next = ret + 1; /* XXXX fix me */
3797 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3799 ArdourDialog dialog (_("Playlist Deletion"));
3800 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3801 "If left alone, no audio files used by it will be cleaned.\n"
3802 "If deleted, audio files used by it alone by will cleaned."),
3805 dialog.set_position (WIN_POS_CENTER);
3806 dialog.get_vbox()->pack_start (label);
3810 dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT);
3811 dialog.add_button (_("Keep playlist"), RESPONSE_REJECT);
3812 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3814 switch (dialog.run ()) {
3815 case RESPONSE_ACCEPT:
3816 /* delete the playlist */
3820 case RESPONSE_REJECT:
3821 /* keep the playlist */
3833 Editor::audio_region_selection_covers (framepos_t where)
3835 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3836 if ((*a)->region()->covers (where)) {
3845 Editor::prepare_for_cleanup ()
3847 cut_buffer->clear_regions ();
3848 cut_buffer->clear_playlists ();
3850 selection->clear_regions ();
3851 selection->clear_playlists ();
3853 _regions->suspend_redisplay ();
3857 Editor::finish_cleanup ()
3859 _regions->resume_redisplay ();
3863 Editor::transport_loop_location()
3866 return _session->locations()->auto_loop_location();
3873 Editor::transport_punch_location()
3876 return _session->locations()->auto_punch_location();
3883 Editor::control_layout_scroll (GdkEventScroll* ev)
3885 if (Keyboard::some_magic_widget_has_focus()) {
3889 switch (ev->direction) {
3891 scroll_tracks_up_line ();
3895 case GDK_SCROLL_DOWN:
3896 scroll_tracks_down_line ();
3900 /* no left/right handling yet */
3908 Editor::session_state_saved (string)
3911 _snapshots->redisplay ();
3915 Editor::maximise_editing_space ()
3917 _mouse_mode_tearoff->set_visible (false);
3918 _tools_tearoff->set_visible (false);
3919 _zoom_tearoff->set_visible (false);
3921 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
3922 pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
3923 pre_maximal_editor_width = this->get_width ();
3924 pre_maximal_editor_height = this->get_height ();
3926 if (post_maximal_horizontal_pane_position == 0) {
3927 post_maximal_horizontal_pane_position = edit_pane.get_width();
3930 if (post_maximal_vertical_pane_position == 0) {
3931 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
3936 if (post_maximal_editor_width) {
3937 edit_pane.set_position (post_maximal_horizontal_pane_position -
3938 abs(post_maximal_editor_width - pre_maximal_editor_width));
3940 edit_pane.set_position (post_maximal_horizontal_pane_position);
3943 if (post_maximal_editor_height) {
3944 editor_summary_pane.set_position (post_maximal_vertical_pane_position -
3945 abs(post_maximal_editor_height - pre_maximal_editor_height));
3947 editor_summary_pane.set_position (post_maximal_vertical_pane_position);
3950 if (Config->get_keep_tearoffs()) {
3951 _mouse_mode_tearoff->set_visible (true);
3952 _tools_tearoff->set_visible (true);
3953 if (Config->get_show_zoom_tools ()) {
3954 _zoom_tearoff->set_visible (true);
3961 Editor::restore_editing_space ()
3963 // user changed width/height of panes during fullscreen
3965 if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
3966 post_maximal_horizontal_pane_position = edit_pane.get_position();
3969 if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
3970 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
3975 _mouse_mode_tearoff->set_visible (true);
3976 _tools_tearoff->set_visible (true);
3977 if (Config->get_show_zoom_tools ()) {
3978 _zoom_tearoff->set_visible (true);
3980 post_maximal_editor_width = this->get_width();
3981 post_maximal_editor_height = this->get_height();
3983 edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
3984 editor_summary_pane.set_position (pre_maximal_vertical_pane_position + abs(this->get_height() - pre_maximal_editor_height));
3988 * Make new playlists for a given track and also any others that belong
3989 * to the same active route group with the `edit' property.
3994 Editor::new_playlists (TimeAxisView* v)
3996 begin_reversible_command (_("new playlists"));
3997 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
3998 _session->playlists->get (playlists);
3999 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4000 commit_reversible_command ();
4004 * Use a copy of the current playlist for a given track and also any others that belong
4005 * to the same active route group with the `edit' property.
4010 Editor::copy_playlists (TimeAxisView* v)
4012 begin_reversible_command (_("copy playlists"));
4013 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4014 _session->playlists->get (playlists);
4015 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4016 commit_reversible_command ();
4019 /** Clear the current playlist for a given track and also any others that belong
4020 * to the same active route group with the `edit' property.
4025 Editor::clear_playlists (TimeAxisView* v)
4027 begin_reversible_command (_("clear playlists"));
4028 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4029 _session->playlists->get (playlists);
4030 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4031 commit_reversible_command ();
4035 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4037 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4041 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4043 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4047 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4049 atv.clear_playlist ();
4053 Editor::on_key_press_event (GdkEventKey* ev)
4055 return key_press_focus_accelerator_handler (*this, ev);
4059 Editor::on_key_release_event (GdkEventKey* ev)
4061 return Gtk::Window::on_key_release_event (ev);
4062 // return key_press_focus_accelerator_handler (*this, ev);
4065 /** Queue up a change to the viewport x origin.
4066 * @param frame New x origin.
4069 Editor::reset_x_origin (framepos_t frame)
4071 queue_visual_change (frame);
4075 Editor::reset_y_origin (double y)
4077 queue_visual_change_y (y);
4081 Editor::reset_zoom (double fpu)
4083 queue_visual_change (fpu);
4087 Editor::reposition_and_zoom (framepos_t frame, double fpu)
4089 reset_x_origin (frame);
4092 if (!no_save_visual) {
4093 undo_visual_stack.push_back (current_visual_state(false));
4097 Editor::VisualState*
4098 Editor::current_visual_state (bool with_tracks)
4100 VisualState* vs = new VisualState;
4101 vs->y_position = vertical_adjustment.get_value();
4102 vs->frames_per_unit = frames_per_unit;
4103 vs->leftmost_frame = leftmost_frame;
4104 vs->zoom_focus = zoom_focus;
4107 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4108 vs->track_states.push_back (TAVState ((*i), &(*i)->get_state()));
4116 Editor::undo_visual_state ()
4118 if (undo_visual_stack.empty()) {
4122 redo_visual_stack.push_back (current_visual_state());
4124 VisualState* vs = undo_visual_stack.back();
4125 undo_visual_stack.pop_back();
4126 use_visual_state (*vs);
4130 Editor::redo_visual_state ()
4132 if (redo_visual_stack.empty()) {
4136 undo_visual_stack.push_back (current_visual_state());
4138 VisualState* vs = redo_visual_stack.back();
4139 redo_visual_stack.pop_back();
4140 use_visual_state (*vs);
4144 Editor::swap_visual_state ()
4146 if (undo_visual_stack.empty()) {
4147 redo_visual_state ();
4149 undo_visual_state ();
4154 Editor::use_visual_state (VisualState& vs)
4156 no_save_visual = true;
4158 _routes->suspend_redisplay ();
4160 vertical_adjustment.set_value (vs.y_position);
4162 set_zoom_focus (vs.zoom_focus);
4163 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4165 for (list<TAVState>::iterator i = vs.track_states.begin(); i != vs.track_states.end(); ++i) {
4166 TrackViewList::iterator t;
4168 /* check if the track still exists - it could have been deleted */
4170 if ((t = find (track_views.begin(), track_views.end(), i->first)) != track_views.end()) {
4171 (*t)->set_state (*(i->second), Stateful::loading_state_version);
4176 if (!vs.track_states.empty()) {
4177 _routes->update_visibility ();
4180 _routes->resume_redisplay ();
4182 no_save_visual = false;
4186 Editor::set_frames_per_unit (double fpu)
4188 /* this is the core function that controls the zoom level of the canvas. it is called
4189 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4192 if (fpu == frames_per_unit) {
4201 /* don't allow zooms that fit more than the maximum number
4202 of frames into an 800 pixel wide space.
4205 if (max_framepos / fpu < 800.0) {
4210 tempo_lines->tempo_map_changed();
4212 frames_per_unit = fpu;
4217 Editor::post_zoom ()
4219 // convert fpu to frame count
4221 framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width);
4223 if (frames_per_unit != zoom_range_clock.current_duration()) {
4224 zoom_range_clock.set (frames);
4227 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4228 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4229 (*i)->reshow_selection (selection->time);
4233 ZoomChanged (); /* EMIT_SIGNAL */
4235 //reset_scrolling_region ();
4237 if (playhead_cursor) {
4238 playhead_cursor->set_position (playhead_cursor->current_frame);
4241 refresh_location_display();
4242 _summary->set_overlays_dirty ();
4244 update_marker_labels ();
4250 Editor::queue_visual_change (framepos_t where)
4252 pending_visual_change.add (VisualChange::TimeOrigin);
4253 pending_visual_change.time_origin = where;
4254 ensure_visual_change_idle_handler ();
4258 Editor::queue_visual_change (double fpu)
4260 pending_visual_change.add (VisualChange::ZoomLevel);
4261 pending_visual_change.frames_per_unit = fpu;
4263 ensure_visual_change_idle_handler ();
4267 Editor::queue_visual_change_y (double y)
4269 pending_visual_change.add (VisualChange::YOrigin);
4270 pending_visual_change.y_origin = y;
4272 ensure_visual_change_idle_handler ();
4276 Editor::ensure_visual_change_idle_handler ()
4278 if (pending_visual_change.idle_handler_id < 0) {
4279 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4284 Editor::_idle_visual_changer (void* arg)
4286 return static_cast<Editor*>(arg)->idle_visual_changer ();
4290 Editor::idle_visual_changer ()
4292 VisualChange::Type p = pending_visual_change.pending;
4293 pending_visual_change.pending = (VisualChange::Type) 0;
4295 double const last_time_origin = horizontal_position ();
4297 if (p & VisualChange::TimeOrigin) {
4298 /* This is a bit of a hack, but set_frames_per_unit
4299 below will (if called) end up with the
4300 CrossfadeViews looking at Editor::leftmost_frame,
4301 and if we're changing origin and zoom in the same
4302 operation it will be the wrong value unless we
4306 leftmost_frame = pending_visual_change.time_origin;
4309 if (p & VisualChange::ZoomLevel) {
4310 set_frames_per_unit (pending_visual_change.frames_per_unit);
4312 compute_fixed_ruler_scale ();
4313 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4314 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4315 update_tempo_based_rulers ();
4317 if (p & VisualChange::TimeOrigin) {
4318 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4320 if (p & VisualChange::YOrigin) {
4321 vertical_adjustment.set_value (pending_visual_change.y_origin);
4324 if (last_time_origin == horizontal_position ()) {
4325 /* changed signal not emitted */
4326 update_fixed_rulers ();
4327 redisplay_tempo (true);
4330 _summary->set_overlays_dirty ();
4332 pending_visual_change.idle_handler_id = -1;
4333 return 0; /* this is always a one-shot call */
4336 struct EditorOrderTimeAxisSorter {
4337 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4338 return a->order () < b->order ();
4343 Editor::sort_track_selection (TrackViewList* sel)
4345 EditorOrderTimeAxisSorter cmp;
4350 selection->tracks.sort (cmp);
4355 Editor::get_preferred_edit_position (bool ignore_playhead)
4358 framepos_t where = 0;
4359 EditPoint ep = _edit_point;
4361 if (entered_marker) {
4362 return entered_marker->position();
4365 if (ignore_playhead && ep == EditAtPlayhead) {
4366 ep = EditAtSelectedMarker;
4370 case EditAtPlayhead:
4371 where = _session->audible_frame();
4374 case EditAtSelectedMarker:
4375 if (!selection->markers.empty()) {
4377 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4380 where = loc->start();
4391 if (!mouse_frame (where, ignored)) {
4392 /* XXX not right but what can we do ? */
4403 Editor::set_loop_range (framepos_t start, framepos_t end, string cmd)
4405 if (!_session) return;
4407 begin_reversible_command (cmd);
4411 if ((tll = transport_loop_location()) == 0) {
4412 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop);
4413 XMLNode &before = _session->locations()->get_state();
4414 _session->locations()->add (loc, true);
4415 _session->set_auto_loop_location (loc);
4416 XMLNode &after = _session->locations()->get_state();
4417 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4419 XMLNode &before = tll->get_state();
4420 tll->set_hidden (false, this);
4421 tll->set (start, end);
4422 XMLNode &after = tll->get_state();
4423 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4426 commit_reversible_command ();
4430 Editor::set_punch_range (framepos_t start, framepos_t end, string cmd)
4432 if (!_session) return;
4434 begin_reversible_command (cmd);
4438 if ((tpl = transport_punch_location()) == 0) {
4439 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoPunch);
4440 XMLNode &before = _session->locations()->get_state();
4441 _session->locations()->add (loc, true);
4442 _session->set_auto_loop_location (loc);
4443 XMLNode &after = _session->locations()->get_state();
4444 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4447 XMLNode &before = tpl->get_state();
4448 tpl->set_hidden (false, this);
4449 tpl->set (start, end);
4450 XMLNode &after = tpl->get_state();
4451 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4454 commit_reversible_command ();
4457 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4458 * @param rs List to which found regions are added.
4459 * @param where Time to look at.
4460 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4463 Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4465 const TrackViewList* tracks;
4468 tracks = &track_views;
4473 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4474 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4476 boost::shared_ptr<Track> tr;
4477 boost::shared_ptr<Playlist> pl;
4479 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4481 Playlist::RegionList* regions = pl->regions_at (
4482 (framepos_t) floor ( (double)where * tr->speed()));
4484 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4485 RegionView* rv = rtv->view()->find_view (*i);
4498 Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4500 const TrackViewList* tracks;
4503 tracks = &track_views;
4508 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4509 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4511 boost::shared_ptr<Track> tr;
4512 boost::shared_ptr<Playlist> pl;
4514 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4516 Playlist::RegionList* regions = pl->regions_touched (
4517 (framepos_t) floor ( (double)where * tr->speed()), max_framepos);
4519 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4521 RegionView* rv = rtv->view()->find_view (*i);
4534 /** Get regions using the following method:
4536 * Make an initial region list using the selected regions, unless
4537 * the edit point is `mouse' and the mouse is over an unselected
4538 * region. In this case, start with just that region.
4540 * Then, make an initial track list of the tracks that these
4541 * regions are on, and if the edit point is not `mouse', add the
4544 * Look at this track list and add any other tracks that are on the
4545 * same active edit-enabled route group as one of the initial tracks.
4547 * Finally take the initial region list and add any regions that are
4548 * under the edit point on one of the tracks on the track list to get
4549 * the returned region list.
4551 * The rationale here is that the mouse edit point is special in that
4552 * its position describes both a time and a track; the other edit
4553 * modes only describe a time. Hence if the edit point is `mouse' we
4554 * ignore selected tracks, as we assume the user means something by
4555 * pointing at a particular track. Also in this case we take note of
4556 * the region directly under the edit point, as there is always just one
4557 * (rather than possibly several with non-mouse edit points).
4561 Editor::get_regions_from_selection_and_edit_point ()
4563 RegionSelection regions;
4565 if (_edit_point == EditAtMouse && entered_regionview && !selection->regions.contains (entered_regionview)) {
4566 regions.add (entered_regionview);
4568 regions = selection->regions;
4571 TrackViewList tracks;
4573 if (_edit_point != EditAtMouse) {
4574 tracks = selection->tracks;
4577 /* Add any other tracks that have regions that are in the same
4578 edit-activated route group as one of our regions.
4580 for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) {
4582 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4583 if (g && g->is_active() && g->is_edit()) {
4584 tracks.add (axis_views_from_routes (g->route_list()));
4589 if (!tracks.empty()) {
4590 /* now find regions that are at the edit position on those tracks */
4591 framepos_t const where = get_preferred_edit_position ();
4592 get_regions_at (regions, where, tracks);
4600 Editor::get_regions_from_selection_and_entered ()
4602 RegionSelection rs = selection->regions;
4604 if (rs.empty() && entered_regionview) {
4605 rs.add (entered_regionview);
4612 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4614 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4616 RouteTimeAxisView* tatv;
4618 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4620 boost::shared_ptr<Playlist> pl;
4621 vector<boost::shared_ptr<Region> > results;
4623 boost::shared_ptr<Track> tr;
4625 if ((tr = tatv->track()) == 0) {
4630 if ((pl = (tr->playlist())) != 0) {
4631 pl->get_region_list_equivalent_regions (region, results);
4634 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4635 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4636 regions.push_back (marv);
4645 Editor::show_rhythm_ferret ()
4647 if (rhythm_ferret == 0) {
4648 rhythm_ferret = new RhythmFerret(*this);
4651 rhythm_ferret->set_session (_session);
4652 rhythm_ferret->show ();
4653 rhythm_ferret->present ();
4657 Editor::first_idle ()
4659 MessageDialog* dialog = 0;
4661 if (track_views.size() > 1) {
4662 dialog = new MessageDialog (*this,
4663 string_compose (_("Please wait while %1 loads visual data"), PROGRAM_NAME),
4668 ARDOUR_UI::instance()->flush_pending ();
4671 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4675 // first idle adds route children (automation tracks), so we need to redisplay here
4676 _routes->redisplay ();
4684 Editor::_idle_resize (gpointer arg)
4686 return ((Editor*)arg)->idle_resize ();
4690 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4692 if (resize_idle_id < 0) {
4693 resize_idle_id = g_idle_add (_idle_resize, this);
4694 _pending_resize_amount = 0;
4697 /* make a note of the smallest resulting height, so that we can clamp the
4698 lower limit at TimeAxisView::hSmall */
4700 int32_t min_resulting = INT32_MAX;
4702 _pending_resize_amount += h;
4703 _pending_resize_view = view;
4705 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4707 if (selection->tracks.contains (_pending_resize_view)) {
4708 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4709 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4713 if (min_resulting < 0) {
4718 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4719 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4723 /** Handle pending resizing of tracks */
4725 Editor::idle_resize ()
4727 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4729 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4730 selection->tracks.contains (_pending_resize_view)) {
4732 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4733 if (*i != _pending_resize_view) {
4734 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4739 _pending_resize_amount = 0;
4741 _group_tabs->set_dirty ();
4742 resize_idle_id = -1;
4750 ENSURE_GUI_THREAD (*this, &Editor::located);
4752 playhead_cursor->set_position (_session->audible_frame ());
4753 if (_follow_playhead && !_pending_initial_locate) {
4754 reset_x_origin_to_follow_playhead ();
4757 _pending_locate_request = false;
4758 _pending_initial_locate = false;
4762 Editor::region_view_added (RegionView *)
4764 _summary->set_dirty ();
4768 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4770 TrackViewList::const_iterator j = track_views.begin ();
4771 while (j != track_views.end()) {
4772 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4773 if (rtv && rtv->route() == r) {
4784 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4788 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4789 TimeAxisView* tv = axis_view_from_route (*i);
4800 Editor::handle_new_route (RouteList& routes)
4802 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4804 RouteTimeAxisView *rtv;
4805 list<RouteTimeAxisView*> new_views;
4807 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4808 boost::shared_ptr<Route> route = (*x);
4810 if (route->is_hidden() || route->is_monitor()) {
4814 DataType dt = route->input()->default_type();
4816 if (dt == ARDOUR::DataType::AUDIO) {
4817 rtv = new AudioTimeAxisView (*this, _session, route, *track_canvas);
4818 } else if (dt == ARDOUR::DataType::MIDI) {
4819 rtv = new MidiTimeAxisView (*this, _session, route, *track_canvas);
4821 throw unknown_type();
4824 new_views.push_back (rtv);
4825 track_views.push_back (rtv);
4827 rtv->effective_gain_display ();
4829 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4832 _routes->routes_added (new_views);
4833 _summary->routes_added (new_views);
4835 if (show_editor_mixer_when_tracks_arrive) {
4836 show_editor_mixer (true);
4839 editor_list_button.set_sensitive (true);
4843 Editor::timeaxisview_deleted (TimeAxisView *tv)
4845 if (_session && _session->deletion_in_progress()) {
4846 /* the situation is under control */
4850 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
4852 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
4854 _routes->route_removed (tv);
4856 if (tv == entered_track) {
4860 TimeAxisView::Children c = tv->get_child_list ();
4861 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
4862 if (entered_track == i->get()) {
4867 /* remove it from the list of track views */
4869 TrackViewList::iterator i;
4871 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
4872 i = track_views.erase (i);
4875 /* update whatever the current mixer strip is displaying, if revelant */
4877 boost::shared_ptr<Route> route;
4880 route = rtav->route ();
4883 if (current_mixer_strip && current_mixer_strip->route() == route) {
4885 TimeAxisView* next_tv;
4887 if (track_views.empty()) {
4889 } else if (i == track_views.end()) {
4890 next_tv = track_views.front();
4897 set_selected_mixer_strip (*next_tv);
4899 /* make the editor mixer strip go away setting the
4900 * button to inactive (which also unticks the menu option)
4903 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
4909 Editor::hide_track_in_display (TimeAxisView* tv, bool /*temponly*/)
4911 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
4913 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
4914 // this will hide the mixer strip
4915 set_selected_mixer_strip (*tv);
4918 _routes->hide_track_in_display (*tv);
4922 Editor::sync_track_view_list_and_routes ()
4924 track_views = TrackViewList (_routes->views ());
4926 _summary->set_dirty ();
4927 _group_tabs->set_dirty ();
4929 return false; // do not call again (until needed)
4933 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
4935 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4940 /** Find a RouteTimeAxisView by the ID of its route */
4942 Editor::get_route_view_by_route_id (PBD::ID& id) const
4944 RouteTimeAxisView* v;
4946 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
4947 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
4948 if(v->route()->id() == id) {
4958 Editor::fit_route_group (RouteGroup *g)
4960 TrackViewList ts = axis_views_from_routes (g->route_list ());
4965 Editor::consider_auditioning (boost::shared_ptr<Region> region)
4967 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
4970 _session->cancel_audition ();
4974 if (_session->is_auditioning()) {
4975 _session->cancel_audition ();
4976 if (r == last_audition_region) {
4981 _session->audition_region (r);
4982 last_audition_region = r;
4987 Editor::hide_a_region (boost::shared_ptr<Region> r)
4989 r->set_hidden (true);
4993 Editor::show_a_region (boost::shared_ptr<Region> r)
4995 r->set_hidden (false);
4999 Editor::audition_region_from_region_list ()
5001 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5005 Editor::hide_region_from_region_list ()
5007 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5011 Editor::show_region_in_region_list ()
5013 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5017 Editor::step_edit_status_change (bool yn)
5020 start_step_editing ();
5022 stop_step_editing ();
5027 Editor::start_step_editing ()
5029 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5033 Editor::stop_step_editing ()
5035 step_edit_connection.disconnect ();
5039 Editor::check_step_edit ()
5041 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5042 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5044 mtv->check_step_edit ();
5048 return true; // do it again, till we stop
5052 Editor::horizontal_scroll_left_press ()
5054 ++_scroll_callbacks;
5056 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5057 /* delay the first auto-repeat */
5061 double x = leftmost_position() - current_page_frames() / 5;
5068 /* do hacky auto-repeat */
5069 if (!_scroll_connection.connected ()) {
5070 _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press), 100);
5071 _scroll_callbacks = 0;
5078 Editor::horizontal_scroll_left_release ()
5080 _scroll_connection.disconnect ();
5084 Editor::horizontal_scroll_right_press ()
5086 ++_scroll_callbacks;
5088 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5089 /* delay the first auto-repeat */
5093 reset_x_origin (leftmost_position() + current_page_frames() / 5);
5095 /* do hacky auto-repeat */
5096 if (!_scroll_connection.connected ()) {
5097 _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press), 100);
5098 _scroll_callbacks = 0;
5105 Editor::horizontal_scroll_right_release ()
5107 _scroll_connection.disconnect ();
5110 /** Queue a change for the Editor viewport x origin to follow the playhead */
5112 Editor::reset_x_origin_to_follow_playhead ()
5114 framepos_t const frame = playhead_cursor->current_frame;
5116 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5118 if (_session->transport_speed() < 0) {
5120 if (frame > (current_page_frames() / 2)) {
5121 center_screen (frame-(current_page_frames()/2));
5123 center_screen (current_page_frames()/2);
5128 if (frame < leftmost_frame) {
5131 if (_session->transport_rolling()) {
5132 /* rolling; end up with the playhead at the right of the page */
5133 l = frame - current_page_frames ();
5135 /* not rolling: end up with the playhead 3/4 of the way along the page */
5136 l = frame - (3 * current_page_frames() / 4);
5143 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5146 if (_session->transport_rolling()) {
5147 /* rolling: end up with the playhead on the left of the page */
5148 center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5150 /* not rolling: end up with the playhead 1/4 of the way along the page */
5151 center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5159 Editor::super_rapid_screen_update ()
5161 if (!_session || !_session->engine().running()) {
5165 /* METERING / MIXER STRIPS */
5167 /* update track meters, if required */
5168 if (is_mapped() && meters_running) {
5169 RouteTimeAxisView* rtv;
5170 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5171 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5172 rtv->fast_update ();
5177 /* and any current mixer strip */
5178 if (current_mixer_strip) {
5179 current_mixer_strip->fast_update ();
5182 /* PLAYHEAD AND VIEWPORT */
5184 framepos_t const frame = _session->audible_frame();
5186 /* There are a few reasons why we might not update the playhead / viewport stuff:
5188 * 1. we don't update things when there's a pending locate request, otherwise
5189 * when the editor requests a locate there is a chance that this method
5190 * will move the playhead before the locate request is processed, causing
5192 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5193 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5196 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5198 last_update_frame = frame;
5200 if (!_dragging_playhead) {
5201 playhead_cursor->set_position (frame);
5204 if (!_stationary_playhead) {
5206 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5207 reset_x_origin_to_follow_playhead ();
5212 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5216 // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5217 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5218 if (target <= 0.0) {
5221 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5222 target = (target * 0.15) + (current * 0.85);
5228 set_horizontal_position (current);
5237 Editor::session_going_away ()
5239 _have_idled = false;
5241 _session_connections.drop_connections ();
5243 super_rapid_screen_update_connection.disconnect ();
5245 selection->clear ();
5246 cut_buffer->clear ();
5248 clicked_regionview = 0;
5249 clicked_axisview = 0;
5250 clicked_routeview = 0;
5251 clicked_crossfadeview = 0;
5252 entered_regionview = 0;
5254 last_update_frame = 0;
5257 playhead_cursor->canvas_item.hide ();
5259 /* rip everything out of the list displays */
5263 _route_groups->clear ();
5265 /* do this first so that deleting a track doesn't reset cms to null
5266 and thus cause a leak.
5269 if (current_mixer_strip) {
5270 if (current_mixer_strip->get_parent() != 0) {
5271 global_hpacker.remove (*current_mixer_strip);
5273 delete current_mixer_strip;
5274 current_mixer_strip = 0;
5277 /* delete all trackviews */
5279 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5282 track_views.clear ();
5284 zoom_range_clock.set_session (0);
5285 nudge_clock.set_session (0);
5287 editor_list_button.set_active(false);
5288 editor_list_button.set_sensitive(false);
5290 /* clear tempo/meter rulers */
5291 remove_metric_marks ();
5293 clear_marker_display ();
5295 delete current_bbt_points;
5296 current_bbt_points = 0;
5298 /* get rid of any existing editor mixer strip */
5300 WindowTitle title(Glib::get_application_name());
5301 title += _("Editor");
5303 set_title (title.get_string());
5305 SessionHandlePtr::session_going_away ();
5310 Editor::show_editor_list (bool yn)
5313 _the_notebook.show ();
5315 _the_notebook.hide ();
5320 Editor::change_region_layering_order ()
5322 framepos_t const position = get_preferred_edit_position ();
5324 if (!clicked_routeview) {
5325 if (layering_order_editor) {
5326 layering_order_editor->hide ();
5331 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
5337 boost::shared_ptr<Playlist> pl = track->playlist();
5343 if (layering_order_editor == 0) {
5344 layering_order_editor = new RegionLayeringOrderEditor(*this);
5347 layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position);
5348 layering_order_editor->maybe_present ();
5352 Editor::update_region_layering_order_editor ()
5354 if (layering_order_editor && layering_order_editor->is_visible ()) {
5355 change_region_layering_order ();
5360 Editor::setup_fade_images ()
5362 _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear")));
5363 _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut")));
5364 _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut")));
5365 _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut")));
5366 _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut")));
5368 _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear")));
5369 _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut")));
5370 _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut")));
5371 _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut")));
5372 _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut")));
5376 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
5378 Editor::action_menu_item (std::string const & name)
5380 Glib::RefPtr<Action> a = editor_actions->get_action (name);
5383 return *manage (a->create_menu_item ());
5387 Editor::resize_text_widgets ()
5389 set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_FUDGE+10, 15);
5390 set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_FUDGE+10, 15);
5391 set_size_request_to_display_given_text (snap_type_selector, snap_type_strings, COMBO_FUDGE+10, 15);
5392 set_size_request_to_display_given_text (snap_mode_selector, snap_mode_strings, COMBO_FUDGE+10, 15);
5393 set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_FUDGE+10, 15);
5397 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
5399 EventBox* b = manage (new EventBox);
5400 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
5401 Label* l = manage (new Label (name));
5405 _the_notebook.append_page (widget, *b);
5409 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
5411 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
5412 _the_notebook.set_current_page (_the_notebook.page_num (*page));
5415 if (ev->type == GDK_2BUTTON_PRESS) {
5417 /* double-click on a notebook tab shrinks or expands the notebook */
5419 if (_notebook_shrunk) {
5420 edit_pane.set_position (pre_maximal_horizontal_pane_position);
5421 _notebook_shrunk = false;
5423 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
5424 edit_pane.set_position (edit_pane.get_position() + page->get_width());
5425 _notebook_shrunk = true;