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_track.h"
62 #include "ardour/audioplaylist.h"
63 #include "ardour/audioregion.h"
64 #include "ardour/location.h"
65 #include "ardour/midi_region.h"
66 #include "ardour/plugin_manager.h"
67 #include "ardour/profile.h"
68 #include "ardour/route_group.h"
69 #include "ardour/session_directory.h"
70 #include "ardour/session_route.h"
71 #include "ardour/session_state_utils.h"
72 #include "ardour/tempo.h"
73 #include "ardour/utils.h"
74 #include "ardour/session_playlists.h"
75 #include "ardour/audioengine.h"
77 #include "control_protocol/control_protocol.h"
81 #include "analysis_window.h"
82 #include "audio_clock.h"
83 #include "audio_region_view.h"
84 #include "audio_streamview.h"
85 #include "audio_time_axis.h"
86 #include "automation_time_axis.h"
87 #include "bundle_manager.h"
88 #include "canvas-noevent-text.h"
89 #include "canvas_impl.h"
90 #include "crossfade_edit.h"
91 #include "crossfade_view.h"
95 #include "editor_cursors.h"
96 #include "editor_drag.h"
97 #include "editor_group_tabs.h"
98 #include "editor_locations.h"
99 #include "editor_regions.h"
100 #include "editor_route_groups.h"
101 #include "editor_routes.h"
102 #include "editor_snapshots.h"
103 #include "editor_summary.h"
104 #include "global_port_matrix.h"
105 #include "gui_object.h"
106 #include "gui_thread.h"
107 #include "keyboard.h"
109 #include "midi_time_axis.h"
110 #include "mixer_strip.h"
111 #include "mixer_ui.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_("zoom range"), 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)))
274 , image_socket_listener(0)
279 , nudge_clock (new AudioClock (X_("nudge"), false, X_("nudge"), true, false, true))
280 , meters_running(false)
281 , _pending_locate_request (false)
282 , _pending_initial_locate (false)
283 , _last_cut_copy_source_track (0)
285 , _region_selection_change_updates_region_list (true)
286 , _following_mixer_selection (false)
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;
310 snap_type_strings = I18N (_snap_type_strings);
311 snap_mode_strings = I18N (_snap_mode_strings);
312 zoom_focus_strings = I18N (_zoom_focus_strings);
313 edit_point_strings = I18N (_edit_point_strings);
314 #ifdef USE_RUBBERBAND
315 rb_opt_strings = I18N (_rb_opt_strings);
319 snap_threshold = 5.0;
320 bbt_beat_subdivision = 4;
323 last_autoscroll_x = 0;
324 last_autoscroll_y = 0;
325 autoscroll_active = false;
326 autoscroll_timeout_tag = -1;
331 current_interthread_info = 0;
332 _show_measures = true;
333 show_gain_after_trim = false;
335 have_pending_keyboard_selection = false;
336 _follow_playhead = true;
337 _stationary_playhead = false;
338 _xfade_visibility = true;
339 editor_ruler_menu = 0;
340 no_ruler_shown_update = false;
342 range_marker_menu = 0;
343 marker_menu_item = 0;
344 tempo_or_meter_marker_menu = 0;
345 transport_marker_menu = 0;
346 new_transport_marker_menu = 0;
347 editor_mixer_strip_width = Wide;
348 show_editor_mixer_when_tracks_arrive = false;
349 region_edit_menu_split_multichannel_item = 0;
350 region_edit_menu_split_item = 0;
353 current_stepping_trackview = 0;
355 entered_regionview = 0;
357 clear_entered_track = false;
360 button_release_can_deselect = true;
361 _dragging_playhead = false;
362 _dragging_edit_point = false;
363 select_new_marker = false;
365 layering_order_editor = 0;
366 no_save_visual = false;
369 scrubbing_direction = 0;
373 location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get();
374 location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get();
375 location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get();
376 location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get();
377 location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get();
379 _edit_point = EditAtMouse;
380 _internal_editing = false;
381 current_canvas_cursor = 0;
383 frames_per_unit = 2048; /* too early to use reset_zoom () */
385 _scroll_callbacks = 0;
387 zoom_focus = ZoomFocusLeft;
388 set_zoom_focus (ZoomFocusLeft);
389 zoom_range_clock->ValueChanged.connect (sigc::mem_fun(*this, &Editor::zoom_adjustment_changed));
391 bbt_label.set_name ("EditorTimeButton");
392 bbt_label.set_size_request (-1, (int)timebar_height);
393 bbt_label.set_alignment (1.0, 0.5);
394 bbt_label.set_padding (5,0);
396 bbt_label.set_no_show_all();
397 minsec_label.set_name ("EditorTimeButton");
398 minsec_label.set_size_request (-1, (int)timebar_height);
399 minsec_label.set_alignment (1.0, 0.5);
400 minsec_label.set_padding (5,0);
401 minsec_label.hide ();
402 minsec_label.set_no_show_all();
403 timecode_label.set_name ("EditorTimeButton");
404 timecode_label.set_size_request (-1, (int)timebar_height);
405 timecode_label.set_alignment (1.0, 0.5);
406 timecode_label.set_padding (5,0);
407 timecode_label.hide ();
408 timecode_label.set_no_show_all();
409 samples_label.set_name ("EditorTimeButton");
410 samples_label.set_size_request (-1, (int)timebar_height);
411 samples_label.set_alignment (1.0, 0.5);
412 samples_label.set_padding (5,0);
413 samples_label.hide ();
414 samples_label.set_no_show_all();
416 tempo_label.set_name ("EditorTimeButton");
417 tempo_label.set_size_request (-1, (int)timebar_height);
418 tempo_label.set_alignment (1.0, 0.5);
419 tempo_label.set_padding (5,0);
421 tempo_label.set_no_show_all();
423 meter_label.set_name ("EditorTimeButton");
424 meter_label.set_size_request (-1, (int)timebar_height);
425 meter_label.set_alignment (1.0, 0.5);
426 meter_label.set_padding (5,0);
428 meter_label.set_no_show_all();
430 mark_label.set_name ("EditorTimeButton");
431 mark_label.set_size_request (-1, (int)timebar_height);
432 mark_label.set_alignment (1.0, 0.5);
433 mark_label.set_padding (5,0);
435 mark_label.set_no_show_all();
437 cd_mark_label.set_name ("EditorTimeButton");
438 cd_mark_label.set_size_request (-1, (int)timebar_height);
439 cd_mark_label.set_alignment (1.0, 0.5);
440 cd_mark_label.set_padding (5,0);
441 cd_mark_label.hide();
442 cd_mark_label.set_no_show_all();
444 range_mark_label.set_name ("EditorTimeButton");
445 range_mark_label.set_size_request (-1, (int)timebar_height);
446 range_mark_label.set_alignment (1.0, 0.5);
447 range_mark_label.set_padding (5,0);
448 range_mark_label.hide();
449 range_mark_label.set_no_show_all();
451 transport_mark_label.set_name ("EditorTimeButton");
452 transport_mark_label.set_size_request (-1, (int)timebar_height);
453 transport_mark_label.set_alignment (1.0, 0.5);
454 transport_mark_label.set_padding (5,0);
455 transport_mark_label.hide();
456 transport_mark_label.set_no_show_all();
458 initialize_rulers ();
459 initialize_canvas ();
461 _summary = new EditorSummary (this);
463 selection->TimeChanged.connect (sigc::mem_fun(*this, &Editor::time_selection_changed));
464 selection->TracksChanged.connect (sigc::mem_fun(*this, &Editor::track_selection_changed));
466 editor_regions_selection_changed_connection = selection->RegionsChanged.connect (sigc::mem_fun(*this, &Editor::region_selection_changed));
468 selection->PointsChanged.connect (sigc::mem_fun(*this, &Editor::point_selection_changed));
469 selection->MarkersChanged.connect (sigc::mem_fun(*this, &Editor::marker_selection_changed));
471 edit_controls_vbox.set_spacing (0);
472 vertical_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &Editor::tie_vertical_scrolling), true);
473 track_canvas->signal_map_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_map_handler));
475 HBox* h = manage (new HBox);
476 _group_tabs = new EditorGroupTabs (this);
477 h->pack_start (*_group_tabs, PACK_SHRINK);
478 h->pack_start (edit_controls_vbox);
479 controls_layout.add (*h);
481 controls_layout.set_name ("EditControlsBase");
482 controls_layout.add_events (Gdk::SCROLL_MASK);
483 controls_layout.signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::control_layout_scroll), false);
485 controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
486 controls_layout.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_release));
488 _cursors = new MouseCursors;
490 ArdourCanvas::Canvas* time_pad = manage(new ArdourCanvas::Canvas());
491 ArdourCanvas::SimpleLine* pad_line_1 = manage(new ArdourCanvas::SimpleLine(*time_pad->root(),
492 0.0, 1.0, 100.0, 1.0));
494 pad_line_1->property_color_rgba() = 0xFF0000FF;
499 time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2);
500 time_canvas_vbox.set_size_request (-1, -1);
502 ruler_label_event_box.add (ruler_label_vbox);
503 ruler_label_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
504 ruler_label_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
506 time_button_event_box.add (time_button_vbox);
507 time_button_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
508 time_button_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
510 /* these enable us to have a dedicated window (for cursor setting, etc.)
511 for the canvas areas.
514 track_canvas_event_box.add (*track_canvas);
516 time_canvas_event_box.add (time_canvas_vbox);
517 time_canvas_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
519 edit_packer.set_col_spacings (0);
520 edit_packer.set_row_spacings (0);
521 edit_packer.set_homogeneous (false);
522 edit_packer.set_border_width (0);
523 edit_packer.set_name ("EditorWindow");
525 /* labels for the rulers */
526 edit_packer.attach (ruler_label_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0);
527 /* labels for the marker "tracks" */
528 edit_packer.attach (time_button_event_box, 1, 2, 1, 2, FILL, SHRINK, 0, 0);
530 edit_packer.attach (time_canvas_event_box, 2, 3, 0, 1, FILL|EXPAND, FILL, 0, 0);
532 edit_packer.attach (controls_layout, 0, 2, 2, 3, FILL, FILL|EXPAND, 0, 0);
534 edit_packer.attach (track_canvas_event_box, 2, 3, 1, 3, FILL|EXPAND, FILL|EXPAND, 0, 0);
536 bottom_hbox.set_border_width (2);
537 bottom_hbox.set_spacing (3);
539 _route_groups = new EditorRouteGroups (this);
540 _routes = new EditorRoutes (this);
541 _regions = new EditorRegions (this);
542 _snapshots = new EditorSnapshots (this);
543 _locations = new EditorLocations (this);
545 add_notebook_page (_("Regions"), _regions->widget ());
546 add_notebook_page (_("Tracks & Busses"), _routes->widget ());
547 add_notebook_page (_("Snapshots"), _snapshots->widget ());
548 add_notebook_page (_("Route Groups"), _route_groups->widget ());
549 add_notebook_page (_("Ranges & Marks"), _locations->widget ());
551 _the_notebook.set_show_tabs (true);
552 _the_notebook.set_scrollable (true);
553 _the_notebook.popup_disable ();
554 _the_notebook.set_tab_pos (Gtk::POS_RIGHT);
555 _the_notebook.show_all ();
557 post_maximal_editor_width = 0;
558 post_maximal_horizontal_pane_position = 0;
559 post_maximal_editor_height = 0;
560 post_maximal_vertical_pane_position = 0;
561 _notebook_shrunk = false;
563 editor_summary_pane.pack1(edit_packer);
565 Button* summary_arrows_left_left = manage (new Button);
566 summary_arrows_left_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
567 summary_arrows_left_left->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), LEFT)));
568 summary_arrows_left_left->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
570 Button* summary_arrows_left_right = manage (new Button);
571 summary_arrows_left_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
572 summary_arrows_left_right->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), RIGHT)));
573 summary_arrows_left_right->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
575 VBox* summary_arrows_left = manage (new VBox);
576 summary_arrows_left->pack_start (*summary_arrows_left_left);
577 summary_arrows_left->pack_start (*summary_arrows_left_right);
579 Button* summary_arrows_right_up = manage (new Button);
580 summary_arrows_right_up->add (*manage (new Arrow (ARROW_UP, SHADOW_NONE)));
581 summary_arrows_right_up->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), UP)));
582 summary_arrows_right_up->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
584 Button* summary_arrows_right_down = manage (new Button);
585 summary_arrows_right_down->add (*manage (new Arrow (ARROW_DOWN, SHADOW_NONE)));
586 summary_arrows_right_down->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), DOWN)));
587 summary_arrows_right_down->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
589 VBox* summary_arrows_right = manage (new VBox);
590 summary_arrows_right->pack_start (*summary_arrows_right_up);
591 summary_arrows_right->pack_start (*summary_arrows_right_down);
593 Frame* summary_frame = manage (new Frame);
594 summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN);
596 summary_frame->add (*_summary);
597 summary_frame->show ();
599 _summary_hbox.pack_start (*summary_arrows_left, false, false);
600 _summary_hbox.pack_start (*summary_frame, true, true);
601 _summary_hbox.pack_start (*summary_arrows_right, false, false);
603 editor_summary_pane.pack2 (_summary_hbox);
605 edit_pane.pack1 (editor_summary_pane, true, true);
606 edit_pane.pack2 (_the_notebook, false, true);
608 editor_summary_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun (*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&editor_summary_pane)));
610 /* XXX: editor_summary_pane might need similar special OS X treatment to the edit_pane */
612 edit_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&edit_pane)));
614 Glib::PropertyProxy<int> proxy = edit_pane.property_position();
615 proxy.signal_changed().connect (bind (sigc::ptr_fun (pane_size_watcher), static_cast<Paned*> (&edit_pane)));
617 top_hbox.pack_start (toolbar_frame);
619 HBox *hbox = manage (new HBox);
620 hbox->pack_start (edit_pane, true, true);
622 global_vpacker.pack_start (top_hbox, false, false);
623 global_vpacker.pack_start (*hbox, true, true);
625 global_hpacker.pack_start (global_vpacker, true, true);
627 set_name ("EditorWindow");
628 add_accel_group (ActionManager::ui_manager->get_accel_group());
630 status_bar_hpacker.show ();
632 vpacker.pack_end (status_bar_hpacker, false, false);
633 vpacker.pack_end (global_hpacker, true, true);
635 /* register actions now so that set_state() can find them and set toggles/checks etc */
638 /* when we start using our own keybinding system for the editor, this
639 * will be uncommented
645 _snap_type = SnapToBeat;
646 set_snap_to (_snap_type);
647 _snap_mode = SnapOff;
648 set_snap_mode (_snap_mode);
649 set_mouse_mode (MouseObject, true);
650 pre_internal_mouse_mode = MouseObject;
651 set_edit_point_preference (EditAtMouse, true);
653 _playlist_selector = new PlaylistSelector();
654 _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
656 RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), ui_bind (&Editor::catch_vanishing_regionview, this, _1), gui_context());
660 nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
661 nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
663 nudge_forward_button.set_name ("TransportButton");
664 nudge_backward_button.set_name ("TransportButton");
666 fade_context_menu.set_name ("ArdourContextMenu");
668 /* icons, titles, WM stuff */
670 list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
671 Glib::RefPtr<Gdk::Pixbuf> icon;
673 if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
674 window_icons.push_back (icon);
676 if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
677 window_icons.push_back (icon);
679 if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
680 window_icons.push_back (icon);
682 if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
683 window_icons.push_back (icon);
685 if (!window_icons.empty()) {
686 // set_icon_list (window_icons);
687 set_default_icon_list (window_icons);
690 WindowTitle title(Glib::get_application_name());
691 title += _("Editor");
692 set_title (title.get_string());
693 set_wmclass (X_("ardour_editor"), PROGRAM_NAME);
696 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
698 signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
699 signal_delete_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
701 /* allow external control surfaces/protocols to do various things */
703 ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context());
704 ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context());
705 ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context());
706 ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), ui_bind (&Editor::control_scroll, this, _1), gui_context());
707 ControlProtocol::SelectByRID.connect (*this, invalidator (*this), ui_bind (&Editor::control_select, this, _1), gui_context());
708 BasicUI::AccessAction.connect (*this, invalidator (*this), ui_bind (&Editor::access_action, this, _1, _2), gui_context());
710 /* problematic: has to return a value and thus cannot be x-thread */
712 Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
714 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
716 TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
718 _ignore_region_action = false;
719 _last_region_menu_was_main = false;
720 _popup_region_menu_item = 0;
722 _show_marker_lines = false;
723 _over_region_trim_target = false;
725 /* Button bindings */
727 button_bindings = new Bindings;
729 XMLNode* node = button_settings();
731 for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) {
732 button_bindings->load (**i);
739 setup_fade_images ();
745 if(image_socket_listener) {
746 if(image_socket_listener->is_connected())
748 image_socket_listener->close_connection() ;
751 delete image_socket_listener ;
752 image_socket_listener = 0 ;
756 delete button_bindings;
758 delete _route_groups;
764 Editor::button_settings () const
766 XMLNode* settings = ARDOUR_UI::instance()->editor_settings();
767 XMLNode* node = find_named_node (*settings, X_("Buttons"));
770 cerr << "new empty Button node\n";
771 node = new XMLNode (X_("Buttons"));
778 Editor::add_toplevel_controls (Container& cont)
780 vpacker.pack_start (cont, false, false);
785 Editor::catch_vanishing_regionview (RegionView *rv)
787 /* note: the selection will take care of the vanishing
788 audioregionview by itself.
791 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
795 if (clicked_regionview == rv) {
796 clicked_regionview = 0;
799 if (entered_regionview == rv) {
800 set_entered_regionview (0);
803 if (!_all_region_actions_sensitized) {
804 sensitize_all_region_actions (true);
807 _over_region_trim_target = false;
811 Editor::set_entered_regionview (RegionView* rv)
813 if (rv == entered_regionview) {
817 if (entered_regionview) {
818 entered_regionview->exited ();
821 if ((entered_regionview = rv) != 0) {
822 entered_regionview->entered (internal_editing ());
825 if (!_all_region_actions_sensitized && _last_region_menu_was_main) {
826 /* This RegionView entry might have changed what region actions
827 are allowed, so sensitize them all in case a key is pressed.
829 sensitize_all_region_actions (true);
834 Editor::set_entered_track (TimeAxisView* tav)
837 entered_track->exited ();
840 if ((entered_track = tav) != 0) {
841 entered_track->entered ();
846 Editor::show_window ()
848 if (!is_visible ()) {
851 /* XXX: this is a bit unfortunate; it would probably
852 be nicer if we could just call show () above rather
853 than needing the show_all ()
856 /* re-hide stuff if necessary */
857 editor_list_button_toggled ();
858 parameter_changed ("show-summary");
859 parameter_changed ("show-group-tabs");
860 parameter_changed ("show-zoom-tools");
862 /* now reset all audio_time_axis heights, because widgets might need
868 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
869 tv = (static_cast<TimeAxisView*>(*i));
873 if (current_mixer_strip) {
874 current_mixer_strip->hide_things ();
875 current_mixer_strip->parameter_changed ("mixer-strip-visibility");
883 Editor::instant_save ()
885 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
890 _session->add_instant_xml(get_state());
892 Config->add_instant_xml(get_state());
897 Editor::zoom_adjustment_changed ()
903 double fpu = zoom_range_clock->current_duration() / _canvas_width;
907 zoom_range_clock->set ((framepos_t) floor (fpu * _canvas_width));
908 } else if (fpu > _session->current_end_frame() / _canvas_width) {
909 fpu = _session->current_end_frame() / _canvas_width;
910 zoom_range_clock->set ((framepos_t) floor (fpu * _canvas_width));
917 Editor::control_select (uint32_t rid)
919 /* handles the (static) signal from the ControlProtocol class that
920 * requests setting the selected track to a given RID
927 boost::shared_ptr<Route> r = _session->route_by_remote_id (rid);
933 TimeAxisView* tav = axis_view_from_route (r);
936 selection->set (tav);
938 selection->clear_tracks ();
943 Editor::control_scroll (float fraction)
945 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
951 double step = fraction * current_page_frames();
954 _control_scroll_target is an optional<T>
956 it acts like a pointer to an framepos_t, with
957 a operator conversion to boolean to check
958 that it has a value could possibly use
959 playhead_cursor->current_frame to store the
960 value and a boolean in the class to know
961 when it's out of date
964 if (!_control_scroll_target) {
965 _control_scroll_target = _session->transport_frame();
966 _dragging_playhead = true;
969 if ((fraction < 0.0f) && (*_control_scroll_target < (framepos_t) fabs(step))) {
970 *_control_scroll_target = 0;
971 } else if ((fraction > 0.0f) && (max_framepos - *_control_scroll_target < step)) {
972 *_control_scroll_target = max_framepos - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
974 *_control_scroll_target += (framepos_t) floor (step);
977 /* move visuals, we'll catch up with it later */
979 playhead_cursor->set_position (*_control_scroll_target);
980 UpdateAllTransportClocks (*_control_scroll_target);
982 if (*_control_scroll_target > (current_page_frames() / 2)) {
983 /* try to center PH in window */
984 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
990 Now we do a timeout to actually bring the session to the right place
991 according to the playhead. This is to avoid reading disk buffers on every
992 call to control_scroll, which is driven by ScrollTimeline and therefore
993 probably by a control surface wheel which can generate lots of events.
995 /* cancel the existing timeout */
997 control_scroll_connection.disconnect ();
999 /* add the next timeout */
1001 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
1005 Editor::deferred_control_scroll (framepos_t /*target*/)
1007 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
1008 // reset for next stream
1009 _control_scroll_target = boost::none;
1010 _dragging_playhead = false;
1015 Editor::access_action (std::string action_group, std::string action_item)
1021 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
1024 act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
1032 Editor::on_realize ()
1034 Window::on_realize ();
1039 Editor::map_position_change (framepos_t frame)
1041 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, frame)
1043 if (_session == 0) {
1047 if (_follow_playhead) {
1048 center_screen (frame);
1051 playhead_cursor->set_position (frame);
1055 Editor::center_screen (framepos_t frame)
1057 double page = _canvas_width * frames_per_unit;
1059 /* if we're off the page, then scroll.
1062 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
1063 center_screen_internal (frame, page);
1068 Editor::center_screen_internal (framepos_t frame, float page)
1073 frame -= (framepos_t) page;
1078 reset_x_origin (frame);
1083 Editor::update_title ()
1085 ENSURE_GUI_THREAD (*this, &Editor::update_title)
1088 bool dirty = _session->dirty();
1090 string session_name;
1092 if (_session->snap_name() != _session->name()) {
1093 session_name = _session->snap_name();
1095 session_name = _session->name();
1099 session_name = "*" + session_name;
1102 WindowTitle title(session_name);
1103 title += Glib::get_application_name();
1104 set_title (title.get_string());
1109 Editor::set_session (Session *t)
1111 SessionHandlePtr::set_session (t);
1117 zoom_range_clock->set_session (_session);
1118 _playlist_selector->set_session (_session);
1119 nudge_clock->set_session (_session);
1120 _summary->set_session (_session);
1121 _group_tabs->set_session (_session);
1122 _route_groups->set_session (_session);
1123 _regions->set_session (_session);
1124 _snapshots->set_session (_session);
1125 _routes->set_session (_session);
1126 _locations->set_session (_session);
1128 if (rhythm_ferret) {
1129 rhythm_ferret->set_session (_session);
1132 if (analysis_window) {
1133 analysis_window->set_session (_session);
1137 sfbrowser->set_session (_session);
1140 compute_fixed_ruler_scale ();
1142 /* Make sure we have auto loop and auto punch ranges */
1144 Location* loc = _session->locations()->auto_loop_location();
1146 loc = new Location (*_session, 0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1148 if (loc->start() == loc->end()) {
1149 loc->set_end (loc->start() + 1);
1152 _session->locations()->add (loc, false);
1153 _session->set_auto_loop_location (loc);
1156 loc->set_name (_("Loop"));
1159 loc = _session->locations()->auto_punch_location();
1162 loc = new Location (*_session, 0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1164 if (loc->start() == loc->end()) {
1165 loc->set_end (loc->start() + 1);
1168 _session->locations()->add (loc, false);
1169 _session->set_auto_punch_location (loc);
1172 loc->set_name (_("Punch"));
1175 refresh_location_display ();
1177 /* This must happen after refresh_location_display(), as (amongst other things) we restore
1178 the selected Marker; this needs the LocationMarker list to be available.
1180 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1181 set_state (*node, Stateful::loading_state_version);
1183 /* catch up with the playhead */
1185 _session->request_locate (playhead_cursor->current_frame);
1186 _pending_initial_locate = true;
1190 /* These signals can all be emitted by a non-GUI thread. Therefore the
1191 handlers for them must not attempt to directly interact with the GUI,
1192 but use Gtkmm2ext::UI::instance()->call_slot();
1195 _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), ui_bind(&Editor::step_edit_status_change, this, _1), gui_context());
1196 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1197 _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context());
1198 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context());
1199 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1200 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context());
1201 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1202 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
1203 _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Editor::session_state_saved, this, _1), gui_context());
1204 _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context());
1205 _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&Editor::location_gone, this, _1), gui_context());
1206 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1207 _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display, this), gui_context());
1208 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1210 if (Profile->get_sae()) {
1211 Timecode::BBT_Time bbt;
1215 framepos_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1);
1216 nudge_clock->set_mode(AudioClock::BBT);
1217 nudge_clock->set (pos, true);
1220 nudge_clock->set_mode (AudioClock::Timecode);
1221 nudge_clock->set (_session->frame_rate() * 5, true);
1224 playhead_cursor->canvas_item.show ();
1226 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1227 Config->map_parameters (pc);
1228 _session->config.map_parameters (pc);
1230 restore_ruler_visibility ();
1231 //tempo_map_changed (PropertyChange (0));
1232 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1234 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1235 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1238 super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
1239 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1242 switch (_snap_type) {
1243 case SnapToRegionStart:
1244 case SnapToRegionEnd:
1245 case SnapToRegionSync:
1246 case SnapToRegionBoundary:
1247 build_region_boundary_cache ();
1254 /* register for undo history */
1255 _session->register_with_memento_command_factory(id(), this);
1257 ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated));
1259 start_updating_meters ();
1263 Editor::action_pre_activated (Glib::RefPtr<Action> const & a)
1265 if (a->get_name() == "RegionMenu") {
1266 /* When the main menu's region menu is opened, we setup the actions so that they look right
1267 in the menu. I can't find a way of getting a signal when this menu is subsequently closed,
1268 so we resensitize all region actions when the entered regionview or the region selection
1269 changes. HOWEVER we can't always resensitize on entered_regionview change because that
1270 happens after the region context menu is opened. So we set a flag here, too.
1274 sensitize_the_right_region_actions ();
1275 _last_region_menu_was_main = true;
1279 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1281 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1283 using namespace Menu_Helpers;
1284 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1287 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1291 MenuList& items (fade_context_menu.items());
1295 switch (item_type) {
1297 case FadeInHandleItem:
1298 if (arv->audio_region()->fade_in_active()) {
1299 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1301 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1304 items.push_back (SeparatorElem());
1306 if (Profile->get_sae()) {
1308 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)));
1309 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)));
1316 *_fade_in_images[FadeLinear],
1317 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)
1321 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1326 *_fade_in_images[FadeFast],
1327 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)
1330 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1335 *_fade_in_images[FadeLogB],
1336 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogB)
1339 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1344 *_fade_in_images[FadeLogA],
1345 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogA)
1348 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1353 *_fade_in_images[FadeSlow],
1354 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow)
1357 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1363 case FadeOutHandleItem:
1364 if (arv->audio_region()->fade_out_active()) {
1365 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1367 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1370 items.push_back (SeparatorElem());
1372 if (Profile->get_sae()) {
1373 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)));
1374 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)));
1380 *_fade_out_images[FadeLinear],
1381 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)
1385 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1390 *_fade_out_images[FadeFast],
1391 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)
1394 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1399 *_fade_out_images[FadeLogB],
1400 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogA)
1403 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1408 *_fade_out_images[FadeLogA],
1409 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogB)
1412 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1417 *_fade_out_images[FadeSlow],
1418 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast)
1421 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1427 fatal << _("programming error: ")
1428 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1433 fade_context_menu.popup (button, time);
1437 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1439 using namespace Menu_Helpers;
1440 Menu* (Editor::*build_menu_function)();
1443 switch (item_type) {
1445 case RegionViewName:
1446 case RegionViewNameHighlight:
1447 case LeftFrameHandle:
1448 case RightFrameHandle:
1449 if (with_selection) {
1450 build_menu_function = &Editor::build_track_selection_context_menu;
1452 build_menu_function = &Editor::build_track_region_context_menu;
1457 if (with_selection) {
1458 build_menu_function = &Editor::build_track_selection_context_menu;
1460 build_menu_function = &Editor::build_track_context_menu;
1464 case CrossfadeViewItem:
1465 build_menu_function = &Editor::build_track_crossfade_context_menu;
1469 if (clicked_routeview->track()) {
1470 build_menu_function = &Editor::build_track_context_menu;
1472 build_menu_function = &Editor::build_track_bus_context_menu;
1477 /* probably shouldn't happen but if it does, we don't care */
1481 menu = (this->*build_menu_function)();
1482 menu->set_name ("ArdourContextMenu");
1484 /* now handle specific situations */
1486 switch (item_type) {
1488 case RegionViewName:
1489 case RegionViewNameHighlight:
1490 case LeftFrameHandle:
1491 case RightFrameHandle:
1492 if (!with_selection) {
1493 if (region_edit_menu_split_item) {
1494 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1495 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1497 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1500 if (region_edit_menu_split_multichannel_item) {
1501 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1502 region_edit_menu_split_multichannel_item->set_sensitive (true);
1504 region_edit_menu_split_multichannel_item->set_sensitive (false);
1513 case CrossfadeViewItem:
1520 /* probably shouldn't happen but if it does, we don't care */
1524 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1526 /* Bounce to disk */
1528 using namespace Menu_Helpers;
1529 MenuList& edit_items = menu->items();
1531 edit_items.push_back (SeparatorElem());
1533 switch (clicked_routeview->audio_track()->freeze_state()) {
1534 case AudioTrack::NoFreeze:
1535 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1538 case AudioTrack::Frozen:
1539 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1542 case AudioTrack::UnFrozen:
1543 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1549 if (item_type == StreamItem && clicked_routeview) {
1550 clicked_routeview->build_underlay_menu(menu);
1553 /* When the region menu is opened, we setup the actions so that they look right
1556 sensitize_the_right_region_actions ();
1557 _last_region_menu_was_main = false;
1559 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1560 menu->popup (button, time);
1564 Editor::build_track_context_menu ()
1566 using namespace Menu_Helpers;
1568 MenuList& edit_items = track_context_menu.items();
1571 add_dstream_context_items (edit_items);
1572 return &track_context_menu;
1576 Editor::build_track_bus_context_menu ()
1578 using namespace Menu_Helpers;
1580 MenuList& edit_items = track_context_menu.items();
1583 add_bus_context_items (edit_items);
1584 return &track_context_menu;
1588 Editor::build_track_region_context_menu ()
1590 using namespace Menu_Helpers;
1591 MenuList& edit_items = track_region_context_menu.items();
1594 /* we've just cleared the track region context menu, so the menu that these
1595 two items were on will have disappeared; stop them dangling.
1597 region_edit_menu_split_item = 0;
1598 region_edit_menu_split_multichannel_item = 0;
1600 /* we might try to use items that are currently attached to a crossfade menu,
1603 track_crossfade_context_menu.items().clear ();
1605 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1608 boost::shared_ptr<Track> tr;
1609 boost::shared_ptr<Playlist> pl;
1611 if ((tr = rtv->track())) {
1612 add_region_context_items (edit_items, tr);
1616 add_dstream_context_items (edit_items);
1618 return &track_region_context_menu;
1622 Editor::build_track_crossfade_context_menu ()
1624 using namespace Menu_Helpers;
1625 MenuList& edit_items = track_crossfade_context_menu.items();
1626 edit_items.clear ();
1628 /* we might try to use items that are currently attached to a crossfade menu,
1631 track_region_context_menu.items().clear ();
1633 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1636 boost::shared_ptr<Track> tr;
1637 boost::shared_ptr<Playlist> pl;
1638 boost::shared_ptr<AudioPlaylist> apl;
1640 if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1642 AudioPlaylist::Crossfades xfades;
1646 /* The xfade menu is a bit of a special case, as we always use the mouse position
1647 to decide whether or not to display it (rather than the edit point). No particularly
1648 strong reasons for this, other than it is a bit surprising to right-click on a xfade
1651 mouse_frame (where, ignored);
1652 apl->crossfades_at (where, xfades);
1654 bool const many = xfades.size() > 1;
1656 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1657 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1660 add_region_context_items (edit_items, tr);
1664 add_dstream_context_items (edit_items);
1666 return &track_crossfade_context_menu;
1670 Editor::analyze_region_selection ()
1672 if (analysis_window == 0) {
1673 analysis_window = new AnalysisWindow();
1676 analysis_window->set_session(_session);
1678 analysis_window->show_all();
1681 analysis_window->set_regionmode();
1682 analysis_window->analyze();
1684 analysis_window->present();
1688 Editor::analyze_range_selection()
1690 if (analysis_window == 0) {
1691 analysis_window = new AnalysisWindow();
1694 analysis_window->set_session(_session);
1696 analysis_window->show_all();
1699 analysis_window->set_rangemode();
1700 analysis_window->analyze();
1702 analysis_window->present();
1706 Editor::build_track_selection_context_menu ()
1708 using namespace Menu_Helpers;
1709 MenuList& edit_items = track_selection_context_menu.items();
1710 edit_items.clear ();
1712 add_selection_context_items (edit_items);
1713 // edit_items.push_back (SeparatorElem());
1714 // add_dstream_context_items (edit_items);
1716 return &track_selection_context_menu;
1719 /** Add context menu items relevant to crossfades.
1720 * @param edit_items List to add the items to.
1723 Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1725 using namespace Menu_Helpers;
1726 Menu *xfade_menu = manage (new Menu);
1727 MenuList& items = xfade_menu->items();
1728 xfade_menu->set_name ("ArdourContextMenu");
1731 if (xfade->active()) {
1737 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1738 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1740 if (xfade->can_follow_overlap()) {
1742 if (xfade->following_overlap()) {
1743 str = _("Convert to Short");
1745 str = _("Convert to Full");
1748 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1752 str = xfade->out()->name();
1754 str += xfade->in()->name();
1756 str = _("Crossfade");
1759 edit_items.push_back (MenuElem (str, *xfade_menu));
1760 edit_items.push_back (SeparatorElem());
1764 Editor::xfade_edit_left_region ()
1766 if (clicked_crossfadeview) {
1767 clicked_crossfadeview->left_view.show_region_editor ();
1772 Editor::xfade_edit_right_region ()
1774 if (clicked_crossfadeview) {
1775 clicked_crossfadeview->right_view.show_region_editor ();
1780 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::shared_ptr<Track> track)
1782 using namespace Menu_Helpers;
1784 /* OK, stick the region submenu at the top of the list, and then add
1788 RegionSelection rs = get_regions_from_selection_and_entered ();
1790 string::size_type pos = 0;
1791 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1793 /* we have to hack up the region name because "_" has a special
1794 meaning for menu titles.
1797 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1798 menu_item_name.replace (pos, 1, "__");
1802 if (_popup_region_menu_item == 0) {
1803 _popup_region_menu_item = new MenuItem (menu_item_name);
1804 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1805 _popup_region_menu_item->show ();
1807 _popup_region_menu_item->set_label (menu_item_name);
1810 /* Use the mouse position rather than the edit point to decide whether to show the `choose top region'
1811 dialogue. If we use the edit point it gets a bit messy because the user still has to click over
1812 *some* region in order to get the region context menu stuff to be displayed at all.
1817 mouse_frame (mouse, ignored);
1819 edit_items.push_back (*_popup_region_menu_item);
1820 if (track->playlist()->count_regions_at (mouse) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1821 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region-context-menu")->create_menu_item ()));
1823 edit_items.push_back (SeparatorElem());
1826 /** Add context menu items relevant to selection ranges.
1827 * @param edit_items List to add the items to.
1830 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1832 using namespace Menu_Helpers;
1834 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1835 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1837 edit_items.push_back (SeparatorElem());
1838 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1840 edit_items.push_back (SeparatorElem());
1842 edit_items.push_back (
1844 _("Move Range Start to Previous Region Boundary"),
1845 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, false)
1849 edit_items.push_back (
1851 _("Move Range Start to Next Region Boundary"),
1852 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, true)
1856 edit_items.push_back (
1858 _("Move Range End to Previous Region Boundary"),
1859 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, false)
1863 edit_items.push_back (
1865 _("Move Range End to Next Region Boundary"),
1866 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, true)
1870 edit_items.push_back (SeparatorElem());
1871 edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection)));
1872 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1874 edit_items.push_back (SeparatorElem());
1875 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1877 edit_items.push_back (SeparatorElem());
1878 edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1879 edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1881 edit_items.push_back (SeparatorElem());
1882 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1884 edit_items.push_back (SeparatorElem());
1885 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1886 edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1887 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1889 edit_items.push_back (SeparatorElem());
1890 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1891 edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1892 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1893 edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1894 edit_items.push_back (MenuElem (_("Export Range..."), sigc::mem_fun(*this, &Editor::export_selection)));
1899 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1901 using namespace Menu_Helpers;
1905 Menu *play_menu = manage (new Menu);
1906 MenuList& play_items = play_menu->items();
1907 play_menu->set_name ("ArdourContextMenu");
1909 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1910 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1911 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1912 play_items.push_back (SeparatorElem());
1913 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
1915 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1919 Menu *select_menu = manage (new Menu);
1920 MenuList& select_items = select_menu->items();
1921 select_menu->set_name ("ArdourContextMenu");
1923 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1924 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1925 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1926 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1927 select_items.push_back (SeparatorElem());
1928 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
1929 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
1930 select_items.push_back (SeparatorElem());
1931 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1932 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1933 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1934 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1935 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
1936 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
1937 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
1939 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1943 Menu *cutnpaste_menu = manage (new Menu);
1944 MenuList& cutnpaste_items = cutnpaste_menu->items();
1945 cutnpaste_menu->set_name ("ArdourContextMenu");
1947 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1948 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1949 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f, true)));
1951 cutnpaste_items.push_back (SeparatorElem());
1953 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
1954 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
1956 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
1958 /* Adding new material */
1960 edit_items.push_back (SeparatorElem());
1961 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
1962 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
1966 Menu *nudge_menu = manage (new Menu());
1967 MenuList& nudge_items = nudge_menu->items();
1968 nudge_menu->set_name ("ArdourContextMenu");
1970 edit_items.push_back (SeparatorElem());
1971 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1972 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1973 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1974 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1976 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1980 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
1982 using namespace Menu_Helpers;
1986 Menu *play_menu = manage (new Menu);
1987 MenuList& play_items = play_menu->items();
1988 play_menu->set_name ("ArdourContextMenu");
1990 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1991 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1992 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1996 Menu *select_menu = manage (new Menu);
1997 MenuList& select_items = select_menu->items();
1998 select_menu->set_name ("ArdourContextMenu");
2000 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2001 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
2002 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
2003 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
2004 select_items.push_back (SeparatorElem());
2005 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
2006 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
2007 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2008 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2010 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2014 Menu *cutnpaste_menu = manage (new Menu);
2015 MenuList& cutnpaste_items = cutnpaste_menu->items();
2016 cutnpaste_menu->set_name ("ArdourContextMenu");
2018 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2019 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2020 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f, true)));
2022 Menu *nudge_menu = manage (new Menu());
2023 MenuList& nudge_items = nudge_menu->items();
2024 nudge_menu->set_name ("ArdourContextMenu");
2026 edit_items.push_back (SeparatorElem());
2027 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2028 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2029 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2030 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2032 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2036 Editor::snap_type() const
2042 Editor::snap_mode() const
2048 Editor::set_snap_to (SnapType st)
2050 unsigned int snap_ind = (unsigned int)st;
2054 if (snap_ind > snap_type_strings.size() - 1) {
2056 _snap_type = (SnapType)snap_ind;
2059 string str = snap_type_strings[snap_ind];
2061 if (str != snap_type_selector.get_active_text()) {
2062 snap_type_selector.set_active_text (str);
2067 switch (_snap_type) {
2068 case SnapToBeatDiv32:
2069 case SnapToBeatDiv28:
2070 case SnapToBeatDiv24:
2071 case SnapToBeatDiv20:
2072 case SnapToBeatDiv16:
2073 case SnapToBeatDiv14:
2074 case SnapToBeatDiv12:
2075 case SnapToBeatDiv10:
2076 case SnapToBeatDiv8:
2077 case SnapToBeatDiv7:
2078 case SnapToBeatDiv6:
2079 case SnapToBeatDiv5:
2080 case SnapToBeatDiv4:
2081 case SnapToBeatDiv3:
2082 case SnapToBeatDiv2:
2083 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
2084 update_tempo_based_rulers ();
2087 case SnapToRegionStart:
2088 case SnapToRegionEnd:
2089 case SnapToRegionSync:
2090 case SnapToRegionBoundary:
2091 build_region_boundary_cache ();
2099 SnapChanged (); /* EMIT SIGNAL */
2103 Editor::set_snap_mode (SnapMode mode)
2106 string str = snap_mode_strings[(int)mode];
2108 if (str != snap_mode_selector.get_active_text ()) {
2109 snap_mode_selector.set_active_text (str);
2115 Editor::set_edit_point_preference (EditPoint ep, bool force)
2117 bool changed = (_edit_point != ep);
2120 string str = edit_point_strings[(int)ep];
2122 if (str != edit_point_selector.get_active_text ()) {
2123 edit_point_selector.set_active_text (str);
2126 set_canvas_cursor ();
2128 if (!force && !changed) {
2132 const char* action=NULL;
2134 switch (_edit_point) {
2135 case EditAtPlayhead:
2136 action = "edit-at-playhead";
2138 case EditAtSelectedMarker:
2139 action = "edit-at-marker";
2142 action = "edit-at-mouse";
2146 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2148 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2152 bool in_track_canvas;
2154 if (!mouse_frame (foo, in_track_canvas)) {
2155 in_track_canvas = false;
2158 reset_canvas_action_sensitivity (in_track_canvas);
2164 Editor::set_state (const XMLNode& node, int /*version*/)
2166 const XMLProperty* prop;
2173 g.base_width = default_width;
2174 g.base_height = default_height;
2178 if ((geometry = find_named_node (node, "geometry")) != 0) {
2182 if ((prop = geometry->property("x_size")) == 0) {
2183 prop = geometry->property ("x-size");
2186 g.base_width = atoi(prop->value());
2188 if ((prop = geometry->property("y_size")) == 0) {
2189 prop = geometry->property ("y-size");
2192 g.base_height = atoi(prop->value());
2195 if ((prop = geometry->property ("x_pos")) == 0) {
2196 prop = geometry->property ("x-pos");
2199 x = atoi (prop->value());
2202 if ((prop = geometry->property ("y_pos")) == 0) {
2203 prop = geometry->property ("y-pos");
2206 y = atoi (prop->value());
2210 set_default_size (g.base_width, g.base_height);
2213 if (_session && (prop = node.property ("playhead"))) {
2215 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2216 playhead_cursor->set_position (pos);
2218 playhead_cursor->set_position (0);
2221 if ((prop = node.property ("mixer-width"))) {
2222 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2225 if ((prop = node.property ("zoom-focus"))) {
2226 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2229 if ((prop = node.property ("zoom"))) {
2230 reset_zoom (PBD::atof (prop->value()));
2232 reset_zoom (frames_per_unit);
2235 if ((prop = node.property ("snap-to"))) {
2236 set_snap_to ((SnapType) atoi (prop->value()));
2239 if ((prop = node.property ("snap-mode"))) {
2240 set_snap_mode ((SnapMode) atoi (prop->value()));
2243 if ((prop = node.property ("mouse-mode"))) {
2244 MouseMode m = str2mousemode(prop->value());
2245 set_mouse_mode (m, true);
2247 set_mouse_mode (MouseObject, true);
2250 if ((prop = node.property ("left-frame")) != 0) {
2252 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2253 reset_x_origin (pos);
2257 if ((prop = node.property ("y-origin")) != 0) {
2258 reset_y_origin (atof (prop->value ()));
2261 if ((prop = node.property ("internal-edit"))) {
2262 bool yn = string_is_affirmative (prop->value());
2263 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2265 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2266 tact->set_active (!yn);
2267 tact->set_active (yn);
2271 if ((prop = node.property ("join-object-range"))) {
2272 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2275 if ((prop = node.property ("edit-point"))) {
2276 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2279 if ((prop = node.property ("show-measures"))) {
2280 bool yn = string_is_affirmative (prop->value());
2281 _show_measures = yn;
2282 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2284 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2285 /* do it twice to force the change */
2286 tact->set_active (!yn);
2287 tact->set_active (yn);
2291 if ((prop = node.property ("follow-playhead"))) {
2292 bool yn = string_is_affirmative (prop->value());
2293 set_follow_playhead (yn);
2294 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2296 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2297 if (tact->get_active() != yn) {
2298 tact->set_active (yn);
2303 if ((prop = node.property ("stationary-playhead"))) {
2304 bool yn = string_is_affirmative (prop->value());
2305 set_stationary_playhead (yn);
2306 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2308 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2309 if (tact->get_active() != yn) {
2310 tact->set_active (yn);
2315 if ((prop = node.property ("region-list-sort-type"))) {
2316 RegionListSortType st;
2317 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2320 if ((prop = node.property ("xfades-visible"))) {
2321 bool yn = string_is_affirmative (prop->value());
2322 _xfade_visibility = !yn;
2323 // set_xfade_visibility (yn);
2326 if ((prop = node.property ("show-editor-mixer"))) {
2328 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2331 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2332 bool yn = string_is_affirmative (prop->value());
2334 /* do it twice to force the change */
2336 tact->set_active (!yn);
2337 tact->set_active (yn);
2340 if ((prop = node.property ("show-editor-list"))) {
2342 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2345 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2346 bool yn = string_is_affirmative (prop->value());
2348 /* do it twice to force the change */
2350 tact->set_active (!yn);
2351 tact->set_active (yn);
2354 if ((prop = node.property (X_("editor-list-page")))) {
2355 _the_notebook.set_current_page (atoi (prop->value ()));
2358 if ((prop = node.property (X_("show-marker-lines")))) {
2359 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2361 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2362 bool yn = string_is_affirmative (prop->value ());
2364 tact->set_active (!yn);
2365 tact->set_active (yn);
2368 XMLNodeList children = node.children ();
2369 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2370 selection->set_state (**i, Stateful::current_state_version);
2371 _regions->set_state (**i);
2378 Editor::get_state ()
2380 XMLNode* node = new XMLNode ("Editor");
2383 id().print (buf, sizeof (buf));
2384 node->add_property ("id", buf);
2386 if (is_realized()) {
2387 Glib::RefPtr<Gdk::Window> win = get_window();
2389 int x, y, width, height;
2390 win->get_root_origin(x, y);
2391 win->get_size(width, height);
2393 XMLNode* geometry = new XMLNode ("geometry");
2395 snprintf(buf, sizeof(buf), "%d", width);
2396 geometry->add_property("x-size", string(buf));
2397 snprintf(buf, sizeof(buf), "%d", height);
2398 geometry->add_property("y-size", string(buf));
2399 snprintf(buf, sizeof(buf), "%d", x);
2400 geometry->add_property("x-pos", string(buf));
2401 snprintf(buf, sizeof(buf), "%d", y);
2402 geometry->add_property("y-pos", string(buf));
2403 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2404 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2405 geometry->add_property("notebook-shrunk", _notebook_shrunk ? "1" : "0");
2406 snprintf(buf,sizeof(buf), "%d",pre_maximal_horizontal_pane_position);
2407 geometry->add_property("pre-maximal-horizontal-pane-position", string(buf));
2408 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2409 geometry->add_property("edit-vertical-pane-pos", string(buf));
2411 node->add_child_nocopy (*geometry);
2414 maybe_add_mixer_strip_width (*node);
2416 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2417 node->add_property ("zoom-focus", buf);
2418 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2419 node->add_property ("zoom", buf);
2420 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2421 node->add_property ("snap-to", buf);
2422 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2423 node->add_property ("snap-mode", buf);
2425 node->add_property ("edit-point", enum_2_string (_edit_point));
2427 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2428 node->add_property ("playhead", buf);
2429 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2430 node->add_property ("left-frame", buf);
2431 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2432 node->add_property ("y-origin", buf);
2434 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2435 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2436 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2437 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2438 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2439 node->add_property ("mouse-mode", enum2str(mouse_mode));
2440 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2441 node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2443 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2445 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2446 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2449 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2451 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2452 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2455 snprintf (buf, sizeof (buf), "%d", _the_notebook.get_current_page ());
2456 node->add_property (X_("editor-list-page"), buf);
2458 if (button_bindings) {
2459 XMLNode* bb = new XMLNode (X_("Buttons"));
2460 button_bindings->save (*bb);
2461 node->add_child_nocopy (*bb);
2464 node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no");
2466 node->add_child_nocopy (selection->get_state ());
2467 node->add_child_nocopy (_regions->get_state ());
2474 /** @param y y offset from the top of all trackviews.
2475 * @return pair: TimeAxisView that y is over, layer index.
2476 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2477 * in stacked or expanded region display mode, otherwise 0.
2479 std::pair<TimeAxisView *, double>
2480 Editor::trackview_by_y_position (double y)
2482 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2484 std::pair<TimeAxisView*, double> const r = (*iter)->covers_y_position (y);
2490 return std::make_pair ( (TimeAxisView *) 0, 0);
2493 /** Snap a position to the grid, if appropriate, taking into account current
2494 * grid settings and also the state of any snap modifier keys that may be pressed.
2495 * @param start Position to snap.
2496 * @param event Event to get current key modifier information from, or 0.
2499 Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2501 if (!_session || !event) {
2505 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2506 if (_snap_mode == SnapOff) {
2507 snap_to_internal (start, direction, for_mark);
2510 if (_snap_mode != SnapOff) {
2511 snap_to_internal (start, direction, for_mark);
2517 Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark)
2519 if (!_session || _snap_mode == SnapOff) {
2523 snap_to_internal (start, direction, for_mark);
2527 Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /*for_mark*/)
2529 const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2530 framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2532 switch (_snap_type) {
2533 case SnapToTimecodeFrame:
2534 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2535 start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2537 start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2541 case SnapToTimecodeSeconds:
2542 if (_session->config.get_timecode_offset_negative()) {
2543 start += _session->config.get_timecode_offset ();
2545 start -= _session->config.get_timecode_offset ();
2547 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2548 start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2550 start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2553 if (_session->config.get_timecode_offset_negative()) {
2554 start -= _session->config.get_timecode_offset ();
2556 start += _session->config.get_timecode_offset ();
2560 case SnapToTimecodeMinutes:
2561 if (_session->config.get_timecode_offset_negative()) {
2562 start += _session->config.get_timecode_offset ();
2564 start -= _session->config.get_timecode_offset ();
2566 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2567 start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2569 start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2571 if (_session->config.get_timecode_offset_negative()) {
2572 start -= _session->config.get_timecode_offset ();
2574 start += _session->config.get_timecode_offset ();
2578 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2584 Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark)
2586 const framepos_t one_second = _session->frame_rate();
2587 const framepos_t one_minute = _session->frame_rate() * 60;
2588 framepos_t presnap = start;
2592 switch (_snap_type) {
2593 case SnapToTimecodeFrame:
2594 case SnapToTimecodeSeconds:
2595 case SnapToTimecodeMinutes:
2596 return timecode_snap_to_internal (start, direction, for_mark);
2599 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2600 start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2602 start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2607 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2608 start = (framepos_t) ceil ((double) start / one_second) * one_second;
2610 start = (framepos_t) floor ((double) start / one_second) * one_second;
2615 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2616 start = (framepos_t) ceil ((double) start / one_minute) * one_minute;
2618 start = (framepos_t) floor ((double) start / one_minute) * one_minute;
2623 start = _session->tempo_map().round_to_bar (start, direction);
2627 start = _session->tempo_map().round_to_beat (start, direction);
2630 case SnapToBeatDiv32:
2631 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2633 case SnapToBeatDiv28:
2634 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2636 case SnapToBeatDiv24:
2637 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2639 case SnapToBeatDiv20:
2640 start = _session->tempo_map().round_to_beat_subdivision (start, 20, direction);
2642 case SnapToBeatDiv16:
2643 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2645 case SnapToBeatDiv14:
2646 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2648 case SnapToBeatDiv12:
2649 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2651 case SnapToBeatDiv10:
2652 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2654 case SnapToBeatDiv8:
2655 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2657 case SnapToBeatDiv7:
2658 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2660 case SnapToBeatDiv6:
2661 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2663 case SnapToBeatDiv5:
2664 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2666 case SnapToBeatDiv4:
2667 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2669 case SnapToBeatDiv3:
2670 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2672 case SnapToBeatDiv2:
2673 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2681 _session->locations()->marks_either_side (start, before, after);
2683 if (before == max_framepos) {
2685 } else if (after == max_framepos) {
2687 } else if (before != max_framepos && after != max_framepos) {
2688 /* have before and after */
2689 if ((start - before) < (after - start)) {
2698 case SnapToRegionStart:
2699 case SnapToRegionEnd:
2700 case SnapToRegionSync:
2701 case SnapToRegionBoundary:
2702 if (!region_boundary_cache.empty()) {
2704 vector<framepos_t>::iterator prev = region_boundary_cache.end ();
2705 vector<framepos_t>::iterator next = region_boundary_cache.end ();
2707 if (direction > 0) {
2708 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2710 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2713 if (next != region_boundary_cache.begin ()) {
2718 framepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2719 framepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2721 if (start > (p + n) / 2) {
2730 switch (_snap_mode) {
2736 if (presnap > start) {
2737 if (presnap > (start + unit_to_frame(snap_threshold))) {
2741 } else if (presnap < start) {
2742 if (presnap < (start - unit_to_frame(snap_threshold))) {
2748 /* handled at entry */
2756 Editor::setup_toolbar ()
2758 HBox* mode_box = manage(new HBox);
2759 mode_box->set_border_width (2);
2760 mode_box->set_spacing(4);
2762 /* table containing mode buttons */
2764 HBox* mouse_mode_button_box = manage (new HBox ());
2765 mouse_mode_button_box->set_spacing (2);
2767 if (Profile->get_sae()) {
2768 mouse_mode_button_box->pack_start (mouse_move_button);
2770 mouse_mode_button_box->pack_start (mouse_move_button);
2771 mouse_mode_button_box->pack_start (join_object_range_button);
2772 mouse_mode_button_box->pack_start (mouse_select_button);
2775 mouse_mode_button_box->pack_start (mouse_zoom_button);
2777 if (!Profile->get_sae()) {
2778 mouse_mode_button_box->pack_start (mouse_gain_button);
2781 mouse_mode_button_box->pack_start (mouse_timefx_button);
2782 mouse_mode_button_box->pack_start (mouse_audition_button);
2783 mouse_mode_button_box->pack_start (mouse_draw_button);
2784 mouse_mode_button_box->pack_start (internal_edit_button);
2786 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2787 if (!Profile->get_sae()) {
2788 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2790 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2792 edit_mode_selector.set_name ("EditModeSelector");
2793 set_popdown_strings (edit_mode_selector, edit_mode_strings);
2794 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2796 mode_box->pack_start (edit_mode_selector, false, false);
2797 mode_box->pack_start (*mouse_mode_button_box, false, false);
2799 _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2800 _mouse_mode_tearoff->set_name ("MouseModeBase");
2801 _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2803 if (Profile->get_sae()) {
2804 _mouse_mode_tearoff->set_can_be_torn_off (false);
2807 _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2808 &_mouse_mode_tearoff->tearoff_window()));
2809 _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2810 &_mouse_mode_tearoff->tearoff_window(), 1));
2811 _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2812 &_mouse_mode_tearoff->tearoff_window()));
2813 _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2814 &_mouse_mode_tearoff->tearoff_window(), 1));
2818 _zoom_box.set_spacing (2);
2819 _zoom_box.set_border_width (2);
2823 zoom_in_button.set_name ("zoom button");
2824 zoom_in_button.set_image (::get_icon ("zoom_in"));
2825 zoom_in_button.set_tweaks (ArdourButton::ShowClick);
2826 act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-in"));
2827 zoom_in_button.set_related_action (act);
2829 zoom_out_button.set_name ("zoom button");
2830 zoom_out_button.set_image (::get_icon ("zoom_out"));
2831 zoom_out_button.set_tweaks (ArdourButton::ShowClick);
2832 act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-out"));
2833 zoom_out_button.set_related_action (act);
2835 zoom_out_full_button.set_name ("zoom button");
2836 zoom_out_full_button.set_image (::get_icon ("zoom_full"));
2837 zoom_out_full_button.set_tweaks (ArdourButton::ShowClick);
2838 act = ActionManager::get_action (X_("Editor"), X_("zoom-to-session"));
2839 zoom_out_full_button.set_related_action (act);
2841 zoom_focus_selector.set_name ("ZoomFocusSelector");
2842 set_popdown_strings (zoom_focus_selector, zoom_focus_strings);
2843 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2845 _zoom_box.pack_start (zoom_out_button, false, false);
2846 _zoom_box.pack_start (zoom_in_button, false, false);
2847 _zoom_box.pack_start (zoom_out_full_button, false, false);
2849 _zoom_box.pack_start (zoom_focus_selector, false, false);
2851 /* Track zoom buttons */
2852 tav_expand_button.set_name ("TrackHeightButton");
2853 tav_expand_button.set_size_request (-1, 20);
2854 tav_expand_button.add (*(manage (new Image (::get_icon ("tav_exp")))));
2855 act = ActionManager::get_action (X_("Editor"), X_("expand-tracks"));
2856 act->connect_proxy (tav_expand_button);
2858 tav_shrink_button.set_name ("TrackHeightButton");
2859 tav_shrink_button.set_size_request (-1, 20);
2860 tav_shrink_button.add (*(manage (new Image (::get_icon ("tav_shrink")))));
2861 act = ActionManager::get_action (X_("Editor"), X_("shrink-tracks"));
2862 act->connect_proxy (tav_shrink_button);
2864 _zoom_box.pack_start (tav_shrink_button);
2865 _zoom_box.pack_start (tav_expand_button);
2867 _zoom_tearoff = manage (new TearOff (_zoom_box));
2869 _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2870 &_zoom_tearoff->tearoff_window()));
2871 _zoom_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2872 &_zoom_tearoff->tearoff_window(), 0));
2873 _zoom_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2874 &_zoom_tearoff->tearoff_window()));
2875 _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2876 &_zoom_tearoff->tearoff_window(), 0));
2878 snap_box.set_spacing (1);
2879 snap_box.set_border_width (2);
2881 snap_type_selector.set_name ("SnapTypeSelector");
2882 set_popdown_strings (snap_type_selector, snap_type_strings);
2883 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2885 snap_mode_selector.set_name ("SnapModeSelector");
2886 set_popdown_strings (snap_mode_selector, snap_mode_strings);
2887 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2889 edit_point_selector.set_name ("EditPointSelector");
2890 set_popdown_strings (edit_point_selector, edit_point_strings);
2891 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2893 snap_box.pack_start (snap_mode_selector, false, false);
2894 snap_box.pack_start (snap_type_selector, false, false);
2895 snap_box.pack_start (edit_point_selector, false, false);
2899 HBox *nudge_box = manage (new HBox);
2900 nudge_box->set_spacing (2);
2901 nudge_box->set_border_width (2);
2903 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2904 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2906 nudge_box->pack_start (nudge_backward_button, false, false);
2907 nudge_box->pack_start (nudge_forward_button, false, false);
2908 nudge_box->pack_start (*nudge_clock, false, false);
2911 /* Pack everything in... */
2913 HBox* hbox = manage (new HBox);
2914 hbox->set_spacing(10);
2916 _tools_tearoff = manage (new TearOff (*hbox));
2917 _tools_tearoff->set_name ("MouseModeBase");
2918 _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
2920 if (Profile->get_sae()) {
2921 _tools_tearoff->set_can_be_torn_off (false);
2924 _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2925 &_tools_tearoff->tearoff_window()));
2926 _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2927 &_tools_tearoff->tearoff_window(), 0));
2928 _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2929 &_tools_tearoff->tearoff_window()));
2930 _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2931 &_tools_tearoff->tearoff_window(), 0));
2933 toolbar_hbox.set_spacing (10);
2934 toolbar_hbox.set_border_width (1);
2936 toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
2937 toolbar_hbox.pack_start (*_zoom_tearoff, false, false);
2938 toolbar_hbox.pack_start (*_tools_tearoff, false, false);
2940 hbox->pack_start (snap_box, false, false);
2941 if (!Profile->get_small_screen()) {
2942 hbox->pack_start (*nudge_box, false, false);
2944 ARDOUR_UI::instance()->editor_transport_box().pack_start (*nudge_box, false, false);
2946 hbox->pack_start (panic_box, false, false);
2950 toolbar_base.set_name ("ToolBarBase");
2951 toolbar_base.add (toolbar_hbox);
2953 _toolbar_viewport.add (toolbar_base);
2954 /* stick to the required height but allow width to vary if there's not enough room */
2955 _toolbar_viewport.set_size_request (1, -1);
2957 toolbar_frame.set_shadow_type (SHADOW_OUT);
2958 toolbar_frame.set_name ("BaseFrame");
2959 toolbar_frame.add (_toolbar_viewport);
2963 Editor::setup_tooltips ()
2965 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
2966 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Select/Move Ranges"));
2967 ARDOUR_UI::instance()->set_tip (mouse_draw_button, _("Draw/Edit MIDI Notes"));
2968 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain"));
2969 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
2970 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
2971 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2972 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
2973 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
2974 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
2975 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
2976 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
2977 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
2978 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
2979 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
2980 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
2981 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
2982 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
2983 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
2984 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
2985 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
2986 ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode"));
2990 Editor::convert_drop_to_paths (
2991 vector<string>& paths,
2992 const RefPtr<Gdk::DragContext>& /*context*/,
2995 const SelectionData& data,
2999 if (_session == 0) {
3003 vector<string> uris = data.get_uris();
3007 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3008 are actually URI lists. So do it by hand.
3011 if (data.get_target() != "text/plain") {
3015 /* Parse the "uri-list" format that Nautilus provides,
3016 where each pathname is delimited by \r\n.
3018 THERE MAY BE NO NULL TERMINATING CHAR!!!
3021 string txt = data.get_text();
3025 p = (const char *) malloc (txt.length() + 1);
3026 txt.copy ((char *) p, txt.length(), 0);
3027 ((char*)p)[txt.length()] = '\0';
3033 while (g_ascii_isspace (*p))
3037 while (*q && (*q != '\n') && (*q != '\r')) {
3044 while (q > p && g_ascii_isspace (*q))
3049 uris.push_back (string (p, q - p + 1));
3053 p = strchr (p, '\n');
3065 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3067 if ((*i).substr (0,7) == "file://") {
3070 PBD::url_decode (p);
3072 // scan forward past three slashes
3074 string::size_type slashcnt = 0;
3075 string::size_type n = 0;
3076 string::iterator x = p.begin();
3078 while (slashcnt < 3 && x != p.end()) {
3081 } else if (slashcnt == 3) {
3088 if (slashcnt != 3 || x == p.end()) {
3089 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3093 paths.push_back (p.substr (n - 1));
3101 Editor::new_tempo_section ()
3107 Editor::map_transport_state ()
3109 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3111 if (_session && _session->transport_stopped()) {
3112 have_pending_keyboard_selection = false;
3115 update_loop_range_view (true);
3120 Editor::State::State (PublicEditor const * e)
3122 selection = new Selection (e);
3125 Editor::State::~State ()
3131 Editor::begin_reversible_command (string name)
3134 _session->begin_reversible_command (name);
3139 Editor::begin_reversible_command (GQuark q)
3142 _session->begin_reversible_command (q);
3147 Editor::commit_reversible_command ()
3150 _session->commit_reversible_command ();
3155 Editor::history_changed ()
3159 if (undo_action && _session) {
3160 if (_session->undo_depth() == 0) {
3161 label = S_("Command|Undo");
3163 label = string_compose(S_("Command|Undo (%1)"), _session->next_undo());
3165 undo_action->property_label() = label;
3168 if (redo_action && _session) {
3169 if (_session->redo_depth() == 0) {
3172 label = string_compose(_("Redo (%1)"), _session->next_redo());
3174 redo_action->property_label() = label;
3179 Editor::duplicate_dialog (bool with_dialog)
3183 if (mouse_mode == MouseRange) {
3184 if (selection->time.length() == 0) {
3189 RegionSelection rs = get_regions_from_selection_and_entered ();
3191 if (mouse_mode != MouseRange && rs.empty()) {
3197 ArdourDialog win (_("Duplicate"));
3198 Label label (_("Number of duplications:"));
3199 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3200 SpinButton spinner (adjustment, 0.0, 1);
3203 win.get_vbox()->set_spacing (12);
3204 win.get_vbox()->pack_start (hbox);
3205 hbox.set_border_width (6);
3206 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3208 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3209 place, visually. so do this by hand.
3212 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3213 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3214 spinner.grab_focus();
3220 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3221 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3222 win.set_default_response (RESPONSE_ACCEPT);
3224 win.set_position (WIN_POS_MOUSE);
3226 spinner.grab_focus ();
3228 switch (win.run ()) {
3229 case RESPONSE_ACCEPT:
3235 times = adjustment.get_value();
3238 if (mouse_mode == MouseRange) {
3239 duplicate_selection (times);
3241 duplicate_some_regions (rs, times);
3246 Editor::set_edit_mode (EditMode m)
3248 Config->set_edit_mode (m);
3252 Editor::cycle_edit_mode ()
3254 switch (Config->get_edit_mode()) {
3256 if (Profile->get_sae()) {
3257 Config->set_edit_mode (Lock);
3259 Config->set_edit_mode (Splice);
3263 Config->set_edit_mode (Lock);
3266 Config->set_edit_mode (Slide);
3272 Editor::edit_mode_selection_done ()
3274 string s = edit_mode_selector.get_active_text ();
3277 Config->set_edit_mode (string_to_edit_mode (s));
3282 Editor::snap_type_selection_done ()
3284 string choice = snap_type_selector.get_active_text();
3285 SnapType snaptype = SnapToBeat;
3287 if (choice == _("Beats/2")) {
3288 snaptype = SnapToBeatDiv2;
3289 } else if (choice == _("Beats/3")) {
3290 snaptype = SnapToBeatDiv3;
3291 } else if (choice == _("Beats/4")) {
3292 snaptype = SnapToBeatDiv4;
3293 } else if (choice == _("Beats/5")) {
3294 snaptype = SnapToBeatDiv5;
3295 } else if (choice == _("Beats/6")) {
3296 snaptype = SnapToBeatDiv6;
3297 } else if (choice == _("Beats/7")) {
3298 snaptype = SnapToBeatDiv7;
3299 } else if (choice == _("Beats/8")) {
3300 snaptype = SnapToBeatDiv8;
3301 } else if (choice == _("Beats/10")) {
3302 snaptype = SnapToBeatDiv10;
3303 } else if (choice == _("Beats/12")) {
3304 snaptype = SnapToBeatDiv12;
3305 } else if (choice == _("Beats/14")) {
3306 snaptype = SnapToBeatDiv14;
3307 } else if (choice == _("Beats/16")) {
3308 snaptype = SnapToBeatDiv16;
3309 } else if (choice == _("Beats/20")) {
3310 snaptype = SnapToBeatDiv20;
3311 } else if (choice == _("Beats/24")) {
3312 snaptype = SnapToBeatDiv24;
3313 } else if (choice == _("Beats/28")) {
3314 snaptype = SnapToBeatDiv28;
3315 } else if (choice == _("Beats/32")) {
3316 snaptype = SnapToBeatDiv32;
3317 } else if (choice == _("Beats")) {
3318 snaptype = SnapToBeat;
3319 } else if (choice == _("Bars")) {
3320 snaptype = SnapToBar;
3321 } else if (choice == _("Marks")) {
3322 snaptype = SnapToMark;
3323 } else if (choice == _("Region starts")) {
3324 snaptype = SnapToRegionStart;
3325 } else if (choice == _("Region ends")) {
3326 snaptype = SnapToRegionEnd;
3327 } else if (choice == _("Region bounds")) {
3328 snaptype = SnapToRegionBoundary;
3329 } else if (choice == _("Region syncs")) {
3330 snaptype = SnapToRegionSync;
3331 } else if (choice == _("CD Frames")) {
3332 snaptype = SnapToCDFrame;
3333 } else if (choice == _("Timecode Frames")) {
3334 snaptype = SnapToTimecodeFrame;
3335 } else if (choice == _("Timecode Seconds")) {
3336 snaptype = SnapToTimecodeSeconds;
3337 } else if (choice == _("Timecode Minutes")) {
3338 snaptype = SnapToTimecodeMinutes;
3339 } else if (choice == _("Seconds")) {
3340 snaptype = SnapToSeconds;
3341 } else if (choice == _("Minutes")) {
3342 snaptype = SnapToMinutes;
3345 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3347 ract->set_active ();
3352 Editor::snap_mode_selection_done ()
3354 string choice = snap_mode_selector.get_active_text();
3355 SnapMode mode = SnapNormal;
3357 if (choice == _("No Grid")) {
3359 } else if (choice == _("Grid")) {
3361 } else if (choice == _("Magnetic")) {
3362 mode = SnapMagnetic;
3365 RefPtr<RadioAction> ract = snap_mode_action (mode);
3368 ract->set_active (true);
3373 Editor::cycle_edit_point (bool with_marker)
3375 switch (_edit_point) {
3377 set_edit_point_preference (EditAtPlayhead);
3379 case EditAtPlayhead:
3381 set_edit_point_preference (EditAtSelectedMarker);
3383 set_edit_point_preference (EditAtMouse);
3386 case EditAtSelectedMarker:
3387 set_edit_point_preference (EditAtMouse);
3393 Editor::edit_point_selection_done ()
3395 string choice = edit_point_selector.get_active_text();
3396 EditPoint ep = EditAtSelectedMarker;
3398 if (choice == _("Marker")) {
3399 set_edit_point_preference (EditAtSelectedMarker);
3400 } else if (choice == _("Playhead")) {
3401 set_edit_point_preference (EditAtPlayhead);
3403 set_edit_point_preference (EditAtMouse);
3406 RefPtr<RadioAction> ract = edit_point_action (ep);
3409 ract->set_active (true);
3414 Editor::zoom_focus_selection_done ()
3416 string choice = zoom_focus_selector.get_active_text();
3417 ZoomFocus focus_type = ZoomFocusLeft;
3419 if (choice == _("Left")) {
3420 focus_type = ZoomFocusLeft;
3421 } else if (choice == _("Right")) {
3422 focus_type = ZoomFocusRight;
3423 } else if (choice == _("Center")) {
3424 focus_type = ZoomFocusCenter;
3425 } else if (choice == _("Playhead")) {
3426 focus_type = ZoomFocusPlayhead;
3427 } else if (choice == _("Mouse")) {
3428 focus_type = ZoomFocusMouse;
3429 } else if (choice == _("Edit point")) {
3430 focus_type = ZoomFocusEdit;
3433 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3436 ract->set_active ();
3441 Editor::edit_controls_button_release (GdkEventButton* ev)
3443 if (Keyboard::is_context_menu_event (ev)) {
3444 ARDOUR_UI::instance()->add_route (this);
3445 } else if (ev->button == 1) {
3446 selection->clear_tracks ();
3453 Editor::mouse_select_button_release (GdkEventButton* ev)
3455 /* this handles just right-clicks */
3457 if (ev->button != 3) {
3465 Editor::set_zoom_focus (ZoomFocus f)
3467 string str = zoom_focus_strings[(int)f];
3469 if (str != zoom_focus_selector.get_active_text()) {
3470 zoom_focus_selector.set_active_text (str);
3473 if (zoom_focus != f) {
3480 Editor::ensure_float (Window& win)
3482 win.set_transient_for (*this);
3486 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3488 /* recover or initialize pane positions. do this here rather than earlier because
3489 we don't want the positions to change the child allocations, which they seem to do.
3495 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3504 XMLNode* geometry = find_named_node (*node, "geometry");
3506 if (which == static_cast<Paned*> (&edit_pane)) {
3508 if (done & Horizontal) {
3512 if (geometry && (prop = geometry->property ("notebook-shrunk"))) {
3513 _notebook_shrunk = string_is_affirmative (prop->value ());
3516 if (geometry && (prop = geometry->property ("pre-maximal-horizontal-pane-position"))) {
3517 pre_maximal_horizontal_pane_position = atoi (prop->value ());
3520 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3521 /* initial allocation is 90% to canvas, 10% to notebook */
3522 pos = (int) floor (alloc.get_width() * 0.90f);
3523 snprintf (buf, sizeof(buf), "%d", pos);
3525 pos = atoi (prop->value());
3528 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3529 edit_pane.set_position (pos);
3530 if (pre_maximal_horizontal_pane_position == 0) {
3531 pre_maximal_horizontal_pane_position = pos;
3535 done = (Pane) (done | Horizontal);
3537 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3539 if (done & Vertical) {
3543 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3544 /* initial allocation is 90% to canvas, 10% to summary */
3545 pos = (int) floor (alloc.get_height() * 0.90f);
3546 snprintf (buf, sizeof(buf), "%d", pos);
3548 pos = atoi (prop->value());
3551 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3552 editor_summary_pane.set_position (pos);
3553 pre_maximal_vertical_pane_position = pos;
3556 done = (Pane) (done | Vertical);
3561 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3563 if ((_tools_tearoff->torn_off() || !_tools_tearoff->visible()) &&
3564 (_mouse_mode_tearoff->torn_off() || !_mouse_mode_tearoff->visible()) &&
3565 (_zoom_tearoff->torn_off() || !_zoom_tearoff->visible())) {
3566 top_hbox.remove (toolbar_frame);
3571 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3573 if (toolbar_frame.get_parent() == 0) {
3574 top_hbox.pack_end (toolbar_frame);
3579 Editor::set_show_measures (bool yn)
3581 if (_show_measures != yn) {
3584 if ((_show_measures = yn) == true) {
3586 tempo_lines->show();
3594 Editor::toggle_follow_playhead ()
3596 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3598 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3599 set_follow_playhead (tact->get_active());
3603 /** @param yn true to follow playhead, otherwise false.
3604 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
3607 Editor::set_follow_playhead (bool yn, bool catch_up)
3609 if (_follow_playhead != yn) {
3610 if ((_follow_playhead = yn) == true && catch_up) {
3612 reset_x_origin_to_follow_playhead ();
3619 Editor::toggle_stationary_playhead ()
3621 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3623 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3624 set_stationary_playhead (tact->get_active());
3629 Editor::set_stationary_playhead (bool yn)
3631 if (_stationary_playhead != yn) {
3632 if ((_stationary_playhead = yn) == true) {
3634 // FIXME need a 3.0 equivalent of this 2.X call
3635 // update_current_screen ();
3642 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3644 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3646 xfade->set_active (!xfade->active());
3651 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3653 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3655 xfade->set_follow_overlap (!xfade->following_overlap());
3660 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3662 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3668 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3672 switch (cew.run ()) {
3673 case RESPONSE_ACCEPT:
3680 PropertyChange all_crossfade_properties;
3681 all_crossfade_properties.add (ARDOUR::Properties::active);
3682 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3683 xfade->PropertyChanged (all_crossfade_properties);
3687 Editor::playlist_selector () const
3689 return *_playlist_selector;
3693 Editor::get_grid_type_as_beats (bool& success, framepos_t position)
3697 switch (_snap_type) {
3702 case SnapToBeatDiv32:
3705 case SnapToBeatDiv28:
3708 case SnapToBeatDiv24:
3711 case SnapToBeatDiv20:
3714 case SnapToBeatDiv16:
3717 case SnapToBeatDiv14:
3720 case SnapToBeatDiv12:
3723 case SnapToBeatDiv10:
3726 case SnapToBeatDiv8:
3729 case SnapToBeatDiv7:
3732 case SnapToBeatDiv6:
3735 case SnapToBeatDiv5:
3738 case SnapToBeatDiv4:
3741 case SnapToBeatDiv3:
3744 case SnapToBeatDiv2:
3750 return _session->tempo_map().meter_at (position).divisions_per_bar();
3755 case SnapToTimecodeFrame:
3756 case SnapToTimecodeSeconds:
3757 case SnapToTimecodeMinutes:
3760 case SnapToRegionStart:
3761 case SnapToRegionEnd:
3762 case SnapToRegionSync:
3763 case SnapToRegionBoundary:
3773 Editor::get_nudge_distance (framepos_t pos, framecnt_t& next)
3777 ret = nudge_clock->current_duration (pos);
3778 next = ret + 1; /* XXXX fix me */
3784 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3786 ArdourDialog dialog (_("Playlist Deletion"));
3787 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3788 "If it is kept, its audio files will not be cleaned.\n"
3789 "If it is deleted, audio files used by it alone will be cleaned."),
3792 dialog.set_position (WIN_POS_CENTER);
3793 dialog.get_vbox()->pack_start (label);
3797 dialog.add_button (_("Delete Playlist"), RESPONSE_ACCEPT);
3798 dialog.add_button (_("Keep Playlist"), RESPONSE_REJECT);
3799 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3801 switch (dialog.run ()) {
3802 case RESPONSE_ACCEPT:
3803 /* delete the playlist */
3807 case RESPONSE_REJECT:
3808 /* keep the playlist */
3820 Editor::audio_region_selection_covers (framepos_t where)
3822 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3823 if ((*a)->region()->covers (where)) {
3832 Editor::prepare_for_cleanup ()
3834 cut_buffer->clear_regions ();
3835 cut_buffer->clear_playlists ();
3837 selection->clear_regions ();
3838 selection->clear_playlists ();
3840 _regions->suspend_redisplay ();
3844 Editor::finish_cleanup ()
3846 _regions->resume_redisplay ();
3850 Editor::transport_loop_location()
3853 return _session->locations()->auto_loop_location();
3860 Editor::transport_punch_location()
3863 return _session->locations()->auto_punch_location();
3870 Editor::control_layout_scroll (GdkEventScroll* ev)
3872 if (Keyboard::some_magic_widget_has_focus()) {
3876 switch (ev->direction) {
3878 scroll_tracks_up_line ();
3882 case GDK_SCROLL_DOWN:
3883 scroll_tracks_down_line ();
3887 /* no left/right handling yet */
3895 Editor::session_state_saved (string)
3898 _snapshots->redisplay ();
3902 Editor::maximise_editing_space ()
3904 /* these calls will leave each tearoff visible *if* it is torn off
3907 _mouse_mode_tearoff->set_visible (false);
3908 _tools_tearoff->set_visible (false);
3909 _zoom_tearoff->set_visible (false);
3911 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
3912 pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
3913 pre_maximal_editor_width = this->get_width ();
3914 pre_maximal_editor_height = this->get_height ();
3916 if (post_maximal_horizontal_pane_position == 0) {
3917 post_maximal_horizontal_pane_position = edit_pane.get_width();
3920 if (post_maximal_vertical_pane_position == 0) {
3921 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
3926 if (post_maximal_editor_width) {
3927 edit_pane.set_position (post_maximal_horizontal_pane_position -
3928 abs(post_maximal_editor_width - pre_maximal_editor_width));
3930 edit_pane.set_position (post_maximal_horizontal_pane_position);
3933 /* Hack: we must do this in an idle handler for it to work; see comment in
3934 restore_editing_space()
3937 Glib::signal_idle().connect (
3939 sigc::mem_fun (*this, &Editor::idle_reset_vertical_pane_position),
3940 post_maximal_vertical_pane_position
3944 if (Config->get_keep_tearoffs()) {
3945 _mouse_mode_tearoff->set_visible (true);
3946 _tools_tearoff->set_visible (true);
3947 if (Config->get_show_zoom_tools ()) {
3948 _zoom_tearoff->set_visible (true);
3955 Editor::idle_reset_vertical_pane_position (int p)
3957 editor_summary_pane.set_position (p);
3962 Editor::restore_editing_space ()
3964 // user changed width/height of panes during fullscreen
3966 if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
3967 post_maximal_horizontal_pane_position = edit_pane.get_position();
3970 if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
3971 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
3976 _mouse_mode_tearoff->set_visible (true);
3977 _tools_tearoff->set_visible (true);
3978 if (Config->get_show_zoom_tools ()) {
3979 _zoom_tearoff->set_visible (true);
3981 post_maximal_editor_width = this->get_width();
3982 post_maximal_editor_height = this->get_height();
3984 edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
3986 /* This is a bit of a hack, but it seems that if you set the vertical pane position
3987 here it gets reset to some wrong value after this method has finished. Doing
3988 the setup in an idle callback seems to work.
3990 Glib::signal_idle().connect (
3992 sigc::mem_fun (*this, &Editor::idle_reset_vertical_pane_position),
3993 pre_maximal_vertical_pane_position
3999 * Make new playlists for a given track and also any others that belong
4000 * to the same active route group with the `edit' property.
4005 Editor::new_playlists (TimeAxisView* v)
4007 begin_reversible_command (_("new playlists"));
4008 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4009 _session->playlists->get (playlists);
4010 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4011 commit_reversible_command ();
4015 * Use a copy of the current playlist for a given track and also any others that belong
4016 * to the same active route group with the `edit' property.
4021 Editor::copy_playlists (TimeAxisView* v)
4023 begin_reversible_command (_("copy playlists"));
4024 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4025 _session->playlists->get (playlists);
4026 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4027 commit_reversible_command ();
4030 /** Clear the current playlist for a given track and also any others that belong
4031 * to the same active route group with the `edit' property.
4036 Editor::clear_playlists (TimeAxisView* v)
4038 begin_reversible_command (_("clear playlists"));
4039 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4040 _session->playlists->get (playlists);
4041 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4042 commit_reversible_command ();
4046 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4048 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4052 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4054 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4058 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4060 atv.clear_playlist ();
4064 Editor::on_key_press_event (GdkEventKey* ev)
4066 return key_press_focus_accelerator_handler (*this, ev);
4070 Editor::on_key_release_event (GdkEventKey* ev)
4072 return Gtk::Window::on_key_release_event (ev);
4073 // return key_press_focus_accelerator_handler (*this, ev);
4076 /** Queue up a change to the viewport x origin.
4077 * @param frame New x origin.
4080 Editor::reset_x_origin (framepos_t frame)
4082 queue_visual_change (frame);
4086 Editor::reset_y_origin (double y)
4088 queue_visual_change_y (y);
4092 Editor::reset_zoom (double fpu)
4094 queue_visual_change (fpu);
4098 Editor::reposition_and_zoom (framepos_t frame, double fpu)
4100 reset_x_origin (frame);
4103 if (!no_save_visual) {
4104 undo_visual_stack.push_back (current_visual_state(false));
4108 Editor::VisualState::VisualState ()
4109 : gui_state (new GUIObjectState)
4113 Editor::VisualState::~VisualState ()
4118 Editor::VisualState*
4119 Editor::current_visual_state (bool with_tracks)
4121 VisualState* vs = new VisualState;
4122 vs->y_position = vertical_adjustment.get_value();
4123 vs->frames_per_unit = frames_per_unit;
4124 vs->leftmost_frame = leftmost_frame;
4125 vs->zoom_focus = zoom_focus;
4128 *(vs->gui_state) = *ARDOUR_UI::instance()->gui_object_state;
4135 Editor::undo_visual_state ()
4137 if (undo_visual_stack.empty()) {
4141 redo_visual_stack.push_back (current_visual_state());
4143 VisualState* vs = undo_visual_stack.back();
4144 undo_visual_stack.pop_back();
4145 use_visual_state (*vs);
4149 Editor::redo_visual_state ()
4151 if (redo_visual_stack.empty()) {
4155 undo_visual_stack.push_back (current_visual_state());
4157 VisualState* vs = redo_visual_stack.back();
4158 redo_visual_stack.pop_back();
4159 use_visual_state (*vs);
4163 Editor::swap_visual_state ()
4165 if (undo_visual_stack.empty()) {
4166 redo_visual_state ();
4168 undo_visual_state ();
4173 Editor::use_visual_state (VisualState& vs)
4175 no_save_visual = true;
4177 _routes->suspend_redisplay ();
4179 vertical_adjustment.set_value (vs.y_position);
4181 set_zoom_focus (vs.zoom_focus);
4182 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4184 *ARDOUR_UI::instance()->gui_object_state = *vs.gui_state;
4186 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4187 (*i)->reset_visual_state ();
4190 _routes->update_visibility ();
4191 _routes->resume_redisplay ();
4193 no_save_visual = false;
4197 Editor::set_frames_per_unit (double fpu)
4199 /* this is the core function that controls the zoom level of the canvas. it is called
4200 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4203 if (fpu == frames_per_unit) {
4212 /* don't allow zooms that fit more than the maximum number
4213 of frames into an 800 pixel wide space.
4216 if (max_framepos / fpu < 800.0) {
4221 tempo_lines->tempo_map_changed();
4223 frames_per_unit = fpu;
4228 Editor::post_zoom ()
4230 // convert fpu to frame count
4232 framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width);
4234 if (frames_per_unit != zoom_range_clock->current_duration()) {
4235 zoom_range_clock->set (frames);
4238 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4239 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4240 (*i)->reshow_selection (selection->time);
4244 ZoomChanged (); /* EMIT_SIGNAL */
4246 //reset_scrolling_region ();
4248 if (playhead_cursor) {
4249 playhead_cursor->set_position (playhead_cursor->current_frame);
4252 refresh_location_display();
4253 _summary->set_overlays_dirty ();
4255 update_marker_labels ();
4261 Editor::queue_visual_change (framepos_t where)
4263 pending_visual_change.add (VisualChange::TimeOrigin);
4264 pending_visual_change.time_origin = where;
4265 ensure_visual_change_idle_handler ();
4269 Editor::queue_visual_change (double fpu)
4271 pending_visual_change.add (VisualChange::ZoomLevel);
4272 pending_visual_change.frames_per_unit = fpu;
4274 ensure_visual_change_idle_handler ();
4278 Editor::queue_visual_change_y (double y)
4280 pending_visual_change.add (VisualChange::YOrigin);
4281 pending_visual_change.y_origin = y;
4283 ensure_visual_change_idle_handler ();
4287 Editor::ensure_visual_change_idle_handler ()
4289 if (pending_visual_change.idle_handler_id < 0) {
4290 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4295 Editor::_idle_visual_changer (void* arg)
4297 return static_cast<Editor*>(arg)->idle_visual_changer ();
4301 Editor::idle_visual_changer ()
4303 VisualChange::Type p = pending_visual_change.pending;
4304 pending_visual_change.pending = (VisualChange::Type) 0;
4306 double const last_time_origin = horizontal_position ();
4308 if (p & VisualChange::TimeOrigin) {
4309 /* This is a bit of a hack, but set_frames_per_unit
4310 below will (if called) end up with the
4311 CrossfadeViews looking at Editor::leftmost_frame,
4312 and if we're changing origin and zoom in the same
4313 operation it will be the wrong value unless we
4317 leftmost_frame = pending_visual_change.time_origin;
4320 if (p & VisualChange::ZoomLevel) {
4321 set_frames_per_unit (pending_visual_change.frames_per_unit);
4323 compute_fixed_ruler_scale ();
4324 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4325 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4326 update_tempo_based_rulers ();
4328 if (p & VisualChange::TimeOrigin) {
4329 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4331 if (p & VisualChange::YOrigin) {
4332 vertical_adjustment.set_value (pending_visual_change.y_origin);
4335 if (last_time_origin == horizontal_position ()) {
4336 /* changed signal not emitted */
4337 update_fixed_rulers ();
4338 redisplay_tempo (true);
4341 _summary->set_overlays_dirty ();
4343 pending_visual_change.idle_handler_id = -1;
4344 return 0; /* this is always a one-shot call */
4347 struct EditorOrderTimeAxisSorter {
4348 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4349 return a->order () < b->order ();
4354 Editor::sort_track_selection (TrackViewList& sel)
4356 EditorOrderTimeAxisSorter cmp;
4361 Editor::get_preferred_edit_position (bool ignore_playhead, bool from_context_menu)
4364 framepos_t where = 0;
4365 EditPoint ep = _edit_point;
4367 if (from_context_menu && (ep == EditAtMouse)) {
4368 return event_frame (&context_click_event, 0, 0);
4371 if (entered_marker) {
4372 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position()));
4373 return entered_marker->position();
4376 if (ignore_playhead && ep == EditAtPlayhead) {
4377 ep = EditAtSelectedMarker;
4381 case EditAtPlayhead:
4382 where = _session->audible_frame();
4383 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where));
4386 case EditAtSelectedMarker:
4387 if (!selection->markers.empty()) {
4389 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4392 where = loc->start();
4396 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where));
4404 if (!mouse_frame (where, ignored)) {
4405 /* XXX not right but what can we do ? */
4409 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where));
4417 Editor::set_loop_range (framepos_t start, framepos_t end, string cmd)
4419 if (!_session) return;
4421 begin_reversible_command (cmd);
4425 if ((tll = transport_loop_location()) == 0) {
4426 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop);
4427 XMLNode &before = _session->locations()->get_state();
4428 _session->locations()->add (loc, true);
4429 _session->set_auto_loop_location (loc);
4430 XMLNode &after = _session->locations()->get_state();
4431 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4433 XMLNode &before = tll->get_state();
4434 tll->set_hidden (false, this);
4435 tll->set (start, end);
4436 XMLNode &after = tll->get_state();
4437 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4440 commit_reversible_command ();
4444 Editor::set_punch_range (framepos_t start, framepos_t end, string cmd)
4446 if (!_session) return;
4448 begin_reversible_command (cmd);
4452 if ((tpl = transport_punch_location()) == 0) {
4453 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoPunch);
4454 XMLNode &before = _session->locations()->get_state();
4455 _session->locations()->add (loc, true);
4456 _session->set_auto_loop_location (loc);
4457 XMLNode &after = _session->locations()->get_state();
4458 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4461 XMLNode &before = tpl->get_state();
4462 tpl->set_hidden (false, this);
4463 tpl->set (start, end);
4464 XMLNode &after = tpl->get_state();
4465 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4468 commit_reversible_command ();
4471 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4472 * @param rs List to which found regions are added.
4473 * @param where Time to look at.
4474 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4477 Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4479 const TrackViewList* tracks;
4482 tracks = &track_views;
4487 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4489 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4492 boost::shared_ptr<Track> tr;
4493 boost::shared_ptr<Playlist> pl;
4495 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4497 boost::shared_ptr<Playlist::RegionList> regions = pl->regions_at (
4498 (framepos_t) floor ( (double) where * tr->speed()));
4500 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4501 RegionView* rv = rtv->view()->find_view (*i);
4512 Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4514 const TrackViewList* tracks;
4517 tracks = &track_views;
4522 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4523 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4525 boost::shared_ptr<Track> tr;
4526 boost::shared_ptr<Playlist> pl;
4528 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4530 boost::shared_ptr<Playlist::RegionList> regions = pl->regions_touched (
4531 (framepos_t) floor ( (double)where * tr->speed()), max_framepos);
4533 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4535 RegionView* rv = rtv->view()->find_view (*i);
4546 /** Start with regions that are selected. Then add equivalent regions
4547 * on tracks in the same active edit-enabled route group as any of
4548 * the regions that we started with.
4552 Editor::get_regions_from_selection ()
4554 return get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
4557 /** Get regions using the following method:
4559 * Make an initial region list using the selected regions, unless
4560 * the edit point is `mouse' and the mouse is over an unselected
4561 * region. In this case, start with just that region.
4563 * Then, make an initial track list of the tracks that these
4564 * regions are on, and if the edit point is not `mouse', add the
4567 * Look at this track list and add any other tracks that are on the
4568 * same active edit-enabled route group as one of the initial tracks.
4570 * Finally take the initial region list and add any regions that are
4571 * under the edit point on one of the tracks on the track list to get
4572 * the returned region list.
4574 * The rationale here is that the mouse edit point is special in that
4575 * its position describes both a time and a track; the other edit
4576 * modes only describe a time. Hence if the edit point is `mouse' we
4577 * ignore selected tracks, as we assume the user means something by
4578 * pointing at a particular track. Also in this case we take note of
4579 * the region directly under the edit point, as there is always just one
4580 * (rather than possibly several with non-mouse edit points).
4584 Editor::get_regions_from_selection_and_edit_point ()
4586 RegionSelection regions;
4588 if (_edit_point == EditAtMouse && entered_regionview && !selection->regions.contains (entered_regionview)) {
4589 regions.add (entered_regionview);
4591 regions = selection->regions;
4594 TrackViewList tracks;
4596 if (_edit_point != EditAtMouse) {
4597 tracks = selection->tracks;
4600 /* Add any other tracks that have regions that are in the same
4601 edit-activated route group as one of our regions.
4603 for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) {
4605 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4607 if (g && g->is_active() && g->is_edit()) {
4608 tracks.add (axis_views_from_routes (g->route_list()));
4612 if (!tracks.empty()) {
4613 /* now find regions that are at the edit position on those tracks */
4614 framepos_t const where = get_preferred_edit_position ();
4615 get_regions_at (regions, where, tracks);
4621 /** Start with regions that are selected, or the entered regionview if none are selected.
4622 * Then add equivalent regions on tracks in the same active edit-enabled route group as any
4623 * of the regions that we started with.
4627 Editor::get_regions_from_selection_and_entered ()
4629 RegionSelection regions = selection->regions;
4631 if (regions.empty() && entered_regionview) {
4632 regions.add (entered_regionview);
4635 return get_equivalent_regions (regions, ARDOUR::Properties::edit.property_id);
4639 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4641 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4643 RouteTimeAxisView* tatv;
4645 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4647 boost::shared_ptr<Playlist> pl;
4648 vector<boost::shared_ptr<Region> > results;
4650 boost::shared_ptr<Track> tr;
4652 if ((tr = tatv->track()) == 0) {
4657 if ((pl = (tr->playlist())) != 0) {
4658 pl->get_region_list_equivalent_regions (region, results);
4661 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4662 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4663 regions.push_back (marv);
4672 Editor::show_rhythm_ferret ()
4674 if (rhythm_ferret == 0) {
4675 rhythm_ferret = new RhythmFerret(*this);
4678 rhythm_ferret->set_session (_session);
4679 rhythm_ferret->show ();
4680 rhythm_ferret->present ();
4684 Editor::first_idle ()
4686 MessageDialog* dialog = 0;
4688 if (track_views.size() > 1) {
4689 dialog = new MessageDialog (
4691 string_compose (_("Please wait while %1 loads visual data."), PROGRAM_NAME),
4695 ARDOUR_UI::instance()->flush_pending ();
4698 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4702 // first idle adds route children (automation tracks), so we need to redisplay here
4703 _routes->redisplay ();
4710 Editor::_idle_resize (gpointer arg)
4712 return ((Editor*)arg)->idle_resize ();
4716 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4718 if (resize_idle_id < 0) {
4719 resize_idle_id = g_idle_add (_idle_resize, this);
4720 _pending_resize_amount = 0;
4723 /* make a note of the smallest resulting height, so that we can clamp the
4724 lower limit at TimeAxisView::hSmall */
4726 int32_t min_resulting = INT32_MAX;
4728 _pending_resize_amount += h;
4729 _pending_resize_view = view;
4731 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4733 if (selection->tracks.contains (_pending_resize_view)) {
4734 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4735 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4739 if (min_resulting < 0) {
4744 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4745 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4749 /** Handle pending resizing of tracks */
4751 Editor::idle_resize ()
4753 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4755 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4756 selection->tracks.contains (_pending_resize_view)) {
4758 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4759 if (*i != _pending_resize_view) {
4760 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4765 _pending_resize_amount = 0;
4767 _group_tabs->set_dirty ();
4768 resize_idle_id = -1;
4776 ENSURE_GUI_THREAD (*this, &Editor::located);
4778 playhead_cursor->set_position (_session->audible_frame ());
4779 if (_follow_playhead && !_pending_initial_locate) {
4780 reset_x_origin_to_follow_playhead ();
4783 _pending_locate_request = false;
4784 _pending_initial_locate = false;
4788 Editor::region_view_added (RegionView *)
4790 _summary->set_dirty ();
4794 Editor::region_view_removed ()
4796 _summary->set_dirty ();
4800 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4802 TrackViewList::const_iterator j = track_views.begin ();
4803 while (j != track_views.end()) {
4804 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4805 if (rtv && rtv->route() == r) {
4816 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4820 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4821 TimeAxisView* tv = axis_view_from_route (*i);
4832 Editor::handle_new_route (RouteList& routes)
4834 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4836 RouteTimeAxisView *rtv;
4837 list<RouteTimeAxisView*> new_views;
4839 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4840 boost::shared_ptr<Route> route = (*x);
4842 if (route->is_hidden() || route->is_monitor()) {
4846 DataType dt = route->input()->default_type();
4848 if (dt == ARDOUR::DataType::AUDIO) {
4849 rtv = new AudioTimeAxisView (*this, _session, *track_canvas);
4850 rtv->set_route (route);
4851 } else if (dt == ARDOUR::DataType::MIDI) {
4852 rtv = new MidiTimeAxisView (*this, _session, *track_canvas);
4853 rtv->set_route (route);
4855 throw unknown_type();
4858 new_views.push_back (rtv);
4859 track_views.push_back (rtv);
4861 rtv->effective_gain_display ();
4863 if (internal_editing()) {
4864 rtv->enter_internal_edit_mode ();
4866 rtv->leave_internal_edit_mode ();
4869 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4870 rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed));
4873 _routes->routes_added (new_views);
4874 _summary->routes_added (new_views);
4876 if (show_editor_mixer_when_tracks_arrive) {
4877 show_editor_mixer (true);
4880 editor_list_button.set_sensitive (true);
4884 Editor::timeaxisview_deleted (TimeAxisView *tv)
4886 if (_session && _session->deletion_in_progress()) {
4887 /* the situation is under control */
4891 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
4893 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
4895 _routes->route_removed (tv);
4897 if (tv == entered_track) {
4901 TimeAxisView::Children c = tv->get_child_list ();
4902 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
4903 if (entered_track == i->get()) {
4908 /* remove it from the list of track views */
4910 TrackViewList::iterator i;
4912 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
4913 i = track_views.erase (i);
4916 /* update whatever the current mixer strip is displaying, if revelant */
4918 boost::shared_ptr<Route> route;
4921 route = rtav->route ();
4924 if (current_mixer_strip && current_mixer_strip->route() == route) {
4926 TimeAxisView* next_tv;
4928 if (track_views.empty()) {
4930 } else if (i == track_views.end()) {
4931 next_tv = track_views.front();
4938 set_selected_mixer_strip (*next_tv);
4940 /* make the editor mixer strip go away setting the
4941 * button to inactive (which also unticks the menu option)
4944 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
4950 Editor::hide_track_in_display (TimeAxisView* tv, bool apply_to_selection)
4952 if (apply_to_selection) {
4953 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ) {
4955 TrackSelection::iterator j = i;
4958 hide_track_in_display (*i, false);
4963 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
4965 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
4966 // this will hide the mixer strip
4967 set_selected_mixer_strip (*tv);
4970 _routes->hide_track_in_display (*tv);
4975 Editor::sync_track_view_list_and_routes ()
4977 track_views = TrackViewList (_routes->views ());
4979 _summary->set_dirty ();
4980 _group_tabs->set_dirty ();
4982 return false; // do not call again (until needed)
4986 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
4988 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4993 /** Find a RouteTimeAxisView by the ID of its route */
4995 Editor::get_route_view_by_route_id (const PBD::ID& id) const
4997 RouteTimeAxisView* v;
4999 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5000 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5001 if(v->route()->id() == id) {
5011 Editor::fit_route_group (RouteGroup *g)
5013 TrackViewList ts = axis_views_from_routes (g->route_list ());
5018 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5020 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5023 _session->cancel_audition ();
5027 if (_session->is_auditioning()) {
5028 _session->cancel_audition ();
5029 if (r == last_audition_region) {
5034 _session->audition_region (r);
5035 last_audition_region = r;
5040 Editor::hide_a_region (boost::shared_ptr<Region> r)
5042 r->set_hidden (true);
5046 Editor::show_a_region (boost::shared_ptr<Region> r)
5048 r->set_hidden (false);
5052 Editor::audition_region_from_region_list ()
5054 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5058 Editor::hide_region_from_region_list ()
5060 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5064 Editor::show_region_in_region_list ()
5066 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5070 Editor::step_edit_status_change (bool yn)
5073 start_step_editing ();
5075 stop_step_editing ();
5080 Editor::start_step_editing ()
5082 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5086 Editor::stop_step_editing ()
5088 step_edit_connection.disconnect ();
5092 Editor::check_step_edit ()
5094 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5095 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5097 mtv->check_step_edit ();
5101 return true; // do it again, till we stop
5105 Editor::scroll_press (Direction dir)
5107 ++_scroll_callbacks;
5109 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5110 /* delay the first auto-repeat */
5116 scroll_backward (1);
5124 scroll_tracks_up_line ();
5128 scroll_tracks_down_line ();
5132 /* do hacky auto-repeat */
5133 if (!_scroll_connection.connected ()) {
5135 _scroll_connection = Glib::signal_timeout().connect (
5136 sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), dir), 100
5139 _scroll_callbacks = 0;
5146 Editor::scroll_release ()
5148 _scroll_connection.disconnect ();
5151 /** Queue a change for the Editor viewport x origin to follow the playhead */
5153 Editor::reset_x_origin_to_follow_playhead ()
5155 framepos_t const frame = playhead_cursor->current_frame;
5157 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5159 if (_session->transport_speed() < 0) {
5161 if (frame > (current_page_frames() / 2)) {
5162 center_screen (frame-(current_page_frames()/2));
5164 center_screen (current_page_frames()/2);
5169 if (frame < leftmost_frame) {
5172 if (_session->transport_rolling()) {
5173 /* rolling; end up with the playhead at the right of the page */
5174 l = frame - current_page_frames ();
5176 /* not rolling: end up with the playhead 3/4 of the way along the page */
5177 l = frame - (3 * current_page_frames() / 4);
5184 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5187 if (_session->transport_rolling()) {
5188 /* rolling: end up with the playhead on the left of the page */
5189 center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5191 /* not rolling: end up with the playhead 1/4 of the way along the page */
5192 center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5200 Editor::super_rapid_screen_update ()
5202 if (!_session || !_session->engine().running()) {
5206 /* METERING / MIXER STRIPS */
5208 /* update track meters, if required */
5209 if (is_mapped() && meters_running) {
5210 RouteTimeAxisView* rtv;
5211 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5212 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5213 rtv->fast_update ();
5218 /* and any current mixer strip */
5219 if (current_mixer_strip) {
5220 current_mixer_strip->fast_update ();
5223 /* PLAYHEAD AND VIEWPORT */
5225 framepos_t const frame = _session->audible_frame();
5227 /* There are a few reasons why we might not update the playhead / viewport stuff:
5229 * 1. we don't update things when there's a pending locate request, otherwise
5230 * when the editor requests a locate there is a chance that this method
5231 * will move the playhead before the locate request is processed, causing
5233 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5234 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5237 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5239 last_update_frame = frame;
5241 if (!_dragging_playhead) {
5242 playhead_cursor->set_position (frame);
5245 if (!_stationary_playhead) {
5247 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5248 reset_x_origin_to_follow_playhead ();
5253 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5257 // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5258 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5259 if (target <= 0.0) {
5262 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5263 target = (target * 0.15) + (current * 0.85);
5269 set_horizontal_position (current);
5278 Editor::session_going_away ()
5280 _have_idled = false;
5282 _session_connections.drop_connections ();
5284 super_rapid_screen_update_connection.disconnect ();
5286 selection->clear ();
5287 cut_buffer->clear ();
5289 clicked_regionview = 0;
5290 clicked_axisview = 0;
5291 clicked_routeview = 0;
5292 clicked_crossfadeview = 0;
5293 entered_regionview = 0;
5295 last_update_frame = 0;
5298 playhead_cursor->canvas_item.hide ();
5300 /* rip everything out of the list displays */
5304 _route_groups->clear ();
5306 /* do this first so that deleting a track doesn't reset cms to null
5307 and thus cause a leak.
5310 if (current_mixer_strip) {
5311 if (current_mixer_strip->get_parent() != 0) {
5312 global_hpacker.remove (*current_mixer_strip);
5314 delete current_mixer_strip;
5315 current_mixer_strip = 0;
5318 /* delete all trackviews */
5320 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5323 track_views.clear ();
5325 zoom_range_clock->set_session (0);
5326 nudge_clock->set_session (0);
5328 editor_list_button.set_active(false);
5329 editor_list_button.set_sensitive(false);
5331 /* clear tempo/meter rulers */
5332 remove_metric_marks ();
5334 clear_marker_display ();
5336 current_bbt_points_begin = current_bbt_points_end;
5338 /* get rid of any existing editor mixer strip */
5340 WindowTitle title(Glib::get_application_name());
5341 title += _("Editor");
5343 set_title (title.get_string());
5345 SessionHandlePtr::session_going_away ();
5350 Editor::show_editor_list (bool yn)
5353 _the_notebook.show ();
5355 _the_notebook.hide ();
5360 Editor::change_region_layering_order (bool from_context_menu)
5362 const framepos_t position = get_preferred_edit_position (false, from_context_menu);
5364 if (!clicked_routeview) {
5365 if (layering_order_editor) {
5366 layering_order_editor->hide ();
5371 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
5377 boost::shared_ptr<Playlist> pl = track->playlist();
5383 if (layering_order_editor == 0) {
5384 layering_order_editor = new RegionLayeringOrderEditor (*this);
5385 layering_order_editor->set_position (WIN_POS_MOUSE);
5388 layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position);
5389 layering_order_editor->maybe_present ();
5393 Editor::update_region_layering_order_editor ()
5395 if (layering_order_editor && layering_order_editor->is_visible ()) {
5396 change_region_layering_order (true);
5401 Editor::setup_fade_images ()
5403 _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear")));
5404 _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut")));
5405 _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut")));
5406 _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut")));
5407 _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut")));
5409 _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear")));
5410 _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut")));
5411 _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut")));
5412 _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut")));
5413 _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut")));
5417 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
5419 Editor::action_menu_item (std::string const & name)
5421 Glib::RefPtr<Action> a = editor_actions->get_action (name);
5424 return *manage (a->create_menu_item ());
5428 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
5430 EventBox* b = manage (new EventBox);
5431 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
5432 Label* l = manage (new Label (name));
5436 _the_notebook.append_page (widget, *b);
5440 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
5442 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
5443 _the_notebook.set_current_page (_the_notebook.page_num (*page));
5446 if (ev->type == GDK_2BUTTON_PRESS) {
5448 /* double-click on a notebook tab shrinks or expands the notebook */
5450 if (_notebook_shrunk) {
5451 edit_pane.set_position (pre_maximal_horizontal_pane_position);
5452 _notebook_shrunk = false;
5454 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
5455 edit_pane.set_position (edit_pane.get_position() + page->get_width());
5456 _notebook_shrunk = true;
5464 Editor::popup_control_point_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
5466 using namespace Menu_Helpers;
5468 MenuList& items = _control_point_context_menu.items ();
5471 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun (*this, &Editor::edit_control_point), item)));
5472 items.push_back (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun (*this, &Editor::remove_control_point), item)));
5473 if (!can_remove_control_point (item)) {
5474 items.back().set_sensitive (false);
5477 _control_point_context_menu.popup (event->button.button, event->button.time);