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, 0, AudioClock::BBT);
1219 nudge_clock->set (_session->frame_rate() * 5, true, 0, AudioClock::Timecode); // default of 5 seconds
1222 playhead_cursor->canvas_item.show ();
1224 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1225 Config->map_parameters (pc);
1226 _session->config.map_parameters (pc);
1228 restore_ruler_visibility ();
1229 //tempo_map_changed (PropertyChange (0));
1230 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1232 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1233 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1236 super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
1237 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1240 switch (_snap_type) {
1241 case SnapToRegionStart:
1242 case SnapToRegionEnd:
1243 case SnapToRegionSync:
1244 case SnapToRegionBoundary:
1245 build_region_boundary_cache ();
1252 /* register for undo history */
1253 _session->register_with_memento_command_factory(id(), this);
1255 ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated));
1257 start_updating_meters ();
1261 Editor::action_pre_activated (Glib::RefPtr<Action> const & a)
1263 if (a->get_name() == "RegionMenu") {
1264 /* When the main menu's region menu is opened, we setup the actions so that they look right
1265 in the menu. I can't find a way of getting a signal when this menu is subsequently closed,
1266 so we resensitize all region actions when the entered regionview or the region selection
1267 changes. HOWEVER we can't always resensitize on entered_regionview change because that
1268 happens after the region context menu is opened. So we set a flag here, too.
1272 sensitize_the_right_region_actions ();
1273 _last_region_menu_was_main = true;
1277 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1279 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1281 using namespace Menu_Helpers;
1282 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1285 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1289 MenuList& items (fade_context_menu.items());
1293 switch (item_type) {
1295 case FadeInHandleItem:
1296 if (arv->audio_region()->fade_in_active()) {
1297 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1299 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1302 items.push_back (SeparatorElem());
1304 if (Profile->get_sae()) {
1306 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)));
1307 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)));
1314 *_fade_in_images[FadeLinear],
1315 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)
1319 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1324 *_fade_in_images[FadeFast],
1325 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)
1328 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1333 *_fade_in_images[FadeLogB],
1334 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogB)
1337 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1342 *_fade_in_images[FadeLogA],
1343 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogA)
1346 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1351 *_fade_in_images[FadeSlow],
1352 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow)
1355 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1361 case FadeOutHandleItem:
1362 if (arv->audio_region()->fade_out_active()) {
1363 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1365 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1368 items.push_back (SeparatorElem());
1370 if (Profile->get_sae()) {
1371 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)));
1372 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)));
1378 *_fade_out_images[FadeLinear],
1379 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)
1383 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1388 *_fade_out_images[FadeFast],
1389 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)
1392 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1397 *_fade_out_images[FadeLogB],
1398 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogA)
1401 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1406 *_fade_out_images[FadeLogA],
1407 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogB)
1410 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1415 *_fade_out_images[FadeSlow],
1416 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast)
1419 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1425 fatal << _("programming error: ")
1426 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1431 fade_context_menu.popup (button, time);
1435 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1437 using namespace Menu_Helpers;
1438 Menu* (Editor::*build_menu_function)();
1441 switch (item_type) {
1443 case RegionViewName:
1444 case RegionViewNameHighlight:
1445 case LeftFrameHandle:
1446 case RightFrameHandle:
1447 if (with_selection) {
1448 build_menu_function = &Editor::build_track_selection_context_menu;
1450 build_menu_function = &Editor::build_track_region_context_menu;
1455 if (with_selection) {
1456 build_menu_function = &Editor::build_track_selection_context_menu;
1458 build_menu_function = &Editor::build_track_context_menu;
1462 case CrossfadeViewItem:
1463 build_menu_function = &Editor::build_track_crossfade_context_menu;
1467 if (clicked_routeview->track()) {
1468 build_menu_function = &Editor::build_track_context_menu;
1470 build_menu_function = &Editor::build_track_bus_context_menu;
1475 /* probably shouldn't happen but if it does, we don't care */
1479 menu = (this->*build_menu_function)();
1480 menu->set_name ("ArdourContextMenu");
1482 /* now handle specific situations */
1484 switch (item_type) {
1486 case RegionViewName:
1487 case RegionViewNameHighlight:
1488 case LeftFrameHandle:
1489 case RightFrameHandle:
1490 if (!with_selection) {
1491 if (region_edit_menu_split_item) {
1492 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1493 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1495 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1498 if (region_edit_menu_split_multichannel_item) {
1499 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1500 region_edit_menu_split_multichannel_item->set_sensitive (true);
1502 region_edit_menu_split_multichannel_item->set_sensitive (false);
1511 case CrossfadeViewItem:
1518 /* probably shouldn't happen but if it does, we don't care */
1522 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1524 /* Bounce to disk */
1526 using namespace Menu_Helpers;
1527 MenuList& edit_items = menu->items();
1529 edit_items.push_back (SeparatorElem());
1531 switch (clicked_routeview->audio_track()->freeze_state()) {
1532 case AudioTrack::NoFreeze:
1533 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1536 case AudioTrack::Frozen:
1537 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1540 case AudioTrack::UnFrozen:
1541 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1547 if (item_type == StreamItem && clicked_routeview) {
1548 clicked_routeview->build_underlay_menu(menu);
1551 /* When the region menu is opened, we setup the actions so that they look right
1554 sensitize_the_right_region_actions ();
1555 _last_region_menu_was_main = false;
1557 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1558 menu->popup (button, time);
1562 Editor::build_track_context_menu ()
1564 using namespace Menu_Helpers;
1566 MenuList& edit_items = track_context_menu.items();
1569 add_dstream_context_items (edit_items);
1570 return &track_context_menu;
1574 Editor::build_track_bus_context_menu ()
1576 using namespace Menu_Helpers;
1578 MenuList& edit_items = track_context_menu.items();
1581 add_bus_context_items (edit_items);
1582 return &track_context_menu;
1586 Editor::build_track_region_context_menu ()
1588 using namespace Menu_Helpers;
1589 MenuList& edit_items = track_region_context_menu.items();
1592 /* we've just cleared the track region context menu, so the menu that these
1593 two items were on will have disappeared; stop them dangling.
1595 region_edit_menu_split_item = 0;
1596 region_edit_menu_split_multichannel_item = 0;
1598 /* we might try to use items that are currently attached to a crossfade menu,
1601 track_crossfade_context_menu.items().clear ();
1603 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1606 boost::shared_ptr<Track> tr;
1607 boost::shared_ptr<Playlist> pl;
1609 if ((tr = rtv->track())) {
1610 add_region_context_items (edit_items, tr);
1614 add_dstream_context_items (edit_items);
1616 return &track_region_context_menu;
1620 Editor::build_track_crossfade_context_menu ()
1622 using namespace Menu_Helpers;
1623 MenuList& edit_items = track_crossfade_context_menu.items();
1624 edit_items.clear ();
1626 /* we might try to use items that are currently attached to a crossfade menu,
1629 track_region_context_menu.items().clear ();
1631 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1634 boost::shared_ptr<Track> tr;
1635 boost::shared_ptr<Playlist> pl;
1636 boost::shared_ptr<AudioPlaylist> apl;
1638 if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1640 AudioPlaylist::Crossfades xfades;
1644 /* The xfade menu is a bit of a special case, as we always use the mouse position
1645 to decide whether or not to display it (rather than the edit point). No particularly
1646 strong reasons for this, other than it is a bit surprising to right-click on a xfade
1649 mouse_frame (where, ignored);
1650 apl->crossfades_at (where, xfades);
1652 bool const many = xfades.size() > 1;
1654 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1655 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1658 add_region_context_items (edit_items, tr);
1662 add_dstream_context_items (edit_items);
1664 return &track_crossfade_context_menu;
1668 Editor::analyze_region_selection ()
1670 if (analysis_window == 0) {
1671 analysis_window = new AnalysisWindow();
1674 analysis_window->set_session(_session);
1676 analysis_window->show_all();
1679 analysis_window->set_regionmode();
1680 analysis_window->analyze();
1682 analysis_window->present();
1686 Editor::analyze_range_selection()
1688 if (analysis_window == 0) {
1689 analysis_window = new AnalysisWindow();
1692 analysis_window->set_session(_session);
1694 analysis_window->show_all();
1697 analysis_window->set_rangemode();
1698 analysis_window->analyze();
1700 analysis_window->present();
1704 Editor::build_track_selection_context_menu ()
1706 using namespace Menu_Helpers;
1707 MenuList& edit_items = track_selection_context_menu.items();
1708 edit_items.clear ();
1710 add_selection_context_items (edit_items);
1711 // edit_items.push_back (SeparatorElem());
1712 // add_dstream_context_items (edit_items);
1714 return &track_selection_context_menu;
1717 /** Add context menu items relevant to crossfades.
1718 * @param edit_items List to add the items to.
1721 Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1723 using namespace Menu_Helpers;
1724 Menu *xfade_menu = manage (new Menu);
1725 MenuList& items = xfade_menu->items();
1726 xfade_menu->set_name ("ArdourContextMenu");
1729 if (xfade->active()) {
1735 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1736 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1738 if (xfade->can_follow_overlap()) {
1740 if (xfade->following_overlap()) {
1741 str = _("Convert to Short");
1743 str = _("Convert to Full");
1746 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1750 str = xfade->out()->name();
1752 str += xfade->in()->name();
1754 str = _("Crossfade");
1757 edit_items.push_back (MenuElem (str, *xfade_menu));
1758 edit_items.push_back (SeparatorElem());
1762 Editor::xfade_edit_left_region ()
1764 if (clicked_crossfadeview) {
1765 clicked_crossfadeview->left_view.show_region_editor ();
1770 Editor::xfade_edit_right_region ()
1772 if (clicked_crossfadeview) {
1773 clicked_crossfadeview->right_view.show_region_editor ();
1778 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::shared_ptr<Track> track)
1780 using namespace Menu_Helpers;
1782 /* OK, stick the region submenu at the top of the list, and then add
1786 RegionSelection rs = get_regions_from_selection_and_entered ();
1788 string::size_type pos = 0;
1789 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1791 /* we have to hack up the region name because "_" has a special
1792 meaning for menu titles.
1795 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1796 menu_item_name.replace (pos, 1, "__");
1800 if (_popup_region_menu_item == 0) {
1801 _popup_region_menu_item = new MenuItem (menu_item_name);
1802 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1803 _popup_region_menu_item->show ();
1805 _popup_region_menu_item->set_label (menu_item_name);
1808 /* Use the mouse position rather than the edit point to decide whether to show the `choose top region'
1809 dialogue. If we use the edit point it gets a bit messy because the user still has to click over
1810 *some* region in order to get the region context menu stuff to be displayed at all.
1815 mouse_frame (mouse, ignored);
1817 edit_items.push_back (*_popup_region_menu_item);
1818 if (track->playlist()->count_regions_at (mouse) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1819 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region")->create_menu_item ()));
1821 edit_items.push_back (SeparatorElem());
1824 /** Add context menu items relevant to selection ranges.
1825 * @param edit_items List to add the items to.
1828 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1830 using namespace Menu_Helpers;
1832 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1833 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1835 edit_items.push_back (SeparatorElem());
1836 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1838 edit_items.push_back (SeparatorElem());
1840 edit_items.push_back (
1842 _("Move Range Start to Previous Region Boundary"),
1843 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, false)
1847 edit_items.push_back (
1849 _("Move Range Start to Next Region Boundary"),
1850 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, true)
1854 edit_items.push_back (
1856 _("Move Range End to Previous Region Boundary"),
1857 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, false)
1861 edit_items.push_back (
1863 _("Move Range End to Next Region Boundary"),
1864 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, true)
1868 edit_items.push_back (SeparatorElem());
1869 edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection)));
1870 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1872 edit_items.push_back (SeparatorElem());
1873 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1875 edit_items.push_back (SeparatorElem());
1876 edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1877 edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1879 edit_items.push_back (SeparatorElem());
1880 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1882 edit_items.push_back (SeparatorElem());
1883 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1884 edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1885 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1887 edit_items.push_back (SeparatorElem());
1888 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1889 edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1890 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1891 edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1892 edit_items.push_back (MenuElem (_("Export Range..."), sigc::mem_fun(*this, &Editor::export_selection)));
1897 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1899 using namespace Menu_Helpers;
1903 Menu *play_menu = manage (new Menu);
1904 MenuList& play_items = play_menu->items();
1905 play_menu->set_name ("ArdourContextMenu");
1907 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1908 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1909 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1910 play_items.push_back (SeparatorElem());
1911 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
1913 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1917 Menu *select_menu = manage (new Menu);
1918 MenuList& select_items = select_menu->items();
1919 select_menu->set_name ("ArdourContextMenu");
1921 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1922 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1923 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1924 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1925 select_items.push_back (SeparatorElem());
1926 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
1927 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
1928 select_items.push_back (SeparatorElem());
1929 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1930 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1931 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1932 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1933 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
1934 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
1935 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
1937 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1941 Menu *cutnpaste_menu = manage (new Menu);
1942 MenuList& cutnpaste_items = cutnpaste_menu->items();
1943 cutnpaste_menu->set_name ("ArdourContextMenu");
1945 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1946 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1947 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1949 cutnpaste_items.push_back (SeparatorElem());
1951 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
1952 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
1954 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
1956 /* Adding new material */
1958 edit_items.push_back (SeparatorElem());
1959 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
1960 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
1964 Menu *nudge_menu = manage (new Menu());
1965 MenuList& nudge_items = nudge_menu->items();
1966 nudge_menu->set_name ("ArdourContextMenu");
1968 edit_items.push_back (SeparatorElem());
1969 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1970 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1971 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1972 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1974 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1978 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
1980 using namespace Menu_Helpers;
1984 Menu *play_menu = manage (new Menu);
1985 MenuList& play_items = play_menu->items();
1986 play_menu->set_name ("ArdourContextMenu");
1988 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1989 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1990 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1994 Menu *select_menu = manage (new Menu);
1995 MenuList& select_items = select_menu->items();
1996 select_menu->set_name ("ArdourContextMenu");
1998 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1999 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
2000 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
2001 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
2002 select_items.push_back (SeparatorElem());
2003 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
2004 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
2005 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2006 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2008 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2012 Menu *cutnpaste_menu = manage (new Menu);
2013 MenuList& cutnpaste_items = cutnpaste_menu->items();
2014 cutnpaste_menu->set_name ("ArdourContextMenu");
2016 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2017 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2018 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
2020 Menu *nudge_menu = manage (new Menu());
2021 MenuList& nudge_items = nudge_menu->items();
2022 nudge_menu->set_name ("ArdourContextMenu");
2024 edit_items.push_back (SeparatorElem());
2025 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2026 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2027 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2028 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2030 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2034 Editor::snap_type() const
2040 Editor::snap_mode() const
2046 Editor::set_snap_to (SnapType st)
2048 unsigned int snap_ind = (unsigned int)st;
2052 if (snap_ind > snap_type_strings.size() - 1) {
2054 _snap_type = (SnapType)snap_ind;
2057 string str = snap_type_strings[snap_ind];
2059 if (str != snap_type_selector.get_active_text()) {
2060 snap_type_selector.set_active_text (str);
2065 switch (_snap_type) {
2066 case SnapToBeatDiv32:
2067 case SnapToBeatDiv28:
2068 case SnapToBeatDiv24:
2069 case SnapToBeatDiv20:
2070 case SnapToBeatDiv16:
2071 case SnapToBeatDiv14:
2072 case SnapToBeatDiv12:
2073 case SnapToBeatDiv10:
2074 case SnapToBeatDiv8:
2075 case SnapToBeatDiv7:
2076 case SnapToBeatDiv6:
2077 case SnapToBeatDiv5:
2078 case SnapToBeatDiv4:
2079 case SnapToBeatDiv3:
2080 case SnapToBeatDiv2:
2081 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
2082 update_tempo_based_rulers ();
2085 case SnapToRegionStart:
2086 case SnapToRegionEnd:
2087 case SnapToRegionSync:
2088 case SnapToRegionBoundary:
2089 build_region_boundary_cache ();
2097 SnapChanged (); /* EMIT SIGNAL */
2101 Editor::set_snap_mode (SnapMode mode)
2104 string str = snap_mode_strings[(int)mode];
2106 if (str != snap_mode_selector.get_active_text ()) {
2107 snap_mode_selector.set_active_text (str);
2113 Editor::set_edit_point_preference (EditPoint ep, bool force)
2115 bool changed = (_edit_point != ep);
2118 string str = edit_point_strings[(int)ep];
2120 if (str != edit_point_selector.get_active_text ()) {
2121 edit_point_selector.set_active_text (str);
2124 set_canvas_cursor ();
2126 if (!force && !changed) {
2130 const char* action=NULL;
2132 switch (_edit_point) {
2133 case EditAtPlayhead:
2134 action = "edit-at-playhead";
2136 case EditAtSelectedMarker:
2137 action = "edit-at-marker";
2140 action = "edit-at-mouse";
2144 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2146 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2150 bool in_track_canvas;
2152 if (!mouse_frame (foo, in_track_canvas)) {
2153 in_track_canvas = false;
2156 reset_canvas_action_sensitivity (in_track_canvas);
2162 Editor::set_state (const XMLNode& node, int /*version*/)
2164 const XMLProperty* prop;
2171 g.base_width = default_width;
2172 g.base_height = default_height;
2176 if ((geometry = find_named_node (node, "geometry")) != 0) {
2180 if ((prop = geometry->property("x_size")) == 0) {
2181 prop = geometry->property ("x-size");
2184 g.base_width = atoi(prop->value());
2186 if ((prop = geometry->property("y_size")) == 0) {
2187 prop = geometry->property ("y-size");
2190 g.base_height = atoi(prop->value());
2193 if ((prop = geometry->property ("x_pos")) == 0) {
2194 prop = geometry->property ("x-pos");
2197 x = atoi (prop->value());
2200 if ((prop = geometry->property ("y_pos")) == 0) {
2201 prop = geometry->property ("y-pos");
2204 y = atoi (prop->value());
2208 set_default_size (g.base_width, g.base_height);
2211 if (_session && (prop = node.property ("playhead"))) {
2213 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2214 playhead_cursor->set_position (pos);
2216 playhead_cursor->set_position (0);
2219 if ((prop = node.property ("mixer-width"))) {
2220 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2223 if ((prop = node.property ("zoom-focus"))) {
2224 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2227 if ((prop = node.property ("zoom"))) {
2228 reset_zoom (PBD::atof (prop->value()));
2230 reset_zoom (frames_per_unit);
2233 if ((prop = node.property ("snap-to"))) {
2234 set_snap_to ((SnapType) atoi (prop->value()));
2237 if ((prop = node.property ("snap-mode"))) {
2238 set_snap_mode ((SnapMode) atoi (prop->value()));
2241 if ((prop = node.property ("mouse-mode"))) {
2242 MouseMode m = str2mousemode(prop->value());
2243 set_mouse_mode (m, true);
2245 set_mouse_mode (MouseObject, true);
2248 if ((prop = node.property ("left-frame")) != 0) {
2250 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2251 reset_x_origin (pos);
2255 if ((prop = node.property ("y-origin")) != 0) {
2256 reset_y_origin (atof (prop->value ()));
2259 if ((prop = node.property ("internal-edit"))) {
2260 bool yn = string_is_affirmative (prop->value());
2261 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2263 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2264 tact->set_active (!yn);
2265 tact->set_active (yn);
2269 if ((prop = node.property ("join-object-range"))) {
2270 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2273 if ((prop = node.property ("edit-point"))) {
2274 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2277 if ((prop = node.property ("show-measures"))) {
2278 bool yn = string_is_affirmative (prop->value());
2279 _show_measures = yn;
2280 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2282 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2283 /* do it twice to force the change */
2284 tact->set_active (!yn);
2285 tact->set_active (yn);
2289 if ((prop = node.property ("follow-playhead"))) {
2290 bool yn = string_is_affirmative (prop->value());
2291 set_follow_playhead (yn);
2292 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2294 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2295 if (tact->get_active() != yn) {
2296 tact->set_active (yn);
2301 if ((prop = node.property ("stationary-playhead"))) {
2302 bool yn = string_is_affirmative (prop->value());
2303 set_stationary_playhead (yn);
2304 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2306 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2307 if (tact->get_active() != yn) {
2308 tact->set_active (yn);
2313 if ((prop = node.property ("region-list-sort-type"))) {
2314 RegionListSortType st;
2315 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2318 if ((prop = node.property ("xfades-visible"))) {
2319 bool yn = string_is_affirmative (prop->value());
2320 _xfade_visibility = !yn;
2321 // set_xfade_visibility (yn);
2324 if ((prop = node.property ("show-editor-mixer"))) {
2326 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2329 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2330 bool yn = string_is_affirmative (prop->value());
2332 /* do it twice to force the change */
2334 tact->set_active (!yn);
2335 tact->set_active (yn);
2338 if ((prop = node.property ("show-editor-list"))) {
2340 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2343 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2344 bool yn = string_is_affirmative (prop->value());
2346 /* do it twice to force the change */
2348 tact->set_active (!yn);
2349 tact->set_active (yn);
2352 if ((prop = node.property (X_("editor-list-page")))) {
2353 _the_notebook.set_current_page (atoi (prop->value ()));
2356 if ((prop = node.property (X_("show-marker-lines")))) {
2357 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2359 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2360 bool yn = string_is_affirmative (prop->value ());
2362 tact->set_active (!yn);
2363 tact->set_active (yn);
2366 XMLNodeList children = node.children ();
2367 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2368 selection->set_state (**i, Stateful::current_state_version);
2369 _regions->set_state (**i);
2376 Editor::get_state ()
2378 XMLNode* node = new XMLNode ("Editor");
2381 id().print (buf, sizeof (buf));
2382 node->add_property ("id", buf);
2384 if (is_realized()) {
2385 Glib::RefPtr<Gdk::Window> win = get_window();
2387 int x, y, width, height;
2388 win->get_root_origin(x, y);
2389 win->get_size(width, height);
2391 XMLNode* geometry = new XMLNode ("geometry");
2393 snprintf(buf, sizeof(buf), "%d", width);
2394 geometry->add_property("x-size", string(buf));
2395 snprintf(buf, sizeof(buf), "%d", height);
2396 geometry->add_property("y-size", string(buf));
2397 snprintf(buf, sizeof(buf), "%d", x);
2398 geometry->add_property("x-pos", string(buf));
2399 snprintf(buf, sizeof(buf), "%d", y);
2400 geometry->add_property("y-pos", string(buf));
2401 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2402 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2403 geometry->add_property("notebook-shrunk", _notebook_shrunk ? "1" : "0");
2404 snprintf(buf,sizeof(buf), "%d",pre_maximal_horizontal_pane_position);
2405 geometry->add_property("pre-maximal-horizontal-pane-position", string(buf));
2406 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2407 geometry->add_property("edit-vertical-pane-pos", string(buf));
2409 node->add_child_nocopy (*geometry);
2412 maybe_add_mixer_strip_width (*node);
2414 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2415 node->add_property ("zoom-focus", buf);
2416 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2417 node->add_property ("zoom", buf);
2418 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2419 node->add_property ("snap-to", buf);
2420 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2421 node->add_property ("snap-mode", buf);
2423 node->add_property ("edit-point", enum_2_string (_edit_point));
2425 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2426 node->add_property ("playhead", buf);
2427 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2428 node->add_property ("left-frame", buf);
2429 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2430 node->add_property ("y-origin", buf);
2432 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2433 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2434 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2435 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2436 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2437 node->add_property ("mouse-mode", enum2str(mouse_mode));
2438 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2439 node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2441 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2443 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2444 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2447 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2449 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2450 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2453 snprintf (buf, sizeof (buf), "%d", _the_notebook.get_current_page ());
2454 node->add_property (X_("editor-list-page"), buf);
2456 if (button_bindings) {
2457 XMLNode* bb = new XMLNode (X_("Buttons"));
2458 button_bindings->save (*bb);
2459 node->add_child_nocopy (*bb);
2462 node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no");
2464 node->add_child_nocopy (selection->get_state ());
2465 node->add_child_nocopy (_regions->get_state ());
2472 /** @param y y offset from the top of all trackviews.
2473 * @return pair: TimeAxisView that y is over, layer index.
2474 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2475 * in stacked region display mode, otherwise 0.
2477 std::pair<TimeAxisView *, layer_t>
2478 Editor::trackview_by_y_position (double y)
2480 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2482 std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
2488 return std::make_pair ( (TimeAxisView *) 0, 0);
2491 /** Snap a position to the grid, if appropriate, taking into account current
2492 * grid settings and also the state of any snap modifier keys that may be pressed.
2493 * @param start Position to snap.
2494 * @param event Event to get current key modifier information from, or 0.
2497 Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2499 if (!_session || !event) {
2503 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2504 if (_snap_mode == SnapOff) {
2505 snap_to_internal (start, direction, for_mark);
2508 if (_snap_mode != SnapOff) {
2509 snap_to_internal (start, direction, for_mark);
2515 Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark)
2517 if (!_session || _snap_mode == SnapOff) {
2521 snap_to_internal (start, direction, for_mark);
2525 Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /*for_mark*/)
2527 const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2528 framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2530 switch (_snap_type) {
2531 case SnapToTimecodeFrame:
2532 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2533 start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2535 start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2539 case SnapToTimecodeSeconds:
2540 if (_session->config.get_timecode_offset_negative()) {
2541 start += _session->config.get_timecode_offset ();
2543 start -= _session->config.get_timecode_offset ();
2545 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2546 start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2548 start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2551 if (_session->config.get_timecode_offset_negative()) {
2552 start -= _session->config.get_timecode_offset ();
2554 start += _session->config.get_timecode_offset ();
2558 case SnapToTimecodeMinutes:
2559 if (_session->config.get_timecode_offset_negative()) {
2560 start += _session->config.get_timecode_offset ();
2562 start -= _session->config.get_timecode_offset ();
2564 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2565 start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2567 start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2569 if (_session->config.get_timecode_offset_negative()) {
2570 start -= _session->config.get_timecode_offset ();
2572 start += _session->config.get_timecode_offset ();
2576 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2582 Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark)
2584 const framepos_t one_second = _session->frame_rate();
2585 const framepos_t one_minute = _session->frame_rate() * 60;
2586 framepos_t presnap = start;
2590 switch (_snap_type) {
2591 case SnapToTimecodeFrame:
2592 case SnapToTimecodeSeconds:
2593 case SnapToTimecodeMinutes:
2594 return timecode_snap_to_internal (start, direction, for_mark);
2597 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2598 start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2600 start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2605 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2606 start = (framepos_t) ceil ((double) start / one_second) * one_second;
2608 start = (framepos_t) floor ((double) start / one_second) * one_second;
2613 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2614 start = (framepos_t) ceil ((double) start / one_minute) * one_minute;
2616 start = (framepos_t) floor ((double) start / one_minute) * one_minute;
2621 start = _session->tempo_map().round_to_bar (start, direction);
2625 start = _session->tempo_map().round_to_beat (start, direction);
2628 case SnapToBeatDiv32:
2629 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2631 case SnapToBeatDiv28:
2632 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2634 case SnapToBeatDiv24:
2635 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2637 case SnapToBeatDiv20:
2638 start = _session->tempo_map().round_to_beat_subdivision (start, 20, direction);
2640 case SnapToBeatDiv16:
2641 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2643 case SnapToBeatDiv14:
2644 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2646 case SnapToBeatDiv12:
2647 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2649 case SnapToBeatDiv10:
2650 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2652 case SnapToBeatDiv8:
2653 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2655 case SnapToBeatDiv7:
2656 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2658 case SnapToBeatDiv6:
2659 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2661 case SnapToBeatDiv5:
2662 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2664 case SnapToBeatDiv4:
2665 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2667 case SnapToBeatDiv3:
2668 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2670 case SnapToBeatDiv2:
2671 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2679 _session->locations()->marks_either_side (start, before, after);
2681 if (before == max_framepos) {
2683 } else if (after == max_framepos) {
2685 } else if (before != max_framepos && after != max_framepos) {
2686 /* have before and after */
2687 if ((start - before) < (after - start)) {
2696 case SnapToRegionStart:
2697 case SnapToRegionEnd:
2698 case SnapToRegionSync:
2699 case SnapToRegionBoundary:
2700 if (!region_boundary_cache.empty()) {
2702 vector<framepos_t>::iterator prev = region_boundary_cache.end ();
2703 vector<framepos_t>::iterator next = region_boundary_cache.end ();
2705 if (direction > 0) {
2706 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2708 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2711 if (next != region_boundary_cache.begin ()) {
2716 framepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2717 framepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2719 if (start > (p + n) / 2) {
2728 switch (_snap_mode) {
2734 if (presnap > start) {
2735 if (presnap > (start + unit_to_frame(snap_threshold))) {
2739 } else if (presnap < start) {
2740 if (presnap < (start - unit_to_frame(snap_threshold))) {
2746 /* handled at entry */
2754 Editor::setup_toolbar ()
2756 HBox* mode_box = manage(new HBox);
2757 mode_box->set_border_width (2);
2758 mode_box->set_spacing(4);
2760 /* table containing mode buttons */
2762 HBox* mouse_mode_button_box = manage (new HBox ());
2763 mouse_mode_button_box->set_spacing (2);
2765 if (Profile->get_sae()) {
2766 mouse_mode_button_box->pack_start (mouse_move_button);
2768 mouse_mode_button_box->pack_start (mouse_move_button);
2769 mouse_mode_button_box->pack_start (join_object_range_button);
2770 mouse_mode_button_box->pack_start (mouse_select_button);
2773 mouse_mode_button_box->pack_start (mouse_zoom_button);
2775 if (!Profile->get_sae()) {
2776 mouse_mode_button_box->pack_start (mouse_gain_button);
2779 mouse_mode_button_box->pack_start (mouse_timefx_button);
2780 mouse_mode_button_box->pack_start (mouse_audition_button);
2781 mouse_mode_button_box->pack_start (internal_edit_button);
2783 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2784 if (!Profile->get_sae()) {
2785 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2787 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2789 edit_mode_selector.set_name ("EditModeSelector");
2790 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2791 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2793 mode_box->pack_start (edit_mode_selector);
2794 mode_box->pack_start (*mouse_mode_button_box);
2796 _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2797 _mouse_mode_tearoff->set_name ("MouseModeBase");
2798 _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2800 if (Profile->get_sae()) {
2801 _mouse_mode_tearoff->set_can_be_torn_off (false);
2804 _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2805 &_mouse_mode_tearoff->tearoff_window()));
2806 _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2807 &_mouse_mode_tearoff->tearoff_window(), 1));
2808 _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2809 &_mouse_mode_tearoff->tearoff_window()));
2810 _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2811 &_mouse_mode_tearoff->tearoff_window(), 1));
2815 _zoom_box.set_spacing (2);
2816 _zoom_box.set_border_width (2);
2820 zoom_in_button.set_name ("zoom button");
2821 zoom_in_button.set_image (::get_icon ("zoom_in"));
2822 zoom_in_button.set_tweaks (ArdourButton::ShowClick);
2823 act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-in"));
2824 zoom_in_button.set_related_action (act);
2826 zoom_out_button.set_name ("zoom button");
2827 zoom_out_button.set_image (::get_icon ("zoom_out"));
2828 zoom_out_button.set_tweaks (ArdourButton::ShowClick);
2829 act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-out"));
2830 zoom_out_button.set_related_action (act);
2832 zoom_out_full_button.set_name ("zoom button");
2833 zoom_out_full_button.set_image (::get_icon ("zoom_full"));
2834 zoom_out_full_button.set_tweaks (ArdourButton::ShowClick);
2835 act = ActionManager::get_action (X_("Editor"), X_("zoom-to-session"));
2836 zoom_out_full_button.set_related_action (act);
2838 zoom_focus_selector.set_name ("ZoomFocusSelector");
2839 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
2840 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2842 _zoom_box.pack_start (zoom_out_button, false, false);
2843 _zoom_box.pack_start (zoom_in_button, false, false);
2844 _zoom_box.pack_start (zoom_out_full_button, false, false);
2846 _zoom_box.pack_start (zoom_focus_selector);
2848 /* Track zoom buttons */
2849 tav_expand_button.set_name ("TrackHeightButton");
2850 tav_expand_button.set_size_request (-1, 20);
2851 tav_expand_button.add (*(manage (new Image (::get_icon ("tav_exp")))));
2852 act = ActionManager::get_action (X_("Editor"), X_("expand-tracks"));
2853 act->connect_proxy (tav_expand_button);
2855 tav_shrink_button.set_name ("TrackHeightButton");
2856 tav_shrink_button.set_size_request (-1, 20);
2857 tav_shrink_button.add (*(manage (new Image (::get_icon ("tav_shrink")))));
2858 act = ActionManager::get_action (X_("Editor"), X_("shrink-tracks"));
2859 act->connect_proxy (tav_shrink_button);
2861 _zoom_box.pack_start (tav_shrink_button);
2862 _zoom_box.pack_start (tav_expand_button);
2864 _zoom_tearoff = manage (new TearOff (_zoom_box));
2866 _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2867 &_zoom_tearoff->tearoff_window()));
2868 _zoom_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2869 &_zoom_tearoff->tearoff_window(), 0));
2870 _zoom_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2871 &_zoom_tearoff->tearoff_window()));
2872 _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2873 &_zoom_tearoff->tearoff_window(), 0));
2875 snap_box.set_spacing (1);
2876 snap_box.set_border_width (2);
2878 snap_type_selector.set_name ("SnapTypeSelector");
2879 set_popdown_strings (snap_type_selector, snap_type_strings, true);
2880 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2882 snap_mode_selector.set_name ("SnapModeSelector");
2883 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
2884 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2886 edit_point_selector.set_name ("EditPointSelector");
2887 set_popdown_strings (edit_point_selector, edit_point_strings, true);
2888 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2890 snap_box.pack_start (snap_mode_selector, false, false);
2891 snap_box.pack_start (snap_type_selector, false, false);
2892 snap_box.pack_start (edit_point_selector, false, false);
2896 HBox *nudge_box = manage (new HBox);
2897 nudge_box->set_spacing (2);
2898 nudge_box->set_border_width (2);
2900 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2901 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2903 nudge_box->pack_start (nudge_backward_button, false, false);
2904 nudge_box->pack_start (nudge_forward_button, false, false);
2905 nudge_box->pack_start (*nudge_clock, false, false);
2908 /* Pack everything in... */
2910 HBox* hbox = manage (new HBox);
2911 hbox->set_spacing(10);
2913 _tools_tearoff = manage (new TearOff (*hbox));
2914 _tools_tearoff->set_name ("MouseModeBase");
2915 _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
2917 if (Profile->get_sae()) {
2918 _tools_tearoff->set_can_be_torn_off (false);
2921 _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2922 &_tools_tearoff->tearoff_window()));
2923 _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2924 &_tools_tearoff->tearoff_window(), 0));
2925 _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2926 &_tools_tearoff->tearoff_window()));
2927 _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2928 &_tools_tearoff->tearoff_window(), 0));
2930 toolbar_hbox.set_spacing (10);
2931 toolbar_hbox.set_border_width (1);
2933 toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
2934 toolbar_hbox.pack_start (*_zoom_tearoff, false, false);
2935 toolbar_hbox.pack_start (*_tools_tearoff, false, false);
2937 hbox->pack_start (snap_box, false, false);
2938 if (!Profile->get_small_screen()) {
2939 hbox->pack_start (*nudge_box, false, false);
2941 ARDOUR_UI::instance()->editor_transport_box().pack_start (*nudge_box, false, false);
2943 hbox->pack_start (panic_box, false, false);
2947 toolbar_base.set_name ("ToolBarBase");
2948 toolbar_base.add (toolbar_hbox);
2950 _toolbar_viewport.add (toolbar_base);
2951 /* stick to the required height but allow width to vary if there's not enough room */
2952 _toolbar_viewport.set_size_request (1, -1);
2954 toolbar_frame.set_shadow_type (SHADOW_OUT);
2955 toolbar_frame.set_name ("BaseFrame");
2956 toolbar_frame.add (_toolbar_viewport);
2958 DPIReset.connect (sigc::mem_fun (*this, &Editor::resize_text_widgets));
2962 Editor::setup_tooltips ()
2964 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
2965 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain"));
2966 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
2967 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
2968 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2969 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
2970 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
2971 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
2972 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
2973 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
2974 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
2975 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
2976 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
2977 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
2978 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
2979 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
2980 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
2981 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
2982 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
2983 ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
2984 ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode"));
2989 Editor::setup_midi_toolbar ()
2993 /* Midi sound notes */
2994 midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
2995 midi_sound_notes.unset_flags (CAN_FOCUS);
2996 midi_sound_notes.set_name (X_("MidiSoundNotesButton"));
3000 panic_box.pack_start (midi_sound_notes , true, true);
3001 // panic_box.pack_start (midi_panic_button, true, true);
3005 Editor::convert_drop_to_paths (
3006 vector<string>& paths,
3007 const RefPtr<Gdk::DragContext>& /*context*/,
3010 const SelectionData& data,
3014 if (_session == 0) {
3018 vector<string> uris = data.get_uris();
3022 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3023 are actually URI lists. So do it by hand.
3026 if (data.get_target() != "text/plain") {
3030 /* Parse the "uri-list" format that Nautilus provides,
3031 where each pathname is delimited by \r\n.
3033 THERE MAY BE NO NULL TERMINATING CHAR!!!
3036 string txt = data.get_text();
3040 p = (const char *) malloc (txt.length() + 1);
3041 txt.copy ((char *) p, txt.length(), 0);
3042 ((char*)p)[txt.length()] = '\0';
3048 while (g_ascii_isspace (*p))
3052 while (*q && (*q != '\n') && (*q != '\r')) {
3059 while (q > p && g_ascii_isspace (*q))
3064 uris.push_back (string (p, q - p + 1));
3068 p = strchr (p, '\n');
3080 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3082 if ((*i).substr (0,7) == "file://") {
3085 PBD::url_decode (p);
3087 // scan forward past three slashes
3089 string::size_type slashcnt = 0;
3090 string::size_type n = 0;
3091 string::iterator x = p.begin();
3093 while (slashcnt < 3 && x != p.end()) {
3096 } else if (slashcnt == 3) {
3103 if (slashcnt != 3 || x == p.end()) {
3104 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3108 paths.push_back (p.substr (n - 1));
3116 Editor::new_tempo_section ()
3122 Editor::map_transport_state ()
3124 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3126 if (_session && _session->transport_stopped()) {
3127 have_pending_keyboard_selection = false;
3130 update_loop_range_view (true);
3135 Editor::State::State (PublicEditor const * e)
3137 selection = new Selection (e);
3140 Editor::State::~State ()
3146 Editor::begin_reversible_command (string name)
3149 _session->begin_reversible_command (name);
3154 Editor::begin_reversible_command (GQuark q)
3157 _session->begin_reversible_command (q);
3162 Editor::commit_reversible_command ()
3165 _session->commit_reversible_command ();
3170 Editor::history_changed ()
3174 if (undo_action && _session) {
3175 if (_session->undo_depth() == 0) {
3178 label = string_compose(_("Undo (%1)"), _session->next_undo());
3180 undo_action->property_label() = label;
3183 if (redo_action && _session) {
3184 if (_session->redo_depth() == 0) {
3187 label = string_compose(_("Redo (%1)"), _session->next_redo());
3189 redo_action->property_label() = label;
3194 Editor::duplicate_dialog (bool with_dialog)
3198 if (mouse_mode == MouseRange) {
3199 if (selection->time.length() == 0) {
3204 RegionSelection rs = get_regions_from_selection_and_entered ();
3206 if (mouse_mode != MouseRange && rs.empty()) {
3212 ArdourDialog win (_("Duplicate"));
3213 Label label (_("Number of duplications:"));
3214 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3215 SpinButton spinner (adjustment, 0.0, 1);
3218 win.get_vbox()->set_spacing (12);
3219 win.get_vbox()->pack_start (hbox);
3220 hbox.set_border_width (6);
3221 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3223 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3224 place, visually. so do this by hand.
3227 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3228 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3229 spinner.grab_focus();
3235 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3236 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3237 win.set_default_response (RESPONSE_ACCEPT);
3239 win.set_position (WIN_POS_MOUSE);
3241 spinner.grab_focus ();
3243 switch (win.run ()) {
3244 case RESPONSE_ACCEPT:
3250 times = adjustment.get_value();
3253 if (mouse_mode == MouseRange) {
3254 duplicate_selection (times);
3256 duplicate_some_regions (rs, times);
3261 Editor::set_edit_mode (EditMode m)
3263 Config->set_edit_mode (m);
3267 Editor::cycle_edit_mode ()
3269 switch (Config->get_edit_mode()) {
3271 if (Profile->get_sae()) {
3272 Config->set_edit_mode (Lock);
3274 Config->set_edit_mode (Splice);
3278 Config->set_edit_mode (Lock);
3281 Config->set_edit_mode (Slide);
3287 Editor::edit_mode_selection_done ()
3289 string s = edit_mode_selector.get_active_text ();
3292 Config->set_edit_mode (string_to_edit_mode (s));
3297 Editor::snap_type_selection_done ()
3299 string choice = snap_type_selector.get_active_text();
3300 SnapType snaptype = SnapToBeat;
3302 if (choice == _("Beats/2")) {
3303 snaptype = SnapToBeatDiv2;
3304 } else if (choice == _("Beats/3")) {
3305 snaptype = SnapToBeatDiv3;
3306 } else if (choice == _("Beats/4")) {
3307 snaptype = SnapToBeatDiv4;
3308 } else if (choice == _("Beats/5")) {
3309 snaptype = SnapToBeatDiv5;
3310 } else if (choice == _("Beats/6")) {
3311 snaptype = SnapToBeatDiv6;
3312 } else if (choice == _("Beats/7")) {
3313 snaptype = SnapToBeatDiv7;
3314 } else if (choice == _("Beats/8")) {
3315 snaptype = SnapToBeatDiv8;
3316 } else if (choice == _("Beats/10")) {
3317 snaptype = SnapToBeatDiv10;
3318 } else if (choice == _("Beats/12")) {
3319 snaptype = SnapToBeatDiv12;
3320 } else if (choice == _("Beats/14")) {
3321 snaptype = SnapToBeatDiv14;
3322 } else if (choice == _("Beats/16")) {
3323 snaptype = SnapToBeatDiv16;
3324 } else if (choice == _("Beats/20")) {
3325 snaptype = SnapToBeatDiv20;
3326 } else if (choice == _("Beats/24")) {
3327 snaptype = SnapToBeatDiv24;
3328 } else if (choice == _("Beats/28")) {
3329 snaptype = SnapToBeatDiv28;
3330 } else if (choice == _("Beats/32")) {
3331 snaptype = SnapToBeatDiv32;
3332 } else if (choice == _("Beats")) {
3333 snaptype = SnapToBeat;
3334 } else if (choice == _("Bars")) {
3335 snaptype = SnapToBar;
3336 } else if (choice == _("Marks")) {
3337 snaptype = SnapToMark;
3338 } else if (choice == _("Region starts")) {
3339 snaptype = SnapToRegionStart;
3340 } else if (choice == _("Region ends")) {
3341 snaptype = SnapToRegionEnd;
3342 } else if (choice == _("Region bounds")) {
3343 snaptype = SnapToRegionBoundary;
3344 } else if (choice == _("Region syncs")) {
3345 snaptype = SnapToRegionSync;
3346 } else if (choice == _("CD Frames")) {
3347 snaptype = SnapToCDFrame;
3348 } else if (choice == _("Timecode Frames")) {
3349 snaptype = SnapToTimecodeFrame;
3350 } else if (choice == _("Timecode Seconds")) {
3351 snaptype = SnapToTimecodeSeconds;
3352 } else if (choice == _("Timecode Minutes")) {
3353 snaptype = SnapToTimecodeMinutes;
3354 } else if (choice == _("Seconds")) {
3355 snaptype = SnapToSeconds;
3356 } else if (choice == _("Minutes")) {
3357 snaptype = SnapToMinutes;
3360 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3362 ract->set_active ();
3367 Editor::snap_mode_selection_done ()
3369 string choice = snap_mode_selector.get_active_text();
3370 SnapMode mode = SnapNormal;
3372 if (choice == _("No Grid")) {
3374 } else if (choice == _("Grid")) {
3376 } else if (choice == _("Magnetic")) {
3377 mode = SnapMagnetic;
3380 RefPtr<RadioAction> ract = snap_mode_action (mode);
3383 ract->set_active (true);
3388 Editor::cycle_edit_point (bool with_marker)
3390 switch (_edit_point) {
3392 set_edit_point_preference (EditAtPlayhead);
3394 case EditAtPlayhead:
3396 set_edit_point_preference (EditAtSelectedMarker);
3398 set_edit_point_preference (EditAtMouse);
3401 case EditAtSelectedMarker:
3402 set_edit_point_preference (EditAtMouse);
3408 Editor::edit_point_selection_done ()
3410 string choice = edit_point_selector.get_active_text();
3411 EditPoint ep = EditAtSelectedMarker;
3413 if (choice == _("Marker")) {
3414 set_edit_point_preference (EditAtSelectedMarker);
3415 } else if (choice == _("Playhead")) {
3416 set_edit_point_preference (EditAtPlayhead);
3418 set_edit_point_preference (EditAtMouse);
3421 RefPtr<RadioAction> ract = edit_point_action (ep);
3424 ract->set_active (true);
3429 Editor::zoom_focus_selection_done ()
3431 string choice = zoom_focus_selector.get_active_text();
3432 ZoomFocus focus_type = ZoomFocusLeft;
3434 if (choice == _("Left")) {
3435 focus_type = ZoomFocusLeft;
3436 } else if (choice == _("Right")) {
3437 focus_type = ZoomFocusRight;
3438 } else if (choice == _("Center")) {
3439 focus_type = ZoomFocusCenter;
3440 } else if (choice == _("Playhead")) {
3441 focus_type = ZoomFocusPlayhead;
3442 } else if (choice == _("Mouse")) {
3443 focus_type = ZoomFocusMouse;
3444 } else if (choice == _("Edit point")) {
3445 focus_type = ZoomFocusEdit;
3448 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3451 ract->set_active ();
3456 Editor::edit_controls_button_release (GdkEventButton* ev)
3458 if (Keyboard::is_context_menu_event (ev)) {
3459 ARDOUR_UI::instance()->add_route (this);
3460 } else if (ev->button == 1) {
3461 selection->clear_tracks ();
3468 Editor::mouse_select_button_release (GdkEventButton* ev)
3470 /* this handles just right-clicks */
3472 if (ev->button != 3) {
3480 Editor::set_zoom_focus (ZoomFocus f)
3482 string str = zoom_focus_strings[(int)f];
3484 if (str != zoom_focus_selector.get_active_text()) {
3485 zoom_focus_selector.set_active_text (str);
3488 if (zoom_focus != f) {
3495 Editor::ensure_float (Window& win)
3497 win.set_transient_for (*this);
3501 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3503 /* recover or initialize pane positions. do this here rather than earlier because
3504 we don't want the positions to change the child allocations, which they seem to do.
3510 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3519 XMLNode* geometry = find_named_node (*node, "geometry");
3521 if (which == static_cast<Paned*> (&edit_pane)) {
3523 if (done & Horizontal) {
3527 if (geometry && (prop = geometry->property ("notebook-shrunk"))) {
3528 _notebook_shrunk = string_is_affirmative (prop->value ());
3531 if (geometry && (prop = geometry->property ("pre-maximal-horizontal-pane-position"))) {
3532 pre_maximal_horizontal_pane_position = atoi (prop->value ());
3535 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3536 /* initial allocation is 90% to canvas, 10% to notebook */
3537 pos = (int) floor (alloc.get_width() * 0.90f);
3538 snprintf (buf, sizeof(buf), "%d", pos);
3540 pos = atoi (prop->value());
3543 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3544 edit_pane.set_position (pos);
3545 if (pre_maximal_horizontal_pane_position == 0) {
3546 pre_maximal_horizontal_pane_position = pos;
3550 done = (Pane) (done | Horizontal);
3552 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3554 if (done & Vertical) {
3558 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3559 /* initial allocation is 90% to canvas, 10% to summary */
3560 pos = (int) floor (alloc.get_height() * 0.90f);
3561 snprintf (buf, sizeof(buf), "%d", pos);
3563 pos = atoi (prop->value());
3566 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3567 editor_summary_pane.set_position (pos);
3568 pre_maximal_vertical_pane_position = pos;
3571 done = (Pane) (done | Vertical);
3576 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3578 if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) {
3579 top_hbox.remove (toolbar_frame);
3584 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3586 if (toolbar_frame.get_parent() == 0) {
3587 top_hbox.pack_end (toolbar_frame);
3592 Editor::set_show_measures (bool yn)
3594 if (_show_measures != yn) {
3597 if ((_show_measures = yn) == true) {
3599 tempo_lines->show();
3607 Editor::toggle_follow_playhead ()
3609 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3611 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3612 set_follow_playhead (tact->get_active());
3616 /** @param yn true to follow playhead, otherwise false.
3617 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
3620 Editor::set_follow_playhead (bool yn, bool catch_up)
3622 if (_follow_playhead != yn) {
3623 if ((_follow_playhead = yn) == true && catch_up) {
3625 reset_x_origin_to_follow_playhead ();
3632 Editor::toggle_stationary_playhead ()
3634 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3636 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3637 set_stationary_playhead (tact->get_active());
3642 Editor::set_stationary_playhead (bool yn)
3644 if (_stationary_playhead != yn) {
3645 if ((_stationary_playhead = yn) == true) {
3647 // FIXME need a 3.0 equivalent of this 2.X call
3648 // update_current_screen ();
3655 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3657 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3659 xfade->set_active (!xfade->active());
3664 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3666 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3668 xfade->set_follow_overlap (!xfade->following_overlap());
3673 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3675 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3681 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3685 switch (cew.run ()) {
3686 case RESPONSE_ACCEPT:
3693 PropertyChange all_crossfade_properties;
3694 all_crossfade_properties.add (ARDOUR::Properties::active);
3695 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3696 xfade->PropertyChanged (all_crossfade_properties);
3700 Editor::playlist_selector () const
3702 return *_playlist_selector;
3706 Editor::get_grid_type_as_beats (bool& success, framepos_t position)
3710 switch (_snap_type) {
3715 case SnapToBeatDiv32:
3718 case SnapToBeatDiv28:
3721 case SnapToBeatDiv24:
3724 case SnapToBeatDiv20:
3727 case SnapToBeatDiv16:
3730 case SnapToBeatDiv14:
3733 case SnapToBeatDiv12:
3736 case SnapToBeatDiv10:
3739 case SnapToBeatDiv8:
3742 case SnapToBeatDiv7:
3745 case SnapToBeatDiv6:
3748 case SnapToBeatDiv5:
3751 case SnapToBeatDiv4:
3754 case SnapToBeatDiv3:
3757 case SnapToBeatDiv2:
3763 return _session->tempo_map().meter_at (position).beats_per_bar();
3768 case SnapToTimecodeFrame:
3769 case SnapToTimecodeSeconds:
3770 case SnapToTimecodeMinutes:
3773 case SnapToRegionStart:
3774 case SnapToRegionEnd:
3775 case SnapToRegionSync:
3776 case SnapToRegionBoundary:
3786 Editor::get_nudge_distance (framepos_t pos, framecnt_t& next)
3790 ret = nudge_clock->current_duration (pos);
3791 next = ret + 1; /* XXXX fix me */
3797 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3799 ArdourDialog dialog (_("Playlist Deletion"));
3800 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3801 "If it is kept, its audio files will not be cleaned.\n"
3802 "If it is deleted, audio files used by it alone will be cleaned."),
3805 dialog.set_position (WIN_POS_CENTER);
3806 dialog.get_vbox()->pack_start (label);
3810 dialog.add_button (_("Delete Playlist"), RESPONSE_ACCEPT);
3811 dialog.add_button (_("Keep Playlist"), RESPONSE_REJECT);
3812 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3814 switch (dialog.run ()) {
3815 case RESPONSE_ACCEPT:
3816 /* delete the playlist */
3820 case RESPONSE_REJECT:
3821 /* keep the playlist */
3833 Editor::audio_region_selection_covers (framepos_t where)
3835 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3836 if ((*a)->region()->covers (where)) {
3845 Editor::prepare_for_cleanup ()
3847 cut_buffer->clear_regions ();
3848 cut_buffer->clear_playlists ();
3850 selection->clear_regions ();
3851 selection->clear_playlists ();
3853 _regions->suspend_redisplay ();
3857 Editor::finish_cleanup ()
3859 _regions->resume_redisplay ();
3863 Editor::transport_loop_location()
3866 return _session->locations()->auto_loop_location();
3873 Editor::transport_punch_location()
3876 return _session->locations()->auto_punch_location();
3883 Editor::control_layout_scroll (GdkEventScroll* ev)
3885 if (Keyboard::some_magic_widget_has_focus()) {
3889 switch (ev->direction) {
3891 scroll_tracks_up_line ();
3895 case GDK_SCROLL_DOWN:
3896 scroll_tracks_down_line ();
3900 /* no left/right handling yet */
3908 Editor::session_state_saved (string)
3911 _snapshots->redisplay ();
3915 Editor::maximise_editing_space ()
3917 _mouse_mode_tearoff->set_visible (false);
3918 _tools_tearoff->set_visible (false);
3919 _zoom_tearoff->set_visible (false);
3921 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
3922 pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
3923 pre_maximal_editor_width = this->get_width ();
3924 pre_maximal_editor_height = this->get_height ();
3926 if (post_maximal_horizontal_pane_position == 0) {
3927 post_maximal_horizontal_pane_position = edit_pane.get_width();
3930 if (post_maximal_vertical_pane_position == 0) {
3931 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
3936 if (post_maximal_editor_width) {
3937 edit_pane.set_position (post_maximal_horizontal_pane_position -
3938 abs(post_maximal_editor_width - pre_maximal_editor_width));
3940 edit_pane.set_position (post_maximal_horizontal_pane_position);
3943 /* Hack: we must do this in an idle handler for it to work; see comment in
3944 restore_editing_space()
3947 Glib::signal_idle().connect (
3949 sigc::mem_fun (*this, &Editor::idle_reset_vertical_pane_position),
3950 post_maximal_vertical_pane_position
3954 if (Config->get_keep_tearoffs()) {
3955 _mouse_mode_tearoff->set_visible (true);
3956 _tools_tearoff->set_visible (true);
3957 if (Config->get_show_zoom_tools ()) {
3958 _zoom_tearoff->set_visible (true);
3965 Editor::idle_reset_vertical_pane_position (int p)
3967 editor_summary_pane.set_position (p);
3972 Editor::restore_editing_space ()
3974 // user changed width/height of panes during fullscreen
3976 if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
3977 post_maximal_horizontal_pane_position = edit_pane.get_position();
3980 if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
3981 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
3986 _mouse_mode_tearoff->set_visible (true);
3987 _tools_tearoff->set_visible (true);
3988 if (Config->get_show_zoom_tools ()) {
3989 _zoom_tearoff->set_visible (true);
3991 post_maximal_editor_width = this->get_width();
3992 post_maximal_editor_height = this->get_height();
3994 edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
3996 /* This is a bit of a hack, but it seems that if you set the vertical pane position
3997 here it gets reset to some wrong value after this method has finished. Doing
3998 the setup in an idle callback seems to work.
4000 Glib::signal_idle().connect (
4002 sigc::mem_fun (*this, &Editor::idle_reset_vertical_pane_position),
4003 pre_maximal_vertical_pane_position
4009 * Make new playlists for a given track and also any others that belong
4010 * to the same active route group with the `edit' property.
4015 Editor::new_playlists (TimeAxisView* v)
4017 begin_reversible_command (_("new playlists"));
4018 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4019 _session->playlists->get (playlists);
4020 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4021 commit_reversible_command ();
4025 * Use a copy of the current playlist for a given track and also any others that belong
4026 * to the same active route group with the `edit' property.
4031 Editor::copy_playlists (TimeAxisView* v)
4033 begin_reversible_command (_("copy playlists"));
4034 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4035 _session->playlists->get (playlists);
4036 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4037 commit_reversible_command ();
4040 /** Clear the current playlist for a given track and also any others that belong
4041 * to the same active route group with the `edit' property.
4046 Editor::clear_playlists (TimeAxisView* v)
4048 begin_reversible_command (_("clear playlists"));
4049 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4050 _session->playlists->get (playlists);
4051 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4052 commit_reversible_command ();
4056 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4058 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4062 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4064 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4068 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4070 atv.clear_playlist ();
4074 Editor::on_key_press_event (GdkEventKey* ev)
4076 return key_press_focus_accelerator_handler (*this, ev);
4080 Editor::on_key_release_event (GdkEventKey* ev)
4082 return Gtk::Window::on_key_release_event (ev);
4083 // return key_press_focus_accelerator_handler (*this, ev);
4086 /** Queue up a change to the viewport x origin.
4087 * @param frame New x origin.
4090 Editor::reset_x_origin (framepos_t frame)
4092 queue_visual_change (frame);
4096 Editor::reset_y_origin (double y)
4098 queue_visual_change_y (y);
4102 Editor::reset_zoom (double fpu)
4104 queue_visual_change (fpu);
4108 Editor::reposition_and_zoom (framepos_t frame, double fpu)
4110 reset_x_origin (frame);
4113 if (!no_save_visual) {
4114 undo_visual_stack.push_back (current_visual_state(false));
4118 Editor::VisualState::VisualState ()
4119 : gui_state (new GUIObjectState)
4123 Editor::VisualState::~VisualState ()
4128 Editor::VisualState*
4129 Editor::current_visual_state (bool with_tracks)
4131 VisualState* vs = new VisualState;
4132 vs->y_position = vertical_adjustment.get_value();
4133 vs->frames_per_unit = frames_per_unit;
4134 vs->leftmost_frame = leftmost_frame;
4135 vs->zoom_focus = zoom_focus;
4138 *(vs->gui_state) = *ARDOUR_UI::instance()->gui_object_state;
4145 Editor::undo_visual_state ()
4147 if (undo_visual_stack.empty()) {
4151 redo_visual_stack.push_back (current_visual_state());
4153 VisualState* vs = undo_visual_stack.back();
4154 undo_visual_stack.pop_back();
4155 use_visual_state (*vs);
4159 Editor::redo_visual_state ()
4161 if (redo_visual_stack.empty()) {
4165 undo_visual_stack.push_back (current_visual_state());
4167 VisualState* vs = redo_visual_stack.back();
4168 redo_visual_stack.pop_back();
4169 use_visual_state (*vs);
4173 Editor::swap_visual_state ()
4175 if (undo_visual_stack.empty()) {
4176 redo_visual_state ();
4178 undo_visual_state ();
4183 Editor::use_visual_state (VisualState& vs)
4185 no_save_visual = true;
4187 _routes->suspend_redisplay ();
4189 vertical_adjustment.set_value (vs.y_position);
4191 set_zoom_focus (vs.zoom_focus);
4192 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4194 *ARDOUR_UI::instance()->gui_object_state = *vs.gui_state;
4196 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4197 (*i)->reset_visual_state ();
4200 _routes->update_visibility ();
4201 _routes->resume_redisplay ();
4203 no_save_visual = false;
4207 Editor::set_frames_per_unit (double fpu)
4209 /* this is the core function that controls the zoom level of the canvas. it is called
4210 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4213 if (fpu == frames_per_unit) {
4222 /* don't allow zooms that fit more than the maximum number
4223 of frames into an 800 pixel wide space.
4226 if (max_framepos / fpu < 800.0) {
4231 tempo_lines->tempo_map_changed();
4233 frames_per_unit = fpu;
4238 Editor::post_zoom ()
4240 // convert fpu to frame count
4242 framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width);
4244 if (frames_per_unit != zoom_range_clock->current_duration()) {
4245 zoom_range_clock->set (frames);
4248 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4249 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4250 (*i)->reshow_selection (selection->time);
4254 ZoomChanged (); /* EMIT_SIGNAL */
4256 //reset_scrolling_region ();
4258 if (playhead_cursor) {
4259 playhead_cursor->set_position (playhead_cursor->current_frame);
4262 refresh_location_display();
4263 _summary->set_overlays_dirty ();
4265 update_marker_labels ();
4271 Editor::queue_visual_change (framepos_t where)
4273 pending_visual_change.add (VisualChange::TimeOrigin);
4274 pending_visual_change.time_origin = where;
4275 ensure_visual_change_idle_handler ();
4279 Editor::queue_visual_change (double fpu)
4281 pending_visual_change.add (VisualChange::ZoomLevel);
4282 pending_visual_change.frames_per_unit = fpu;
4284 ensure_visual_change_idle_handler ();
4288 Editor::queue_visual_change_y (double y)
4290 pending_visual_change.add (VisualChange::YOrigin);
4291 pending_visual_change.y_origin = y;
4293 ensure_visual_change_idle_handler ();
4297 Editor::ensure_visual_change_idle_handler ()
4299 if (pending_visual_change.idle_handler_id < 0) {
4300 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4305 Editor::_idle_visual_changer (void* arg)
4307 return static_cast<Editor*>(arg)->idle_visual_changer ();
4311 Editor::idle_visual_changer ()
4313 VisualChange::Type p = pending_visual_change.pending;
4314 pending_visual_change.pending = (VisualChange::Type) 0;
4316 double const last_time_origin = horizontal_position ();
4318 if (p & VisualChange::TimeOrigin) {
4319 /* This is a bit of a hack, but set_frames_per_unit
4320 below will (if called) end up with the
4321 CrossfadeViews looking at Editor::leftmost_frame,
4322 and if we're changing origin and zoom in the same
4323 operation it will be the wrong value unless we
4327 leftmost_frame = pending_visual_change.time_origin;
4330 if (p & VisualChange::ZoomLevel) {
4331 set_frames_per_unit (pending_visual_change.frames_per_unit);
4333 compute_fixed_ruler_scale ();
4334 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4335 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4336 update_tempo_based_rulers ();
4338 if (p & VisualChange::TimeOrigin) {
4339 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4341 if (p & VisualChange::YOrigin) {
4342 vertical_adjustment.set_value (pending_visual_change.y_origin);
4345 if (last_time_origin == horizontal_position ()) {
4346 /* changed signal not emitted */
4347 update_fixed_rulers ();
4348 redisplay_tempo (true);
4351 _summary->set_overlays_dirty ();
4353 pending_visual_change.idle_handler_id = -1;
4354 return 0; /* this is always a one-shot call */
4357 struct EditorOrderTimeAxisSorter {
4358 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4359 return a->order () < b->order ();
4364 Editor::sort_track_selection (TrackViewList* sel)
4366 EditorOrderTimeAxisSorter cmp;
4371 selection->tracks.sort (cmp);
4376 Editor::get_preferred_edit_position (bool ignore_playhead)
4379 framepos_t where = 0;
4380 EditPoint ep = _edit_point;
4382 if (entered_marker) {
4383 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position()));
4384 return entered_marker->position();
4387 if (ignore_playhead && ep == EditAtPlayhead) {
4388 ep = EditAtSelectedMarker;
4392 case EditAtPlayhead:
4393 where = _session->audible_frame();
4394 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where));
4397 case EditAtSelectedMarker:
4398 if (!selection->markers.empty()) {
4400 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4403 where = loc->start();
4407 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where));
4415 if (!mouse_frame (where, ignored)) {
4416 /* XXX not right but what can we do ? */
4420 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where));
4428 Editor::set_loop_range (framepos_t start, framepos_t end, string cmd)
4430 if (!_session) return;
4432 begin_reversible_command (cmd);
4436 if ((tll = transport_loop_location()) == 0) {
4437 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop);
4438 XMLNode &before = _session->locations()->get_state();
4439 _session->locations()->add (loc, true);
4440 _session->set_auto_loop_location (loc);
4441 XMLNode &after = _session->locations()->get_state();
4442 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4444 XMLNode &before = tll->get_state();
4445 tll->set_hidden (false, this);
4446 tll->set (start, end);
4447 XMLNode &after = tll->get_state();
4448 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4451 commit_reversible_command ();
4455 Editor::set_punch_range (framepos_t start, framepos_t end, string cmd)
4457 if (!_session) return;
4459 begin_reversible_command (cmd);
4463 if ((tpl = transport_punch_location()) == 0) {
4464 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoPunch);
4465 XMLNode &before = _session->locations()->get_state();
4466 _session->locations()->add (loc, true);
4467 _session->set_auto_loop_location (loc);
4468 XMLNode &after = _session->locations()->get_state();
4469 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4472 XMLNode &before = tpl->get_state();
4473 tpl->set_hidden (false, this);
4474 tpl->set (start, end);
4475 XMLNode &after = tpl->get_state();
4476 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4479 commit_reversible_command ();
4482 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4483 * @param rs List to which found regions are added.
4484 * @param where Time to look at.
4485 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4488 Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4490 const TrackViewList* tracks;
4493 tracks = &track_views;
4498 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4500 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4503 boost::shared_ptr<Track> tr;
4504 boost::shared_ptr<Playlist> pl;
4506 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4508 Playlist::RegionList* regions = pl->regions_at (
4509 (framepos_t) floor ( (double) where * tr->speed()));
4511 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4512 RegionView* rv = rtv->view()->find_view (*i);
4525 Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4527 const TrackViewList* tracks;
4530 tracks = &track_views;
4535 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4536 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4538 boost::shared_ptr<Track> tr;
4539 boost::shared_ptr<Playlist> pl;
4541 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4543 Playlist::RegionList* regions = pl->regions_touched (
4544 (framepos_t) floor ( (double)where * tr->speed()), max_framepos);
4546 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4548 RegionView* rv = rtv->view()->find_view (*i);
4561 /** Start with regions that are selected. Then add equivalent regions
4562 * on tracks in the same active edit-enabled route group as any of
4563 * the regions that we started with.
4567 Editor::get_regions_from_selection ()
4569 return get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
4572 /** Get regions using the following method:
4574 * Make an initial region list using the selected regions, unless
4575 * the edit point is `mouse' and the mouse is over an unselected
4576 * region. In this case, start with just that region.
4578 * Then, make an initial track list of the tracks that these
4579 * regions are on, and if the edit point is not `mouse', add the
4582 * Look at this track list and add any other tracks that are on the
4583 * same active edit-enabled route group as one of the initial tracks.
4585 * Finally take the initial region list and add any regions that are
4586 * under the edit point on one of the tracks on the track list to get
4587 * the returned region list.
4589 * The rationale here is that the mouse edit point is special in that
4590 * its position describes both a time and a track; the other edit
4591 * modes only describe a time. Hence if the edit point is `mouse' we
4592 * ignore selected tracks, as we assume the user means something by
4593 * pointing at a particular track. Also in this case we take note of
4594 * the region directly under the edit point, as there is always just one
4595 * (rather than possibly several with non-mouse edit points).
4599 Editor::get_regions_from_selection_and_edit_point ()
4601 RegionSelection regions;
4603 if (_edit_point == EditAtMouse && entered_regionview && !selection->regions.contains (entered_regionview)) {
4604 regions.add (entered_regionview);
4606 regions = selection->regions;
4609 TrackViewList tracks;
4611 if (_edit_point != EditAtMouse) {
4612 tracks = selection->tracks;
4615 /* Add any other tracks that have regions that are in the same
4616 edit-activated route group as one of our regions.
4618 for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) {
4620 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4622 if (g && g->is_active() && g->is_edit()) {
4623 tracks.add (axis_views_from_routes (g->route_list()));
4627 if (!tracks.empty()) {
4628 /* now find regions that are at the edit position on those tracks */
4629 framepos_t const where = get_preferred_edit_position ();
4630 get_regions_at (regions, where, tracks);
4636 /** Start with regions that are selected, or the entered regionview if none are selected.
4637 * Then add equivalent regions on tracks in the same active edit-enabled route group as any
4638 * of the regions that we started with.
4642 Editor::get_regions_from_selection_and_entered ()
4644 RegionSelection regions = selection->regions;
4646 if (regions.empty() && entered_regionview) {
4647 regions.add (entered_regionview);
4650 return get_equivalent_regions (regions, ARDOUR::Properties::edit.property_id);
4654 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4656 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4658 RouteTimeAxisView* tatv;
4660 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4662 boost::shared_ptr<Playlist> pl;
4663 vector<boost::shared_ptr<Region> > results;
4665 boost::shared_ptr<Track> tr;
4667 if ((tr = tatv->track()) == 0) {
4672 if ((pl = (tr->playlist())) != 0) {
4673 pl->get_region_list_equivalent_regions (region, results);
4676 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4677 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4678 regions.push_back (marv);
4687 Editor::show_rhythm_ferret ()
4689 if (rhythm_ferret == 0) {
4690 rhythm_ferret = new RhythmFerret(*this);
4693 rhythm_ferret->set_session (_session);
4694 rhythm_ferret->show ();
4695 rhythm_ferret->present ();
4699 Editor::first_idle ()
4701 MessageDialog* dialog = 0;
4703 if (track_views.size() > 1) {
4704 dialog = new MessageDialog (
4706 string_compose (_("Please wait while %1 loads visual data."), PROGRAM_NAME),
4710 ARDOUR_UI::instance()->flush_pending ();
4713 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4717 // first idle adds route children (automation tracks), so we need to redisplay here
4718 _routes->redisplay ();
4725 Editor::_idle_resize (gpointer arg)
4727 return ((Editor*)arg)->idle_resize ();
4731 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4733 if (resize_idle_id < 0) {
4734 resize_idle_id = g_idle_add (_idle_resize, this);
4735 _pending_resize_amount = 0;
4738 /* make a note of the smallest resulting height, so that we can clamp the
4739 lower limit at TimeAxisView::hSmall */
4741 int32_t min_resulting = INT32_MAX;
4743 _pending_resize_amount += h;
4744 _pending_resize_view = view;
4746 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4748 if (selection->tracks.contains (_pending_resize_view)) {
4749 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4750 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4754 if (min_resulting < 0) {
4759 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4760 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4764 /** Handle pending resizing of tracks */
4766 Editor::idle_resize ()
4768 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4770 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4771 selection->tracks.contains (_pending_resize_view)) {
4773 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4774 if (*i != _pending_resize_view) {
4775 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4780 _pending_resize_amount = 0;
4782 _group_tabs->set_dirty ();
4783 resize_idle_id = -1;
4791 ENSURE_GUI_THREAD (*this, &Editor::located);
4793 playhead_cursor->set_position (_session->audible_frame ());
4794 if (_follow_playhead && !_pending_initial_locate) {
4795 reset_x_origin_to_follow_playhead ();
4798 _pending_locate_request = false;
4799 _pending_initial_locate = false;
4803 Editor::region_view_added (RegionView *)
4805 _summary->set_dirty ();
4809 Editor::region_view_removed ()
4811 _summary->set_dirty ();
4815 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4817 TrackViewList::const_iterator j = track_views.begin ();
4818 while (j != track_views.end()) {
4819 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4820 if (rtv && rtv->route() == r) {
4831 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4835 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4836 TimeAxisView* tv = axis_view_from_route (*i);
4847 Editor::handle_new_route (RouteList& routes)
4849 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4851 RouteTimeAxisView *rtv;
4852 list<RouteTimeAxisView*> new_views;
4854 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4855 boost::shared_ptr<Route> route = (*x);
4857 if (route->is_hidden() || route->is_monitor()) {
4861 DataType dt = route->input()->default_type();
4863 if (dt == ARDOUR::DataType::AUDIO) {
4864 rtv = new AudioTimeAxisView (*this, _session, *track_canvas);
4865 rtv->set_route (route);
4866 } else if (dt == ARDOUR::DataType::MIDI) {
4867 rtv = new MidiTimeAxisView (*this, _session, *track_canvas);
4868 rtv->set_route (route);
4870 throw unknown_type();
4873 new_views.push_back (rtv);
4874 track_views.push_back (rtv);
4876 rtv->effective_gain_display ();
4878 if (internal_editing()) {
4879 rtv->enter_internal_edit_mode ();
4881 rtv->leave_internal_edit_mode ();
4884 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4885 rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed));
4888 _routes->routes_added (new_views);
4889 _summary->routes_added (new_views);
4891 if (show_editor_mixer_when_tracks_arrive) {
4892 show_editor_mixer (true);
4895 editor_list_button.set_sensitive (true);
4899 Editor::timeaxisview_deleted (TimeAxisView *tv)
4901 if (_session && _session->deletion_in_progress()) {
4902 /* the situation is under control */
4906 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
4908 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
4910 _routes->route_removed (tv);
4912 if (tv == entered_track) {
4916 TimeAxisView::Children c = tv->get_child_list ();
4917 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
4918 if (entered_track == i->get()) {
4923 /* remove it from the list of track views */
4925 TrackViewList::iterator i;
4927 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
4928 i = track_views.erase (i);
4931 /* update whatever the current mixer strip is displaying, if revelant */
4933 boost::shared_ptr<Route> route;
4936 route = rtav->route ();
4939 if (current_mixer_strip && current_mixer_strip->route() == route) {
4941 TimeAxisView* next_tv;
4943 if (track_views.empty()) {
4945 } else if (i == track_views.end()) {
4946 next_tv = track_views.front();
4953 set_selected_mixer_strip (*next_tv);
4955 /* make the editor mixer strip go away setting the
4956 * button to inactive (which also unticks the menu option)
4959 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
4965 Editor::hide_track_in_display (TimeAxisView* tv, bool apply_to_selection)
4967 if (apply_to_selection) {
4968 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ) {
4970 TrackSelection::iterator j = i;
4973 hide_track_in_display (*i, false);
4978 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
4980 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
4981 // this will hide the mixer strip
4982 set_selected_mixer_strip (*tv);
4985 _routes->hide_track_in_display (*tv);
4990 Editor::sync_track_view_list_and_routes ()
4992 track_views = TrackViewList (_routes->views ());
4994 _summary->set_dirty ();
4995 _group_tabs->set_dirty ();
4997 return false; // do not call again (until needed)
5001 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
5003 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5008 /** Find a RouteTimeAxisView by the ID of its route */
5010 Editor::get_route_view_by_route_id (const PBD::ID& id) const
5012 RouteTimeAxisView* v;
5014 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5015 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5016 if(v->route()->id() == id) {
5026 Editor::fit_route_group (RouteGroup *g)
5028 TrackViewList ts = axis_views_from_routes (g->route_list ());
5033 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5035 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5038 _session->cancel_audition ();
5042 if (_session->is_auditioning()) {
5043 _session->cancel_audition ();
5044 if (r == last_audition_region) {
5049 _session->audition_region (r);
5050 last_audition_region = r;
5055 Editor::hide_a_region (boost::shared_ptr<Region> r)
5057 r->set_hidden (true);
5061 Editor::show_a_region (boost::shared_ptr<Region> r)
5063 r->set_hidden (false);
5067 Editor::audition_region_from_region_list ()
5069 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5073 Editor::hide_region_from_region_list ()
5075 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5079 Editor::show_region_in_region_list ()
5081 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5085 Editor::step_edit_status_change (bool yn)
5088 start_step_editing ();
5090 stop_step_editing ();
5095 Editor::start_step_editing ()
5097 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5101 Editor::stop_step_editing ()
5103 step_edit_connection.disconnect ();
5107 Editor::check_step_edit ()
5109 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5110 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5112 mtv->check_step_edit ();
5116 return true; // do it again, till we stop
5120 Editor::scroll_press (Direction dir)
5122 ++_scroll_callbacks;
5124 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5125 /* delay the first auto-repeat */
5131 scroll_backward (1);
5139 scroll_tracks_up_line ();
5143 scroll_tracks_down_line ();
5147 /* do hacky auto-repeat */
5148 if (!_scroll_connection.connected ()) {
5150 _scroll_connection = Glib::signal_timeout().connect (
5151 sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), dir), 100
5154 _scroll_callbacks = 0;
5161 Editor::scroll_release ()
5163 _scroll_connection.disconnect ();
5166 /** Queue a change for the Editor viewport x origin to follow the playhead */
5168 Editor::reset_x_origin_to_follow_playhead ()
5170 framepos_t const frame = playhead_cursor->current_frame;
5172 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5174 if (_session->transport_speed() < 0) {
5176 if (frame > (current_page_frames() / 2)) {
5177 center_screen (frame-(current_page_frames()/2));
5179 center_screen (current_page_frames()/2);
5184 if (frame < leftmost_frame) {
5187 if (_session->transport_rolling()) {
5188 /* rolling; end up with the playhead at the right of the page */
5189 l = frame - current_page_frames ();
5191 /* not rolling: end up with the playhead 3/4 of the way along the page */
5192 l = frame - (3 * current_page_frames() / 4);
5199 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5202 if (_session->transport_rolling()) {
5203 /* rolling: end up with the playhead on the left of the page */
5204 center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5206 /* not rolling: end up with the playhead 1/4 of the way along the page */
5207 center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5215 Editor::super_rapid_screen_update ()
5217 if (!_session || !_session->engine().running()) {
5221 /* METERING / MIXER STRIPS */
5223 /* update track meters, if required */
5224 if (is_mapped() && meters_running) {
5225 RouteTimeAxisView* rtv;
5226 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5227 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5228 rtv->fast_update ();
5233 /* and any current mixer strip */
5234 if (current_mixer_strip) {
5235 current_mixer_strip->fast_update ();
5238 /* PLAYHEAD AND VIEWPORT */
5240 framepos_t const frame = _session->audible_frame();
5242 /* There are a few reasons why we might not update the playhead / viewport stuff:
5244 * 1. we don't update things when there's a pending locate request, otherwise
5245 * when the editor requests a locate there is a chance that this method
5246 * will move the playhead before the locate request is processed, causing
5248 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5249 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5252 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5254 last_update_frame = frame;
5256 if (!_dragging_playhead) {
5257 playhead_cursor->set_position (frame);
5260 if (!_stationary_playhead) {
5262 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5263 reset_x_origin_to_follow_playhead ();
5268 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5272 // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5273 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5274 if (target <= 0.0) {
5277 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5278 target = (target * 0.15) + (current * 0.85);
5284 set_horizontal_position (current);
5293 Editor::session_going_away ()
5295 _have_idled = false;
5297 _session_connections.drop_connections ();
5299 super_rapid_screen_update_connection.disconnect ();
5301 selection->clear ();
5302 cut_buffer->clear ();
5304 clicked_regionview = 0;
5305 clicked_axisview = 0;
5306 clicked_routeview = 0;
5307 clicked_crossfadeview = 0;
5308 entered_regionview = 0;
5310 last_update_frame = 0;
5313 playhead_cursor->canvas_item.hide ();
5315 /* rip everything out of the list displays */
5319 _route_groups->clear ();
5321 /* do this first so that deleting a track doesn't reset cms to null
5322 and thus cause a leak.
5325 if (current_mixer_strip) {
5326 if (current_mixer_strip->get_parent() != 0) {
5327 global_hpacker.remove (*current_mixer_strip);
5329 delete current_mixer_strip;
5330 current_mixer_strip = 0;
5333 /* delete all trackviews */
5335 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5338 track_views.clear ();
5340 zoom_range_clock->set_session (0);
5341 nudge_clock->set_session (0);
5343 editor_list_button.set_active(false);
5344 editor_list_button.set_sensitive(false);
5346 /* clear tempo/meter rulers */
5347 remove_metric_marks ();
5349 clear_marker_display ();
5351 delete current_bbt_points;
5352 current_bbt_points = 0;
5354 /* get rid of any existing editor mixer strip */
5356 WindowTitle title(Glib::get_application_name());
5357 title += _("Editor");
5359 set_title (title.get_string());
5361 SessionHandlePtr::session_going_away ();
5366 Editor::show_editor_list (bool yn)
5369 _the_notebook.show ();
5371 _the_notebook.hide ();
5376 Editor::change_region_layering_order ()
5378 framepos_t const position = get_preferred_edit_position ();
5380 if (!clicked_routeview) {
5381 if (layering_order_editor) {
5382 layering_order_editor->hide ();
5387 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
5393 boost::shared_ptr<Playlist> pl = track->playlist();
5399 if (layering_order_editor == 0) {
5400 layering_order_editor = new RegionLayeringOrderEditor(*this);
5403 layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position);
5404 layering_order_editor->maybe_present ();
5408 Editor::update_region_layering_order_editor ()
5410 if (layering_order_editor && layering_order_editor->is_visible ()) {
5411 change_region_layering_order ();
5416 Editor::setup_fade_images ()
5418 _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear")));
5419 _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut")));
5420 _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut")));
5421 _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut")));
5422 _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut")));
5424 _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear")));
5425 _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut")));
5426 _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut")));
5427 _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut")));
5428 _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut")));
5432 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
5434 Editor::action_menu_item (std::string const & name)
5436 Glib::RefPtr<Action> a = editor_actions->get_action (name);
5439 return *manage (a->create_menu_item ());
5443 Editor::resize_text_widgets ()
5445 set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_FUDGE+10, 15);
5446 set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_FUDGE+10, 15);
5447 set_size_request_to_display_given_text (snap_type_selector, snap_type_strings, COMBO_FUDGE+10, 15);
5448 set_size_request_to_display_given_text (snap_mode_selector, snap_mode_strings, COMBO_FUDGE+10, 15);
5449 set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_FUDGE+10, 15);
5453 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
5455 EventBox* b = manage (new EventBox);
5456 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
5457 Label* l = manage (new Label (name));
5461 _the_notebook.append_page (widget, *b);
5465 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
5467 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
5468 _the_notebook.set_current_page (_the_notebook.page_num (*page));
5471 if (ev->type == GDK_2BUTTON_PRESS) {
5473 /* double-click on a notebook tab shrinks or expands the notebook */
5475 if (_notebook_shrunk) {
5476 edit_pane.set_position (pre_maximal_horizontal_pane_position);
5477 _notebook_shrunk = false;
5479 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
5480 edit_pane.set_position (edit_pane.get_position() + page->get_width());
5481 _notebook_shrunk = true;
5489 Editor::popup_control_point_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
5491 using namespace Menu_Helpers;
5493 MenuList& items = _control_point_context_menu.items ();
5496 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun (*this, &Editor::edit_control_point), item)));
5497 items.push_back (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun (*this, &Editor::remove_control_point), item)));
5498 if (!can_remove_control_point (item)) {
5499 items.back().set_sensitive (false);
5502 _control_point_context_menu.popup (event->button.button, event->button.time);