2 Copyright (C) 2000-2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /* Note: public Editor methods are documented in public_editor.h */
30 #include "ardour_ui.h"
32 * ardour_ui.h include was moved to the top of the list
33 * due to a conflicting definition of 'Style' between
34 * Apple's MacTypes.h and BarController.
37 #include <boost/none.hpp>
39 #include <sigc++/bind.h>
41 #include "pbd/convert.h"
42 #include "pbd/error.h"
43 #include "pbd/enumwriter.h"
44 #include "pbd/memento_command.h"
45 #include "pbd/unknown_type.h"
47 #include <glibmm/miscutils.h>
48 #include <gtkmm/image.h>
49 #include <gdkmm/color.h>
50 #include <gdkmm/bitmap.h>
52 #include "gtkmm2ext/bindings.h"
53 #include "gtkmm2ext/grouped_buttons.h"
54 #include "gtkmm2ext/gtk_ui.h"
55 #include "gtkmm2ext/tearoff.h"
56 #include "gtkmm2ext/utils.h"
57 #include "gtkmm2ext/window_title.h"
58 #include "gtkmm2ext/choice.h"
59 #include "gtkmm2ext/cell_renderer_pixbuf_toggle.h"
61 #include "ardour/audio_track.h"
62 #include "ardour/audioplaylist.h"
63 #include "ardour/audioregion.h"
64 #include "ardour/location.h"
65 #include "ardour/midi_region.h"
66 #include "ardour/plugin_manager.h"
67 #include "ardour/profile.h"
68 #include "ardour/route_group.h"
69 #include "ardour/session_directory.h"
70 #include "ardour/session_route.h"
71 #include "ardour/session_state_utils.h"
72 #include "ardour/tempo.h"
73 #include "ardour/utils.h"
74 #include "ardour/session_playlists.h"
75 #include "ardour/audioengine.h"
77 #include "control_protocol/control_protocol.h"
81 #include "analysis_window.h"
82 #include "audio_clock.h"
83 #include "audio_region_view.h"
84 #include "audio_streamview.h"
85 #include "audio_time_axis.h"
86 #include "automation_time_axis.h"
87 #include "bundle_manager.h"
88 #include "canvas-noevent-text.h"
89 #include "canvas_impl.h"
90 #include "crossfade_edit.h"
91 #include "crossfade_view.h"
95 #include "editor_cursors.h"
96 #include "editor_drag.h"
97 #include "editor_group_tabs.h"
98 #include "editor_locations.h"
99 #include "editor_regions.h"
100 #include "editor_route_groups.h"
101 #include "editor_routes.h"
102 #include "editor_snapshots.h"
103 #include "editor_summary.h"
104 #include "global_port_matrix.h"
105 #include "gui_object.h"
106 #include "gui_thread.h"
107 #include "keyboard.h"
109 #include "midi_time_axis.h"
110 #include "mixer_strip.h"
111 #include "mixer_ui.h"
112 #include "mouse_cursors.h"
113 #include "playlist_selector.h"
114 #include "public_editor.h"
115 #include "region_layering_order_editor.h"
116 #include "rgb_macros.h"
117 #include "rhythm_ferret.h"
118 #include "selection.h"
120 #include "simpleline.h"
121 #include "tempo_lines.h"
122 #include "time_axis_view.h"
128 #include "imageframe_socket_handler.h"
132 using namespace ARDOUR;
135 using namespace Glib;
136 using namespace Gtkmm2ext;
137 using namespace Editing;
139 using PBD::internationalize;
141 using Gtkmm2ext::Keyboard;
143 const double Editor::timebar_height = 15.0;
145 static const gchar *_snap_type_strings[] = {
147 N_("Timecode Frames"),
148 N_("Timecode Seconds"),
149 N_("Timecode Minutes"),
177 static const gchar *_snap_mode_strings[] = {
184 static const gchar *_edit_point_strings[] = {
191 static const gchar *_zoom_focus_strings[] = {
201 #ifdef USE_RUBBERBAND
202 static const gchar *_rb_opt_strings[] = {
205 N_("Balanced multitimbral mixture"),
206 N_("Unpitched percussion with stable notes"),
207 N_("Crisp monophonic instrumental"),
208 N_("Unpitched solo percussion"),
209 N_("Resample without preserving pitch"),
215 show_me_the_size (Requisition* r, const char* what)
217 cerr << "size of " << what << " = " << r->width << " x " << r->height << endl;
222 pane_size_watcher (Paned* pane)
224 /* if the handle of a pane vanishes into (at least) the tabs of a notebook,
225 it is no longer accessible. so stop that. this doesn't happen on X11,
226 just the quartz backend.
231 int max_width_of_lhs = GTK_WIDGET(pane->gobj())->allocation.width - 25;
233 gint pos = pane->get_position ();
235 if (pos > max_width_of_lhs) {
236 pane->set_position (max_width_of_lhs);
242 : _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
244 /* time display buttons */
245 , minsec_label (_("Mins:Secs"))
246 , bbt_label (_("Bars:Beats"))
247 , timecode_label (_("Timecode"))
248 , samples_label (_("Samples"))
249 , tempo_label (_("Tempo"))
250 , meter_label (_("Meter"))
251 , mark_label (_("Location Markers"))
252 , range_mark_label (_("Range Markers"))
253 , transport_mark_label (_("Loop/Punch Ranges"))
254 , cd_mark_label (_("CD Markers"))
255 , edit_packer (4, 4, true)
257 /* the values here don't matter: layout widgets
258 reset them as needed.
261 , vertical_adjustment (0.0, 0.0, 10.0, 400.0)
263 /* tool bar related */
265 , zoom_range_clock (new AudioClock (X_("zoomrange"), false, X_("zoom range"), true, false, true))
267 , toolbar_selection_clock_table (2,3)
269 , automation_mode_button (_("mode"))
271 , _toolbar_viewport (*manage (new Gtk::Adjustment (0, 0, 1e10)), *manage (new Gtk::Adjustment (0, 0, 1e10)))
274 , image_socket_listener(0)
279 , nudge_clock (new AudioClock (X_("nudge"), false, X_("nudge"), true, false, true))
280 , meters_running(false)
281 , _pending_locate_request (false)
282 , _pending_initial_locate (false)
283 , _last_cut_copy_source_track (0)
285 , _region_selection_change_updates_region_list (true)
286 , _following_mixer_selection (false)
290 /* we are a singleton */
292 PublicEditor::_instance = this;
296 selection = new Selection (this);
297 cut_buffer = new Selection (this);
299 clicked_regionview = 0;
300 clicked_axisview = 0;
301 clicked_routeview = 0;
302 clicked_crossfadeview = 0;
303 clicked_control_point = 0;
304 last_update_frame = 0;
305 pre_press_cursor = 0;
306 _drags = new DragManager (this);
307 current_mixer_strip = 0;
308 current_bbt_points = 0;
311 snap_type_strings = I18N (_snap_type_strings);
312 snap_mode_strings = I18N (_snap_mode_strings);
313 zoom_focus_strings = I18N (_zoom_focus_strings);
314 edit_point_strings = I18N (_edit_point_strings);
315 #ifdef USE_RUBBERBAND
316 rb_opt_strings = I18N (_rb_opt_strings);
320 snap_threshold = 5.0;
321 bbt_beat_subdivision = 4;
324 last_autoscroll_x = 0;
325 last_autoscroll_y = 0;
326 autoscroll_active = false;
327 autoscroll_timeout_tag = -1;
332 current_interthread_info = 0;
333 _show_measures = true;
334 show_gain_after_trim = false;
335 last_item_entered = 0;
337 have_pending_keyboard_selection = false;
338 _follow_playhead = true;
339 _stationary_playhead = false;
340 _xfade_visibility = true;
341 editor_ruler_menu = 0;
342 no_ruler_shown_update = false;
344 range_marker_menu = 0;
345 marker_menu_item = 0;
346 tempo_or_meter_marker_menu = 0;
347 transport_marker_menu = 0;
348 new_transport_marker_menu = 0;
349 editor_mixer_strip_width = Wide;
350 show_editor_mixer_when_tracks_arrive = false;
351 region_edit_menu_split_multichannel_item = 0;
352 region_edit_menu_split_item = 0;
355 current_stepping_trackview = 0;
357 entered_regionview = 0;
359 clear_entered_track = false;
362 button_release_can_deselect = true;
363 _dragging_playhead = false;
364 _dragging_edit_point = false;
365 select_new_marker = false;
367 layering_order_editor = 0;
368 no_save_visual = false;
371 scrubbing_direction = 0;
375 location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get();
376 location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get();
377 location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get();
378 location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get();
379 location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get();
381 _edit_point = EditAtMouse;
382 _internal_editing = false;
383 current_canvas_cursor = 0;
385 frames_per_unit = 2048; /* too early to use reset_zoom () */
387 _scroll_callbacks = 0;
389 zoom_focus = ZoomFocusLeft;
390 set_zoom_focus (ZoomFocusLeft);
391 zoom_range_clock->ValueChanged.connect (sigc::mem_fun(*this, &Editor::zoom_adjustment_changed));
393 bbt_label.set_name ("EditorTimeButton");
394 bbt_label.set_size_request (-1, (int)timebar_height);
395 bbt_label.set_alignment (1.0, 0.5);
396 bbt_label.set_padding (5,0);
398 bbt_label.set_no_show_all();
399 minsec_label.set_name ("EditorTimeButton");
400 minsec_label.set_size_request (-1, (int)timebar_height);
401 minsec_label.set_alignment (1.0, 0.5);
402 minsec_label.set_padding (5,0);
403 minsec_label.hide ();
404 minsec_label.set_no_show_all();
405 timecode_label.set_name ("EditorTimeButton");
406 timecode_label.set_size_request (-1, (int)timebar_height);
407 timecode_label.set_alignment (1.0, 0.5);
408 timecode_label.set_padding (5,0);
409 timecode_label.hide ();
410 timecode_label.set_no_show_all();
411 samples_label.set_name ("EditorTimeButton");
412 samples_label.set_size_request (-1, (int)timebar_height);
413 samples_label.set_alignment (1.0, 0.5);
414 samples_label.set_padding (5,0);
415 samples_label.hide ();
416 samples_label.set_no_show_all();
418 tempo_label.set_name ("EditorTimeButton");
419 tempo_label.set_size_request (-1, (int)timebar_height);
420 tempo_label.set_alignment (1.0, 0.5);
421 tempo_label.set_padding (5,0);
423 tempo_label.set_no_show_all();
425 meter_label.set_name ("EditorTimeButton");
426 meter_label.set_size_request (-1, (int)timebar_height);
427 meter_label.set_alignment (1.0, 0.5);
428 meter_label.set_padding (5,0);
430 meter_label.set_no_show_all();
432 mark_label.set_name ("EditorTimeButton");
433 mark_label.set_size_request (-1, (int)timebar_height);
434 mark_label.set_alignment (1.0, 0.5);
435 mark_label.set_padding (5,0);
437 mark_label.set_no_show_all();
439 cd_mark_label.set_name ("EditorTimeButton");
440 cd_mark_label.set_size_request (-1, (int)timebar_height);
441 cd_mark_label.set_alignment (1.0, 0.5);
442 cd_mark_label.set_padding (5,0);
443 cd_mark_label.hide();
444 cd_mark_label.set_no_show_all();
446 range_mark_label.set_name ("EditorTimeButton");
447 range_mark_label.set_size_request (-1, (int)timebar_height);
448 range_mark_label.set_alignment (1.0, 0.5);
449 range_mark_label.set_padding (5,0);
450 range_mark_label.hide();
451 range_mark_label.set_no_show_all();
453 transport_mark_label.set_name ("EditorTimeButton");
454 transport_mark_label.set_size_request (-1, (int)timebar_height);
455 transport_mark_label.set_alignment (1.0, 0.5);
456 transport_mark_label.set_padding (5,0);
457 transport_mark_label.hide();
458 transport_mark_label.set_no_show_all();
460 initialize_rulers ();
461 initialize_canvas ();
463 _summary = new EditorSummary (this);
465 selection->TimeChanged.connect (sigc::mem_fun(*this, &Editor::time_selection_changed));
466 selection->TracksChanged.connect (sigc::mem_fun(*this, &Editor::track_selection_changed));
468 editor_regions_selection_changed_connection = selection->RegionsChanged.connect (sigc::mem_fun(*this, &Editor::region_selection_changed));
470 selection->PointsChanged.connect (sigc::mem_fun(*this, &Editor::point_selection_changed));
471 selection->MarkersChanged.connect (sigc::mem_fun(*this, &Editor::marker_selection_changed));
473 edit_controls_vbox.set_spacing (0);
474 vertical_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &Editor::tie_vertical_scrolling), true);
475 track_canvas->signal_map_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_map_handler));
477 HBox* h = manage (new HBox);
478 _group_tabs = new EditorGroupTabs (this);
479 h->pack_start (*_group_tabs, PACK_SHRINK);
480 h->pack_start (edit_controls_vbox);
481 controls_layout.add (*h);
483 controls_layout.set_name ("EditControlsBase");
484 controls_layout.add_events (Gdk::SCROLL_MASK);
485 controls_layout.signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::control_layout_scroll), false);
487 controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
488 controls_layout.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_release));
490 _cursors = new MouseCursors;
492 ArdourCanvas::Canvas* time_pad = manage(new ArdourCanvas::Canvas());
493 ArdourCanvas::SimpleLine* pad_line_1 = manage(new ArdourCanvas::SimpleLine(*time_pad->root(),
494 0.0, 1.0, 100.0, 1.0));
496 pad_line_1->property_color_rgba() = 0xFF0000FF;
501 time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2);
502 time_canvas_vbox.set_size_request (-1, -1);
504 ruler_label_event_box.add (ruler_label_vbox);
505 ruler_label_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
506 ruler_label_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
508 time_button_event_box.add (time_button_vbox);
509 time_button_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
510 time_button_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
512 /* these enable us to have a dedicated window (for cursor setting, etc.)
513 for the canvas areas.
516 track_canvas_event_box.add (*track_canvas);
518 time_canvas_event_box.add (time_canvas_vbox);
519 time_canvas_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
521 edit_packer.set_col_spacings (0);
522 edit_packer.set_row_spacings (0);
523 edit_packer.set_homogeneous (false);
524 edit_packer.set_border_width (0);
525 edit_packer.set_name ("EditorWindow");
527 /* labels for the rulers */
528 edit_packer.attach (ruler_label_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0);
529 /* labels for the marker "tracks" */
530 edit_packer.attach (time_button_event_box, 1, 2, 1, 2, FILL, SHRINK, 0, 0);
532 edit_packer.attach (time_canvas_event_box, 2, 3, 0, 1, FILL|EXPAND, FILL, 0, 0);
534 edit_packer.attach (controls_layout, 0, 2, 2, 3, FILL, FILL|EXPAND, 0, 0);
536 edit_packer.attach (track_canvas_event_box, 2, 3, 1, 3, FILL|EXPAND, FILL|EXPAND, 0, 0);
538 bottom_hbox.set_border_width (2);
539 bottom_hbox.set_spacing (3);
541 _route_groups = new EditorRouteGroups (this);
542 _routes = new EditorRoutes (this);
543 _regions = new EditorRegions (this);
544 _snapshots = new EditorSnapshots (this);
545 _locations = new EditorLocations (this);
547 add_notebook_page (_("Regions"), _regions->widget ());
548 add_notebook_page (_("Tracks & Busses"), _routes->widget ());
549 add_notebook_page (_("Snapshots"), _snapshots->widget ());
550 add_notebook_page (_("Route Groups"), _route_groups->widget ());
551 add_notebook_page (_("Ranges & Marks"), _locations->widget ());
553 _the_notebook.set_show_tabs (true);
554 _the_notebook.set_scrollable (true);
555 _the_notebook.popup_disable ();
556 _the_notebook.set_tab_pos (Gtk::POS_RIGHT);
557 _the_notebook.show_all ();
559 post_maximal_editor_width = 0;
560 post_maximal_horizontal_pane_position = 0;
561 post_maximal_editor_height = 0;
562 post_maximal_vertical_pane_position = 0;
563 _notebook_shrunk = false;
565 editor_summary_pane.pack1(edit_packer);
567 Button* summary_arrows_left_left = manage (new Button);
568 summary_arrows_left_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
569 summary_arrows_left_left->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), LEFT)));
570 summary_arrows_left_left->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
572 Button* summary_arrows_left_right = manage (new Button);
573 summary_arrows_left_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
574 summary_arrows_left_right->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), RIGHT)));
575 summary_arrows_left_right->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
577 VBox* summary_arrows_left = manage (new VBox);
578 summary_arrows_left->pack_start (*summary_arrows_left_left);
579 summary_arrows_left->pack_start (*summary_arrows_left_right);
581 Button* summary_arrows_right_up = manage (new Button);
582 summary_arrows_right_up->add (*manage (new Arrow (ARROW_UP, SHADOW_NONE)));
583 summary_arrows_right_up->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), UP)));
584 summary_arrows_right_up->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
586 Button* summary_arrows_right_down = manage (new Button);
587 summary_arrows_right_down->add (*manage (new Arrow (ARROW_DOWN, SHADOW_NONE)));
588 summary_arrows_right_down->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), DOWN)));
589 summary_arrows_right_down->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
591 VBox* summary_arrows_right = manage (new VBox);
592 summary_arrows_right->pack_start (*summary_arrows_right_up);
593 summary_arrows_right->pack_start (*summary_arrows_right_down);
595 Frame* summary_frame = manage (new Frame);
596 summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN);
598 summary_frame->add (*_summary);
599 summary_frame->show ();
601 _summary_hbox.pack_start (*summary_arrows_left, false, false);
602 _summary_hbox.pack_start (*summary_frame, true, true);
603 _summary_hbox.pack_start (*summary_arrows_right, false, false);
605 editor_summary_pane.pack2 (_summary_hbox);
607 edit_pane.pack1 (editor_summary_pane, true, true);
608 edit_pane.pack2 (_the_notebook, false, true);
610 editor_summary_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun (*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&editor_summary_pane)));
612 /* XXX: editor_summary_pane might need similar special OS X treatment to the edit_pane */
614 edit_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&edit_pane)));
616 Glib::PropertyProxy<int> proxy = edit_pane.property_position();
617 proxy.signal_changed().connect (bind (sigc::ptr_fun (pane_size_watcher), static_cast<Paned*> (&edit_pane)));
619 top_hbox.pack_start (toolbar_frame);
621 HBox *hbox = manage (new HBox);
622 hbox->pack_start (edit_pane, true, true);
624 global_vpacker.pack_start (top_hbox, false, false);
625 global_vpacker.pack_start (*hbox, true, true);
627 global_hpacker.pack_start (global_vpacker, true, true);
629 set_name ("EditorWindow");
630 add_accel_group (ActionManager::ui_manager->get_accel_group());
632 status_bar_hpacker.show ();
634 vpacker.pack_end (status_bar_hpacker, false, false);
635 vpacker.pack_end (global_hpacker, true, true);
637 /* register actions now so that set_state() can find them and set toggles/checks etc */
642 setup_midi_toolbar ();
644 _snap_type = SnapToBeat;
645 set_snap_to (_snap_type);
646 _snap_mode = SnapOff;
647 set_snap_mode (_snap_mode);
648 set_mouse_mode (MouseObject, true);
649 pre_internal_mouse_mode = MouseObject;
650 set_edit_point_preference (EditAtMouse, true);
652 _playlist_selector = new PlaylistSelector();
653 _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
655 RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), ui_bind (&Editor::catch_vanishing_regionview, this, _1), gui_context());
659 nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
660 nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
662 nudge_forward_button.set_name ("TransportButton");
663 nudge_backward_button.set_name ("TransportButton");
665 fade_context_menu.set_name ("ArdourContextMenu");
667 /* icons, titles, WM stuff */
669 list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
670 Glib::RefPtr<Gdk::Pixbuf> icon;
672 if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
673 window_icons.push_back (icon);
675 if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
676 window_icons.push_back (icon);
678 if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
679 window_icons.push_back (icon);
681 if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
682 window_icons.push_back (icon);
684 if (!window_icons.empty()) {
685 // set_icon_list (window_icons);
686 set_default_icon_list (window_icons);
689 WindowTitle title(Glib::get_application_name());
690 title += _("Editor");
691 set_title (title.get_string());
692 set_wmclass (X_("ardour_editor"), PROGRAM_NAME);
695 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
697 signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
698 signal_delete_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
700 /* allow external control surfaces/protocols to do various things */
702 ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context());
703 ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context());
704 ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context());
705 ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), ui_bind (&Editor::control_scroll, this, _1), gui_context());
706 ControlProtocol::SelectByRID.connect (*this, invalidator (*this), ui_bind (&Editor::control_select, this, _1), gui_context());
707 BasicUI::AccessAction.connect (*this, invalidator (*this), ui_bind (&Editor::access_action, this, _1, _2), gui_context());
709 /* problematic: has to return a value and thus cannot be x-thread */
711 Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
713 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
715 TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
717 _ignore_region_action = false;
718 _last_region_menu_was_main = false;
719 _popup_region_menu_item = 0;
721 _show_marker_lines = false;
722 _over_region_trim_target = false;
724 /* Button bindings */
726 button_bindings = new Bindings;
728 XMLNode* node = button_settings();
730 for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) {
731 button_bindings->load (**i);
738 setup_fade_images ();
744 if(image_socket_listener) {
745 if(image_socket_listener->is_connected())
747 image_socket_listener->close_connection() ;
750 delete image_socket_listener ;
751 image_socket_listener = 0 ;
755 delete button_bindings;
757 delete _route_groups;
763 Editor::button_settings () const
765 XMLNode* settings = ARDOUR_UI::instance()->editor_settings();
766 XMLNode* node = find_named_node (*settings, X_("Buttons"));
769 cerr << "new empty Button node\n";
770 node = new XMLNode (X_("Buttons"));
777 Editor::add_toplevel_controls (Container& cont)
779 vpacker.pack_start (cont, false, false);
784 Editor::catch_vanishing_regionview (RegionView *rv)
786 /* note: the selection will take care of the vanishing
787 audioregionview by itself.
790 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
794 if (clicked_regionview == rv) {
795 clicked_regionview = 0;
798 if (entered_regionview == rv) {
799 set_entered_regionview (0);
802 if (!_all_region_actions_sensitized) {
803 sensitize_all_region_actions (true);
806 _over_region_trim_target = false;
810 Editor::set_entered_regionview (RegionView* rv)
812 if (rv == entered_regionview) {
816 if (entered_regionview) {
817 entered_regionview->exited ();
820 if ((entered_regionview = rv) != 0) {
821 entered_regionview->entered (internal_editing ());
824 if (!_all_region_actions_sensitized && _last_region_menu_was_main) {
825 /* This RegionView entry might have changed what region actions
826 are allowed, so sensitize them all in case a key is pressed.
828 sensitize_all_region_actions (true);
833 Editor::set_entered_track (TimeAxisView* tav)
836 entered_track->exited ();
839 if ((entered_track = tav) != 0) {
840 entered_track->entered ();
845 Editor::show_window ()
847 if (!is_visible ()) {
850 /* XXX: this is a bit unfortunate; it would probably
851 be nicer if we could just call show () above rather
852 than needing the show_all ()
855 /* re-hide stuff if necessary */
856 editor_list_button_toggled ();
857 parameter_changed ("show-summary");
858 parameter_changed ("show-group-tabs");
859 parameter_changed ("show-zoom-tools");
861 /* now reset all audio_time_axis heights, because widgets might need
867 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
868 tv = (static_cast<TimeAxisView*>(*i));
872 if (current_mixer_strip) {
873 current_mixer_strip->hide_things ();
874 current_mixer_strip->parameter_changed ("mixer-strip-visibility");
882 Editor::instant_save ()
884 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
889 _session->add_instant_xml(get_state());
891 Config->add_instant_xml(get_state());
896 Editor::zoom_adjustment_changed ()
902 double fpu = zoom_range_clock->current_duration() / _canvas_width;
906 zoom_range_clock->set ((framepos_t) floor (fpu * _canvas_width));
907 } else if (fpu > _session->current_end_frame() / _canvas_width) {
908 fpu = _session->current_end_frame() / _canvas_width;
909 zoom_range_clock->set ((framepos_t) floor (fpu * _canvas_width));
916 Editor::control_select (uint32_t rid)
918 /* handles the (static) signal from the ControlProtocol class that
919 * requests setting the selected track to a given RID
926 boost::shared_ptr<Route> r = _session->route_by_remote_id (rid);
932 TimeAxisView* tav = axis_view_from_route (r);
935 selection->set (tav);
937 selection->clear_tracks ();
942 Editor::control_scroll (float fraction)
944 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
950 double step = fraction * current_page_frames();
953 _control_scroll_target is an optional<T>
955 it acts like a pointer to an framepos_t, with
956 a operator conversion to boolean to check
957 that it has a value could possibly use
958 playhead_cursor->current_frame to store the
959 value and a boolean in the class to know
960 when it's out of date
963 if (!_control_scroll_target) {
964 _control_scroll_target = _session->transport_frame();
965 _dragging_playhead = true;
968 if ((fraction < 0.0f) && (*_control_scroll_target < (framepos_t) fabs(step))) {
969 *_control_scroll_target = 0;
970 } else if ((fraction > 0.0f) && (max_framepos - *_control_scroll_target < step)) {
971 *_control_scroll_target = max_framepos - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
973 *_control_scroll_target += (framepos_t) floor (step);
976 /* move visuals, we'll catch up with it later */
978 playhead_cursor->set_position (*_control_scroll_target);
979 UpdateAllTransportClocks (*_control_scroll_target);
981 if (*_control_scroll_target > (current_page_frames() / 2)) {
982 /* try to center PH in window */
983 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
989 Now we do a timeout to actually bring the session to the right place
990 according to the playhead. This is to avoid reading disk buffers on every
991 call to control_scroll, which is driven by ScrollTimeline and therefore
992 probably by a control surface wheel which can generate lots of events.
994 /* cancel the existing timeout */
996 control_scroll_connection.disconnect ();
998 /* add the next timeout */
1000 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
1004 Editor::deferred_control_scroll (framepos_t /*target*/)
1006 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
1007 // reset for next stream
1008 _control_scroll_target = boost::none;
1009 _dragging_playhead = false;
1014 Editor::access_action (std::string action_group, std::string action_item)
1020 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
1023 act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
1031 Editor::on_realize ()
1033 Window::on_realize ();
1038 Editor::map_position_change (framepos_t frame)
1040 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, frame)
1042 if (_session == 0) {
1046 if (_follow_playhead) {
1047 center_screen (frame);
1050 playhead_cursor->set_position (frame);
1054 Editor::center_screen (framepos_t frame)
1056 double page = _canvas_width * frames_per_unit;
1058 /* if we're off the page, then scroll.
1061 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
1062 center_screen_internal (frame, page);
1067 Editor::center_screen_internal (framepos_t frame, float page)
1072 frame -= (framepos_t) page;
1077 reset_x_origin (frame);
1082 Editor::update_title ()
1084 ENSURE_GUI_THREAD (*this, &Editor::update_title)
1087 bool dirty = _session->dirty();
1089 string session_name;
1091 if (_session->snap_name() != _session->name()) {
1092 session_name = _session->snap_name();
1094 session_name = _session->name();
1098 session_name = "*" + session_name;
1101 WindowTitle title(session_name);
1102 title += Glib::get_application_name();
1103 set_title (title.get_string());
1108 Editor::set_session (Session *t)
1110 SessionHandlePtr::set_session (t);
1116 zoom_range_clock->set_session (_session);
1117 _playlist_selector->set_session (_session);
1118 nudge_clock->set_session (_session);
1119 _summary->set_session (_session);
1120 _group_tabs->set_session (_session);
1121 _route_groups->set_session (_session);
1122 _regions->set_session (_session);
1123 _snapshots->set_session (_session);
1124 _routes->set_session (_session);
1125 _locations->set_session (_session);
1127 if (rhythm_ferret) {
1128 rhythm_ferret->set_session (_session);
1131 if (analysis_window) {
1132 analysis_window->set_session (_session);
1136 sfbrowser->set_session (_session);
1139 compute_fixed_ruler_scale ();
1141 /* Make sure we have auto loop and auto punch ranges */
1143 Location* loc = _session->locations()->auto_loop_location();
1145 loc = new Location (*_session, 0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1147 if (loc->start() == loc->end()) {
1148 loc->set_end (loc->start() + 1);
1151 _session->locations()->add (loc, false);
1152 _session->set_auto_loop_location (loc);
1155 loc->set_name (_("Loop"));
1158 loc = _session->locations()->auto_punch_location();
1161 loc = new Location (*_session, 0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1163 if (loc->start() == loc->end()) {
1164 loc->set_end (loc->start() + 1);
1167 _session->locations()->add (loc, false);
1168 _session->set_auto_punch_location (loc);
1171 loc->set_name (_("Punch"));
1174 refresh_location_display ();
1176 /* This must happen after refresh_location_display(), as (amongst other things) we restore
1177 the selected Marker; this needs the LocationMarker list to be available.
1179 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1180 set_state (*node, Stateful::loading_state_version);
1182 /* catch up with the playhead */
1184 _session->request_locate (playhead_cursor->current_frame);
1185 _pending_initial_locate = true;
1189 /* These signals can all be emitted by a non-GUI thread. Therefore the
1190 handlers for them must not attempt to directly interact with the GUI,
1191 but use Gtkmm2ext::UI::instance()->call_slot();
1194 _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), ui_bind(&Editor::step_edit_status_change, this, _1), gui_context());
1195 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1196 _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context());
1197 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context());
1198 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1199 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context());
1200 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1201 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
1202 _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Editor::session_state_saved, this, _1), gui_context());
1203 _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context());
1204 _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&Editor::location_gone, this, _1), gui_context());
1205 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1206 _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display, this), gui_context());
1207 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1209 if (Profile->get_sae()) {
1210 Timecode::BBT_Time bbt;
1214 framepos_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1);
1215 nudge_clock->set_mode(AudioClock::BBT);
1216 nudge_clock->set (pos, true);
1219 nudge_clock->set_mode (AudioClock::Timecode);
1220 nudge_clock->set (_session->frame_rate() * 5, true);
1223 playhead_cursor->canvas_item.show ();
1225 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1226 Config->map_parameters (pc);
1227 _session->config.map_parameters (pc);
1229 restore_ruler_visibility ();
1230 //tempo_map_changed (PropertyChange (0));
1231 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1233 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1234 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1237 super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
1238 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1241 switch (_snap_type) {
1242 case SnapToRegionStart:
1243 case SnapToRegionEnd:
1244 case SnapToRegionSync:
1245 case SnapToRegionBoundary:
1246 build_region_boundary_cache ();
1253 /* register for undo history */
1254 _session->register_with_memento_command_factory(id(), this);
1256 ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated));
1258 start_updating_meters ();
1262 Editor::action_pre_activated (Glib::RefPtr<Action> const & a)
1264 if (a->get_name() == "RegionMenu") {
1265 /* When the main menu's region menu is opened, we setup the actions so that they look right
1266 in the menu. I can't find a way of getting a signal when this menu is subsequently closed,
1267 so we resensitize all region actions when the entered regionview or the region selection
1268 changes. HOWEVER we can't always resensitize on entered_regionview change because that
1269 happens after the region context menu is opened. So we set a flag here, too.
1273 sensitize_the_right_region_actions ();
1274 _last_region_menu_was_main = true;
1278 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1280 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1282 using namespace Menu_Helpers;
1283 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1286 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1290 MenuList& items (fade_context_menu.items());
1294 switch (item_type) {
1296 case FadeInHandleItem:
1297 if (arv->audio_region()->fade_in_active()) {
1298 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1300 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1303 items.push_back (SeparatorElem());
1305 if (Profile->get_sae()) {
1307 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)));
1308 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)));
1315 *_fade_in_images[FadeLinear],
1316 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)
1320 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1325 *_fade_in_images[FadeFast],
1326 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)
1329 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1334 *_fade_in_images[FadeLogB],
1335 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogB)
1338 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1343 *_fade_in_images[FadeLogA],
1344 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogA)
1347 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1352 *_fade_in_images[FadeSlow],
1353 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow)
1356 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1362 case FadeOutHandleItem:
1363 if (arv->audio_region()->fade_out_active()) {
1364 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1366 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1369 items.push_back (SeparatorElem());
1371 if (Profile->get_sae()) {
1372 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)));
1373 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)));
1379 *_fade_out_images[FadeLinear],
1380 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)
1384 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1389 *_fade_out_images[FadeFast],
1390 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)
1393 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1398 *_fade_out_images[FadeLogB],
1399 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogA)
1402 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1407 *_fade_out_images[FadeLogA],
1408 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogB)
1411 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1416 *_fade_out_images[FadeSlow],
1417 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast)
1420 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1426 fatal << _("programming error: ")
1427 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1432 fade_context_menu.popup (button, time);
1436 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1438 using namespace Menu_Helpers;
1439 Menu* (Editor::*build_menu_function)();
1442 switch (item_type) {
1444 case RegionViewName:
1445 case RegionViewNameHighlight:
1446 case LeftFrameHandle:
1447 case RightFrameHandle:
1448 if (with_selection) {
1449 build_menu_function = &Editor::build_track_selection_context_menu;
1451 build_menu_function = &Editor::build_track_region_context_menu;
1456 if (with_selection) {
1457 build_menu_function = &Editor::build_track_selection_context_menu;
1459 build_menu_function = &Editor::build_track_context_menu;
1463 case CrossfadeViewItem:
1464 build_menu_function = &Editor::build_track_crossfade_context_menu;
1468 if (clicked_routeview->track()) {
1469 build_menu_function = &Editor::build_track_context_menu;
1471 build_menu_function = &Editor::build_track_bus_context_menu;
1476 /* probably shouldn't happen but if it does, we don't care */
1480 menu = (this->*build_menu_function)();
1481 menu->set_name ("ArdourContextMenu");
1483 /* now handle specific situations */
1485 switch (item_type) {
1487 case RegionViewName:
1488 case RegionViewNameHighlight:
1489 case LeftFrameHandle:
1490 case RightFrameHandle:
1491 if (!with_selection) {
1492 if (region_edit_menu_split_item) {
1493 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1494 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1496 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1499 if (region_edit_menu_split_multichannel_item) {
1500 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1501 region_edit_menu_split_multichannel_item->set_sensitive (true);
1503 region_edit_menu_split_multichannel_item->set_sensitive (false);
1512 case CrossfadeViewItem:
1519 /* probably shouldn't happen but if it does, we don't care */
1523 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1525 /* Bounce to disk */
1527 using namespace Menu_Helpers;
1528 MenuList& edit_items = menu->items();
1530 edit_items.push_back (SeparatorElem());
1532 switch (clicked_routeview->audio_track()->freeze_state()) {
1533 case AudioTrack::NoFreeze:
1534 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1537 case AudioTrack::Frozen:
1538 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1541 case AudioTrack::UnFrozen:
1542 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1548 if (item_type == StreamItem && clicked_routeview) {
1549 clicked_routeview->build_underlay_menu(menu);
1552 /* When the region menu is opened, we setup the actions so that they look right
1555 sensitize_the_right_region_actions ();
1556 _last_region_menu_was_main = false;
1558 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1559 menu->popup (button, time);
1563 Editor::build_track_context_menu ()
1565 using namespace Menu_Helpers;
1567 MenuList& edit_items = track_context_menu.items();
1570 add_dstream_context_items (edit_items);
1571 return &track_context_menu;
1575 Editor::build_track_bus_context_menu ()
1577 using namespace Menu_Helpers;
1579 MenuList& edit_items = track_context_menu.items();
1582 add_bus_context_items (edit_items);
1583 return &track_context_menu;
1587 Editor::build_track_region_context_menu ()
1589 using namespace Menu_Helpers;
1590 MenuList& edit_items = track_region_context_menu.items();
1593 /* we've just cleared the track region context menu, so the menu that these
1594 two items were on will have disappeared; stop them dangling.
1596 region_edit_menu_split_item = 0;
1597 region_edit_menu_split_multichannel_item = 0;
1599 /* we might try to use items that are currently attached to a crossfade menu,
1602 track_crossfade_context_menu.items().clear ();
1604 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1607 boost::shared_ptr<Track> tr;
1608 boost::shared_ptr<Playlist> pl;
1610 if ((tr = rtv->track())) {
1611 add_region_context_items (edit_items, tr);
1615 add_dstream_context_items (edit_items);
1617 return &track_region_context_menu;
1621 Editor::build_track_crossfade_context_menu ()
1623 using namespace Menu_Helpers;
1624 MenuList& edit_items = track_crossfade_context_menu.items();
1625 edit_items.clear ();
1627 /* we might try to use items that are currently attached to a crossfade menu,
1630 track_region_context_menu.items().clear ();
1632 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1635 boost::shared_ptr<Track> tr;
1636 boost::shared_ptr<Playlist> pl;
1637 boost::shared_ptr<AudioPlaylist> apl;
1639 if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1641 AudioPlaylist::Crossfades xfades;
1645 /* The xfade menu is a bit of a special case, as we always use the mouse position
1646 to decide whether or not to display it (rather than the edit point). No particularly
1647 strong reasons for this, other than it is a bit surprising to right-click on a xfade
1650 mouse_frame (where, ignored);
1651 apl->crossfades_at (where, xfades);
1653 bool const many = xfades.size() > 1;
1655 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1656 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1659 add_region_context_items (edit_items, tr);
1663 add_dstream_context_items (edit_items);
1665 return &track_crossfade_context_menu;
1669 Editor::analyze_region_selection ()
1671 if (analysis_window == 0) {
1672 analysis_window = new AnalysisWindow();
1675 analysis_window->set_session(_session);
1677 analysis_window->show_all();
1680 analysis_window->set_regionmode();
1681 analysis_window->analyze();
1683 analysis_window->present();
1687 Editor::analyze_range_selection()
1689 if (analysis_window == 0) {
1690 analysis_window = new AnalysisWindow();
1693 analysis_window->set_session(_session);
1695 analysis_window->show_all();
1698 analysis_window->set_rangemode();
1699 analysis_window->analyze();
1701 analysis_window->present();
1705 Editor::build_track_selection_context_menu ()
1707 using namespace Menu_Helpers;
1708 MenuList& edit_items = track_selection_context_menu.items();
1709 edit_items.clear ();
1711 add_selection_context_items (edit_items);
1712 // edit_items.push_back (SeparatorElem());
1713 // add_dstream_context_items (edit_items);
1715 return &track_selection_context_menu;
1718 /** Add context menu items relevant to crossfades.
1719 * @param edit_items List to add the items to.
1722 Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1724 using namespace Menu_Helpers;
1725 Menu *xfade_menu = manage (new Menu);
1726 MenuList& items = xfade_menu->items();
1727 xfade_menu->set_name ("ArdourContextMenu");
1730 if (xfade->active()) {
1736 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1737 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1739 if (xfade->can_follow_overlap()) {
1741 if (xfade->following_overlap()) {
1742 str = _("Convert to Short");
1744 str = _("Convert to Full");
1747 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1751 str = xfade->out()->name();
1753 str += xfade->in()->name();
1755 str = _("Crossfade");
1758 edit_items.push_back (MenuElem (str, *xfade_menu));
1759 edit_items.push_back (SeparatorElem());
1763 Editor::xfade_edit_left_region ()
1765 if (clicked_crossfadeview) {
1766 clicked_crossfadeview->left_view.show_region_editor ();
1771 Editor::xfade_edit_right_region ()
1773 if (clicked_crossfadeview) {
1774 clicked_crossfadeview->right_view.show_region_editor ();
1779 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::shared_ptr<Track> track)
1781 using namespace Menu_Helpers;
1783 /* OK, stick the region submenu at the top of the list, and then add
1787 RegionSelection rs = get_regions_from_selection_and_entered ();
1789 string::size_type pos = 0;
1790 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1792 /* we have to hack up the region name because "_" has a special
1793 meaning for menu titles.
1796 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1797 menu_item_name.replace (pos, 1, "__");
1801 if (_popup_region_menu_item == 0) {
1802 _popup_region_menu_item = new MenuItem (menu_item_name);
1803 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1804 _popup_region_menu_item->show ();
1806 _popup_region_menu_item->set_label (menu_item_name);
1809 /* Use the mouse position rather than the edit point to decide whether to show the `choose top region'
1810 dialogue. If we use the edit point it gets a bit messy because the user still has to click over
1811 *some* region in order to get the region context menu stuff to be displayed at all.
1816 mouse_frame (mouse, ignored);
1818 edit_items.push_back (*_popup_region_menu_item);
1819 if (track->playlist()->count_regions_at (mouse) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1820 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region")->create_menu_item ()));
1822 edit_items.push_back (SeparatorElem());
1825 /** Add context menu items relevant to selection ranges.
1826 * @param edit_items List to add the items to.
1829 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1831 using namespace Menu_Helpers;
1833 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1834 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1836 edit_items.push_back (SeparatorElem());
1837 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1839 edit_items.push_back (SeparatorElem());
1841 edit_items.push_back (
1843 _("Move Range Start to Previous Region Boundary"),
1844 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, false)
1848 edit_items.push_back (
1850 _("Move Range Start to Next Region Boundary"),
1851 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, true)
1855 edit_items.push_back (
1857 _("Move Range End to Previous Region Boundary"),
1858 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, false)
1862 edit_items.push_back (
1864 _("Move Range End to Next Region Boundary"),
1865 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, true)
1869 edit_items.push_back (SeparatorElem());
1870 edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection)));
1871 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1873 edit_items.push_back (SeparatorElem());
1874 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1876 edit_items.push_back (SeparatorElem());
1877 edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1878 edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1880 edit_items.push_back (SeparatorElem());
1881 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1883 edit_items.push_back (SeparatorElem());
1884 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1885 edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1886 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1888 edit_items.push_back (SeparatorElem());
1889 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1890 edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1891 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1892 edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1893 edit_items.push_back (MenuElem (_("Export Range..."), sigc::mem_fun(*this, &Editor::export_selection)));
1898 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1900 using namespace Menu_Helpers;
1904 Menu *play_menu = manage (new Menu);
1905 MenuList& play_items = play_menu->items();
1906 play_menu->set_name ("ArdourContextMenu");
1908 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1909 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1910 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1911 play_items.push_back (SeparatorElem());
1912 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
1914 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1918 Menu *select_menu = manage (new Menu);
1919 MenuList& select_items = select_menu->items();
1920 select_menu->set_name ("ArdourContextMenu");
1922 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1923 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1924 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1925 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1926 select_items.push_back (SeparatorElem());
1927 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
1928 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
1929 select_items.push_back (SeparatorElem());
1930 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1931 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1932 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1933 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1934 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
1935 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
1936 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
1938 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1942 Menu *cutnpaste_menu = manage (new Menu);
1943 MenuList& cutnpaste_items = cutnpaste_menu->items();
1944 cutnpaste_menu->set_name ("ArdourContextMenu");
1946 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1947 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1948 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1950 cutnpaste_items.push_back (SeparatorElem());
1952 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
1953 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
1955 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
1957 /* Adding new material */
1959 edit_items.push_back (SeparatorElem());
1960 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
1961 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
1965 Menu *nudge_menu = manage (new Menu());
1966 MenuList& nudge_items = nudge_menu->items();
1967 nudge_menu->set_name ("ArdourContextMenu");
1969 edit_items.push_back (SeparatorElem());
1970 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1971 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1972 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1973 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1975 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1979 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
1981 using namespace Menu_Helpers;
1985 Menu *play_menu = manage (new Menu);
1986 MenuList& play_items = play_menu->items();
1987 play_menu->set_name ("ArdourContextMenu");
1989 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1990 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1991 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1995 Menu *select_menu = manage (new Menu);
1996 MenuList& select_items = select_menu->items();
1997 select_menu->set_name ("ArdourContextMenu");
1999 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2000 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
2001 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
2002 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
2003 select_items.push_back (SeparatorElem());
2004 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
2005 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
2006 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2007 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2009 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2013 Menu *cutnpaste_menu = manage (new Menu);
2014 MenuList& cutnpaste_items = cutnpaste_menu->items();
2015 cutnpaste_menu->set_name ("ArdourContextMenu");
2017 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2018 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2019 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
2021 Menu *nudge_menu = manage (new Menu());
2022 MenuList& nudge_items = nudge_menu->items();
2023 nudge_menu->set_name ("ArdourContextMenu");
2025 edit_items.push_back (SeparatorElem());
2026 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2027 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2028 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2029 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2031 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2035 Editor::snap_type() const
2041 Editor::snap_mode() const
2047 Editor::set_snap_to (SnapType st)
2049 unsigned int snap_ind = (unsigned int)st;
2053 if (snap_ind > snap_type_strings.size() - 1) {
2055 _snap_type = (SnapType)snap_ind;
2058 string str = snap_type_strings[snap_ind];
2060 if (str != snap_type_selector.get_active_text()) {
2061 snap_type_selector.set_active_text (str);
2066 switch (_snap_type) {
2067 case SnapToBeatDiv32:
2068 case SnapToBeatDiv28:
2069 case SnapToBeatDiv24:
2070 case SnapToBeatDiv20:
2071 case SnapToBeatDiv16:
2072 case SnapToBeatDiv14:
2073 case SnapToBeatDiv12:
2074 case SnapToBeatDiv10:
2075 case SnapToBeatDiv8:
2076 case SnapToBeatDiv7:
2077 case SnapToBeatDiv6:
2078 case SnapToBeatDiv5:
2079 case SnapToBeatDiv4:
2080 case SnapToBeatDiv3:
2081 case SnapToBeatDiv2:
2082 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
2083 update_tempo_based_rulers ();
2086 case SnapToRegionStart:
2087 case SnapToRegionEnd:
2088 case SnapToRegionSync:
2089 case SnapToRegionBoundary:
2090 build_region_boundary_cache ();
2098 SnapChanged (); /* EMIT SIGNAL */
2102 Editor::set_snap_mode (SnapMode mode)
2105 string str = snap_mode_strings[(int)mode];
2107 if (str != snap_mode_selector.get_active_text ()) {
2108 snap_mode_selector.set_active_text (str);
2114 Editor::set_edit_point_preference (EditPoint ep, bool force)
2116 bool changed = (_edit_point != ep);
2119 string str = edit_point_strings[(int)ep];
2121 if (str != edit_point_selector.get_active_text ()) {
2122 edit_point_selector.set_active_text (str);
2125 set_canvas_cursor ();
2127 if (!force && !changed) {
2131 const char* action=NULL;
2133 switch (_edit_point) {
2134 case EditAtPlayhead:
2135 action = "edit-at-playhead";
2137 case EditAtSelectedMarker:
2138 action = "edit-at-marker";
2141 action = "edit-at-mouse";
2145 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2147 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2151 bool in_track_canvas;
2153 if (!mouse_frame (foo, in_track_canvas)) {
2154 in_track_canvas = false;
2157 reset_canvas_action_sensitivity (in_track_canvas);
2163 Editor::set_state (const XMLNode& node, int /*version*/)
2165 const XMLProperty* prop;
2172 g.base_width = default_width;
2173 g.base_height = default_height;
2177 if ((geometry = find_named_node (node, "geometry")) != 0) {
2181 if ((prop = geometry->property("x_size")) == 0) {
2182 prop = geometry->property ("x-size");
2185 g.base_width = atoi(prop->value());
2187 if ((prop = geometry->property("y_size")) == 0) {
2188 prop = geometry->property ("y-size");
2191 g.base_height = atoi(prop->value());
2194 if ((prop = geometry->property ("x_pos")) == 0) {
2195 prop = geometry->property ("x-pos");
2198 x = atoi (prop->value());
2201 if ((prop = geometry->property ("y_pos")) == 0) {
2202 prop = geometry->property ("y-pos");
2205 y = atoi (prop->value());
2209 set_default_size (g.base_width, g.base_height);
2212 if (_session && (prop = node.property ("playhead"))) {
2214 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2215 playhead_cursor->set_position (pos);
2217 playhead_cursor->set_position (0);
2220 if ((prop = node.property ("mixer-width"))) {
2221 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2224 if ((prop = node.property ("zoom-focus"))) {
2225 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2228 if ((prop = node.property ("zoom"))) {
2229 reset_zoom (PBD::atof (prop->value()));
2231 reset_zoom (frames_per_unit);
2234 if ((prop = node.property ("snap-to"))) {
2235 set_snap_to ((SnapType) atoi (prop->value()));
2238 if ((prop = node.property ("snap-mode"))) {
2239 set_snap_mode ((SnapMode) atoi (prop->value()));
2242 if ((prop = node.property ("mouse-mode"))) {
2243 MouseMode m = str2mousemode(prop->value());
2244 set_mouse_mode (m, true);
2246 set_mouse_mode (MouseObject, true);
2249 if ((prop = node.property ("left-frame")) != 0) {
2251 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2252 reset_x_origin (pos);
2256 if ((prop = node.property ("y-origin")) != 0) {
2257 reset_y_origin (atof (prop->value ()));
2260 if ((prop = node.property ("internal-edit"))) {
2261 bool yn = string_is_affirmative (prop->value());
2262 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2264 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2265 tact->set_active (!yn);
2266 tact->set_active (yn);
2270 if ((prop = node.property ("join-object-range"))) {
2271 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2274 if ((prop = node.property ("edit-point"))) {
2275 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2278 if ((prop = node.property ("show-measures"))) {
2279 bool yn = string_is_affirmative (prop->value());
2280 _show_measures = yn;
2281 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2283 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2284 /* do it twice to force the change */
2285 tact->set_active (!yn);
2286 tact->set_active (yn);
2290 if ((prop = node.property ("follow-playhead"))) {
2291 bool yn = string_is_affirmative (prop->value());
2292 set_follow_playhead (yn);
2293 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2295 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2296 if (tact->get_active() != yn) {
2297 tact->set_active (yn);
2302 if ((prop = node.property ("stationary-playhead"))) {
2303 bool yn = string_is_affirmative (prop->value());
2304 set_stationary_playhead (yn);
2305 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2307 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2308 if (tact->get_active() != yn) {
2309 tact->set_active (yn);
2314 if ((prop = node.property ("region-list-sort-type"))) {
2315 RegionListSortType st;
2316 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2319 if ((prop = node.property ("xfades-visible"))) {
2320 bool yn = string_is_affirmative (prop->value());
2321 _xfade_visibility = !yn;
2322 // set_xfade_visibility (yn);
2325 if ((prop = node.property ("show-editor-mixer"))) {
2327 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2330 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2331 bool yn = string_is_affirmative (prop->value());
2333 /* do it twice to force the change */
2335 tact->set_active (!yn);
2336 tact->set_active (yn);
2339 if ((prop = node.property ("show-editor-list"))) {
2341 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2344 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2345 bool yn = string_is_affirmative (prop->value());
2347 /* do it twice to force the change */
2349 tact->set_active (!yn);
2350 tact->set_active (yn);
2353 if ((prop = node.property (X_("editor-list-page")))) {
2354 _the_notebook.set_current_page (atoi (prop->value ()));
2357 if ((prop = node.property (X_("show-marker-lines")))) {
2358 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2360 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2361 bool yn = string_is_affirmative (prop->value ());
2363 tact->set_active (!yn);
2364 tact->set_active (yn);
2367 XMLNodeList children = node.children ();
2368 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2369 selection->set_state (**i, Stateful::current_state_version);
2370 _regions->set_state (**i);
2377 Editor::get_state ()
2379 XMLNode* node = new XMLNode ("Editor");
2382 id().print (buf, sizeof (buf));
2383 node->add_property ("id", buf);
2385 if (is_realized()) {
2386 Glib::RefPtr<Gdk::Window> win = get_window();
2388 int x, y, width, height;
2389 win->get_root_origin(x, y);
2390 win->get_size(width, height);
2392 XMLNode* geometry = new XMLNode ("geometry");
2394 snprintf(buf, sizeof(buf), "%d", width);
2395 geometry->add_property("x-size", string(buf));
2396 snprintf(buf, sizeof(buf), "%d", height);
2397 geometry->add_property("y-size", string(buf));
2398 snprintf(buf, sizeof(buf), "%d", x);
2399 geometry->add_property("x-pos", string(buf));
2400 snprintf(buf, sizeof(buf), "%d", y);
2401 geometry->add_property("y-pos", string(buf));
2402 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2403 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2404 geometry->add_property("notebook-shrunk", _notebook_shrunk ? "1" : "0");
2405 snprintf(buf,sizeof(buf), "%d",pre_maximal_horizontal_pane_position);
2406 geometry->add_property("pre-maximal-horizontal-pane-position", string(buf));
2407 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2408 geometry->add_property("edit-vertical-pane-pos", string(buf));
2410 node->add_child_nocopy (*geometry);
2413 maybe_add_mixer_strip_width (*node);
2415 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2416 node->add_property ("zoom-focus", buf);
2417 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2418 node->add_property ("zoom", buf);
2419 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2420 node->add_property ("snap-to", buf);
2421 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2422 node->add_property ("snap-mode", buf);
2424 node->add_property ("edit-point", enum_2_string (_edit_point));
2426 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2427 node->add_property ("playhead", buf);
2428 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2429 node->add_property ("left-frame", buf);
2430 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2431 node->add_property ("y-origin", buf);
2433 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2434 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2435 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2436 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2437 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2438 node->add_property ("mouse-mode", enum2str(mouse_mode));
2439 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2440 node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2442 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2444 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2445 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2448 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2450 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2451 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2454 snprintf (buf, sizeof (buf), "%d", _the_notebook.get_current_page ());
2455 node->add_property (X_("editor-list-page"), buf);
2457 if (button_bindings) {
2458 XMLNode* bb = new XMLNode (X_("Buttons"));
2459 button_bindings->save (*bb);
2460 node->add_child_nocopy (*bb);
2463 node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no");
2465 node->add_child_nocopy (selection->get_state ());
2466 node->add_child_nocopy (_regions->get_state ());
2473 /** @param y y offset from the top of all trackviews.
2474 * @return pair: TimeAxisView that y is over, layer index.
2475 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2476 * in stacked region display mode, otherwise 0.
2478 std::pair<TimeAxisView *, layer_t>
2479 Editor::trackview_by_y_position (double y)
2481 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2483 std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
2489 return std::make_pair ( (TimeAxisView *) 0, 0);
2492 /** Snap a position to the grid, if appropriate, taking into account current
2493 * grid settings and also the state of any snap modifier keys that may be pressed.
2494 * @param start Position to snap.
2495 * @param event Event to get current key modifier information from, or 0.
2498 Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2500 if (!_session || !event) {
2504 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2505 if (_snap_mode == SnapOff) {
2506 snap_to_internal (start, direction, for_mark);
2509 if (_snap_mode != SnapOff) {
2510 snap_to_internal (start, direction, for_mark);
2516 Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark)
2518 if (!_session || _snap_mode == SnapOff) {
2522 snap_to_internal (start, direction, for_mark);
2526 Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /*for_mark*/)
2528 const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2529 framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2531 switch (_snap_type) {
2532 case SnapToTimecodeFrame:
2533 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2534 start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2536 start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2540 case SnapToTimecodeSeconds:
2541 if (_session->config.get_timecode_offset_negative()) {
2542 start += _session->config.get_timecode_offset ();
2544 start -= _session->config.get_timecode_offset ();
2546 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2547 start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2549 start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2552 if (_session->config.get_timecode_offset_negative()) {
2553 start -= _session->config.get_timecode_offset ();
2555 start += _session->config.get_timecode_offset ();
2559 case SnapToTimecodeMinutes:
2560 if (_session->config.get_timecode_offset_negative()) {
2561 start += _session->config.get_timecode_offset ();
2563 start -= _session->config.get_timecode_offset ();
2565 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2566 start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2568 start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2570 if (_session->config.get_timecode_offset_negative()) {
2571 start -= _session->config.get_timecode_offset ();
2573 start += _session->config.get_timecode_offset ();
2577 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2583 Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark)
2585 const framepos_t one_second = _session->frame_rate();
2586 const framepos_t one_minute = _session->frame_rate() * 60;
2587 framepos_t presnap = start;
2591 switch (_snap_type) {
2592 case SnapToTimecodeFrame:
2593 case SnapToTimecodeSeconds:
2594 case SnapToTimecodeMinutes:
2595 return timecode_snap_to_internal (start, direction, for_mark);
2598 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2599 start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2601 start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2606 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2607 start = (framepos_t) ceil ((double) start / one_second) * one_second;
2609 start = (framepos_t) floor ((double) start / one_second) * one_second;
2614 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2615 start = (framepos_t) ceil ((double) start / one_minute) * one_minute;
2617 start = (framepos_t) floor ((double) start / one_minute) * one_minute;
2622 start = _session->tempo_map().round_to_bar (start, direction);
2626 start = _session->tempo_map().round_to_beat (start, direction);
2629 case SnapToBeatDiv32:
2630 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2632 case SnapToBeatDiv28:
2633 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2635 case SnapToBeatDiv24:
2636 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2638 case SnapToBeatDiv20:
2639 start = _session->tempo_map().round_to_beat_subdivision (start, 20, direction);
2641 case SnapToBeatDiv16:
2642 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2644 case SnapToBeatDiv14:
2645 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2647 case SnapToBeatDiv12:
2648 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2650 case SnapToBeatDiv10:
2651 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2653 case SnapToBeatDiv8:
2654 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2656 case SnapToBeatDiv7:
2657 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2659 case SnapToBeatDiv6:
2660 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2662 case SnapToBeatDiv5:
2663 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2665 case SnapToBeatDiv4:
2666 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2668 case SnapToBeatDiv3:
2669 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2671 case SnapToBeatDiv2:
2672 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2680 _session->locations()->marks_either_side (start, before, after);
2682 if (before == max_framepos) {
2684 } else if (after == max_framepos) {
2686 } else if (before != max_framepos && after != max_framepos) {
2687 /* have before and after */
2688 if ((start - before) < (after - start)) {
2697 case SnapToRegionStart:
2698 case SnapToRegionEnd:
2699 case SnapToRegionSync:
2700 case SnapToRegionBoundary:
2701 if (!region_boundary_cache.empty()) {
2703 vector<framepos_t>::iterator prev = region_boundary_cache.end ();
2704 vector<framepos_t>::iterator next = region_boundary_cache.end ();
2706 if (direction > 0) {
2707 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2709 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2712 if (next != region_boundary_cache.begin ()) {
2717 framepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2718 framepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2720 if (start > (p + n) / 2) {
2729 switch (_snap_mode) {
2735 if (presnap > start) {
2736 if (presnap > (start + unit_to_frame(snap_threshold))) {
2740 } else if (presnap < start) {
2741 if (presnap < (start - unit_to_frame(snap_threshold))) {
2747 /* handled at entry */
2755 Editor::setup_toolbar ()
2757 HBox* mode_box = manage(new HBox);
2758 mode_box->set_border_width (2);
2759 mode_box->set_spacing(4);
2761 /* table containing mode buttons */
2763 HBox* mouse_mode_button_box = manage (new HBox ());
2764 mouse_mode_button_box->set_spacing (2);
2766 if (Profile->get_sae()) {
2767 mouse_mode_button_box->pack_start (mouse_move_button);
2769 mouse_mode_button_box->pack_start (mouse_move_button);
2770 mouse_mode_button_box->pack_start (join_object_range_button);
2771 mouse_mode_button_box->pack_start (mouse_select_button);
2774 mouse_mode_button_box->pack_start (mouse_zoom_button);
2776 if (!Profile->get_sae()) {
2777 mouse_mode_button_box->pack_start (mouse_gain_button);
2780 mouse_mode_button_box->pack_start (mouse_timefx_button);
2781 mouse_mode_button_box->pack_start (mouse_audition_button);
2782 mouse_mode_button_box->pack_start (internal_edit_button);
2784 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2785 if (!Profile->get_sae()) {
2786 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2788 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2790 edit_mode_selector.set_name ("EditModeSelector");
2791 set_popdown_strings (edit_mode_selector, edit_mode_strings);
2792 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2794 mode_box->pack_start (edit_mode_selector, false, false);
2795 mode_box->pack_start (*mouse_mode_button_box, false, false);
2797 _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2798 _mouse_mode_tearoff->set_name ("MouseModeBase");
2799 _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2801 if (Profile->get_sae()) {
2802 _mouse_mode_tearoff->set_can_be_torn_off (false);
2805 _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2806 &_mouse_mode_tearoff->tearoff_window()));
2807 _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2808 &_mouse_mode_tearoff->tearoff_window(), 1));
2809 _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2810 &_mouse_mode_tearoff->tearoff_window()));
2811 _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2812 &_mouse_mode_tearoff->tearoff_window(), 1));
2816 _zoom_box.set_spacing (2);
2817 _zoom_box.set_border_width (2);
2821 zoom_in_button.set_name ("zoom button");
2822 zoom_in_button.set_image (::get_icon ("zoom_in"));
2823 zoom_in_button.set_tweaks (ArdourButton::ShowClick);
2824 act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-in"));
2825 zoom_in_button.set_related_action (act);
2827 zoom_out_button.set_name ("zoom button");
2828 zoom_out_button.set_image (::get_icon ("zoom_out"));
2829 zoom_out_button.set_tweaks (ArdourButton::ShowClick);
2830 act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-out"));
2831 zoom_out_button.set_related_action (act);
2833 zoom_out_full_button.set_name ("zoom button");
2834 zoom_out_full_button.set_image (::get_icon ("zoom_full"));
2835 zoom_out_full_button.set_tweaks (ArdourButton::ShowClick);
2836 act = ActionManager::get_action (X_("Editor"), X_("zoom-to-session"));
2837 zoom_out_full_button.set_related_action (act);
2839 zoom_focus_selector.set_name ("ZoomFocusSelector");
2840 set_popdown_strings (zoom_focus_selector, zoom_focus_strings);
2841 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2843 _zoom_box.pack_start (zoom_out_button, false, false);
2844 _zoom_box.pack_start (zoom_in_button, false, false);
2845 _zoom_box.pack_start (zoom_out_full_button, false, false);
2847 _zoom_box.pack_start (zoom_focus_selector, false, false);
2849 /* Track zoom buttons */
2850 tav_expand_button.set_name ("TrackHeightButton");
2851 tav_expand_button.set_size_request (-1, 20);
2852 tav_expand_button.add (*(manage (new Image (::get_icon ("tav_exp")))));
2853 act = ActionManager::get_action (X_("Editor"), X_("expand-tracks"));
2854 act->connect_proxy (tav_expand_button);
2856 tav_shrink_button.set_name ("TrackHeightButton");
2857 tav_shrink_button.set_size_request (-1, 20);
2858 tav_shrink_button.add (*(manage (new Image (::get_icon ("tav_shrink")))));
2859 act = ActionManager::get_action (X_("Editor"), X_("shrink-tracks"));
2860 act->connect_proxy (tav_shrink_button);
2862 _zoom_box.pack_start (tav_shrink_button);
2863 _zoom_box.pack_start (tav_expand_button);
2865 _zoom_tearoff = manage (new TearOff (_zoom_box));
2867 _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2868 &_zoom_tearoff->tearoff_window()));
2869 _zoom_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2870 &_zoom_tearoff->tearoff_window(), 0));
2871 _zoom_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2872 &_zoom_tearoff->tearoff_window()));
2873 _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2874 &_zoom_tearoff->tearoff_window(), 0));
2876 snap_box.set_spacing (1);
2877 snap_box.set_border_width (2);
2879 snap_type_selector.set_name ("SnapTypeSelector");
2880 set_popdown_strings (snap_type_selector, snap_type_strings);
2881 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2883 snap_mode_selector.set_name ("SnapModeSelector");
2884 set_popdown_strings (snap_mode_selector, snap_mode_strings);
2885 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2887 edit_point_selector.set_name ("EditPointSelector");
2888 set_popdown_strings (edit_point_selector, edit_point_strings);
2889 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2891 snap_box.pack_start (snap_mode_selector, false, false);
2892 snap_box.pack_start (snap_type_selector, false, false);
2893 snap_box.pack_start (edit_point_selector, false, false);
2897 HBox *nudge_box = manage (new HBox);
2898 nudge_box->set_spacing (2);
2899 nudge_box->set_border_width (2);
2901 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2902 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2904 nudge_box->pack_start (nudge_backward_button, false, false);
2905 nudge_box->pack_start (nudge_forward_button, false, false);
2906 nudge_box->pack_start (*nudge_clock, false, false);
2909 /* Pack everything in... */
2911 HBox* hbox = manage (new HBox);
2912 hbox->set_spacing(10);
2914 _tools_tearoff = manage (new TearOff (*hbox));
2915 _tools_tearoff->set_name ("MouseModeBase");
2916 _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
2918 if (Profile->get_sae()) {
2919 _tools_tearoff->set_can_be_torn_off (false);
2922 _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2923 &_tools_tearoff->tearoff_window()));
2924 _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2925 &_tools_tearoff->tearoff_window(), 0));
2926 _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2927 &_tools_tearoff->tearoff_window()));
2928 _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2929 &_tools_tearoff->tearoff_window(), 0));
2931 toolbar_hbox.set_spacing (10);
2932 toolbar_hbox.set_border_width (1);
2934 toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
2935 toolbar_hbox.pack_start (*_zoom_tearoff, false, false);
2936 toolbar_hbox.pack_start (*_tools_tearoff, false, false);
2938 hbox->pack_start (snap_box, false, false);
2939 if (!Profile->get_small_screen()) {
2940 hbox->pack_start (*nudge_box, false, false);
2942 ARDOUR_UI::instance()->editor_transport_box().pack_start (*nudge_box, false, false);
2944 hbox->pack_start (panic_box, false, false);
2948 toolbar_base.set_name ("ToolBarBase");
2949 toolbar_base.add (toolbar_hbox);
2951 _toolbar_viewport.add (toolbar_base);
2952 /* stick to the required height but allow width to vary if there's not enough room */
2953 _toolbar_viewport.set_size_request (1, -1);
2955 toolbar_frame.set_shadow_type (SHADOW_OUT);
2956 toolbar_frame.set_name ("BaseFrame");
2957 toolbar_frame.add (_toolbar_viewport);
2961 Editor::setup_tooltips ()
2963 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
2964 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain"));
2965 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
2966 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
2967 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2968 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
2969 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
2970 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
2971 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
2972 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
2973 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
2974 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
2975 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
2976 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
2977 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
2978 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
2979 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
2980 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
2981 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
2982 ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
2983 ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode"));
2988 Editor::setup_midi_toolbar ()
2992 /* Midi sound notes */
2993 midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
2994 midi_sound_notes.unset_flags (CAN_FOCUS);
2995 midi_sound_notes.set_name (X_("MidiSoundNotesButton"));
2999 panic_box.pack_start (midi_sound_notes , true, true);
3000 // panic_box.pack_start (midi_panic_button, true, true);
3004 Editor::convert_drop_to_paths (
3005 vector<string>& paths,
3006 const RefPtr<Gdk::DragContext>& /*context*/,
3009 const SelectionData& data,
3013 if (_session == 0) {
3017 vector<string> uris = data.get_uris();
3021 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3022 are actually URI lists. So do it by hand.
3025 if (data.get_target() != "text/plain") {
3029 /* Parse the "uri-list" format that Nautilus provides,
3030 where each pathname is delimited by \r\n.
3032 THERE MAY BE NO NULL TERMINATING CHAR!!!
3035 string txt = data.get_text();
3039 p = (const char *) malloc (txt.length() + 1);
3040 txt.copy ((char *) p, txt.length(), 0);
3041 ((char*)p)[txt.length()] = '\0';
3047 while (g_ascii_isspace (*p))
3051 while (*q && (*q != '\n') && (*q != '\r')) {
3058 while (q > p && g_ascii_isspace (*q))
3063 uris.push_back (string (p, q - p + 1));
3067 p = strchr (p, '\n');
3079 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3081 if ((*i).substr (0,7) == "file://") {
3084 PBD::url_decode (p);
3086 // scan forward past three slashes
3088 string::size_type slashcnt = 0;
3089 string::size_type n = 0;
3090 string::iterator x = p.begin();
3092 while (slashcnt < 3 && x != p.end()) {
3095 } else if (slashcnt == 3) {
3102 if (slashcnt != 3 || x == p.end()) {
3103 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3107 paths.push_back (p.substr (n - 1));
3115 Editor::new_tempo_section ()
3121 Editor::map_transport_state ()
3123 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3125 if (_session && _session->transport_stopped()) {
3126 have_pending_keyboard_selection = false;
3129 update_loop_range_view (true);
3134 Editor::State::State (PublicEditor const * e)
3136 selection = new Selection (e);
3139 Editor::State::~State ()
3145 Editor::begin_reversible_command (string name)
3148 _session->begin_reversible_command (name);
3153 Editor::begin_reversible_command (GQuark q)
3156 _session->begin_reversible_command (q);
3161 Editor::commit_reversible_command ()
3164 _session->commit_reversible_command ();
3169 Editor::history_changed ()
3173 if (undo_action && _session) {
3174 if (_session->undo_depth() == 0) {
3177 label = string_compose(_("Undo (%1)"), _session->next_undo());
3179 undo_action->property_label() = label;
3182 if (redo_action && _session) {
3183 if (_session->redo_depth() == 0) {
3186 label = string_compose(_("Redo (%1)"), _session->next_redo());
3188 redo_action->property_label() = label;
3193 Editor::duplicate_dialog (bool with_dialog)
3197 if (mouse_mode == MouseRange) {
3198 if (selection->time.length() == 0) {
3203 RegionSelection rs = get_regions_from_selection_and_entered ();
3205 if (mouse_mode != MouseRange && rs.empty()) {
3211 ArdourDialog win (_("Duplicate"));
3212 Label label (_("Number of duplications:"));
3213 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3214 SpinButton spinner (adjustment, 0.0, 1);
3217 win.get_vbox()->set_spacing (12);
3218 win.get_vbox()->pack_start (hbox);
3219 hbox.set_border_width (6);
3220 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3222 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3223 place, visually. so do this by hand.
3226 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3227 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3228 spinner.grab_focus();
3234 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3235 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3236 win.set_default_response (RESPONSE_ACCEPT);
3238 win.set_position (WIN_POS_MOUSE);
3240 spinner.grab_focus ();
3242 switch (win.run ()) {
3243 case RESPONSE_ACCEPT:
3249 times = adjustment.get_value();
3252 if (mouse_mode == MouseRange) {
3253 duplicate_selection (times);
3255 duplicate_some_regions (rs, times);
3260 Editor::set_edit_mode (EditMode m)
3262 Config->set_edit_mode (m);
3266 Editor::cycle_edit_mode ()
3268 switch (Config->get_edit_mode()) {
3270 if (Profile->get_sae()) {
3271 Config->set_edit_mode (Lock);
3273 Config->set_edit_mode (Splice);
3277 Config->set_edit_mode (Lock);
3280 Config->set_edit_mode (Slide);
3286 Editor::edit_mode_selection_done ()
3288 string s = edit_mode_selector.get_active_text ();
3291 Config->set_edit_mode (string_to_edit_mode (s));
3296 Editor::snap_type_selection_done ()
3298 string choice = snap_type_selector.get_active_text();
3299 SnapType snaptype = SnapToBeat;
3301 if (choice == _("Beats/2")) {
3302 snaptype = SnapToBeatDiv2;
3303 } else if (choice == _("Beats/3")) {
3304 snaptype = SnapToBeatDiv3;
3305 } else if (choice == _("Beats/4")) {
3306 snaptype = SnapToBeatDiv4;
3307 } else if (choice == _("Beats/5")) {
3308 snaptype = SnapToBeatDiv5;
3309 } else if (choice == _("Beats/6")) {
3310 snaptype = SnapToBeatDiv6;
3311 } else if (choice == _("Beats/7")) {
3312 snaptype = SnapToBeatDiv7;
3313 } else if (choice == _("Beats/8")) {
3314 snaptype = SnapToBeatDiv8;
3315 } else if (choice == _("Beats/10")) {
3316 snaptype = SnapToBeatDiv10;
3317 } else if (choice == _("Beats/12")) {
3318 snaptype = SnapToBeatDiv12;
3319 } else if (choice == _("Beats/14")) {
3320 snaptype = SnapToBeatDiv14;
3321 } else if (choice == _("Beats/16")) {
3322 snaptype = SnapToBeatDiv16;
3323 } else if (choice == _("Beats/20")) {
3324 snaptype = SnapToBeatDiv20;
3325 } else if (choice == _("Beats/24")) {
3326 snaptype = SnapToBeatDiv24;
3327 } else if (choice == _("Beats/28")) {
3328 snaptype = SnapToBeatDiv28;
3329 } else if (choice == _("Beats/32")) {
3330 snaptype = SnapToBeatDiv32;
3331 } else if (choice == _("Beats")) {
3332 snaptype = SnapToBeat;
3333 } else if (choice == _("Bars")) {
3334 snaptype = SnapToBar;
3335 } else if (choice == _("Marks")) {
3336 snaptype = SnapToMark;
3337 } else if (choice == _("Region starts")) {
3338 snaptype = SnapToRegionStart;
3339 } else if (choice == _("Region ends")) {
3340 snaptype = SnapToRegionEnd;
3341 } else if (choice == _("Region bounds")) {
3342 snaptype = SnapToRegionBoundary;
3343 } else if (choice == _("Region syncs")) {
3344 snaptype = SnapToRegionSync;
3345 } else if (choice == _("CD Frames")) {
3346 snaptype = SnapToCDFrame;
3347 } else if (choice == _("Timecode Frames")) {
3348 snaptype = SnapToTimecodeFrame;
3349 } else if (choice == _("Timecode Seconds")) {
3350 snaptype = SnapToTimecodeSeconds;
3351 } else if (choice == _("Timecode Minutes")) {
3352 snaptype = SnapToTimecodeMinutes;
3353 } else if (choice == _("Seconds")) {
3354 snaptype = SnapToSeconds;
3355 } else if (choice == _("Minutes")) {
3356 snaptype = SnapToMinutes;
3359 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3361 ract->set_active ();
3366 Editor::snap_mode_selection_done ()
3368 string choice = snap_mode_selector.get_active_text();
3369 SnapMode mode = SnapNormal;
3371 if (choice == _("No Grid")) {
3373 } else if (choice == _("Grid")) {
3375 } else if (choice == _("Magnetic")) {
3376 mode = SnapMagnetic;
3379 RefPtr<RadioAction> ract = snap_mode_action (mode);
3382 ract->set_active (true);
3387 Editor::cycle_edit_point (bool with_marker)
3389 switch (_edit_point) {
3391 set_edit_point_preference (EditAtPlayhead);
3393 case EditAtPlayhead:
3395 set_edit_point_preference (EditAtSelectedMarker);
3397 set_edit_point_preference (EditAtMouse);
3400 case EditAtSelectedMarker:
3401 set_edit_point_preference (EditAtMouse);
3407 Editor::edit_point_selection_done ()
3409 string choice = edit_point_selector.get_active_text();
3410 EditPoint ep = EditAtSelectedMarker;
3412 if (choice == _("Marker")) {
3413 set_edit_point_preference (EditAtSelectedMarker);
3414 } else if (choice == _("Playhead")) {
3415 set_edit_point_preference (EditAtPlayhead);
3417 set_edit_point_preference (EditAtMouse);
3420 RefPtr<RadioAction> ract = edit_point_action (ep);
3423 ract->set_active (true);
3428 Editor::zoom_focus_selection_done ()
3430 string choice = zoom_focus_selector.get_active_text();
3431 ZoomFocus focus_type = ZoomFocusLeft;
3433 if (choice == _("Left")) {
3434 focus_type = ZoomFocusLeft;
3435 } else if (choice == _("Right")) {
3436 focus_type = ZoomFocusRight;
3437 } else if (choice == _("Center")) {
3438 focus_type = ZoomFocusCenter;
3439 } else if (choice == _("Playhead")) {
3440 focus_type = ZoomFocusPlayhead;
3441 } else if (choice == _("Mouse")) {
3442 focus_type = ZoomFocusMouse;
3443 } else if (choice == _("Edit point")) {
3444 focus_type = ZoomFocusEdit;
3447 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3450 ract->set_active ();
3455 Editor::edit_controls_button_release (GdkEventButton* ev)
3457 if (Keyboard::is_context_menu_event (ev)) {
3458 ARDOUR_UI::instance()->add_route (this);
3459 } else if (ev->button == 1) {
3460 selection->clear_tracks ();
3467 Editor::mouse_select_button_release (GdkEventButton* ev)
3469 /* this handles just right-clicks */
3471 if (ev->button != 3) {
3479 Editor::set_zoom_focus (ZoomFocus f)
3481 string str = zoom_focus_strings[(int)f];
3483 if (str != zoom_focus_selector.get_active_text()) {
3484 zoom_focus_selector.set_active_text (str);
3487 if (zoom_focus != f) {
3494 Editor::ensure_float (Window& win)
3496 win.set_transient_for (*this);
3500 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3502 /* recover or initialize pane positions. do this here rather than earlier because
3503 we don't want the positions to change the child allocations, which they seem to do.
3509 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3518 XMLNode* geometry = find_named_node (*node, "geometry");
3520 if (which == static_cast<Paned*> (&edit_pane)) {
3522 if (done & Horizontal) {
3526 if (geometry && (prop = geometry->property ("notebook-shrunk"))) {
3527 _notebook_shrunk = string_is_affirmative (prop->value ());
3530 if (geometry && (prop = geometry->property ("pre-maximal-horizontal-pane-position"))) {
3531 pre_maximal_horizontal_pane_position = atoi (prop->value ());
3534 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3535 /* initial allocation is 90% to canvas, 10% to notebook */
3536 pos = (int) floor (alloc.get_width() * 0.90f);
3537 snprintf (buf, sizeof(buf), "%d", pos);
3539 pos = atoi (prop->value());
3542 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3543 edit_pane.set_position (pos);
3544 if (pre_maximal_horizontal_pane_position == 0) {
3545 pre_maximal_horizontal_pane_position = pos;
3549 done = (Pane) (done | Horizontal);
3551 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3553 if (done & Vertical) {
3557 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3558 /* initial allocation is 90% to canvas, 10% to summary */
3559 pos = (int) floor (alloc.get_height() * 0.90f);
3560 snprintf (buf, sizeof(buf), "%d", pos);
3562 pos = atoi (prop->value());
3565 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3566 editor_summary_pane.set_position (pos);
3567 pre_maximal_vertical_pane_position = pos;
3570 done = (Pane) (done | Vertical);
3575 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3577 if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) {
3578 top_hbox.remove (toolbar_frame);
3583 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3585 if (toolbar_frame.get_parent() == 0) {
3586 top_hbox.pack_end (toolbar_frame);
3591 Editor::set_show_measures (bool yn)
3593 if (_show_measures != yn) {
3596 if ((_show_measures = yn) == true) {
3598 tempo_lines->show();
3606 Editor::toggle_follow_playhead ()
3608 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3610 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3611 set_follow_playhead (tact->get_active());
3615 /** @param yn true to follow playhead, otherwise false.
3616 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
3619 Editor::set_follow_playhead (bool yn, bool catch_up)
3621 if (_follow_playhead != yn) {
3622 if ((_follow_playhead = yn) == true && catch_up) {
3624 reset_x_origin_to_follow_playhead ();
3631 Editor::toggle_stationary_playhead ()
3633 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3635 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3636 set_stationary_playhead (tact->get_active());
3641 Editor::set_stationary_playhead (bool yn)
3643 if (_stationary_playhead != yn) {
3644 if ((_stationary_playhead = yn) == true) {
3646 // FIXME need a 3.0 equivalent of this 2.X call
3647 // update_current_screen ();
3654 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3656 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3658 xfade->set_active (!xfade->active());
3663 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3665 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3667 xfade->set_follow_overlap (!xfade->following_overlap());
3672 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3674 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3680 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3684 switch (cew.run ()) {
3685 case RESPONSE_ACCEPT:
3692 PropertyChange all_crossfade_properties;
3693 all_crossfade_properties.add (ARDOUR::Properties::active);
3694 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3695 xfade->PropertyChanged (all_crossfade_properties);
3699 Editor::playlist_selector () const
3701 return *_playlist_selector;
3705 Editor::get_grid_type_as_beats (bool& success, framepos_t position)
3709 switch (_snap_type) {
3714 case SnapToBeatDiv32:
3717 case SnapToBeatDiv28:
3720 case SnapToBeatDiv24:
3723 case SnapToBeatDiv20:
3726 case SnapToBeatDiv16:
3729 case SnapToBeatDiv14:
3732 case SnapToBeatDiv12:
3735 case SnapToBeatDiv10:
3738 case SnapToBeatDiv8:
3741 case SnapToBeatDiv7:
3744 case SnapToBeatDiv6:
3747 case SnapToBeatDiv5:
3750 case SnapToBeatDiv4:
3753 case SnapToBeatDiv3:
3756 case SnapToBeatDiv2:
3762 return _session->tempo_map().meter_at (position).beats_per_bar();
3767 case SnapToTimecodeFrame:
3768 case SnapToTimecodeSeconds:
3769 case SnapToTimecodeMinutes:
3772 case SnapToRegionStart:
3773 case SnapToRegionEnd:
3774 case SnapToRegionSync:
3775 case SnapToRegionBoundary:
3785 Editor::get_nudge_distance (framepos_t pos, framecnt_t& next)
3789 ret = nudge_clock->current_duration (pos);
3790 next = ret + 1; /* XXXX fix me */
3796 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3798 ArdourDialog dialog (_("Playlist Deletion"));
3799 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3800 "If it is kept, its audio files will not be cleaned.\n"
3801 "If it is deleted, audio files used by it alone will be cleaned."),
3804 dialog.set_position (WIN_POS_CENTER);
3805 dialog.get_vbox()->pack_start (label);
3809 dialog.add_button (_("Delete Playlist"), RESPONSE_ACCEPT);
3810 dialog.add_button (_("Keep Playlist"), RESPONSE_REJECT);
3811 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3813 switch (dialog.run ()) {
3814 case RESPONSE_ACCEPT:
3815 /* delete the playlist */
3819 case RESPONSE_REJECT:
3820 /* keep the playlist */
3832 Editor::audio_region_selection_covers (framepos_t where)
3834 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3835 if ((*a)->region()->covers (where)) {
3844 Editor::prepare_for_cleanup ()
3846 cut_buffer->clear_regions ();
3847 cut_buffer->clear_playlists ();
3849 selection->clear_regions ();
3850 selection->clear_playlists ();
3852 _regions->suspend_redisplay ();
3856 Editor::finish_cleanup ()
3858 _regions->resume_redisplay ();
3862 Editor::transport_loop_location()
3865 return _session->locations()->auto_loop_location();
3872 Editor::transport_punch_location()
3875 return _session->locations()->auto_punch_location();
3882 Editor::control_layout_scroll (GdkEventScroll* ev)
3884 if (Keyboard::some_magic_widget_has_focus()) {
3888 switch (ev->direction) {
3890 scroll_tracks_up_line ();
3894 case GDK_SCROLL_DOWN:
3895 scroll_tracks_down_line ();
3899 /* no left/right handling yet */
3907 Editor::session_state_saved (string)
3910 _snapshots->redisplay ();
3914 Editor::maximise_editing_space ()
3916 _mouse_mode_tearoff->set_visible (false);
3917 _tools_tearoff->set_visible (false);
3918 _zoom_tearoff->set_visible (false);
3920 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
3921 pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
3922 pre_maximal_editor_width = this->get_width ();
3923 pre_maximal_editor_height = this->get_height ();
3925 if (post_maximal_horizontal_pane_position == 0) {
3926 post_maximal_horizontal_pane_position = edit_pane.get_width();
3929 if (post_maximal_vertical_pane_position == 0) {
3930 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
3935 if (post_maximal_editor_width) {
3936 edit_pane.set_position (post_maximal_horizontal_pane_position -
3937 abs(post_maximal_editor_width - pre_maximal_editor_width));
3939 edit_pane.set_position (post_maximal_horizontal_pane_position);
3942 /* Hack: we must do this in an idle handler for it to work; see comment in
3943 restore_editing_space()
3946 Glib::signal_idle().connect (
3948 sigc::mem_fun (*this, &Editor::idle_reset_vertical_pane_position),
3949 post_maximal_vertical_pane_position
3953 if (Config->get_keep_tearoffs()) {
3954 _mouse_mode_tearoff->set_visible (true);
3955 _tools_tearoff->set_visible (true);
3956 if (Config->get_show_zoom_tools ()) {
3957 _zoom_tearoff->set_visible (true);
3964 Editor::idle_reset_vertical_pane_position (int p)
3966 editor_summary_pane.set_position (p);
3971 Editor::restore_editing_space ()
3973 // user changed width/height of panes during fullscreen
3975 if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
3976 post_maximal_horizontal_pane_position = edit_pane.get_position();
3979 if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
3980 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
3985 _mouse_mode_tearoff->set_visible (true);
3986 _tools_tearoff->set_visible (true);
3987 if (Config->get_show_zoom_tools ()) {
3988 _zoom_tearoff->set_visible (true);
3990 post_maximal_editor_width = this->get_width();
3991 post_maximal_editor_height = this->get_height();
3993 edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
3995 /* This is a bit of a hack, but it seems that if you set the vertical pane position
3996 here it gets reset to some wrong value after this method has finished. Doing
3997 the setup in an idle callback seems to work.
3999 Glib::signal_idle().connect (
4001 sigc::mem_fun (*this, &Editor::idle_reset_vertical_pane_position),
4002 pre_maximal_vertical_pane_position
4008 * Make new playlists for a given track and also any others that belong
4009 * to the same active route group with the `edit' property.
4014 Editor::new_playlists (TimeAxisView* v)
4016 begin_reversible_command (_("new playlists"));
4017 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4018 _session->playlists->get (playlists);
4019 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4020 commit_reversible_command ();
4024 * Use a copy of the current playlist for a given track and also any others that belong
4025 * to the same active route group with the `edit' property.
4030 Editor::copy_playlists (TimeAxisView* v)
4032 begin_reversible_command (_("copy playlists"));
4033 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4034 _session->playlists->get (playlists);
4035 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4036 commit_reversible_command ();
4039 /** Clear the current playlist for a given track and also any others that belong
4040 * to the same active route group with the `edit' property.
4045 Editor::clear_playlists (TimeAxisView* v)
4047 begin_reversible_command (_("clear playlists"));
4048 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4049 _session->playlists->get (playlists);
4050 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4051 commit_reversible_command ();
4055 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4057 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4061 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4063 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4067 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4069 atv.clear_playlist ();
4073 Editor::on_key_press_event (GdkEventKey* ev)
4075 return key_press_focus_accelerator_handler (*this, ev);
4079 Editor::on_key_release_event (GdkEventKey* ev)
4081 return Gtk::Window::on_key_release_event (ev);
4082 // return key_press_focus_accelerator_handler (*this, ev);
4085 /** Queue up a change to the viewport x origin.
4086 * @param frame New x origin.
4089 Editor::reset_x_origin (framepos_t frame)
4091 queue_visual_change (frame);
4095 Editor::reset_y_origin (double y)
4097 queue_visual_change_y (y);
4101 Editor::reset_zoom (double fpu)
4103 queue_visual_change (fpu);
4107 Editor::reposition_and_zoom (framepos_t frame, double fpu)
4109 reset_x_origin (frame);
4112 if (!no_save_visual) {
4113 undo_visual_stack.push_back (current_visual_state(false));
4117 Editor::VisualState::VisualState ()
4118 : gui_state (new GUIObjectState)
4122 Editor::VisualState::~VisualState ()
4127 Editor::VisualState*
4128 Editor::current_visual_state (bool with_tracks)
4130 VisualState* vs = new VisualState;
4131 vs->y_position = vertical_adjustment.get_value();
4132 vs->frames_per_unit = frames_per_unit;
4133 vs->leftmost_frame = leftmost_frame;
4134 vs->zoom_focus = zoom_focus;
4137 *(vs->gui_state) = *ARDOUR_UI::instance()->gui_object_state;
4144 Editor::undo_visual_state ()
4146 if (undo_visual_stack.empty()) {
4150 redo_visual_stack.push_back (current_visual_state());
4152 VisualState* vs = undo_visual_stack.back();
4153 undo_visual_stack.pop_back();
4154 use_visual_state (*vs);
4158 Editor::redo_visual_state ()
4160 if (redo_visual_stack.empty()) {
4164 undo_visual_stack.push_back (current_visual_state());
4166 VisualState* vs = redo_visual_stack.back();
4167 redo_visual_stack.pop_back();
4168 use_visual_state (*vs);
4172 Editor::swap_visual_state ()
4174 if (undo_visual_stack.empty()) {
4175 redo_visual_state ();
4177 undo_visual_state ();
4182 Editor::use_visual_state (VisualState& vs)
4184 no_save_visual = true;
4186 _routes->suspend_redisplay ();
4188 vertical_adjustment.set_value (vs.y_position);
4190 set_zoom_focus (vs.zoom_focus);
4191 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4193 *ARDOUR_UI::instance()->gui_object_state = *vs.gui_state;
4195 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4196 (*i)->reset_visual_state ();
4199 _routes->update_visibility ();
4200 _routes->resume_redisplay ();
4202 no_save_visual = false;
4206 Editor::set_frames_per_unit (double fpu)
4208 /* this is the core function that controls the zoom level of the canvas. it is called
4209 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4212 if (fpu == frames_per_unit) {
4221 /* don't allow zooms that fit more than the maximum number
4222 of frames into an 800 pixel wide space.
4225 if (max_framepos / fpu < 800.0) {
4230 tempo_lines->tempo_map_changed();
4232 frames_per_unit = fpu;
4237 Editor::post_zoom ()
4239 // convert fpu to frame count
4241 framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width);
4243 if (frames_per_unit != zoom_range_clock->current_duration()) {
4244 zoom_range_clock->set (frames);
4247 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4248 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4249 (*i)->reshow_selection (selection->time);
4253 ZoomChanged (); /* EMIT_SIGNAL */
4255 //reset_scrolling_region ();
4257 if (playhead_cursor) {
4258 playhead_cursor->set_position (playhead_cursor->current_frame);
4261 refresh_location_display();
4262 _summary->set_overlays_dirty ();
4264 update_marker_labels ();
4270 Editor::queue_visual_change (framepos_t where)
4272 pending_visual_change.add (VisualChange::TimeOrigin);
4273 pending_visual_change.time_origin = where;
4274 ensure_visual_change_idle_handler ();
4278 Editor::queue_visual_change (double fpu)
4280 pending_visual_change.add (VisualChange::ZoomLevel);
4281 pending_visual_change.frames_per_unit = fpu;
4283 ensure_visual_change_idle_handler ();
4287 Editor::queue_visual_change_y (double y)
4289 pending_visual_change.add (VisualChange::YOrigin);
4290 pending_visual_change.y_origin = y;
4292 ensure_visual_change_idle_handler ();
4296 Editor::ensure_visual_change_idle_handler ()
4298 if (pending_visual_change.idle_handler_id < 0) {
4299 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4304 Editor::_idle_visual_changer (void* arg)
4306 return static_cast<Editor*>(arg)->idle_visual_changer ();
4310 Editor::idle_visual_changer ()
4312 VisualChange::Type p = pending_visual_change.pending;
4313 pending_visual_change.pending = (VisualChange::Type) 0;
4315 double const last_time_origin = horizontal_position ();
4317 if (p & VisualChange::TimeOrigin) {
4318 /* This is a bit of a hack, but set_frames_per_unit
4319 below will (if called) end up with the
4320 CrossfadeViews looking at Editor::leftmost_frame,
4321 and if we're changing origin and zoom in the same
4322 operation it will be the wrong value unless we
4326 leftmost_frame = pending_visual_change.time_origin;
4329 if (p & VisualChange::ZoomLevel) {
4330 set_frames_per_unit (pending_visual_change.frames_per_unit);
4332 compute_fixed_ruler_scale ();
4333 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4334 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4335 update_tempo_based_rulers ();
4337 if (p & VisualChange::TimeOrigin) {
4338 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4340 if (p & VisualChange::YOrigin) {
4341 vertical_adjustment.set_value (pending_visual_change.y_origin);
4344 if (last_time_origin == horizontal_position ()) {
4345 /* changed signal not emitted */
4346 update_fixed_rulers ();
4347 redisplay_tempo (true);
4350 _summary->set_overlays_dirty ();
4352 pending_visual_change.idle_handler_id = -1;
4353 return 0; /* this is always a one-shot call */
4356 struct EditorOrderTimeAxisSorter {
4357 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4358 return a->order () < b->order ();
4363 Editor::sort_track_selection (TrackViewList* sel)
4365 EditorOrderTimeAxisSorter cmp;
4370 selection->tracks.sort (cmp);
4375 Editor::get_preferred_edit_position (bool ignore_playhead)
4378 framepos_t where = 0;
4379 EditPoint ep = _edit_point;
4381 if (entered_marker) {
4382 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position()));
4383 return entered_marker->position();
4386 if (ignore_playhead && ep == EditAtPlayhead) {
4387 ep = EditAtSelectedMarker;
4391 case EditAtPlayhead:
4392 where = _session->audible_frame();
4393 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where));
4396 case EditAtSelectedMarker:
4397 if (!selection->markers.empty()) {
4399 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4402 where = loc->start();
4406 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where));
4414 if (!mouse_frame (where, ignored)) {
4415 /* XXX not right but what can we do ? */
4419 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where));
4427 Editor::set_loop_range (framepos_t start, framepos_t end, string cmd)
4429 if (!_session) return;
4431 begin_reversible_command (cmd);
4435 if ((tll = transport_loop_location()) == 0) {
4436 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop);
4437 XMLNode &before = _session->locations()->get_state();
4438 _session->locations()->add (loc, true);
4439 _session->set_auto_loop_location (loc);
4440 XMLNode &after = _session->locations()->get_state();
4441 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4443 XMLNode &before = tll->get_state();
4444 tll->set_hidden (false, this);
4445 tll->set (start, end);
4446 XMLNode &after = tll->get_state();
4447 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4450 commit_reversible_command ();
4454 Editor::set_punch_range (framepos_t start, framepos_t end, string cmd)
4456 if (!_session) return;
4458 begin_reversible_command (cmd);
4462 if ((tpl = transport_punch_location()) == 0) {
4463 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoPunch);
4464 XMLNode &before = _session->locations()->get_state();
4465 _session->locations()->add (loc, true);
4466 _session->set_auto_loop_location (loc);
4467 XMLNode &after = _session->locations()->get_state();
4468 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4471 XMLNode &before = tpl->get_state();
4472 tpl->set_hidden (false, this);
4473 tpl->set (start, end);
4474 XMLNode &after = tpl->get_state();
4475 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4478 commit_reversible_command ();
4481 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4482 * @param rs List to which found regions are added.
4483 * @param where Time to look at.
4484 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4487 Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4489 const TrackViewList* tracks;
4492 tracks = &track_views;
4497 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4499 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4502 boost::shared_ptr<Track> tr;
4503 boost::shared_ptr<Playlist> pl;
4505 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4507 Playlist::RegionList* regions = pl->regions_at (
4508 (framepos_t) floor ( (double) where * tr->speed()));
4510 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4511 RegionView* rv = rtv->view()->find_view (*i);
4524 Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4526 const TrackViewList* tracks;
4529 tracks = &track_views;
4534 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4535 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4537 boost::shared_ptr<Track> tr;
4538 boost::shared_ptr<Playlist> pl;
4540 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4542 Playlist::RegionList* regions = pl->regions_touched (
4543 (framepos_t) floor ( (double)where * tr->speed()), max_framepos);
4545 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4547 RegionView* rv = rtv->view()->find_view (*i);
4560 /** Start with regions that are selected. Then add equivalent regions
4561 * on tracks in the same active edit-enabled route group as any of
4562 * the regions that we started with.
4566 Editor::get_regions_from_selection ()
4568 return get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
4571 /** Get regions using the following method:
4573 * Make an initial region list using the selected regions, unless
4574 * the edit point is `mouse' and the mouse is over an unselected
4575 * region. In this case, start with just that region.
4577 * Then, make an initial track list of the tracks that these
4578 * regions are on, and if the edit point is not `mouse', add the
4581 * Look at this track list and add any other tracks that are on the
4582 * same active edit-enabled route group as one of the initial tracks.
4584 * Finally take the initial region list and add any regions that are
4585 * under the edit point on one of the tracks on the track list to get
4586 * the returned region list.
4588 * The rationale here is that the mouse edit point is special in that
4589 * its position describes both a time and a track; the other edit
4590 * modes only describe a time. Hence if the edit point is `mouse' we
4591 * ignore selected tracks, as we assume the user means something by
4592 * pointing at a particular track. Also in this case we take note of
4593 * the region directly under the edit point, as there is always just one
4594 * (rather than possibly several with non-mouse edit points).
4598 Editor::get_regions_from_selection_and_edit_point ()
4600 RegionSelection regions;
4602 if (_edit_point == EditAtMouse && entered_regionview && !selection->regions.contains (entered_regionview)) {
4603 regions.add (entered_regionview);
4605 regions = selection->regions;
4608 TrackViewList tracks;
4610 if (_edit_point != EditAtMouse) {
4611 tracks = selection->tracks;
4614 /* Add any other tracks that have regions that are in the same
4615 edit-activated route group as one of our regions.
4617 for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) {
4619 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4621 if (g && g->is_active() && g->is_edit()) {
4622 tracks.add (axis_views_from_routes (g->route_list()));
4626 if (!tracks.empty()) {
4627 /* now find regions that are at the edit position on those tracks */
4628 framepos_t const where = get_preferred_edit_position ();
4629 get_regions_at (regions, where, tracks);
4635 /** Start with regions that are selected, or the entered regionview if none are selected.
4636 * Then add equivalent regions on tracks in the same active edit-enabled route group as any
4637 * of the regions that we started with.
4641 Editor::get_regions_from_selection_and_entered ()
4643 RegionSelection regions = selection->regions;
4645 if (regions.empty() && entered_regionview) {
4646 regions.add (entered_regionview);
4649 return get_equivalent_regions (regions, ARDOUR::Properties::edit.property_id);
4653 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4655 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4657 RouteTimeAxisView* tatv;
4659 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4661 boost::shared_ptr<Playlist> pl;
4662 vector<boost::shared_ptr<Region> > results;
4664 boost::shared_ptr<Track> tr;
4666 if ((tr = tatv->track()) == 0) {
4671 if ((pl = (tr->playlist())) != 0) {
4672 pl->get_region_list_equivalent_regions (region, results);
4675 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4676 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4677 regions.push_back (marv);
4686 Editor::show_rhythm_ferret ()
4688 if (rhythm_ferret == 0) {
4689 rhythm_ferret = new RhythmFerret(*this);
4692 rhythm_ferret->set_session (_session);
4693 rhythm_ferret->show ();
4694 rhythm_ferret->present ();
4698 Editor::first_idle ()
4700 MessageDialog* dialog = 0;
4702 if (track_views.size() > 1) {
4703 dialog = new MessageDialog (
4705 string_compose (_("Please wait while %1 loads visual data."), PROGRAM_NAME),
4709 ARDOUR_UI::instance()->flush_pending ();
4712 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4716 // first idle adds route children (automation tracks), so we need to redisplay here
4717 _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 (const 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::add_notebook_page (string const & name, Gtk::Widget& widget)
5444 EventBox* b = manage (new EventBox);
5445 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
5446 Label* l = manage (new Label (name));
5450 _the_notebook.append_page (widget, *b);
5454 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
5456 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
5457 _the_notebook.set_current_page (_the_notebook.page_num (*page));
5460 if (ev->type == GDK_2BUTTON_PRESS) {
5462 /* double-click on a notebook tab shrinks or expands the notebook */
5464 if (_notebook_shrunk) {
5465 edit_pane.set_position (pre_maximal_horizontal_pane_position);
5466 _notebook_shrunk = false;
5468 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
5469 edit_pane.set_position (edit_pane.get_position() + page->get_width());
5470 _notebook_shrunk = true;
5478 Editor::popup_control_point_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
5480 using namespace Menu_Helpers;
5482 MenuList& items = _control_point_context_menu.items ();
5485 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun (*this, &Editor::edit_control_point), item)));
5486 items.push_back (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun (*this, &Editor::remove_control_point), item)));
5487 if (!can_remove_control_point (item)) {
5488 items.back().set_sensitive (false);
5491 _control_point_context_menu.popup (event->button.button, event->button.time);