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/bindings.h"
53 #include "gtkmm2ext/grouped_buttons.h"
54 #include "gtkmm2ext/gtk_ui.h"
55 #include "gtkmm2ext/tearoff.h"
56 #include "gtkmm2ext/utils.h"
57 #include "gtkmm2ext/window_title.h"
58 #include "gtkmm2ext/choice.h"
59 #include "gtkmm2ext/cell_renderer_pixbuf_toggle.h"
61 #include "ardour/audio_diskstream.h"
62 #include "ardour/audio_track.h"
63 #include "ardour/audioplaylist.h"
64 #include "ardour/audioregion.h"
65 #include "ardour/location.h"
66 #include "ardour/midi_region.h"
67 #include "ardour/plugin_manager.h"
68 #include "ardour/profile.h"
69 #include "ardour/route_group.h"
70 #include "ardour/session_directory.h"
71 #include "ardour/session_route.h"
72 #include "ardour/session_state_utils.h"
73 #include "ardour/tempo.h"
74 #include "ardour/utils.h"
75 #include "ardour/session_playlists.h"
76 #include "ardour/audioengine.h"
78 #include "control_protocol/control_protocol.h"
82 #include "analysis_window.h"
83 #include "audio_clock.h"
84 #include "audio_region_view.h"
85 #include "audio_streamview.h"
86 #include "audio_time_axis.h"
87 #include "automation_time_axis.h"
88 #include "bundle_manager.h"
89 #include "canvas-noevent-text.h"
90 #include "canvas_impl.h"
91 #include "crossfade_edit.h"
92 #include "crossfade_view.h"
96 #include "editor_cursors.h"
97 #include "editor_drag.h"
98 #include "editor_group_tabs.h"
99 #include "editor_locations.h"
100 #include "editor_regions.h"
101 #include "editor_route_groups.h"
102 #include "editor_routes.h"
103 #include "editor_snapshots.h"
104 #include "editor_summary.h"
105 #include "global_port_matrix.h"
106 #include "gui_object.h"
107 #include "gui_thread.h"
108 #include "keyboard.h"
110 #include "midi_time_axis.h"
111 #include "mixer_strip.h"
112 #include "mouse_cursors.h"
113 #include "playlist_selector.h"
114 #include "public_editor.h"
115 #include "region_layering_order_editor.h"
116 #include "rgb_macros.h"
117 #include "rhythm_ferret.h"
118 #include "selection.h"
120 #include "simpleline.h"
121 #include "tempo_lines.h"
122 #include "time_axis_view.h"
128 #include "imageframe_socket_handler.h"
132 using namespace ARDOUR;
135 using namespace Glib;
136 using namespace Gtkmm2ext;
137 using namespace Editing;
139 using PBD::internationalize;
141 using Gtkmm2ext::Keyboard;
143 const double Editor::timebar_height = 15.0;
145 static const gchar *_snap_type_strings[] = {
147 N_("Timecode Frames"),
148 N_("Timecode Seconds"),
149 N_("Timecode Minutes"),
177 static const gchar *_snap_mode_strings[] = {
184 static const gchar *_edit_point_strings[] = {
191 static const gchar *_zoom_focus_strings[] = {
201 #ifdef USE_RUBBERBAND
202 static const gchar *_rb_opt_strings[] = {
205 N_("Balanced multitimbral mixture"),
206 N_("Unpitched percussion with stable notes"),
207 N_("Crisp monophonic instrumental"),
208 N_("Unpitched solo percussion"),
209 N_("Resample without preserving pitch"),
215 show_me_the_size (Requisition* r, const char* what)
217 cerr << "size of " << what << " = " << r->width << " x " << r->height << endl;
222 pane_size_watcher (Paned* pane)
224 /* if the handle of a pane vanishes into (at least) the tabs of a notebook,
225 it is no longer accessible. so stop that. this doesn't happen on X11,
226 just the quartz backend.
231 int max_width_of_lhs = GTK_WIDGET(pane->gobj())->allocation.width - 25;
233 gint pos = pane->get_position ();
235 if (pos > max_width_of_lhs) {
236 pane->set_position (max_width_of_lhs);
242 : _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
244 /* time display buttons */
245 , minsec_label (_("Mins:Secs"))
246 , bbt_label (_("Bars:Beats"))
247 , timecode_label (_("Timecode"))
248 , samples_label (_("Samples"))
249 , tempo_label (_("Tempo"))
250 , meter_label (_("Meter"))
251 , mark_label (_("Location Markers"))
252 , range_mark_label (_("Range Markers"))
253 , transport_mark_label (_("Loop/Punch Ranges"))
254 , cd_mark_label (_("CD Markers"))
255 , edit_packer (4, 4, true)
257 /* the values here don't matter: layout widgets
258 reset them as needed.
261 , vertical_adjustment (0.0, 0.0, 10.0, 400.0)
263 /* tool bar related */
265 , zoom_range_clock (new AudioClock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, false, true))
267 , toolbar_selection_clock_table (2,3)
269 , automation_mode_button (_("mode"))
271 , _toolbar_viewport (*manage (new Gtk::Adjustment (0, 0, 1e10)), *manage (new Gtk::Adjustment (0, 0, 1e10)))
272 , midi_panic_button (_("Panic"))
275 , image_socket_listener(0)
280 , nudge_clock (new AudioClock (X_("nudge"), false, X_("NudgeClock"), true, false, true))
281 , meters_running(false)
282 , _pending_locate_request (false)
283 , _pending_initial_locate (false)
284 , _last_cut_copy_source_track (0)
286 , _region_selection_change_updates_region_list (true)
290 /* we are a singleton */
292 PublicEditor::_instance = this;
296 selection = new Selection (this);
297 cut_buffer = new Selection (this);
299 clicked_regionview = 0;
300 clicked_axisview = 0;
301 clicked_routeview = 0;
302 clicked_crossfadeview = 0;
303 clicked_control_point = 0;
304 last_update_frame = 0;
305 pre_press_cursor = 0;
306 _drags = new DragManager (this);
307 current_mixer_strip = 0;
308 current_bbt_points = 0;
311 snap_type_strings = I18N (_snap_type_strings);
312 snap_mode_strings = I18N (_snap_mode_strings);
313 zoom_focus_strings = I18N (_zoom_focus_strings);
314 edit_point_strings = I18N (_edit_point_strings);
315 #ifdef USE_RUBBERBAND
316 rb_opt_strings = I18N (_rb_opt_strings);
320 snap_threshold = 5.0;
321 bbt_beat_subdivision = 4;
324 last_autoscroll_x = 0;
325 last_autoscroll_y = 0;
326 autoscroll_active = false;
327 autoscroll_timeout_tag = -1;
332 current_interthread_info = 0;
333 _show_measures = true;
334 show_gain_after_trim = false;
335 last_item_entered = 0;
337 have_pending_keyboard_selection = false;
338 _follow_playhead = true;
339 _stationary_playhead = false;
340 _xfade_visibility = true;
341 editor_ruler_menu = 0;
342 no_ruler_shown_update = false;
344 range_marker_menu = 0;
345 marker_menu_item = 0;
346 tempo_or_meter_marker_menu = 0;
347 transport_marker_menu = 0;
348 new_transport_marker_menu = 0;
349 editor_mixer_strip_width = Wide;
350 show_editor_mixer_when_tracks_arrive = false;
351 region_edit_menu_split_multichannel_item = 0;
352 region_edit_menu_split_item = 0;
355 current_stepping_trackview = 0;
357 entered_regionview = 0;
359 clear_entered_track = false;
362 button_release_can_deselect = true;
363 _dragging_playhead = false;
364 _dragging_edit_point = false;
365 select_new_marker = false;
367 layering_order_editor = 0;
368 no_save_visual = false;
371 scrubbing_direction = 0;
375 location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get();
376 location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get();
377 location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get();
378 location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get();
379 location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get();
381 _edit_point = EditAtMouse;
382 _internal_editing = false;
383 current_canvas_cursor = 0;
385 frames_per_unit = 2048; /* too early to use reset_zoom () */
387 _scroll_callbacks = 0;
389 zoom_focus = ZoomFocusLeft;
390 set_zoom_focus (ZoomFocusLeft);
391 zoom_range_clock->ValueChanged.connect (sigc::mem_fun(*this, &Editor::zoom_adjustment_changed));
393 bbt_label.set_name ("EditorTimeButton");
394 bbt_label.set_size_request (-1, (int)timebar_height);
395 bbt_label.set_alignment (1.0, 0.5);
396 bbt_label.set_padding (5,0);
398 bbt_label.set_no_show_all();
399 minsec_label.set_name ("EditorTimeButton");
400 minsec_label.set_size_request (-1, (int)timebar_height);
401 minsec_label.set_alignment (1.0, 0.5);
402 minsec_label.set_padding (5,0);
403 minsec_label.hide ();
404 minsec_label.set_no_show_all();
405 timecode_label.set_name ("EditorTimeButton");
406 timecode_label.set_size_request (-1, (int)timebar_height);
407 timecode_label.set_alignment (1.0, 0.5);
408 timecode_label.set_padding (5,0);
409 timecode_label.hide ();
410 timecode_label.set_no_show_all();
411 samples_label.set_name ("EditorTimeButton");
412 samples_label.set_size_request (-1, (int)timebar_height);
413 samples_label.set_alignment (1.0, 0.5);
414 samples_label.set_padding (5,0);
415 samples_label.hide ();
416 samples_label.set_no_show_all();
418 tempo_label.set_name ("EditorTimeButton");
419 tempo_label.set_size_request (-1, (int)timebar_height);
420 tempo_label.set_alignment (1.0, 0.5);
421 tempo_label.set_padding (5,0);
423 tempo_label.set_no_show_all();
425 meter_label.set_name ("EditorTimeButton");
426 meter_label.set_size_request (-1, (int)timebar_height);
427 meter_label.set_alignment (1.0, 0.5);
428 meter_label.set_padding (5,0);
430 meter_label.set_no_show_all();
432 mark_label.set_name ("EditorTimeButton");
433 mark_label.set_size_request (-1, (int)timebar_height);
434 mark_label.set_alignment (1.0, 0.5);
435 mark_label.set_padding (5,0);
437 mark_label.set_no_show_all();
439 cd_mark_label.set_name ("EditorTimeButton");
440 cd_mark_label.set_size_request (-1, (int)timebar_height);
441 cd_mark_label.set_alignment (1.0, 0.5);
442 cd_mark_label.set_padding (5,0);
443 cd_mark_label.hide();
444 cd_mark_label.set_no_show_all();
446 range_mark_label.set_name ("EditorTimeButton");
447 range_mark_label.set_size_request (-1, (int)timebar_height);
448 range_mark_label.set_alignment (1.0, 0.5);
449 range_mark_label.set_padding (5,0);
450 range_mark_label.hide();
451 range_mark_label.set_no_show_all();
453 transport_mark_label.set_name ("EditorTimeButton");
454 transport_mark_label.set_size_request (-1, (int)timebar_height);
455 transport_mark_label.set_alignment (1.0, 0.5);
456 transport_mark_label.set_padding (5,0);
457 transport_mark_label.hide();
458 transport_mark_label.set_no_show_all();
460 initialize_rulers ();
461 initialize_canvas ();
463 _summary = new EditorSummary (this);
465 selection->TimeChanged.connect (sigc::mem_fun(*this, &Editor::time_selection_changed));
466 selection->TracksChanged.connect (sigc::mem_fun(*this, &Editor::track_selection_changed));
468 editor_regions_selection_changed_connection = selection->RegionsChanged.connect (sigc::mem_fun(*this, &Editor::region_selection_changed));
470 selection->PointsChanged.connect (sigc::mem_fun(*this, &Editor::point_selection_changed));
471 selection->MarkersChanged.connect (sigc::mem_fun(*this, &Editor::marker_selection_changed));
473 edit_controls_vbox.set_spacing (0);
474 vertical_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &Editor::tie_vertical_scrolling), true);
475 track_canvas->signal_map_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_map_handler));
477 HBox* h = manage (new HBox);
478 _group_tabs = new EditorGroupTabs (this);
479 h->pack_start (*_group_tabs, PACK_SHRINK);
480 h->pack_start (edit_controls_vbox);
481 controls_layout.add (*h);
483 controls_layout.set_name ("EditControlsBase");
484 controls_layout.add_events (Gdk::SCROLL_MASK);
485 controls_layout.signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::control_layout_scroll), false);
487 controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
488 controls_layout.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_release));
490 _cursors = new MouseCursors;
492 ArdourCanvas::Canvas* time_pad = manage(new ArdourCanvas::Canvas());
493 ArdourCanvas::SimpleLine* pad_line_1 = manage(new ArdourCanvas::SimpleLine(*time_pad->root(),
494 0.0, 1.0, 100.0, 1.0));
496 pad_line_1->property_color_rgba() = 0xFF0000FF;
501 time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2);
502 time_canvas_vbox.set_size_request (-1, -1);
504 ruler_label_event_box.add (ruler_label_vbox);
505 ruler_label_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
506 ruler_label_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
508 time_button_event_box.add (time_button_vbox);
509 time_button_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
510 time_button_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
512 /* these enable us to have a dedicated window (for cursor setting, etc.)
513 for the canvas areas.
516 track_canvas_event_box.add (*track_canvas);
518 time_canvas_event_box.add (time_canvas_vbox);
519 time_canvas_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
521 edit_packer.set_col_spacings (0);
522 edit_packer.set_row_spacings (0);
523 edit_packer.set_homogeneous (false);
524 edit_packer.set_border_width (0);
525 edit_packer.set_name ("EditorWindow");
527 /* labels for the rulers */
528 edit_packer.attach (ruler_label_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0);
529 /* labels for the marker "tracks" */
530 edit_packer.attach (time_button_event_box, 1, 2, 1, 2, FILL, SHRINK, 0, 0);
532 edit_packer.attach (time_canvas_event_box, 2, 3, 0, 1, FILL|EXPAND, FILL, 0, 0);
534 edit_packer.attach (controls_layout, 0, 2, 2, 3, FILL, FILL|EXPAND, 0, 0);
536 edit_packer.attach (track_canvas_event_box, 2, 3, 1, 3, FILL|EXPAND, FILL|EXPAND, 0, 0);
538 bottom_hbox.set_border_width (2);
539 bottom_hbox.set_spacing (3);
541 _route_groups = new EditorRouteGroups (this);
542 _routes = new EditorRoutes (this);
543 _regions = new EditorRegions (this);
544 _snapshots = new EditorSnapshots (this);
545 _locations = new EditorLocations (this);
547 add_notebook_page (_("Regions"), _regions->widget ());
548 add_notebook_page (_("Tracks & Busses"), _routes->widget ());
549 add_notebook_page (_("Snapshots"), _snapshots->widget ());
550 add_notebook_page (_("Route Groups"), _route_groups->widget ());
551 add_notebook_page (_("Ranges & Marks"), _locations->widget ());
553 _the_notebook.set_show_tabs (true);
554 _the_notebook.set_scrollable (true);
555 _the_notebook.popup_disable ();
556 _the_notebook.set_tab_pos (Gtk::POS_RIGHT);
557 _the_notebook.show_all ();
559 post_maximal_editor_width = 0;
560 post_maximal_horizontal_pane_position = 0;
561 post_maximal_editor_height = 0;
562 post_maximal_vertical_pane_position = 0;
563 _notebook_shrunk = false;
565 editor_summary_pane.pack1(edit_packer);
567 Button* summary_arrows_left_left = manage (new Button);
568 summary_arrows_left_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
569 summary_arrows_left_left->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), LEFT)));
570 summary_arrows_left_left->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_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::bind (sigc::mem_fun (*this, &Editor::scroll_press), RIGHT)));
575 summary_arrows_left_right->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
577 VBox* summary_arrows_left = manage (new VBox);
578 summary_arrows_left->pack_start (*summary_arrows_left_left);
579 summary_arrows_left->pack_start (*summary_arrows_left_right);
581 Button* summary_arrows_right_up = manage (new Button);
582 summary_arrows_right_up->add (*manage (new Arrow (ARROW_UP, SHADOW_NONE)));
583 summary_arrows_right_up->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), UP)));
584 summary_arrows_right_up->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
586 Button* summary_arrows_right_down = manage (new Button);
587 summary_arrows_right_down->add (*manage (new Arrow (ARROW_DOWN, SHADOW_NONE)));
588 summary_arrows_right_down->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), DOWN)));
589 summary_arrows_right_down->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
591 VBox* summary_arrows_right = manage (new VBox);
592 summary_arrows_right->pack_start (*summary_arrows_right_up);
593 summary_arrows_right->pack_start (*summary_arrows_right_down);
595 Frame* summary_frame = manage (new Frame);
596 summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN);
598 summary_frame->add (*_summary);
599 summary_frame->show ();
601 _summary_hbox.pack_start (*summary_arrows_left, false, false);
602 _summary_hbox.pack_start (*summary_frame, true, true);
603 _summary_hbox.pack_start (*summary_arrows_right, false, false);
605 editor_summary_pane.pack2 (_summary_hbox);
607 edit_pane.pack1 (editor_summary_pane, true, true);
608 edit_pane.pack2 (_the_notebook, false, true);
610 editor_summary_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun (*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&editor_summary_pane)));
612 /* XXX: editor_summary_pane might need similar special OS X treatment to the edit_pane */
614 edit_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&edit_pane)));
616 Glib::PropertyProxy<int> proxy = edit_pane.property_position();
617 proxy.signal_changed().connect (bind (sigc::ptr_fun (pane_size_watcher), static_cast<Paned*> (&edit_pane)));
619 top_hbox.pack_start (toolbar_frame);
621 HBox *hbox = manage (new HBox);
622 hbox->pack_start (edit_pane, true, true);
624 global_vpacker.pack_start (top_hbox, false, false);
625 global_vpacker.pack_start (*hbox, true, true);
627 global_hpacker.pack_start (global_vpacker, true, true);
629 set_name ("EditorWindow");
630 add_accel_group (ActionManager::ui_manager->get_accel_group());
632 status_bar_hpacker.show ();
634 vpacker.pack_end (status_bar_hpacker, false, false);
635 vpacker.pack_end (global_hpacker, true, true);
637 /* register actions now so that set_state() can find them and set toggles/checks etc */
642 setup_midi_toolbar ();
644 _snap_type = SnapToBeat;
645 set_snap_to (_snap_type);
646 _snap_mode = SnapOff;
647 set_snap_mode (_snap_mode);
648 set_mouse_mode (MouseObject, true);
649 pre_internal_mouse_mode = MouseObject;
650 set_edit_point_preference (EditAtMouse, true);
652 _playlist_selector = new PlaylistSelector();
653 _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
655 RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), ui_bind (&Editor::catch_vanishing_regionview, this, _1), gui_context());
659 nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
660 nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
662 nudge_forward_button.set_name ("TransportButton");
663 nudge_backward_button.set_name ("TransportButton");
665 fade_context_menu.set_name ("ArdourContextMenu");
667 /* icons, titles, WM stuff */
669 list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
670 Glib::RefPtr<Gdk::Pixbuf> icon;
672 if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
673 window_icons.push_back (icon);
675 if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
676 window_icons.push_back (icon);
678 if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
679 window_icons.push_back (icon);
681 if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
682 window_icons.push_back (icon);
684 if (!window_icons.empty()) {
685 // set_icon_list (window_icons);
686 set_default_icon_list (window_icons);
689 WindowTitle title(Glib::get_application_name());
690 title += _("Editor");
691 set_title (title.get_string());
692 set_wmclass (X_("ardour_editor"), PROGRAM_NAME);
695 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
697 signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
698 signal_delete_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
700 /* allow external control surfaces/protocols to do various things */
702 ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context());
703 ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context());
704 ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context());
705 ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), ui_bind (&Editor::control_scroll, this, _1), gui_context());
706 ControlProtocol::SelectByRID.connect (*this, invalidator (*this), ui_bind (&Editor::control_select, this, _1), gui_context());
707 BasicUI::AccessAction.connect (*this, invalidator (*this), ui_bind (&Editor::access_action, this, _1, _2), gui_context());
709 /* problematic: has to return a value and thus cannot be x-thread */
711 Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
713 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
715 TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
717 _ignore_region_action = false;
718 _last_region_menu_was_main = false;
719 _popup_region_menu_item = 0;
721 _show_marker_lines = false;
722 _over_region_trim_target = false;
724 /* Button bindings */
726 button_bindings = new Bindings;
728 XMLNode* node = button_settings();
730 for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) {
731 button_bindings->load (**i);
738 setup_fade_images ();
744 if(image_socket_listener) {
745 if(image_socket_listener->is_connected())
747 image_socket_listener->close_connection() ;
750 delete image_socket_listener ;
751 image_socket_listener = 0 ;
755 delete button_bindings;
757 delete _route_groups;
763 Editor::button_settings () const
765 XMLNode* settings = ARDOUR_UI::instance()->editor_settings();
766 XMLNode* node = find_named_node (*settings, X_("Buttons"));
769 cerr << "new empty Button node\n";
770 node = new XMLNode (X_("Buttons"));
777 Editor::add_toplevel_controls (Container& cont)
779 vpacker.pack_start (cont, false, false);
784 Editor::catch_vanishing_regionview (RegionView *rv)
786 /* note: the selection will take care of the vanishing
787 audioregionview by itself.
790 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
794 if (clicked_regionview == rv) {
795 clicked_regionview = 0;
798 if (entered_regionview == rv) {
799 set_entered_regionview (0);
802 if (!_all_region_actions_sensitized) {
803 sensitize_all_region_actions (true);
806 _over_region_trim_target = false;
810 Editor::set_entered_regionview (RegionView* rv)
812 if (rv == entered_regionview) {
816 if (entered_regionview) {
817 entered_regionview->exited ();
820 if ((entered_regionview = rv) != 0) {
821 entered_regionview->entered (internal_editing ());
824 if (!_all_region_actions_sensitized && _last_region_menu_was_main) {
825 /* This RegionView entry might have changed what region actions
826 are allowed, so sensitize them all in case a key is pressed.
828 sensitize_all_region_actions (true);
833 Editor::set_entered_track (TimeAxisView* tav)
836 entered_track->exited ();
839 if ((entered_track = tav) != 0) {
840 entered_track->entered ();
845 Editor::show_window ()
847 if (!is_visible ()) {
850 /* XXX: this is a bit unfortunate; it would probably
851 be nicer if we could just call show () above rather
852 than needing the show_all ()
855 /* re-hide stuff if necessary */
856 editor_list_button_toggled ();
857 parameter_changed ("show-summary");
858 parameter_changed ("show-group-tabs");
859 parameter_changed ("show-zoom-tools");
861 /* now reset all audio_time_axis heights, because widgets might need
867 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
868 tv = (static_cast<TimeAxisView*>(*i));
872 if (current_mixer_strip) {
873 current_mixer_strip->hide_things ();
881 Editor::instant_save ()
883 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
888 _session->add_instant_xml(get_state());
890 Config->add_instant_xml(get_state());
895 Editor::zoom_adjustment_changed ()
901 double fpu = zoom_range_clock->current_duration() / _canvas_width;
905 zoom_range_clock->set ((framepos_t) floor (fpu * _canvas_width));
906 } else if (fpu > _session->current_end_frame() / _canvas_width) {
907 fpu = _session->current_end_frame() / _canvas_width;
908 zoom_range_clock->set ((framepos_t) floor (fpu * _canvas_width));
915 Editor::control_select (uint32_t rid)
917 /* handles the (static) signal from the ControlProtocol class that
918 * requests setting the selected track to a given RID
925 boost::shared_ptr<Route> r = _session->route_by_remote_id (rid);
931 TimeAxisView* tav = axis_view_from_route (r);
934 selection->set (tav);
936 selection->clear_tracks ();
941 Editor::control_scroll (float fraction)
943 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
949 double step = fraction * current_page_frames();
952 _control_scroll_target is an optional<T>
954 it acts like a pointer to an framepos_t, with
955 a operator conversion to boolean to check
956 that it has a value could possibly use
957 playhead_cursor->current_frame to store the
958 value and a boolean in the class to know
959 when it's out of date
962 if (!_control_scroll_target) {
963 _control_scroll_target = _session->transport_frame();
964 _dragging_playhead = true;
967 if ((fraction < 0.0f) && (*_control_scroll_target < (framepos_t) fabs(step))) {
968 *_control_scroll_target = 0;
969 } else if ((fraction > 0.0f) && (max_framepos - *_control_scroll_target < step)) {
970 *_control_scroll_target = max_framepos - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
972 *_control_scroll_target += (framepos_t) floor (step);
975 /* move visuals, we'll catch up with it later */
977 playhead_cursor->set_position (*_control_scroll_target);
978 UpdateAllTransportClocks (*_control_scroll_target);
980 if (*_control_scroll_target > (current_page_frames() / 2)) {
981 /* try to center PH in window */
982 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
988 Now we do a timeout to actually bring the session to the right place
989 according to the playhead. This is to avoid reading disk buffers on every
990 call to control_scroll, which is driven by ScrollTimeline and therefore
991 probably by a control surface wheel which can generate lots of events.
993 /* cancel the existing timeout */
995 control_scroll_connection.disconnect ();
997 /* add the next timeout */
999 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
1003 Editor::deferred_control_scroll (framepos_t /*target*/)
1005 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
1006 // reset for next stream
1007 _control_scroll_target = boost::none;
1008 _dragging_playhead = false;
1013 Editor::access_action (std::string action_group, std::string action_item)
1019 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
1022 act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
1030 Editor::on_realize ()
1032 Window::on_realize ();
1037 Editor::map_position_change (framepos_t frame)
1039 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, frame)
1041 if (_session == 0) {
1045 if (_follow_playhead) {
1046 center_screen (frame);
1049 playhead_cursor->set_position (frame);
1053 Editor::center_screen (framepos_t frame)
1055 double page = _canvas_width * frames_per_unit;
1057 /* if we're off the page, then scroll.
1060 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
1061 center_screen_internal (frame, page);
1066 Editor::center_screen_internal (framepos_t frame, float page)
1071 frame -= (framepos_t) page;
1076 reset_x_origin (frame);
1081 Editor::update_title ()
1083 ENSURE_GUI_THREAD (*this, &Editor::update_title)
1086 bool dirty = _session->dirty();
1088 string session_name;
1090 if (_session->snap_name() != _session->name()) {
1091 session_name = _session->snap_name();
1093 session_name = _session->name();
1097 session_name = "*" + session_name;
1100 WindowTitle title(session_name);
1101 title += Glib::get_application_name();
1102 set_title (title.get_string());
1107 Editor::set_session (Session *t)
1109 SessionHandlePtr::set_session (t);
1115 zoom_range_clock->set_session (_session);
1116 _playlist_selector->set_session (_session);
1117 nudge_clock->set_session (_session);
1118 _summary->set_session (_session);
1119 _group_tabs->set_session (_session);
1120 _route_groups->set_session (_session);
1121 _regions->set_session (_session);
1122 _snapshots->set_session (_session);
1123 _routes->set_session (_session);
1124 _locations->set_session (_session);
1126 if (rhythm_ferret) {
1127 rhythm_ferret->set_session (_session);
1130 if (analysis_window) {
1131 analysis_window->set_session (_session);
1135 sfbrowser->set_session (_session);
1138 compute_fixed_ruler_scale ();
1140 /* Make sure we have auto loop and auto punch ranges */
1142 Location* loc = _session->locations()->auto_loop_location();
1144 loc = new Location (*_session, 0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1146 if (loc->start() == loc->end()) {
1147 loc->set_end (loc->start() + 1);
1150 _session->locations()->add (loc, false);
1151 _session->set_auto_loop_location (loc);
1154 loc->set_name (_("Loop"));
1157 loc = _session->locations()->auto_punch_location();
1160 loc = new Location (*_session, 0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1162 if (loc->start() == loc->end()) {
1163 loc->set_end (loc->start() + 1);
1166 _session->locations()->add (loc, false);
1167 _session->set_auto_punch_location (loc);
1170 loc->set_name (_("Punch"));
1173 refresh_location_display ();
1175 /* This must happen after refresh_location_display(), as (amongst other things) we restore
1176 the selected Marker; this needs the LocationMarker list to be available.
1178 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1179 set_state (*node, Stateful::loading_state_version);
1181 /* catch up with the playhead */
1183 _session->request_locate (playhead_cursor->current_frame);
1184 _pending_initial_locate = true;
1188 /* These signals can all be emitted by a non-GUI thread. Therefore the
1189 handlers for them must not attempt to directly interact with the GUI,
1190 but use Gtkmm2ext::UI::instance()->call_slot();
1193 _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), ui_bind(&Editor::step_edit_status_change, this, _1), gui_context());
1194 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1195 _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context());
1196 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context());
1197 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1198 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context());
1199 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1200 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
1201 _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Editor::session_state_saved, this, _1), gui_context());
1202 _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context());
1203 _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&Editor::location_gone, this, _1), gui_context());
1204 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1205 _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display, this), gui_context());
1206 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1208 if (Profile->get_sae()) {
1209 Timecode::BBT_Time bbt;
1213 framepos_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1);
1214 nudge_clock->set_mode(AudioClock::BBT);
1215 nudge_clock->set (pos, true, 0, AudioClock::BBT);
1218 nudge_clock->set (_session->frame_rate() * 5, true, 0, AudioClock::Timecode); // default of 5 seconds
1221 playhead_cursor->canvas_item.show ();
1223 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1224 Config->map_parameters (pc);
1225 _session->config.map_parameters (pc);
1227 restore_ruler_visibility ();
1228 //tempo_map_changed (PropertyChange (0));
1229 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1231 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1232 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1235 super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
1236 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1239 switch (_snap_type) {
1240 case SnapToRegionStart:
1241 case SnapToRegionEnd:
1242 case SnapToRegionSync:
1243 case SnapToRegionBoundary:
1244 build_region_boundary_cache ();
1251 /* register for undo history */
1252 _session->register_with_memento_command_factory(id(), this);
1254 ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated));
1256 start_updating_meters ();
1260 Editor::action_pre_activated (Glib::RefPtr<Action> const & a)
1262 if (a->get_name() == "RegionMenu") {
1263 /* When the main menu's region menu is opened, we setup the actions so that they look right
1264 in the menu. I can't find a way of getting a signal when this menu is subsequently closed,
1265 so we resensitize all region actions when the entered regionview or the region selection
1266 changes. HOWEVER we can't always resensitize on entered_regionview change because that
1267 happens after the region context menu is opened. So we set a flag here, too.
1271 sensitize_the_right_region_actions ();
1272 _last_region_menu_was_main = true;
1276 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1278 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1280 using namespace Menu_Helpers;
1281 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1284 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1288 MenuList& items (fade_context_menu.items());
1292 switch (item_type) {
1294 case FadeInHandleItem:
1295 if (arv->audio_region()->fade_in_active()) {
1296 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1298 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1301 items.push_back (SeparatorElem());
1303 if (Profile->get_sae()) {
1305 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)));
1306 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)));
1313 *_fade_in_images[FadeLinear],
1314 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)
1318 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1323 *_fade_in_images[FadeFast],
1324 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)
1327 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1332 *_fade_in_images[FadeLogB],
1333 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogB)
1336 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1341 *_fade_in_images[FadeLogA],
1342 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogA)
1345 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1350 *_fade_in_images[FadeSlow],
1351 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow)
1354 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1360 case FadeOutHandleItem:
1361 if (arv->audio_region()->fade_out_active()) {
1362 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1364 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1367 items.push_back (SeparatorElem());
1369 if (Profile->get_sae()) {
1370 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)));
1371 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)));
1377 *_fade_out_images[FadeLinear],
1378 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)
1382 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1387 *_fade_out_images[FadeFast],
1388 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)
1391 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1396 *_fade_out_images[FadeLogB],
1397 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogA)
1400 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1405 *_fade_out_images[FadeLogA],
1406 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogB)
1409 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1414 *_fade_out_images[FadeSlow],
1415 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast)
1418 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1424 fatal << _("programming error: ")
1425 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1430 fade_context_menu.popup (button, time);
1434 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1436 using namespace Menu_Helpers;
1437 Menu* (Editor::*build_menu_function)();
1440 switch (item_type) {
1442 case RegionViewName:
1443 case RegionViewNameHighlight:
1444 case LeftFrameHandle:
1445 case RightFrameHandle:
1446 if (with_selection) {
1447 build_menu_function = &Editor::build_track_selection_context_menu;
1449 build_menu_function = &Editor::build_track_region_context_menu;
1454 if (with_selection) {
1455 build_menu_function = &Editor::build_track_selection_context_menu;
1457 build_menu_function = &Editor::build_track_context_menu;
1461 case CrossfadeViewItem:
1462 build_menu_function = &Editor::build_track_crossfade_context_menu;
1466 if (clicked_routeview->track()) {
1467 build_menu_function = &Editor::build_track_context_menu;
1469 build_menu_function = &Editor::build_track_bus_context_menu;
1474 /* probably shouldn't happen but if it does, we don't care */
1478 menu = (this->*build_menu_function)();
1479 menu->set_name ("ArdourContextMenu");
1481 /* now handle specific situations */
1483 switch (item_type) {
1485 case RegionViewName:
1486 case RegionViewNameHighlight:
1487 case LeftFrameHandle:
1488 case RightFrameHandle:
1489 if (!with_selection) {
1490 if (region_edit_menu_split_item) {
1491 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1492 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1494 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1497 if (region_edit_menu_split_multichannel_item) {
1498 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1499 region_edit_menu_split_multichannel_item->set_sensitive (true);
1501 region_edit_menu_split_multichannel_item->set_sensitive (false);
1510 case CrossfadeViewItem:
1517 /* probably shouldn't happen but if it does, we don't care */
1521 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1523 /* Bounce to disk */
1525 using namespace Menu_Helpers;
1526 MenuList& edit_items = menu->items();
1528 edit_items.push_back (SeparatorElem());
1530 switch (clicked_routeview->audio_track()->freeze_state()) {
1531 case AudioTrack::NoFreeze:
1532 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1535 case AudioTrack::Frozen:
1536 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1539 case AudioTrack::UnFrozen:
1540 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1546 if (item_type == StreamItem && clicked_routeview) {
1547 clicked_routeview->build_underlay_menu(menu);
1550 /* When the region menu is opened, we setup the actions so that they look right
1553 sensitize_the_right_region_actions ();
1554 _last_region_menu_was_main = false;
1556 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1557 menu->popup (button, time);
1561 Editor::build_track_context_menu ()
1563 using namespace Menu_Helpers;
1565 MenuList& edit_items = track_context_menu.items();
1568 add_dstream_context_items (edit_items);
1569 return &track_context_menu;
1573 Editor::build_track_bus_context_menu ()
1575 using namespace Menu_Helpers;
1577 MenuList& edit_items = track_context_menu.items();
1580 add_bus_context_items (edit_items);
1581 return &track_context_menu;
1585 Editor::build_track_region_context_menu ()
1587 using namespace Menu_Helpers;
1588 MenuList& edit_items = track_region_context_menu.items();
1591 /* we've just cleared the track region context menu, so the menu that these
1592 two items were on will have disappeared; stop them dangling.
1594 region_edit_menu_split_item = 0;
1595 region_edit_menu_split_multichannel_item = 0;
1597 /* we might try to use items that are currently attached to a crossfade menu,
1600 track_crossfade_context_menu.items().clear ();
1602 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1605 boost::shared_ptr<Track> tr;
1606 boost::shared_ptr<Playlist> pl;
1608 if ((tr = rtv->track())) {
1609 add_region_context_items (edit_items, tr);
1613 add_dstream_context_items (edit_items);
1615 return &track_region_context_menu;
1619 Editor::build_track_crossfade_context_menu ()
1621 using namespace Menu_Helpers;
1622 MenuList& edit_items = track_crossfade_context_menu.items();
1623 edit_items.clear ();
1625 /* we might try to use items that are currently attached to a crossfade menu,
1628 track_region_context_menu.items().clear ();
1630 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1633 boost::shared_ptr<Track> tr;
1634 boost::shared_ptr<Playlist> pl;
1635 boost::shared_ptr<AudioPlaylist> apl;
1637 if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1639 AudioPlaylist::Crossfades xfades;
1643 /* The xfade menu is a bit of a special case, as we always use the mouse position
1644 to decide whether or not to display it (rather than the edit point). No particularly
1645 strong reasons for this, other than it is a bit surprising to right-click on a xfade
1648 mouse_frame (where, ignored);
1649 apl->crossfades_at (where, xfades);
1651 bool const many = xfades.size() > 1;
1653 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1654 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1657 add_region_context_items (edit_items, tr);
1661 add_dstream_context_items (edit_items);
1663 return &track_crossfade_context_menu;
1667 Editor::analyze_region_selection ()
1669 if (analysis_window == 0) {
1670 analysis_window = new AnalysisWindow();
1673 analysis_window->set_session(_session);
1675 analysis_window->show_all();
1678 analysis_window->set_regionmode();
1679 analysis_window->analyze();
1681 analysis_window->present();
1685 Editor::analyze_range_selection()
1687 if (analysis_window == 0) {
1688 analysis_window = new AnalysisWindow();
1691 analysis_window->set_session(_session);
1693 analysis_window->show_all();
1696 analysis_window->set_rangemode();
1697 analysis_window->analyze();
1699 analysis_window->present();
1703 Editor::build_track_selection_context_menu ()
1705 using namespace Menu_Helpers;
1706 MenuList& edit_items = track_selection_context_menu.items();
1707 edit_items.clear ();
1709 add_selection_context_items (edit_items);
1710 // edit_items.push_back (SeparatorElem());
1711 // add_dstream_context_items (edit_items);
1713 return &track_selection_context_menu;
1716 /** Add context menu items relevant to crossfades.
1717 * @param edit_items List to add the items to.
1720 Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1722 using namespace Menu_Helpers;
1723 Menu *xfade_menu = manage (new Menu);
1724 MenuList& items = xfade_menu->items();
1725 xfade_menu->set_name ("ArdourContextMenu");
1728 if (xfade->active()) {
1734 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1735 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1737 if (xfade->can_follow_overlap()) {
1739 if (xfade->following_overlap()) {
1740 str = _("Convert to Short");
1742 str = _("Convert to Full");
1745 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1749 str = xfade->out()->name();
1751 str += xfade->in()->name();
1753 str = _("Crossfade");
1756 edit_items.push_back (MenuElem (str, *xfade_menu));
1757 edit_items.push_back (SeparatorElem());
1761 Editor::xfade_edit_left_region ()
1763 if (clicked_crossfadeview) {
1764 clicked_crossfadeview->left_view.show_region_editor ();
1769 Editor::xfade_edit_right_region ()
1771 if (clicked_crossfadeview) {
1772 clicked_crossfadeview->right_view.show_region_editor ();
1777 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::shared_ptr<Track> track)
1779 using namespace Menu_Helpers;
1781 /* OK, stick the region submenu at the top of the list, and then add
1785 RegionSelection rs = get_regions_from_selection_and_entered ();
1787 string::size_type pos = 0;
1788 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1790 /* we have to hack up the region name because "_" has a special
1791 meaning for menu titles.
1794 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1795 menu_item_name.replace (pos, 1, "__");
1799 if (_popup_region_menu_item == 0) {
1800 _popup_region_menu_item = new MenuItem (menu_item_name);
1801 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1802 _popup_region_menu_item->show ();
1804 _popup_region_menu_item->set_label (menu_item_name);
1807 /* Use the mouse position rather than the edit point to decide whether to show the `choose top region'
1808 dialogue. If we use the edit point it gets a bit messy because the user still has to click over
1809 *some* region in order to get the region context menu stuff to be displayed at all.
1814 mouse_frame (mouse, ignored);
1816 edit_items.push_back (*_popup_region_menu_item);
1817 if (track->playlist()->count_regions_at (mouse) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1818 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region")->create_menu_item ()));
1820 edit_items.push_back (SeparatorElem());
1823 /** Add context menu items relevant to selection ranges.
1824 * @param edit_items List to add the items to.
1827 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1829 using namespace Menu_Helpers;
1831 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1832 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1834 edit_items.push_back (SeparatorElem());
1835 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1837 edit_items.push_back (SeparatorElem());
1839 edit_items.push_back (
1841 _("Move Range Start to Previous Region Boundary"),
1842 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, false)
1846 edit_items.push_back (
1848 _("Move Range Start to Next Region Boundary"),
1849 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, true)
1853 edit_items.push_back (
1855 _("Move Range End to Previous Region Boundary"),
1856 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, false)
1860 edit_items.push_back (
1862 _("Move Range End to Next Region Boundary"),
1863 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, true)
1867 edit_items.push_back (SeparatorElem());
1868 edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection)));
1869 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1871 edit_items.push_back (SeparatorElem());
1872 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1874 edit_items.push_back (SeparatorElem());
1875 edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1876 edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1878 edit_items.push_back (SeparatorElem());
1879 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1881 edit_items.push_back (SeparatorElem());
1882 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1883 edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1884 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1886 edit_items.push_back (SeparatorElem());
1887 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1888 edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1889 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1890 edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1891 edit_items.push_back (MenuElem (_("Export Range..."), sigc::mem_fun(*this, &Editor::export_selection)));
1896 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1898 using namespace Menu_Helpers;
1902 Menu *play_menu = manage (new Menu);
1903 MenuList& play_items = play_menu->items();
1904 play_menu->set_name ("ArdourContextMenu");
1906 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1907 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1908 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1909 play_items.push_back (SeparatorElem());
1910 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
1912 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1916 Menu *select_menu = manage (new Menu);
1917 MenuList& select_items = select_menu->items();
1918 select_menu->set_name ("ArdourContextMenu");
1920 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1921 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1922 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1923 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1924 select_items.push_back (SeparatorElem());
1925 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
1926 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
1927 select_items.push_back (SeparatorElem());
1928 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1929 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1930 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1931 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1932 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
1933 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
1934 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
1936 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1940 Menu *cutnpaste_menu = manage (new Menu);
1941 MenuList& cutnpaste_items = cutnpaste_menu->items();
1942 cutnpaste_menu->set_name ("ArdourContextMenu");
1944 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1945 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1946 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1948 cutnpaste_items.push_back (SeparatorElem());
1950 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
1951 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
1953 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
1955 /* Adding new material */
1957 edit_items.push_back (SeparatorElem());
1958 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
1959 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
1963 Menu *nudge_menu = manage (new Menu());
1964 MenuList& nudge_items = nudge_menu->items();
1965 nudge_menu->set_name ("ArdourContextMenu");
1967 edit_items.push_back (SeparatorElem());
1968 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1969 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1970 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1971 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1973 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1977 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
1979 using namespace Menu_Helpers;
1983 Menu *play_menu = manage (new Menu);
1984 MenuList& play_items = play_menu->items();
1985 play_menu->set_name ("ArdourContextMenu");
1987 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1988 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1989 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1993 Menu *select_menu = manage (new Menu);
1994 MenuList& select_items = select_menu->items();
1995 select_menu->set_name ("ArdourContextMenu");
1997 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1998 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1999 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
2000 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
2001 select_items.push_back (SeparatorElem());
2002 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
2003 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
2004 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2005 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2007 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2011 Menu *cutnpaste_menu = manage (new Menu);
2012 MenuList& cutnpaste_items = cutnpaste_menu->items();
2013 cutnpaste_menu->set_name ("ArdourContextMenu");
2015 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2016 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2017 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
2019 Menu *nudge_menu = manage (new Menu());
2020 MenuList& nudge_items = nudge_menu->items();
2021 nudge_menu->set_name ("ArdourContextMenu");
2023 edit_items.push_back (SeparatorElem());
2024 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2025 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2026 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2027 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2029 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2033 Editor::snap_type() const
2039 Editor::snap_mode() const
2045 Editor::set_snap_to (SnapType st)
2047 unsigned int snap_ind = (unsigned int)st;
2051 if (snap_ind > snap_type_strings.size() - 1) {
2053 _snap_type = (SnapType)snap_ind;
2056 string str = snap_type_strings[snap_ind];
2058 if (str != snap_type_selector.get_active_text()) {
2059 snap_type_selector.set_active_text (str);
2064 switch (_snap_type) {
2065 case SnapToBeatDiv32:
2066 case SnapToBeatDiv28:
2067 case SnapToBeatDiv24:
2068 case SnapToBeatDiv20:
2069 case SnapToBeatDiv16:
2070 case SnapToBeatDiv14:
2071 case SnapToBeatDiv12:
2072 case SnapToBeatDiv10:
2073 case SnapToBeatDiv8:
2074 case SnapToBeatDiv7:
2075 case SnapToBeatDiv6:
2076 case SnapToBeatDiv5:
2077 case SnapToBeatDiv4:
2078 case SnapToBeatDiv3:
2079 case SnapToBeatDiv2:
2080 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
2081 update_tempo_based_rulers ();
2084 case SnapToRegionStart:
2085 case SnapToRegionEnd:
2086 case SnapToRegionSync:
2087 case SnapToRegionBoundary:
2088 build_region_boundary_cache ();
2096 SnapChanged (); /* EMIT SIGNAL */
2100 Editor::set_snap_mode (SnapMode mode)
2103 string str = snap_mode_strings[(int)mode];
2105 if (str != snap_mode_selector.get_active_text ()) {
2106 snap_mode_selector.set_active_text (str);
2112 Editor::set_edit_point_preference (EditPoint ep, bool force)
2114 bool changed = (_edit_point != ep);
2117 string str = edit_point_strings[(int)ep];
2119 if (str != edit_point_selector.get_active_text ()) {
2120 edit_point_selector.set_active_text (str);
2123 set_canvas_cursor ();
2125 if (!force && !changed) {
2129 const char* action=NULL;
2131 switch (_edit_point) {
2132 case EditAtPlayhead:
2133 action = "edit-at-playhead";
2135 case EditAtSelectedMarker:
2136 action = "edit-at-marker";
2139 action = "edit-at-mouse";
2143 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2145 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2149 bool in_track_canvas;
2151 if (!mouse_frame (foo, in_track_canvas)) {
2152 in_track_canvas = false;
2155 reset_canvas_action_sensitivity (in_track_canvas);
2161 Editor::set_state (const XMLNode& node, int /*version*/)
2163 const XMLProperty* prop;
2170 g.base_width = default_width;
2171 g.base_height = default_height;
2175 if ((geometry = find_named_node (node, "geometry")) != 0) {
2179 if ((prop = geometry->property("x_size")) == 0) {
2180 prop = geometry->property ("x-size");
2183 g.base_width = atoi(prop->value());
2185 if ((prop = geometry->property("y_size")) == 0) {
2186 prop = geometry->property ("y-size");
2189 g.base_height = atoi(prop->value());
2192 if ((prop = geometry->property ("x_pos")) == 0) {
2193 prop = geometry->property ("x-pos");
2196 x = atoi (prop->value());
2199 if ((prop = geometry->property ("y_pos")) == 0) {
2200 prop = geometry->property ("y-pos");
2203 y = atoi (prop->value());
2207 set_default_size (g.base_width, g.base_height);
2210 if (_session && (prop = node.property ("playhead"))) {
2212 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2213 playhead_cursor->set_position (pos);
2215 playhead_cursor->set_position (0);
2218 if ((prop = node.property ("mixer-width"))) {
2219 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2222 if ((prop = node.property ("zoom-focus"))) {
2223 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2226 if ((prop = node.property ("zoom"))) {
2227 reset_zoom (PBD::atof (prop->value()));
2229 reset_zoom (frames_per_unit);
2232 if ((prop = node.property ("snap-to"))) {
2233 set_snap_to ((SnapType) atoi (prop->value()));
2236 if ((prop = node.property ("snap-mode"))) {
2237 set_snap_mode ((SnapMode) atoi (prop->value()));
2240 if ((prop = node.property ("mouse-mode"))) {
2241 MouseMode m = str2mousemode(prop->value());
2242 set_mouse_mode (m, true);
2244 set_mouse_mode (MouseObject, true);
2247 if ((prop = node.property ("left-frame")) != 0) {
2249 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2250 reset_x_origin (pos);
2254 if ((prop = node.property ("y-origin")) != 0) {
2255 reset_y_origin (atof (prop->value ()));
2258 if ((prop = node.property ("internal-edit"))) {
2259 bool yn = string_is_affirmative (prop->value());
2260 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2262 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2263 tact->set_active (!yn);
2264 tact->set_active (yn);
2268 if ((prop = node.property ("join-object-range"))) {
2269 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2272 if ((prop = node.property ("edit-point"))) {
2273 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2276 if ((prop = node.property ("show-measures"))) {
2277 bool yn = string_is_affirmative (prop->value());
2278 _show_measures = yn;
2279 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2281 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2282 /* do it twice to force the change */
2283 tact->set_active (!yn);
2284 tact->set_active (yn);
2288 if ((prop = node.property ("follow-playhead"))) {
2289 bool yn = string_is_affirmative (prop->value());
2290 set_follow_playhead (yn);
2291 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2293 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2294 if (tact->get_active() != yn) {
2295 tact->set_active (yn);
2300 if ((prop = node.property ("stationary-playhead"))) {
2301 bool yn = string_is_affirmative (prop->value());
2302 set_stationary_playhead (yn);
2303 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2305 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2306 if (tact->get_active() != yn) {
2307 tact->set_active (yn);
2312 if ((prop = node.property ("region-list-sort-type"))) {
2313 RegionListSortType st;
2314 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2317 if ((prop = node.property ("xfades-visible"))) {
2318 bool yn = string_is_affirmative (prop->value());
2319 _xfade_visibility = !yn;
2320 // set_xfade_visibility (yn);
2323 if ((prop = node.property ("show-editor-mixer"))) {
2325 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2328 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2329 bool yn = string_is_affirmative (prop->value());
2331 /* do it twice to force the change */
2333 tact->set_active (!yn);
2334 tact->set_active (yn);
2337 if ((prop = node.property ("show-editor-list"))) {
2339 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2342 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2343 bool yn = string_is_affirmative (prop->value());
2345 /* do it twice to force the change */
2347 tact->set_active (!yn);
2348 tact->set_active (yn);
2351 if ((prop = node.property (X_("editor-list-page")))) {
2352 _the_notebook.set_current_page (atoi (prop->value ()));
2355 if ((prop = node.property (X_("show-marker-lines")))) {
2356 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2358 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2359 bool yn = string_is_affirmative (prop->value ());
2361 tact->set_active (!yn);
2362 tact->set_active (yn);
2365 XMLNodeList children = node.children ();
2366 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2367 selection->set_state (**i, Stateful::current_state_version);
2368 _regions->set_state (**i);
2375 Editor::get_state ()
2377 XMLNode* node = new XMLNode ("Editor");
2380 id().print (buf, sizeof (buf));
2381 node->add_property ("id", buf);
2383 if (is_realized()) {
2384 Glib::RefPtr<Gdk::Window> win = get_window();
2386 int x, y, width, height;
2387 win->get_root_origin(x, y);
2388 win->get_size(width, height);
2390 XMLNode* geometry = new XMLNode ("geometry");
2392 snprintf(buf, sizeof(buf), "%d", width);
2393 geometry->add_property("x-size", string(buf));
2394 snprintf(buf, sizeof(buf), "%d", height);
2395 geometry->add_property("y-size", string(buf));
2396 snprintf(buf, sizeof(buf), "%d", x);
2397 geometry->add_property("x-pos", string(buf));
2398 snprintf(buf, sizeof(buf), "%d", y);
2399 geometry->add_property("y-pos", string(buf));
2400 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2401 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2402 geometry->add_property("notebook-shrunk", _notebook_shrunk ? "1" : "0");
2403 snprintf(buf,sizeof(buf), "%d",pre_maximal_horizontal_pane_position);
2404 geometry->add_property("pre-maximal-horizontal-pane-position", string(buf));
2405 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2406 geometry->add_property("edit-vertical-pane-pos", string(buf));
2408 node->add_child_nocopy (*geometry);
2411 maybe_add_mixer_strip_width (*node);
2413 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2414 node->add_property ("zoom-focus", buf);
2415 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2416 node->add_property ("zoom", buf);
2417 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2418 node->add_property ("snap-to", buf);
2419 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2420 node->add_property ("snap-mode", buf);
2422 node->add_property ("edit-point", enum_2_string (_edit_point));
2424 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2425 node->add_property ("playhead", buf);
2426 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2427 node->add_property ("left-frame", buf);
2428 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2429 node->add_property ("y-origin", buf);
2431 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2432 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2433 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2434 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2435 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2436 node->add_property ("mouse-mode", enum2str(mouse_mode));
2437 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2438 node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2440 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2442 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2443 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2446 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2448 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2449 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2452 snprintf (buf, sizeof (buf), "%d", _the_notebook.get_current_page ());
2453 node->add_property (X_("editor-list-page"), buf);
2455 if (button_bindings) {
2456 XMLNode* bb = new XMLNode (X_("Buttons"));
2457 button_bindings->save (*bb);
2458 node->add_child_nocopy (*bb);
2461 node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no");
2463 node->add_child_nocopy (selection->get_state ());
2464 node->add_child_nocopy (_regions->get_state ());
2471 /** @param y y offset from the top of all trackviews.
2472 * @return pair: TimeAxisView that y is over, layer index.
2473 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2474 * in stacked region display mode, otherwise 0.
2476 std::pair<TimeAxisView *, layer_t>
2477 Editor::trackview_by_y_position (double y)
2479 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2481 std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
2487 return std::make_pair ( (TimeAxisView *) 0, 0);
2490 /** Snap a position to the grid, if appropriate, taking into account current
2491 * grid settings and also the state of any snap modifier keys that may be pressed.
2492 * @param start Position to snap.
2493 * @param event Event to get current key modifier information from, or 0.
2496 Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2498 if (!_session || !event) {
2502 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2503 if (_snap_mode == SnapOff) {
2504 snap_to_internal (start, direction, for_mark);
2507 if (_snap_mode != SnapOff) {
2508 snap_to_internal (start, direction, for_mark);
2514 Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark)
2516 if (!_session || _snap_mode == SnapOff) {
2520 snap_to_internal (start, direction, for_mark);
2524 Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /*for_mark*/)
2526 const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2527 framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2529 switch (_snap_type) {
2530 case SnapToTimecodeFrame:
2531 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2532 start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2534 start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2538 case SnapToTimecodeSeconds:
2539 if (_session->config.get_timecode_offset_negative()) {
2540 start += _session->config.get_timecode_offset ();
2542 start -= _session->config.get_timecode_offset ();
2544 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2545 start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2547 start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2550 if (_session->config.get_timecode_offset_negative()) {
2551 start -= _session->config.get_timecode_offset ();
2553 start += _session->config.get_timecode_offset ();
2557 case SnapToTimecodeMinutes:
2558 if (_session->config.get_timecode_offset_negative()) {
2559 start += _session->config.get_timecode_offset ();
2561 start -= _session->config.get_timecode_offset ();
2563 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2564 start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2566 start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2568 if (_session->config.get_timecode_offset_negative()) {
2569 start -= _session->config.get_timecode_offset ();
2571 start += _session->config.get_timecode_offset ();
2575 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2581 Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark)
2583 const framepos_t one_second = _session->frame_rate();
2584 const framepos_t one_minute = _session->frame_rate() * 60;
2585 framepos_t presnap = start;
2589 switch (_snap_type) {
2590 case SnapToTimecodeFrame:
2591 case SnapToTimecodeSeconds:
2592 case SnapToTimecodeMinutes:
2593 return timecode_snap_to_internal (start, direction, for_mark);
2596 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2597 start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2599 start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2604 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2605 start = (framepos_t) ceil ((double) start / one_second) * one_second;
2607 start = (framepos_t) floor ((double) start / one_second) * one_second;
2612 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2613 start = (framepos_t) ceil ((double) start / one_minute) * one_minute;
2615 start = (framepos_t) floor ((double) start / one_minute) * one_minute;
2620 start = _session->tempo_map().round_to_bar (start, direction);
2624 start = _session->tempo_map().round_to_beat (start, direction);
2627 case SnapToBeatDiv32:
2628 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2630 case SnapToBeatDiv28:
2631 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2633 case SnapToBeatDiv24:
2634 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2636 case SnapToBeatDiv20:
2637 start = _session->tempo_map().round_to_beat_subdivision (start, 20, direction);
2639 case SnapToBeatDiv16:
2640 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2642 case SnapToBeatDiv14:
2643 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2645 case SnapToBeatDiv12:
2646 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2648 case SnapToBeatDiv10:
2649 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2651 case SnapToBeatDiv8:
2652 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2654 case SnapToBeatDiv7:
2655 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2657 case SnapToBeatDiv6:
2658 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2660 case SnapToBeatDiv5:
2661 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2663 case SnapToBeatDiv4:
2664 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2666 case SnapToBeatDiv3:
2667 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2669 case SnapToBeatDiv2:
2670 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2678 _session->locations()->marks_either_side (start, before, after);
2680 if (before == max_framepos) {
2682 } else if (after == max_framepos) {
2684 } else if (before != max_framepos && after != max_framepos) {
2685 /* have before and after */
2686 if ((start - before) < (after - start)) {
2695 case SnapToRegionStart:
2696 case SnapToRegionEnd:
2697 case SnapToRegionSync:
2698 case SnapToRegionBoundary:
2699 if (!region_boundary_cache.empty()) {
2701 vector<framepos_t>::iterator prev = region_boundary_cache.end ();
2702 vector<framepos_t>::iterator next = region_boundary_cache.end ();
2704 if (direction > 0) {
2705 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2707 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2710 if (next != region_boundary_cache.begin ()) {
2715 framepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2716 framepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2718 if (start > (p + n) / 2) {
2727 switch (_snap_mode) {
2733 if (presnap > start) {
2734 if (presnap > (start + unit_to_frame(snap_threshold))) {
2738 } else if (presnap < start) {
2739 if (presnap < (start - unit_to_frame(snap_threshold))) {
2745 /* handled at entry */
2753 Editor::setup_toolbar ()
2755 HBox* mode_box = manage(new HBox);
2756 mode_box->set_border_width (2);
2757 mode_box->set_spacing(4);
2759 /* table containing mode buttons */
2761 HBox* mouse_mode_button_box = manage (new HBox ());
2763 if (Profile->get_sae()) {
2764 mouse_mode_button_box->pack_start (mouse_move_button);
2766 mouse_mode_button_box->pack_start (mouse_move_button);
2767 mouse_mode_button_box->pack_start (join_object_range_button);
2768 mouse_mode_button_box->pack_start (mouse_select_button);
2771 mouse_mode_button_box->pack_start (mouse_zoom_button);
2773 if (!Profile->get_sae()) {
2774 mouse_mode_button_box->pack_start (mouse_gain_button);
2777 mouse_mode_button_box->pack_start (mouse_timefx_button);
2778 mouse_mode_button_box->pack_start (mouse_audition_button);
2779 mouse_mode_button_box->pack_start (internal_edit_button);
2781 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2782 if (!Profile->get_sae()) {
2783 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2785 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2787 edit_mode_selector.set_name ("EditModeSelector");
2788 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2789 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2791 mode_box->pack_start (edit_mode_selector);
2792 mode_box->pack_start (*mouse_mode_button_box);
2794 _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2795 _mouse_mode_tearoff->set_name ("MouseModeBase");
2796 _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2798 if (Profile->get_sae()) {
2799 _mouse_mode_tearoff->set_can_be_torn_off (false);
2802 _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2803 &_mouse_mode_tearoff->tearoff_window()));
2804 _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2805 &_mouse_mode_tearoff->tearoff_window(), 1));
2806 _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2807 &_mouse_mode_tearoff->tearoff_window()));
2808 _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2809 &_mouse_mode_tearoff->tearoff_window(), 1));
2811 mouse_move_button.set_mode (false);
2812 mouse_select_button.set_mode (false);
2813 mouse_gain_button.set_mode (false);
2814 mouse_zoom_button.set_mode (false);
2815 mouse_timefx_button.set_mode (false);
2816 mouse_audition_button.set_mode (false);
2817 join_object_range_button.set_mode (false);
2819 mouse_move_button.set_name ("MouseModeButton");
2820 mouse_select_button.set_name ("MouseModeButton");
2821 mouse_gain_button.set_name ("MouseModeButton");
2822 mouse_zoom_button.set_name ("MouseModeButton");
2823 mouse_timefx_button.set_name ("MouseModeButton");
2824 mouse_audition_button.set_name ("MouseModeButton");
2825 internal_edit_button.set_name ("MouseModeButton");
2826 join_object_range_button.set_name ("MouseModeButton");
2828 mouse_move_button.unset_flags (CAN_FOCUS);
2829 mouse_select_button.unset_flags (CAN_FOCUS);
2830 mouse_gain_button.unset_flags (CAN_FOCUS);
2831 mouse_zoom_button.unset_flags (CAN_FOCUS);
2832 mouse_timefx_button.unset_flags (CAN_FOCUS);
2833 mouse_audition_button.unset_flags (CAN_FOCUS);
2834 internal_edit_button.unset_flags (CAN_FOCUS);
2835 join_object_range_button.unset_flags (CAN_FOCUS);
2839 _zoom_box.set_spacing (1);
2840 _zoom_box.set_border_width (0);
2842 zoom_in_button.set_name ("EditorTimeButton");
2843 zoom_in_button.set_image (*(manage (new Image (::get_icon ("zoom_in")))));
2844 zoom_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), false));
2846 zoom_out_button.set_name ("EditorTimeButton");
2847 zoom_out_button.set_image (*(manage (new Image (::get_icon ("zoom_out")))));
2848 zoom_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), true));
2850 zoom_out_full_button.set_name ("EditorTimeButton");
2851 zoom_out_full_button.set_image (*(manage (new Image (::get_icon ("zoom_full")))));
2852 zoom_out_full_button.signal_clicked().connect (sigc::mem_fun(*this, &Editor::temporal_zoom_session));
2854 zoom_focus_selector.set_name ("ZoomFocusSelector");
2855 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
2856 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2858 _zoom_box.pack_start (zoom_out_button, false, false);
2859 _zoom_box.pack_start (zoom_in_button, false, false);
2860 _zoom_box.pack_start (zoom_out_full_button, false, false);
2862 _zoom_box.pack_start (zoom_focus_selector);
2864 /* Track zoom buttons */
2865 tav_expand_button.set_name ("TrackHeightButton");
2866 tav_expand_button.set_size_request (-1, 20);
2867 tav_expand_button.add (*(manage (new Image (::get_icon ("tav_exp")))));
2868 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("expand-tracks"));
2869 act->connect_proxy (tav_expand_button);
2871 tav_shrink_button.set_name ("TrackHeightButton");
2872 tav_shrink_button.set_size_request (-1, 20);
2873 tav_shrink_button.add (*(manage (new Image (::get_icon ("tav_shrink")))));
2874 act = ActionManager::get_action (X_("Editor"), X_("shrink-tracks"));
2875 act->connect_proxy (tav_shrink_button);
2877 _zoom_box.pack_start (tav_shrink_button);
2878 _zoom_box.pack_start (tav_expand_button);
2880 _zoom_tearoff = manage (new TearOff (_zoom_box));
2882 _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2883 &_zoom_tearoff->tearoff_window()));
2884 _zoom_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2885 &_zoom_tearoff->tearoff_window(), 0));
2886 _zoom_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2887 &_zoom_tearoff->tearoff_window()));
2888 _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2889 &_zoom_tearoff->tearoff_window(), 0));
2891 snap_box.set_spacing (1);
2892 snap_box.set_border_width (2);
2894 snap_type_selector.set_name ("SnapTypeSelector");
2895 set_popdown_strings (snap_type_selector, snap_type_strings, true);
2896 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2898 snap_mode_selector.set_name ("SnapModeSelector");
2899 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
2900 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2902 edit_point_selector.set_name ("EditPointSelector");
2903 set_popdown_strings (edit_point_selector, edit_point_strings, true);
2904 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2906 snap_box.pack_start (snap_mode_selector, false, false);
2907 snap_box.pack_start (snap_type_selector, false, false);
2908 snap_box.pack_start (edit_point_selector, false, false);
2912 HBox *nudge_box = manage (new HBox);
2913 nudge_box->set_spacing(1);
2914 nudge_box->set_border_width (2);
2916 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2917 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2919 nudge_box->pack_start (nudge_backward_button, false, false);
2920 nudge_box->pack_start (nudge_forward_button, false, false);
2921 nudge_box->pack_start (*nudge_clock, false, false);
2924 /* Pack everything in... */
2926 HBox* hbox = manage (new HBox);
2927 hbox->set_spacing(10);
2929 _tools_tearoff = manage (new TearOff (*hbox));
2930 _tools_tearoff->set_name ("MouseModeBase");
2931 _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
2933 if (Profile->get_sae()) {
2934 _tools_tearoff->set_can_be_torn_off (false);
2937 _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2938 &_tools_tearoff->tearoff_window()));
2939 _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2940 &_tools_tearoff->tearoff_window(), 0));
2941 _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2942 &_tools_tearoff->tearoff_window()));
2943 _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2944 &_tools_tearoff->tearoff_window(), 0));
2946 toolbar_hbox.set_spacing (10);
2947 toolbar_hbox.set_border_width (1);
2949 toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
2950 toolbar_hbox.pack_start (*_zoom_tearoff, false, false);
2951 toolbar_hbox.pack_start (*_tools_tearoff, false, false);
2953 hbox->pack_start (snap_box, false, false);
2954 if (!Profile->get_small_screen()) {
2955 hbox->pack_start (*nudge_box, false, false);
2957 ARDOUR_UI::instance()->editor_transport_box().pack_start (*nudge_box, false, false);
2959 hbox->pack_start (panic_box, false, false);
2963 toolbar_base.set_name ("ToolBarBase");
2964 toolbar_base.add (toolbar_hbox);
2966 _toolbar_viewport.add (toolbar_base);
2967 /* stick to the required height but allow width to vary if there's not enough room */
2968 _toolbar_viewport.set_size_request (1, -1);
2970 toolbar_frame.set_shadow_type (SHADOW_OUT);
2971 toolbar_frame.set_name ("BaseFrame");
2972 toolbar_frame.add (_toolbar_viewport);
2974 DPIReset.connect (sigc::mem_fun (*this, &Editor::resize_text_widgets));
2978 Editor::setup_tooltips ()
2980 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
2981 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain"));
2982 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
2983 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
2984 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2985 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
2986 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
2987 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
2988 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
2989 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
2990 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
2991 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
2992 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
2993 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
2994 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
2995 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
2996 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
2997 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
2998 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
2999 ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
3000 ARDOUR_UI::instance()->set_tip (midi_panic_button, _("Send note off and reset controller messages on all MIDI channels"));
3001 ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode"));
3005 Editor::midi_panic ()
3007 cerr << "MIDI panic\n";
3010 _session->midi_panic();
3015 Editor::setup_midi_toolbar ()
3019 /* Midi sound notes */
3020 midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
3021 midi_sound_notes.unset_flags (CAN_FOCUS);
3022 midi_sound_notes.set_name (X_("MidiSoundNotesButton"));
3026 act = ActionManager::get_action (X_("MIDI"), X_("panic"));
3027 midi_panic_button.set_name("MidiPanicButton");
3028 act->connect_proxy (midi_panic_button);
3030 panic_box.pack_start (midi_sound_notes , true, true);
3031 panic_box.pack_start (midi_panic_button, true, true);
3035 Editor::convert_drop_to_paths (
3036 vector<string>& paths,
3037 const RefPtr<Gdk::DragContext>& /*context*/,
3040 const SelectionData& data,
3044 if (_session == 0) {
3048 vector<string> uris = data.get_uris();
3052 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3053 are actually URI lists. So do it by hand.
3056 if (data.get_target() != "text/plain") {
3060 /* Parse the "uri-list" format that Nautilus provides,
3061 where each pathname is delimited by \r\n.
3063 THERE MAY BE NO NULL TERMINATING CHAR!!!
3066 string txt = data.get_text();
3070 p = (const char *) malloc (txt.length() + 1);
3071 txt.copy ((char *) p, txt.length(), 0);
3072 ((char*)p)[txt.length()] = '\0';
3078 while (g_ascii_isspace (*p))
3082 while (*q && (*q != '\n') && (*q != '\r')) {
3089 while (q > p && g_ascii_isspace (*q))
3094 uris.push_back (string (p, q - p + 1));
3098 p = strchr (p, '\n');
3110 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3112 if ((*i).substr (0,7) == "file://") {
3115 PBD::url_decode (p);
3117 // scan forward past three slashes
3119 string::size_type slashcnt = 0;
3120 string::size_type n = 0;
3121 string::iterator x = p.begin();
3123 while (slashcnt < 3 && x != p.end()) {
3126 } else if (slashcnt == 3) {
3133 if (slashcnt != 3 || x == p.end()) {
3134 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3138 paths.push_back (p.substr (n - 1));
3146 Editor::new_tempo_section ()
3152 Editor::map_transport_state ()
3154 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3156 if (_session && _session->transport_stopped()) {
3157 have_pending_keyboard_selection = false;
3160 update_loop_range_view (true);
3165 Editor::State::State (PublicEditor const * e)
3167 selection = new Selection (e);
3170 Editor::State::~State ()
3176 Editor::begin_reversible_command (string name)
3179 _session->begin_reversible_command (name);
3184 Editor::begin_reversible_command (GQuark q)
3187 _session->begin_reversible_command (q);
3192 Editor::commit_reversible_command ()
3195 _session->commit_reversible_command ();
3200 Editor::history_changed ()
3204 if (undo_action && _session) {
3205 if (_session->undo_depth() == 0) {
3208 label = string_compose(_("Undo (%1)"), _session->next_undo());
3210 undo_action->property_label() = label;
3213 if (redo_action && _session) {
3214 if (_session->redo_depth() == 0) {
3217 label = string_compose(_("Redo (%1)"), _session->next_redo());
3219 redo_action->property_label() = label;
3224 Editor::duplicate_dialog (bool with_dialog)
3228 if (mouse_mode == MouseRange) {
3229 if (selection->time.length() == 0) {
3234 RegionSelection rs = get_regions_from_selection_and_entered ();
3236 if (mouse_mode != MouseRange && rs.empty()) {
3242 ArdourDialog win (_("Duplicate"));
3243 Label label (_("Number of duplications:"));
3244 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3245 SpinButton spinner (adjustment, 0.0, 1);
3248 win.get_vbox()->set_spacing (12);
3249 win.get_vbox()->pack_start (hbox);
3250 hbox.set_border_width (6);
3251 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3253 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3254 place, visually. so do this by hand.
3257 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3258 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3259 spinner.grab_focus();
3265 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3266 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3267 win.set_default_response (RESPONSE_ACCEPT);
3269 win.set_position (WIN_POS_MOUSE);
3271 spinner.grab_focus ();
3273 switch (win.run ()) {
3274 case RESPONSE_ACCEPT:
3280 times = adjustment.get_value();
3283 if (mouse_mode == MouseRange) {
3284 duplicate_selection (times);
3286 duplicate_some_regions (rs, times);
3291 Editor::set_edit_mode (EditMode m)
3293 Config->set_edit_mode (m);
3297 Editor::cycle_edit_mode ()
3299 switch (Config->get_edit_mode()) {
3301 if (Profile->get_sae()) {
3302 Config->set_edit_mode (Lock);
3304 Config->set_edit_mode (Splice);
3308 Config->set_edit_mode (Lock);
3311 Config->set_edit_mode (Slide);
3317 Editor::edit_mode_selection_done ()
3319 string s = edit_mode_selector.get_active_text ();
3322 Config->set_edit_mode (string_to_edit_mode (s));
3327 Editor::snap_type_selection_done ()
3329 string choice = snap_type_selector.get_active_text();
3330 SnapType snaptype = SnapToBeat;
3332 if (choice == _("Beats/2")) {
3333 snaptype = SnapToBeatDiv2;
3334 } else if (choice == _("Beats/3")) {
3335 snaptype = SnapToBeatDiv3;
3336 } else if (choice == _("Beats/4")) {
3337 snaptype = SnapToBeatDiv4;
3338 } else if (choice == _("Beats/5")) {
3339 snaptype = SnapToBeatDiv5;
3340 } else if (choice == _("Beats/6")) {
3341 snaptype = SnapToBeatDiv6;
3342 } else if (choice == _("Beats/7")) {
3343 snaptype = SnapToBeatDiv7;
3344 } else if (choice == _("Beats/8")) {
3345 snaptype = SnapToBeatDiv8;
3346 } else if (choice == _("Beats/10")) {
3347 snaptype = SnapToBeatDiv10;
3348 } else if (choice == _("Beats/12")) {
3349 snaptype = SnapToBeatDiv12;
3350 } else if (choice == _("Beats/14")) {
3351 snaptype = SnapToBeatDiv14;
3352 } else if (choice == _("Beats/16")) {
3353 snaptype = SnapToBeatDiv16;
3354 } else if (choice == _("Beats/20")) {
3355 snaptype = SnapToBeatDiv20;
3356 } else if (choice == _("Beats/24")) {
3357 snaptype = SnapToBeatDiv24;
3358 } else if (choice == _("Beats/28")) {
3359 snaptype = SnapToBeatDiv28;
3360 } else if (choice == _("Beats/32")) {
3361 snaptype = SnapToBeatDiv32;
3362 } else if (choice == _("Beats")) {
3363 snaptype = SnapToBeat;
3364 } else if (choice == _("Bars")) {
3365 snaptype = SnapToBar;
3366 } else if (choice == _("Marks")) {
3367 snaptype = SnapToMark;
3368 } else if (choice == _("Region starts")) {
3369 snaptype = SnapToRegionStart;
3370 } else if (choice == _("Region ends")) {
3371 snaptype = SnapToRegionEnd;
3372 } else if (choice == _("Region bounds")) {
3373 snaptype = SnapToRegionBoundary;
3374 } else if (choice == _("Region syncs")) {
3375 snaptype = SnapToRegionSync;
3376 } else if (choice == _("CD Frames")) {
3377 snaptype = SnapToCDFrame;
3378 } else if (choice == _("Timecode Frames")) {
3379 snaptype = SnapToTimecodeFrame;
3380 } else if (choice == _("Timecode Seconds")) {
3381 snaptype = SnapToTimecodeSeconds;
3382 } else if (choice == _("Timecode Minutes")) {
3383 snaptype = SnapToTimecodeMinutes;
3384 } else if (choice == _("Seconds")) {
3385 snaptype = SnapToSeconds;
3386 } else if (choice == _("Minutes")) {
3387 snaptype = SnapToMinutes;
3390 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3392 ract->set_active ();
3397 Editor::snap_mode_selection_done ()
3399 string choice = snap_mode_selector.get_active_text();
3400 SnapMode mode = SnapNormal;
3402 if (choice == _("No Grid")) {
3404 } else if (choice == _("Grid")) {
3406 } else if (choice == _("Magnetic")) {
3407 mode = SnapMagnetic;
3410 RefPtr<RadioAction> ract = snap_mode_action (mode);
3413 ract->set_active (true);
3418 Editor::cycle_edit_point (bool with_marker)
3420 switch (_edit_point) {
3422 set_edit_point_preference (EditAtPlayhead);
3424 case EditAtPlayhead:
3426 set_edit_point_preference (EditAtSelectedMarker);
3428 set_edit_point_preference (EditAtMouse);
3431 case EditAtSelectedMarker:
3432 set_edit_point_preference (EditAtMouse);
3438 Editor::edit_point_selection_done ()
3440 string choice = edit_point_selector.get_active_text();
3441 EditPoint ep = EditAtSelectedMarker;
3443 if (choice == _("Marker")) {
3444 set_edit_point_preference (EditAtSelectedMarker);
3445 } else if (choice == _("Playhead")) {
3446 set_edit_point_preference (EditAtPlayhead);
3448 set_edit_point_preference (EditAtMouse);
3451 RefPtr<RadioAction> ract = edit_point_action (ep);
3454 ract->set_active (true);
3459 Editor::zoom_focus_selection_done ()
3461 string choice = zoom_focus_selector.get_active_text();
3462 ZoomFocus focus_type = ZoomFocusLeft;
3464 if (choice == _("Left")) {
3465 focus_type = ZoomFocusLeft;
3466 } else if (choice == _("Right")) {
3467 focus_type = ZoomFocusRight;
3468 } else if (choice == _("Center")) {
3469 focus_type = ZoomFocusCenter;
3470 } else if (choice == _("Playhead")) {
3471 focus_type = ZoomFocusPlayhead;
3472 } else if (choice == _("Mouse")) {
3473 focus_type = ZoomFocusMouse;
3474 } else if (choice == _("Edit point")) {
3475 focus_type = ZoomFocusEdit;
3478 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3481 ract->set_active ();
3486 Editor::edit_controls_button_release (GdkEventButton* ev)
3488 if (Keyboard::is_context_menu_event (ev)) {
3489 ARDOUR_UI::instance()->add_route (this);
3490 } else if (ev->button == 1) {
3491 selection->clear_tracks ();
3498 Editor::mouse_select_button_release (GdkEventButton* ev)
3500 /* this handles just right-clicks */
3502 if (ev->button != 3) {
3510 Editor::set_zoom_focus (ZoomFocus f)
3512 string str = zoom_focus_strings[(int)f];
3514 if (str != zoom_focus_selector.get_active_text()) {
3515 zoom_focus_selector.set_active_text (str);
3518 if (zoom_focus != f) {
3525 Editor::ensure_float (Window& win)
3527 win.set_transient_for (*this);
3531 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3533 /* recover or initialize pane positions. do this here rather than earlier because
3534 we don't want the positions to change the child allocations, which they seem to do.
3540 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3549 XMLNode* geometry = find_named_node (*node, "geometry");
3551 if (which == static_cast<Paned*> (&edit_pane)) {
3553 if (done & Horizontal) {
3557 if (geometry && (prop = geometry->property ("notebook-shrunk"))) {
3558 _notebook_shrunk = string_is_affirmative (prop->value ());
3561 if (geometry && (prop = geometry->property ("pre-maximal-horizontal-pane-position"))) {
3562 pre_maximal_horizontal_pane_position = atoi (prop->value ());
3565 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3566 /* initial allocation is 90% to canvas, 10% to notebook */
3567 pos = (int) floor (alloc.get_width() * 0.90f);
3568 snprintf (buf, sizeof(buf), "%d", pos);
3570 pos = atoi (prop->value());
3573 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3574 edit_pane.set_position (pos);
3575 if (pre_maximal_horizontal_pane_position == 0) {
3576 pre_maximal_horizontal_pane_position = pos;
3580 done = (Pane) (done | Horizontal);
3582 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3584 if (done & Vertical) {
3588 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3589 /* initial allocation is 90% to canvas, 10% to summary */
3590 pos = (int) floor (alloc.get_height() * 0.90f);
3591 snprintf (buf, sizeof(buf), "%d", pos);
3593 pos = atoi (prop->value());
3596 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3597 editor_summary_pane.set_position (pos);
3598 pre_maximal_vertical_pane_position = pos;
3601 done = (Pane) (done | Vertical);
3606 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3608 if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) {
3609 top_hbox.remove (toolbar_frame);
3614 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3616 if (toolbar_frame.get_parent() == 0) {
3617 top_hbox.pack_end (toolbar_frame);
3622 Editor::set_show_measures (bool yn)
3624 if (_show_measures != yn) {
3627 if ((_show_measures = yn) == true) {
3629 tempo_lines->show();
3637 Editor::toggle_follow_playhead ()
3639 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3641 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3642 set_follow_playhead (tact->get_active());
3646 /** @param yn true to follow playhead, otherwise false.
3647 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
3650 Editor::set_follow_playhead (bool yn, bool catch_up)
3652 if (_follow_playhead != yn) {
3653 if ((_follow_playhead = yn) == true && catch_up) {
3655 reset_x_origin_to_follow_playhead ();
3662 Editor::toggle_stationary_playhead ()
3664 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3666 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3667 set_stationary_playhead (tact->get_active());
3672 Editor::set_stationary_playhead (bool yn)
3674 if (_stationary_playhead != yn) {
3675 if ((_stationary_playhead = yn) == true) {
3677 // FIXME need a 3.0 equivalent of this 2.X call
3678 // update_current_screen ();
3685 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3687 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3689 xfade->set_active (!xfade->active());
3694 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3696 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3698 xfade->set_follow_overlap (!xfade->following_overlap());
3703 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3705 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3711 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3715 switch (cew.run ()) {
3716 case RESPONSE_ACCEPT:
3723 PropertyChange all_crossfade_properties;
3724 all_crossfade_properties.add (ARDOUR::Properties::active);
3725 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3726 xfade->PropertyChanged (all_crossfade_properties);
3730 Editor::playlist_selector () const
3732 return *_playlist_selector;
3736 Editor::get_grid_type_as_beats (bool& success, framepos_t position)
3740 switch (_snap_type) {
3745 case SnapToBeatDiv32:
3748 case SnapToBeatDiv28:
3751 case SnapToBeatDiv24:
3754 case SnapToBeatDiv20:
3757 case SnapToBeatDiv16:
3760 case SnapToBeatDiv14:
3763 case SnapToBeatDiv12:
3766 case SnapToBeatDiv10:
3769 case SnapToBeatDiv8:
3772 case SnapToBeatDiv7:
3775 case SnapToBeatDiv6:
3778 case SnapToBeatDiv5:
3781 case SnapToBeatDiv4:
3784 case SnapToBeatDiv3:
3787 case SnapToBeatDiv2:
3793 return _session->tempo_map().meter_at (position).beats_per_bar();
3798 case SnapToTimecodeFrame:
3799 case SnapToTimecodeSeconds:
3800 case SnapToTimecodeMinutes:
3803 case SnapToRegionStart:
3804 case SnapToRegionEnd:
3805 case SnapToRegionSync:
3806 case SnapToRegionBoundary:
3816 Editor::get_nudge_distance (framepos_t pos, framecnt_t& next)
3820 ret = nudge_clock->current_duration (pos);
3821 next = ret + 1; /* XXXX fix me */
3827 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3829 ArdourDialog dialog (_("Playlist Deletion"));
3830 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3831 "If it is kept, its audio files will not be cleaned.\n"
3832 "If it is deleted, audio files used by it alone will be cleaned."),
3835 dialog.set_position (WIN_POS_CENTER);
3836 dialog.get_vbox()->pack_start (label);
3840 dialog.add_button (_("Delete Playlist"), RESPONSE_ACCEPT);
3841 dialog.add_button (_("Keep Playlist"), RESPONSE_REJECT);
3842 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3844 switch (dialog.run ()) {
3845 case RESPONSE_ACCEPT:
3846 /* delete the playlist */
3850 case RESPONSE_REJECT:
3851 /* keep the playlist */
3863 Editor::audio_region_selection_covers (framepos_t where)
3865 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3866 if ((*a)->region()->covers (where)) {
3875 Editor::prepare_for_cleanup ()
3877 cut_buffer->clear_regions ();
3878 cut_buffer->clear_playlists ();
3880 selection->clear_regions ();
3881 selection->clear_playlists ();
3883 _regions->suspend_redisplay ();
3887 Editor::finish_cleanup ()
3889 _regions->resume_redisplay ();
3893 Editor::transport_loop_location()
3896 return _session->locations()->auto_loop_location();
3903 Editor::transport_punch_location()
3906 return _session->locations()->auto_punch_location();
3913 Editor::control_layout_scroll (GdkEventScroll* ev)
3915 if (Keyboard::some_magic_widget_has_focus()) {
3919 switch (ev->direction) {
3921 scroll_tracks_up_line ();
3925 case GDK_SCROLL_DOWN:
3926 scroll_tracks_down_line ();
3930 /* no left/right handling yet */
3938 Editor::session_state_saved (string)
3941 _snapshots->redisplay ();
3945 Editor::maximise_editing_space ()
3947 _mouse_mode_tearoff->set_visible (false);
3948 _tools_tearoff->set_visible (false);
3949 _zoom_tearoff->set_visible (false);
3951 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
3952 pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
3953 pre_maximal_editor_width = this->get_width ();
3954 pre_maximal_editor_height = this->get_height ();
3956 if (post_maximal_horizontal_pane_position == 0) {
3957 post_maximal_horizontal_pane_position = edit_pane.get_width();
3960 if (post_maximal_vertical_pane_position == 0) {
3961 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
3966 if (post_maximal_editor_width) {
3967 edit_pane.set_position (post_maximal_horizontal_pane_position -
3968 abs(post_maximal_editor_width - pre_maximal_editor_width));
3970 edit_pane.set_position (post_maximal_horizontal_pane_position);
3973 /* Hack: we must do this in an idle handler for it to work; see comment in
3974 restore_editing_space()
3977 Glib::signal_idle().connect (
3979 sigc::mem_fun (*this, &Editor::idle_reset_vertical_pane_position),
3980 post_maximal_vertical_pane_position
3984 if (Config->get_keep_tearoffs()) {
3985 _mouse_mode_tearoff->set_visible (true);
3986 _tools_tearoff->set_visible (true);
3987 if (Config->get_show_zoom_tools ()) {
3988 _zoom_tearoff->set_visible (true);
3995 Editor::idle_reset_vertical_pane_position (int p)
3997 editor_summary_pane.set_position (p);
4002 Editor::restore_editing_space ()
4004 // user changed width/height of panes during fullscreen
4006 if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
4007 post_maximal_horizontal_pane_position = edit_pane.get_position();
4010 if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
4011 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
4016 _mouse_mode_tearoff->set_visible (true);
4017 _tools_tearoff->set_visible (true);
4018 if (Config->get_show_zoom_tools ()) {
4019 _zoom_tearoff->set_visible (true);
4021 post_maximal_editor_width = this->get_width();
4022 post_maximal_editor_height = this->get_height();
4024 edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
4026 /* This is a bit of a hack, but it seems that if you set the vertical pane position
4027 here it gets reset to some wrong value after this method has finished. Doing
4028 the setup in an idle callback seems to work.
4030 Glib::signal_idle().connect (
4032 sigc::mem_fun (*this, &Editor::idle_reset_vertical_pane_position),
4033 pre_maximal_vertical_pane_position
4039 * Make new playlists for a given track and also any others that belong
4040 * to the same active route group with the `edit' property.
4045 Editor::new_playlists (TimeAxisView* v)
4047 begin_reversible_command (_("new playlists"));
4048 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4049 _session->playlists->get (playlists);
4050 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4051 commit_reversible_command ();
4055 * Use a copy of the current playlist for a given track and also any others that belong
4056 * to the same active route group with the `edit' property.
4061 Editor::copy_playlists (TimeAxisView* v)
4063 begin_reversible_command (_("copy playlists"));
4064 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4065 _session->playlists->get (playlists);
4066 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4067 commit_reversible_command ();
4070 /** Clear the current playlist for a given track and also any others that belong
4071 * to the same active route group with the `edit' property.
4076 Editor::clear_playlists (TimeAxisView* v)
4078 begin_reversible_command (_("clear playlists"));
4079 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4080 _session->playlists->get (playlists);
4081 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4082 commit_reversible_command ();
4086 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4088 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4092 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4094 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4098 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4100 atv.clear_playlist ();
4104 Editor::on_key_press_event (GdkEventKey* ev)
4106 return key_press_focus_accelerator_handler (*this, ev);
4110 Editor::on_key_release_event (GdkEventKey* ev)
4112 return Gtk::Window::on_key_release_event (ev);
4113 // return key_press_focus_accelerator_handler (*this, ev);
4116 /** Queue up a change to the viewport x origin.
4117 * @param frame New x origin.
4120 Editor::reset_x_origin (framepos_t frame)
4122 queue_visual_change (frame);
4126 Editor::reset_y_origin (double y)
4128 queue_visual_change_y (y);
4132 Editor::reset_zoom (double fpu)
4134 queue_visual_change (fpu);
4138 Editor::reposition_and_zoom (framepos_t frame, double fpu)
4140 reset_x_origin (frame);
4143 if (!no_save_visual) {
4144 undo_visual_stack.push_back (current_visual_state(false));
4148 Editor::VisualState::VisualState ()
4149 : gui_state (new GUIObjectState)
4153 Editor::VisualState::~VisualState ()
4158 Editor::VisualState*
4159 Editor::current_visual_state (bool with_tracks)
4161 VisualState* vs = new VisualState;
4162 vs->y_position = vertical_adjustment.get_value();
4163 vs->frames_per_unit = frames_per_unit;
4164 vs->leftmost_frame = leftmost_frame;
4165 vs->zoom_focus = zoom_focus;
4168 *(vs->gui_state) = *ARDOUR_UI::instance()->gui_object_state;
4175 Editor::undo_visual_state ()
4177 if (undo_visual_stack.empty()) {
4181 redo_visual_stack.push_back (current_visual_state());
4183 VisualState* vs = undo_visual_stack.back();
4184 undo_visual_stack.pop_back();
4185 use_visual_state (*vs);
4189 Editor::redo_visual_state ()
4191 if (redo_visual_stack.empty()) {
4195 undo_visual_stack.push_back (current_visual_state());
4197 VisualState* vs = redo_visual_stack.back();
4198 redo_visual_stack.pop_back();
4199 use_visual_state (*vs);
4203 Editor::swap_visual_state ()
4205 if (undo_visual_stack.empty()) {
4206 redo_visual_state ();
4208 undo_visual_state ();
4213 Editor::use_visual_state (VisualState& vs)
4215 no_save_visual = true;
4217 _routes->suspend_redisplay ();
4219 vertical_adjustment.set_value (vs.y_position);
4221 set_zoom_focus (vs.zoom_focus);
4222 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4224 *ARDOUR_UI::instance()->gui_object_state = *vs.gui_state;
4226 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4227 (*i)->reset_visual_state ();
4230 _routes->update_visibility ();
4231 _routes->resume_redisplay ();
4233 no_save_visual = false;
4237 Editor::set_frames_per_unit (double fpu)
4239 /* this is the core function that controls the zoom level of the canvas. it is called
4240 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4243 if (fpu == frames_per_unit) {
4252 /* don't allow zooms that fit more than the maximum number
4253 of frames into an 800 pixel wide space.
4256 if (max_framepos / fpu < 800.0) {
4261 tempo_lines->tempo_map_changed();
4263 frames_per_unit = fpu;
4268 Editor::post_zoom ()
4270 // convert fpu to frame count
4272 framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width);
4274 if (frames_per_unit != zoom_range_clock->current_duration()) {
4275 zoom_range_clock->set (frames);
4278 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4279 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4280 (*i)->reshow_selection (selection->time);
4284 ZoomChanged (); /* EMIT_SIGNAL */
4286 //reset_scrolling_region ();
4288 if (playhead_cursor) {
4289 playhead_cursor->set_position (playhead_cursor->current_frame);
4292 refresh_location_display();
4293 _summary->set_overlays_dirty ();
4295 update_marker_labels ();
4301 Editor::queue_visual_change (framepos_t where)
4303 pending_visual_change.add (VisualChange::TimeOrigin);
4304 pending_visual_change.time_origin = where;
4305 ensure_visual_change_idle_handler ();
4309 Editor::queue_visual_change (double fpu)
4311 pending_visual_change.add (VisualChange::ZoomLevel);
4312 pending_visual_change.frames_per_unit = fpu;
4314 ensure_visual_change_idle_handler ();
4318 Editor::queue_visual_change_y (double y)
4320 pending_visual_change.add (VisualChange::YOrigin);
4321 pending_visual_change.y_origin = y;
4323 ensure_visual_change_idle_handler ();
4327 Editor::ensure_visual_change_idle_handler ()
4329 if (pending_visual_change.idle_handler_id < 0) {
4330 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4335 Editor::_idle_visual_changer (void* arg)
4337 return static_cast<Editor*>(arg)->idle_visual_changer ();
4341 Editor::idle_visual_changer ()
4343 VisualChange::Type p = pending_visual_change.pending;
4344 pending_visual_change.pending = (VisualChange::Type) 0;
4346 double const last_time_origin = horizontal_position ();
4348 if (p & VisualChange::TimeOrigin) {
4349 /* This is a bit of a hack, but set_frames_per_unit
4350 below will (if called) end up with the
4351 CrossfadeViews looking at Editor::leftmost_frame,
4352 and if we're changing origin and zoom in the same
4353 operation it will be the wrong value unless we
4357 leftmost_frame = pending_visual_change.time_origin;
4360 if (p & VisualChange::ZoomLevel) {
4361 set_frames_per_unit (pending_visual_change.frames_per_unit);
4363 compute_fixed_ruler_scale ();
4364 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4365 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4366 update_tempo_based_rulers ();
4368 if (p & VisualChange::TimeOrigin) {
4369 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4371 if (p & VisualChange::YOrigin) {
4372 vertical_adjustment.set_value (pending_visual_change.y_origin);
4375 if (last_time_origin == horizontal_position ()) {
4376 /* changed signal not emitted */
4377 update_fixed_rulers ();
4378 redisplay_tempo (true);
4381 _summary->set_overlays_dirty ();
4383 pending_visual_change.idle_handler_id = -1;
4384 return 0; /* this is always a one-shot call */
4387 struct EditorOrderTimeAxisSorter {
4388 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4389 return a->order () < b->order ();
4394 Editor::sort_track_selection (TrackViewList* sel)
4396 EditorOrderTimeAxisSorter cmp;
4401 selection->tracks.sort (cmp);
4406 Editor::get_preferred_edit_position (bool ignore_playhead)
4409 framepos_t where = 0;
4410 EditPoint ep = _edit_point;
4412 if (entered_marker) {
4413 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position()));
4414 return entered_marker->position();
4417 if (ignore_playhead && ep == EditAtPlayhead) {
4418 ep = EditAtSelectedMarker;
4422 case EditAtPlayhead:
4423 where = _session->audible_frame();
4424 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where));
4427 case EditAtSelectedMarker:
4428 if (!selection->markers.empty()) {
4430 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4433 where = loc->start();
4437 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where));
4445 if (!mouse_frame (where, ignored)) {
4446 /* XXX not right but what can we do ? */
4450 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where));
4458 Editor::set_loop_range (framepos_t start, framepos_t end, string cmd)
4460 if (!_session) return;
4462 begin_reversible_command (cmd);
4466 if ((tll = transport_loop_location()) == 0) {
4467 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop);
4468 XMLNode &before = _session->locations()->get_state();
4469 _session->locations()->add (loc, true);
4470 _session->set_auto_loop_location (loc);
4471 XMLNode &after = _session->locations()->get_state();
4472 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4474 XMLNode &before = tll->get_state();
4475 tll->set_hidden (false, this);
4476 tll->set (start, end);
4477 XMLNode &after = tll->get_state();
4478 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4481 commit_reversible_command ();
4485 Editor::set_punch_range (framepos_t start, framepos_t end, string cmd)
4487 if (!_session) return;
4489 begin_reversible_command (cmd);
4493 if ((tpl = transport_punch_location()) == 0) {
4494 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoPunch);
4495 XMLNode &before = _session->locations()->get_state();
4496 _session->locations()->add (loc, true);
4497 _session->set_auto_loop_location (loc);
4498 XMLNode &after = _session->locations()->get_state();
4499 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4502 XMLNode &before = tpl->get_state();
4503 tpl->set_hidden (false, this);
4504 tpl->set (start, end);
4505 XMLNode &after = tpl->get_state();
4506 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4509 commit_reversible_command ();
4512 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4513 * @param rs List to which found regions are added.
4514 * @param where Time to look at.
4515 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4518 Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4520 const TrackViewList* tracks;
4523 tracks = &track_views;
4528 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4530 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4533 boost::shared_ptr<Track> tr;
4534 boost::shared_ptr<Playlist> pl;
4536 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4538 Playlist::RegionList* regions = pl->regions_at (
4539 (framepos_t) floor ( (double) where * tr->speed()));
4541 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4542 RegionView* rv = rtv->view()->find_view (*i);
4555 Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4557 const TrackViewList* tracks;
4560 tracks = &track_views;
4565 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4566 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4568 boost::shared_ptr<Track> tr;
4569 boost::shared_ptr<Playlist> pl;
4571 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4573 Playlist::RegionList* regions = pl->regions_touched (
4574 (framepos_t) floor ( (double)where * tr->speed()), max_framepos);
4576 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4578 RegionView* rv = rtv->view()->find_view (*i);
4591 /** Start with regions that are selected. Then add equivalent regions
4592 * on tracks in the same active edit-enabled route group as any of
4593 * the regions that we started with.
4597 Editor::get_regions_from_selection ()
4599 return get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
4602 /** Get regions using the following method:
4604 * Make an initial region list using the selected regions, unless
4605 * the edit point is `mouse' and the mouse is over an unselected
4606 * region. In this case, start with just that region.
4608 * Then, make an initial track list of the tracks that these
4609 * regions are on, and if the edit point is not `mouse', add the
4612 * Look at this track list and add any other tracks that are on the
4613 * same active edit-enabled route group as one of the initial tracks.
4615 * Finally take the initial region list and add any regions that are
4616 * under the edit point on one of the tracks on the track list to get
4617 * the returned region list.
4619 * The rationale here is that the mouse edit point is special in that
4620 * its position describes both a time and a track; the other edit
4621 * modes only describe a time. Hence if the edit point is `mouse' we
4622 * ignore selected tracks, as we assume the user means something by
4623 * pointing at a particular track. Also in this case we take note of
4624 * the region directly under the edit point, as there is always just one
4625 * (rather than possibly several with non-mouse edit points).
4629 Editor::get_regions_from_selection_and_edit_point ()
4631 RegionSelection regions;
4633 if (_edit_point == EditAtMouse && entered_regionview && !selection->regions.contains (entered_regionview)) {
4634 regions.add (entered_regionview);
4636 regions = selection->regions;
4639 TrackViewList tracks;
4641 if (_edit_point != EditAtMouse) {
4642 tracks = selection->tracks;
4645 /* Add any other tracks that have regions that are in the same
4646 edit-activated route group as one of our regions.
4648 for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) {
4650 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4652 if (g && g->is_active() && g->is_edit()) {
4653 tracks.add (axis_views_from_routes (g->route_list()));
4657 if (!tracks.empty()) {
4658 /* now find regions that are at the edit position on those tracks */
4659 framepos_t const where = get_preferred_edit_position ();
4660 get_regions_at (regions, where, tracks);
4666 /** Start with regions that are selected, or the entered regionview if none are selected.
4667 * Then add equivalent regions on tracks in the same active edit-enabled route group as any
4668 * of the regions that we started with.
4672 Editor::get_regions_from_selection_and_entered ()
4674 RegionSelection regions = selection->regions;
4676 if (regions.empty() && entered_regionview) {
4677 regions.add (entered_regionview);
4680 return get_equivalent_regions (regions, ARDOUR::Properties::edit.property_id);
4684 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4686 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4688 RouteTimeAxisView* tatv;
4690 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4692 boost::shared_ptr<Playlist> pl;
4693 vector<boost::shared_ptr<Region> > results;
4695 boost::shared_ptr<Track> tr;
4697 if ((tr = tatv->track()) == 0) {
4702 if ((pl = (tr->playlist())) != 0) {
4703 pl->get_region_list_equivalent_regions (region, results);
4706 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4707 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4708 regions.push_back (marv);
4717 Editor::show_rhythm_ferret ()
4719 if (rhythm_ferret == 0) {
4720 rhythm_ferret = new RhythmFerret(*this);
4723 rhythm_ferret->set_session (_session);
4724 rhythm_ferret->show ();
4725 rhythm_ferret->present ();
4729 Editor::first_idle ()
4731 MessageDialog* dialog = 0;
4733 if (track_views.size() > 1) {
4734 dialog = new MessageDialog (*this,
4735 string_compose (_("Please wait while %1 loads visual data"), PROGRAM_NAME),
4740 ARDOUR_UI::instance()->flush_pending ();
4743 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4747 // first idle adds route children (automation tracks), so we need to redisplay here
4748 _routes->redisplay ();
4756 Editor::_idle_resize (gpointer arg)
4758 return ((Editor*)arg)->idle_resize ();
4762 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4764 if (resize_idle_id < 0) {
4765 resize_idle_id = g_idle_add (_idle_resize, this);
4766 _pending_resize_amount = 0;
4769 /* make a note of the smallest resulting height, so that we can clamp the
4770 lower limit at TimeAxisView::hSmall */
4772 int32_t min_resulting = INT32_MAX;
4774 _pending_resize_amount += h;
4775 _pending_resize_view = view;
4777 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4779 if (selection->tracks.contains (_pending_resize_view)) {
4780 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4781 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4785 if (min_resulting < 0) {
4790 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4791 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4795 /** Handle pending resizing of tracks */
4797 Editor::idle_resize ()
4799 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4801 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4802 selection->tracks.contains (_pending_resize_view)) {
4804 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4805 if (*i != _pending_resize_view) {
4806 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4811 _pending_resize_amount = 0;
4813 _group_tabs->set_dirty ();
4814 resize_idle_id = -1;
4822 ENSURE_GUI_THREAD (*this, &Editor::located);
4824 playhead_cursor->set_position (_session->audible_frame ());
4825 if (_follow_playhead && !_pending_initial_locate) {
4826 reset_x_origin_to_follow_playhead ();
4829 _pending_locate_request = false;
4830 _pending_initial_locate = false;
4834 Editor::region_view_added (RegionView *)
4836 _summary->set_dirty ();
4840 Editor::region_view_removed ()
4842 _summary->set_dirty ();
4846 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4848 TrackViewList::const_iterator j = track_views.begin ();
4849 while (j != track_views.end()) {
4850 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4851 if (rtv && rtv->route() == r) {
4862 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4866 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4867 TimeAxisView* tv = axis_view_from_route (*i);
4878 Editor::handle_new_route (RouteList& routes)
4880 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4882 RouteTimeAxisView *rtv;
4883 list<RouteTimeAxisView*> new_views;
4885 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4886 boost::shared_ptr<Route> route = (*x);
4888 if (route->is_hidden() || route->is_monitor()) {
4892 DataType dt = route->input()->default_type();
4894 if (dt == ARDOUR::DataType::AUDIO) {
4895 rtv = new AudioTimeAxisView (*this, _session, *track_canvas);
4896 rtv->set_route (route);
4897 } else if (dt == ARDOUR::DataType::MIDI) {
4898 rtv = new MidiTimeAxisView (*this, _session, *track_canvas);
4899 rtv->set_route (route);
4901 throw unknown_type();
4904 new_views.push_back (rtv);
4905 track_views.push_back (rtv);
4907 rtv->effective_gain_display ();
4909 if (internal_editing()) {
4910 rtv->enter_internal_edit_mode ();
4912 rtv->leave_internal_edit_mode ();
4915 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4916 rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed));
4919 _routes->routes_added (new_views);
4920 _summary->routes_added (new_views);
4922 if (show_editor_mixer_when_tracks_arrive) {
4923 show_editor_mixer (true);
4926 editor_list_button.set_sensitive (true);
4930 Editor::timeaxisview_deleted (TimeAxisView *tv)
4932 if (_session && _session->deletion_in_progress()) {
4933 /* the situation is under control */
4937 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
4939 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
4941 _routes->route_removed (tv);
4943 if (tv == entered_track) {
4947 TimeAxisView::Children c = tv->get_child_list ();
4948 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
4949 if (entered_track == i->get()) {
4954 /* remove it from the list of track views */
4956 TrackViewList::iterator i;
4958 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
4959 i = track_views.erase (i);
4962 /* update whatever the current mixer strip is displaying, if revelant */
4964 boost::shared_ptr<Route> route;
4967 route = rtav->route ();
4970 if (current_mixer_strip && current_mixer_strip->route() == route) {
4972 TimeAxisView* next_tv;
4974 if (track_views.empty()) {
4976 } else if (i == track_views.end()) {
4977 next_tv = track_views.front();
4984 set_selected_mixer_strip (*next_tv);
4986 /* make the editor mixer strip go away setting the
4987 * button to inactive (which also unticks the menu option)
4990 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
4996 Editor::hide_track_in_display (TimeAxisView* tv, bool apply_to_selection)
4998 if (apply_to_selection) {
4999 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ) {
5001 TrackSelection::iterator j = i;
5004 hide_track_in_display (*i, false);
5009 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
5011 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
5012 // this will hide the mixer strip
5013 set_selected_mixer_strip (*tv);
5016 _routes->hide_track_in_display (*tv);
5021 Editor::sync_track_view_list_and_routes ()
5023 track_views = TrackViewList (_routes->views ());
5025 _summary->set_dirty ();
5026 _group_tabs->set_dirty ();
5028 return false; // do not call again (until needed)
5032 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
5034 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5039 /** Find a RouteTimeAxisView by the ID of its route */
5041 Editor::get_route_view_by_route_id (PBD::ID& id) const
5043 RouteTimeAxisView* v;
5045 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5046 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5047 if(v->route()->id() == id) {
5057 Editor::fit_route_group (RouteGroup *g)
5059 TrackViewList ts = axis_views_from_routes (g->route_list ());
5064 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5066 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5069 _session->cancel_audition ();
5073 if (_session->is_auditioning()) {
5074 _session->cancel_audition ();
5075 if (r == last_audition_region) {
5080 _session->audition_region (r);
5081 last_audition_region = r;
5086 Editor::hide_a_region (boost::shared_ptr<Region> r)
5088 r->set_hidden (true);
5092 Editor::show_a_region (boost::shared_ptr<Region> r)
5094 r->set_hidden (false);
5098 Editor::audition_region_from_region_list ()
5100 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5104 Editor::hide_region_from_region_list ()
5106 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5110 Editor::show_region_in_region_list ()
5112 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5116 Editor::step_edit_status_change (bool yn)
5119 start_step_editing ();
5121 stop_step_editing ();
5126 Editor::start_step_editing ()
5128 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5132 Editor::stop_step_editing ()
5134 step_edit_connection.disconnect ();
5138 Editor::check_step_edit ()
5140 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5141 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5143 mtv->check_step_edit ();
5147 return true; // do it again, till we stop
5151 Editor::scroll_press (Direction dir)
5153 ++_scroll_callbacks;
5155 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5156 /* delay the first auto-repeat */
5162 scroll_backward (1);
5170 scroll_tracks_up_line ();
5174 scroll_tracks_down_line ();
5178 /* do hacky auto-repeat */
5179 if (!_scroll_connection.connected ()) {
5181 _scroll_connection = Glib::signal_timeout().connect (
5182 sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), dir), 100
5185 _scroll_callbacks = 0;
5192 Editor::scroll_release ()
5194 _scroll_connection.disconnect ();
5197 /** Queue a change for the Editor viewport x origin to follow the playhead */
5199 Editor::reset_x_origin_to_follow_playhead ()
5201 framepos_t const frame = playhead_cursor->current_frame;
5203 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5205 if (_session->transport_speed() < 0) {
5207 if (frame > (current_page_frames() / 2)) {
5208 center_screen (frame-(current_page_frames()/2));
5210 center_screen (current_page_frames()/2);
5215 if (frame < leftmost_frame) {
5218 if (_session->transport_rolling()) {
5219 /* rolling; end up with the playhead at the right of the page */
5220 l = frame - current_page_frames ();
5222 /* not rolling: end up with the playhead 3/4 of the way along the page */
5223 l = frame - (3 * current_page_frames() / 4);
5230 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5233 if (_session->transport_rolling()) {
5234 /* rolling: end up with the playhead on the left of the page */
5235 center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5237 /* not rolling: end up with the playhead 1/4 of the way along the page */
5238 center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5246 Editor::super_rapid_screen_update ()
5248 if (!_session || !_session->engine().running()) {
5252 /* METERING / MIXER STRIPS */
5254 /* update track meters, if required */
5255 if (is_mapped() && meters_running) {
5256 RouteTimeAxisView* rtv;
5257 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5258 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5259 rtv->fast_update ();
5264 /* and any current mixer strip */
5265 if (current_mixer_strip) {
5266 current_mixer_strip->fast_update ();
5269 /* PLAYHEAD AND VIEWPORT */
5271 framepos_t const frame = _session->audible_frame();
5273 /* There are a few reasons why we might not update the playhead / viewport stuff:
5275 * 1. we don't update things when there's a pending locate request, otherwise
5276 * when the editor requests a locate there is a chance that this method
5277 * will move the playhead before the locate request is processed, causing
5279 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5280 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5283 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5285 last_update_frame = frame;
5287 if (!_dragging_playhead) {
5288 playhead_cursor->set_position (frame);
5291 if (!_stationary_playhead) {
5293 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5294 reset_x_origin_to_follow_playhead ();
5299 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5303 // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5304 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5305 if (target <= 0.0) {
5308 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5309 target = (target * 0.15) + (current * 0.85);
5315 set_horizontal_position (current);
5324 Editor::session_going_away ()
5326 _have_idled = false;
5328 _session_connections.drop_connections ();
5330 super_rapid_screen_update_connection.disconnect ();
5332 selection->clear ();
5333 cut_buffer->clear ();
5335 clicked_regionview = 0;
5336 clicked_axisview = 0;
5337 clicked_routeview = 0;
5338 clicked_crossfadeview = 0;
5339 entered_regionview = 0;
5341 last_update_frame = 0;
5344 playhead_cursor->canvas_item.hide ();
5346 /* rip everything out of the list displays */
5350 _route_groups->clear ();
5352 /* do this first so that deleting a track doesn't reset cms to null
5353 and thus cause a leak.
5356 if (current_mixer_strip) {
5357 if (current_mixer_strip->get_parent() != 0) {
5358 global_hpacker.remove (*current_mixer_strip);
5360 delete current_mixer_strip;
5361 current_mixer_strip = 0;
5364 /* delete all trackviews */
5366 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5369 track_views.clear ();
5371 zoom_range_clock->set_session (0);
5372 nudge_clock->set_session (0);
5374 editor_list_button.set_active(false);
5375 editor_list_button.set_sensitive(false);
5377 /* clear tempo/meter rulers */
5378 remove_metric_marks ();
5380 clear_marker_display ();
5382 delete current_bbt_points;
5383 current_bbt_points = 0;
5385 /* get rid of any existing editor mixer strip */
5387 WindowTitle title(Glib::get_application_name());
5388 title += _("Editor");
5390 set_title (title.get_string());
5392 SessionHandlePtr::session_going_away ();
5397 Editor::show_editor_list (bool yn)
5400 _the_notebook.show ();
5402 _the_notebook.hide ();
5407 Editor::change_region_layering_order ()
5409 framepos_t const position = get_preferred_edit_position ();
5411 if (!clicked_routeview) {
5412 if (layering_order_editor) {
5413 layering_order_editor->hide ();
5418 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
5424 boost::shared_ptr<Playlist> pl = track->playlist();
5430 if (layering_order_editor == 0) {
5431 layering_order_editor = new RegionLayeringOrderEditor(*this);
5434 layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position);
5435 layering_order_editor->maybe_present ();
5439 Editor::update_region_layering_order_editor ()
5441 if (layering_order_editor && layering_order_editor->is_visible ()) {
5442 change_region_layering_order ();
5447 Editor::setup_fade_images ()
5449 _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear")));
5450 _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut")));
5451 _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut")));
5452 _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut")));
5453 _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut")));
5455 _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear")));
5456 _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut")));
5457 _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut")));
5458 _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut")));
5459 _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut")));
5463 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
5465 Editor::action_menu_item (std::string const & name)
5467 Glib::RefPtr<Action> a = editor_actions->get_action (name);
5470 return *manage (a->create_menu_item ());
5474 Editor::resize_text_widgets ()
5476 set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_FUDGE+10, 15);
5477 set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_FUDGE+10, 15);
5478 set_size_request_to_display_given_text (snap_type_selector, snap_type_strings, COMBO_FUDGE+10, 15);
5479 set_size_request_to_display_given_text (snap_mode_selector, snap_mode_strings, COMBO_FUDGE+10, 15);
5480 set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_FUDGE+10, 15);
5484 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
5486 EventBox* b = manage (new EventBox);
5487 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
5488 Label* l = manage (new Label (name));
5492 _the_notebook.append_page (widget, *b);
5496 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
5498 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
5499 _the_notebook.set_current_page (_the_notebook.page_num (*page));
5502 if (ev->type == GDK_2BUTTON_PRESS) {
5504 /* double-click on a notebook tab shrinks or expands the notebook */
5506 if (_notebook_shrunk) {
5507 edit_pane.set_position (pre_maximal_horizontal_pane_position);
5508 _notebook_shrunk = false;
5510 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
5511 edit_pane.set_position (edit_pane.get_position() + page->get_width());
5512 _notebook_shrunk = true;
5520 Editor::popup_control_point_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
5522 using namespace Menu_Helpers;
5524 MenuList& items = _control_point_context_menu.items ();
5527 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun (*this, &Editor::edit_control_point), item)));
5528 items.push_back (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun (*this, &Editor::remove_control_point), item)));
5529 if (!can_remove_control_point (item)) {
5530 items.back().set_sensitive (false);
5533 _control_point_context_menu.popup (event->button.button, event->button.time);