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, true);
2792 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2794 mode_box->pack_start (edit_mode_selector);
2795 mode_box->pack_start (*mouse_mode_button_box);
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, true);
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);
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, true);
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, true);
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, true);
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);
2959 DPIReset.connect (sigc::mem_fun (*this, &Editor::resize_text_widgets));
2963 Editor::setup_tooltips ()
2965 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
2966 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain"));
2967 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
2968 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
2969 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2970 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
2971 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
2972 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
2973 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
2974 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
2975 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
2976 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
2977 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
2978 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
2979 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
2980 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
2981 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
2982 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
2983 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
2984 ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
2985 ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode"));
2990 Editor::setup_midi_toolbar ()
2994 /* Midi sound notes */
2995 midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
2996 midi_sound_notes.unset_flags (CAN_FOCUS);
2997 midi_sound_notes.set_name (X_("MidiSoundNotesButton"));
3001 panic_box.pack_start (midi_sound_notes , true, true);
3002 // panic_box.pack_start (midi_panic_button, true, true);
3006 Editor::convert_drop_to_paths (
3007 vector<string>& paths,
3008 const RefPtr<Gdk::DragContext>& /*context*/,
3011 const SelectionData& data,
3015 if (_session == 0) {
3019 vector<string> uris = data.get_uris();
3023 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3024 are actually URI lists. So do it by hand.
3027 if (data.get_target() != "text/plain") {
3031 /* Parse the "uri-list" format that Nautilus provides,
3032 where each pathname is delimited by \r\n.
3034 THERE MAY BE NO NULL TERMINATING CHAR!!!
3037 string txt = data.get_text();
3041 p = (const char *) malloc (txt.length() + 1);
3042 txt.copy ((char *) p, txt.length(), 0);
3043 ((char*)p)[txt.length()] = '\0';
3049 while (g_ascii_isspace (*p))
3053 while (*q && (*q != '\n') && (*q != '\r')) {
3060 while (q > p && g_ascii_isspace (*q))
3065 uris.push_back (string (p, q - p + 1));
3069 p = strchr (p, '\n');
3081 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3083 if ((*i).substr (0,7) == "file://") {
3086 PBD::url_decode (p);
3088 // scan forward past three slashes
3090 string::size_type slashcnt = 0;
3091 string::size_type n = 0;
3092 string::iterator x = p.begin();
3094 while (slashcnt < 3 && x != p.end()) {
3097 } else if (slashcnt == 3) {
3104 if (slashcnt != 3 || x == p.end()) {
3105 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3109 paths.push_back (p.substr (n - 1));
3117 Editor::new_tempo_section ()
3123 Editor::map_transport_state ()
3125 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3127 if (_session && _session->transport_stopped()) {
3128 have_pending_keyboard_selection = false;
3131 update_loop_range_view (true);
3136 Editor::State::State (PublicEditor const * e)
3138 selection = new Selection (e);
3141 Editor::State::~State ()
3147 Editor::begin_reversible_command (string name)
3150 _session->begin_reversible_command (name);
3155 Editor::begin_reversible_command (GQuark q)
3158 _session->begin_reversible_command (q);
3163 Editor::commit_reversible_command ()
3166 _session->commit_reversible_command ();
3171 Editor::history_changed ()
3175 if (undo_action && _session) {
3176 if (_session->undo_depth() == 0) {
3179 label = string_compose(_("Undo (%1)"), _session->next_undo());
3181 undo_action->property_label() = label;
3184 if (redo_action && _session) {
3185 if (_session->redo_depth() == 0) {
3188 label = string_compose(_("Redo (%1)"), _session->next_redo());
3190 redo_action->property_label() = label;
3195 Editor::duplicate_dialog (bool with_dialog)
3199 if (mouse_mode == MouseRange) {
3200 if (selection->time.length() == 0) {
3205 RegionSelection rs = get_regions_from_selection_and_entered ();
3207 if (mouse_mode != MouseRange && rs.empty()) {
3213 ArdourDialog win (_("Duplicate"));
3214 Label label (_("Number of duplications:"));
3215 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3216 SpinButton spinner (adjustment, 0.0, 1);
3219 win.get_vbox()->set_spacing (12);
3220 win.get_vbox()->pack_start (hbox);
3221 hbox.set_border_width (6);
3222 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3224 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3225 place, visually. so do this by hand.
3228 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3229 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3230 spinner.grab_focus();
3236 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3237 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3238 win.set_default_response (RESPONSE_ACCEPT);
3240 win.set_position (WIN_POS_MOUSE);
3242 spinner.grab_focus ();
3244 switch (win.run ()) {
3245 case RESPONSE_ACCEPT:
3251 times = adjustment.get_value();
3254 if (mouse_mode == MouseRange) {
3255 duplicate_selection (times);
3257 duplicate_some_regions (rs, times);
3262 Editor::set_edit_mode (EditMode m)
3264 Config->set_edit_mode (m);
3268 Editor::cycle_edit_mode ()
3270 switch (Config->get_edit_mode()) {
3272 if (Profile->get_sae()) {
3273 Config->set_edit_mode (Lock);
3275 Config->set_edit_mode (Splice);
3279 Config->set_edit_mode (Lock);
3282 Config->set_edit_mode (Slide);
3288 Editor::edit_mode_selection_done ()
3290 string s = edit_mode_selector.get_active_text ();
3293 Config->set_edit_mode (string_to_edit_mode (s));
3298 Editor::snap_type_selection_done ()
3300 string choice = snap_type_selector.get_active_text();
3301 SnapType snaptype = SnapToBeat;
3303 if (choice == _("Beats/2")) {
3304 snaptype = SnapToBeatDiv2;
3305 } else if (choice == _("Beats/3")) {
3306 snaptype = SnapToBeatDiv3;
3307 } else if (choice == _("Beats/4")) {
3308 snaptype = SnapToBeatDiv4;
3309 } else if (choice == _("Beats/5")) {
3310 snaptype = SnapToBeatDiv5;
3311 } else if (choice == _("Beats/6")) {
3312 snaptype = SnapToBeatDiv6;
3313 } else if (choice == _("Beats/7")) {
3314 snaptype = SnapToBeatDiv7;
3315 } else if (choice == _("Beats/8")) {
3316 snaptype = SnapToBeatDiv8;
3317 } else if (choice == _("Beats/10")) {
3318 snaptype = SnapToBeatDiv10;
3319 } else if (choice == _("Beats/12")) {
3320 snaptype = SnapToBeatDiv12;
3321 } else if (choice == _("Beats/14")) {
3322 snaptype = SnapToBeatDiv14;
3323 } else if (choice == _("Beats/16")) {
3324 snaptype = SnapToBeatDiv16;
3325 } else if (choice == _("Beats/20")) {
3326 snaptype = SnapToBeatDiv20;
3327 } else if (choice == _("Beats/24")) {
3328 snaptype = SnapToBeatDiv24;
3329 } else if (choice == _("Beats/28")) {
3330 snaptype = SnapToBeatDiv28;
3331 } else if (choice == _("Beats/32")) {
3332 snaptype = SnapToBeatDiv32;
3333 } else if (choice == _("Beats")) {
3334 snaptype = SnapToBeat;
3335 } else if (choice == _("Bars")) {
3336 snaptype = SnapToBar;
3337 } else if (choice == _("Marks")) {
3338 snaptype = SnapToMark;
3339 } else if (choice == _("Region starts")) {
3340 snaptype = SnapToRegionStart;
3341 } else if (choice == _("Region ends")) {
3342 snaptype = SnapToRegionEnd;
3343 } else if (choice == _("Region bounds")) {
3344 snaptype = SnapToRegionBoundary;
3345 } else if (choice == _("Region syncs")) {
3346 snaptype = SnapToRegionSync;
3347 } else if (choice == _("CD Frames")) {
3348 snaptype = SnapToCDFrame;
3349 } else if (choice == _("Timecode Frames")) {
3350 snaptype = SnapToTimecodeFrame;
3351 } else if (choice == _("Timecode Seconds")) {
3352 snaptype = SnapToTimecodeSeconds;
3353 } else if (choice == _("Timecode Minutes")) {
3354 snaptype = SnapToTimecodeMinutes;
3355 } else if (choice == _("Seconds")) {
3356 snaptype = SnapToSeconds;
3357 } else if (choice == _("Minutes")) {
3358 snaptype = SnapToMinutes;
3361 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3363 ract->set_active ();
3368 Editor::snap_mode_selection_done ()
3370 string choice = snap_mode_selector.get_active_text();
3371 SnapMode mode = SnapNormal;
3373 if (choice == _("No Grid")) {
3375 } else if (choice == _("Grid")) {
3377 } else if (choice == _("Magnetic")) {
3378 mode = SnapMagnetic;
3381 RefPtr<RadioAction> ract = snap_mode_action (mode);
3384 ract->set_active (true);
3389 Editor::cycle_edit_point (bool with_marker)
3391 switch (_edit_point) {
3393 set_edit_point_preference (EditAtPlayhead);
3395 case EditAtPlayhead:
3397 set_edit_point_preference (EditAtSelectedMarker);
3399 set_edit_point_preference (EditAtMouse);
3402 case EditAtSelectedMarker:
3403 set_edit_point_preference (EditAtMouse);
3409 Editor::edit_point_selection_done ()
3411 string choice = edit_point_selector.get_active_text();
3412 EditPoint ep = EditAtSelectedMarker;
3414 if (choice == _("Marker")) {
3415 set_edit_point_preference (EditAtSelectedMarker);
3416 } else if (choice == _("Playhead")) {
3417 set_edit_point_preference (EditAtPlayhead);
3419 set_edit_point_preference (EditAtMouse);
3422 RefPtr<RadioAction> ract = edit_point_action (ep);
3425 ract->set_active (true);
3430 Editor::zoom_focus_selection_done ()
3432 string choice = zoom_focus_selector.get_active_text();
3433 ZoomFocus focus_type = ZoomFocusLeft;
3435 if (choice == _("Left")) {
3436 focus_type = ZoomFocusLeft;
3437 } else if (choice == _("Right")) {
3438 focus_type = ZoomFocusRight;
3439 } else if (choice == _("Center")) {
3440 focus_type = ZoomFocusCenter;
3441 } else if (choice == _("Playhead")) {
3442 focus_type = ZoomFocusPlayhead;
3443 } else if (choice == _("Mouse")) {
3444 focus_type = ZoomFocusMouse;
3445 } else if (choice == _("Edit point")) {
3446 focus_type = ZoomFocusEdit;
3449 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3452 ract->set_active ();
3457 Editor::edit_controls_button_release (GdkEventButton* ev)
3459 if (Keyboard::is_context_menu_event (ev)) {
3460 ARDOUR_UI::instance()->add_route (this);
3461 } else if (ev->button == 1) {
3462 selection->clear_tracks ();
3469 Editor::mouse_select_button_release (GdkEventButton* ev)
3471 /* this handles just right-clicks */
3473 if (ev->button != 3) {
3481 Editor::set_zoom_focus (ZoomFocus f)
3483 string str = zoom_focus_strings[(int)f];
3485 if (str != zoom_focus_selector.get_active_text()) {
3486 zoom_focus_selector.set_active_text (str);
3489 if (zoom_focus != f) {
3496 Editor::ensure_float (Window& win)
3498 win.set_transient_for (*this);
3502 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3504 /* recover or initialize pane positions. do this here rather than earlier because
3505 we don't want the positions to change the child allocations, which they seem to do.
3511 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3520 XMLNode* geometry = find_named_node (*node, "geometry");
3522 if (which == static_cast<Paned*> (&edit_pane)) {
3524 if (done & Horizontal) {
3528 if (geometry && (prop = geometry->property ("notebook-shrunk"))) {
3529 _notebook_shrunk = string_is_affirmative (prop->value ());
3532 if (geometry && (prop = geometry->property ("pre-maximal-horizontal-pane-position"))) {
3533 pre_maximal_horizontal_pane_position = atoi (prop->value ());
3536 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3537 /* initial allocation is 90% to canvas, 10% to notebook */
3538 pos = (int) floor (alloc.get_width() * 0.90f);
3539 snprintf (buf, sizeof(buf), "%d", pos);
3541 pos = atoi (prop->value());
3544 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3545 edit_pane.set_position (pos);
3546 if (pre_maximal_horizontal_pane_position == 0) {
3547 pre_maximal_horizontal_pane_position = pos;
3551 done = (Pane) (done | Horizontal);
3553 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3555 if (done & Vertical) {
3559 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3560 /* initial allocation is 90% to canvas, 10% to summary */
3561 pos = (int) floor (alloc.get_height() * 0.90f);
3562 snprintf (buf, sizeof(buf), "%d", pos);
3564 pos = atoi (prop->value());
3567 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3568 editor_summary_pane.set_position (pos);
3569 pre_maximal_vertical_pane_position = pos;
3572 done = (Pane) (done | Vertical);
3577 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3579 if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) {
3580 top_hbox.remove (toolbar_frame);
3585 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3587 if (toolbar_frame.get_parent() == 0) {
3588 top_hbox.pack_end (toolbar_frame);
3593 Editor::set_show_measures (bool yn)
3595 if (_show_measures != yn) {
3598 if ((_show_measures = yn) == true) {
3600 tempo_lines->show();
3608 Editor::toggle_follow_playhead ()
3610 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3612 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3613 set_follow_playhead (tact->get_active());
3617 /** @param yn true to follow playhead, otherwise false.
3618 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
3621 Editor::set_follow_playhead (bool yn, bool catch_up)
3623 if (_follow_playhead != yn) {
3624 if ((_follow_playhead = yn) == true && catch_up) {
3626 reset_x_origin_to_follow_playhead ();
3633 Editor::toggle_stationary_playhead ()
3635 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3637 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3638 set_stationary_playhead (tact->get_active());
3643 Editor::set_stationary_playhead (bool yn)
3645 if (_stationary_playhead != yn) {
3646 if ((_stationary_playhead = yn) == true) {
3648 // FIXME need a 3.0 equivalent of this 2.X call
3649 // update_current_screen ();
3656 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3658 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3660 xfade->set_active (!xfade->active());
3665 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3667 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3669 xfade->set_follow_overlap (!xfade->following_overlap());
3674 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3676 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3682 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3686 switch (cew.run ()) {
3687 case RESPONSE_ACCEPT:
3694 PropertyChange all_crossfade_properties;
3695 all_crossfade_properties.add (ARDOUR::Properties::active);
3696 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3697 xfade->PropertyChanged (all_crossfade_properties);
3701 Editor::playlist_selector () const
3703 return *_playlist_selector;
3707 Editor::get_grid_type_as_beats (bool& success, framepos_t position)
3711 switch (_snap_type) {
3716 case SnapToBeatDiv32:
3719 case SnapToBeatDiv28:
3722 case SnapToBeatDiv24:
3725 case SnapToBeatDiv20:
3728 case SnapToBeatDiv16:
3731 case SnapToBeatDiv14:
3734 case SnapToBeatDiv12:
3737 case SnapToBeatDiv10:
3740 case SnapToBeatDiv8:
3743 case SnapToBeatDiv7:
3746 case SnapToBeatDiv6:
3749 case SnapToBeatDiv5:
3752 case SnapToBeatDiv4:
3755 case SnapToBeatDiv3:
3758 case SnapToBeatDiv2:
3764 return _session->tempo_map().meter_at (position).beats_per_bar();
3769 case SnapToTimecodeFrame:
3770 case SnapToTimecodeSeconds:
3771 case SnapToTimecodeMinutes:
3774 case SnapToRegionStart:
3775 case SnapToRegionEnd:
3776 case SnapToRegionSync:
3777 case SnapToRegionBoundary:
3787 Editor::get_nudge_distance (framepos_t pos, framecnt_t& next)
3791 ret = nudge_clock->current_duration (pos);
3792 next = ret + 1; /* XXXX fix me */
3798 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3800 ArdourDialog dialog (_("Playlist Deletion"));
3801 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3802 "If it is kept, its audio files will not be cleaned.\n"
3803 "If it is deleted, audio files used by it alone will be cleaned."),
3806 dialog.set_position (WIN_POS_CENTER);
3807 dialog.get_vbox()->pack_start (label);
3811 dialog.add_button (_("Delete Playlist"), RESPONSE_ACCEPT);
3812 dialog.add_button (_("Keep Playlist"), RESPONSE_REJECT);
3813 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3815 switch (dialog.run ()) {
3816 case RESPONSE_ACCEPT:
3817 /* delete the playlist */
3821 case RESPONSE_REJECT:
3822 /* keep the playlist */
3834 Editor::audio_region_selection_covers (framepos_t where)
3836 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3837 if ((*a)->region()->covers (where)) {
3846 Editor::prepare_for_cleanup ()
3848 cut_buffer->clear_regions ();
3849 cut_buffer->clear_playlists ();
3851 selection->clear_regions ();
3852 selection->clear_playlists ();
3854 _regions->suspend_redisplay ();
3858 Editor::finish_cleanup ()
3860 _regions->resume_redisplay ();
3864 Editor::transport_loop_location()
3867 return _session->locations()->auto_loop_location();
3874 Editor::transport_punch_location()
3877 return _session->locations()->auto_punch_location();
3884 Editor::control_layout_scroll (GdkEventScroll* ev)
3886 if (Keyboard::some_magic_widget_has_focus()) {
3890 switch (ev->direction) {
3892 scroll_tracks_up_line ();
3896 case GDK_SCROLL_DOWN:
3897 scroll_tracks_down_line ();
3901 /* no left/right handling yet */
3909 Editor::session_state_saved (string)
3912 _snapshots->redisplay ();
3916 Editor::maximise_editing_space ()
3918 _mouse_mode_tearoff->set_visible (false);
3919 _tools_tearoff->set_visible (false);
3920 _zoom_tearoff->set_visible (false);
3922 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
3923 pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
3924 pre_maximal_editor_width = this->get_width ();
3925 pre_maximal_editor_height = this->get_height ();
3927 if (post_maximal_horizontal_pane_position == 0) {
3928 post_maximal_horizontal_pane_position = edit_pane.get_width();
3931 if (post_maximal_vertical_pane_position == 0) {
3932 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
3937 if (post_maximal_editor_width) {
3938 edit_pane.set_position (post_maximal_horizontal_pane_position -
3939 abs(post_maximal_editor_width - pre_maximal_editor_width));
3941 edit_pane.set_position (post_maximal_horizontal_pane_position);
3944 /* Hack: we must do this in an idle handler for it to work; see comment in
3945 restore_editing_space()
3948 Glib::signal_idle().connect (
3950 sigc::mem_fun (*this, &Editor::idle_reset_vertical_pane_position),
3951 post_maximal_vertical_pane_position
3955 if (Config->get_keep_tearoffs()) {
3956 _mouse_mode_tearoff->set_visible (true);
3957 _tools_tearoff->set_visible (true);
3958 if (Config->get_show_zoom_tools ()) {
3959 _zoom_tearoff->set_visible (true);
3966 Editor::idle_reset_vertical_pane_position (int p)
3968 editor_summary_pane.set_position (p);
3973 Editor::restore_editing_space ()
3975 // user changed width/height of panes during fullscreen
3977 if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
3978 post_maximal_horizontal_pane_position = edit_pane.get_position();
3981 if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
3982 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
3987 _mouse_mode_tearoff->set_visible (true);
3988 _tools_tearoff->set_visible (true);
3989 if (Config->get_show_zoom_tools ()) {
3990 _zoom_tearoff->set_visible (true);
3992 post_maximal_editor_width = this->get_width();
3993 post_maximal_editor_height = this->get_height();
3995 edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
3997 /* This is a bit of a hack, but it seems that if you set the vertical pane position
3998 here it gets reset to some wrong value after this method has finished. Doing
3999 the setup in an idle callback seems to work.
4001 Glib::signal_idle().connect (
4003 sigc::mem_fun (*this, &Editor::idle_reset_vertical_pane_position),
4004 pre_maximal_vertical_pane_position
4010 * Make new playlists for a given track and also any others that belong
4011 * to the same active route group with the `edit' property.
4016 Editor::new_playlists (TimeAxisView* v)
4018 begin_reversible_command (_("new playlists"));
4019 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4020 _session->playlists->get (playlists);
4021 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4022 commit_reversible_command ();
4026 * Use a copy of the current playlist for a given track and also any others that belong
4027 * to the same active route group with the `edit' property.
4032 Editor::copy_playlists (TimeAxisView* v)
4034 begin_reversible_command (_("copy playlists"));
4035 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4036 _session->playlists->get (playlists);
4037 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4038 commit_reversible_command ();
4041 /** Clear the current playlist for a given track and also any others that belong
4042 * to the same active route group with the `edit' property.
4047 Editor::clear_playlists (TimeAxisView* v)
4049 begin_reversible_command (_("clear playlists"));
4050 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4051 _session->playlists->get (playlists);
4052 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4053 commit_reversible_command ();
4057 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4059 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4063 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4065 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4069 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4071 atv.clear_playlist ();
4075 Editor::on_key_press_event (GdkEventKey* ev)
4077 return key_press_focus_accelerator_handler (*this, ev);
4081 Editor::on_key_release_event (GdkEventKey* ev)
4083 return Gtk::Window::on_key_release_event (ev);
4084 // return key_press_focus_accelerator_handler (*this, ev);
4087 /** Queue up a change to the viewport x origin.
4088 * @param frame New x origin.
4091 Editor::reset_x_origin (framepos_t frame)
4093 queue_visual_change (frame);
4097 Editor::reset_y_origin (double y)
4099 queue_visual_change_y (y);
4103 Editor::reset_zoom (double fpu)
4105 queue_visual_change (fpu);
4109 Editor::reposition_and_zoom (framepos_t frame, double fpu)
4111 reset_x_origin (frame);
4114 if (!no_save_visual) {
4115 undo_visual_stack.push_back (current_visual_state(false));
4119 Editor::VisualState::VisualState ()
4120 : gui_state (new GUIObjectState)
4124 Editor::VisualState::~VisualState ()
4129 Editor::VisualState*
4130 Editor::current_visual_state (bool with_tracks)
4132 VisualState* vs = new VisualState;
4133 vs->y_position = vertical_adjustment.get_value();
4134 vs->frames_per_unit = frames_per_unit;
4135 vs->leftmost_frame = leftmost_frame;
4136 vs->zoom_focus = zoom_focus;
4139 *(vs->gui_state) = *ARDOUR_UI::instance()->gui_object_state;
4146 Editor::undo_visual_state ()
4148 if (undo_visual_stack.empty()) {
4152 redo_visual_stack.push_back (current_visual_state());
4154 VisualState* vs = undo_visual_stack.back();
4155 undo_visual_stack.pop_back();
4156 use_visual_state (*vs);
4160 Editor::redo_visual_state ()
4162 if (redo_visual_stack.empty()) {
4166 undo_visual_stack.push_back (current_visual_state());
4168 VisualState* vs = redo_visual_stack.back();
4169 redo_visual_stack.pop_back();
4170 use_visual_state (*vs);
4174 Editor::swap_visual_state ()
4176 if (undo_visual_stack.empty()) {
4177 redo_visual_state ();
4179 undo_visual_state ();
4184 Editor::use_visual_state (VisualState& vs)
4186 no_save_visual = true;
4188 _routes->suspend_redisplay ();
4190 vertical_adjustment.set_value (vs.y_position);
4192 set_zoom_focus (vs.zoom_focus);
4193 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4195 *ARDOUR_UI::instance()->gui_object_state = *vs.gui_state;
4197 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4198 (*i)->reset_visual_state ();
4201 _routes->update_visibility ();
4202 _routes->resume_redisplay ();
4204 no_save_visual = false;
4208 Editor::set_frames_per_unit (double fpu)
4210 /* this is the core function that controls the zoom level of the canvas. it is called
4211 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4214 if (fpu == frames_per_unit) {
4223 /* don't allow zooms that fit more than the maximum number
4224 of frames into an 800 pixel wide space.
4227 if (max_framepos / fpu < 800.0) {
4232 tempo_lines->tempo_map_changed();
4234 frames_per_unit = fpu;
4239 Editor::post_zoom ()
4241 // convert fpu to frame count
4243 framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width);
4245 if (frames_per_unit != zoom_range_clock->current_duration()) {
4246 zoom_range_clock->set (frames);
4249 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4250 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4251 (*i)->reshow_selection (selection->time);
4255 ZoomChanged (); /* EMIT_SIGNAL */
4257 //reset_scrolling_region ();
4259 if (playhead_cursor) {
4260 playhead_cursor->set_position (playhead_cursor->current_frame);
4263 refresh_location_display();
4264 _summary->set_overlays_dirty ();
4266 update_marker_labels ();
4272 Editor::queue_visual_change (framepos_t where)
4274 pending_visual_change.add (VisualChange::TimeOrigin);
4275 pending_visual_change.time_origin = where;
4276 ensure_visual_change_idle_handler ();
4280 Editor::queue_visual_change (double fpu)
4282 pending_visual_change.add (VisualChange::ZoomLevel);
4283 pending_visual_change.frames_per_unit = fpu;
4285 ensure_visual_change_idle_handler ();
4289 Editor::queue_visual_change_y (double y)
4291 pending_visual_change.add (VisualChange::YOrigin);
4292 pending_visual_change.y_origin = y;
4294 ensure_visual_change_idle_handler ();
4298 Editor::ensure_visual_change_idle_handler ()
4300 if (pending_visual_change.idle_handler_id < 0) {
4301 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4306 Editor::_idle_visual_changer (void* arg)
4308 return static_cast<Editor*>(arg)->idle_visual_changer ();
4312 Editor::idle_visual_changer ()
4314 VisualChange::Type p = pending_visual_change.pending;
4315 pending_visual_change.pending = (VisualChange::Type) 0;
4317 double const last_time_origin = horizontal_position ();
4319 if (p & VisualChange::TimeOrigin) {
4320 /* This is a bit of a hack, but set_frames_per_unit
4321 below will (if called) end up with the
4322 CrossfadeViews looking at Editor::leftmost_frame,
4323 and if we're changing origin and zoom in the same
4324 operation it will be the wrong value unless we
4328 leftmost_frame = pending_visual_change.time_origin;
4331 if (p & VisualChange::ZoomLevel) {
4332 set_frames_per_unit (pending_visual_change.frames_per_unit);
4334 compute_fixed_ruler_scale ();
4335 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4336 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4337 update_tempo_based_rulers ();
4339 if (p & VisualChange::TimeOrigin) {
4340 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4342 if (p & VisualChange::YOrigin) {
4343 vertical_adjustment.set_value (pending_visual_change.y_origin);
4346 if (last_time_origin == horizontal_position ()) {
4347 /* changed signal not emitted */
4348 update_fixed_rulers ();
4349 redisplay_tempo (true);
4352 _summary->set_overlays_dirty ();
4354 pending_visual_change.idle_handler_id = -1;
4355 return 0; /* this is always a one-shot call */
4358 struct EditorOrderTimeAxisSorter {
4359 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4360 return a->order () < b->order ();
4365 Editor::sort_track_selection (TrackViewList* sel)
4367 EditorOrderTimeAxisSorter cmp;
4372 selection->tracks.sort (cmp);
4377 Editor::get_preferred_edit_position (bool ignore_playhead)
4380 framepos_t where = 0;
4381 EditPoint ep = _edit_point;
4383 if (entered_marker) {
4384 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position()));
4385 return entered_marker->position();
4388 if (ignore_playhead && ep == EditAtPlayhead) {
4389 ep = EditAtSelectedMarker;
4393 case EditAtPlayhead:
4394 where = _session->audible_frame();
4395 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where));
4398 case EditAtSelectedMarker:
4399 if (!selection->markers.empty()) {
4401 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4404 where = loc->start();
4408 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where));
4416 if (!mouse_frame (where, ignored)) {
4417 /* XXX not right but what can we do ? */
4421 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where));
4429 Editor::set_loop_range (framepos_t start, framepos_t end, string cmd)
4431 if (!_session) return;
4433 begin_reversible_command (cmd);
4437 if ((tll = transport_loop_location()) == 0) {
4438 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop);
4439 XMLNode &before = _session->locations()->get_state();
4440 _session->locations()->add (loc, true);
4441 _session->set_auto_loop_location (loc);
4442 XMLNode &after = _session->locations()->get_state();
4443 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4445 XMLNode &before = tll->get_state();
4446 tll->set_hidden (false, this);
4447 tll->set (start, end);
4448 XMLNode &after = tll->get_state();
4449 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4452 commit_reversible_command ();
4456 Editor::set_punch_range (framepos_t start, framepos_t end, string cmd)
4458 if (!_session) return;
4460 begin_reversible_command (cmd);
4464 if ((tpl = transport_punch_location()) == 0) {
4465 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoPunch);
4466 XMLNode &before = _session->locations()->get_state();
4467 _session->locations()->add (loc, true);
4468 _session->set_auto_loop_location (loc);
4469 XMLNode &after = _session->locations()->get_state();
4470 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4473 XMLNode &before = tpl->get_state();
4474 tpl->set_hidden (false, this);
4475 tpl->set (start, end);
4476 XMLNode &after = tpl->get_state();
4477 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4480 commit_reversible_command ();
4483 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4484 * @param rs List to which found regions are added.
4485 * @param where Time to look at.
4486 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4489 Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4491 const TrackViewList* tracks;
4494 tracks = &track_views;
4499 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4501 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4504 boost::shared_ptr<Track> tr;
4505 boost::shared_ptr<Playlist> pl;
4507 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4509 Playlist::RegionList* regions = pl->regions_at (
4510 (framepos_t) floor ( (double) where * tr->speed()));
4512 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4513 RegionView* rv = rtv->view()->find_view (*i);
4526 Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4528 const TrackViewList* tracks;
4531 tracks = &track_views;
4536 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4537 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4539 boost::shared_ptr<Track> tr;
4540 boost::shared_ptr<Playlist> pl;
4542 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4544 Playlist::RegionList* regions = pl->regions_touched (
4545 (framepos_t) floor ( (double)where * tr->speed()), max_framepos);
4547 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4549 RegionView* rv = rtv->view()->find_view (*i);
4562 /** Start with regions that are selected. Then add equivalent regions
4563 * on tracks in the same active edit-enabled route group as any of
4564 * the regions that we started with.
4568 Editor::get_regions_from_selection ()
4570 return get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
4573 /** Get regions using the following method:
4575 * Make an initial region list using the selected regions, unless
4576 * the edit point is `mouse' and the mouse is over an unselected
4577 * region. In this case, start with just that region.
4579 * Then, make an initial track list of the tracks that these
4580 * regions are on, and if the edit point is not `mouse', add the
4583 * Look at this track list and add any other tracks that are on the
4584 * same active edit-enabled route group as one of the initial tracks.
4586 * Finally take the initial region list and add any regions that are
4587 * under the edit point on one of the tracks on the track list to get
4588 * the returned region list.
4590 * The rationale here is that the mouse edit point is special in that
4591 * its position describes both a time and a track; the other edit
4592 * modes only describe a time. Hence if the edit point is `mouse' we
4593 * ignore selected tracks, as we assume the user means something by
4594 * pointing at a particular track. Also in this case we take note of
4595 * the region directly under the edit point, as there is always just one
4596 * (rather than possibly several with non-mouse edit points).
4600 Editor::get_regions_from_selection_and_edit_point ()
4602 RegionSelection regions;
4604 if (_edit_point == EditAtMouse && entered_regionview && !selection->regions.contains (entered_regionview)) {
4605 regions.add (entered_regionview);
4607 regions = selection->regions;
4610 TrackViewList tracks;
4612 if (_edit_point != EditAtMouse) {
4613 tracks = selection->tracks;
4616 /* Add any other tracks that have regions that are in the same
4617 edit-activated route group as one of our regions.
4619 for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) {
4621 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4623 if (g && g->is_active() && g->is_edit()) {
4624 tracks.add (axis_views_from_routes (g->route_list()));
4628 if (!tracks.empty()) {
4629 /* now find regions that are at the edit position on those tracks */
4630 framepos_t const where = get_preferred_edit_position ();
4631 get_regions_at (regions, where, tracks);
4637 /** Start with regions that are selected, or the entered regionview if none are selected.
4638 * Then add equivalent regions on tracks in the same active edit-enabled route group as any
4639 * of the regions that we started with.
4643 Editor::get_regions_from_selection_and_entered ()
4645 RegionSelection regions = selection->regions;
4647 if (regions.empty() && entered_regionview) {
4648 regions.add (entered_regionview);
4651 return get_equivalent_regions (regions, ARDOUR::Properties::edit.property_id);
4655 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4657 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4659 RouteTimeAxisView* tatv;
4661 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4663 boost::shared_ptr<Playlist> pl;
4664 vector<boost::shared_ptr<Region> > results;
4666 boost::shared_ptr<Track> tr;
4668 if ((tr = tatv->track()) == 0) {
4673 if ((pl = (tr->playlist())) != 0) {
4674 pl->get_region_list_equivalent_regions (region, results);
4677 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4678 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4679 regions.push_back (marv);
4688 Editor::show_rhythm_ferret ()
4690 if (rhythm_ferret == 0) {
4691 rhythm_ferret = new RhythmFerret(*this);
4694 rhythm_ferret->set_session (_session);
4695 rhythm_ferret->show ();
4696 rhythm_ferret->present ();
4700 Editor::first_idle ()
4702 MessageDialog* dialog = 0;
4704 if (track_views.size() > 1) {
4705 dialog = new MessageDialog (
4707 string_compose (_("Please wait while %1 loads visual data."), PROGRAM_NAME),
4711 ARDOUR_UI::instance()->flush_pending ();
4714 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4718 // first idle adds route children (automation tracks), so we need to redisplay here
4719 _routes->redisplay ();
4726 Editor::_idle_resize (gpointer arg)
4728 return ((Editor*)arg)->idle_resize ();
4732 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4734 if (resize_idle_id < 0) {
4735 resize_idle_id = g_idle_add (_idle_resize, this);
4736 _pending_resize_amount = 0;
4739 /* make a note of the smallest resulting height, so that we can clamp the
4740 lower limit at TimeAxisView::hSmall */
4742 int32_t min_resulting = INT32_MAX;
4744 _pending_resize_amount += h;
4745 _pending_resize_view = view;
4747 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4749 if (selection->tracks.contains (_pending_resize_view)) {
4750 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4751 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4755 if (min_resulting < 0) {
4760 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4761 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4765 /** Handle pending resizing of tracks */
4767 Editor::idle_resize ()
4769 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4771 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4772 selection->tracks.contains (_pending_resize_view)) {
4774 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4775 if (*i != _pending_resize_view) {
4776 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4781 _pending_resize_amount = 0;
4783 _group_tabs->set_dirty ();
4784 resize_idle_id = -1;
4792 ENSURE_GUI_THREAD (*this, &Editor::located);
4794 playhead_cursor->set_position (_session->audible_frame ());
4795 if (_follow_playhead && !_pending_initial_locate) {
4796 reset_x_origin_to_follow_playhead ();
4799 _pending_locate_request = false;
4800 _pending_initial_locate = false;
4804 Editor::region_view_added (RegionView *)
4806 _summary->set_dirty ();
4810 Editor::region_view_removed ()
4812 _summary->set_dirty ();
4816 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4818 TrackViewList::const_iterator j = track_views.begin ();
4819 while (j != track_views.end()) {
4820 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4821 if (rtv && rtv->route() == r) {
4832 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4836 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4837 TimeAxisView* tv = axis_view_from_route (*i);
4848 Editor::handle_new_route (RouteList& routes)
4850 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4852 RouteTimeAxisView *rtv;
4853 list<RouteTimeAxisView*> new_views;
4855 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4856 boost::shared_ptr<Route> route = (*x);
4858 if (route->is_hidden() || route->is_monitor()) {
4862 DataType dt = route->input()->default_type();
4864 if (dt == ARDOUR::DataType::AUDIO) {
4865 rtv = new AudioTimeAxisView (*this, _session, *track_canvas);
4866 rtv->set_route (route);
4867 } else if (dt == ARDOUR::DataType::MIDI) {
4868 rtv = new MidiTimeAxisView (*this, _session, *track_canvas);
4869 rtv->set_route (route);
4871 throw unknown_type();
4874 new_views.push_back (rtv);
4875 track_views.push_back (rtv);
4877 rtv->effective_gain_display ();
4879 if (internal_editing()) {
4880 rtv->enter_internal_edit_mode ();
4882 rtv->leave_internal_edit_mode ();
4885 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4886 rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed));
4889 _routes->routes_added (new_views);
4890 _summary->routes_added (new_views);
4892 if (show_editor_mixer_when_tracks_arrive) {
4893 show_editor_mixer (true);
4896 editor_list_button.set_sensitive (true);
4900 Editor::timeaxisview_deleted (TimeAxisView *tv)
4902 if (_session && _session->deletion_in_progress()) {
4903 /* the situation is under control */
4907 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
4909 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
4911 _routes->route_removed (tv);
4913 if (tv == entered_track) {
4917 TimeAxisView::Children c = tv->get_child_list ();
4918 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
4919 if (entered_track == i->get()) {
4924 /* remove it from the list of track views */
4926 TrackViewList::iterator i;
4928 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
4929 i = track_views.erase (i);
4932 /* update whatever the current mixer strip is displaying, if revelant */
4934 boost::shared_ptr<Route> route;
4937 route = rtav->route ();
4940 if (current_mixer_strip && current_mixer_strip->route() == route) {
4942 TimeAxisView* next_tv;
4944 if (track_views.empty()) {
4946 } else if (i == track_views.end()) {
4947 next_tv = track_views.front();
4954 set_selected_mixer_strip (*next_tv);
4956 /* make the editor mixer strip go away setting the
4957 * button to inactive (which also unticks the menu option)
4960 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
4966 Editor::hide_track_in_display (TimeAxisView* tv, bool apply_to_selection)
4968 if (apply_to_selection) {
4969 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ) {
4971 TrackSelection::iterator j = i;
4974 hide_track_in_display (*i, false);
4979 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
4981 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
4982 // this will hide the mixer strip
4983 set_selected_mixer_strip (*tv);
4986 _routes->hide_track_in_display (*tv);
4991 Editor::sync_track_view_list_and_routes ()
4993 track_views = TrackViewList (_routes->views ());
4995 _summary->set_dirty ();
4996 _group_tabs->set_dirty ();
4998 return false; // do not call again (until needed)
5002 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
5004 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5009 /** Find a RouteTimeAxisView by the ID of its route */
5011 Editor::get_route_view_by_route_id (const PBD::ID& id) const
5013 RouteTimeAxisView* v;
5015 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5016 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5017 if(v->route()->id() == id) {
5027 Editor::fit_route_group (RouteGroup *g)
5029 TrackViewList ts = axis_views_from_routes (g->route_list ());
5034 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5036 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5039 _session->cancel_audition ();
5043 if (_session->is_auditioning()) {
5044 _session->cancel_audition ();
5045 if (r == last_audition_region) {
5050 _session->audition_region (r);
5051 last_audition_region = r;
5056 Editor::hide_a_region (boost::shared_ptr<Region> r)
5058 r->set_hidden (true);
5062 Editor::show_a_region (boost::shared_ptr<Region> r)
5064 r->set_hidden (false);
5068 Editor::audition_region_from_region_list ()
5070 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5074 Editor::hide_region_from_region_list ()
5076 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5080 Editor::show_region_in_region_list ()
5082 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5086 Editor::step_edit_status_change (bool yn)
5089 start_step_editing ();
5091 stop_step_editing ();
5096 Editor::start_step_editing ()
5098 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5102 Editor::stop_step_editing ()
5104 step_edit_connection.disconnect ();
5108 Editor::check_step_edit ()
5110 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5111 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5113 mtv->check_step_edit ();
5117 return true; // do it again, till we stop
5121 Editor::scroll_press (Direction dir)
5123 ++_scroll_callbacks;
5125 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5126 /* delay the first auto-repeat */
5132 scroll_backward (1);
5140 scroll_tracks_up_line ();
5144 scroll_tracks_down_line ();
5148 /* do hacky auto-repeat */
5149 if (!_scroll_connection.connected ()) {
5151 _scroll_connection = Glib::signal_timeout().connect (
5152 sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), dir), 100
5155 _scroll_callbacks = 0;
5162 Editor::scroll_release ()
5164 _scroll_connection.disconnect ();
5167 /** Queue a change for the Editor viewport x origin to follow the playhead */
5169 Editor::reset_x_origin_to_follow_playhead ()
5171 framepos_t const frame = playhead_cursor->current_frame;
5173 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5175 if (_session->transport_speed() < 0) {
5177 if (frame > (current_page_frames() / 2)) {
5178 center_screen (frame-(current_page_frames()/2));
5180 center_screen (current_page_frames()/2);
5185 if (frame < leftmost_frame) {
5188 if (_session->transport_rolling()) {
5189 /* rolling; end up with the playhead at the right of the page */
5190 l = frame - current_page_frames ();
5192 /* not rolling: end up with the playhead 3/4 of the way along the page */
5193 l = frame - (3 * current_page_frames() / 4);
5200 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5203 if (_session->transport_rolling()) {
5204 /* rolling: end up with the playhead on the left of the page */
5205 center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5207 /* not rolling: end up with the playhead 1/4 of the way along the page */
5208 center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5216 Editor::super_rapid_screen_update ()
5218 if (!_session || !_session->engine().running()) {
5222 /* METERING / MIXER STRIPS */
5224 /* update track meters, if required */
5225 if (is_mapped() && meters_running) {
5226 RouteTimeAxisView* rtv;
5227 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5228 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5229 rtv->fast_update ();
5234 /* and any current mixer strip */
5235 if (current_mixer_strip) {
5236 current_mixer_strip->fast_update ();
5239 /* PLAYHEAD AND VIEWPORT */
5241 framepos_t const frame = _session->audible_frame();
5243 /* There are a few reasons why we might not update the playhead / viewport stuff:
5245 * 1. we don't update things when there's a pending locate request, otherwise
5246 * when the editor requests a locate there is a chance that this method
5247 * will move the playhead before the locate request is processed, causing
5249 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5250 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5253 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5255 last_update_frame = frame;
5257 if (!_dragging_playhead) {
5258 playhead_cursor->set_position (frame);
5261 if (!_stationary_playhead) {
5263 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5264 reset_x_origin_to_follow_playhead ();
5269 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5273 // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5274 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5275 if (target <= 0.0) {
5278 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5279 target = (target * 0.15) + (current * 0.85);
5285 set_horizontal_position (current);
5294 Editor::session_going_away ()
5296 _have_idled = false;
5298 _session_connections.drop_connections ();
5300 super_rapid_screen_update_connection.disconnect ();
5302 selection->clear ();
5303 cut_buffer->clear ();
5305 clicked_regionview = 0;
5306 clicked_axisview = 0;
5307 clicked_routeview = 0;
5308 clicked_crossfadeview = 0;
5309 entered_regionview = 0;
5311 last_update_frame = 0;
5314 playhead_cursor->canvas_item.hide ();
5316 /* rip everything out of the list displays */
5320 _route_groups->clear ();
5322 /* do this first so that deleting a track doesn't reset cms to null
5323 and thus cause a leak.
5326 if (current_mixer_strip) {
5327 if (current_mixer_strip->get_parent() != 0) {
5328 global_hpacker.remove (*current_mixer_strip);
5330 delete current_mixer_strip;
5331 current_mixer_strip = 0;
5334 /* delete all trackviews */
5336 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5339 track_views.clear ();
5341 zoom_range_clock->set_session (0);
5342 nudge_clock->set_session (0);
5344 editor_list_button.set_active(false);
5345 editor_list_button.set_sensitive(false);
5347 /* clear tempo/meter rulers */
5348 remove_metric_marks ();
5350 clear_marker_display ();
5352 delete current_bbt_points;
5353 current_bbt_points = 0;
5355 /* get rid of any existing editor mixer strip */
5357 WindowTitle title(Glib::get_application_name());
5358 title += _("Editor");
5360 set_title (title.get_string());
5362 SessionHandlePtr::session_going_away ();
5367 Editor::show_editor_list (bool yn)
5370 _the_notebook.show ();
5372 _the_notebook.hide ();
5377 Editor::change_region_layering_order ()
5379 framepos_t const position = get_preferred_edit_position ();
5381 if (!clicked_routeview) {
5382 if (layering_order_editor) {
5383 layering_order_editor->hide ();
5388 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
5394 boost::shared_ptr<Playlist> pl = track->playlist();
5400 if (layering_order_editor == 0) {
5401 layering_order_editor = new RegionLayeringOrderEditor(*this);
5404 layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position);
5405 layering_order_editor->maybe_present ();
5409 Editor::update_region_layering_order_editor ()
5411 if (layering_order_editor && layering_order_editor->is_visible ()) {
5412 change_region_layering_order ();
5417 Editor::setup_fade_images ()
5419 _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear")));
5420 _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut")));
5421 _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut")));
5422 _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut")));
5423 _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut")));
5425 _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear")));
5426 _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut")));
5427 _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut")));
5428 _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut")));
5429 _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut")));
5433 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
5435 Editor::action_menu_item (std::string const & name)
5437 Glib::RefPtr<Action> a = editor_actions->get_action (name);
5440 return *manage (a->create_menu_item ());
5444 Editor::resize_text_widgets ()
5446 set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_FUDGE+10, 15);
5447 set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_FUDGE+10, 15);
5448 set_size_request_to_display_given_text (snap_type_selector, snap_type_strings, COMBO_FUDGE+10, 15);
5449 set_size_request_to_display_given_text (snap_mode_selector, snap_mode_strings, COMBO_FUDGE+10, 15);
5450 set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_FUDGE+10, 15);
5454 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
5456 EventBox* b = manage (new EventBox);
5457 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
5458 Label* l = manage (new Label (name));
5462 _the_notebook.append_page (widget, *b);
5466 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
5468 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
5469 _the_notebook.set_current_page (_the_notebook.page_num (*page));
5472 if (ev->type == GDK_2BUTTON_PRESS) {
5474 /* double-click on a notebook tab shrinks or expands the notebook */
5476 if (_notebook_shrunk) {
5477 edit_pane.set_position (pre_maximal_horizontal_pane_position);
5478 _notebook_shrunk = false;
5480 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
5481 edit_pane.set_position (edit_pane.get_position() + page->get_width());
5482 _notebook_shrunk = true;
5490 Editor::popup_control_point_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
5492 using namespace Menu_Helpers;
5494 MenuList& items = _control_point_context_menu.items ();
5497 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun (*this, &Editor::edit_control_point), item)));
5498 items.push_back (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun (*this, &Editor::remove_control_point), item)));
5499 if (!can_remove_control_point (item)) {
5500 items.back().set_sensitive (false);
5503 _control_point_context_menu.popup (event->button.button, event->button.time);