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 (_("Samples"))
245 , tempo_label (_("Tempo"))
246 , meter_label (_("Meter"))
247 , mark_label (_("Location Markers"))
248 , range_mark_label (_("Range Markers"))
249 , transport_mark_label (_("Loop/Punch Ranges"))
250 , cd_mark_label (_("CD Markers"))
251 , edit_packer (4, 4, true)
253 /* the values here don't matter: layout widgets
254 reset them as needed.
257 , vertical_adjustment (0.0, 0.0, 10.0, 400.0)
259 /* tool bar related */
261 , zoom_range_clock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, false, true)
263 , toolbar_selection_clock_table (2,3)
265 , automation_mode_button (_("mode"))
266 , global_automation_button (_("automation"))
268 , _toolbar_viewport (*manage (new Gtk::Adjustment (0, 0, 1e10)), *manage (new Gtk::Adjustment (0, 0, 1e10)))
269 , midi_panic_button (_("Panic"))
272 , image_socket_listener(0)
277 , nudge_clock (X_("nudge"), false, X_("NudgeClock"), true, false, true)
278 , meters_running(false)
279 , _pending_locate_request (false)
280 , _pending_initial_locate (false)
281 , _last_cut_copy_source_track (0)
283 , _region_selection_change_updates_region_list (true)
287 /* we are a singleton */
289 PublicEditor::_instance = this;
293 selection = new Selection (this);
294 cut_buffer = new Selection (this);
296 clicked_regionview = 0;
297 clicked_axisview = 0;
298 clicked_routeview = 0;
299 clicked_crossfadeview = 0;
300 clicked_control_point = 0;
301 last_update_frame = 0;
302 pre_press_cursor = 0;
303 _drags = new DragManager (this);
304 current_mixer_strip = 0;
305 current_bbt_points = 0;
308 snap_type_strings = I18N (_snap_type_strings);
309 snap_mode_strings = I18N (_snap_mode_strings);
310 zoom_focus_strings = I18N (_zoom_focus_strings);
311 edit_point_strings = I18N (_edit_point_strings);
312 #ifdef USE_RUBBERBAND
313 rb_opt_strings = I18N (_rb_opt_strings);
317 snap_threshold = 5.0;
318 bbt_beat_subdivision = 4;
321 last_autoscroll_x = 0;
322 last_autoscroll_y = 0;
323 autoscroll_active = false;
324 autoscroll_timeout_tag = -1;
329 current_interthread_info = 0;
330 _show_measures = true;
331 show_gain_after_trim = false;
332 verbose_cursor_on = true;
333 last_item_entered = 0;
335 have_pending_keyboard_selection = false;
336 _follow_playhead = true;
337 _stationary_playhead = false;
338 _xfade_visibility = true;
339 editor_ruler_menu = 0;
340 no_ruler_shown_update = false;
342 range_marker_menu = 0;
343 marker_menu_item = 0;
344 tempo_or_meter_marker_menu = 0;
345 transport_marker_menu = 0;
346 new_transport_marker_menu = 0;
347 editor_mixer_strip_width = Wide;
348 show_editor_mixer_when_tracks_arrive = false;
349 region_edit_menu_split_multichannel_item = 0;
350 region_edit_menu_split_item = 0;
353 current_stepping_trackview = 0;
355 entered_regionview = 0;
357 clear_entered_track = false;
360 button_release_can_deselect = true;
361 _dragging_playhead = false;
362 _dragging_edit_point = false;
363 select_new_marker = false;
365 layering_order_editor = 0;
367 no_save_visual = false;
370 scrubbing_direction = 0;
374 location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get();
375 location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get();
376 location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get();
377 location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get();
378 location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get();
380 _edit_point = EditAtMouse;
381 _internal_editing = false;
382 current_canvas_cursor = 0;
384 frames_per_unit = 2048; /* too early to use reset_zoom () */
386 _scroll_callbacks = 0;
388 zoom_focus = ZoomFocusLeft;
389 set_zoom_focus (ZoomFocusLeft);
390 zoom_range_clock.ValueChanged.connect (sigc::mem_fun(*this, &Editor::zoom_adjustment_changed));
392 bbt_label.set_name ("EditorTimeButton");
393 bbt_label.set_size_request (-1, (int)timebar_height);
394 bbt_label.set_alignment (1.0, 0.5);
395 bbt_label.set_padding (5,0);
397 bbt_label.set_no_show_all();
398 minsec_label.set_name ("EditorTimeButton");
399 minsec_label.set_size_request (-1, (int)timebar_height);
400 minsec_label.set_alignment (1.0, 0.5);
401 minsec_label.set_padding (5,0);
402 minsec_label.hide ();
403 minsec_label.set_no_show_all();
404 timecode_label.set_name ("EditorTimeButton");
405 timecode_label.set_size_request (-1, (int)timebar_height);
406 timecode_label.set_alignment (1.0, 0.5);
407 timecode_label.set_padding (5,0);
408 timecode_label.hide ();
409 timecode_label.set_no_show_all();
410 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);
539 nlabel = manage (new Label (_("Regions")));
540 nlabel->set_angle (-90);
541 the_notebook.append_page (_regions->widget (), *nlabel);
542 nlabel = manage (new Label (_("Tracks & Busses")));
543 nlabel->set_angle (-90);
544 the_notebook.append_page (_routes->widget (), *nlabel);
545 nlabel = manage (new Label (_("Snapshots")));
546 nlabel->set_angle (-90);
547 the_notebook.append_page (_snapshots->widget (), *nlabel);
548 nlabel = manage (new Label (_("Route Groups")));
549 nlabel->set_angle (-90);
550 the_notebook.append_page (_route_groups->widget (), *nlabel);
551 nlabel = manage (new Label (_("Ranges & Marks")));
552 nlabel->set_angle (-90);
553 the_notebook.append_page (_locations->widget (), *nlabel);
555 the_notebook.set_show_tabs (true);
556 the_notebook.set_scrollable (true);
557 the_notebook.popup_disable ();
558 the_notebook.set_tab_pos (Gtk::POS_RIGHT);
559 the_notebook.show_all ();
561 post_maximal_editor_width = 0;
562 post_maximal_horizontal_pane_position = 0;
563 post_maximal_editor_height = 0;
564 post_maximal_vertical_pane_position = 0;
566 editor_summary_pane.pack1(edit_packer);
568 Button* summary_arrows_left_left = manage (new Button);
569 summary_arrows_left_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
570 summary_arrows_left_left->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press)));
571 summary_arrows_left_left->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_release));
572 Button* summary_arrows_left_right = manage (new Button);
573 summary_arrows_left_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
574 summary_arrows_left_right->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press)));
575 summary_arrows_left_right->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_release));
576 VBox* summary_arrows_left = manage (new VBox);
577 summary_arrows_left->pack_start (*summary_arrows_left_left);
578 summary_arrows_left->pack_start (*summary_arrows_left_right);
580 Button* summary_arrows_right_left = manage (new Button);
581 summary_arrows_right_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
582 summary_arrows_right_left->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press)));
583 summary_arrows_right_left->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_release));
584 Button* summary_arrows_right_right = manage (new Button);
585 summary_arrows_right_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
586 summary_arrows_right_right->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press)));
587 summary_arrows_right_right->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_release));
588 VBox* summary_arrows_right = manage (new VBox);
589 summary_arrows_right->pack_start (*summary_arrows_right_left);
590 summary_arrows_right->pack_start (*summary_arrows_right_right);
592 Frame* summary_frame = manage (new Frame);
593 summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN);
594 summary_frame->add (*_summary);
595 summary_frame->show ();
597 _summary_hbox.pack_start (*summary_arrows_left, false, false);
598 _summary_hbox.pack_start (*summary_frame, true, true);
599 _summary_hbox.pack_start (*summary_arrows_right, false, false);
601 editor_summary_pane.pack2 (_summary_hbox);
603 edit_pane.pack1 (editor_summary_pane, true, true);
604 edit_pane.pack2 (the_notebook, false, true);
606 editor_summary_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun (*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&editor_summary_pane)));
608 /* XXX: editor_summary_pane might need similar special OS X treatment to the edit_pane */
610 edit_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&edit_pane)));
612 Glib::PropertyProxy<int> proxy = edit_pane.property_position();
613 proxy.signal_changed().connect (bind (sigc::ptr_fun (pane_size_watcher), static_cast<Paned*> (&edit_pane)));
615 top_hbox.pack_start (toolbar_frame);
617 HBox *hbox = manage (new HBox);
618 hbox->pack_start (edit_pane, true, true);
620 global_vpacker.pack_start (top_hbox, false, false);
621 global_vpacker.pack_start (*hbox, true, true);
623 global_hpacker.pack_start (global_vpacker, true, true);
625 set_name ("EditorWindow");
626 add_accel_group (ActionManager::ui_manager->get_accel_group());
628 status_bar_hpacker.show ();
630 vpacker.pack_end (status_bar_hpacker, false, false);
631 vpacker.pack_end (global_hpacker, true, true);
633 /* register actions now so that set_state() can find them and set toggles/checks etc */
638 setup_midi_toolbar ();
640 _snap_type = SnapToBeat;
641 set_snap_to (_snap_type);
642 _snap_mode = SnapOff;
643 set_snap_mode (_snap_mode);
644 set_mouse_mode (MouseObject, true);
645 set_edit_point_preference (EditAtMouse, true);
647 _playlist_selector = new PlaylistSelector();
648 _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
650 RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), ui_bind (&Editor::catch_vanishing_regionview, this, _1), gui_context());
654 nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
655 nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
657 nudge_forward_button.set_name ("TransportButton");
658 nudge_backward_button.set_name ("TransportButton");
660 fade_context_menu.set_name ("ArdourContextMenu");
662 /* icons, titles, WM stuff */
664 list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
665 Glib::RefPtr<Gdk::Pixbuf> icon;
667 if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
668 window_icons.push_back (icon);
670 if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
671 window_icons.push_back (icon);
673 if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
674 window_icons.push_back (icon);
676 if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
677 window_icons.push_back (icon);
679 if (!window_icons.empty()) {
680 set_icon_list (window_icons);
681 set_default_icon_list (window_icons);
684 WindowTitle title(Glib::get_application_name());
685 title += _("Editor");
686 set_title (title.get_string());
687 set_wmclass (X_("ardour_editor"), PROGRAM_NAME);
690 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
692 signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
693 signal_delete_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
695 /* allow external control surfaces/protocols to do various things */
697 ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context());
698 ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context());
699 ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context());
700 ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), ui_bind (&Editor::control_scroll, this, _1), gui_context());
701 BasicUI::AccessAction.connect (*this, invalidator (*this), ui_bind (&Editor::access_action, this, _1, _2), gui_context());
703 /* problematic: has to return a value and thus cannot be x-thread */
705 Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
707 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
709 TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
711 _ignore_region_action = false;
712 _last_region_menu_was_main = false;
713 _popup_region_menu_item = 0;
715 _show_marker_lines = false;
716 _over_region_trim_target = false;
721 setup_fade_images ();
727 if(image_socket_listener) {
728 if(image_socket_listener->is_connected())
730 image_socket_listener->close_connection() ;
733 delete image_socket_listener ;
734 image_socket_listener = 0 ;
739 delete _route_groups;
745 Editor::add_toplevel_controls (Container& cont)
747 vpacker.pack_start (cont, false, false);
752 Editor::catch_vanishing_regionview (RegionView *rv)
754 /* note: the selection will take care of the vanishing
755 audioregionview by itself.
758 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
762 if (clicked_regionview == rv) {
763 clicked_regionview = 0;
766 if (entered_regionview == rv) {
767 set_entered_regionview (0);
770 if (!_all_region_actions_sensitized) {
771 sensitize_all_region_actions (true);
776 Editor::set_entered_regionview (RegionView* rv)
778 if (rv == entered_regionview) {
782 if (entered_regionview) {
783 entered_regionview->exited ();
786 if ((entered_regionview = rv) != 0) {
787 entered_regionview->entered (internal_editing ());
790 if (!_all_region_actions_sensitized && _last_region_menu_was_main) {
791 /* This RegionView entry might have changed what region actions
792 are allowed, so sensitize them all in case a key is pressed.
794 sensitize_all_region_actions (true);
799 Editor::set_entered_track (TimeAxisView* tav)
802 entered_track->exited ();
805 if ((entered_track = tav) != 0) {
806 entered_track->entered ();
811 Editor::show_window ()
813 if (!is_visible ()) {
816 /* re-hide stuff if necessary */
817 editor_list_button_toggled ();
818 parameter_changed ("show-summary");
819 parameter_changed ("show-edit-group-tabs");
820 parameter_changed ("show-zoom-tools");
822 /* now reset all audio_time_axis heights, because widgets might need
828 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
829 tv = (static_cast<TimeAxisView*>(*i));
838 Editor::instant_save ()
840 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
845 _session->add_instant_xml(get_state());
847 Config->add_instant_xml(get_state());
852 Editor::zoom_adjustment_changed ()
858 double fpu = zoom_range_clock.current_duration() / _canvas_width;
862 zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width));
863 } else if (fpu > _session->current_end_frame() / _canvas_width) {
864 fpu = _session->current_end_frame() / _canvas_width;
865 zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width));
872 Editor::control_scroll (float fraction)
874 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
880 double step = fraction * current_page_frames();
883 _control_scroll_target is an optional<T>
885 it acts like a pointer to an framepos_t, with
886 a operator conversion to boolean to check
887 that it has a value could possibly use
888 playhead_cursor->current_frame to store the
889 value and a boolean in the class to know
890 when it's out of date
893 if (!_control_scroll_target) {
894 _control_scroll_target = _session->transport_frame();
895 _dragging_playhead = true;
898 if ((fraction < 0.0f) && (*_control_scroll_target < (framepos_t) fabs(step))) {
899 *_control_scroll_target = 0;
900 } else if ((fraction > 0.0f) && (max_framepos - *_control_scroll_target < step)) {
901 *_control_scroll_target = max_framepos - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
903 *_control_scroll_target += (framepos_t) floor (step);
906 /* move visuals, we'll catch up with it later */
908 playhead_cursor->set_position (*_control_scroll_target);
909 UpdateAllTransportClocks (*_control_scroll_target);
911 if (*_control_scroll_target > (current_page_frames() / 2)) {
912 /* try to center PH in window */
913 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
919 Now we do a timeout to actually bring the session to the right place
920 according to the playhead. This is to avoid reading disk buffers on every
921 call to control_scroll, which is driven by ScrollTimeline and therefore
922 probably by a control surface wheel which can generate lots of events.
924 /* cancel the existing timeout */
926 control_scroll_connection.disconnect ();
928 /* add the next timeout */
930 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
934 Editor::deferred_control_scroll (framepos_t /*target*/)
936 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
937 // reset for next stream
938 _control_scroll_target = boost::none;
939 _dragging_playhead = false;
944 Editor::access_action (std::string action_group, std::string action_item)
950 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
953 act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
961 Editor::on_realize ()
963 Window::on_realize ();
968 Editor::map_position_change (framepos_t frame)
970 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, frame)
976 if (_follow_playhead) {
977 center_screen (frame);
980 playhead_cursor->set_position (frame);
984 Editor::center_screen (framepos_t frame)
986 double page = _canvas_width * frames_per_unit;
988 /* if we're off the page, then scroll.
991 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
992 center_screen_internal (frame, page);
997 Editor::center_screen_internal (framepos_t frame, float page)
1002 frame -= (framepos_t) page;
1007 reset_x_origin (frame);
1012 Editor::update_title ()
1014 ENSURE_GUI_THREAD (*this, &Editor::update_title)
1017 bool dirty = _session->dirty();
1019 string session_name;
1021 if (_session->snap_name() != _session->name()) {
1022 session_name = _session->snap_name();
1024 session_name = _session->name();
1028 session_name = "*" + session_name;
1031 WindowTitle title(session_name);
1032 title += Glib::get_application_name();
1033 set_title (title.get_string());
1038 Editor::set_session (Session *t)
1040 SessionHandlePtr::set_session (t);
1046 zoom_range_clock.set_session (_session);
1047 _playlist_selector->set_session (_session);
1048 nudge_clock.set_session (_session);
1049 _summary->set_session (_session);
1050 _group_tabs->set_session (_session);
1051 _route_groups->set_session (_session);
1052 _regions->set_session (_session);
1053 _snapshots->set_session (_session);
1054 _routes->set_session (_session);
1055 _locations->set_session (_session);
1057 if (rhythm_ferret) {
1058 rhythm_ferret->set_session (_session);
1061 if (analysis_window) {
1062 analysis_window->set_session (_session);
1066 sfbrowser->set_session (_session);
1069 compute_fixed_ruler_scale ();
1071 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1072 set_state (*node, Stateful::loading_state_version);
1074 /* catch up with the playhead */
1076 _session->request_locate (playhead_cursor->current_frame);
1077 _pending_initial_locate = true;
1081 /* These signals can all be emitted by a non-GUI thread. Therefore the
1082 handlers for them must not attempt to directly interact with the GUI,
1083 but use Gtkmm2ext::UI::instance()->call_slot();
1086 _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), ui_bind(&Editor::step_edit_status_change, this, _1), gui_context());
1087 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1088 _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context());
1089 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context());
1090 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1091 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context());
1092 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1093 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
1094 _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Editor::session_state_saved, this, _1), gui_context());
1095 _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context());
1096 _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&Editor::location_gone, this, _1), gui_context());
1097 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1098 _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display_s, this, _1), gui_context());
1099 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1101 if (Profile->get_sae()) {
1102 Timecode::BBT_Time bbt;
1106 framepos_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1);
1107 nudge_clock.set_mode(AudioClock::BBT);
1108 nudge_clock.set (pos, true, 0, AudioClock::BBT);
1111 nudge_clock.set (_session->frame_rate() * 5, true, 0, AudioClock::Timecode); // default of 5 seconds
1114 playhead_cursor->canvas_item.show ();
1116 Location* loc = _session->locations()->auto_loop_location();
1118 loc = new Location (*_session, 0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1119 if (loc->start() == loc->end()) {
1120 loc->set_end (loc->start() + 1);
1122 _session->locations()->add (loc, false);
1123 _session->set_auto_loop_location (loc);
1126 loc->set_name (_("Loop"));
1129 loc = _session->locations()->auto_punch_location();
1131 loc = new Location (*_session, 0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1132 if (loc->start() == loc->end()) {
1133 loc->set_end (loc->start() + 1);
1135 _session->locations()->add (loc, false);
1136 _session->set_auto_punch_location (loc);
1139 loc->set_name (_("Punch"));
1142 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1143 Config->map_parameters (pc);
1144 _session->config.map_parameters (pc);
1146 refresh_location_display ();
1148 restore_ruler_visibility ();
1149 //tempo_map_changed (PropertyChange (0));
1150 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1152 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1153 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1156 super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
1157 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1160 switch (_snap_type) {
1161 case SnapToRegionStart:
1162 case SnapToRegionEnd:
1163 case SnapToRegionSync:
1164 case SnapToRegionBoundary:
1165 build_region_boundary_cache ();
1172 /* register for undo history */
1173 _session->register_with_memento_command_factory(_id, this);
1175 ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated));
1177 start_updating_meters ();
1181 Editor::action_pre_activated (Glib::RefPtr<Action> const & a)
1183 if (a->get_name() == "RegionMenu") {
1184 /* When the main menu's region menu is opened, we setup the actions so that they look right
1185 in the menu. I can't find a way of getting a signal when this menu is subsequently closed,
1186 so we resensitize all region actions when the entered regionview or the region selection
1187 changes. HOWEVER we can't always resensitize on entered_regionview change because that
1188 happens after the region context menu is opened. So we set a flag here, too.
1192 sensitize_the_right_region_actions ();
1193 _last_region_menu_was_main = true;
1197 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1199 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1201 using namespace Menu_Helpers;
1202 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1205 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1209 MenuList& items (fade_context_menu.items());
1213 switch (item_type) {
1215 case FadeInHandleItem:
1216 if (arv->audio_region()->fade_in_active()) {
1217 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1219 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1222 items.push_back (SeparatorElem());
1224 if (Profile->get_sae()) {
1226 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)));
1227 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)));
1234 *_fade_in_images[FadeLinear],
1235 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)
1239 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1244 *_fade_in_images[FadeFast],
1245 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)
1248 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1253 *_fade_in_images[FadeLogB],
1254 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogB)
1257 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1262 *_fade_in_images[FadeLogA],
1263 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogA)
1266 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1271 *_fade_in_images[FadeSlow],
1272 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow)
1275 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1281 case FadeOutHandleItem:
1282 if (arv->audio_region()->fade_out_active()) {
1283 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1285 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1288 items.push_back (SeparatorElem());
1290 if (Profile->get_sae()) {
1291 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)));
1292 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)));
1298 *_fade_out_images[FadeLinear],
1299 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)
1303 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1308 *_fade_out_images[FadeFast],
1309 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)
1312 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1317 *_fade_out_images[FadeLogB],
1318 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogA)
1321 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1326 *_fade_out_images[FadeLogA],
1327 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogB)
1330 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1335 *_fade_out_images[FadeSlow],
1336 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast)
1339 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1345 fatal << _("programming error: ")
1346 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1351 fade_context_menu.popup (button, time);
1355 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1357 using namespace Menu_Helpers;
1358 Menu* (Editor::*build_menu_function)();
1361 switch (item_type) {
1363 case RegionViewName:
1364 case RegionViewNameHighlight:
1365 case LeftFrameHandle:
1366 case RightFrameHandle:
1367 if (with_selection) {
1368 build_menu_function = &Editor::build_track_selection_context_menu;
1370 build_menu_function = &Editor::build_track_region_context_menu;
1375 if (with_selection) {
1376 build_menu_function = &Editor::build_track_selection_context_menu;
1378 build_menu_function = &Editor::build_track_context_menu;
1382 case CrossfadeViewItem:
1383 build_menu_function = &Editor::build_track_crossfade_context_menu;
1387 if (clicked_routeview->track()) {
1388 build_menu_function = &Editor::build_track_context_menu;
1390 build_menu_function = &Editor::build_track_bus_context_menu;
1395 /* probably shouldn't happen but if it does, we don't care */
1399 menu = (this->*build_menu_function)();
1400 menu->set_name ("ArdourContextMenu");
1402 /* now handle specific situations */
1404 switch (item_type) {
1406 case RegionViewName:
1407 case RegionViewNameHighlight:
1408 case LeftFrameHandle:
1409 case RightFrameHandle:
1410 if (!with_selection) {
1411 if (region_edit_menu_split_item) {
1412 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1413 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1415 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1418 if (region_edit_menu_split_multichannel_item) {
1419 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1420 region_edit_menu_split_multichannel_item->set_sensitive (true);
1422 region_edit_menu_split_multichannel_item->set_sensitive (false);
1431 case CrossfadeViewItem:
1438 /* probably shouldn't happen but if it does, we don't care */
1442 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1444 /* Bounce to disk */
1446 using namespace Menu_Helpers;
1447 MenuList& edit_items = menu->items();
1449 edit_items.push_back (SeparatorElem());
1451 switch (clicked_routeview->audio_track()->freeze_state()) {
1452 case AudioTrack::NoFreeze:
1453 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1456 case AudioTrack::Frozen:
1457 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1460 case AudioTrack::UnFrozen:
1461 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1467 if (item_type == StreamItem && clicked_routeview) {
1468 clicked_routeview->build_underlay_menu(menu);
1471 /* When the region menu is opened, we setup the actions so that they look right
1474 sensitize_the_right_region_actions ();
1475 _last_region_menu_was_main = false;
1477 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1478 menu->popup (button, time);
1482 Editor::build_track_context_menu ()
1484 using namespace Menu_Helpers;
1486 MenuList& edit_items = track_context_menu.items();
1489 add_dstream_context_items (edit_items);
1490 return &track_context_menu;
1494 Editor::build_track_bus_context_menu ()
1496 using namespace Menu_Helpers;
1498 MenuList& edit_items = track_context_menu.items();
1501 add_bus_context_items (edit_items);
1502 return &track_context_menu;
1506 Editor::build_track_region_context_menu ()
1508 using namespace Menu_Helpers;
1509 MenuList& edit_items = track_region_context_menu.items();
1512 /* we've just cleared the track region context menu, so the menu that these
1513 two items were on will have disappeared; stop them dangling.
1515 region_edit_menu_split_item = 0;
1516 region_edit_menu_split_multichannel_item = 0;
1518 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1521 boost::shared_ptr<Track> tr;
1522 boost::shared_ptr<Playlist> pl;
1524 /* Don't offer a region submenu if we are in internal edit mode, as we don't select regions in this
1525 mode and so offering region context is somewhat confusing.
1527 if ((tr = rtv->track()) && ((pl = tr->playlist())) && !internal_editing()) {
1528 framepos_t const framepos = (framepos_t) floor ((double) get_preferred_edit_position() * tr->speed());
1529 uint32_t regions_at = pl->count_regions_at (framepos);
1530 add_region_context_items (edit_items, regions_at > 1);
1534 add_dstream_context_items (edit_items);
1536 return &track_region_context_menu;
1540 Editor::build_track_crossfade_context_menu ()
1542 using namespace Menu_Helpers;
1543 MenuList& edit_items = track_crossfade_context_menu.items();
1544 edit_items.clear ();
1546 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1549 boost::shared_ptr<Track> tr;
1550 boost::shared_ptr<Playlist> pl;
1551 boost::shared_ptr<AudioPlaylist> apl;
1553 if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1555 AudioPlaylist::Crossfades xfades;
1557 apl->crossfades_at (get_preferred_edit_position (), xfades);
1559 bool many = xfades.size() > 1;
1561 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1562 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1565 framepos_t framepos = (framepos_t) floor ((double) get_preferred_edit_position() * tr->speed());
1566 uint32_t regions_at = pl->count_regions_at (framepos);
1567 add_region_context_items (edit_items, regions_at > 1);
1571 add_dstream_context_items (edit_items);
1573 return &track_crossfade_context_menu;
1577 Editor::analyze_region_selection ()
1579 if (analysis_window == 0) {
1580 analysis_window = new AnalysisWindow();
1583 analysis_window->set_session(_session);
1585 analysis_window->show_all();
1588 analysis_window->set_regionmode();
1589 analysis_window->analyze();
1591 analysis_window->present();
1595 Editor::analyze_range_selection()
1597 if (analysis_window == 0) {
1598 analysis_window = new AnalysisWindow();
1601 analysis_window->set_session(_session);
1603 analysis_window->show_all();
1606 analysis_window->set_rangemode();
1607 analysis_window->analyze();
1609 analysis_window->present();
1613 Editor::build_track_selection_context_menu ()
1615 using namespace Menu_Helpers;
1616 MenuList& edit_items = track_selection_context_menu.items();
1617 edit_items.clear ();
1619 add_selection_context_items (edit_items);
1620 // edit_items.push_back (SeparatorElem());
1621 // add_dstream_context_items (edit_items);
1623 return &track_selection_context_menu;
1626 /** Add context menu items relevant to crossfades.
1627 * @param edit_items List to add the items to.
1630 Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1632 using namespace Menu_Helpers;
1633 Menu *xfade_menu = manage (new Menu);
1634 MenuList& items = xfade_menu->items();
1635 xfade_menu->set_name ("ArdourContextMenu");
1638 if (xfade->active()) {
1644 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1645 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1647 if (xfade->can_follow_overlap()) {
1649 if (xfade->following_overlap()) {
1650 str = _("Convert to Short");
1652 str = _("Convert to Full");
1655 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1659 str = xfade->out()->name();
1661 str += xfade->in()->name();
1663 str = _("Crossfade");
1666 edit_items.push_back (MenuElem (str, *xfade_menu));
1667 edit_items.push_back (SeparatorElem());
1671 Editor::xfade_edit_left_region ()
1673 if (clicked_crossfadeview) {
1674 clicked_crossfadeview->left_view.show_region_editor ();
1679 Editor::xfade_edit_right_region ()
1681 if (clicked_crossfadeview) {
1682 clicked_crossfadeview->right_view.show_region_editor ();
1687 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, bool multiple_regions_at_position)
1689 using namespace Menu_Helpers;
1691 /* OK, stick the region submenu at the top of the list, and then add
1695 /* we have to hack up the region name because "_" has a special
1696 meaning for menu titles.
1699 RegionSelection rs = get_regions_from_selection_and_entered ();
1701 string::size_type pos = 0;
1702 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1704 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1705 menu_item_name.replace (pos, 1, "__");
1709 if (_popup_region_menu_item == 0) {
1710 _popup_region_menu_item = new MenuItem (menu_item_name);
1711 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1712 _popup_region_menu_item->show ();
1714 _popup_region_menu_item->set_label (menu_item_name);
1717 edit_items.push_back (*_popup_region_menu_item);
1718 if (multiple_regions_at_position && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1719 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region")->create_menu_item ()));
1721 edit_items.push_back (SeparatorElem());
1724 /** Add context menu items relevant to selection ranges.
1725 * @param edit_items List to add the items to.
1728 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1730 using namespace Menu_Helpers;
1732 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1733 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1735 edit_items.push_back (SeparatorElem());
1736 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1738 if (!selection->regions.empty()) {
1739 edit_items.push_back (SeparatorElem());
1740 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)));
1741 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)));
1744 edit_items.push_back (SeparatorElem());
1745 edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection)));
1746 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1748 edit_items.push_back (SeparatorElem());
1749 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1751 edit_items.push_back (SeparatorElem());
1752 edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1753 edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1755 edit_items.push_back (SeparatorElem());
1756 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1758 edit_items.push_back (SeparatorElem());
1759 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1760 edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1761 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1763 edit_items.push_back (SeparatorElem());
1764 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1765 edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1766 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1767 edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1768 edit_items.push_back (MenuElem (_("Export Range"), sigc::mem_fun(*this, &Editor::export_selection)));
1773 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1775 using namespace Menu_Helpers;
1779 Menu *play_menu = manage (new Menu);
1780 MenuList& play_items = play_menu->items();
1781 play_menu->set_name ("ArdourContextMenu");
1783 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1784 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1785 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1786 play_items.push_back (SeparatorElem());
1787 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
1789 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1793 Menu *select_menu = manage (new Menu);
1794 MenuList& select_items = select_menu->items();
1795 select_menu->set_name ("ArdourContextMenu");
1797 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1798 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1799 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1800 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1801 select_items.push_back (SeparatorElem());
1802 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
1803 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
1804 select_items.push_back (SeparatorElem());
1805 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1806 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1807 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1808 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1809 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
1810 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
1811 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
1813 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1817 Menu *cutnpaste_menu = manage (new Menu);
1818 MenuList& cutnpaste_items = cutnpaste_menu->items();
1819 cutnpaste_menu->set_name ("ArdourContextMenu");
1821 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1822 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1823 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1825 cutnpaste_items.push_back (SeparatorElem());
1827 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
1828 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
1830 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
1832 /* Adding new material */
1834 edit_items.push_back (SeparatorElem());
1835 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
1836 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
1840 Menu *nudge_menu = manage (new Menu());
1841 MenuList& nudge_items = nudge_menu->items();
1842 nudge_menu->set_name ("ArdourContextMenu");
1844 edit_items.push_back (SeparatorElem());
1845 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1846 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1847 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1848 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1850 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1854 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
1856 using namespace Menu_Helpers;
1860 Menu *play_menu = manage (new Menu);
1861 MenuList& play_items = play_menu->items();
1862 play_menu->set_name ("ArdourContextMenu");
1864 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1865 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1866 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1870 Menu *select_menu = manage (new Menu);
1871 MenuList& select_items = select_menu->items();
1872 select_menu->set_name ("ArdourContextMenu");
1874 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1875 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1876 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1877 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1878 select_items.push_back (SeparatorElem());
1879 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1880 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1881 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1882 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1884 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1888 Menu *cutnpaste_menu = manage (new Menu);
1889 MenuList& cutnpaste_items = cutnpaste_menu->items();
1890 cutnpaste_menu->set_name ("ArdourContextMenu");
1892 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1893 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1894 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1896 Menu *nudge_menu = manage (new Menu());
1897 MenuList& nudge_items = nudge_menu->items();
1898 nudge_menu->set_name ("ArdourContextMenu");
1900 edit_items.push_back (SeparatorElem());
1901 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1902 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1903 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1904 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1906 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1910 Editor::snap_type() const
1916 Editor::snap_mode() const
1922 Editor::set_snap_to (SnapType st)
1924 unsigned int snap_ind = (unsigned int)st;
1928 if (snap_ind > snap_type_strings.size() - 1) {
1930 _snap_type = (SnapType)snap_ind;
1933 string str = snap_type_strings[snap_ind];
1935 if (str != snap_type_selector.get_active_text()) {
1936 snap_type_selector.set_active_text (str);
1941 switch (_snap_type) {
1942 case SnapToBeatDiv32:
1943 case SnapToBeatDiv28:
1944 case SnapToBeatDiv24:
1945 case SnapToBeatDiv20:
1946 case SnapToBeatDiv16:
1947 case SnapToBeatDiv14:
1948 case SnapToBeatDiv12:
1949 case SnapToBeatDiv10:
1950 case SnapToBeatDiv8:
1951 case SnapToBeatDiv7:
1952 case SnapToBeatDiv6:
1953 case SnapToBeatDiv5:
1954 case SnapToBeatDiv4:
1955 case SnapToBeatDiv3:
1956 case SnapToBeatDiv2:
1957 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
1958 update_tempo_based_rulers ();
1961 case SnapToRegionStart:
1962 case SnapToRegionEnd:
1963 case SnapToRegionSync:
1964 case SnapToRegionBoundary:
1965 build_region_boundary_cache ();
1973 SnapChanged (); /* EMIT SIGNAL */
1977 Editor::set_snap_mode (SnapMode mode)
1980 string str = snap_mode_strings[(int)mode];
1982 if (str != snap_mode_selector.get_active_text ()) {
1983 snap_mode_selector.set_active_text (str);
1989 Editor::set_edit_point_preference (EditPoint ep, bool force)
1991 bool changed = (_edit_point != ep);
1994 string str = edit_point_strings[(int)ep];
1996 if (str != edit_point_selector.get_active_text ()) {
1997 edit_point_selector.set_active_text (str);
2000 set_canvas_cursor ();
2002 if (!force && !changed) {
2006 const char* action=NULL;
2008 switch (_edit_point) {
2009 case EditAtPlayhead:
2010 action = "edit-at-playhead";
2012 case EditAtSelectedMarker:
2013 action = "edit-at-marker";
2016 action = "edit-at-mouse";
2020 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2022 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2026 bool in_track_canvas;
2028 if (!mouse_frame (foo, in_track_canvas)) {
2029 in_track_canvas = false;
2032 reset_canvas_action_sensitivity (in_track_canvas);
2038 Editor::set_state (const XMLNode& node, int /*version*/)
2040 const XMLProperty* prop;
2042 int x, y, xoff, yoff;
2045 if ((prop = node.property ("id")) != 0) {
2046 _id = prop->value ();
2049 g.base_width = default_width;
2050 g.base_height = default_height;
2056 if ((geometry = find_named_node (node, "geometry")) != 0) {
2060 if ((prop = geometry->property("x_size")) == 0) {
2061 prop = geometry->property ("x-size");
2064 g.base_width = atoi(prop->value());
2066 if ((prop = geometry->property("y_size")) == 0) {
2067 prop = geometry->property ("y-size");
2070 g.base_height = atoi(prop->value());
2073 if ((prop = geometry->property ("x_pos")) == 0) {
2074 prop = geometry->property ("x-pos");
2077 x = atoi (prop->value());
2080 if ((prop = geometry->property ("y_pos")) == 0) {
2081 prop = geometry->property ("y-pos");
2084 y = atoi (prop->value());
2087 if ((prop = geometry->property ("x_off")) == 0) {
2088 prop = geometry->property ("x-off");
2091 xoff = atoi (prop->value());
2093 if ((prop = geometry->property ("y_off")) == 0) {
2094 prop = geometry->property ("y-off");
2097 yoff = atoi (prop->value());
2101 set_default_size (g.base_width, g.base_height);
2104 if (_session && (prop = node.property ("playhead"))) {
2106 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2107 playhead_cursor->set_position (pos);
2109 playhead_cursor->set_position (0);
2112 if ((prop = node.property ("mixer-width"))) {
2113 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2116 if ((prop = node.property ("zoom-focus"))) {
2117 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2120 if ((prop = node.property ("zoom"))) {
2121 reset_zoom (PBD::atof (prop->value()));
2123 reset_zoom (frames_per_unit);
2126 if ((prop = node.property ("snap-to"))) {
2127 set_snap_to ((SnapType) atoi (prop->value()));
2130 if ((prop = node.property ("snap-mode"))) {
2131 set_snap_mode ((SnapMode) atoi (prop->value()));
2134 if ((prop = node.property ("mouse-mode"))) {
2135 MouseMode m = str2mousemode(prop->value());
2136 set_mouse_mode (m, true);
2138 set_mouse_mode (MouseObject, true);
2141 if ((prop = node.property ("left-frame")) != 0){
2143 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2144 reset_x_origin (pos);
2148 if ((prop = node.property ("y-origin")) != 0) {
2149 reset_y_origin (atof (prop->value ()));
2152 if ((prop = node.property ("internal-edit"))) {
2153 bool yn = string_is_affirmative (prop->value());
2154 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2156 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2157 tact->set_active (!yn);
2158 tact->set_active (yn);
2162 if ((prop = node.property ("join-object-range"))) {
2163 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2166 if ((prop = node.property ("edit-point"))) {
2167 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2170 if ((prop = node.property ("show-measures"))) {
2171 bool yn = string_is_affirmative (prop->value());
2172 _show_measures = yn;
2173 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2175 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2176 /* do it twice to force the change */
2177 tact->set_active (!yn);
2178 tact->set_active (yn);
2182 if ((prop = node.property ("follow-playhead"))) {
2183 bool yn = string_is_affirmative (prop->value());
2184 set_follow_playhead (yn);
2185 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2187 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2188 if (tact->get_active() != yn) {
2189 tact->set_active (yn);
2194 if ((prop = node.property ("stationary-playhead"))) {
2195 bool yn = (prop->value() == "yes");
2196 set_stationary_playhead (yn);
2197 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2199 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2200 if (tact->get_active() != yn) {
2201 tact->set_active (yn);
2206 if ((prop = node.property ("region-list-sort-type"))) {
2207 RegionListSortType st;
2208 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2211 if ((prop = node.property ("xfades-visible"))) {
2212 bool yn = string_is_affirmative (prop->value());
2213 _xfade_visibility = !yn;
2214 // set_xfade_visibility (yn);
2217 if ((prop = node.property ("show-editor-mixer"))) {
2219 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2222 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2223 bool yn = string_is_affirmative (prop->value());
2225 /* do it twice to force the change */
2227 tact->set_active (!yn);
2228 tact->set_active (yn);
2231 if ((prop = node.property ("show-editor-list"))) {
2233 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2236 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2237 bool yn = string_is_affirmative (prop->value());
2239 /* do it twice to force the change */
2241 tact->set_active (!yn);
2242 tact->set_active (yn);
2245 if ((prop = node.property (X_("editor-list-page")))) {
2246 the_notebook.set_current_page (atoi (prop->value ()));
2249 if ((prop = node.property (X_("show-marker-lines")))) {
2250 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2252 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2253 bool yn = string_is_affirmative (prop->value ());
2255 tact->set_active (!yn);
2256 tact->set_active (yn);
2259 XMLNodeList children = node.children ();
2260 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2261 selection->set_state (**i, Stateful::current_state_version);
2262 _regions->set_state (**i);
2269 Editor::get_state ()
2271 XMLNode* node = new XMLNode ("Editor");
2274 _id.print (buf, sizeof (buf));
2275 node->add_property ("id", buf);
2277 if (is_realized()) {
2278 Glib::RefPtr<Gdk::Window> win = get_window();
2280 int x, y, xoff, yoff, width, height;
2281 win->get_root_origin(x, y);
2282 win->get_position(xoff, yoff);
2283 win->get_size(width, height);
2285 XMLNode* geometry = new XMLNode ("geometry");
2287 snprintf(buf, sizeof(buf), "%d", width);
2288 geometry->add_property("x-size", string(buf));
2289 snprintf(buf, sizeof(buf), "%d", height);
2290 geometry->add_property("y-size", string(buf));
2291 snprintf(buf, sizeof(buf), "%d", x);
2292 geometry->add_property("x-pos", string(buf));
2293 snprintf(buf, sizeof(buf), "%d", y);
2294 geometry->add_property("y-pos", string(buf));
2295 snprintf(buf, sizeof(buf), "%d", xoff);
2296 geometry->add_property("x-off", string(buf));
2297 snprintf(buf, sizeof(buf), "%d", yoff);
2298 geometry->add_property("y-off", string(buf));
2299 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2300 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2301 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2302 geometry->add_property("edit-vertical-pane-pos", string(buf));
2304 node->add_child_nocopy (*geometry);
2307 maybe_add_mixer_strip_width (*node);
2309 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2310 node->add_property ("zoom-focus", buf);
2311 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2312 node->add_property ("zoom", buf);
2313 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2314 node->add_property ("snap-to", buf);
2315 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2316 node->add_property ("snap-mode", buf);
2318 node->add_property ("edit-point", enum_2_string (_edit_point));
2320 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2321 node->add_property ("playhead", buf);
2322 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2323 node->add_property ("left-frame", buf);
2324 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2325 node->add_property ("y-origin", buf);
2327 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2328 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2329 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2330 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2331 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2332 node->add_property ("mouse-mode", enum2str(mouse_mode));
2333 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2334 node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2336 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2338 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2339 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2342 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2344 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2345 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2348 snprintf (buf, sizeof (buf), "%d", the_notebook.get_current_page ());
2349 node->add_property (X_("editor-list-page"), buf);
2351 node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no");
2353 node->add_child_nocopy (selection->get_state ());
2354 node->add_child_nocopy (_regions->get_state ());
2361 /** @param y y offset from the top of all trackviews.
2362 * @return pair: TimeAxisView that y is over, layer index.
2363 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2364 * in stacked region display mode, otherwise 0.
2366 std::pair<TimeAxisView *, layer_t>
2367 Editor::trackview_by_y_position (double y)
2369 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2371 std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
2377 return std::make_pair ( (TimeAxisView *) 0, 0);
2380 /** Snap a position to the grid, if appropriate, taking into account current
2381 * grid settings and also the state of any snap modifier keys that may be pressed.
2382 * @param start Position to snap.
2383 * @param event Event to get current key modifier information from, or 0.
2386 Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2388 if (!_session || !event) {
2392 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2393 if (_snap_mode == SnapOff) {
2394 snap_to_internal (start, direction, for_mark);
2397 if (_snap_mode != SnapOff) {
2398 snap_to_internal (start, direction, for_mark);
2404 Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark)
2406 if (!_session || _snap_mode == SnapOff) {
2410 snap_to_internal (start, direction, for_mark);
2414 Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /*for_mark*/)
2416 const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2417 framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2419 switch (_snap_type) {
2420 case SnapToTimecodeFrame:
2421 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2422 start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2424 start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2428 case SnapToTimecodeSeconds:
2429 if (_session->config.get_timecode_offset_negative()) {
2430 start += _session->config.get_timecode_offset ();
2432 start -= _session->config.get_timecode_offset ();
2434 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2435 start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2437 start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2440 if (_session->config.get_timecode_offset_negative()) {
2441 start -= _session->config.get_timecode_offset ();
2443 start += _session->config.get_timecode_offset ();
2447 case SnapToTimecodeMinutes:
2448 if (_session->config.get_timecode_offset_negative()) {
2449 start += _session->config.get_timecode_offset ();
2451 start -= _session->config.get_timecode_offset ();
2453 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2454 start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2456 start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2458 if (_session->config.get_timecode_offset_negative()) {
2459 start -= _session->config.get_timecode_offset ();
2461 start += _session->config.get_timecode_offset ();
2465 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2471 Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark)
2473 const framepos_t one_second = _session->frame_rate();
2474 const framepos_t one_minute = _session->frame_rate() * 60;
2475 framepos_t presnap = start;
2479 switch (_snap_type) {
2480 case SnapToTimecodeFrame:
2481 case SnapToTimecodeSeconds:
2482 case SnapToTimecodeMinutes:
2483 return timecode_snap_to_internal (start, direction, for_mark);
2486 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2487 start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2489 start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2494 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2495 start = (framepos_t) ceil ((double) start / one_second) * one_second;
2497 start = (framepos_t) floor ((double) start / one_second) * one_second;
2502 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2503 start = (framepos_t) ceil ((double) start / one_minute) * one_minute;
2505 start = (framepos_t) floor ((double) start / one_minute) * one_minute;
2510 start = _session->tempo_map().round_to_bar (start, direction);
2514 start = _session->tempo_map().round_to_beat (start, direction);
2517 case SnapToBeatDiv32:
2518 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2520 case SnapToBeatDiv28:
2521 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2523 case SnapToBeatDiv24:
2524 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2526 case SnapToBeatDiv20:
2527 start = _session->tempo_map().round_to_beat_subdivision (start, 20, direction);
2529 case SnapToBeatDiv16:
2530 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2532 case SnapToBeatDiv14:
2533 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2535 case SnapToBeatDiv12:
2536 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2538 case SnapToBeatDiv10:
2539 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2541 case SnapToBeatDiv8:
2542 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2544 case SnapToBeatDiv7:
2545 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2547 case SnapToBeatDiv6:
2548 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2550 case SnapToBeatDiv5:
2551 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2553 case SnapToBeatDiv4:
2554 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2556 case SnapToBeatDiv3:
2557 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2559 case SnapToBeatDiv2:
2560 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2568 _session->locations()->marks_either_side (start, before, after);
2570 if (before == max_framepos) {
2572 } else if (after == max_framepos) {
2574 } else if (before != max_framepos && after != max_framepos) {
2575 /* have before and after */
2576 if ((start - before) < (after - start)) {
2585 case SnapToRegionStart:
2586 case SnapToRegionEnd:
2587 case SnapToRegionSync:
2588 case SnapToRegionBoundary:
2589 if (!region_boundary_cache.empty()) {
2591 vector<framepos_t>::iterator prev = region_boundary_cache.end ();
2592 vector<framepos_t>::iterator next = region_boundary_cache.end ();
2594 if (direction > 0) {
2595 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2597 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2600 if (next != region_boundary_cache.begin ()) {
2605 framepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2606 framepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2608 if (start > (p + n) / 2) {
2617 switch (_snap_mode) {
2623 if (presnap > start) {
2624 if (presnap > (start + unit_to_frame(snap_threshold))) {
2628 } else if (presnap < start) {
2629 if (presnap < (start - unit_to_frame(snap_threshold))) {
2635 /* handled at entry */
2643 Editor::setup_toolbar ()
2647 /* Mode Buttons (tool selection) */
2649 mouse_move_button.set_relief(Gtk::RELIEF_NONE);
2650 mouse_select_button.set_relief(Gtk::RELIEF_NONE);
2651 mouse_gain_button.set_relief(Gtk::RELIEF_NONE);
2652 mouse_zoom_button.set_relief(Gtk::RELIEF_NONE);
2653 mouse_timefx_button.set_relief(Gtk::RELIEF_NONE);
2654 mouse_audition_button.set_relief(Gtk::RELIEF_NONE);
2655 // internal_edit_button.set_relief(Gtk::RELIEF_NONE);
2656 join_object_range_button.set_relief(Gtk::RELIEF_NONE);
2658 HBox* mode_box = manage(new HBox);
2659 mode_box->set_border_width (2);
2660 mode_box->set_spacing(4);
2662 /* table containing mode buttons */
2664 HBox* mouse_mode_button_box = manage (new HBox ());
2666 if (Profile->get_sae()) {
2667 mouse_mode_button_box->pack_start (mouse_move_button);
2669 mouse_mode_button_box->pack_start (mouse_move_button);
2670 mouse_mode_button_box->pack_start (join_object_range_button);
2671 mouse_mode_button_box->pack_start (mouse_select_button);
2674 mouse_mode_button_box->pack_start (mouse_zoom_button);
2676 if (!Profile->get_sae()) {
2677 mouse_mode_button_box->pack_start (mouse_gain_button);
2680 mouse_mode_button_box->pack_start (mouse_timefx_button);
2681 mouse_mode_button_box->pack_start (mouse_audition_button);
2682 mouse_mode_button_box->pack_start (internal_edit_button);
2684 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2685 if (!Profile->get_sae()) {
2686 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2688 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2690 edit_mode_selector.set_name ("EditModeSelector");
2691 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2692 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2694 mode_box->pack_start (edit_mode_selector);
2695 mode_box->pack_start (*mouse_mode_button_box);
2697 _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2698 _mouse_mode_tearoff->set_name ("MouseModeBase");
2699 _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2701 if (Profile->get_sae()) {
2702 _mouse_mode_tearoff->set_can_be_torn_off (false);
2705 _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2706 &_mouse_mode_tearoff->tearoff_window()));
2707 _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2708 &_mouse_mode_tearoff->tearoff_window(), 1));
2709 _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2710 &_mouse_mode_tearoff->tearoff_window()));
2711 _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2712 &_mouse_mode_tearoff->tearoff_window(), 1));
2714 mouse_move_button.set_mode (false);
2715 mouse_select_button.set_mode (false);
2716 mouse_gain_button.set_mode (false);
2717 mouse_zoom_button.set_mode (false);
2718 mouse_timefx_button.set_mode (false);
2719 mouse_audition_button.set_mode (false);
2720 join_object_range_button.set_mode (false);
2722 mouse_move_button.set_name ("MouseModeButton");
2723 mouse_select_button.set_name ("MouseModeButton");
2724 mouse_gain_button.set_name ("MouseModeButton");
2725 mouse_zoom_button.set_name ("MouseModeButton");
2726 mouse_timefx_button.set_name ("MouseModeButton");
2727 mouse_audition_button.set_name ("MouseModeButton");
2728 internal_edit_button.set_name ("MouseModeButton");
2729 join_object_range_button.set_name ("MouseModeButton");
2731 mouse_move_button.unset_flags (CAN_FOCUS);
2732 mouse_select_button.unset_flags (CAN_FOCUS);
2733 mouse_gain_button.unset_flags (CAN_FOCUS);
2734 mouse_zoom_button.unset_flags (CAN_FOCUS);
2735 mouse_timefx_button.unset_flags (CAN_FOCUS);
2736 mouse_audition_button.unset_flags (CAN_FOCUS);
2737 internal_edit_button.unset_flags (CAN_FOCUS);
2738 join_object_range_button.unset_flags (CAN_FOCUS);
2742 _zoom_box.set_spacing (1);
2743 _zoom_box.set_border_width (0);
2745 zoom_in_button.set_name ("EditorTimeButton");
2746 zoom_in_button.set_image (*(manage (new Image (Stock::ZOOM_IN, Gtk::ICON_SIZE_MENU))));
2747 zoom_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), false));
2749 zoom_out_button.set_name ("EditorTimeButton");
2750 zoom_out_button.set_image (*(manage (new Image (Stock::ZOOM_OUT, Gtk::ICON_SIZE_MENU))));
2751 zoom_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), true));
2753 zoom_out_full_button.set_name ("EditorTimeButton");
2754 zoom_out_full_button.set_image (*(manage (new Image (Stock::ZOOM_100, Gtk::ICON_SIZE_MENU))));
2755 zoom_out_full_button.signal_clicked().connect (sigc::mem_fun(*this, &Editor::temporal_zoom_session));
2757 zoom_focus_selector.set_name ("ZoomFocusSelector");
2758 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
2759 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2761 _zoom_box.pack_start (zoom_out_button, false, false);
2762 _zoom_box.pack_start (zoom_in_button, false, false);
2763 _zoom_box.pack_start (zoom_out_full_button, false, false);
2765 _zoom_box.pack_start (zoom_focus_selector);
2767 /* Track zoom buttons */
2768 tav_expand_button.set_name ("TrackHeightButton");
2769 tav_expand_button.set_size_request(-1,20);
2770 tav_expand_button.add (*(manage (new Image (::get_icon("tav_exp")))));
2771 tav_expand_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), true));
2773 tav_shrink_button.set_name ("TrackHeightButton");
2774 tav_shrink_button.set_size_request(-1,20);
2775 tav_shrink_button.add (*(manage (new Image (::get_icon("tav_shrink")))));
2776 tav_shrink_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), false));
2778 _zoom_box.pack_start (tav_shrink_button);
2779 _zoom_box.pack_start (tav_expand_button);
2781 _zoom_tearoff = manage (new TearOff (_zoom_box));
2783 _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2784 &_zoom_tearoff->tearoff_window()));
2785 _zoom_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2786 &_zoom_tearoff->tearoff_window(), 0));
2787 _zoom_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2788 &_zoom_tearoff->tearoff_window()));
2789 _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2790 &_zoom_tearoff->tearoff_window(), 0));
2792 snap_box.set_spacing (1);
2793 snap_box.set_border_width (2);
2795 snap_type_selector.set_name ("SnapTypeSelector");
2796 set_popdown_strings (snap_type_selector, snap_type_strings, true);
2797 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2799 snap_mode_selector.set_name ("SnapModeSelector");
2800 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
2801 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2803 edit_point_selector.set_name ("EditPointSelector");
2804 set_popdown_strings (edit_point_selector, edit_point_strings, true);
2805 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2807 snap_box.pack_start (snap_mode_selector, false, false);
2808 snap_box.pack_start (snap_type_selector, false, false);
2809 snap_box.pack_start (edit_point_selector, false, false);
2813 HBox *nudge_box = manage (new HBox);
2814 nudge_box->set_spacing(1);
2815 nudge_box->set_border_width (2);
2817 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2818 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2820 nudge_box->pack_start (nudge_backward_button, false, false);
2821 nudge_box->pack_start (nudge_forward_button, false, false);
2822 nudge_box->pack_start (nudge_clock, false, false);
2825 /* Pack everything in... */
2827 HBox* hbox = manage (new HBox);
2828 hbox->set_spacing(10);
2830 _tools_tearoff = manage (new TearOff (*hbox));
2831 _tools_tearoff->set_name ("MouseModeBase");
2832 _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
2834 if (Profile->get_sae()) {
2835 _tools_tearoff->set_can_be_torn_off (false);
2838 _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2839 &_tools_tearoff->tearoff_window()));
2840 _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2841 &_tools_tearoff->tearoff_window(), 0));
2842 _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2843 &_tools_tearoff->tearoff_window()));
2844 _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2845 &_tools_tearoff->tearoff_window(), 0));
2847 toolbar_hbox.set_spacing (10);
2848 toolbar_hbox.set_border_width (1);
2850 toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
2851 toolbar_hbox.pack_start (*_zoom_tearoff, false, false);
2852 toolbar_hbox.pack_start (*_tools_tearoff, false, false);
2854 hbox->pack_start (snap_box, false, false);
2855 hbox->pack_start (*nudge_box, false, false);
2856 hbox->pack_start (panic_box, false, false);
2860 toolbar_base.set_name ("ToolBarBase");
2861 toolbar_base.add (toolbar_hbox);
2863 _toolbar_viewport.add (toolbar_base);
2864 /* stick to the required height but allow width to vary if there's not enough room */
2865 _toolbar_viewport.set_size_request (1, -1);
2867 toolbar_frame.set_shadow_type (SHADOW_OUT);
2868 toolbar_frame.set_name ("BaseFrame");
2869 toolbar_frame.add (_toolbar_viewport);
2871 DPIReset.connect (sigc::mem_fun (*this, &Editor::resize_text_widgets));
2875 Editor::setup_tooltips ()
2877 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
2878 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain"));
2879 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
2880 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
2881 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2882 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
2883 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
2884 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
2885 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
2886 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
2887 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
2888 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
2889 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
2890 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
2891 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
2892 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
2893 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
2894 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
2895 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
2896 ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
2897 ARDOUR_UI::instance()->set_tip (midi_panic_button, _("Send note off and reset controller messages on all MIDI channels"));
2898 ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode"));
2902 Editor::midi_panic ()
2904 cerr << "MIDI panic\n";
2907 _session->midi_panic();
2912 Editor::setup_midi_toolbar ()
2916 /* Midi sound notes */
2917 midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
2918 midi_sound_notes.set_relief(Gtk::RELIEF_NONE);
2919 midi_sound_notes.unset_flags (CAN_FOCUS);
2923 act = ActionManager::get_action (X_("MIDI"), X_("panic"));
2924 midi_panic_button.set_name("MidiPanicButton");
2925 act->connect_proxy (midi_panic_button);
2927 panic_box.pack_start (midi_sound_notes , true, true);
2928 panic_box.pack_start (midi_panic_button, true, true);
2932 Editor::convert_drop_to_paths (
2933 vector<string>& paths,
2934 const RefPtr<Gdk::DragContext>& /*context*/,
2937 const SelectionData& data,
2941 if (_session == 0) {
2945 vector<string> uris = data.get_uris();
2949 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
2950 are actually URI lists. So do it by hand.
2953 if (data.get_target() != "text/plain") {
2957 /* Parse the "uri-list" format that Nautilus provides,
2958 where each pathname is delimited by \r\n.
2960 THERE MAY BE NO NULL TERMINATING CHAR!!!
2963 string txt = data.get_text();
2967 p = (const char *) malloc (txt.length() + 1);
2968 txt.copy ((char *) p, txt.length(), 0);
2969 ((char*)p)[txt.length()] = '\0';
2975 while (g_ascii_isspace (*p))
2979 while (*q && (*q != '\n') && (*q != '\r')) {
2986 while (q > p && g_ascii_isspace (*q))
2991 uris.push_back (string (p, q - p + 1));
2995 p = strchr (p, '\n');
3007 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3009 if ((*i).substr (0,7) == "file://") {
3012 PBD::url_decode (p);
3014 // scan forward past three slashes
3016 string::size_type slashcnt = 0;
3017 string::size_type n = 0;
3018 string::iterator x = p.begin();
3020 while (slashcnt < 3 && x != p.end()) {
3023 } else if (slashcnt == 3) {
3030 if (slashcnt != 3 || x == p.end()) {
3031 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3035 paths.push_back (p.substr (n - 1));
3043 Editor::new_tempo_section ()
3049 Editor::map_transport_state ()
3051 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3053 if (_session && _session->transport_stopped()) {
3054 have_pending_keyboard_selection = false;
3057 update_loop_range_view (true);
3062 Editor::State::State (PublicEditor const * e)
3064 selection = new Selection (e);
3067 Editor::State::~State ()
3073 Editor::begin_reversible_command (string name)
3076 _session->begin_reversible_command (name);
3081 Editor::commit_reversible_command ()
3084 _session->commit_reversible_command ();
3089 Editor::set_route_group_solo (Route& route, bool yn)
3091 RouteGroup *route_group;
3093 if ((route_group = route.route_group()) != 0) {
3094 route_group->apply (&Route::set_solo, yn, this);
3096 route.set_solo (yn, this);
3101 Editor::set_route_group_mute (Route& route, bool yn)
3103 RouteGroup *route_group = 0;
3105 if ((route_group = route.route_group()) != 0) {
3106 route_group->apply (&Route::set_mute, yn, this);
3108 route.set_mute (yn, this);
3113 Editor::history_changed ()
3117 if (undo_action && _session) {
3118 if (_session->undo_depth() == 0) {
3121 label = string_compose(_("Undo (%1)"), _session->next_undo());
3123 undo_action->property_label() = label;
3126 if (redo_action && _session) {
3127 if (_session->redo_depth() == 0) {
3130 label = string_compose(_("Redo (%1)"), _session->next_redo());
3132 redo_action->property_label() = label;
3137 Editor::duplicate_dialog (bool with_dialog)
3141 if (mouse_mode == MouseRange) {
3142 if (selection->time.length() == 0) {
3147 RegionSelection rs = get_regions_from_selection_and_entered ();
3149 if (mouse_mode != MouseRange && rs.empty()) {
3155 ArdourDialog win (_("Duplicate"));
3156 Label label (_("Number of duplications:"));
3157 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3158 SpinButton spinner (adjustment, 0.0, 1);
3161 win.get_vbox()->set_spacing (12);
3162 win.get_vbox()->pack_start (hbox);
3163 hbox.set_border_width (6);
3164 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3166 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3167 place, visually. so do this by hand.
3170 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3171 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3172 spinner.grab_focus();
3178 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3179 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3180 win.set_default_response (RESPONSE_ACCEPT);
3182 win.set_position (WIN_POS_MOUSE);
3184 spinner.grab_focus ();
3186 switch (win.run ()) {
3187 case RESPONSE_ACCEPT:
3193 times = adjustment.get_value();
3196 if (mouse_mode == MouseRange) {
3197 duplicate_selection (times);
3199 duplicate_some_regions (rs, times);
3204 Editor::show_verbose_canvas_cursor ()
3206 verbose_canvas_cursor->raise_to_top();
3207 verbose_canvas_cursor->show();
3208 verbose_cursor_visible = true;
3212 Editor::hide_verbose_canvas_cursor ()
3214 verbose_canvas_cursor->hide();
3215 verbose_cursor_visible = false;
3219 Editor::clamp_verbose_cursor_x (double x)
3224 x = min (_canvas_width - 200.0, x);
3230 Editor::clamp_verbose_cursor_y (double y)
3232 if (y < canvas_timebars_vsize) {
3233 y = canvas_timebars_vsize;
3235 y = min (_canvas_height - 50, y);
3241 Editor::show_verbose_canvas_cursor_with (const string & txt, int32_t xoffset, int32_t yoffset)
3243 verbose_canvas_cursor->property_text() = txt.c_str();
3248 track_canvas->get_pointer (x, y);
3249 track_canvas->window_to_world (x, y, wx, wy);
3254 /* don't get too close to the edge */
3255 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (wx);
3256 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (wy);
3258 show_verbose_canvas_cursor ();
3262 Editor::set_verbose_canvas_cursor (const string & txt, double x, double y)
3264 verbose_canvas_cursor->property_text() = txt.c_str();
3265 /* don't get too close to the edge */
3266 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (x);
3267 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (y);
3271 Editor::set_verbose_canvas_cursor_text (const string & txt)
3273 verbose_canvas_cursor->property_text() = txt.c_str();
3277 Editor::set_edit_mode (EditMode m)
3279 Config->set_edit_mode (m);
3283 Editor::cycle_edit_mode ()
3285 switch (Config->get_edit_mode()) {
3287 if (Profile->get_sae()) {
3288 Config->set_edit_mode (Lock);
3290 Config->set_edit_mode (Splice);
3294 Config->set_edit_mode (Lock);
3297 Config->set_edit_mode (Slide);
3303 Editor::edit_mode_selection_done ()
3305 string s = edit_mode_selector.get_active_text ();
3308 Config->set_edit_mode (string_to_edit_mode (s));
3313 Editor::snap_type_selection_done ()
3315 string choice = snap_type_selector.get_active_text();
3316 SnapType snaptype = SnapToBeat;
3318 if (choice == _("Beats/2")) {
3319 snaptype = SnapToBeatDiv2;
3320 } else if (choice == _("Beats/3")) {
3321 snaptype = SnapToBeatDiv3;
3322 } else if (choice == _("Beats/4")) {
3323 snaptype = SnapToBeatDiv4;
3324 } else if (choice == _("Beats/5")) {
3325 snaptype = SnapToBeatDiv5;
3326 } else if (choice == _("Beats/6")) {
3327 snaptype = SnapToBeatDiv6;
3328 } else if (choice == _("Beats/7")) {
3329 snaptype = SnapToBeatDiv7;
3330 } else if (choice == _("Beats/8")) {
3331 snaptype = SnapToBeatDiv8;
3332 } else if (choice == _("Beats/10")) {
3333 snaptype = SnapToBeatDiv10;
3334 } else if (choice == _("Beats/12")) {
3335 snaptype = SnapToBeatDiv12;
3336 } else if (choice == _("Beats/14")) {
3337 snaptype = SnapToBeatDiv14;
3338 } else if (choice == _("Beats/16")) {
3339 snaptype = SnapToBeatDiv16;
3340 } else if (choice == _("Beats/20")) {
3341 snaptype = SnapToBeatDiv20;
3342 } else if (choice == _("Beats/24")) {
3343 snaptype = SnapToBeatDiv24;
3344 } else if (choice == _("Beats/28")) {
3345 snaptype = SnapToBeatDiv28;
3346 } else if (choice == _("Beats/32")) {
3347 snaptype = SnapToBeatDiv32;
3348 } else if (choice == _("Beats")) {
3349 snaptype = SnapToBeat;
3350 } else if (choice == _("Bars")) {
3351 snaptype = SnapToBar;
3352 } else if (choice == _("Marks")) {
3353 snaptype = SnapToMark;
3354 } else if (choice == _("Region starts")) {
3355 snaptype = SnapToRegionStart;
3356 } else if (choice == _("Region ends")) {
3357 snaptype = SnapToRegionEnd;
3358 } else if (choice == _("Region bounds")) {
3359 snaptype = SnapToRegionBoundary;
3360 } else if (choice == _("Region syncs")) {
3361 snaptype = SnapToRegionSync;
3362 } else if (choice == _("CD Frames")) {
3363 snaptype = SnapToCDFrame;
3364 } else if (choice == _("Timecode Frames")) {
3365 snaptype = SnapToTimecodeFrame;
3366 } else if (choice == _("Timecode Seconds")) {
3367 snaptype = SnapToTimecodeSeconds;
3368 } else if (choice == _("Timecode Minutes")) {
3369 snaptype = SnapToTimecodeMinutes;
3370 } else if (choice == _("Seconds")) {
3371 snaptype = SnapToSeconds;
3372 } else if (choice == _("Minutes")) {
3373 snaptype = SnapToMinutes;
3376 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3378 ract->set_active ();
3383 Editor::snap_mode_selection_done ()
3385 string choice = snap_mode_selector.get_active_text();
3386 SnapMode mode = SnapNormal;
3388 if (choice == _("No Grid")) {
3390 } else if (choice == _("Grid")) {
3392 } else if (choice == _("Magnetic")) {
3393 mode = SnapMagnetic;
3396 RefPtr<RadioAction> ract = snap_mode_action (mode);
3399 ract->set_active (true);
3404 Editor::cycle_edit_point (bool with_marker)
3406 switch (_edit_point) {
3408 set_edit_point_preference (EditAtPlayhead);
3410 case EditAtPlayhead:
3412 set_edit_point_preference (EditAtSelectedMarker);
3414 set_edit_point_preference (EditAtMouse);
3417 case EditAtSelectedMarker:
3418 set_edit_point_preference (EditAtMouse);
3424 Editor::edit_point_selection_done ()
3426 string choice = edit_point_selector.get_active_text();
3427 EditPoint ep = EditAtSelectedMarker;
3429 if (choice == _("Marker")) {
3430 set_edit_point_preference (EditAtSelectedMarker);
3431 } else if (choice == _("Playhead")) {
3432 set_edit_point_preference (EditAtPlayhead);
3434 set_edit_point_preference (EditAtMouse);
3437 RefPtr<RadioAction> ract = edit_point_action (ep);
3440 ract->set_active (true);
3445 Editor::zoom_focus_selection_done ()
3447 string choice = zoom_focus_selector.get_active_text();
3448 ZoomFocus focus_type = ZoomFocusLeft;
3450 if (choice == _("Left")) {
3451 focus_type = ZoomFocusLeft;
3452 } else if (choice == _("Right")) {
3453 focus_type = ZoomFocusRight;
3454 } else if (choice == _("Center")) {
3455 focus_type = ZoomFocusCenter;
3456 } else if (choice == _("Playhead")) {
3457 focus_type = ZoomFocusPlayhead;
3458 } else if (choice == _("Mouse")) {
3459 focus_type = ZoomFocusMouse;
3460 } else if (choice == _("Edit point")) {
3461 focus_type = ZoomFocusEdit;
3464 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3467 ract->set_active ();
3472 Editor::edit_controls_button_release (GdkEventButton* ev)
3474 if (Keyboard::is_context_menu_event (ev)) {
3475 ARDOUR_UI::instance()->add_route (this);
3481 Editor::mouse_select_button_release (GdkEventButton* ev)
3483 /* this handles just right-clicks */
3485 if (ev->button != 3) {
3493 Editor::set_zoom_focus (ZoomFocus f)
3495 string str = zoom_focus_strings[(int)f];
3497 if (str != zoom_focus_selector.get_active_text()) {
3498 zoom_focus_selector.set_active_text (str);
3501 if (zoom_focus != f) {
3504 ZoomFocusChanged (); /* EMIT_SIGNAL */
3511 Editor::ensure_float (Window& win)
3513 win.set_transient_for (*this);
3517 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3519 /* recover or initialize pane positions. do this here rather than earlier because
3520 we don't want the positions to change the child allocations, which they seem to do.
3526 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3538 width = default_width;
3539 height = default_height;
3541 if ((geometry = find_named_node (*node, "geometry")) != 0) {
3543 prop = geometry->property ("x-size");
3545 width = atoi (prop->value());
3547 prop = geometry->property ("y-size");
3549 height = atoi (prop->value());
3553 if (which == static_cast<Paned*> (&edit_pane)) {
3555 if (done & Horizontal) {
3559 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3560 /* initial allocation is 90% to canvas, 10% to notebook */
3561 pos = (int) floor (alloc.get_width() * 0.90f);
3562 snprintf (buf, sizeof(buf), "%d", pos);
3564 pos = atoi (prop->value());
3567 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3568 edit_pane.set_position (pos);
3569 pre_maximal_horizontal_pane_position = pos;
3572 done = (Pane) (done | Horizontal);
3574 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3576 if (done & Vertical) {
3580 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3581 /* initial allocation is 90% to canvas, 10% to summary */
3582 pos = (int) floor (alloc.get_height() * 0.90f);
3583 snprintf (buf, sizeof(buf), "%d", pos);
3585 pos = atoi (prop->value());
3588 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3589 editor_summary_pane.set_position (pos);
3590 pre_maximal_vertical_pane_position = pos;
3593 done = (Pane) (done | Vertical);
3598 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3600 if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) {
3601 top_hbox.remove (toolbar_frame);
3606 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3608 if (toolbar_frame.get_parent() == 0) {
3609 top_hbox.pack_end (toolbar_frame);
3614 Editor::set_show_measures (bool yn)
3616 if (_show_measures != yn) {
3619 if ((_show_measures = yn) == true) {
3621 tempo_lines->show();
3629 Editor::toggle_follow_playhead ()
3631 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3633 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3634 set_follow_playhead (tact->get_active());
3639 Editor::set_follow_playhead (bool yn)
3641 if (_follow_playhead != yn) {
3642 if ((_follow_playhead = yn) == true) {
3644 reset_x_origin_to_follow_playhead ();
3651 Editor::toggle_stationary_playhead ()
3653 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3655 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3656 set_stationary_playhead (tact->get_active());
3661 Editor::set_stationary_playhead (bool yn)
3663 if (_stationary_playhead != yn) {
3664 if ((_stationary_playhead = yn) == true) {
3666 // FIXME need a 3.0 equivalent of this 2.X call
3667 // update_current_screen ();
3674 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3676 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3678 xfade->set_active (!xfade->active());
3683 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3685 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3687 xfade->set_follow_overlap (!xfade->following_overlap());
3692 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3694 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3700 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3704 switch (cew.run ()) {
3705 case RESPONSE_ACCEPT:
3712 PropertyChange all_crossfade_properties;
3713 all_crossfade_properties.add (ARDOUR::Properties::active);
3714 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3715 xfade->PropertyChanged (all_crossfade_properties);
3719 Editor::playlist_selector () const
3721 return *_playlist_selector;
3725 Editor::get_grid_type_as_beats (bool& success, framepos_t position)
3729 switch (_snap_type) {
3734 case SnapToBeatDiv32:
3737 case SnapToBeatDiv28:
3740 case SnapToBeatDiv24:
3743 case SnapToBeatDiv20:
3746 case SnapToBeatDiv16:
3749 case SnapToBeatDiv14:
3752 case SnapToBeatDiv12:
3755 case SnapToBeatDiv10:
3758 case SnapToBeatDiv8:
3761 case SnapToBeatDiv7:
3764 case SnapToBeatDiv6:
3767 case SnapToBeatDiv5:
3770 case SnapToBeatDiv4:
3773 case SnapToBeatDiv3:
3776 case SnapToBeatDiv2:
3782 return _session->tempo_map().meter_at (position).beats_per_bar();
3787 case SnapToTimecodeFrame:
3788 case SnapToTimecodeSeconds:
3789 case SnapToTimecodeMinutes:
3792 case SnapToRegionStart:
3793 case SnapToRegionEnd:
3794 case SnapToRegionSync:
3795 case SnapToRegionBoundary:
3805 Editor::get_nudge_distance (framepos_t pos, framecnt_t& next)
3809 ret = nudge_clock.current_duration (pos);
3810 next = ret + 1; /* XXXX fix me */
3816 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3818 ArdourDialog dialog (_("Playlist Deletion"));
3819 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3820 "If left alone, no audio files used by it will be cleaned.\n"
3821 "If deleted, audio files used by it alone by will cleaned."),
3824 dialog.set_position (WIN_POS_CENTER);
3825 dialog.get_vbox()->pack_start (label);
3829 dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT);
3830 dialog.add_button (_("Keep playlist"), RESPONSE_REJECT);
3831 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3833 switch (dialog.run ()) {
3834 case RESPONSE_ACCEPT:
3835 /* delete the playlist */
3839 case RESPONSE_REJECT:
3840 /* keep the playlist */
3852 Editor::audio_region_selection_covers (framepos_t where)
3854 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3855 if ((*a)->region()->covers (where)) {
3864 Editor::prepare_for_cleanup ()
3866 cut_buffer->clear_regions ();
3867 cut_buffer->clear_playlists ();
3869 selection->clear_regions ();
3870 selection->clear_playlists ();
3872 _regions->suspend_redisplay ();
3876 Editor::finish_cleanup ()
3878 _regions->resume_redisplay ();
3882 Editor::transport_loop_location()
3885 return _session->locations()->auto_loop_location();
3892 Editor::transport_punch_location()
3895 return _session->locations()->auto_punch_location();
3902 Editor::control_layout_scroll (GdkEventScroll* ev)
3904 if (Keyboard::some_magic_widget_has_focus()) {
3908 switch (ev->direction) {
3910 scroll_tracks_up_line ();
3914 case GDK_SCROLL_DOWN:
3915 scroll_tracks_down_line ();
3919 /* no left/right handling yet */
3927 Editor::session_state_saved (string)
3930 _snapshots->redisplay ();
3934 Editor::maximise_editing_space ()
3936 _mouse_mode_tearoff->set_visible (false);
3937 _tools_tearoff->set_visible (false);
3938 _zoom_tearoff->set_visible (false);
3940 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
3941 pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
3942 pre_maximal_editor_width = this->get_width ();
3943 pre_maximal_editor_height = this->get_height ();
3945 if (post_maximal_horizontal_pane_position == 0) {
3946 post_maximal_horizontal_pane_position = edit_pane.get_width();
3949 if (post_maximal_vertical_pane_position == 0) {
3950 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
3955 if (post_maximal_editor_width) {
3956 edit_pane.set_position (post_maximal_horizontal_pane_position -
3957 abs(post_maximal_editor_width - pre_maximal_editor_width));
3959 edit_pane.set_position (post_maximal_horizontal_pane_position);
3962 if (post_maximal_editor_height) {
3963 editor_summary_pane.set_position (post_maximal_vertical_pane_position -
3964 abs(post_maximal_editor_height - pre_maximal_editor_height));
3966 editor_summary_pane.set_position (post_maximal_vertical_pane_position);
3969 if (Config->get_keep_tearoffs()) {
3970 _mouse_mode_tearoff->set_visible (true);
3971 _tools_tearoff->set_visible (true);
3972 if (Config->get_show_zoom_tools ()) {
3973 _zoom_tearoff->set_visible (true);
3980 Editor::restore_editing_space ()
3982 // user changed width/height of panes during fullscreen
3984 if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
3985 post_maximal_horizontal_pane_position = edit_pane.get_position();
3988 if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
3989 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
3994 _mouse_mode_tearoff->set_visible (true);
3995 _tools_tearoff->set_visible (true);
3996 if (Config->get_show_zoom_tools ()) {
3997 _zoom_tearoff->set_visible (true);
3999 post_maximal_editor_width = this->get_width();
4000 post_maximal_editor_height = this->get_height();
4002 edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
4003 editor_summary_pane.set_position (pre_maximal_vertical_pane_position + abs(this->get_height() - pre_maximal_editor_height));
4007 * Make new playlists for a given track and also any others that belong
4008 * to the same active route group with the `edit' property.
4013 Editor::new_playlists (TimeAxisView* v)
4015 begin_reversible_command (_("new playlists"));
4016 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4017 _session->playlists->get (playlists);
4018 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4019 commit_reversible_command ();
4023 * Use a copy of the current playlist for a given track and also any others that belong
4024 * to the same active route group with the `edit' property.
4029 Editor::copy_playlists (TimeAxisView* v)
4031 begin_reversible_command (_("copy playlists"));
4032 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4033 _session->playlists->get (playlists);
4034 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4035 commit_reversible_command ();
4038 /** Clear the current playlist for a given track and also any others that belong
4039 * to the same active route group with the `edit' property.
4044 Editor::clear_playlists (TimeAxisView* v)
4046 begin_reversible_command (_("clear playlists"));
4047 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4048 _session->playlists->get (playlists);
4049 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4050 commit_reversible_command ();
4054 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4056 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4060 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4062 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4066 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4068 atv.clear_playlist ();
4072 Editor::on_key_press_event (GdkEventKey* ev)
4074 return key_press_focus_accelerator_handler (*this, ev);
4078 Editor::on_key_release_event (GdkEventKey* ev)
4080 return Gtk::Window::on_key_release_event (ev);
4081 // return key_press_focus_accelerator_handler (*this, ev);
4084 /** Queue up a change to the viewport x origin.
4085 * @param frame New x origin.
4088 Editor::reset_x_origin (framepos_t frame)
4090 queue_visual_change (frame);
4094 Editor::reset_y_origin (double y)
4096 queue_visual_change_y (y);
4100 Editor::reset_zoom (double fpu)
4102 queue_visual_change (fpu);
4106 Editor::reposition_and_zoom (framepos_t frame, double fpu)
4108 reset_x_origin (frame);
4111 if (!no_save_visual) {
4112 undo_visual_stack.push_back (current_visual_state(false));
4116 Editor::VisualState*
4117 Editor::current_visual_state (bool with_tracks)
4119 VisualState* vs = new VisualState;
4120 vs->y_position = vertical_adjustment.get_value();
4121 vs->frames_per_unit = frames_per_unit;
4122 vs->leftmost_frame = leftmost_frame;
4123 vs->zoom_focus = zoom_focus;
4126 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4127 vs->track_states.push_back (TAVState ((*i), &(*i)->get_state()));
4135 Editor::undo_visual_state ()
4137 if (undo_visual_stack.empty()) {
4141 redo_visual_stack.push_back (current_visual_state());
4143 VisualState* vs = undo_visual_stack.back();
4144 undo_visual_stack.pop_back();
4145 use_visual_state (*vs);
4149 Editor::redo_visual_state ()
4151 if (redo_visual_stack.empty()) {
4155 undo_visual_stack.push_back (current_visual_state());
4157 VisualState* vs = redo_visual_stack.back();
4158 redo_visual_stack.pop_back();
4159 use_visual_state (*vs);
4163 Editor::swap_visual_state ()
4165 if (undo_visual_stack.empty()) {
4166 redo_visual_state ();
4168 undo_visual_state ();
4173 Editor::use_visual_state (VisualState& vs)
4175 no_save_visual = true;
4177 _routes->suspend_redisplay ();
4179 vertical_adjustment.set_value (vs.y_position);
4181 set_zoom_focus (vs.zoom_focus);
4182 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4184 for (list<TAVState>::iterator i = vs.track_states.begin(); i != vs.track_states.end(); ++i) {
4185 TrackViewList::iterator t;
4187 /* check if the track still exists - it could have been deleted */
4189 if ((t = find (track_views.begin(), track_views.end(), i->first)) != track_views.end()) {
4190 (*t)->set_state (*(i->second), Stateful::loading_state_version);
4195 if (!vs.track_states.empty()) {
4196 _routes->update_visibility ();
4199 _routes->resume_redisplay ();
4201 no_save_visual = false;
4205 Editor::set_frames_per_unit (double fpu)
4207 /* this is the core function that controls the zoom level of the canvas. it is called
4208 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4211 if (fpu == frames_per_unit) {
4220 /* don't allow zooms that fit more than the maximum number
4221 of frames into an 800 pixel wide space.
4224 if (max_framepos / fpu < 800.0) {
4229 tempo_lines->tempo_map_changed();
4231 frames_per_unit = fpu;
4236 Editor::post_zoom ()
4238 // convert fpu to frame count
4240 framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width);
4242 if (frames_per_unit != zoom_range_clock.current_duration()) {
4243 zoom_range_clock.set (frames);
4246 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4247 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4248 (*i)->reshow_selection (selection->time);
4252 ZoomChanged (); /* EMIT_SIGNAL */
4254 //reset_scrolling_region ();
4256 if (playhead_cursor) {
4257 playhead_cursor->set_position (playhead_cursor->current_frame);
4260 refresh_location_display();
4261 _summary->set_overlays_dirty ();
4263 update_marker_labels ();
4269 Editor::queue_visual_change (framepos_t where)
4271 pending_visual_change.add (VisualChange::TimeOrigin);
4272 pending_visual_change.time_origin = where;
4273 ensure_visual_change_idle_handler ();
4277 Editor::queue_visual_change (double fpu)
4279 pending_visual_change.add (VisualChange::ZoomLevel);
4280 pending_visual_change.frames_per_unit = fpu;
4282 ensure_visual_change_idle_handler ();
4286 Editor::queue_visual_change_y (double y)
4288 pending_visual_change.add (VisualChange::YOrigin);
4289 pending_visual_change.y_origin = y;
4291 ensure_visual_change_idle_handler ();
4295 Editor::ensure_visual_change_idle_handler ()
4297 if (pending_visual_change.idle_handler_id < 0) {
4298 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4303 Editor::_idle_visual_changer (void* arg)
4305 return static_cast<Editor*>(arg)->idle_visual_changer ();
4309 Editor::idle_visual_changer ()
4311 VisualChange::Type p = pending_visual_change.pending;
4312 pending_visual_change.pending = (VisualChange::Type) 0;
4314 double const last_time_origin = horizontal_position ();
4316 if (p & VisualChange::TimeOrigin) {
4317 /* This is a bit of a hack, but set_frames_per_unit
4318 below will (if called) end up with the
4319 CrossfadeViews looking at Editor::leftmost_frame,
4320 and if we're changing origin and zoom in the same
4321 operation it will be the wrong value unless we
4325 leftmost_frame = pending_visual_change.time_origin;
4328 if (p & VisualChange::ZoomLevel) {
4329 set_frames_per_unit (pending_visual_change.frames_per_unit);
4331 compute_fixed_ruler_scale ();
4332 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4333 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4334 update_tempo_based_rulers ();
4336 if (p & VisualChange::TimeOrigin) {
4337 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4339 if (p & VisualChange::YOrigin) {
4340 vertical_adjustment.set_value (pending_visual_change.y_origin);
4343 if (last_time_origin == horizontal_position ()) {
4344 /* changed signal not emitted */
4345 update_fixed_rulers ();
4346 redisplay_tempo (true);
4349 _summary->set_overlays_dirty ();
4351 pending_visual_change.idle_handler_id = -1;
4352 return 0; /* this is always a one-shot call */
4355 struct EditorOrderTimeAxisSorter {
4356 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4357 return a->order () < b->order ();
4362 Editor::sort_track_selection (TrackViewList* sel)
4364 EditorOrderTimeAxisSorter cmp;
4369 selection->tracks.sort (cmp);
4374 Editor::get_preferred_edit_position (bool ignore_playhead)
4377 framepos_t where = 0;
4378 EditPoint ep = _edit_point;
4380 if (entered_marker) {
4381 return entered_marker->position();
4384 if (ignore_playhead && ep == EditAtPlayhead) {
4385 ep = EditAtSelectedMarker;
4389 case EditAtPlayhead:
4390 where = _session->audible_frame();
4393 case EditAtSelectedMarker:
4394 if (!selection->markers.empty()) {
4396 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4399 where = loc->start();
4410 if (!mouse_frame (where, ignored)) {
4411 /* XXX not right but what can we do ? */
4422 Editor::set_loop_range (framepos_t start, framepos_t end, string cmd)
4424 if (!_session) return;
4426 begin_reversible_command (cmd);
4430 if ((tll = transport_loop_location()) == 0) {
4431 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop);
4432 XMLNode &before = _session->locations()->get_state();
4433 _session->locations()->add (loc, true);
4434 _session->set_auto_loop_location (loc);
4435 XMLNode &after = _session->locations()->get_state();
4436 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4438 XMLNode &before = tll->get_state();
4439 tll->set_hidden (false, this);
4440 tll->set (start, end);
4441 XMLNode &after = tll->get_state();
4442 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4445 commit_reversible_command ();
4449 Editor::set_punch_range (framepos_t start, framepos_t end, string cmd)
4451 if (!_session) return;
4453 begin_reversible_command (cmd);
4457 if ((tpl = transport_punch_location()) == 0) {
4458 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoPunch);
4459 XMLNode &before = _session->locations()->get_state();
4460 _session->locations()->add (loc, true);
4461 _session->set_auto_loop_location (loc);
4462 XMLNode &after = _session->locations()->get_state();
4463 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4466 XMLNode &before = tpl->get_state();
4467 tpl->set_hidden (false, this);
4468 tpl->set (start, end);
4469 XMLNode &after = tpl->get_state();
4470 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4473 commit_reversible_command ();
4476 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4477 * @param rs List to which found regions are added.
4478 * @param where Time to look at.
4479 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4482 Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4484 const TrackViewList* tracks;
4487 tracks = &track_views;
4492 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4493 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4495 boost::shared_ptr<Track> tr;
4496 boost::shared_ptr<Playlist> pl;
4498 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4500 Playlist::RegionList* regions = pl->regions_at (
4501 (framepos_t) floor ( (double)where * tr->speed()));
4503 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4504 RegionView* rv = rtv->view()->find_view (*i);
4517 Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4519 const TrackViewList* tracks;
4522 tracks = &track_views;
4527 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4528 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4530 boost::shared_ptr<Track> tr;
4531 boost::shared_ptr<Playlist> pl;
4533 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4535 Playlist::RegionList* regions = pl->regions_touched (
4536 (framepos_t) floor ( (double)where * tr->speed()), max_framepos);
4538 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4540 RegionView* rv = rtv->view()->find_view (*i);
4553 /** Get regions using the following method:
4555 * Make an initial region list using the selected regions, unless
4556 * the edit point is `mouse' and the mouse is over an unselected
4557 * region. In this case, start with just that region.
4559 * Then, make an initial track list of the tracks that these
4560 * regions are on, and if the edit point is not `mouse', add the
4563 * Look at this track list and add any other tracks that are on the
4564 * same active edit-enabled route group as one of the initial tracks.
4566 * Finally take the initial region list and add any regions that are
4567 * under the edit point on one of the tracks on the track list to get
4568 * the returned region list.
4570 * The rationale here is that the mouse edit point is special in that
4571 * its position describes both a time and a track; the other edit
4572 * modes only describe a time. Hence if the edit point is `mouse' we
4573 * ignore selected tracks, as we assume the user means something by
4574 * pointing at a particular track. Also in this case we take note of
4575 * the region directly under the edit point, as there is always just one
4576 * (rather than possibly several with non-mouse edit points).
4580 Editor::get_regions_from_selection_and_edit_point ()
4582 RegionSelection regions;
4584 if (_edit_point == EditAtMouse && entered_regionview && !selection->regions.contains (entered_regionview)) {
4585 regions.add (entered_regionview);
4587 regions = selection->regions;
4590 TrackViewList tracks;
4592 if (_edit_point != EditAtMouse) {
4593 tracks = selection->tracks;
4596 /* Add any other tracks that have regions that are in the same
4597 edit-activated route group as one of our regions.
4599 for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) {
4601 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4602 if (g && g->is_active() && g->is_edit()) {
4603 tracks.add (axis_views_from_routes (g->route_list()));
4608 if (!tracks.empty()) {
4609 /* now find regions that are at the edit position on those tracks */
4610 framepos_t const where = get_preferred_edit_position ();
4611 get_regions_at (regions, where, tracks);
4619 Editor::get_regions_from_selection_and_entered ()
4621 RegionSelection rs = selection->regions;
4623 if (rs.empty() && entered_regionview) {
4624 rs.add (entered_regionview);
4631 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4633 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4635 RouteTimeAxisView* tatv;
4637 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4639 boost::shared_ptr<Playlist> pl;
4640 vector<boost::shared_ptr<Region> > results;
4642 boost::shared_ptr<Track> tr;
4644 if ((tr = tatv->track()) == 0) {
4649 if ((pl = (tr->playlist())) != 0) {
4650 pl->get_region_list_equivalent_regions (region, results);
4653 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4654 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4655 regions.push_back (marv);
4664 Editor::show_rhythm_ferret ()
4666 if (rhythm_ferret == 0) {
4667 rhythm_ferret = new RhythmFerret(*this);
4670 rhythm_ferret->set_session (_session);
4671 rhythm_ferret->show ();
4672 rhythm_ferret->present ();
4676 Editor::first_idle ()
4678 MessageDialog* dialog = 0;
4680 if (track_views.size() > 1) {
4681 dialog = new MessageDialog (*this,
4682 string_compose (_("Please wait while %1 loads visual data"), PROGRAM_NAME),
4687 ARDOUR_UI::instance()->flush_pending ();
4690 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4694 // first idle adds route children (automation tracks), so we need to redisplay here
4695 _routes->redisplay ();
4703 Editor::_idle_resize (gpointer arg)
4705 return ((Editor*)arg)->idle_resize ();
4709 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4711 if (resize_idle_id < 0) {
4712 resize_idle_id = g_idle_add (_idle_resize, this);
4713 _pending_resize_amount = 0;
4716 /* make a note of the smallest resulting height, so that we can clamp the
4717 lower limit at TimeAxisView::hSmall */
4719 int32_t min_resulting = INT32_MAX;
4721 _pending_resize_amount += h;
4722 _pending_resize_view = view;
4724 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4726 if (selection->tracks.contains (_pending_resize_view)) {
4727 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4728 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4732 if (min_resulting < 0) {
4737 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4738 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4742 /** Handle pending resizing of tracks */
4744 Editor::idle_resize ()
4746 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4748 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4749 selection->tracks.contains (_pending_resize_view)) {
4751 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4752 if (*i != _pending_resize_view) {
4753 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4758 _pending_resize_amount = 0;
4760 _group_tabs->set_dirty ();
4761 resize_idle_id = -1;
4769 ENSURE_GUI_THREAD (*this, &Editor::located);
4771 playhead_cursor->set_position (_session->audible_frame ());
4772 if (_follow_playhead && !_pending_initial_locate) {
4773 reset_x_origin_to_follow_playhead ();
4776 _pending_locate_request = false;
4777 _pending_initial_locate = false;
4781 Editor::region_view_added (RegionView *)
4783 _summary->set_dirty ();
4787 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4789 TrackViewList::const_iterator j = track_views.begin ();
4790 while (j != track_views.end()) {
4791 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4792 if (rtv && rtv->route() == r) {
4803 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4807 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4808 TimeAxisView* tv = axis_view_from_route (*i);
4819 Editor::handle_new_route (RouteList& routes)
4821 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4823 RouteTimeAxisView *rtv;
4824 list<RouteTimeAxisView*> new_views;
4826 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4827 boost::shared_ptr<Route> route = (*x);
4829 if (route->is_hidden() || route->is_monitor()) {
4833 DataType dt = route->input()->default_type();
4835 if (dt == ARDOUR::DataType::AUDIO) {
4836 rtv = new AudioTimeAxisView (*this, _session, route, *track_canvas);
4837 } else if (dt == ARDOUR::DataType::MIDI) {
4838 rtv = new MidiTimeAxisView (*this, _session, route, *track_canvas);
4840 throw unknown_type();
4843 new_views.push_back (rtv);
4844 track_views.push_back (rtv);
4846 rtv->effective_gain_display ();
4848 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4851 _routes->routes_added (new_views);
4853 if (show_editor_mixer_when_tracks_arrive) {
4854 show_editor_mixer (true);
4857 editor_list_button.set_sensitive (true);
4859 _summary->set_dirty ();
4863 Editor::timeaxisview_deleted (TimeAxisView *tv)
4865 if (_session && _session->deletion_in_progress()) {
4866 /* the situation is under control */
4870 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
4872 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
4874 _routes->route_removed (tv);
4876 if (tv == entered_track) {
4880 TimeAxisView::Children c = tv->get_child_list ();
4881 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
4882 if (entered_track == i->get()) {
4887 /* remove it from the list of track views */
4889 TrackViewList::iterator i;
4891 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
4892 i = track_views.erase (i);
4895 /* update whatever the current mixer strip is displaying, if revelant */
4897 boost::shared_ptr<Route> route;
4900 route = rtav->route ();
4903 if (current_mixer_strip && current_mixer_strip->route() == route) {
4905 TimeAxisView* next_tv;
4907 if (track_views.empty()) {
4909 } else if (i == track_views.end()) {
4910 next_tv = track_views.front();
4917 set_selected_mixer_strip (*next_tv);
4919 /* make the editor mixer strip go away setting the
4920 * button to inactive (which also unticks the menu option)
4923 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
4929 Editor::hide_track_in_display (TimeAxisView* tv, bool /*temponly*/)
4931 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
4933 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
4934 // this will hide the mixer strip
4935 set_selected_mixer_strip (*tv);
4938 _routes->hide_track_in_display (*tv);
4942 Editor::sync_track_view_list_and_routes ()
4944 track_views = TrackViewList (_routes->views ());
4946 _summary->set_dirty ();
4947 _group_tabs->set_dirty ();
4949 return false; // do not call again (until needed)
4953 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
4955 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4960 /** Find a RouteTimeAxisView by the ID of its route */
4962 Editor::get_route_view_by_route_id (PBD::ID& id) const
4964 RouteTimeAxisView* v;
4966 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
4967 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
4968 if(v->route()->id() == id) {
4978 Editor::fit_route_group (RouteGroup *g)
4980 TrackViewList ts = axis_views_from_routes (g->route_list ());
4985 Editor::consider_auditioning (boost::shared_ptr<Region> region)
4987 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
4990 _session->cancel_audition ();
4994 if (_session->is_auditioning()) {
4995 _session->cancel_audition ();
4996 if (r == last_audition_region) {
5001 _session->audition_region (r);
5002 last_audition_region = r;
5007 Editor::hide_a_region (boost::shared_ptr<Region> r)
5009 r->set_hidden (true);
5013 Editor::show_a_region (boost::shared_ptr<Region> r)
5015 r->set_hidden (false);
5019 Editor::audition_region_from_region_list ()
5021 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5025 Editor::hide_region_from_region_list ()
5027 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5031 Editor::show_region_in_region_list ()
5033 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5037 Editor::step_edit_status_change (bool yn)
5040 start_step_editing ();
5042 stop_step_editing ();
5047 Editor::start_step_editing ()
5049 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5053 Editor::stop_step_editing ()
5055 step_edit_connection.disconnect ();
5059 Editor::check_step_edit ()
5061 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5062 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5064 mtv->check_step_edit ();
5068 return true; // do it again, till we stop
5072 Editor::horizontal_scroll_left_press ()
5074 ++_scroll_callbacks;
5076 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5077 /* delay the first auto-repeat */
5081 double x = leftmost_position() - current_page_frames() / 5;
5088 /* do hacky auto-repeat */
5089 if (!_scroll_connection.connected ()) {
5090 _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press), 100);
5091 _scroll_callbacks = 0;
5098 Editor::horizontal_scroll_left_release ()
5100 _scroll_connection.disconnect ();
5104 Editor::horizontal_scroll_right_press ()
5106 ++_scroll_callbacks;
5108 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5109 /* delay the first auto-repeat */
5113 reset_x_origin (leftmost_position() + current_page_frames() / 5);
5115 /* do hacky auto-repeat */
5116 if (!_scroll_connection.connected ()) {
5117 _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press), 100);
5118 _scroll_callbacks = 0;
5125 Editor::horizontal_scroll_right_release ()
5127 _scroll_connection.disconnect ();
5130 /** Queue a change for the Editor viewport x origin to follow the playhead */
5132 Editor::reset_x_origin_to_follow_playhead ()
5134 framepos_t const frame = playhead_cursor->current_frame;
5136 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5138 if (_session->transport_speed() < 0) {
5140 if (frame > (current_page_frames() / 2)) {
5141 center_screen (frame-(current_page_frames()/2));
5143 center_screen (current_page_frames()/2);
5148 if (frame < leftmost_frame) {
5151 if (_session->transport_rolling()) {
5152 /* rolling; end up with the playhead at the right of the page */
5153 l = frame - current_page_frames ();
5155 /* not rolling: end up with the playhead 3/4 of the way along the page */
5156 l = frame - (3 * current_page_frames() / 4);
5163 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5166 if (_session->transport_rolling()) {
5167 /* rolling: end up with the playhead on the left of the page */
5168 center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5170 /* not rolling: end up with the playhead 1/4 of the way along the page */
5171 center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5179 Editor::super_rapid_screen_update ()
5181 if (!_session || !_session->engine().running()) {
5185 /* METERING / MIXER STRIPS */
5187 /* update track meters, if required */
5188 if (is_mapped() && meters_running) {
5189 RouteTimeAxisView* rtv;
5190 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5191 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5192 rtv->fast_update ();
5197 /* and any current mixer strip */
5198 if (current_mixer_strip) {
5199 current_mixer_strip->fast_update ();
5202 /* PLAYHEAD AND VIEWPORT */
5204 framepos_t const frame = _session->audible_frame();
5206 /* There are a few reasons why we might not update the playhead / viewport stuff:
5208 * 1. we don't update things when there's a pending locate request, otherwise
5209 * when the editor requests a locate there is a chance that this method
5210 * will move the playhead before the locate request is processed, causing
5212 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5213 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5216 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5218 last_update_frame = frame;
5220 if (!_dragging_playhead) {
5221 playhead_cursor->set_position (frame);
5224 if (!_stationary_playhead) {
5226 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5227 reset_x_origin_to_follow_playhead ();
5232 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5236 // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5237 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5238 if (target <= 0.0) {
5241 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5242 target = (target * 0.15) + (current * 0.85);
5248 set_horizontal_position (current);
5257 Editor::session_going_away ()
5259 _have_idled = false;
5261 _session_connections.drop_connections ();
5263 super_rapid_screen_update_connection.disconnect ();
5265 selection->clear ();
5266 cut_buffer->clear ();
5268 clicked_regionview = 0;
5269 clicked_axisview = 0;
5270 clicked_routeview = 0;
5271 clicked_crossfadeview = 0;
5272 entered_regionview = 0;
5274 last_update_frame = 0;
5277 playhead_cursor->canvas_item.hide ();
5279 /* rip everything out of the list displays */
5283 _route_groups->clear ();
5285 /* do this first so that deleting a track doesn't reset cms to null
5286 and thus cause a leak.
5289 if (current_mixer_strip) {
5290 if (current_mixer_strip->get_parent() != 0) {
5291 global_hpacker.remove (*current_mixer_strip);
5293 delete current_mixer_strip;
5294 current_mixer_strip = 0;
5297 /* delete all trackviews */
5299 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5302 track_views.clear ();
5304 zoom_range_clock.set_session (0);
5305 nudge_clock.set_session (0);
5307 editor_list_button.set_active(false);
5308 editor_list_button.set_sensitive(false);
5310 /* clear tempo/meter rulers */
5311 remove_metric_marks ();
5313 clear_marker_display ();
5315 delete current_bbt_points;
5316 current_bbt_points = 0;
5318 /* get rid of any existing editor mixer strip */
5320 WindowTitle title(Glib::get_application_name());
5321 title += _("Editor");
5323 set_title (title.get_string());
5325 SessionHandlePtr::session_going_away ();
5330 Editor::show_editor_list (bool yn)
5333 the_notebook.show();
5335 the_notebook.hide();
5340 Editor::change_region_layering_order ()
5342 framepos_t const position = get_preferred_edit_position ();
5344 if (!clicked_routeview) {
5345 if (layering_order_editor) {
5346 layering_order_editor->hide ();
5351 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
5357 boost::shared_ptr<Playlist> pl = track->playlist();
5363 if (layering_order_editor == 0) {
5364 layering_order_editor = new RegionLayeringOrderEditor(*this);
5367 layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position);
5368 layering_order_editor->maybe_present ();
5372 Editor::update_region_layering_order_editor ()
5374 if (layering_order_editor && layering_order_editor->is_visible ()) {
5375 change_region_layering_order ();
5380 Editor::setup_fade_images ()
5382 _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear")));
5383 _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut")));
5384 _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut")));
5385 _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut")));
5386 _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut")));
5388 _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear")));
5389 _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut")));
5390 _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut")));
5391 _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut")));
5392 _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut")));
5396 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
5398 Editor::action_menu_item (std::string const & name)
5400 Glib::RefPtr<Action> a = editor_actions->get_action (name);
5403 return *manage (a->create_menu_item ());
5407 Editor::resize_text_widgets ()
5409 set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_FUDGE+10, 15);
5410 set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_FUDGE+10, 15);
5411 set_size_request_to_display_given_text (snap_type_selector, snap_type_strings, COMBO_FUDGE+10, 15);
5412 set_size_request_to_display_given_text (snap_mode_selector, snap_mode_strings, COMBO_FUDGE+10, 15);
5413 set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_FUDGE+10, 15);