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 "mouse_cursors.h"
112 #include "playlist_selector.h"
113 #include "public_editor.h"
114 #include "region_layering_order_editor.h"
115 #include "rgb_macros.h"
116 #include "rhythm_ferret.h"
117 #include "selection.h"
119 #include "simpleline.h"
120 #include "tempo_lines.h"
121 #include "time_axis_view.h"
127 #include "imageframe_socket_handler.h"
131 using namespace ARDOUR;
134 using namespace Glib;
135 using namespace Gtkmm2ext;
136 using namespace Editing;
138 using PBD::internationalize;
140 using Gtkmm2ext::Keyboard;
142 const double Editor::timebar_height = 15.0;
144 static const gchar *_snap_type_strings[] = {
146 N_("Timecode Frames"),
147 N_("Timecode Seconds"),
148 N_("Timecode Minutes"),
176 static const gchar *_snap_mode_strings[] = {
183 static const gchar *_edit_point_strings[] = {
190 static const gchar *_zoom_focus_strings[] = {
200 #ifdef USE_RUBBERBAND
201 static const gchar *_rb_opt_strings[] = {
204 N_("Balanced multitimbral mixture"),
205 N_("Unpitched percussion with stable notes"),
206 N_("Crisp monophonic instrumental"),
207 N_("Unpitched solo percussion"),
208 N_("Resample without preserving pitch"),
214 show_me_the_size (Requisition* r, const char* what)
216 cerr << "size of " << what << " = " << r->width << " x " << r->height << endl;
221 pane_size_watcher (Paned* pane)
223 /* if the handle of a pane vanishes into (at least) the tabs of a notebook,
224 it is no longer accessible. so stop that. this doesn't happen on X11,
225 just the quartz backend.
230 int max_width_of_lhs = GTK_WIDGET(pane->gobj())->allocation.width - 25;
232 gint pos = pane->get_position ();
234 if (pos > max_width_of_lhs) {
235 pane->set_position (max_width_of_lhs);
241 : _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
243 /* time display buttons */
244 , minsec_label (_("Mins:Secs"))
245 , bbt_label (_("Bars:Beats"))
246 , timecode_label (_("Timecode"))
247 , samples_label (_("Samples"))
248 , tempo_label (_("Tempo"))
249 , meter_label (_("Meter"))
250 , mark_label (_("Location Markers"))
251 , range_mark_label (_("Range Markers"))
252 , transport_mark_label (_("Loop/Punch Ranges"))
253 , cd_mark_label (_("CD Markers"))
254 , edit_packer (4, 4, true)
256 /* the values here don't matter: layout widgets
257 reset them as needed.
260 , vertical_adjustment (0.0, 0.0, 10.0, 400.0)
262 /* tool bar related */
264 , zoom_range_clock (new AudioClock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, false, true))
266 , toolbar_selection_clock_table (2,3)
268 , automation_mode_button (_("mode"))
270 , _toolbar_viewport (*manage (new Gtk::Adjustment (0, 0, 1e10)), *manage (new Gtk::Adjustment (0, 0, 1e10)))
273 , image_socket_listener(0)
278 , nudge_clock (new AudioClock (X_("nudge"), false, X_("NudgeClock"), true, false, true))
279 , meters_running(false)
280 , _pending_locate_request (false)
281 , _pending_initial_locate (false)
282 , _last_cut_copy_source_track (0)
284 , _region_selection_change_updates_region_list (true)
288 /* we are a singleton */
290 PublicEditor::_instance = this;
294 selection = new Selection (this);
295 cut_buffer = new Selection (this);
297 clicked_regionview = 0;
298 clicked_axisview = 0;
299 clicked_routeview = 0;
300 clicked_crossfadeview = 0;
301 clicked_control_point = 0;
302 last_update_frame = 0;
303 pre_press_cursor = 0;
304 _drags = new DragManager (this);
305 current_mixer_strip = 0;
306 current_bbt_points = 0;
309 snap_type_strings = I18N (_snap_type_strings);
310 snap_mode_strings = I18N (_snap_mode_strings);
311 zoom_focus_strings = I18N (_zoom_focus_strings);
312 edit_point_strings = I18N (_edit_point_strings);
313 #ifdef USE_RUBBERBAND
314 rb_opt_strings = I18N (_rb_opt_strings);
318 snap_threshold = 5.0;
319 bbt_beat_subdivision = 4;
322 last_autoscroll_x = 0;
323 last_autoscroll_y = 0;
324 autoscroll_active = false;
325 autoscroll_timeout_tag = -1;
330 current_interthread_info = 0;
331 _show_measures = true;
332 show_gain_after_trim = false;
333 last_item_entered = 0;
335 have_pending_keyboard_selection = false;
336 _follow_playhead = true;
337 _stationary_playhead = false;
338 _xfade_visibility = true;
339 editor_ruler_menu = 0;
340 no_ruler_shown_update = false;
342 range_marker_menu = 0;
343 marker_menu_item = 0;
344 tempo_or_meter_marker_menu = 0;
345 transport_marker_menu = 0;
346 new_transport_marker_menu = 0;
347 editor_mixer_strip_width = Wide;
348 show_editor_mixer_when_tracks_arrive = false;
349 region_edit_menu_split_multichannel_item = 0;
350 region_edit_menu_split_item = 0;
353 current_stepping_trackview = 0;
355 entered_regionview = 0;
357 clear_entered_track = false;
360 button_release_can_deselect = true;
361 _dragging_playhead = false;
362 _dragging_edit_point = false;
363 select_new_marker = false;
365 layering_order_editor = 0;
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 */
640 setup_midi_toolbar ();
642 _snap_type = SnapToBeat;
643 set_snap_to (_snap_type);
644 _snap_mode = SnapOff;
645 set_snap_mode (_snap_mode);
646 set_mouse_mode (MouseObject, true);
647 pre_internal_mouse_mode = MouseObject;
648 set_edit_point_preference (EditAtMouse, true);
650 _playlist_selector = new PlaylistSelector();
651 _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
653 RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), ui_bind (&Editor::catch_vanishing_regionview, this, _1), gui_context());
657 nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
658 nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
660 nudge_forward_button.set_name ("TransportButton");
661 nudge_backward_button.set_name ("TransportButton");
663 fade_context_menu.set_name ("ArdourContextMenu");
665 /* icons, titles, WM stuff */
667 list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
668 Glib::RefPtr<Gdk::Pixbuf> icon;
670 if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
671 window_icons.push_back (icon);
673 if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
674 window_icons.push_back (icon);
676 if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
677 window_icons.push_back (icon);
679 if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
680 window_icons.push_back (icon);
682 if (!window_icons.empty()) {
683 // set_icon_list (window_icons);
684 set_default_icon_list (window_icons);
687 WindowTitle title(Glib::get_application_name());
688 title += _("Editor");
689 set_title (title.get_string());
690 set_wmclass (X_("ardour_editor"), PROGRAM_NAME);
693 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
695 signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
696 signal_delete_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
698 /* allow external control surfaces/protocols to do various things */
700 ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context());
701 ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context());
702 ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context());
703 ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), ui_bind (&Editor::control_scroll, this, _1), gui_context());
704 ControlProtocol::SelectByRID.connect (*this, invalidator (*this), ui_bind (&Editor::control_select, this, _1), gui_context());
705 BasicUI::AccessAction.connect (*this, invalidator (*this), ui_bind (&Editor::access_action, this, _1, _2), gui_context());
707 /* problematic: has to return a value and thus cannot be x-thread */
709 Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
711 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
713 TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
715 _ignore_region_action = false;
716 _last_region_menu_was_main = false;
717 _popup_region_menu_item = 0;
719 _show_marker_lines = false;
720 _over_region_trim_target = false;
722 /* Button bindings */
724 button_bindings = new Bindings;
726 XMLNode* node = button_settings();
728 for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) {
729 button_bindings->load (**i);
736 setup_fade_images ();
742 if(image_socket_listener) {
743 if(image_socket_listener->is_connected())
745 image_socket_listener->close_connection() ;
748 delete image_socket_listener ;
749 image_socket_listener = 0 ;
753 delete button_bindings;
755 delete _route_groups;
761 Editor::button_settings () const
763 XMLNode* settings = ARDOUR_UI::instance()->editor_settings();
764 XMLNode* node = find_named_node (*settings, X_("Buttons"));
767 cerr << "new empty Button node\n";
768 node = new XMLNode (X_("Buttons"));
775 Editor::add_toplevel_controls (Container& cont)
777 vpacker.pack_start (cont, false, false);
782 Editor::catch_vanishing_regionview (RegionView *rv)
784 /* note: the selection will take care of the vanishing
785 audioregionview by itself.
788 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
792 if (clicked_regionview == rv) {
793 clicked_regionview = 0;
796 if (entered_regionview == rv) {
797 set_entered_regionview (0);
800 if (!_all_region_actions_sensitized) {
801 sensitize_all_region_actions (true);
804 _over_region_trim_target = false;
808 Editor::set_entered_regionview (RegionView* rv)
810 if (rv == entered_regionview) {
814 if (entered_regionview) {
815 entered_regionview->exited ();
818 if ((entered_regionview = rv) != 0) {
819 entered_regionview->entered (internal_editing ());
822 if (!_all_region_actions_sensitized && _last_region_menu_was_main) {
823 /* This RegionView entry might have changed what region actions
824 are allowed, so sensitize them all in case a key is pressed.
826 sensitize_all_region_actions (true);
831 Editor::set_entered_track (TimeAxisView* tav)
834 entered_track->exited ();
837 if ((entered_track = tav) != 0) {
838 entered_track->entered ();
843 Editor::show_window ()
845 if (!is_visible ()) {
848 /* XXX: this is a bit unfortunate; it would probably
849 be nicer if we could just call show () above rather
850 than needing the show_all ()
853 /* re-hide stuff if necessary */
854 editor_list_button_toggled ();
855 parameter_changed ("show-summary");
856 parameter_changed ("show-group-tabs");
857 parameter_changed ("show-zoom-tools");
859 /* now reset all audio_time_axis heights, because widgets might need
865 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
866 tv = (static_cast<TimeAxisView*>(*i));
870 if (current_mixer_strip) {
871 current_mixer_strip->hide_things ();
872 current_mixer_strip->parameter_changed ("mixer-strip-visibility");
880 Editor::instant_save ()
882 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
887 _session->add_instant_xml(get_state());
889 Config->add_instant_xml(get_state());
894 Editor::zoom_adjustment_changed ()
900 double fpu = zoom_range_clock->current_duration() / _canvas_width;
904 zoom_range_clock->set ((framepos_t) floor (fpu * _canvas_width));
905 } else if (fpu > _session->current_end_frame() / _canvas_width) {
906 fpu = _session->current_end_frame() / _canvas_width;
907 zoom_range_clock->set ((framepos_t) floor (fpu * _canvas_width));
914 Editor::control_select (uint32_t rid)
916 /* handles the (static) signal from the ControlProtocol class that
917 * requests setting the selected track to a given RID
924 boost::shared_ptr<Route> r = _session->route_by_remote_id (rid);
930 TimeAxisView* tav = axis_view_from_route (r);
933 selection->set (tav);
935 selection->clear_tracks ();
940 Editor::control_scroll (float fraction)
942 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
948 double step = fraction * current_page_frames();
951 _control_scroll_target is an optional<T>
953 it acts like a pointer to an framepos_t, with
954 a operator conversion to boolean to check
955 that it has a value could possibly use
956 playhead_cursor->current_frame to store the
957 value and a boolean in the class to know
958 when it's out of date
961 if (!_control_scroll_target) {
962 _control_scroll_target = _session->transport_frame();
963 _dragging_playhead = true;
966 if ((fraction < 0.0f) && (*_control_scroll_target < (framepos_t) fabs(step))) {
967 *_control_scroll_target = 0;
968 } else if ((fraction > 0.0f) && (max_framepos - *_control_scroll_target < step)) {
969 *_control_scroll_target = max_framepos - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
971 *_control_scroll_target += (framepos_t) floor (step);
974 /* move visuals, we'll catch up with it later */
976 playhead_cursor->set_position (*_control_scroll_target);
977 UpdateAllTransportClocks (*_control_scroll_target);
979 if (*_control_scroll_target > (current_page_frames() / 2)) {
980 /* try to center PH in window */
981 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
987 Now we do a timeout to actually bring the session to the right place
988 according to the playhead. This is to avoid reading disk buffers on every
989 call to control_scroll, which is driven by ScrollTimeline and therefore
990 probably by a control surface wheel which can generate lots of events.
992 /* cancel the existing timeout */
994 control_scroll_connection.disconnect ();
996 /* add the next timeout */
998 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
1002 Editor::deferred_control_scroll (framepos_t /*target*/)
1004 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
1005 // reset for next stream
1006 _control_scroll_target = boost::none;
1007 _dragging_playhead = false;
1012 Editor::access_action (std::string action_group, std::string action_item)
1018 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
1021 act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
1029 Editor::on_realize ()
1031 Window::on_realize ();
1036 Editor::map_position_change (framepos_t frame)
1038 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, frame)
1040 if (_session == 0) {
1044 if (_follow_playhead) {
1045 center_screen (frame);
1048 playhead_cursor->set_position (frame);
1052 Editor::center_screen (framepos_t frame)
1054 double page = _canvas_width * frames_per_unit;
1056 /* if we're off the page, then scroll.
1059 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
1060 center_screen_internal (frame, page);
1065 Editor::center_screen_internal (framepos_t frame, float page)
1070 frame -= (framepos_t) page;
1075 reset_x_origin (frame);
1080 Editor::update_title ()
1082 ENSURE_GUI_THREAD (*this, &Editor::update_title)
1085 bool dirty = _session->dirty();
1087 string session_name;
1089 if (_session->snap_name() != _session->name()) {
1090 session_name = _session->snap_name();
1092 session_name = _session->name();
1096 session_name = "*" + session_name;
1099 WindowTitle title(session_name);
1100 title += Glib::get_application_name();
1101 set_title (title.get_string());
1106 Editor::set_session (Session *t)
1108 SessionHandlePtr::set_session (t);
1114 zoom_range_clock->set_session (_session);
1115 _playlist_selector->set_session (_session);
1116 nudge_clock->set_session (_session);
1117 _summary->set_session (_session);
1118 _group_tabs->set_session (_session);
1119 _route_groups->set_session (_session);
1120 _regions->set_session (_session);
1121 _snapshots->set_session (_session);
1122 _routes->set_session (_session);
1123 _locations->set_session (_session);
1125 if (rhythm_ferret) {
1126 rhythm_ferret->set_session (_session);
1129 if (analysis_window) {
1130 analysis_window->set_session (_session);
1134 sfbrowser->set_session (_session);
1137 compute_fixed_ruler_scale ();
1139 /* Make sure we have auto loop and auto punch ranges */
1141 Location* loc = _session->locations()->auto_loop_location();
1143 loc = new Location (*_session, 0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1145 if (loc->start() == loc->end()) {
1146 loc->set_end (loc->start() + 1);
1149 _session->locations()->add (loc, false);
1150 _session->set_auto_loop_location (loc);
1153 loc->set_name (_("Loop"));
1156 loc = _session->locations()->auto_punch_location();
1159 loc = new Location (*_session, 0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1161 if (loc->start() == loc->end()) {
1162 loc->set_end (loc->start() + 1);
1165 _session->locations()->add (loc, false);
1166 _session->set_auto_punch_location (loc);
1169 loc->set_name (_("Punch"));
1172 refresh_location_display ();
1174 /* This must happen after refresh_location_display(), as (amongst other things) we restore
1175 the selected Marker; this needs the LocationMarker list to be available.
1177 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1178 set_state (*node, Stateful::loading_state_version);
1180 /* catch up with the playhead */
1182 _session->request_locate (playhead_cursor->current_frame);
1183 _pending_initial_locate = true;
1187 /* These signals can all be emitted by a non-GUI thread. Therefore the
1188 handlers for them must not attempt to directly interact with the GUI,
1189 but use Gtkmm2ext::UI::instance()->call_slot();
1192 _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), ui_bind(&Editor::step_edit_status_change, this, _1), gui_context());
1193 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1194 _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context());
1195 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context());
1196 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1197 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context());
1198 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1199 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
1200 _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Editor::session_state_saved, this, _1), gui_context());
1201 _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context());
1202 _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&Editor::location_gone, this, _1), gui_context());
1203 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1204 _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display, this), gui_context());
1205 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1207 if (Profile->get_sae()) {
1208 Timecode::BBT_Time bbt;
1212 framepos_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1);
1213 nudge_clock->set_mode(AudioClock::BBT);
1214 nudge_clock->set (pos, true, 0, AudioClock::BBT);
1217 nudge_clock->set (_session->frame_rate() * 5, true, 0, AudioClock::Timecode); // default of 5 seconds
1220 playhead_cursor->canvas_item.show ();
1222 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1223 Config->map_parameters (pc);
1224 _session->config.map_parameters (pc);
1226 restore_ruler_visibility ();
1227 //tempo_map_changed (PropertyChange (0));
1228 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1230 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1231 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1234 super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
1235 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1238 switch (_snap_type) {
1239 case SnapToRegionStart:
1240 case SnapToRegionEnd:
1241 case SnapToRegionSync:
1242 case SnapToRegionBoundary:
1243 build_region_boundary_cache ();
1250 /* register for undo history */
1251 _session->register_with_memento_command_factory(id(), this);
1253 ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated));
1255 start_updating_meters ();
1259 Editor::action_pre_activated (Glib::RefPtr<Action> const & a)
1261 if (a->get_name() == "RegionMenu") {
1262 /* When the main menu's region menu is opened, we setup the actions so that they look right
1263 in the menu. I can't find a way of getting a signal when this menu is subsequently closed,
1264 so we resensitize all region actions when the entered regionview or the region selection
1265 changes. HOWEVER we can't always resensitize on entered_regionview change because that
1266 happens after the region context menu is opened. So we set a flag here, too.
1270 sensitize_the_right_region_actions ();
1271 _last_region_menu_was_main = true;
1275 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1277 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1279 using namespace Menu_Helpers;
1280 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1283 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1287 MenuList& items (fade_context_menu.items());
1291 switch (item_type) {
1293 case FadeInHandleItem:
1294 if (arv->audio_region()->fade_in_active()) {
1295 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1297 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1300 items.push_back (SeparatorElem());
1302 if (Profile->get_sae()) {
1304 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)));
1305 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)));
1312 *_fade_in_images[FadeLinear],
1313 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)
1317 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1322 *_fade_in_images[FadeFast],
1323 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)
1326 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1331 *_fade_in_images[FadeLogB],
1332 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogB)
1335 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1340 *_fade_in_images[FadeLogA],
1341 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogA)
1344 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1349 *_fade_in_images[FadeSlow],
1350 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow)
1353 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1359 case FadeOutHandleItem:
1360 if (arv->audio_region()->fade_out_active()) {
1361 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1363 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1366 items.push_back (SeparatorElem());
1368 if (Profile->get_sae()) {
1369 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)));
1370 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)));
1376 *_fade_out_images[FadeLinear],
1377 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)
1381 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1386 *_fade_out_images[FadeFast],
1387 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)
1390 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1395 *_fade_out_images[FadeLogB],
1396 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogA)
1399 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1404 *_fade_out_images[FadeLogA],
1405 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogB)
1408 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1413 *_fade_out_images[FadeSlow],
1414 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast)
1417 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1423 fatal << _("programming error: ")
1424 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1429 fade_context_menu.popup (button, time);
1433 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1435 using namespace Menu_Helpers;
1436 Menu* (Editor::*build_menu_function)();
1439 switch (item_type) {
1441 case RegionViewName:
1442 case RegionViewNameHighlight:
1443 case LeftFrameHandle:
1444 case RightFrameHandle:
1445 if (with_selection) {
1446 build_menu_function = &Editor::build_track_selection_context_menu;
1448 build_menu_function = &Editor::build_track_region_context_menu;
1453 if (with_selection) {
1454 build_menu_function = &Editor::build_track_selection_context_menu;
1456 build_menu_function = &Editor::build_track_context_menu;
1460 case CrossfadeViewItem:
1461 build_menu_function = &Editor::build_track_crossfade_context_menu;
1465 if (clicked_routeview->track()) {
1466 build_menu_function = &Editor::build_track_context_menu;
1468 build_menu_function = &Editor::build_track_bus_context_menu;
1473 /* probably shouldn't happen but if it does, we don't care */
1477 menu = (this->*build_menu_function)();
1478 menu->set_name ("ArdourContextMenu");
1480 /* now handle specific situations */
1482 switch (item_type) {
1484 case RegionViewName:
1485 case RegionViewNameHighlight:
1486 case LeftFrameHandle:
1487 case RightFrameHandle:
1488 if (!with_selection) {
1489 if (region_edit_menu_split_item) {
1490 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1491 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1493 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1496 if (region_edit_menu_split_multichannel_item) {
1497 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1498 region_edit_menu_split_multichannel_item->set_sensitive (true);
1500 region_edit_menu_split_multichannel_item->set_sensitive (false);
1509 case CrossfadeViewItem:
1516 /* probably shouldn't happen but if it does, we don't care */
1520 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1522 /* Bounce to disk */
1524 using namespace Menu_Helpers;
1525 MenuList& edit_items = menu->items();
1527 edit_items.push_back (SeparatorElem());
1529 switch (clicked_routeview->audio_track()->freeze_state()) {
1530 case AudioTrack::NoFreeze:
1531 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1534 case AudioTrack::Frozen:
1535 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1538 case AudioTrack::UnFrozen:
1539 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1545 if (item_type == StreamItem && clicked_routeview) {
1546 clicked_routeview->build_underlay_menu(menu);
1549 /* When the region menu is opened, we setup the actions so that they look right
1552 sensitize_the_right_region_actions ();
1553 _last_region_menu_was_main = false;
1555 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1556 menu->popup (button, time);
1560 Editor::build_track_context_menu ()
1562 using namespace Menu_Helpers;
1564 MenuList& edit_items = track_context_menu.items();
1567 add_dstream_context_items (edit_items);
1568 return &track_context_menu;
1572 Editor::build_track_bus_context_menu ()
1574 using namespace Menu_Helpers;
1576 MenuList& edit_items = track_context_menu.items();
1579 add_bus_context_items (edit_items);
1580 return &track_context_menu;
1584 Editor::build_track_region_context_menu ()
1586 using namespace Menu_Helpers;
1587 MenuList& edit_items = track_region_context_menu.items();
1590 /* we've just cleared the track region context menu, so the menu that these
1591 two items were on will have disappeared; stop them dangling.
1593 region_edit_menu_split_item = 0;
1594 region_edit_menu_split_multichannel_item = 0;
1596 /* we might try to use items that are currently attached to a crossfade menu,
1599 track_crossfade_context_menu.items().clear ();
1601 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1604 boost::shared_ptr<Track> tr;
1605 boost::shared_ptr<Playlist> pl;
1607 if ((tr = rtv->track())) {
1608 add_region_context_items (edit_items, tr);
1612 add_dstream_context_items (edit_items);
1614 return &track_region_context_menu;
1618 Editor::build_track_crossfade_context_menu ()
1620 using namespace Menu_Helpers;
1621 MenuList& edit_items = track_crossfade_context_menu.items();
1622 edit_items.clear ();
1624 /* we might try to use items that are currently attached to a crossfade menu,
1627 track_region_context_menu.items().clear ();
1629 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1632 boost::shared_ptr<Track> tr;
1633 boost::shared_ptr<Playlist> pl;
1634 boost::shared_ptr<AudioPlaylist> apl;
1636 if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1638 AudioPlaylist::Crossfades xfades;
1642 /* The xfade menu is a bit of a special case, as we always use the mouse position
1643 to decide whether or not to display it (rather than the edit point). No particularly
1644 strong reasons for this, other than it is a bit surprising to right-click on a xfade
1647 mouse_frame (where, ignored);
1648 apl->crossfades_at (where, xfades);
1650 bool const many = xfades.size() > 1;
1652 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1653 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1656 add_region_context_items (edit_items, tr);
1660 add_dstream_context_items (edit_items);
1662 return &track_crossfade_context_menu;
1666 Editor::analyze_region_selection ()
1668 if (analysis_window == 0) {
1669 analysis_window = new AnalysisWindow();
1672 analysis_window->set_session(_session);
1674 analysis_window->show_all();
1677 analysis_window->set_regionmode();
1678 analysis_window->analyze();
1680 analysis_window->present();
1684 Editor::analyze_range_selection()
1686 if (analysis_window == 0) {
1687 analysis_window = new AnalysisWindow();
1690 analysis_window->set_session(_session);
1692 analysis_window->show_all();
1695 analysis_window->set_rangemode();
1696 analysis_window->analyze();
1698 analysis_window->present();
1702 Editor::build_track_selection_context_menu ()
1704 using namespace Menu_Helpers;
1705 MenuList& edit_items = track_selection_context_menu.items();
1706 edit_items.clear ();
1708 add_selection_context_items (edit_items);
1709 // edit_items.push_back (SeparatorElem());
1710 // add_dstream_context_items (edit_items);
1712 return &track_selection_context_menu;
1715 /** Add context menu items relevant to crossfades.
1716 * @param edit_items List to add the items to.
1719 Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1721 using namespace Menu_Helpers;
1722 Menu *xfade_menu = manage (new Menu);
1723 MenuList& items = xfade_menu->items();
1724 xfade_menu->set_name ("ArdourContextMenu");
1727 if (xfade->active()) {
1733 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1734 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1736 if (xfade->can_follow_overlap()) {
1738 if (xfade->following_overlap()) {
1739 str = _("Convert to Short");
1741 str = _("Convert to Full");
1744 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1748 str = xfade->out()->name();
1750 str += xfade->in()->name();
1752 str = _("Crossfade");
1755 edit_items.push_back (MenuElem (str, *xfade_menu));
1756 edit_items.push_back (SeparatorElem());
1760 Editor::xfade_edit_left_region ()
1762 if (clicked_crossfadeview) {
1763 clicked_crossfadeview->left_view.show_region_editor ();
1768 Editor::xfade_edit_right_region ()
1770 if (clicked_crossfadeview) {
1771 clicked_crossfadeview->right_view.show_region_editor ();
1776 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::shared_ptr<Track> track)
1778 using namespace Menu_Helpers;
1780 /* OK, stick the region submenu at the top of the list, and then add
1784 RegionSelection rs = get_regions_from_selection_and_entered ();
1786 string::size_type pos = 0;
1787 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1789 /* we have to hack up the region name because "_" has a special
1790 meaning for menu titles.
1793 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1794 menu_item_name.replace (pos, 1, "__");
1798 if (_popup_region_menu_item == 0) {
1799 _popup_region_menu_item = new MenuItem (menu_item_name);
1800 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1801 _popup_region_menu_item->show ();
1803 _popup_region_menu_item->set_label (menu_item_name);
1806 /* Use the mouse position rather than the edit point to decide whether to show the `choose top region'
1807 dialogue. If we use the edit point it gets a bit messy because the user still has to click over
1808 *some* region in order to get the region context menu stuff to be displayed at all.
1813 mouse_frame (mouse, ignored);
1815 edit_items.push_back (*_popup_region_menu_item);
1816 if (track->playlist()->count_regions_at (mouse) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1817 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region")->create_menu_item ()));
1819 edit_items.push_back (SeparatorElem());
1822 /** Add context menu items relevant to selection ranges.
1823 * @param edit_items List to add the items to.
1826 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1828 using namespace Menu_Helpers;
1830 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1831 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1833 edit_items.push_back (SeparatorElem());
1834 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1836 edit_items.push_back (SeparatorElem());
1838 edit_items.push_back (
1840 _("Move Range Start to Previous Region Boundary"),
1841 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, false)
1845 edit_items.push_back (
1847 _("Move Range Start to Next Region Boundary"),
1848 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, true)
1852 edit_items.push_back (
1854 _("Move Range End to Previous Region Boundary"),
1855 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, false)
1859 edit_items.push_back (
1861 _("Move Range End to Next Region Boundary"),
1862 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, true)
1866 edit_items.push_back (SeparatorElem());
1867 edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection)));
1868 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1870 edit_items.push_back (SeparatorElem());
1871 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1873 edit_items.push_back (SeparatorElem());
1874 edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1875 edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1877 edit_items.push_back (SeparatorElem());
1878 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1880 edit_items.push_back (SeparatorElem());
1881 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1882 edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1883 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1885 edit_items.push_back (SeparatorElem());
1886 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1887 edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1888 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1889 edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1890 edit_items.push_back (MenuElem (_("Export Range..."), sigc::mem_fun(*this, &Editor::export_selection)));
1895 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1897 using namespace Menu_Helpers;
1901 Menu *play_menu = manage (new Menu);
1902 MenuList& play_items = play_menu->items();
1903 play_menu->set_name ("ArdourContextMenu");
1905 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1906 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1907 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1908 play_items.push_back (SeparatorElem());
1909 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
1911 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1915 Menu *select_menu = manage (new Menu);
1916 MenuList& select_items = select_menu->items();
1917 select_menu->set_name ("ArdourContextMenu");
1919 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1920 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1921 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1922 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1923 select_items.push_back (SeparatorElem());
1924 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
1925 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
1926 select_items.push_back (SeparatorElem());
1927 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1928 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1929 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1930 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1931 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
1932 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
1933 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
1935 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1939 Menu *cutnpaste_menu = manage (new Menu);
1940 MenuList& cutnpaste_items = cutnpaste_menu->items();
1941 cutnpaste_menu->set_name ("ArdourContextMenu");
1943 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1944 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1945 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1947 cutnpaste_items.push_back (SeparatorElem());
1949 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
1950 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
1952 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
1954 /* Adding new material */
1956 edit_items.push_back (SeparatorElem());
1957 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
1958 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
1962 Menu *nudge_menu = manage (new Menu());
1963 MenuList& nudge_items = nudge_menu->items();
1964 nudge_menu->set_name ("ArdourContextMenu");
1966 edit_items.push_back (SeparatorElem());
1967 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1968 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1969 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1970 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1972 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1976 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
1978 using namespace Menu_Helpers;
1982 Menu *play_menu = manage (new Menu);
1983 MenuList& play_items = play_menu->items();
1984 play_menu->set_name ("ArdourContextMenu");
1986 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1987 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1988 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1992 Menu *select_menu = manage (new Menu);
1993 MenuList& select_items = select_menu->items();
1994 select_menu->set_name ("ArdourContextMenu");
1996 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1997 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1998 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1999 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
2000 select_items.push_back (SeparatorElem());
2001 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
2002 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
2003 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2004 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2006 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2010 Menu *cutnpaste_menu = manage (new Menu);
2011 MenuList& cutnpaste_items = cutnpaste_menu->items();
2012 cutnpaste_menu->set_name ("ArdourContextMenu");
2014 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2015 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2016 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
2018 Menu *nudge_menu = manage (new Menu());
2019 MenuList& nudge_items = nudge_menu->items();
2020 nudge_menu->set_name ("ArdourContextMenu");
2022 edit_items.push_back (SeparatorElem());
2023 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2024 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2025 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2026 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2028 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2032 Editor::snap_type() const
2038 Editor::snap_mode() const
2044 Editor::set_snap_to (SnapType st)
2046 unsigned int snap_ind = (unsigned int)st;
2050 if (snap_ind > snap_type_strings.size() - 1) {
2052 _snap_type = (SnapType)snap_ind;
2055 string str = snap_type_strings[snap_ind];
2057 if (str != snap_type_selector.get_active_text()) {
2058 snap_type_selector.set_active_text (str);
2063 switch (_snap_type) {
2064 case SnapToBeatDiv32:
2065 case SnapToBeatDiv28:
2066 case SnapToBeatDiv24:
2067 case SnapToBeatDiv20:
2068 case SnapToBeatDiv16:
2069 case SnapToBeatDiv14:
2070 case SnapToBeatDiv12:
2071 case SnapToBeatDiv10:
2072 case SnapToBeatDiv8:
2073 case SnapToBeatDiv7:
2074 case SnapToBeatDiv6:
2075 case SnapToBeatDiv5:
2076 case SnapToBeatDiv4:
2077 case SnapToBeatDiv3:
2078 case SnapToBeatDiv2:
2079 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
2080 update_tempo_based_rulers ();
2083 case SnapToRegionStart:
2084 case SnapToRegionEnd:
2085 case SnapToRegionSync:
2086 case SnapToRegionBoundary:
2087 build_region_boundary_cache ();
2095 SnapChanged (); /* EMIT SIGNAL */
2099 Editor::set_snap_mode (SnapMode mode)
2102 string str = snap_mode_strings[(int)mode];
2104 if (str != snap_mode_selector.get_active_text ()) {
2105 snap_mode_selector.set_active_text (str);
2111 Editor::set_edit_point_preference (EditPoint ep, bool force)
2113 bool changed = (_edit_point != ep);
2116 string str = edit_point_strings[(int)ep];
2118 if (str != edit_point_selector.get_active_text ()) {
2119 edit_point_selector.set_active_text (str);
2122 set_canvas_cursor ();
2124 if (!force && !changed) {
2128 const char* action=NULL;
2130 switch (_edit_point) {
2131 case EditAtPlayhead:
2132 action = "edit-at-playhead";
2134 case EditAtSelectedMarker:
2135 action = "edit-at-marker";
2138 action = "edit-at-mouse";
2142 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2144 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2148 bool in_track_canvas;
2150 if (!mouse_frame (foo, in_track_canvas)) {
2151 in_track_canvas = false;
2154 reset_canvas_action_sensitivity (in_track_canvas);
2160 Editor::set_state (const XMLNode& node, int /*version*/)
2162 const XMLProperty* prop;
2169 g.base_width = default_width;
2170 g.base_height = default_height;
2174 if ((geometry = find_named_node (node, "geometry")) != 0) {
2178 if ((prop = geometry->property("x_size")) == 0) {
2179 prop = geometry->property ("x-size");
2182 g.base_width = atoi(prop->value());
2184 if ((prop = geometry->property("y_size")) == 0) {
2185 prop = geometry->property ("y-size");
2188 g.base_height = atoi(prop->value());
2191 if ((prop = geometry->property ("x_pos")) == 0) {
2192 prop = geometry->property ("x-pos");
2195 x = atoi (prop->value());
2198 if ((prop = geometry->property ("y_pos")) == 0) {
2199 prop = geometry->property ("y-pos");
2202 y = atoi (prop->value());
2206 set_default_size (g.base_width, g.base_height);
2209 if (_session && (prop = node.property ("playhead"))) {
2211 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2212 playhead_cursor->set_position (pos);
2214 playhead_cursor->set_position (0);
2217 if ((prop = node.property ("mixer-width"))) {
2218 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2221 if ((prop = node.property ("zoom-focus"))) {
2222 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2225 if ((prop = node.property ("zoom"))) {
2226 reset_zoom (PBD::atof (prop->value()));
2228 reset_zoom (frames_per_unit);
2231 if ((prop = node.property ("snap-to"))) {
2232 set_snap_to ((SnapType) atoi (prop->value()));
2235 if ((prop = node.property ("snap-mode"))) {
2236 set_snap_mode ((SnapMode) atoi (prop->value()));
2239 if ((prop = node.property ("mouse-mode"))) {
2240 MouseMode m = str2mousemode(prop->value());
2241 set_mouse_mode (m, true);
2243 set_mouse_mode (MouseObject, true);
2246 if ((prop = node.property ("left-frame")) != 0) {
2248 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2249 reset_x_origin (pos);
2253 if ((prop = node.property ("y-origin")) != 0) {
2254 reset_y_origin (atof (prop->value ()));
2257 if ((prop = node.property ("internal-edit"))) {
2258 bool yn = string_is_affirmative (prop->value());
2259 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2261 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2262 tact->set_active (!yn);
2263 tact->set_active (yn);
2267 if ((prop = node.property ("join-object-range"))) {
2268 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2271 if ((prop = node.property ("edit-point"))) {
2272 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2275 if ((prop = node.property ("show-measures"))) {
2276 bool yn = string_is_affirmative (prop->value());
2277 _show_measures = yn;
2278 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2280 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2281 /* do it twice to force the change */
2282 tact->set_active (!yn);
2283 tact->set_active (yn);
2287 if ((prop = node.property ("follow-playhead"))) {
2288 bool yn = string_is_affirmative (prop->value());
2289 set_follow_playhead (yn);
2290 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2292 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2293 if (tact->get_active() != yn) {
2294 tact->set_active (yn);
2299 if ((prop = node.property ("stationary-playhead"))) {
2300 bool yn = string_is_affirmative (prop->value());
2301 set_stationary_playhead (yn);
2302 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2304 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2305 if (tact->get_active() != yn) {
2306 tact->set_active (yn);
2311 if ((prop = node.property ("region-list-sort-type"))) {
2312 RegionListSortType st;
2313 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2316 if ((prop = node.property ("xfades-visible"))) {
2317 bool yn = string_is_affirmative (prop->value());
2318 _xfade_visibility = !yn;
2319 // set_xfade_visibility (yn);
2322 if ((prop = node.property ("show-editor-mixer"))) {
2324 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2327 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2328 bool yn = string_is_affirmative (prop->value());
2330 /* do it twice to force the change */
2332 tact->set_active (!yn);
2333 tact->set_active (yn);
2336 if ((prop = node.property ("show-editor-list"))) {
2338 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2341 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2342 bool yn = string_is_affirmative (prop->value());
2344 /* do it twice to force the change */
2346 tact->set_active (!yn);
2347 tact->set_active (yn);
2350 if ((prop = node.property (X_("editor-list-page")))) {
2351 _the_notebook.set_current_page (atoi (prop->value ()));
2354 if ((prop = node.property (X_("show-marker-lines")))) {
2355 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2357 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2358 bool yn = string_is_affirmative (prop->value ());
2360 tact->set_active (!yn);
2361 tact->set_active (yn);
2364 XMLNodeList children = node.children ();
2365 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2366 selection->set_state (**i, Stateful::current_state_version);
2367 _regions->set_state (**i);
2374 Editor::get_state ()
2376 XMLNode* node = new XMLNode ("Editor");
2379 id().print (buf, sizeof (buf));
2380 node->add_property ("id", buf);
2382 if (is_realized()) {
2383 Glib::RefPtr<Gdk::Window> win = get_window();
2385 int x, y, width, height;
2386 win->get_root_origin(x, y);
2387 win->get_size(width, height);
2389 XMLNode* geometry = new XMLNode ("geometry");
2391 snprintf(buf, sizeof(buf), "%d", width);
2392 geometry->add_property("x-size", string(buf));
2393 snprintf(buf, sizeof(buf), "%d", height);
2394 geometry->add_property("y-size", string(buf));
2395 snprintf(buf, sizeof(buf), "%d", x);
2396 geometry->add_property("x-pos", string(buf));
2397 snprintf(buf, sizeof(buf), "%d", y);
2398 geometry->add_property("y-pos", string(buf));
2399 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2400 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2401 geometry->add_property("notebook-shrunk", _notebook_shrunk ? "1" : "0");
2402 snprintf(buf,sizeof(buf), "%d",pre_maximal_horizontal_pane_position);
2403 geometry->add_property("pre-maximal-horizontal-pane-position", string(buf));
2404 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2405 geometry->add_property("edit-vertical-pane-pos", string(buf));
2407 node->add_child_nocopy (*geometry);
2410 maybe_add_mixer_strip_width (*node);
2412 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2413 node->add_property ("zoom-focus", buf);
2414 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2415 node->add_property ("zoom", buf);
2416 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2417 node->add_property ("snap-to", buf);
2418 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2419 node->add_property ("snap-mode", buf);
2421 node->add_property ("edit-point", enum_2_string (_edit_point));
2423 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2424 node->add_property ("playhead", buf);
2425 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2426 node->add_property ("left-frame", buf);
2427 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2428 node->add_property ("y-origin", buf);
2430 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2431 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2432 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2433 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2434 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2435 node->add_property ("mouse-mode", enum2str(mouse_mode));
2436 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2437 node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2439 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2441 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2442 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2445 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2447 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2448 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2451 snprintf (buf, sizeof (buf), "%d", _the_notebook.get_current_page ());
2452 node->add_property (X_("editor-list-page"), buf);
2454 if (button_bindings) {
2455 XMLNode* bb = new XMLNode (X_("Buttons"));
2456 button_bindings->save (*bb);
2457 node->add_child_nocopy (*bb);
2460 node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no");
2462 node->add_child_nocopy (selection->get_state ());
2463 node->add_child_nocopy (_regions->get_state ());
2470 /** @param y y offset from the top of all trackviews.
2471 * @return pair: TimeAxisView that y is over, layer index.
2472 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2473 * in stacked region display mode, otherwise 0.
2475 std::pair<TimeAxisView *, layer_t>
2476 Editor::trackview_by_y_position (double y)
2478 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2480 std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
2486 return std::make_pair ( (TimeAxisView *) 0, 0);
2489 /** Snap a position to the grid, if appropriate, taking into account current
2490 * grid settings and also the state of any snap modifier keys that may be pressed.
2491 * @param start Position to snap.
2492 * @param event Event to get current key modifier information from, or 0.
2495 Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2497 if (!_session || !event) {
2501 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2502 if (_snap_mode == SnapOff) {
2503 snap_to_internal (start, direction, for_mark);
2506 if (_snap_mode != SnapOff) {
2507 snap_to_internal (start, direction, for_mark);
2513 Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark)
2515 if (!_session || _snap_mode == SnapOff) {
2519 snap_to_internal (start, direction, for_mark);
2523 Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /*for_mark*/)
2525 const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2526 framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2528 switch (_snap_type) {
2529 case SnapToTimecodeFrame:
2530 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2531 start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2533 start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2537 case SnapToTimecodeSeconds:
2538 if (_session->config.get_timecode_offset_negative()) {
2539 start += _session->config.get_timecode_offset ();
2541 start -= _session->config.get_timecode_offset ();
2543 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2544 start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2546 start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2549 if (_session->config.get_timecode_offset_negative()) {
2550 start -= _session->config.get_timecode_offset ();
2552 start += _session->config.get_timecode_offset ();
2556 case SnapToTimecodeMinutes:
2557 if (_session->config.get_timecode_offset_negative()) {
2558 start += _session->config.get_timecode_offset ();
2560 start -= _session->config.get_timecode_offset ();
2562 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2563 start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2565 start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2567 if (_session->config.get_timecode_offset_negative()) {
2568 start -= _session->config.get_timecode_offset ();
2570 start += _session->config.get_timecode_offset ();
2574 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2580 Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark)
2582 const framepos_t one_second = _session->frame_rate();
2583 const framepos_t one_minute = _session->frame_rate() * 60;
2584 framepos_t presnap = start;
2588 switch (_snap_type) {
2589 case SnapToTimecodeFrame:
2590 case SnapToTimecodeSeconds:
2591 case SnapToTimecodeMinutes:
2592 return timecode_snap_to_internal (start, direction, for_mark);
2595 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2596 start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2598 start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2603 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2604 start = (framepos_t) ceil ((double) start / one_second) * one_second;
2606 start = (framepos_t) floor ((double) start / one_second) * one_second;
2611 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2612 start = (framepos_t) ceil ((double) start / one_minute) * one_minute;
2614 start = (framepos_t) floor ((double) start / one_minute) * one_minute;
2619 start = _session->tempo_map().round_to_bar (start, direction);
2623 start = _session->tempo_map().round_to_beat (start, direction);
2626 case SnapToBeatDiv32:
2627 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2629 case SnapToBeatDiv28:
2630 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2632 case SnapToBeatDiv24:
2633 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2635 case SnapToBeatDiv20:
2636 start = _session->tempo_map().round_to_beat_subdivision (start, 20, direction);
2638 case SnapToBeatDiv16:
2639 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2641 case SnapToBeatDiv14:
2642 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2644 case SnapToBeatDiv12:
2645 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2647 case SnapToBeatDiv10:
2648 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2650 case SnapToBeatDiv8:
2651 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2653 case SnapToBeatDiv7:
2654 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2656 case SnapToBeatDiv6:
2657 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2659 case SnapToBeatDiv5:
2660 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2662 case SnapToBeatDiv4:
2663 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2665 case SnapToBeatDiv3:
2666 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2668 case SnapToBeatDiv2:
2669 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2677 _session->locations()->marks_either_side (start, before, after);
2679 if (before == max_framepos) {
2681 } else if (after == max_framepos) {
2683 } else if (before != max_framepos && after != max_framepos) {
2684 /* have before and after */
2685 if ((start - before) < (after - start)) {
2694 case SnapToRegionStart:
2695 case SnapToRegionEnd:
2696 case SnapToRegionSync:
2697 case SnapToRegionBoundary:
2698 if (!region_boundary_cache.empty()) {
2700 vector<framepos_t>::iterator prev = region_boundary_cache.end ();
2701 vector<framepos_t>::iterator next = region_boundary_cache.end ();
2703 if (direction > 0) {
2704 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2706 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2709 if (next != region_boundary_cache.begin ()) {
2714 framepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2715 framepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2717 if (start > (p + n) / 2) {
2726 switch (_snap_mode) {
2732 if (presnap > start) {
2733 if (presnap > (start + unit_to_frame(snap_threshold))) {
2737 } else if (presnap < start) {
2738 if (presnap < (start - unit_to_frame(snap_threshold))) {
2744 /* handled at entry */
2752 Editor::setup_toolbar ()
2754 HBox* mode_box = manage(new HBox);
2755 mode_box->set_border_width (2);
2756 mode_box->set_spacing(4);
2758 /* table containing mode buttons */
2760 HBox* mouse_mode_button_box = manage (new HBox ());
2761 mouse_mode_button_box->set_spacing (2);
2763 if (Profile->get_sae()) {
2764 mouse_mode_button_box->pack_start (mouse_move_button);
2766 mouse_mode_button_box->pack_start (mouse_move_button);
2767 mouse_mode_button_box->pack_start (join_object_range_button);
2768 mouse_mode_button_box->pack_start (mouse_select_button);
2771 mouse_mode_button_box->pack_start (mouse_zoom_button);
2773 if (!Profile->get_sae()) {
2774 mouse_mode_button_box->pack_start (mouse_gain_button);
2777 mouse_mode_button_box->pack_start (mouse_timefx_button);
2778 mouse_mode_button_box->pack_start (mouse_audition_button);
2779 mouse_mode_button_box->pack_start (internal_edit_button);
2781 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2782 if (!Profile->get_sae()) {
2783 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2785 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2787 edit_mode_selector.set_name ("EditModeSelector");
2788 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2789 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2791 mode_box->pack_start (edit_mode_selector);
2792 mode_box->pack_start (*mouse_mode_button_box);
2794 _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2795 _mouse_mode_tearoff->set_name ("MouseModeBase");
2796 _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2798 if (Profile->get_sae()) {
2799 _mouse_mode_tearoff->set_can_be_torn_off (false);
2802 _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2803 &_mouse_mode_tearoff->tearoff_window()));
2804 _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2805 &_mouse_mode_tearoff->tearoff_window(), 1));
2806 _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2807 &_mouse_mode_tearoff->tearoff_window()));
2808 _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2809 &_mouse_mode_tearoff->tearoff_window(), 1));
2813 _zoom_box.set_spacing (1);
2814 _zoom_box.set_border_width (0);
2818 zoom_in_button.set_name ("zoom button");
2819 zoom_in_button.set_image (::get_icon ("zoom_in"));
2820 zoom_in_button.set_tweaks (ArdourButton::ShowClick);
2821 act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-in"));
2822 zoom_in_button.set_related_action (act);
2824 zoom_out_button.set_name ("zoom button");
2825 zoom_out_button.set_image (::get_icon ("zoom_out"));
2826 zoom_out_button.set_tweaks (ArdourButton::ShowClick);
2827 act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-out"));
2828 zoom_out_button.set_related_action (act);
2830 zoom_out_full_button.set_name ("zoom button");
2831 zoom_out_full_button.set_image (::get_icon ("zoom_full"));
2832 zoom_out_full_button.set_tweaks (ArdourButton::ShowClick);
2833 act = ActionManager::get_action (X_("Editor"), X_("zoom-to-session"));
2834 zoom_out_full_button.set_related_action (act);
2836 zoom_focus_selector.set_name ("ZoomFocusSelector");
2837 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
2838 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2840 _zoom_box.pack_start (zoom_out_button, false, false);
2841 _zoom_box.pack_start (zoom_in_button, false, false);
2842 _zoom_box.pack_start (zoom_out_full_button, false, false);
2844 _zoom_box.pack_start (zoom_focus_selector);
2846 /* Track zoom buttons */
2847 tav_expand_button.set_name ("TrackHeightButton");
2848 tav_expand_button.set_size_request (-1, 20);
2849 tav_expand_button.add (*(manage (new Image (::get_icon ("tav_exp")))));
2850 act = ActionManager::get_action (X_("Editor"), X_("expand-tracks"));
2851 act->connect_proxy (tav_expand_button);
2853 tav_shrink_button.set_name ("TrackHeightButton");
2854 tav_shrink_button.set_size_request (-1, 20);
2855 tav_shrink_button.add (*(manage (new Image (::get_icon ("tav_shrink")))));
2856 act = ActionManager::get_action (X_("Editor"), X_("shrink-tracks"));
2857 act->connect_proxy (tav_shrink_button);
2859 _zoom_box.pack_start (tav_shrink_button);
2860 _zoom_box.pack_start (tav_expand_button);
2862 _zoom_tearoff = manage (new TearOff (_zoom_box));
2864 _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2865 &_zoom_tearoff->tearoff_window()));
2866 _zoom_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2867 &_zoom_tearoff->tearoff_window(), 0));
2868 _zoom_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2869 &_zoom_tearoff->tearoff_window()));
2870 _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2871 &_zoom_tearoff->tearoff_window(), 0));
2873 snap_box.set_spacing (1);
2874 snap_box.set_border_width (2);
2876 snap_type_selector.set_name ("SnapTypeSelector");
2877 set_popdown_strings (snap_type_selector, snap_type_strings, true);
2878 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2880 snap_mode_selector.set_name ("SnapModeSelector");
2881 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
2882 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2884 edit_point_selector.set_name ("EditPointSelector");
2885 set_popdown_strings (edit_point_selector, edit_point_strings, true);
2886 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2888 snap_box.pack_start (snap_mode_selector, false, false);
2889 snap_box.pack_start (snap_type_selector, false, false);
2890 snap_box.pack_start (edit_point_selector, false, false);
2894 HBox *nudge_box = manage (new HBox);
2895 nudge_box->set_spacing(1);
2896 nudge_box->set_border_width (2);
2898 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2899 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2901 nudge_box->pack_start (nudge_backward_button, false, false);
2902 nudge_box->pack_start (nudge_forward_button, false, false);
2903 nudge_box->pack_start (*nudge_clock, false, false);
2906 /* Pack everything in... */
2908 HBox* hbox = manage (new HBox);
2909 hbox->set_spacing(10);
2911 _tools_tearoff = manage (new TearOff (*hbox));
2912 _tools_tearoff->set_name ("MouseModeBase");
2913 _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
2915 if (Profile->get_sae()) {
2916 _tools_tearoff->set_can_be_torn_off (false);
2919 _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2920 &_tools_tearoff->tearoff_window()));
2921 _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2922 &_tools_tearoff->tearoff_window(), 0));
2923 _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2924 &_tools_tearoff->tearoff_window()));
2925 _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2926 &_tools_tearoff->tearoff_window(), 0));
2928 toolbar_hbox.set_spacing (10);
2929 toolbar_hbox.set_border_width (1);
2931 toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
2932 toolbar_hbox.pack_start (*_zoom_tearoff, false, false);
2933 toolbar_hbox.pack_start (*_tools_tearoff, false, false);
2935 hbox->pack_start (snap_box, false, false);
2936 if (!Profile->get_small_screen()) {
2937 hbox->pack_start (*nudge_box, false, false);
2939 ARDOUR_UI::instance()->editor_transport_box().pack_start (*nudge_box, false, false);
2941 hbox->pack_start (panic_box, false, false);
2945 toolbar_base.set_name ("ToolBarBase");
2946 toolbar_base.add (toolbar_hbox);
2948 _toolbar_viewport.add (toolbar_base);
2949 /* stick to the required height but allow width to vary if there's not enough room */
2950 _toolbar_viewport.set_size_request (1, -1);
2952 toolbar_frame.set_shadow_type (SHADOW_OUT);
2953 toolbar_frame.set_name ("BaseFrame");
2954 toolbar_frame.add (_toolbar_viewport);
2956 DPIReset.connect (sigc::mem_fun (*this, &Editor::resize_text_widgets));
2960 Editor::setup_tooltips ()
2962 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
2963 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain"));
2964 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
2965 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
2966 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2967 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
2968 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
2969 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
2970 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
2971 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
2972 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
2973 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
2974 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
2975 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
2976 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
2977 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
2978 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
2979 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
2980 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
2981 ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
2982 ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode"));
2987 Editor::setup_midi_toolbar ()
2991 /* Midi sound notes */
2992 midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
2993 midi_sound_notes.unset_flags (CAN_FOCUS);
2994 midi_sound_notes.set_name (X_("MidiSoundNotesButton"));
2998 panic_box.pack_start (midi_sound_notes , true, true);
2999 // panic_box.pack_start (midi_panic_button, true, true);
3003 Editor::convert_drop_to_paths (
3004 vector<string>& paths,
3005 const RefPtr<Gdk::DragContext>& /*context*/,
3008 const SelectionData& data,
3012 if (_session == 0) {
3016 vector<string> uris = data.get_uris();
3020 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3021 are actually URI lists. So do it by hand.
3024 if (data.get_target() != "text/plain") {
3028 /* Parse the "uri-list" format that Nautilus provides,
3029 where each pathname is delimited by \r\n.
3031 THERE MAY BE NO NULL TERMINATING CHAR!!!
3034 string txt = data.get_text();
3038 p = (const char *) malloc (txt.length() + 1);
3039 txt.copy ((char *) p, txt.length(), 0);
3040 ((char*)p)[txt.length()] = '\0';
3046 while (g_ascii_isspace (*p))
3050 while (*q && (*q != '\n') && (*q != '\r')) {
3057 while (q > p && g_ascii_isspace (*q))
3062 uris.push_back (string (p, q - p + 1));
3066 p = strchr (p, '\n');
3078 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3080 if ((*i).substr (0,7) == "file://") {
3083 PBD::url_decode (p);
3085 // scan forward past three slashes
3087 string::size_type slashcnt = 0;
3088 string::size_type n = 0;
3089 string::iterator x = p.begin();
3091 while (slashcnt < 3 && x != p.end()) {
3094 } else if (slashcnt == 3) {
3101 if (slashcnt != 3 || x == p.end()) {
3102 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3106 paths.push_back (p.substr (n - 1));
3114 Editor::new_tempo_section ()
3120 Editor::map_transport_state ()
3122 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3124 if (_session && _session->transport_stopped()) {
3125 have_pending_keyboard_selection = false;
3128 update_loop_range_view (true);
3133 Editor::State::State (PublicEditor const * e)
3135 selection = new Selection (e);
3138 Editor::State::~State ()
3144 Editor::begin_reversible_command (string name)
3147 _session->begin_reversible_command (name);
3152 Editor::begin_reversible_command (GQuark q)
3155 _session->begin_reversible_command (q);
3160 Editor::commit_reversible_command ()
3163 _session->commit_reversible_command ();
3168 Editor::history_changed ()
3172 if (undo_action && _session) {
3173 if (_session->undo_depth() == 0) {
3176 label = string_compose(_("Undo (%1)"), _session->next_undo());
3178 undo_action->property_label() = label;
3181 if (redo_action && _session) {
3182 if (_session->redo_depth() == 0) {
3185 label = string_compose(_("Redo (%1)"), _session->next_redo());
3187 redo_action->property_label() = label;
3192 Editor::duplicate_dialog (bool with_dialog)
3196 if (mouse_mode == MouseRange) {
3197 if (selection->time.length() == 0) {
3202 RegionSelection rs = get_regions_from_selection_and_entered ();
3204 if (mouse_mode != MouseRange && rs.empty()) {
3210 ArdourDialog win (_("Duplicate"));
3211 Label label (_("Number of duplications:"));
3212 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3213 SpinButton spinner (adjustment, 0.0, 1);
3216 win.get_vbox()->set_spacing (12);
3217 win.get_vbox()->pack_start (hbox);
3218 hbox.set_border_width (6);
3219 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3221 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3222 place, visually. so do this by hand.
3225 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3226 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3227 spinner.grab_focus();
3233 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3234 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3235 win.set_default_response (RESPONSE_ACCEPT);
3237 win.set_position (WIN_POS_MOUSE);
3239 spinner.grab_focus ();
3241 switch (win.run ()) {
3242 case RESPONSE_ACCEPT:
3248 times = adjustment.get_value();
3251 if (mouse_mode == MouseRange) {
3252 duplicate_selection (times);
3254 duplicate_some_regions (rs, times);
3259 Editor::set_edit_mode (EditMode m)
3261 Config->set_edit_mode (m);
3265 Editor::cycle_edit_mode ()
3267 switch (Config->get_edit_mode()) {
3269 if (Profile->get_sae()) {
3270 Config->set_edit_mode (Lock);
3272 Config->set_edit_mode (Splice);
3276 Config->set_edit_mode (Lock);
3279 Config->set_edit_mode (Slide);
3285 Editor::edit_mode_selection_done ()
3287 string s = edit_mode_selector.get_active_text ();
3290 Config->set_edit_mode (string_to_edit_mode (s));
3295 Editor::snap_type_selection_done ()
3297 string choice = snap_type_selector.get_active_text();
3298 SnapType snaptype = SnapToBeat;
3300 if (choice == _("Beats/2")) {
3301 snaptype = SnapToBeatDiv2;
3302 } else if (choice == _("Beats/3")) {
3303 snaptype = SnapToBeatDiv3;
3304 } else if (choice == _("Beats/4")) {
3305 snaptype = SnapToBeatDiv4;
3306 } else if (choice == _("Beats/5")) {
3307 snaptype = SnapToBeatDiv5;
3308 } else if (choice == _("Beats/6")) {
3309 snaptype = SnapToBeatDiv6;
3310 } else if (choice == _("Beats/7")) {
3311 snaptype = SnapToBeatDiv7;
3312 } else if (choice == _("Beats/8")) {
3313 snaptype = SnapToBeatDiv8;
3314 } else if (choice == _("Beats/10")) {
3315 snaptype = SnapToBeatDiv10;
3316 } else if (choice == _("Beats/12")) {
3317 snaptype = SnapToBeatDiv12;
3318 } else if (choice == _("Beats/14")) {
3319 snaptype = SnapToBeatDiv14;
3320 } else if (choice == _("Beats/16")) {
3321 snaptype = SnapToBeatDiv16;
3322 } else if (choice == _("Beats/20")) {
3323 snaptype = SnapToBeatDiv20;
3324 } else if (choice == _("Beats/24")) {
3325 snaptype = SnapToBeatDiv24;
3326 } else if (choice == _("Beats/28")) {
3327 snaptype = SnapToBeatDiv28;
3328 } else if (choice == _("Beats/32")) {
3329 snaptype = SnapToBeatDiv32;
3330 } else if (choice == _("Beats")) {
3331 snaptype = SnapToBeat;
3332 } else if (choice == _("Bars")) {
3333 snaptype = SnapToBar;
3334 } else if (choice == _("Marks")) {
3335 snaptype = SnapToMark;
3336 } else if (choice == _("Region starts")) {
3337 snaptype = SnapToRegionStart;
3338 } else if (choice == _("Region ends")) {
3339 snaptype = SnapToRegionEnd;
3340 } else if (choice == _("Region bounds")) {
3341 snaptype = SnapToRegionBoundary;
3342 } else if (choice == _("Region syncs")) {
3343 snaptype = SnapToRegionSync;
3344 } else if (choice == _("CD Frames")) {
3345 snaptype = SnapToCDFrame;
3346 } else if (choice == _("Timecode Frames")) {
3347 snaptype = SnapToTimecodeFrame;
3348 } else if (choice == _("Timecode Seconds")) {
3349 snaptype = SnapToTimecodeSeconds;
3350 } else if (choice == _("Timecode Minutes")) {
3351 snaptype = SnapToTimecodeMinutes;
3352 } else if (choice == _("Seconds")) {
3353 snaptype = SnapToSeconds;
3354 } else if (choice == _("Minutes")) {
3355 snaptype = SnapToMinutes;
3358 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3360 ract->set_active ();
3365 Editor::snap_mode_selection_done ()
3367 string choice = snap_mode_selector.get_active_text();
3368 SnapMode mode = SnapNormal;
3370 if (choice == _("No Grid")) {
3372 } else if (choice == _("Grid")) {
3374 } else if (choice == _("Magnetic")) {
3375 mode = SnapMagnetic;
3378 RefPtr<RadioAction> ract = snap_mode_action (mode);
3381 ract->set_active (true);
3386 Editor::cycle_edit_point (bool with_marker)
3388 switch (_edit_point) {
3390 set_edit_point_preference (EditAtPlayhead);
3392 case EditAtPlayhead:
3394 set_edit_point_preference (EditAtSelectedMarker);
3396 set_edit_point_preference (EditAtMouse);
3399 case EditAtSelectedMarker:
3400 set_edit_point_preference (EditAtMouse);
3406 Editor::edit_point_selection_done ()
3408 string choice = edit_point_selector.get_active_text();
3409 EditPoint ep = EditAtSelectedMarker;
3411 if (choice == _("Marker")) {
3412 set_edit_point_preference (EditAtSelectedMarker);
3413 } else if (choice == _("Playhead")) {
3414 set_edit_point_preference (EditAtPlayhead);
3416 set_edit_point_preference (EditAtMouse);
3419 RefPtr<RadioAction> ract = edit_point_action (ep);
3422 ract->set_active (true);
3427 Editor::zoom_focus_selection_done ()
3429 string choice = zoom_focus_selector.get_active_text();
3430 ZoomFocus focus_type = ZoomFocusLeft;
3432 if (choice == _("Left")) {
3433 focus_type = ZoomFocusLeft;
3434 } else if (choice == _("Right")) {
3435 focus_type = ZoomFocusRight;
3436 } else if (choice == _("Center")) {
3437 focus_type = ZoomFocusCenter;
3438 } else if (choice == _("Playhead")) {
3439 focus_type = ZoomFocusPlayhead;
3440 } else if (choice == _("Mouse")) {
3441 focus_type = ZoomFocusMouse;
3442 } else if (choice == _("Edit point")) {
3443 focus_type = ZoomFocusEdit;
3446 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3449 ract->set_active ();
3454 Editor::edit_controls_button_release (GdkEventButton* ev)
3456 if (Keyboard::is_context_menu_event (ev)) {
3457 ARDOUR_UI::instance()->add_route (this);
3458 } else if (ev->button == 1) {
3459 selection->clear_tracks ();
3466 Editor::mouse_select_button_release (GdkEventButton* ev)
3468 /* this handles just right-clicks */
3470 if (ev->button != 3) {
3478 Editor::set_zoom_focus (ZoomFocus f)
3480 string str = zoom_focus_strings[(int)f];
3482 if (str != zoom_focus_selector.get_active_text()) {
3483 zoom_focus_selector.set_active_text (str);
3486 if (zoom_focus != f) {
3493 Editor::ensure_float (Window& win)
3495 win.set_transient_for (*this);
3499 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3501 /* recover or initialize pane positions. do this here rather than earlier because
3502 we don't want the positions to change the child allocations, which they seem to do.
3508 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3517 XMLNode* geometry = find_named_node (*node, "geometry");
3519 if (which == static_cast<Paned*> (&edit_pane)) {
3521 if (done & Horizontal) {
3525 if (geometry && (prop = geometry->property ("notebook-shrunk"))) {
3526 _notebook_shrunk = string_is_affirmative (prop->value ());
3529 if (geometry && (prop = geometry->property ("pre-maximal-horizontal-pane-position"))) {
3530 pre_maximal_horizontal_pane_position = atoi (prop->value ());
3533 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3534 /* initial allocation is 90% to canvas, 10% to notebook */
3535 pos = (int) floor (alloc.get_width() * 0.90f);
3536 snprintf (buf, sizeof(buf), "%d", pos);
3538 pos = atoi (prop->value());
3541 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3542 edit_pane.set_position (pos);
3543 if (pre_maximal_horizontal_pane_position == 0) {
3544 pre_maximal_horizontal_pane_position = pos;
3548 done = (Pane) (done | Horizontal);
3550 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3552 if (done & Vertical) {
3556 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3557 /* initial allocation is 90% to canvas, 10% to summary */
3558 pos = (int) floor (alloc.get_height() * 0.90f);
3559 snprintf (buf, sizeof(buf), "%d", pos);
3561 pos = atoi (prop->value());
3564 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3565 editor_summary_pane.set_position (pos);
3566 pre_maximal_vertical_pane_position = pos;
3569 done = (Pane) (done | Vertical);
3574 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3576 if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) {
3577 top_hbox.remove (toolbar_frame);
3582 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3584 if (toolbar_frame.get_parent() == 0) {
3585 top_hbox.pack_end (toolbar_frame);
3590 Editor::set_show_measures (bool yn)
3592 if (_show_measures != yn) {
3595 if ((_show_measures = yn) == true) {
3597 tempo_lines->show();
3605 Editor::toggle_follow_playhead ()
3607 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3609 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3610 set_follow_playhead (tact->get_active());
3614 /** @param yn true to follow playhead, otherwise false.
3615 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
3618 Editor::set_follow_playhead (bool yn, bool catch_up)
3620 if (_follow_playhead != yn) {
3621 if ((_follow_playhead = yn) == true && catch_up) {
3623 reset_x_origin_to_follow_playhead ();
3630 Editor::toggle_stationary_playhead ()
3632 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3634 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3635 set_stationary_playhead (tact->get_active());
3640 Editor::set_stationary_playhead (bool yn)
3642 if (_stationary_playhead != yn) {
3643 if ((_stationary_playhead = yn) == true) {
3645 // FIXME need a 3.0 equivalent of this 2.X call
3646 // update_current_screen ();
3653 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3655 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3657 xfade->set_active (!xfade->active());
3662 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3664 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3666 xfade->set_follow_overlap (!xfade->following_overlap());
3671 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3673 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3679 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3683 switch (cew.run ()) {
3684 case RESPONSE_ACCEPT:
3691 PropertyChange all_crossfade_properties;
3692 all_crossfade_properties.add (ARDOUR::Properties::active);
3693 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3694 xfade->PropertyChanged (all_crossfade_properties);
3698 Editor::playlist_selector () const
3700 return *_playlist_selector;
3704 Editor::get_grid_type_as_beats (bool& success, framepos_t position)
3708 switch (_snap_type) {
3713 case SnapToBeatDiv32:
3716 case SnapToBeatDiv28:
3719 case SnapToBeatDiv24:
3722 case SnapToBeatDiv20:
3725 case SnapToBeatDiv16:
3728 case SnapToBeatDiv14:
3731 case SnapToBeatDiv12:
3734 case SnapToBeatDiv10:
3737 case SnapToBeatDiv8:
3740 case SnapToBeatDiv7:
3743 case SnapToBeatDiv6:
3746 case SnapToBeatDiv5:
3749 case SnapToBeatDiv4:
3752 case SnapToBeatDiv3:
3755 case SnapToBeatDiv2:
3761 return _session->tempo_map().meter_at (position).beats_per_bar();
3766 case SnapToTimecodeFrame:
3767 case SnapToTimecodeSeconds:
3768 case SnapToTimecodeMinutes:
3771 case SnapToRegionStart:
3772 case SnapToRegionEnd:
3773 case SnapToRegionSync:
3774 case SnapToRegionBoundary:
3784 Editor::get_nudge_distance (framepos_t pos, framecnt_t& next)
3788 ret = nudge_clock->current_duration (pos);
3789 next = ret + 1; /* XXXX fix me */
3795 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3797 ArdourDialog dialog (_("Playlist Deletion"));
3798 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3799 "If it is kept, its audio files will not be cleaned.\n"
3800 "If it is deleted, audio files used by it alone will be cleaned."),
3803 dialog.set_position (WIN_POS_CENTER);
3804 dialog.get_vbox()->pack_start (label);
3808 dialog.add_button (_("Delete Playlist"), RESPONSE_ACCEPT);
3809 dialog.add_button (_("Keep Playlist"), RESPONSE_REJECT);
3810 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3812 switch (dialog.run ()) {
3813 case RESPONSE_ACCEPT:
3814 /* delete the playlist */
3818 case RESPONSE_REJECT:
3819 /* keep the playlist */
3831 Editor::audio_region_selection_covers (framepos_t where)
3833 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3834 if ((*a)->region()->covers (where)) {
3843 Editor::prepare_for_cleanup ()
3845 cut_buffer->clear_regions ();
3846 cut_buffer->clear_playlists ();
3848 selection->clear_regions ();
3849 selection->clear_playlists ();
3851 _regions->suspend_redisplay ();
3855 Editor::finish_cleanup ()
3857 _regions->resume_redisplay ();
3861 Editor::transport_loop_location()
3864 return _session->locations()->auto_loop_location();
3871 Editor::transport_punch_location()
3874 return _session->locations()->auto_punch_location();
3881 Editor::control_layout_scroll (GdkEventScroll* ev)
3883 if (Keyboard::some_magic_widget_has_focus()) {
3887 switch (ev->direction) {
3889 scroll_tracks_up_line ();
3893 case GDK_SCROLL_DOWN:
3894 scroll_tracks_down_line ();
3898 /* no left/right handling yet */
3906 Editor::session_state_saved (string)
3909 _snapshots->redisplay ();
3913 Editor::maximise_editing_space ()
3915 _mouse_mode_tearoff->set_visible (false);
3916 _tools_tearoff->set_visible (false);
3917 _zoom_tearoff->set_visible (false);
3919 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
3920 pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
3921 pre_maximal_editor_width = this->get_width ();
3922 pre_maximal_editor_height = this->get_height ();
3924 if (post_maximal_horizontal_pane_position == 0) {
3925 post_maximal_horizontal_pane_position = edit_pane.get_width();
3928 if (post_maximal_vertical_pane_position == 0) {
3929 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
3934 if (post_maximal_editor_width) {
3935 edit_pane.set_position (post_maximal_horizontal_pane_position -
3936 abs(post_maximal_editor_width - pre_maximal_editor_width));
3938 edit_pane.set_position (post_maximal_horizontal_pane_position);
3941 /* Hack: we must do this in an idle handler for it to work; see comment in
3942 restore_editing_space()
3945 Glib::signal_idle().connect (
3947 sigc::mem_fun (*this, &Editor::idle_reset_vertical_pane_position),
3948 post_maximal_vertical_pane_position
3952 if (Config->get_keep_tearoffs()) {
3953 _mouse_mode_tearoff->set_visible (true);
3954 _tools_tearoff->set_visible (true);
3955 if (Config->get_show_zoom_tools ()) {
3956 _zoom_tearoff->set_visible (true);
3963 Editor::idle_reset_vertical_pane_position (int p)
3965 editor_summary_pane.set_position (p);
3970 Editor::restore_editing_space ()
3972 // user changed width/height of panes during fullscreen
3974 if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
3975 post_maximal_horizontal_pane_position = edit_pane.get_position();
3978 if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
3979 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
3984 _mouse_mode_tearoff->set_visible (true);
3985 _tools_tearoff->set_visible (true);
3986 if (Config->get_show_zoom_tools ()) {
3987 _zoom_tearoff->set_visible (true);
3989 post_maximal_editor_width = this->get_width();
3990 post_maximal_editor_height = this->get_height();
3992 edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
3994 /* This is a bit of a hack, but it seems that if you set the vertical pane position
3995 here it gets reset to some wrong value after this method has finished. Doing
3996 the setup in an idle callback seems to work.
3998 Glib::signal_idle().connect (
4000 sigc::mem_fun (*this, &Editor::idle_reset_vertical_pane_position),
4001 pre_maximal_vertical_pane_position
4007 * Make new playlists for a given track and also any others that belong
4008 * to the same active route group with the `edit' property.
4013 Editor::new_playlists (TimeAxisView* v)
4015 begin_reversible_command (_("new playlists"));
4016 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4017 _session->playlists->get (playlists);
4018 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4019 commit_reversible_command ();
4023 * Use a copy of the current playlist for a given track and also any others that belong
4024 * to the same active route group with the `edit' property.
4029 Editor::copy_playlists (TimeAxisView* v)
4031 begin_reversible_command (_("copy playlists"));
4032 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4033 _session->playlists->get (playlists);
4034 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4035 commit_reversible_command ();
4038 /** Clear the current playlist for a given track and also any others that belong
4039 * to the same active route group with the `edit' property.
4044 Editor::clear_playlists (TimeAxisView* v)
4046 begin_reversible_command (_("clear playlists"));
4047 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4048 _session->playlists->get (playlists);
4049 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4050 commit_reversible_command ();
4054 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4056 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4060 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4062 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4066 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4068 atv.clear_playlist ();
4072 Editor::on_key_press_event (GdkEventKey* ev)
4074 return key_press_focus_accelerator_handler (*this, ev);
4078 Editor::on_key_release_event (GdkEventKey* ev)
4080 return Gtk::Window::on_key_release_event (ev);
4081 // return key_press_focus_accelerator_handler (*this, ev);
4084 /** Queue up a change to the viewport x origin.
4085 * @param frame New x origin.
4088 Editor::reset_x_origin (framepos_t frame)
4090 queue_visual_change (frame);
4094 Editor::reset_y_origin (double y)
4096 queue_visual_change_y (y);
4100 Editor::reset_zoom (double fpu)
4102 queue_visual_change (fpu);
4106 Editor::reposition_and_zoom (framepos_t frame, double fpu)
4108 reset_x_origin (frame);
4111 if (!no_save_visual) {
4112 undo_visual_stack.push_back (current_visual_state(false));
4116 Editor::VisualState::VisualState ()
4117 : gui_state (new GUIObjectState)
4121 Editor::VisualState::~VisualState ()
4126 Editor::VisualState*
4127 Editor::current_visual_state (bool with_tracks)
4129 VisualState* vs = new VisualState;
4130 vs->y_position = vertical_adjustment.get_value();
4131 vs->frames_per_unit = frames_per_unit;
4132 vs->leftmost_frame = leftmost_frame;
4133 vs->zoom_focus = zoom_focus;
4136 *(vs->gui_state) = *ARDOUR_UI::instance()->gui_object_state;
4143 Editor::undo_visual_state ()
4145 if (undo_visual_stack.empty()) {
4149 redo_visual_stack.push_back (current_visual_state());
4151 VisualState* vs = undo_visual_stack.back();
4152 undo_visual_stack.pop_back();
4153 use_visual_state (*vs);
4157 Editor::redo_visual_state ()
4159 if (redo_visual_stack.empty()) {
4163 undo_visual_stack.push_back (current_visual_state());
4165 VisualState* vs = redo_visual_stack.back();
4166 redo_visual_stack.pop_back();
4167 use_visual_state (*vs);
4171 Editor::swap_visual_state ()
4173 if (undo_visual_stack.empty()) {
4174 redo_visual_state ();
4176 undo_visual_state ();
4181 Editor::use_visual_state (VisualState& vs)
4183 no_save_visual = true;
4185 _routes->suspend_redisplay ();
4187 vertical_adjustment.set_value (vs.y_position);
4189 set_zoom_focus (vs.zoom_focus);
4190 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4192 *ARDOUR_UI::instance()->gui_object_state = *vs.gui_state;
4194 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4195 (*i)->reset_visual_state ();
4198 _routes->update_visibility ();
4199 _routes->resume_redisplay ();
4201 no_save_visual = false;
4205 Editor::set_frames_per_unit (double fpu)
4207 /* this is the core function that controls the zoom level of the canvas. it is called
4208 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4211 if (fpu == frames_per_unit) {
4220 /* don't allow zooms that fit more than the maximum number
4221 of frames into an 800 pixel wide space.
4224 if (max_framepos / fpu < 800.0) {
4229 tempo_lines->tempo_map_changed();
4231 frames_per_unit = fpu;
4236 Editor::post_zoom ()
4238 // convert fpu to frame count
4240 framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width);
4242 if (frames_per_unit != zoom_range_clock->current_duration()) {
4243 zoom_range_clock->set (frames);
4246 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4247 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4248 (*i)->reshow_selection (selection->time);
4252 ZoomChanged (); /* EMIT_SIGNAL */
4254 //reset_scrolling_region ();
4256 if (playhead_cursor) {
4257 playhead_cursor->set_position (playhead_cursor->current_frame);
4260 refresh_location_display();
4261 _summary->set_overlays_dirty ();
4263 update_marker_labels ();
4269 Editor::queue_visual_change (framepos_t where)
4271 pending_visual_change.add (VisualChange::TimeOrigin);
4272 pending_visual_change.time_origin = where;
4273 ensure_visual_change_idle_handler ();
4277 Editor::queue_visual_change (double fpu)
4279 pending_visual_change.add (VisualChange::ZoomLevel);
4280 pending_visual_change.frames_per_unit = fpu;
4282 ensure_visual_change_idle_handler ();
4286 Editor::queue_visual_change_y (double y)
4288 pending_visual_change.add (VisualChange::YOrigin);
4289 pending_visual_change.y_origin = y;
4291 ensure_visual_change_idle_handler ();
4295 Editor::ensure_visual_change_idle_handler ()
4297 if (pending_visual_change.idle_handler_id < 0) {
4298 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4303 Editor::_idle_visual_changer (void* arg)
4305 return static_cast<Editor*>(arg)->idle_visual_changer ();
4309 Editor::idle_visual_changer ()
4311 VisualChange::Type p = pending_visual_change.pending;
4312 pending_visual_change.pending = (VisualChange::Type) 0;
4314 double const last_time_origin = horizontal_position ();
4316 if (p & VisualChange::TimeOrigin) {
4317 /* This is a bit of a hack, but set_frames_per_unit
4318 below will (if called) end up with the
4319 CrossfadeViews looking at Editor::leftmost_frame,
4320 and if we're changing origin and zoom in the same
4321 operation it will be the wrong value unless we
4325 leftmost_frame = pending_visual_change.time_origin;
4328 if (p & VisualChange::ZoomLevel) {
4329 set_frames_per_unit (pending_visual_change.frames_per_unit);
4331 compute_fixed_ruler_scale ();
4332 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4333 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4334 update_tempo_based_rulers ();
4336 if (p & VisualChange::TimeOrigin) {
4337 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4339 if (p & VisualChange::YOrigin) {
4340 vertical_adjustment.set_value (pending_visual_change.y_origin);
4343 if (last_time_origin == horizontal_position ()) {
4344 /* changed signal not emitted */
4345 update_fixed_rulers ();
4346 redisplay_tempo (true);
4349 _summary->set_overlays_dirty ();
4351 pending_visual_change.idle_handler_id = -1;
4352 return 0; /* this is always a one-shot call */
4355 struct EditorOrderTimeAxisSorter {
4356 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4357 return a->order () < b->order ();
4362 Editor::sort_track_selection (TrackViewList* sel)
4364 EditorOrderTimeAxisSorter cmp;
4369 selection->tracks.sort (cmp);
4374 Editor::get_preferred_edit_position (bool ignore_playhead)
4377 framepos_t where = 0;
4378 EditPoint ep = _edit_point;
4380 if (entered_marker) {
4381 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position()));
4382 return entered_marker->position();
4385 if (ignore_playhead && ep == EditAtPlayhead) {
4386 ep = EditAtSelectedMarker;
4390 case EditAtPlayhead:
4391 where = _session->audible_frame();
4392 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where));
4395 case EditAtSelectedMarker:
4396 if (!selection->markers.empty()) {
4398 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4401 where = loc->start();
4405 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where));
4413 if (!mouse_frame (where, ignored)) {
4414 /* XXX not right but what can we do ? */
4418 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where));
4426 Editor::set_loop_range (framepos_t start, framepos_t end, string cmd)
4428 if (!_session) return;
4430 begin_reversible_command (cmd);
4434 if ((tll = transport_loop_location()) == 0) {
4435 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop);
4436 XMLNode &before = _session->locations()->get_state();
4437 _session->locations()->add (loc, true);
4438 _session->set_auto_loop_location (loc);
4439 XMLNode &after = _session->locations()->get_state();
4440 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4442 XMLNode &before = tll->get_state();
4443 tll->set_hidden (false, this);
4444 tll->set (start, end);
4445 XMLNode &after = tll->get_state();
4446 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4449 commit_reversible_command ();
4453 Editor::set_punch_range (framepos_t start, framepos_t end, string cmd)
4455 if (!_session) return;
4457 begin_reversible_command (cmd);
4461 if ((tpl = transport_punch_location()) == 0) {
4462 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoPunch);
4463 XMLNode &before = _session->locations()->get_state();
4464 _session->locations()->add (loc, true);
4465 _session->set_auto_loop_location (loc);
4466 XMLNode &after = _session->locations()->get_state();
4467 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4470 XMLNode &before = tpl->get_state();
4471 tpl->set_hidden (false, this);
4472 tpl->set (start, end);
4473 XMLNode &after = tpl->get_state();
4474 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4477 commit_reversible_command ();
4480 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4481 * @param rs List to which found regions are added.
4482 * @param where Time to look at.
4483 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4486 Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4488 const TrackViewList* tracks;
4491 tracks = &track_views;
4496 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4498 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4501 boost::shared_ptr<Track> tr;
4502 boost::shared_ptr<Playlist> pl;
4504 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4506 Playlist::RegionList* regions = pl->regions_at (
4507 (framepos_t) floor ( (double) where * tr->speed()));
4509 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4510 RegionView* rv = rtv->view()->find_view (*i);
4523 Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4525 const TrackViewList* tracks;
4528 tracks = &track_views;
4533 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4534 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4536 boost::shared_ptr<Track> tr;
4537 boost::shared_ptr<Playlist> pl;
4539 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4541 Playlist::RegionList* regions = pl->regions_touched (
4542 (framepos_t) floor ( (double)where * tr->speed()), max_framepos);
4544 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4546 RegionView* rv = rtv->view()->find_view (*i);
4559 /** Start with regions that are selected. Then add equivalent regions
4560 * on tracks in the same active edit-enabled route group as any of
4561 * the regions that we started with.
4565 Editor::get_regions_from_selection ()
4567 return get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
4570 /** Get regions using the following method:
4572 * Make an initial region list using the selected regions, unless
4573 * the edit point is `mouse' and the mouse is over an unselected
4574 * region. In this case, start with just that region.
4576 * Then, make an initial track list of the tracks that these
4577 * regions are on, and if the edit point is not `mouse', add the
4580 * Look at this track list and add any other tracks that are on the
4581 * same active edit-enabled route group as one of the initial tracks.
4583 * Finally take the initial region list and add any regions that are
4584 * under the edit point on one of the tracks on the track list to get
4585 * the returned region list.
4587 * The rationale here is that the mouse edit point is special in that
4588 * its position describes both a time and a track; the other edit
4589 * modes only describe a time. Hence if the edit point is `mouse' we
4590 * ignore selected tracks, as we assume the user means something by
4591 * pointing at a particular track. Also in this case we take note of
4592 * the region directly under the edit point, as there is always just one
4593 * (rather than possibly several with non-mouse edit points).
4597 Editor::get_regions_from_selection_and_edit_point ()
4599 RegionSelection regions;
4601 if (_edit_point == EditAtMouse && entered_regionview && !selection->regions.contains (entered_regionview)) {
4602 regions.add (entered_regionview);
4604 regions = selection->regions;
4607 TrackViewList tracks;
4609 if (_edit_point != EditAtMouse) {
4610 tracks = selection->tracks;
4613 /* Add any other tracks that have regions that are in the same
4614 edit-activated route group as one of our regions.
4616 for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) {
4618 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4620 if (g && g->is_active() && g->is_edit()) {
4621 tracks.add (axis_views_from_routes (g->route_list()));
4625 if (!tracks.empty()) {
4626 /* now find regions that are at the edit position on those tracks */
4627 framepos_t const where = get_preferred_edit_position ();
4628 get_regions_at (regions, where, tracks);
4634 /** Start with regions that are selected, or the entered regionview if none are selected.
4635 * Then add equivalent regions on tracks in the same active edit-enabled route group as any
4636 * of the regions that we started with.
4640 Editor::get_regions_from_selection_and_entered ()
4642 RegionSelection regions = selection->regions;
4644 if (regions.empty() && entered_regionview) {
4645 regions.add (entered_regionview);
4648 return get_equivalent_regions (regions, ARDOUR::Properties::edit.property_id);
4652 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4654 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4656 RouteTimeAxisView* tatv;
4658 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4660 boost::shared_ptr<Playlist> pl;
4661 vector<boost::shared_ptr<Region> > results;
4663 boost::shared_ptr<Track> tr;
4665 if ((tr = tatv->track()) == 0) {
4670 if ((pl = (tr->playlist())) != 0) {
4671 pl->get_region_list_equivalent_regions (region, results);
4674 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4675 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4676 regions.push_back (marv);
4685 Editor::show_rhythm_ferret ()
4687 if (rhythm_ferret == 0) {
4688 rhythm_ferret = new RhythmFerret(*this);
4691 rhythm_ferret->set_session (_session);
4692 rhythm_ferret->show ();
4693 rhythm_ferret->present ();
4697 Editor::first_idle ()
4699 MessageDialog* dialog = 0;
4701 if (track_views.size() > 1) {
4702 dialog = new MessageDialog (*this,
4703 string_compose (_("Please wait while %1 loads visual data"), PROGRAM_NAME),
4708 ARDOUR_UI::instance()->flush_pending ();
4711 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4715 // first idle adds route children (automation tracks), so we need to redisplay here
4716 _routes->redisplay ();
4724 Editor::_idle_resize (gpointer arg)
4726 return ((Editor*)arg)->idle_resize ();
4730 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4732 if (resize_idle_id < 0) {
4733 resize_idle_id = g_idle_add (_idle_resize, this);
4734 _pending_resize_amount = 0;
4737 /* make a note of the smallest resulting height, so that we can clamp the
4738 lower limit at TimeAxisView::hSmall */
4740 int32_t min_resulting = INT32_MAX;
4742 _pending_resize_amount += h;
4743 _pending_resize_view = view;
4745 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4747 if (selection->tracks.contains (_pending_resize_view)) {
4748 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4749 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4753 if (min_resulting < 0) {
4758 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4759 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4763 /** Handle pending resizing of tracks */
4765 Editor::idle_resize ()
4767 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4769 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4770 selection->tracks.contains (_pending_resize_view)) {
4772 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4773 if (*i != _pending_resize_view) {
4774 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4779 _pending_resize_amount = 0;
4781 _group_tabs->set_dirty ();
4782 resize_idle_id = -1;
4790 ENSURE_GUI_THREAD (*this, &Editor::located);
4792 playhead_cursor->set_position (_session->audible_frame ());
4793 if (_follow_playhead && !_pending_initial_locate) {
4794 reset_x_origin_to_follow_playhead ();
4797 _pending_locate_request = false;
4798 _pending_initial_locate = false;
4802 Editor::region_view_added (RegionView *)
4804 _summary->set_dirty ();
4808 Editor::region_view_removed ()
4810 _summary->set_dirty ();
4814 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4816 TrackViewList::const_iterator j = track_views.begin ();
4817 while (j != track_views.end()) {
4818 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4819 if (rtv && rtv->route() == r) {
4830 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4834 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4835 TimeAxisView* tv = axis_view_from_route (*i);
4846 Editor::handle_new_route (RouteList& routes)
4848 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4850 RouteTimeAxisView *rtv;
4851 list<RouteTimeAxisView*> new_views;
4853 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4854 boost::shared_ptr<Route> route = (*x);
4856 if (route->is_hidden() || route->is_monitor()) {
4860 DataType dt = route->input()->default_type();
4862 if (dt == ARDOUR::DataType::AUDIO) {
4863 rtv = new AudioTimeAxisView (*this, _session, *track_canvas);
4864 rtv->set_route (route);
4865 } else if (dt == ARDOUR::DataType::MIDI) {
4866 rtv = new MidiTimeAxisView (*this, _session, *track_canvas);
4867 rtv->set_route (route);
4869 throw unknown_type();
4872 new_views.push_back (rtv);
4873 track_views.push_back (rtv);
4875 rtv->effective_gain_display ();
4877 if (internal_editing()) {
4878 rtv->enter_internal_edit_mode ();
4880 rtv->leave_internal_edit_mode ();
4883 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4884 rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed));
4887 _routes->routes_added (new_views);
4888 _summary->routes_added (new_views);
4890 if (show_editor_mixer_when_tracks_arrive) {
4891 show_editor_mixer (true);
4894 editor_list_button.set_sensitive (true);
4898 Editor::timeaxisview_deleted (TimeAxisView *tv)
4900 if (_session && _session->deletion_in_progress()) {
4901 /* the situation is under control */
4905 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
4907 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
4909 _routes->route_removed (tv);
4911 if (tv == entered_track) {
4915 TimeAxisView::Children c = tv->get_child_list ();
4916 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
4917 if (entered_track == i->get()) {
4922 /* remove it from the list of track views */
4924 TrackViewList::iterator i;
4926 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
4927 i = track_views.erase (i);
4930 /* update whatever the current mixer strip is displaying, if revelant */
4932 boost::shared_ptr<Route> route;
4935 route = rtav->route ();
4938 if (current_mixer_strip && current_mixer_strip->route() == route) {
4940 TimeAxisView* next_tv;
4942 if (track_views.empty()) {
4944 } else if (i == track_views.end()) {
4945 next_tv = track_views.front();
4952 set_selected_mixer_strip (*next_tv);
4954 /* make the editor mixer strip go away setting the
4955 * button to inactive (which also unticks the menu option)
4958 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
4964 Editor::hide_track_in_display (TimeAxisView* tv, bool apply_to_selection)
4966 if (apply_to_selection) {
4967 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ) {
4969 TrackSelection::iterator j = i;
4972 hide_track_in_display (*i, false);
4977 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
4979 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
4980 // this will hide the mixer strip
4981 set_selected_mixer_strip (*tv);
4984 _routes->hide_track_in_display (*tv);
4989 Editor::sync_track_view_list_and_routes ()
4991 track_views = TrackViewList (_routes->views ());
4993 _summary->set_dirty ();
4994 _group_tabs->set_dirty ();
4996 return false; // do not call again (until needed)
5000 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
5002 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5007 /** Find a RouteTimeAxisView by the ID of its route */
5009 Editor::get_route_view_by_route_id (PBD::ID& id) const
5011 RouteTimeAxisView* v;
5013 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5014 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5015 if(v->route()->id() == id) {
5025 Editor::fit_route_group (RouteGroup *g)
5027 TrackViewList ts = axis_views_from_routes (g->route_list ());
5032 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5034 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5037 _session->cancel_audition ();
5041 if (_session->is_auditioning()) {
5042 _session->cancel_audition ();
5043 if (r == last_audition_region) {
5048 _session->audition_region (r);
5049 last_audition_region = r;
5054 Editor::hide_a_region (boost::shared_ptr<Region> r)
5056 r->set_hidden (true);
5060 Editor::show_a_region (boost::shared_ptr<Region> r)
5062 r->set_hidden (false);
5066 Editor::audition_region_from_region_list ()
5068 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5072 Editor::hide_region_from_region_list ()
5074 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5078 Editor::show_region_in_region_list ()
5080 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5084 Editor::step_edit_status_change (bool yn)
5087 start_step_editing ();
5089 stop_step_editing ();
5094 Editor::start_step_editing ()
5096 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5100 Editor::stop_step_editing ()
5102 step_edit_connection.disconnect ();
5106 Editor::check_step_edit ()
5108 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5109 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5111 mtv->check_step_edit ();
5115 return true; // do it again, till we stop
5119 Editor::scroll_press (Direction dir)
5121 ++_scroll_callbacks;
5123 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5124 /* delay the first auto-repeat */
5130 scroll_backward (1);
5138 scroll_tracks_up_line ();
5142 scroll_tracks_down_line ();
5146 /* do hacky auto-repeat */
5147 if (!_scroll_connection.connected ()) {
5149 _scroll_connection = Glib::signal_timeout().connect (
5150 sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), dir), 100
5153 _scroll_callbacks = 0;
5160 Editor::scroll_release ()
5162 _scroll_connection.disconnect ();
5165 /** Queue a change for the Editor viewport x origin to follow the playhead */
5167 Editor::reset_x_origin_to_follow_playhead ()
5169 framepos_t const frame = playhead_cursor->current_frame;
5171 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5173 if (_session->transport_speed() < 0) {
5175 if (frame > (current_page_frames() / 2)) {
5176 center_screen (frame-(current_page_frames()/2));
5178 center_screen (current_page_frames()/2);
5183 if (frame < leftmost_frame) {
5186 if (_session->transport_rolling()) {
5187 /* rolling; end up with the playhead at the right of the page */
5188 l = frame - current_page_frames ();
5190 /* not rolling: end up with the playhead 3/4 of the way along the page */
5191 l = frame - (3 * current_page_frames() / 4);
5198 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5201 if (_session->transport_rolling()) {
5202 /* rolling: end up with the playhead on the left of the page */
5203 center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5205 /* not rolling: end up with the playhead 1/4 of the way along the page */
5206 center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5214 Editor::super_rapid_screen_update ()
5216 if (!_session || !_session->engine().running()) {
5220 /* METERING / MIXER STRIPS */
5222 /* update track meters, if required */
5223 if (is_mapped() && meters_running) {
5224 RouteTimeAxisView* rtv;
5225 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5226 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5227 rtv->fast_update ();
5232 /* and any current mixer strip */
5233 if (current_mixer_strip) {
5234 current_mixer_strip->fast_update ();
5237 /* PLAYHEAD AND VIEWPORT */
5239 framepos_t const frame = _session->audible_frame();
5241 /* There are a few reasons why we might not update the playhead / viewport stuff:
5243 * 1. we don't update things when there's a pending locate request, otherwise
5244 * when the editor requests a locate there is a chance that this method
5245 * will move the playhead before the locate request is processed, causing
5247 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5248 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5251 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5253 last_update_frame = frame;
5255 if (!_dragging_playhead) {
5256 playhead_cursor->set_position (frame);
5259 if (!_stationary_playhead) {
5261 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5262 reset_x_origin_to_follow_playhead ();
5267 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5271 // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5272 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5273 if (target <= 0.0) {
5276 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5277 target = (target * 0.15) + (current * 0.85);
5283 set_horizontal_position (current);
5292 Editor::session_going_away ()
5294 _have_idled = false;
5296 _session_connections.drop_connections ();
5298 super_rapid_screen_update_connection.disconnect ();
5300 selection->clear ();
5301 cut_buffer->clear ();
5303 clicked_regionview = 0;
5304 clicked_axisview = 0;
5305 clicked_routeview = 0;
5306 clicked_crossfadeview = 0;
5307 entered_regionview = 0;
5309 last_update_frame = 0;
5312 playhead_cursor->canvas_item.hide ();
5314 /* rip everything out of the list displays */
5318 _route_groups->clear ();
5320 /* do this first so that deleting a track doesn't reset cms to null
5321 and thus cause a leak.
5324 if (current_mixer_strip) {
5325 if (current_mixer_strip->get_parent() != 0) {
5326 global_hpacker.remove (*current_mixer_strip);
5328 delete current_mixer_strip;
5329 current_mixer_strip = 0;
5332 /* delete all trackviews */
5334 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5337 track_views.clear ();
5339 zoom_range_clock->set_session (0);
5340 nudge_clock->set_session (0);
5342 editor_list_button.set_active(false);
5343 editor_list_button.set_sensitive(false);
5345 /* clear tempo/meter rulers */
5346 remove_metric_marks ();
5348 clear_marker_display ();
5350 delete current_bbt_points;
5351 current_bbt_points = 0;
5353 /* get rid of any existing editor mixer strip */
5355 WindowTitle title(Glib::get_application_name());
5356 title += _("Editor");
5358 set_title (title.get_string());
5360 SessionHandlePtr::session_going_away ();
5365 Editor::show_editor_list (bool yn)
5368 _the_notebook.show ();
5370 _the_notebook.hide ();
5375 Editor::change_region_layering_order ()
5377 framepos_t const position = get_preferred_edit_position ();
5379 if (!clicked_routeview) {
5380 if (layering_order_editor) {
5381 layering_order_editor->hide ();
5386 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
5392 boost::shared_ptr<Playlist> pl = track->playlist();
5398 if (layering_order_editor == 0) {
5399 layering_order_editor = new RegionLayeringOrderEditor(*this);
5402 layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position);
5403 layering_order_editor->maybe_present ();
5407 Editor::update_region_layering_order_editor ()
5409 if (layering_order_editor && layering_order_editor->is_visible ()) {
5410 change_region_layering_order ();
5415 Editor::setup_fade_images ()
5417 _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear")));
5418 _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut")));
5419 _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut")));
5420 _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut")));
5421 _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut")));
5423 _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear")));
5424 _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut")));
5425 _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut")));
5426 _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut")));
5427 _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut")));
5431 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
5433 Editor::action_menu_item (std::string const & name)
5435 Glib::RefPtr<Action> a = editor_actions->get_action (name);
5438 return *manage (a->create_menu_item ());
5442 Editor::resize_text_widgets ()
5444 set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_FUDGE+10, 15);
5445 set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_FUDGE+10, 15);
5446 set_size_request_to_display_given_text (snap_type_selector, snap_type_strings, COMBO_FUDGE+10, 15);
5447 set_size_request_to_display_given_text (snap_mode_selector, snap_mode_strings, COMBO_FUDGE+10, 15);
5448 set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_FUDGE+10, 15);
5452 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
5454 EventBox* b = manage (new EventBox);
5455 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
5456 Label* l = manage (new Label (name));
5460 _the_notebook.append_page (widget, *b);
5464 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
5466 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
5467 _the_notebook.set_current_page (_the_notebook.page_num (*page));
5470 if (ev->type == GDK_2BUTTON_PRESS) {
5472 /* double-click on a notebook tab shrinks or expands the notebook */
5474 if (_notebook_shrunk) {
5475 edit_pane.set_position (pre_maximal_horizontal_pane_position);
5476 _notebook_shrunk = false;
5478 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
5479 edit_pane.set_position (edit_pane.get_position() + page->get_width());
5480 _notebook_shrunk = true;
5488 Editor::popup_control_point_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
5490 using namespace Menu_Helpers;
5492 MenuList& items = _control_point_context_menu.items ();
5495 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun (*this, &Editor::edit_control_point), item)));
5496 items.push_back (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun (*this, &Editor::remove_control_point), item)));
5497 if (!can_remove_control_point (item)) {
5498 items.back().set_sensitive (false);
5501 _control_point_context_menu.popup (event->button.button, event->button.time);