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 */
22 #define __STDC_LIMIT_MACROS 1
31 #include "ardour_ui.h"
33 * ardour_ui.h include was moved to the top of the list
34 * due to a conflicting definition of 'Style' between
35 * Apple's MacTypes.h and BarController.
38 #include <boost/none.hpp>
40 #include <sigc++/bind.h>
42 #include "pbd/convert.h"
43 #include "pbd/error.h"
44 #include "pbd/enumwriter.h"
45 #include "pbd/memento_command.h"
46 #include "pbd/unknown_type.h"
48 #include <glibmm/miscutils.h>
49 #include <gtkmm/image.h>
50 #include <gdkmm/color.h>
51 #include <gdkmm/bitmap.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_diskstream.h"
62 #include "ardour/audio_track.h"
63 #include "ardour/audioplaylist.h"
64 #include "ardour/audioregion.h"
65 #include "ardour/location.h"
66 #include "ardour/midi_region.h"
67 #include "ardour/plugin_manager.h"
68 #include "ardour/profile.h"
69 #include "ardour/route_group.h"
70 #include "ardour/session_directory.h"
71 #include "ardour/session_route.h"
72 #include "ardour/session_state_utils.h"
73 #include "ardour/tempo.h"
74 #include "ardour/utils.h"
75 #include "ardour/session_playlists.h"
76 #include "ardour/audioengine.h"
78 #include "control_protocol/control_protocol.h"
83 #include "playlist_selector.h"
84 #include "audio_region_view.h"
85 #include "rgb_macros.h"
86 #include "selection.h"
87 #include "audio_streamview.h"
88 #include "time_axis_view.h"
89 #include "audio_time_axis.h"
91 #include "crossfade_view.h"
92 #include "canvas-noevent-text.h"
94 #include "public_editor.h"
95 #include "crossfade_edit.h"
96 #include "canvas_impl.h"
99 #include "gui_thread.h"
100 #include "simpleline.h"
101 #include "rhythm_ferret.h"
103 #include "tempo_lines.h"
104 #include "analysis_window.h"
105 #include "bundle_manager.h"
106 #include "global_port_matrix.h"
107 #include "editor_drag.h"
108 #include "editor_group_tabs.h"
109 #include "automation_time_axis.h"
110 #include "editor_routes.h"
111 #include "midi_time_axis.h"
112 #include "mixer_strip.h"
113 #include "editor_route_groups.h"
114 #include "editor_regions.h"
115 #include "editor_locations.h"
116 #include "editor_snapshots.h"
117 #include "editor_summary.h"
118 #include "region_layering_order_editor.h"
123 #include "imageframe_socket_handler.h"
127 using namespace ARDOUR;
130 using namespace Glib;
131 using namespace Gtkmm2ext;
132 using namespace Editing;
134 using PBD::internationalize;
136 using Gtkmm2ext::Keyboard;
138 const double Editor::timebar_height = 15.0;
140 #include "editor_xpms"
142 static const gchar *_snap_type_strings[] = {
144 N_("Timecode Frames"),
145 N_("Timecode Seconds"),
146 N_("Timecode Minutes"),
174 static const gchar *_snap_mode_strings[] = {
181 static const gchar *_edit_point_strings[] = {
188 static const gchar *_zoom_focus_strings[] = {
198 #ifdef USE_RUBBERBAND
199 static const gchar *_rb_opt_strings[] = {
202 N_("Balanced multitimbral mixture"),
203 N_("Unpitched percussion with stable notes"),
204 N_("Crisp monophonic instrumental"),
205 N_("Unpitched solo percussion"),
206 N_("Resample without preserving pitch"),
211 /* Soundfile drag-n-drop */
213 Gdk::Cursor* Editor::cross_hair_cursor = 0;
214 Gdk::Cursor* Editor::selector_cursor = 0;
215 Gdk::Cursor* Editor::trimmer_cursor = 0;
216 Gdk::Cursor* Editor::left_side_trim_cursor = 0;
217 Gdk::Cursor* Editor::right_side_trim_cursor = 0;
218 Gdk::Cursor* Editor::fade_in_cursor = 0;
219 Gdk::Cursor* Editor::fade_out_cursor = 0;
220 Gdk::Cursor* Editor::grabber_cursor = 0;
221 Gdk::Cursor* Editor::grabber_note_cursor = 0;
222 Gdk::Cursor* Editor::grabber_edit_point_cursor = 0;
223 Gdk::Cursor* Editor::zoom_in_cursor = 0;
224 Gdk::Cursor* Editor::zoom_out_cursor = 0;
225 Gdk::Cursor* Editor::time_fx_cursor = 0;
226 Gdk::Cursor* Editor::fader_cursor = 0;
227 Gdk::Cursor* Editor::speaker_cursor = 0;
228 Gdk::Cursor* Editor::midi_pencil_cursor = 0;
229 Gdk::Cursor* Editor::midi_select_cursor = 0;
230 Gdk::Cursor* Editor::midi_resize_cursor = 0;
231 Gdk::Cursor* Editor::midi_erase_cursor = 0;
232 Gdk::Cursor* Editor::wait_cursor = 0;
233 Gdk::Cursor* Editor::timebar_cursor = 0;
234 Gdk::Cursor* Editor::transparent_cursor = 0;
235 Gdk::Cursor* Editor::up_down_cursor = 0;
238 show_me_the_size (Requisition* r, const char* what)
240 cerr << "size of " << what << " = " << r->width << " x " << r->height << endl;
245 pane_size_watcher (Paned* pane)
247 /* if the handle of a pane vanishes into (at least) the tabs of a notebook,
248 it is no longer accessible. so stop that. this doesn't happen on X11,
249 just the quartz backend.
254 int max_width_of_lhs = GTK_WIDGET(pane->gobj())->allocation.width - 25;
256 gint pos = pane->get_position ();
258 if (pos > max_width_of_lhs) {
259 pane->set_position (max_width_of_lhs);
265 : _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
267 /* time display buttons */
268 , minsec_label (_("Mins:Secs"))
269 , bbt_label (_("Bars:Beats"))
270 , timecode_label (_("Timecode"))
271 , frame_label (_("Samples"))
272 , tempo_label (_("Tempo"))
273 , meter_label (_("Meter"))
274 , mark_label (_("Location Markers"))
275 , range_mark_label (_("Range Markers"))
276 , transport_mark_label (_("Loop/Punch Ranges"))
277 , cd_mark_label (_("CD Markers"))
278 , edit_packer (4, 4, true)
280 /* the values here don't matter: layout widgets
281 reset them as needed.
284 , vertical_adjustment (0.0, 0.0, 10.0, 400.0)
286 /* tool bar related */
288 , zoom_range_clock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, false, true)
290 , toolbar_selection_clock_table (2,3)
292 , automation_mode_button (_("mode"))
293 , global_automation_button (_("automation"))
295 , midi_panic_button (_("Panic"))
298 , image_socket_listener(0)
303 , nudge_clock (X_("nudge"), false, X_("NudgeClock"), true, false, true)
304 , meters_running(false)
305 , _pending_locate_request (false)
306 , _pending_initial_locate (false)
307 , _last_cut_copy_source_track (0)
309 , _region_selection_change_updates_region_list (true)
313 /* we are a singleton */
315 PublicEditor::_instance = this;
319 selection = new Selection (this);
320 cut_buffer = new Selection (this);
322 clicked_regionview = 0;
323 clicked_axisview = 0;
324 clicked_routeview = 0;
325 clicked_crossfadeview = 0;
326 clicked_control_point = 0;
327 last_update_frame = 0;
328 pre_press_cursor = 0;
329 _drags = new DragManager (this);
330 current_mixer_strip = 0;
331 current_bbt_points = 0;
334 snap_type_strings = I18N (_snap_type_strings);
335 snap_mode_strings = I18N (_snap_mode_strings);
336 zoom_focus_strings = I18N (_zoom_focus_strings);
337 edit_point_strings = I18N (_edit_point_strings);
338 #ifdef USE_RUBBERBAND
339 rb_opt_strings = I18N (_rb_opt_strings);
343 snap_threshold = 5.0;
344 bbt_beat_subdivision = 4;
347 last_autoscroll_x = 0;
348 last_autoscroll_y = 0;
349 autoscroll_active = false;
350 autoscroll_timeout_tag = -1;
355 current_interthread_info = 0;
356 _show_measures = true;
357 show_gain_after_trim = false;
358 verbose_cursor_on = true;
359 last_item_entered = 0;
361 have_pending_keyboard_selection = false;
362 _follow_playhead = true;
363 _stationary_playhead = false;
364 _xfade_visibility = true;
365 editor_ruler_menu = 0;
366 no_ruler_shown_update = false;
368 session_range_marker_menu = 0;
369 range_marker_menu = 0;
370 marker_menu_item = 0;
371 tempo_or_meter_marker_menu = 0;
372 transport_marker_menu = 0;
373 new_transport_marker_menu = 0;
374 editor_mixer_strip_width = Wide;
375 show_editor_mixer_when_tracks_arrive = false;
376 region_edit_menu_split_multichannel_item = 0;
377 region_edit_menu_split_item = 0;
380 current_stepping_trackview = 0;
382 entered_regionview = 0;
384 clear_entered_track = false;
387 button_release_can_deselect = true;
388 _dragging_playhead = false;
389 _dragging_edit_point = false;
390 select_new_marker = false;
392 layering_order_editor = 0;
394 no_save_visual = false;
397 scrubbing_direction = 0;
401 location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get();
402 location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get();
403 location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get();
404 location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get();
405 location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get();
407 _edit_point = EditAtMouse;
408 _internal_editing = false;
409 current_canvas_cursor = 0;
411 frames_per_unit = 2048; /* too early to use reset_zoom () */
413 _scroll_callbacks = 0;
415 zoom_focus = ZoomFocusLeft;
416 set_zoom_focus (ZoomFocusLeft);
417 zoom_range_clock.ValueChanged.connect (sigc::mem_fun(*this, &Editor::zoom_adjustment_changed));
419 bbt_label.set_name ("EditorTimeButton");
420 bbt_label.set_size_request (-1, (int)timebar_height);
421 bbt_label.set_alignment (1.0, 0.5);
422 bbt_label.set_padding (5,0);
424 bbt_label.set_no_show_all();
425 minsec_label.set_name ("EditorTimeButton");
426 minsec_label.set_size_request (-1, (int)timebar_height);
427 minsec_label.set_alignment (1.0, 0.5);
428 minsec_label.set_padding (5,0);
429 minsec_label.hide ();
430 minsec_label.set_no_show_all();
431 timecode_label.set_name ("EditorTimeButton");
432 timecode_label.set_size_request (-1, (int)timebar_height);
433 timecode_label.set_alignment (1.0, 0.5);
434 timecode_label.set_padding (5,0);
435 timecode_label.hide ();
436 timecode_label.set_no_show_all();
437 frame_label.set_name ("EditorTimeButton");
438 frame_label.set_size_request (-1, (int)timebar_height);
439 frame_label.set_alignment (1.0, 0.5);
440 frame_label.set_padding (5,0);
442 frame_label.set_no_show_all();
444 tempo_label.set_name ("EditorTimeButton");
445 tempo_label.set_size_request (-1, (int)timebar_height);
446 tempo_label.set_alignment (1.0, 0.5);
447 tempo_label.set_padding (5,0);
449 tempo_label.set_no_show_all();
450 meter_label.set_name ("EditorTimeButton");
451 meter_label.set_size_request (-1, (int)timebar_height);
452 meter_label.set_alignment (1.0, 0.5);
453 meter_label.set_padding (5,0);
455 meter_label.set_no_show_all();
456 mark_label.set_name ("EditorTimeButton");
457 mark_label.set_size_request (-1, (int)timebar_height);
458 mark_label.set_alignment (1.0, 0.5);
459 mark_label.set_padding (5,0);
461 mark_label.set_no_show_all();
462 cd_mark_label.set_name ("EditorTimeButton");
463 cd_mark_label.set_size_request (-1, (int)timebar_height);
464 cd_mark_label.set_alignment (1.0, 0.5);
465 cd_mark_label.set_padding (5,0);
466 cd_mark_label.hide();
467 cd_mark_label.set_no_show_all();
468 range_mark_label.set_name ("EditorTimeButton");
469 range_mark_label.set_size_request (-1, (int)timebar_height);
470 range_mark_label.set_alignment (1.0, 0.5);
471 range_mark_label.set_padding (5,0);
472 range_mark_label.hide();
473 range_mark_label.set_no_show_all();
474 transport_mark_label.set_name ("EditorTimeButton");
475 transport_mark_label.set_size_request (-1, (int)timebar_height);
476 transport_mark_label.set_alignment (1.0, 0.5);
477 transport_mark_label.set_padding (5,0);
478 transport_mark_label.hide();
479 transport_mark_label.set_no_show_all();
481 initialize_rulers ();
482 initialize_canvas ();
483 _summary = new EditorSummary (this);
485 selection->TimeChanged.connect (sigc::mem_fun(*this, &Editor::time_selection_changed));
486 selection->TracksChanged.connect (sigc::mem_fun(*this, &Editor::track_selection_changed));
487 editor_regions_selection_changed_connection = selection->RegionsChanged.connect (sigc::mem_fun(*this, &Editor::region_selection_changed));
488 selection->PointsChanged.connect (sigc::mem_fun(*this, &Editor::point_selection_changed));
489 selection->MarkersChanged.connect (sigc::mem_fun(*this, &Editor::marker_selection_changed));
491 edit_controls_vbox.set_spacing (0);
492 vertical_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &Editor::tie_vertical_scrolling), true);
493 track_canvas->signal_map_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_map_handler));
495 HBox* h = manage (new HBox);
496 _group_tabs = new EditorGroupTabs (this);
497 h->pack_start (*_group_tabs, PACK_SHRINK);
498 h->pack_start (edit_controls_vbox);
499 controls_layout.add (*h);
501 controls_layout.set_name ("EditControlsBase");
502 controls_layout.add_events (Gdk::SCROLL_MASK);
503 controls_layout.signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::control_layout_scroll), false);
505 controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
506 controls_layout.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_release));
507 controls_layout_size_request_connection = controls_layout.signal_size_request().connect (sigc::mem_fun (*this, &Editor::controls_layout_size_request));
511 ArdourCanvas::Canvas* time_pad = manage(new ArdourCanvas::Canvas());
512 ArdourCanvas::SimpleLine* pad_line_1 = manage(new ArdourCanvas::SimpleLine(*time_pad->root(),
513 0.0, 1.0, 100.0, 1.0));
514 pad_line_1->property_color_rgba() = 0xFF0000FF;
518 time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2);
519 time_canvas_vbox.set_size_request (-1, -1);
521 ruler_label_event_box.add (ruler_label_vbox);
522 ruler_label_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
523 ruler_label_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
525 time_button_event_box.add (time_button_vbox);
526 time_button_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
527 time_button_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
529 /* these enable us to have a dedicated window (for cursor setting, etc.)
530 for the canvas areas.
533 track_canvas_event_box.add (*track_canvas);
535 time_canvas_event_box.add (time_canvas_vbox);
536 time_canvas_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
538 edit_packer.set_col_spacings (0);
539 edit_packer.set_row_spacings (0);
540 edit_packer.set_homogeneous (false);
541 edit_packer.set_border_width (0);
542 edit_packer.set_name ("EditorWindow");
544 /* labels for the rulers */
545 edit_packer.attach (ruler_label_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0);
546 /* labels for the marker "tracks" */
547 edit_packer.attach (time_button_event_box, 1, 2, 1, 2, FILL, SHRINK, 0, 0);
549 edit_packer.attach (time_canvas_event_box, 2, 3, 0, 1, FILL|EXPAND, FILL, 0, 0);
551 edit_packer.attach (controls_layout, 0, 2, 2, 3, FILL, FILL|EXPAND, 0, 0);
553 edit_packer.attach (track_canvas_event_box, 2, 3, 1, 3, FILL|EXPAND, FILL|EXPAND, 0, 0);
555 bottom_hbox.set_border_width (2);
556 bottom_hbox.set_spacing (3);
558 _route_groups = new EditorRouteGroups (this);
559 _routes = new EditorRoutes (this);
560 _regions = new EditorRegions (this);
561 _snapshots = new EditorSnapshots (this);
562 _locations = new EditorLocations (this);
566 nlabel = manage (new Label (_("Regions")));
567 nlabel->set_angle (-90);
568 the_notebook.append_page (_regions->widget (), *nlabel);
569 nlabel = manage (new Label (_("Tracks & Busses")));
570 nlabel->set_angle (-90);
571 the_notebook.append_page (_routes->widget (), *nlabel);
572 nlabel = manage (new Label (_("Snapshots")));
573 nlabel->set_angle (-90);
574 the_notebook.append_page (_snapshots->widget (), *nlabel);
575 nlabel = manage (new Label (_("Route Groups")));
576 nlabel->set_angle (-90);
577 the_notebook.append_page (_route_groups->widget (), *nlabel);
578 nlabel = manage (new Label (_("Ranges & Marks")));
579 nlabel->set_angle (-90);
580 the_notebook.append_page (_locations->widget (), *nlabel);
582 the_notebook.set_show_tabs (true);
583 the_notebook.set_scrollable (true);
584 the_notebook.popup_disable ();
585 the_notebook.set_tab_pos (Gtk::POS_RIGHT);
586 the_notebook.show_all ();
588 post_maximal_editor_width = 0;
589 post_maximal_horizontal_pane_position = 0;
590 post_maximal_editor_height = 0;
591 post_maximal_vertical_pane_position = 0;
593 editor_summary_pane.pack1(edit_packer);
595 Button* summary_arrows_left_left = manage (new Button);
596 summary_arrows_left_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
597 summary_arrows_left_left->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press)));
598 summary_arrows_left_left->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_release));
599 Button* summary_arrows_left_right = manage (new Button);
600 summary_arrows_left_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
601 summary_arrows_left_right->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press)));
602 summary_arrows_left_right->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_release));
603 VBox* summary_arrows_left = manage (new VBox);
604 summary_arrows_left->pack_start (*summary_arrows_left_left);
605 summary_arrows_left->pack_start (*summary_arrows_left_right);
607 Button* summary_arrows_right_left = manage (new Button);
608 summary_arrows_right_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
609 summary_arrows_right_left->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press)));
610 summary_arrows_right_left->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_release));
611 Button* summary_arrows_right_right = manage (new Button);
612 summary_arrows_right_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
613 summary_arrows_right_right->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press)));
614 summary_arrows_right_right->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_release));
615 VBox* summary_arrows_right = manage (new VBox);
616 summary_arrows_right->pack_start (*summary_arrows_right_left);
617 summary_arrows_right->pack_start (*summary_arrows_right_right);
619 Frame* summary_frame = manage (new Frame);
620 summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN);
621 summary_frame->add (*_summary);
622 summary_frame->show ();
624 _summary_hbox.pack_start (*summary_arrows_left, false, false);
625 _summary_hbox.pack_start (*summary_frame, true, true);
626 _summary_hbox.pack_start (*summary_arrows_right, false, false);
628 editor_summary_pane.pack2 (_summary_hbox);
630 edit_pane.pack1 (editor_summary_pane, true, true);
631 edit_pane.pack2 (the_notebook, false, true);
633 editor_summary_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun (*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&editor_summary_pane)));
635 /* XXX: editor_summary_pane might need similar special OS X treatment to the edit_pane */
637 edit_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&edit_pane)));
639 Glib::PropertyProxy<int> proxy = edit_pane.property_position();
640 proxy.signal_changed().connect (bind (sigc::ptr_fun (pane_size_watcher), static_cast<Paned*> (&edit_pane)));
642 top_hbox.pack_start (toolbar_frame, false, true);
644 HBox *hbox = manage (new HBox);
645 hbox->pack_start (edit_pane, true, true);
647 global_vpacker.pack_start (top_hbox, false, false);
648 global_vpacker.pack_start (*hbox, true, true);
650 global_hpacker.pack_start (global_vpacker, true, true);
652 set_name ("EditorWindow");
653 add_accel_group (ActionManager::ui_manager->get_accel_group());
655 status_bar_hpacker.show ();
657 vpacker.pack_end (status_bar_hpacker, false, false);
658 vpacker.pack_end (global_hpacker, true, true);
660 /* register actions now so that set_state() can find them and set toggles/checks etc */
665 setup_midi_toolbar ();
667 _snap_type = SnapToBeat;
668 set_snap_to (_snap_type);
669 _snap_mode = SnapOff;
670 set_snap_mode (_snap_mode);
671 set_mouse_mode (MouseObject, true);
672 set_edit_point_preference (EditAtMouse, true);
674 _playlist_selector = new PlaylistSelector();
675 _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
677 RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), ui_bind (&Editor::catch_vanishing_regionview, this, _1), gui_context());
681 nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
682 nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
684 nudge_forward_button.set_name ("TransportButton");
685 nudge_backward_button.set_name ("TransportButton");
687 fade_context_menu.set_name ("ArdourContextMenu");
689 /* icons, titles, WM stuff */
691 list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
692 Glib::RefPtr<Gdk::Pixbuf> icon;
694 if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
695 window_icons.push_back (icon);
697 if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
698 window_icons.push_back (icon);
700 if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
701 window_icons.push_back (icon);
703 if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
704 window_icons.push_back (icon);
706 if (!window_icons.empty()) {
707 set_icon_list (window_icons);
708 set_default_icon_list (window_icons);
711 WindowTitle title(Glib::get_application_name());
712 title += _("Editor");
713 set_title (title.get_string());
714 set_wmclass (X_("ardour_editor"), PROGRAM_NAME);
717 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
719 signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
720 signal_delete_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
722 /* allow external control surfaces/protocols to do various things */
724 ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context());
725 ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context());
726 ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context());
727 ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), ui_bind (&Editor::control_scroll, this, _1), gui_context());
728 BasicUI::AccessAction.connect (*this, invalidator (*this), ui_bind (&Editor::access_action, this, _1, _2), gui_context());
730 /* problematic: has to return a value and thus cannot be x-thread */
732 Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
734 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
736 TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
738 _ignore_region_action = false;
739 _last_region_menu_was_main = false;
740 _popup_region_menu_item = 0;
742 _show_marker_lines = false;
747 setup_fade_images ();
753 if(image_socket_listener) {
754 if(image_socket_listener->is_connected())
756 image_socket_listener->close_connection() ;
759 delete image_socket_listener ;
760 image_socket_listener = 0 ;
765 delete _route_groups;
771 Editor::add_toplevel_controls (Container& cont)
773 vpacker.pack_start (cont, false, false);
778 Editor::catch_vanishing_regionview (RegionView *rv)
780 /* note: the selection will take care of the vanishing
781 audioregionview by itself.
784 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
788 if (clicked_regionview == rv) {
789 clicked_regionview = 0;
792 if (entered_regionview == rv) {
793 set_entered_regionview (0);
796 if (!_all_region_actions_sensitized) {
797 sensitize_all_region_actions (true);
802 Editor::set_entered_regionview (RegionView* rv)
804 if (rv == entered_regionview) {
808 if (entered_regionview) {
809 entered_regionview->exited ();
812 if ((entered_regionview = rv) != 0) {
813 entered_regionview->entered (internal_editing ());
816 if (!_all_region_actions_sensitized && _last_region_menu_was_main) {
817 /* This RegionView entry might have changed what region actions
818 are allowed, so sensitize them all in case a key is pressed.
820 sensitize_all_region_actions (true);
825 Editor::set_entered_track (TimeAxisView* tav)
828 entered_track->exited ();
831 if ((entered_track = tav) != 0) {
832 entered_track->entered ();
837 Editor::show_window ()
839 if (! is_visible ()) {
842 /* re-hide editor list if necessary */
843 editor_list_button_toggled ();
845 /* re-hide summary widget if necessary */
846 parameter_changed ("show-summary");
848 parameter_changed ("show-edit-group-tabs");
850 /* now reset all audio_time_axis heights, because widgets might need
856 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
857 tv = (static_cast<TimeAxisView*>(*i));
866 Editor::instant_save ()
868 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
873 _session->add_instant_xml(get_state());
875 Config->add_instant_xml(get_state());
880 Editor::zoom_adjustment_changed ()
886 double fpu = zoom_range_clock.current_duration() / _canvas_width;
890 zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width));
891 } else if (fpu > _session->current_end_frame() / _canvas_width) {
892 fpu = _session->current_end_frame() / _canvas_width;
893 zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width));
900 Editor::control_scroll (float fraction)
902 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
908 double step = fraction * current_page_frames();
911 _control_scroll_target is an optional<T>
913 it acts like a pointer to an framepos_t, with
914 a operator conversion to boolean to check
915 that it has a value could possibly use
916 playhead_cursor->current_frame to store the
917 value and a boolean in the class to know
918 when it's out of date
921 if (!_control_scroll_target) {
922 _control_scroll_target = _session->transport_frame();
923 _dragging_playhead = true;
926 if ((fraction < 0.0f) && (*_control_scroll_target < (framepos_t) fabs(step))) {
927 *_control_scroll_target = 0;
928 } else if ((fraction > 0.0f) && (max_framepos - *_control_scroll_target < step)) {
929 *_control_scroll_target = max_framepos - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
931 *_control_scroll_target += (framepos_t) floor (step);
934 /* move visuals, we'll catch up with it later */
936 playhead_cursor->set_position (*_control_scroll_target);
937 UpdateAllTransportClocks (*_control_scroll_target);
939 if (*_control_scroll_target > (current_page_frames() / 2)) {
940 /* try to center PH in window */
941 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
947 Now we do a timeout to actually bring the session to the right place
948 according to the playhead. This is to avoid reading disk buffers on every
949 call to control_scroll, which is driven by ScrollTimeline and therefore
950 probably by a control surface wheel which can generate lots of events.
952 /* cancel the existing timeout */
954 control_scroll_connection.disconnect ();
956 /* add the next timeout */
958 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
962 Editor::deferred_control_scroll (framepos_t /*target*/)
964 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
965 // reset for next stream
966 _control_scroll_target = boost::none;
967 _dragging_playhead = false;
972 Editor::access_action (std::string action_group, std::string action_item)
978 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
981 act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
989 Editor::on_realize ()
991 Window::on_realize ();
996 Editor::map_position_change (framepos_t frame)
998 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, frame)
1000 if (_session == 0) {
1004 if (_follow_playhead) {
1005 center_screen (frame);
1008 playhead_cursor->set_position (frame);
1012 Editor::center_screen (framepos_t frame)
1014 double page = _canvas_width * frames_per_unit;
1016 /* if we're off the page, then scroll.
1019 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
1020 center_screen_internal (frame, page);
1025 Editor::center_screen_internal (framepos_t frame, float page)
1030 frame -= (framepos_t) page;
1035 reset_x_origin (frame);
1040 Editor::update_title ()
1042 ENSURE_GUI_THREAD (*this, &Editor::update_title)
1045 bool dirty = _session->dirty();
1047 string session_name;
1049 if (_session->snap_name() != _session->name()) {
1050 session_name = _session->snap_name();
1052 session_name = _session->name();
1056 session_name = "*" + session_name;
1059 WindowTitle title(session_name);
1060 title += Glib::get_application_name();
1061 set_title (title.get_string());
1066 Editor::set_session (Session *t)
1068 SessionHandlePtr::set_session (t);
1074 zoom_range_clock.set_session (_session);
1075 _playlist_selector->set_session (_session);
1076 nudge_clock.set_session (_session);
1077 _summary->set_session (_session);
1078 _group_tabs->set_session (_session);
1079 _route_groups->set_session (_session);
1080 _regions->set_session (_session);
1081 _snapshots->set_session (_session);
1082 _routes->set_session (_session);
1083 _locations->set_session (_session);
1085 if (rhythm_ferret) {
1086 rhythm_ferret->set_session (_session);
1089 if (analysis_window) {
1090 analysis_window->set_session (_session);
1094 sfbrowser->set_session (_session);
1097 compute_fixed_ruler_scale ();
1099 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1100 set_state (*node, Stateful::loading_state_version);
1102 /* catch up with the playhead */
1104 _session->request_locate (playhead_cursor->current_frame);
1105 _pending_initial_locate = true;
1109 /* These signals can all be emitted by a non-GUI thread. Therefore the
1110 handlers for them must not attempt to directly interact with the GUI,
1111 but use Gtkmm2ext::UI::instance()->call_slot();
1114 _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), ui_bind(&Editor::step_edit_status_change, this, _1), gui_context());
1115 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1116 _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context());
1117 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context());
1118 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1119 _session->TimecodeOffsetChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_just_timecode, this), gui_context());
1120 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context());
1121 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1122 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
1123 _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Editor::session_state_saved, this, _1), gui_context());
1124 _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context());
1125 _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&Editor::location_gone, this, _1), gui_context());
1126 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1127 _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display_s, this, _1), gui_context());
1128 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1130 if (Profile->get_sae()) {
1135 nframes_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1);
1136 nudge_clock.set_mode(AudioClock::BBT);
1137 nudge_clock.set (pos, true, 0, AudioClock::BBT);
1140 nudge_clock.set (_session->frame_rate() * 5, true, 0, AudioClock::Timecode); // default of 5 seconds
1143 playhead_cursor->canvas_item.show ();
1145 Location* loc = _session->locations()->auto_loop_location();
1147 loc = new Location (*_session, 0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1148 if (loc->start() == loc->end()) {
1149 loc->set_end (loc->start() + 1);
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();
1160 loc = new Location (*_session, 0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1161 if (loc->start() == loc->end()) {
1162 loc->set_end (loc->start() + 1);
1164 _session->locations()->add (loc, false);
1165 _session->set_auto_punch_location (loc);
1168 loc->set_name (_("Punch"));
1171 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1172 Config->map_parameters (pc);
1173 _session->config.map_parameters (pc);
1175 refresh_location_display ();
1177 restore_ruler_visibility ();
1178 //tempo_map_changed (PropertyChange (0));
1179 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1181 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1182 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1185 super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
1186 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1189 switch (_snap_type) {
1190 case SnapToRegionStart:
1191 case SnapToRegionEnd:
1192 case SnapToRegionSync:
1193 case SnapToRegionBoundary:
1194 build_region_boundary_cache ();
1201 /* register for undo history */
1202 _session->register_with_memento_command_factory(_id, this);
1204 ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated));
1206 start_updating_meters ();
1210 Editor::action_pre_activated (Glib::RefPtr<Action> const & a)
1212 if (a->get_name() == "RegionMenu") {
1213 /* When the main menu's region menu is opened, we setup the actions so that they look right
1214 in the menu. I can't find a way of getting a signal when this menu is subsequently closed,
1215 so we resensitize all region actions when the entered regionview or the region selection
1216 changes. HOWEVER we can't always resensitize on entered_regionview change because that
1217 happens after the region context menu is opened. So we set a flag here, too.
1221 sensitize_the_right_region_actions ();
1222 _last_region_menu_was_main = true;
1227 Editor::build_cursors ()
1229 using namespace Gdk;
1232 Glib::RefPtr<Gdk::Pixbuf> zoom_in_cursor_pixbuf (::get_icon ("zoom_in_cursor"));
1233 zoom_in_cursor = new Gdk::Cursor (Gdk::Display::get_default(), zoom_in_cursor_pixbuf, 5, 5);
1237 Glib::RefPtr<Gdk::Pixbuf> zoom_out_cursor_pixbuf (::get_icon ("zoom_out_cursor"));
1238 zoom_out_cursor = new Gdk::Cursor (Gdk::Display::get_default(), zoom_out_cursor_pixbuf, 5, 5);
1241 Gdk::Color fbg ("#ffffff" );
1242 Gdk::Color ffg ("#000000" );
1245 RefPtr<Bitmap> source, mask;
1247 source = Bitmap::create (fader_cursor_bits, fader_cursor_width, fader_cursor_height);
1248 mask = Bitmap::create (fader_cursor_mask_bits, fader_cursor_width, fader_cursor_height);
1249 fader_cursor = new Gdk::Cursor (source, mask, ffg, fbg, fader_cursor_x_hot, fader_cursor_y_hot);
1253 RefPtr<Bitmap> source, mask;
1254 source = Bitmap::create (speaker_cursor_bits, speaker_cursor_width, speaker_cursor_height);
1255 mask = Bitmap::create (speaker_cursor_mask_bits, speaker_cursor_width, speaker_cursor_height);
1256 speaker_cursor = new Gdk::Cursor (source, mask, ffg, fbg, speaker_cursor_x_hot, speaker_cursor_y_hot);
1260 RefPtr<Bitmap> bits;
1261 char pix[4] = { 0, 0, 0, 0 };
1262 bits = Bitmap::create (pix, 2, 2);
1264 transparent_cursor = new Gdk::Cursor (bits, bits, c, c, 0, 0);
1268 RefPtr<Bitmap> bits;
1269 char pix[4] = { 0, 0, 0, 0 };
1270 bits = Bitmap::create (pix, 2, 2);
1272 transparent_cursor = new Gdk::Cursor (bits, bits, c, c, 0, 0);
1276 Glib::RefPtr<Gdk::Pixbuf> grabber_pixbuf (::get_icon ("grabber"));
1277 grabber_cursor = new Gdk::Cursor (Gdk::Display::get_default(), grabber_pixbuf, 5, 0);
1281 Glib::RefPtr<Gdk::Pixbuf> grabber_note_pixbuf (::get_icon ("grabber_note"));
1282 grabber_note_cursor = new Gdk::Cursor (Gdk::Display::get_default(), grabber_note_pixbuf, 5, 10);
1286 Glib::RefPtr<Gdk::Pixbuf> grabber_edit_point_pixbuf (::get_icon ("grabber_edit_point"));
1287 grabber_edit_point_cursor = new Gdk::Cursor (Gdk::Display::get_default(), grabber_edit_point_pixbuf, 5, 17);
1290 cross_hair_cursor = new Gdk::Cursor (CROSSHAIR);
1291 trimmer_cursor = new Gdk::Cursor (SB_H_DOUBLE_ARROW);
1294 Glib::RefPtr<Gdk::Pixbuf> apixbuf (::get_icon ("trim_left_cursor"));
1295 left_side_trim_cursor = new Gdk::Cursor (Gdk::Display::get_default(), apixbuf, 5, 11);
1299 Glib::RefPtr<Gdk::Pixbuf> apixbuf (::get_icon ("trim_right_cursor"));
1300 right_side_trim_cursor = new Gdk::Cursor (Gdk::Display::get_default(), apixbuf, 23, 11);
1304 Glib::RefPtr<Gdk::Pixbuf> apixbuf (::get_icon ("fade_in_cursor"));
1305 fade_in_cursor = new Gdk::Cursor (Gdk::Display::get_default(), apixbuf, 0, 40);
1309 Glib::RefPtr<Gdk::Pixbuf> apixbuf (::get_icon ("fade_out_cursor"));
1310 fade_out_cursor = new Gdk::Cursor (Gdk::Display::get_default(), apixbuf, 27, 40);
1313 selector_cursor = new Gdk::Cursor (XTERM);
1314 time_fx_cursor = new Gdk::Cursor (SIZING);
1315 wait_cursor = new Gdk::Cursor (WATCH);
1316 timebar_cursor = new Gdk::Cursor(LEFT_PTR);
1317 midi_pencil_cursor = new Gdk::Cursor (PENCIL);
1318 midi_select_cursor = new Gdk::Cursor (CENTER_PTR);
1319 midi_resize_cursor = new Gdk::Cursor (SIZING);
1320 midi_erase_cursor = new Gdk::Cursor (DRAPED_BOX);
1321 up_down_cursor = new Gdk::Cursor (Gdk::SB_V_DOUBLE_ARROW);
1324 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1326 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1328 using namespace Menu_Helpers;
1329 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1332 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1336 MenuList& items (fade_context_menu.items());
1340 switch (item_type) {
1342 case FadeInHandleItem:
1343 if (arv->audio_region()->fade_in_active()) {
1344 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1346 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1349 items.push_back (SeparatorElem());
1351 if (Profile->get_sae()) {
1353 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)));
1354 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)));
1361 *_fade_in_images[FadeLinear],
1362 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)
1366 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1371 *_fade_in_images[FadeFast],
1372 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)
1375 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1380 *_fade_in_images[FadeLogB],
1381 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogB)
1384 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1389 *_fade_in_images[FadeLogA],
1390 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogA)
1393 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1398 *_fade_in_images[FadeSlow],
1399 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow)
1402 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1408 case FadeOutHandleItem:
1409 if (arv->audio_region()->fade_out_active()) {
1410 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1412 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1415 items.push_back (SeparatorElem());
1417 if (Profile->get_sae()) {
1418 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)));
1419 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)));
1425 *_fade_out_images[FadeLinear],
1426 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)
1430 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1435 *_fade_out_images[FadeFast],
1436 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)
1439 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1444 *_fade_out_images[FadeLogB],
1445 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogA)
1448 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1453 *_fade_out_images[FadeLogA],
1454 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogB)
1457 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1462 *_fade_out_images[FadeSlow],
1463 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast)
1466 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1472 fatal << _("programming error: ")
1473 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1478 fade_context_menu.popup (button, time);
1482 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection, framepos_t frame)
1484 using namespace Menu_Helpers;
1485 Menu* (Editor::*build_menu_function)(framepos_t);
1488 switch (item_type) {
1490 case RegionViewName:
1491 case RegionViewNameHighlight:
1492 case LeftFrameHandle:
1493 case RightFrameHandle:
1494 if (with_selection) {
1495 build_menu_function = &Editor::build_track_selection_context_menu;
1497 build_menu_function = &Editor::build_track_region_context_menu;
1502 if (with_selection) {
1503 build_menu_function = &Editor::build_track_selection_context_menu;
1505 build_menu_function = &Editor::build_track_context_menu;
1509 case CrossfadeViewItem:
1510 build_menu_function = &Editor::build_track_crossfade_context_menu;
1514 if (clicked_routeview->track()) {
1515 build_menu_function = &Editor::build_track_context_menu;
1517 build_menu_function = &Editor::build_track_bus_context_menu;
1522 /* probably shouldn't happen but if it does, we don't care */
1526 menu = (this->*build_menu_function)(frame);
1527 menu->set_name ("ArdourContextMenu");
1529 /* now handle specific situations */
1531 switch (item_type) {
1533 case RegionViewName:
1534 case RegionViewNameHighlight:
1535 case LeftFrameHandle:
1536 case RightFrameHandle:
1537 if (!with_selection) {
1538 if (region_edit_menu_split_item) {
1539 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1540 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1542 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1545 if (region_edit_menu_split_multichannel_item) {
1546 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1547 region_edit_menu_split_multichannel_item->set_sensitive (true);
1549 region_edit_menu_split_multichannel_item->set_sensitive (false);
1558 case CrossfadeViewItem:
1565 /* probably shouldn't happen but if it does, we don't care */
1569 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1571 /* Bounce to disk */
1573 using namespace Menu_Helpers;
1574 MenuList& edit_items = menu->items();
1576 edit_items.push_back (SeparatorElem());
1578 switch (clicked_routeview->audio_track()->freeze_state()) {
1579 case AudioTrack::NoFreeze:
1580 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1583 case AudioTrack::Frozen:
1584 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1587 case AudioTrack::UnFrozen:
1588 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1594 if (item_type == StreamItem && clicked_routeview) {
1595 clicked_routeview->build_underlay_menu(menu);
1598 /* When the region menu is opened, we setup the actions so that they look right
1601 sensitize_the_right_region_actions ();
1602 _last_region_menu_was_main = false;
1604 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1605 menu->popup (button, time);
1609 Editor::build_track_context_menu (framepos_t)
1611 using namespace Menu_Helpers;
1613 MenuList& edit_items = track_context_menu.items();
1616 add_dstream_context_items (edit_items);
1617 return &track_context_menu;
1621 Editor::build_track_bus_context_menu (framepos_t)
1623 using namespace Menu_Helpers;
1625 MenuList& edit_items = track_context_menu.items();
1628 add_bus_context_items (edit_items);
1629 return &track_context_menu;
1633 Editor::build_track_region_context_menu (framepos_t frame)
1635 using namespace Menu_Helpers;
1636 MenuList& edit_items = track_region_context_menu.items();
1639 /* we've just cleared the track region context menu, so the menu that these
1640 two items were on will have disappeared; stop them dangling.
1642 region_edit_menu_split_item = 0;
1643 region_edit_menu_split_multichannel_item = 0;
1645 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1648 boost::shared_ptr<Track> tr;
1649 boost::shared_ptr<Playlist> pl;
1651 /* Don't offer a region submenu if we are in internal edit mode, as we don't select regions in this
1652 mode and so offering region context is somewhat confusing.
1654 if ((tr = rtv->track()) && ((pl = tr->playlist())) && !internal_editing()) {
1655 framepos_t framepos = (framepos_t) floor ((double)frame * tr->speed());
1656 uint32_t regions_at = pl->count_regions_at (framepos);
1657 add_region_context_items (edit_items, regions_at > 1);
1661 add_dstream_context_items (edit_items);
1663 return &track_region_context_menu;
1667 Editor::build_track_crossfade_context_menu (framepos_t frame)
1669 using namespace Menu_Helpers;
1670 MenuList& edit_items = track_crossfade_context_menu.items();
1671 edit_items.clear ();
1673 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1676 boost::shared_ptr<Track> tr;
1677 boost::shared_ptr<Playlist> pl;
1678 boost::shared_ptr<AudioPlaylist> apl;
1680 if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1682 AudioPlaylist::Crossfades xfades;
1684 apl->crossfades_at (frame, xfades);
1686 bool many = xfades.size() > 1;
1688 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1689 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1692 framepos_t framepos = (framepos_t) floor ((double)frame * tr->speed());
1693 uint32_t regions_at = pl->count_regions_at (framepos);
1694 add_region_context_items (edit_items, regions_at > 1);
1698 add_dstream_context_items (edit_items);
1700 return &track_crossfade_context_menu;
1704 Editor::analyze_region_selection ()
1706 if (analysis_window == 0) {
1707 analysis_window = new AnalysisWindow();
1710 analysis_window->set_session(_session);
1712 analysis_window->show_all();
1715 analysis_window->set_regionmode();
1716 analysis_window->analyze();
1718 analysis_window->present();
1722 Editor::analyze_range_selection()
1724 if (analysis_window == 0) {
1725 analysis_window = new AnalysisWindow();
1728 analysis_window->set_session(_session);
1730 analysis_window->show_all();
1733 analysis_window->set_rangemode();
1734 analysis_window->analyze();
1736 analysis_window->present();
1740 Editor::build_track_selection_context_menu (framepos_t)
1742 using namespace Menu_Helpers;
1743 MenuList& edit_items = track_selection_context_menu.items();
1744 edit_items.clear ();
1746 add_selection_context_items (edit_items);
1747 // edit_items.push_back (SeparatorElem());
1748 // add_dstream_context_items (edit_items);
1750 return &track_selection_context_menu;
1753 /** Add context menu items relevant to crossfades.
1754 * @param edit_items List to add the items to.
1757 Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1759 using namespace Menu_Helpers;
1760 Menu *xfade_menu = manage (new Menu);
1761 MenuList& items = xfade_menu->items();
1762 xfade_menu->set_name ("ArdourContextMenu");
1765 if (xfade->active()) {
1771 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1772 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1774 if (xfade->can_follow_overlap()) {
1776 if (xfade->following_overlap()) {
1777 str = _("Convert to Short");
1779 str = _("Convert to Full");
1782 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1786 str = xfade->out()->name();
1788 str += xfade->in()->name();
1790 str = _("Crossfade");
1793 edit_items.push_back (MenuElem (str, *xfade_menu));
1794 edit_items.push_back (SeparatorElem());
1798 Editor::xfade_edit_left_region ()
1800 if (clicked_crossfadeview) {
1801 clicked_crossfadeview->left_view.show_region_editor ();
1806 Editor::xfade_edit_right_region ()
1808 if (clicked_crossfadeview) {
1809 clicked_crossfadeview->right_view.show_region_editor ();
1814 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, bool multiple_regions_at_position)
1816 using namespace Menu_Helpers;
1818 /* OK, stick the region submenu at the top of the list, and then add
1822 /* we have to hack up the region name because "_" has a special
1823 meaning for menu titles.
1826 RegionSelection rs = get_regions_from_selection_and_entered ();
1828 string::size_type pos = 0;
1829 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1831 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1832 menu_item_name.replace (pos, 1, "__");
1836 if (_popup_region_menu_item == 0) {
1837 _popup_region_menu_item = new MenuItem (menu_item_name);
1838 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1839 _popup_region_menu_item->show ();
1841 _popup_region_menu_item->set_label (menu_item_name);
1844 edit_items.push_back (*_popup_region_menu_item);
1845 if (multiple_regions_at_position && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1846 edit_items.push_back (action_menu_item ("choose-top-region"));
1848 edit_items.push_back (SeparatorElem());
1851 /** Add context menu items relevant to selection ranges.
1852 * @param edit_items List to add the items to.
1855 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1857 using namespace Menu_Helpers;
1859 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1860 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1862 edit_items.push_back (SeparatorElem());
1863 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1865 if (!selection->regions.empty()) {
1866 edit_items.push_back (SeparatorElem());
1867 edit_items.push_back (MenuElem (_("Extend Range to End of Region"), sigc::bind (sigc::mem_fun(*this, &Editor::extend_selection_to_end_of_region), false)));
1868 edit_items.push_back (MenuElem (_("Extend Range to Start of Region"), sigc::bind (sigc::mem_fun(*this, &Editor::extend_selection_to_start_of_region), false)));
1871 edit_items.push_back (SeparatorElem());
1872 edit_items.push_back (MenuElem (_("Convert to region in-place"), mem_fun(*this, &Editor::separate_region_from_selection)));
1873 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1875 edit_items.push_back (SeparatorElem());
1876 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1878 edit_items.push_back (SeparatorElem());
1879 edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1880 edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1882 edit_items.push_back (SeparatorElem());
1883 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1885 edit_items.push_back (SeparatorElem());
1886 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1887 edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1888 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1890 edit_items.push_back (SeparatorElem());
1891 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1892 edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1893 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1894 edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1895 edit_items.push_back (MenuElem (_("Export Range"), sigc::mem_fun(*this, &Editor::export_selection)));
1900 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1902 using namespace Menu_Helpers;
1906 Menu *play_menu = manage (new Menu);
1907 MenuList& play_items = play_menu->items();
1908 play_menu->set_name ("ArdourContextMenu");
1910 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1911 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1912 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1913 play_items.push_back (SeparatorElem());
1914 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
1916 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1920 Menu *select_menu = manage (new Menu);
1921 MenuList& select_items = select_menu->items();
1922 select_menu->set_name ("ArdourContextMenu");
1924 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1925 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1926 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1927 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1928 select_items.push_back (SeparatorElem());
1929 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
1930 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
1931 select_items.push_back (SeparatorElem());
1932 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1933 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1934 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1935 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1936 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
1937 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
1938 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
1940 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1944 Menu *cutnpaste_menu = manage (new Menu);
1945 MenuList& cutnpaste_items = cutnpaste_menu->items();
1946 cutnpaste_menu->set_name ("ArdourContextMenu");
1948 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1949 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1950 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1952 cutnpaste_items.push_back (SeparatorElem());
1954 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
1955 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
1957 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
1959 /* Adding new material */
1961 edit_items.push_back (SeparatorElem());
1962 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
1963 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
1967 Menu *nudge_menu = manage (new Menu());
1968 MenuList& nudge_items = nudge_menu->items();
1969 nudge_menu->set_name ("ArdourContextMenu");
1971 edit_items.push_back (SeparatorElem());
1972 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1973 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1974 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1975 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1977 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1981 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
1983 using namespace Menu_Helpers;
1987 Menu *play_menu = manage (new Menu);
1988 MenuList& play_items = play_menu->items();
1989 play_menu->set_name ("ArdourContextMenu");
1991 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1992 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1993 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1997 Menu *select_menu = manage (new Menu);
1998 MenuList& select_items = select_menu->items();
1999 select_menu->set_name ("ArdourContextMenu");
2001 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2002 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
2003 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
2004 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
2005 select_items.push_back (SeparatorElem());
2006 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
2007 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
2008 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2009 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2011 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2015 Menu *cutnpaste_menu = manage (new Menu);
2016 MenuList& cutnpaste_items = cutnpaste_menu->items();
2017 cutnpaste_menu->set_name ("ArdourContextMenu");
2019 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2020 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2021 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
2023 Menu *nudge_menu = manage (new Menu());
2024 MenuList& nudge_items = nudge_menu->items();
2025 nudge_menu->set_name ("ArdourContextMenu");
2027 edit_items.push_back (SeparatorElem());
2028 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2029 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2030 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2031 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2033 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2037 Editor::snap_type() const
2043 Editor::snap_mode() const
2049 Editor::set_snap_to (SnapType st)
2051 unsigned int snap_ind = (unsigned int)st;
2055 if (snap_ind > snap_type_strings.size() - 1) {
2057 _snap_type = (SnapType)snap_ind;
2060 string str = snap_type_strings[snap_ind];
2062 if (str != snap_type_selector.get_active_text()) {
2063 snap_type_selector.set_active_text (str);
2068 switch (_snap_type) {
2069 case SnapToBeatDiv32:
2070 case SnapToBeatDiv28:
2071 case SnapToBeatDiv24:
2072 case SnapToBeatDiv20:
2073 case SnapToBeatDiv16:
2074 case SnapToBeatDiv14:
2075 case SnapToBeatDiv12:
2076 case SnapToBeatDiv10:
2077 case SnapToBeatDiv8:
2078 case SnapToBeatDiv7:
2079 case SnapToBeatDiv6:
2080 case SnapToBeatDiv5:
2081 case SnapToBeatDiv4:
2082 case SnapToBeatDiv3:
2083 case SnapToBeatDiv2:
2084 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
2085 update_tempo_based_rulers ();
2088 case SnapToRegionStart:
2089 case SnapToRegionEnd:
2090 case SnapToRegionSync:
2091 case SnapToRegionBoundary:
2092 build_region_boundary_cache ();
2100 SnapChanged (); /* EMIT SIGNAL */
2104 Editor::set_snap_mode (SnapMode mode)
2107 string str = snap_mode_strings[(int)mode];
2109 if (str != snap_mode_selector.get_active_text ()) {
2110 snap_mode_selector.set_active_text (str);
2116 Editor::set_edit_point_preference (EditPoint ep, bool force)
2118 bool changed = (_edit_point != ep);
2121 string str = edit_point_strings[(int)ep];
2123 if (str != edit_point_selector.get_active_text ()) {
2124 edit_point_selector.set_active_text (str);
2127 set_canvas_cursor ();
2129 if (!force && !changed) {
2133 const char* action=NULL;
2135 switch (_edit_point) {
2136 case EditAtPlayhead:
2137 action = "edit-at-playhead";
2139 case EditAtSelectedMarker:
2140 action = "edit-at-marker";
2143 action = "edit-at-mouse";
2147 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2149 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2153 bool in_track_canvas;
2155 if (!mouse_frame (foo, in_track_canvas)) {
2156 in_track_canvas = false;
2159 reset_canvas_action_sensitivity (in_track_canvas);
2165 Editor::set_state (const XMLNode& node, int /*version*/)
2167 const XMLProperty* prop;
2169 int x, y, xoff, yoff;
2172 if ((prop = node.property ("id")) != 0) {
2173 _id = prop->value ();
2176 g.base_width = default_width;
2177 g.base_height = default_height;
2183 if ((geometry = find_named_node (node, "geometry")) != 0) {
2187 if ((prop = geometry->property("x_size")) == 0) {
2188 prop = geometry->property ("x-size");
2191 g.base_width = atoi(prop->value());
2193 if ((prop = geometry->property("y_size")) == 0) {
2194 prop = geometry->property ("y-size");
2197 g.base_height = atoi(prop->value());
2200 if ((prop = geometry->property ("x_pos")) == 0) {
2201 prop = geometry->property ("x-pos");
2204 x = atoi (prop->value());
2207 if ((prop = geometry->property ("y_pos")) == 0) {
2208 prop = geometry->property ("y-pos");
2211 y = atoi (prop->value());
2214 if ((prop = geometry->property ("x_off")) == 0) {
2215 prop = geometry->property ("x-off");
2218 xoff = atoi (prop->value());
2220 if ((prop = geometry->property ("y_off")) == 0) {
2221 prop = geometry->property ("y-off");
2224 yoff = atoi (prop->value());
2228 set_default_size (g.base_width, g.base_height);
2231 if (_session && (prop = node.property ("playhead"))) {
2233 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2234 playhead_cursor->set_position (pos);
2236 playhead_cursor->set_position (0);
2239 if ((prop = node.property ("mixer-width"))) {
2240 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2243 if ((prop = node.property ("zoom-focus"))) {
2244 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2247 if ((prop = node.property ("zoom"))) {
2248 reset_zoom (PBD::atof (prop->value()));
2250 reset_zoom (frames_per_unit);
2253 if ((prop = node.property ("snap-to"))) {
2254 set_snap_to ((SnapType) atoi (prop->value()));
2257 if ((prop = node.property ("snap-mode"))) {
2258 set_snap_mode ((SnapMode) atoi (prop->value()));
2261 if ((prop = node.property ("mouse-mode"))) {
2262 MouseMode m = str2mousemode(prop->value());
2263 set_mouse_mode (m, true);
2265 set_mouse_mode (MouseObject, true);
2268 if ((prop = node.property ("left-frame")) != 0){
2270 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2271 reset_x_origin (pos);
2275 if ((prop = node.property ("y-origin")) != 0) {
2276 reset_y_origin (atof (prop->value ()));
2279 if ((prop = node.property ("internal-edit"))) {
2280 bool yn = string_is_affirmative (prop->value());
2281 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2283 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2284 tact->set_active (!yn);
2285 tact->set_active (yn);
2289 if ((prop = node.property ("join-object-range"))) {
2290 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2293 if ((prop = node.property ("edit-point"))) {
2294 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2297 if ((prop = node.property ("show-measures"))) {
2298 bool yn = string_is_affirmative (prop->value());
2299 _show_measures = yn;
2300 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2302 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2303 /* do it twice to force the change */
2304 tact->set_active (!yn);
2305 tact->set_active (yn);
2309 if ((prop = node.property ("follow-playhead"))) {
2310 bool yn = string_is_affirmative (prop->value());
2311 set_follow_playhead (yn);
2312 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2314 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2315 if (tact->get_active() != yn) {
2316 tact->set_active (yn);
2321 if ((prop = node.property ("stationary-playhead"))) {
2322 bool yn = (prop->value() == "yes");
2323 set_stationary_playhead (yn);
2324 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2326 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2327 if (tact->get_active() != yn) {
2328 tact->set_active (yn);
2333 if ((prop = node.property ("region-list-sort-type"))) {
2334 RegionListSortType st;
2335 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2338 if ((prop = node.property ("xfades-visible"))) {
2339 bool yn = string_is_affirmative (prop->value());
2340 _xfade_visibility = !yn;
2341 // set_xfade_visibility (yn);
2344 if ((prop = node.property ("show-editor-mixer"))) {
2346 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2349 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2350 bool yn = string_is_affirmative (prop->value());
2352 /* do it twice to force the change */
2354 tact->set_active (!yn);
2355 tact->set_active (yn);
2358 if ((prop = node.property ("show-editor-list"))) {
2360 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2363 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2364 bool yn = string_is_affirmative (prop->value());
2366 /* do it twice to force the change */
2368 tact->set_active (!yn);
2369 tact->set_active (yn);
2372 if ((prop = node.property (X_("editor-list-page")))) {
2373 the_notebook.set_current_page (atoi (prop->value ()));
2376 if ((prop = node.property (X_("show-marker-lines")))) {
2377 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2379 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2380 bool yn = string_is_affirmative (prop->value ());
2382 tact->set_active (!yn);
2383 tact->set_active (yn);
2386 XMLNodeList children = node.children ();
2387 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2388 selection->set_state (**i, Stateful::current_state_version);
2389 _regions->set_state (**i);
2396 Editor::get_state ()
2398 XMLNode* node = new XMLNode ("Editor");
2401 _id.print (buf, sizeof (buf));
2402 node->add_property ("id", buf);
2404 if (is_realized()) {
2405 Glib::RefPtr<Gdk::Window> win = get_window();
2407 int x, y, xoff, yoff, width, height;
2408 win->get_root_origin(x, y);
2409 win->get_position(xoff, yoff);
2410 win->get_size(width, height);
2412 XMLNode* geometry = new XMLNode ("geometry");
2414 snprintf(buf, sizeof(buf), "%d", width);
2415 geometry->add_property("x-size", string(buf));
2416 snprintf(buf, sizeof(buf), "%d", height);
2417 geometry->add_property("y-size", string(buf));
2418 snprintf(buf, sizeof(buf), "%d", x);
2419 geometry->add_property("x-pos", string(buf));
2420 snprintf(buf, sizeof(buf), "%d", y);
2421 geometry->add_property("y-pos", string(buf));
2422 snprintf(buf, sizeof(buf), "%d", xoff);
2423 geometry->add_property("x-off", string(buf));
2424 snprintf(buf, sizeof(buf), "%d", yoff);
2425 geometry->add_property("y-off", string(buf));
2426 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2427 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2428 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2429 geometry->add_property("edit-vertical-pane-pos", string(buf));
2431 node->add_child_nocopy (*geometry);
2434 maybe_add_mixer_strip_width (*node);
2436 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2437 node->add_property ("zoom-focus", buf);
2438 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2439 node->add_property ("zoom", buf);
2440 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2441 node->add_property ("snap-to", buf);
2442 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2443 node->add_property ("snap-mode", buf);
2445 node->add_property ("edit-point", enum_2_string (_edit_point));
2447 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2448 node->add_property ("playhead", buf);
2449 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2450 node->add_property ("left-frame", buf);
2451 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2452 node->add_property ("y-origin", buf);
2454 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2455 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2456 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2457 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2458 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2459 node->add_property ("mouse-mode", enum2str(mouse_mode));
2460 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2461 node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2463 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2465 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2466 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2469 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2471 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2472 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2475 snprintf (buf, sizeof (buf), "%d", the_notebook.get_current_page ());
2476 node->add_property (X_("editor-list-page"), buf);
2478 node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no");
2480 node->add_child_nocopy (selection->get_state ());
2481 node->add_child_nocopy (_regions->get_state ());
2488 /** @param y y offset from the top of all trackviews.
2489 * @return pair: TimeAxisView that y is over, layer index.
2490 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2491 * in stacked region display mode, otherwise 0.
2493 std::pair<TimeAxisView *, layer_t>
2494 Editor::trackview_by_y_position (double y)
2496 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2498 std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
2504 return std::make_pair ( (TimeAxisView *) 0, 0);
2507 /** Snap a position to the grid, if appropriate, taking into account current
2508 * grid settings and also the state of any snap modifier keys that may be pressed.
2509 * @param start Position to snap.
2510 * @param event Event to get current key modifier information from, or 0.
2513 Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2515 if (!_session || !event) {
2519 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2520 if (_snap_mode == SnapOff) {
2521 snap_to_internal (start, direction, for_mark);
2524 if (_snap_mode != SnapOff) {
2525 snap_to_internal (start, direction, for_mark);
2531 Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark)
2533 if (!_session || _snap_mode == SnapOff) {
2537 snap_to_internal (start, direction, for_mark);
2541 Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /*for_mark*/)
2543 const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2544 framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2546 switch (_snap_type) {
2547 case SnapToTimecodeFrame:
2548 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2549 start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2551 start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2555 case SnapToTimecodeSeconds:
2556 if (_session->timecode_offset_negative())
2558 start += _session->timecode_offset ();
2560 start -= _session->timecode_offset ();
2562 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2563 start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2565 start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2568 if (_session->timecode_offset_negative())
2570 start -= _session->timecode_offset ();
2572 start += _session->timecode_offset ();
2576 case SnapToTimecodeMinutes:
2577 if (_session->timecode_offset_negative())
2579 start += _session->timecode_offset ();
2581 start -= _session->timecode_offset ();
2583 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2584 start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2586 start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2588 if (_session->timecode_offset_negative())
2590 start -= _session->timecode_offset ();
2592 start += _session->timecode_offset ();
2596 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2602 Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark)
2604 const framepos_t one_second = _session->frame_rate();
2605 const framepos_t one_minute = _session->frame_rate() * 60;
2606 framepos_t presnap = start;
2610 switch (_snap_type) {
2611 case SnapToTimecodeFrame:
2612 case SnapToTimecodeSeconds:
2613 case SnapToTimecodeMinutes:
2614 return timecode_snap_to_internal (start, direction, for_mark);
2617 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2618 start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2620 start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2625 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2626 start = (framepos_t) ceil ((double) start / one_second) * one_second;
2628 start = (framepos_t) floor ((double) start / one_second) * one_second;
2633 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2634 start = (framepos_t) ceil ((double) start / one_minute) * one_minute;
2636 start = (framepos_t) floor ((double) start / one_minute) * one_minute;
2641 start = _session->tempo_map().round_to_bar (start, direction);
2645 start = _session->tempo_map().round_to_beat (start, direction);
2648 case SnapToBeatDiv32:
2649 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2651 case SnapToBeatDiv28:
2652 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2654 case SnapToBeatDiv24:
2655 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2657 case SnapToBeatDiv20:
2658 start = _session->tempo_map().round_to_beat_subdivision (start, 20, direction);
2660 case SnapToBeatDiv16:
2661 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2663 case SnapToBeatDiv14:
2664 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2666 case SnapToBeatDiv12:
2667 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2669 case SnapToBeatDiv10:
2670 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2672 case SnapToBeatDiv8:
2673 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2675 case SnapToBeatDiv7:
2676 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2678 case SnapToBeatDiv6:
2679 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2681 case SnapToBeatDiv5:
2682 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2684 case SnapToBeatDiv4:
2685 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2687 case SnapToBeatDiv3:
2688 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2690 case SnapToBeatDiv2:
2691 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2699 _session->locations()->marks_either_side (start, before, after);
2701 if (before == max_framepos) {
2703 } else if (after == max_framepos) {
2705 } else if (before != max_framepos && after != max_framepos) {
2706 /* have before and after */
2707 if ((start - before) < (after - start)) {
2716 case SnapToRegionStart:
2717 case SnapToRegionEnd:
2718 case SnapToRegionSync:
2719 case SnapToRegionBoundary:
2720 if (!region_boundary_cache.empty()) {
2722 vector<framepos_t>::iterator prev = region_boundary_cache.end ();
2723 vector<framepos_t>::iterator next = region_boundary_cache.end ();
2725 if (direction > 0) {
2726 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2728 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2731 if (next != region_boundary_cache.begin ()) {
2736 framepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2737 framepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2739 if (start > (p + n) / 2) {
2748 switch (_snap_mode) {
2754 if (presnap > start) {
2755 if (presnap > (start + unit_to_frame(snap_threshold))) {
2759 } else if (presnap < start) {
2760 if (presnap < (start - unit_to_frame(snap_threshold))) {
2766 /* handled at entry */
2774 Editor::setup_toolbar ()
2778 /* Mode Buttons (tool selection) */
2780 mouse_move_button.set_relief(Gtk::RELIEF_NONE);
2781 mouse_select_button.set_relief(Gtk::RELIEF_NONE);
2782 mouse_gain_button.set_relief(Gtk::RELIEF_NONE);
2783 mouse_zoom_button.set_relief(Gtk::RELIEF_NONE);
2784 mouse_timefx_button.set_relief(Gtk::RELIEF_NONE);
2785 mouse_audition_button.set_relief(Gtk::RELIEF_NONE);
2786 // internal_edit_button.set_relief(Gtk::RELIEF_NONE);
2787 join_object_range_button.set_relief(Gtk::RELIEF_NONE);
2789 HBox* mode_box = manage(new HBox);
2790 mode_box->set_border_width (2);
2791 mode_box->set_spacing(4);
2793 /* table containing mode buttons */
2795 HBox* mouse_mode_button_box = manage (new HBox ());
2797 if (Profile->get_sae()) {
2798 mouse_mode_button_box->pack_start (mouse_move_button);
2800 mouse_mode_button_box->pack_start (mouse_move_button);
2801 mouse_mode_button_box->pack_start (join_object_range_button);
2802 mouse_mode_button_box->pack_start (mouse_select_button);
2805 mouse_mode_button_box->pack_start (mouse_zoom_button);
2807 if (!Profile->get_sae()) {
2808 mouse_mode_button_box->pack_start (mouse_gain_button);
2811 mouse_mode_button_box->pack_start (mouse_timefx_button);
2812 mouse_mode_button_box->pack_start (mouse_audition_button);
2813 mouse_mode_button_box->pack_start (internal_edit_button);
2815 vector<string> edit_mode_strings;
2816 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2817 if (!Profile->get_sae()) {
2818 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2820 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2822 edit_mode_selector.set_name ("EditModeSelector");
2823 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2824 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2826 mode_box->pack_start (edit_mode_selector);
2827 mode_box->pack_start (*mouse_mode_button_box);
2829 _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2830 _mouse_mode_tearoff->set_name ("MouseModeBase");
2831 _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2833 if (Profile->get_sae()) {
2834 _mouse_mode_tearoff->set_can_be_torn_off (false);
2837 _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2838 &_mouse_mode_tearoff->tearoff_window()));
2839 _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2840 &_mouse_mode_tearoff->tearoff_window(), 1));
2841 _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2842 &_mouse_mode_tearoff->tearoff_window()));
2843 _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2844 &_mouse_mode_tearoff->tearoff_window(), 1));
2846 mouse_move_button.set_mode (false);
2847 mouse_select_button.set_mode (false);
2848 mouse_gain_button.set_mode (false);
2849 mouse_zoom_button.set_mode (false);
2850 mouse_timefx_button.set_mode (false);
2851 mouse_audition_button.set_mode (false);
2852 join_object_range_button.set_mode (false);
2854 mouse_move_button.set_name ("MouseModeButton");
2855 mouse_select_button.set_name ("MouseModeButton");
2856 mouse_gain_button.set_name ("MouseModeButton");
2857 mouse_zoom_button.set_name ("MouseModeButton");
2858 mouse_timefx_button.set_name ("MouseModeButton");
2859 mouse_audition_button.set_name ("MouseModeButton");
2860 internal_edit_button.set_name ("MouseModeButton");
2861 join_object_range_button.set_name ("MouseModeButton");
2863 mouse_move_button.unset_flags (CAN_FOCUS);
2864 mouse_select_button.unset_flags (CAN_FOCUS);
2865 mouse_gain_button.unset_flags (CAN_FOCUS);
2866 mouse_zoom_button.unset_flags (CAN_FOCUS);
2867 mouse_timefx_button.unset_flags (CAN_FOCUS);
2868 mouse_audition_button.unset_flags (CAN_FOCUS);
2869 internal_edit_button.unset_flags (CAN_FOCUS);
2870 join_object_range_button.unset_flags (CAN_FOCUS);
2874 _zoom_box.set_spacing (1);
2875 _zoom_box.set_border_width (0);
2877 zoom_in_button.set_name ("EditorTimeButton");
2878 zoom_in_button.set_image (*(manage (new Image (Stock::ZOOM_IN, Gtk::ICON_SIZE_MENU))));
2879 zoom_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), false));
2881 zoom_out_button.set_name ("EditorTimeButton");
2882 zoom_out_button.set_image (*(manage (new Image (Stock::ZOOM_OUT, Gtk::ICON_SIZE_MENU))));
2883 zoom_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), true));
2885 zoom_out_full_button.set_name ("EditorTimeButton");
2886 zoom_out_full_button.set_image (*(manage (new Image (Stock::ZOOM_100, Gtk::ICON_SIZE_MENU))));
2887 zoom_out_full_button.signal_clicked().connect (sigc::mem_fun(*this, &Editor::temporal_zoom_session));
2889 zoom_focus_selector.set_name ("ZoomFocusSelector");
2890 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
2891 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2893 _zoom_box.pack_start (zoom_out_button, false, false);
2894 _zoom_box.pack_start (zoom_in_button, false, false);
2895 _zoom_box.pack_start (zoom_out_full_button, false, false);
2897 _zoom_box.pack_start (zoom_focus_selector);
2899 /* Track zoom buttons */
2900 tav_expand_button.set_name ("TrackHeightButton");
2901 tav_expand_button.set_size_request(-1,20);
2902 tav_expand_button.add (*(manage (new Image (::get_icon("tav_exp")))));
2903 tav_expand_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), true));
2905 tav_shrink_button.set_name ("TrackHeightButton");
2906 tav_shrink_button.set_size_request(-1,20);
2907 tav_shrink_button.add (*(manage (new Image (::get_icon("tav_shrink")))));
2908 tav_shrink_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), false));
2910 _zoom_box.pack_start (tav_shrink_button);
2911 _zoom_box.pack_start (tav_expand_button);
2913 _zoom_tearoff = manage (new TearOff (_zoom_box));
2915 _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2916 &_zoom_tearoff->tearoff_window()));
2917 _zoom_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2918 &_zoom_tearoff->tearoff_window(), 0));
2919 _zoom_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2920 &_zoom_tearoff->tearoff_window()));
2921 _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2922 &_zoom_tearoff->tearoff_window(), 0));
2924 snap_box.set_spacing (1);
2925 snap_box.set_border_width (2);
2927 snap_type_selector.set_name ("SnapTypeSelector");
2928 set_popdown_strings (snap_type_selector, snap_type_strings, true);
2929 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2931 snap_mode_selector.set_name ("SnapModeSelector");
2932 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
2933 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2935 edit_point_selector.set_name ("EditPointSelector");
2936 set_popdown_strings (edit_point_selector, edit_point_strings, true);
2937 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2939 snap_box.pack_start (snap_mode_selector, false, false);
2940 snap_box.pack_start (snap_type_selector, false, false);
2941 snap_box.pack_start (edit_point_selector, false, false);
2945 HBox *nudge_box = manage (new HBox);
2946 nudge_box->set_spacing(1);
2947 nudge_box->set_border_width (2);
2949 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2950 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2952 nudge_box->pack_start (nudge_backward_button, false, false);
2953 nudge_box->pack_start (nudge_forward_button, false, false);
2954 nudge_box->pack_start (nudge_clock, false, false);
2957 /* Pack everything in... */
2959 HBox* hbox = manage (new HBox);
2960 hbox->set_spacing(10);
2962 _tools_tearoff = manage (new TearOff (*hbox));
2963 _tools_tearoff->set_name ("MouseModeBase");
2964 _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
2966 if (Profile->get_sae()) {
2967 _tools_tearoff->set_can_be_torn_off (false);
2970 _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2971 &_tools_tearoff->tearoff_window()));
2972 _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2973 &_tools_tearoff->tearoff_window(), 0));
2974 _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2975 &_tools_tearoff->tearoff_window()));
2976 _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2977 &_tools_tearoff->tearoff_window(), 0));
2979 toolbar_hbox.set_spacing (10);
2980 toolbar_hbox.set_border_width (1);
2982 toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
2983 toolbar_hbox.pack_start (*_zoom_tearoff, false, false);
2984 toolbar_hbox.pack_start (*_tools_tearoff, false, false);
2986 hbox->pack_start (snap_box, false, false);
2987 hbox->pack_start (*nudge_box, false, false);
2988 hbox->pack_start (panic_box, false, false);
2992 toolbar_base.set_name ("ToolBarBase");
2993 toolbar_base.add (toolbar_hbox);
2995 toolbar_frame.set_shadow_type (SHADOW_OUT);
2996 toolbar_frame.set_name ("BaseFrame");
2997 toolbar_frame.add (toolbar_base);
3001 Editor::setup_tooltips ()
3003 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
3004 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain"));
3005 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
3006 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
3007 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
3008 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
3009 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
3010 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
3011 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
3012 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
3013 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
3014 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
3015 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
3016 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
3017 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
3018 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
3019 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
3020 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
3021 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
3022 ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
3023 ARDOUR_UI::instance()->set_tip (midi_panic_button, _("Send note off and reset controller messages on all MIDI channels"));
3024 ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode"));
3028 Editor::midi_panic ()
3030 cerr << "MIDI panic\n";
3033 _session->midi_panic();
3038 Editor::setup_midi_toolbar ()
3042 /* Midi sound notes */
3043 midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
3044 midi_sound_notes.set_relief(Gtk::RELIEF_NONE);
3045 midi_sound_notes.unset_flags (CAN_FOCUS);
3049 act = ActionManager::get_action (X_("MIDI"), X_("panic"));
3050 midi_panic_button.set_name("MidiPanicButton");
3051 act->connect_proxy (midi_panic_button);
3053 panic_box.pack_start (midi_sound_notes , true, true);
3054 panic_box.pack_start (midi_panic_button, true, true);
3058 Editor::convert_drop_to_paths (
3059 vector<string>& paths,
3060 const RefPtr<Gdk::DragContext>& /*context*/,
3063 const SelectionData& data,
3067 if (_session == 0) {
3071 vector<string> uris = data.get_uris();
3075 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3076 are actually URI lists. So do it by hand.
3079 if (data.get_target() != "text/plain") {
3083 /* Parse the "uri-list" format that Nautilus provides,
3084 where each pathname is delimited by \r\n.
3086 THERE MAY BE NO NULL TERMINATING CHAR!!!
3089 string txt = data.get_text();
3093 p = (const char *) malloc (txt.length() + 1);
3094 txt.copy ((char *) p, txt.length(), 0);
3095 ((char*)p)[txt.length()] = '\0';
3101 while (g_ascii_isspace (*p))
3105 while (*q && (*q != '\n') && (*q != '\r')) {
3112 while (q > p && g_ascii_isspace (*q))
3117 uris.push_back (string (p, q - p + 1));
3121 p = strchr (p, '\n');
3133 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3135 if ((*i).substr (0,7) == "file://") {
3138 PBD::url_decode (p);
3140 // scan forward past three slashes
3142 string::size_type slashcnt = 0;
3143 string::size_type n = 0;
3144 string::iterator x = p.begin();
3146 while (slashcnt < 3 && x != p.end()) {
3149 } else if (slashcnt == 3) {
3156 if (slashcnt != 3 || x == p.end()) {
3157 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3161 paths.push_back (p.substr (n - 1));
3169 Editor::new_tempo_section ()
3175 Editor::map_transport_state ()
3177 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3179 if (_session && _session->transport_stopped()) {
3180 have_pending_keyboard_selection = false;
3183 update_loop_range_view (true);
3188 Editor::State::State (PublicEditor const * e)
3190 selection = new Selection (e);
3193 Editor::State::~State ()
3199 Editor::begin_reversible_command (string name)
3202 _session->begin_reversible_command (name);
3207 Editor::commit_reversible_command ()
3210 _session->commit_reversible_command ();
3215 Editor::set_route_group_solo (Route& route, bool yn)
3217 RouteGroup *route_group;
3219 if ((route_group = route.route_group()) != 0) {
3220 route_group->apply (&Route::set_solo, yn, this);
3222 route.set_solo (yn, this);
3227 Editor::set_route_group_mute (Route& route, bool yn)
3229 RouteGroup *route_group = 0;
3231 if ((route_group = route.route_group()) != 0) {
3232 route_group->apply (&Route::set_mute, yn, this);
3234 route.set_mute (yn, this);
3239 Editor::history_changed ()
3243 if (undo_action && _session) {
3244 if (_session->undo_depth() == 0) {
3247 label = string_compose(_("Undo (%1)"), _session->next_undo());
3249 undo_action->property_label() = label;
3252 if (redo_action && _session) {
3253 if (_session->redo_depth() == 0) {
3256 label = string_compose(_("Redo (%1)"), _session->next_redo());
3258 redo_action->property_label() = label;
3263 Editor::duplicate_dialog (bool with_dialog)
3267 if (mouse_mode == MouseRange) {
3268 if (selection->time.length() == 0) {
3273 RegionSelection rs = get_regions_from_selection_and_entered ();
3275 if (mouse_mode != MouseRange && rs.empty()) {
3281 ArdourDialog win (_("Duplicate"));
3282 Label label (_("Number of duplications:"));
3283 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3284 SpinButton spinner (adjustment, 0.0, 1);
3287 win.get_vbox()->set_spacing (12);
3288 win.get_vbox()->pack_start (hbox);
3289 hbox.set_border_width (6);
3290 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3292 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3293 place, visually. so do this by hand.
3296 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3297 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3298 spinner.grab_focus();
3304 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3305 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3306 win.set_default_response (RESPONSE_ACCEPT);
3308 win.set_position (WIN_POS_MOUSE);
3310 spinner.grab_focus ();
3312 switch (win.run ()) {
3313 case RESPONSE_ACCEPT:
3319 times = adjustment.get_value();
3322 if (mouse_mode == MouseRange) {
3323 duplicate_selection (times);
3325 duplicate_some_regions (rs, times);
3330 Editor::show_verbose_canvas_cursor ()
3332 verbose_canvas_cursor->raise_to_top();
3333 verbose_canvas_cursor->show();
3334 verbose_cursor_visible = true;
3338 Editor::hide_verbose_canvas_cursor ()
3340 verbose_canvas_cursor->hide();
3341 verbose_cursor_visible = false;
3345 Editor::clamp_verbose_cursor_x (double x)
3350 x = min (_canvas_width - 200.0, x);
3356 Editor::clamp_verbose_cursor_y (double y)
3358 if (y < canvas_timebars_vsize) {
3359 y = canvas_timebars_vsize;
3361 y = min (_canvas_height - 50, y);
3367 Editor::show_verbose_canvas_cursor_with (const string & txt, int32_t xoffset, int32_t yoffset)
3369 verbose_canvas_cursor->property_text() = txt.c_str();
3374 track_canvas->get_pointer (x, y);
3375 track_canvas->window_to_world (x, y, wx, wy);
3380 /* don't get too close to the edge */
3381 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (wx);
3382 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (wy);
3384 show_verbose_canvas_cursor ();
3388 Editor::set_verbose_canvas_cursor (const string & txt, double x, double y)
3390 verbose_canvas_cursor->property_text() = txt.c_str();
3391 /* don't get too close to the edge */
3392 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (x);
3393 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (y);
3397 Editor::set_verbose_canvas_cursor_text (const string & txt)
3399 verbose_canvas_cursor->property_text() = txt.c_str();
3403 Editor::set_edit_mode (EditMode m)
3405 Config->set_edit_mode (m);
3409 Editor::cycle_edit_mode ()
3411 switch (Config->get_edit_mode()) {
3413 if (Profile->get_sae()) {
3414 Config->set_edit_mode (Lock);
3416 Config->set_edit_mode (Splice);
3420 Config->set_edit_mode (Lock);
3423 Config->set_edit_mode (Slide);
3429 Editor::edit_mode_selection_done ()
3431 Config->set_edit_mode (string_to_edit_mode (edit_mode_selector.get_active_text ()));
3435 Editor::snap_type_selection_done ()
3437 string choice = snap_type_selector.get_active_text();
3438 SnapType snaptype = SnapToBeat;
3440 if (choice == _("Beats/2")) {
3441 snaptype = SnapToBeatDiv2;
3442 } else if (choice == _("Beats/3")) {
3443 snaptype = SnapToBeatDiv3;
3444 } else if (choice == _("Beats/4")) {
3445 snaptype = SnapToBeatDiv4;
3446 } else if (choice == _("Beats/5")) {
3447 snaptype = SnapToBeatDiv5;
3448 } else if (choice == _("Beats/6")) {
3449 snaptype = SnapToBeatDiv6;
3450 } else if (choice == _("Beats/7")) {
3451 snaptype = SnapToBeatDiv7;
3452 } else if (choice == _("Beats/8")) {
3453 snaptype = SnapToBeatDiv8;
3454 } else if (choice == _("Beats/10")) {
3455 snaptype = SnapToBeatDiv10;
3456 } else if (choice == _("Beats/12")) {
3457 snaptype = SnapToBeatDiv12;
3458 } else if (choice == _("Beats/14")) {
3459 snaptype = SnapToBeatDiv14;
3460 } else if (choice == _("Beats/16")) {
3461 snaptype = SnapToBeatDiv16;
3462 } else if (choice == _("Beats/20")) {
3463 snaptype = SnapToBeatDiv20;
3464 } else if (choice == _("Beats/24")) {
3465 snaptype = SnapToBeatDiv24;
3466 } else if (choice == _("Beats/28")) {
3467 snaptype = SnapToBeatDiv28;
3468 } else if (choice == _("Beats/32")) {
3469 snaptype = SnapToBeatDiv32;
3470 } else if (choice == _("Beats")) {
3471 snaptype = SnapToBeat;
3472 } else if (choice == _("Bars")) {
3473 snaptype = SnapToBar;
3474 } else if (choice == _("Marks")) {
3475 snaptype = SnapToMark;
3476 } else if (choice == _("Region starts")) {
3477 snaptype = SnapToRegionStart;
3478 } else if (choice == _("Region ends")) {
3479 snaptype = SnapToRegionEnd;
3480 } else if (choice == _("Region bounds")) {
3481 snaptype = SnapToRegionBoundary;
3482 } else if (choice == _("Region syncs")) {
3483 snaptype = SnapToRegionSync;
3484 } else if (choice == _("CD Frames")) {
3485 snaptype = SnapToCDFrame;
3486 } else if (choice == _("Timecode Frames")) {
3487 snaptype = SnapToTimecodeFrame;
3488 } else if (choice == _("Timecode Seconds")) {
3489 snaptype = SnapToTimecodeSeconds;
3490 } else if (choice == _("Timecode Minutes")) {
3491 snaptype = SnapToTimecodeMinutes;
3492 } else if (choice == _("Seconds")) {
3493 snaptype = SnapToSeconds;
3494 } else if (choice == _("Minutes")) {
3495 snaptype = SnapToMinutes;
3498 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3500 ract->set_active ();
3505 Editor::snap_mode_selection_done ()
3507 string choice = snap_mode_selector.get_active_text();
3508 SnapMode mode = SnapNormal;
3510 if (choice == _("No Grid")) {
3512 } else if (choice == _("Grid")) {
3514 } else if (choice == _("Magnetic")) {
3515 mode = SnapMagnetic;
3518 RefPtr<RadioAction> ract = snap_mode_action (mode);
3521 ract->set_active (true);
3526 Editor::cycle_edit_point (bool with_marker)
3528 switch (_edit_point) {
3530 set_edit_point_preference (EditAtPlayhead);
3532 case EditAtPlayhead:
3534 set_edit_point_preference (EditAtSelectedMarker);
3536 set_edit_point_preference (EditAtMouse);
3539 case EditAtSelectedMarker:
3540 set_edit_point_preference (EditAtMouse);
3546 Editor::edit_point_selection_done ()
3548 string choice = edit_point_selector.get_active_text();
3549 EditPoint ep = EditAtSelectedMarker;
3551 if (choice == _("Marker")) {
3552 set_edit_point_preference (EditAtSelectedMarker);
3553 } else if (choice == _("Playhead")) {
3554 set_edit_point_preference (EditAtPlayhead);
3556 set_edit_point_preference (EditAtMouse);
3559 RefPtr<RadioAction> ract = edit_point_action (ep);
3562 ract->set_active (true);
3567 Editor::zoom_focus_selection_done ()
3569 string choice = zoom_focus_selector.get_active_text();
3570 ZoomFocus focus_type = ZoomFocusLeft;
3572 if (choice == _("Left")) {
3573 focus_type = ZoomFocusLeft;
3574 } else if (choice == _("Right")) {
3575 focus_type = ZoomFocusRight;
3576 } else if (choice == _("Center")) {
3577 focus_type = ZoomFocusCenter;
3578 } else if (choice == _("Playhead")) {
3579 focus_type = ZoomFocusPlayhead;
3580 } else if (choice == _("Mouse")) {
3581 focus_type = ZoomFocusMouse;
3582 } else if (choice == _("Edit point")) {
3583 focus_type = ZoomFocusEdit;
3586 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3589 ract->set_active ();
3594 Editor::edit_controls_button_release (GdkEventButton* ev)
3596 if (Keyboard::is_context_menu_event (ev)) {
3597 ARDOUR_UI::instance()->add_route (this);
3603 Editor::mouse_select_button_release (GdkEventButton* ev)
3605 /* this handles just right-clicks */
3607 if (ev->button != 3) {
3615 Editor::set_zoom_focus (ZoomFocus f)
3617 string str = zoom_focus_strings[(int)f];
3619 if (str != zoom_focus_selector.get_active_text()) {
3620 zoom_focus_selector.set_active_text (str);
3623 if (zoom_focus != f) {
3626 ZoomFocusChanged (); /* EMIT_SIGNAL */
3633 Editor::ensure_float (Window& win)
3635 win.set_transient_for (*this);
3639 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3641 /* recover or initialize pane positions. do this here rather than earlier because
3642 we don't want the positions to change the child allocations, which they seem to do.
3648 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3660 width = default_width;
3661 height = default_height;
3663 if ((geometry = find_named_node (*node, "geometry")) != 0) {
3665 prop = geometry->property ("x-size");
3667 width = atoi (prop->value());
3669 prop = geometry->property ("y-size");
3671 height = atoi (prop->value());
3675 if (which == static_cast<Paned*> (&edit_pane)) {
3677 if (done & Horizontal) {
3681 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3682 /* initial allocation is 90% to canvas, 10% to notebook */
3683 pos = (int) floor (alloc.get_width() * 0.90f);
3684 snprintf (buf, sizeof(buf), "%d", pos);
3686 pos = atoi (prop->value());
3689 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3690 edit_pane.set_position (pos);
3691 pre_maximal_horizontal_pane_position = pos;
3694 done = (Pane) (done | Horizontal);
3696 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3698 if (done & Vertical) {
3702 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3703 /* initial allocation is 90% to canvas, 10% to summary */
3704 pos = (int) floor (alloc.get_height() * 0.90f);
3705 snprintf (buf, sizeof(buf), "%d", pos);
3707 pos = atoi (prop->value());
3710 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3711 editor_summary_pane.set_position (pos);
3712 pre_maximal_vertical_pane_position = pos;
3715 done = (Pane) (done | Vertical);
3720 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3722 if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) {
3723 top_hbox.remove (toolbar_frame);
3728 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3730 if (toolbar_frame.get_parent() == 0) {
3731 top_hbox.pack_end (toolbar_frame);
3736 Editor::set_show_measures (bool yn)
3738 if (_show_measures != yn) {
3741 if ((_show_measures = yn) == true) {
3743 tempo_lines->show();
3751 Editor::toggle_follow_playhead ()
3753 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3755 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3756 set_follow_playhead (tact->get_active());
3761 Editor::set_follow_playhead (bool yn)
3763 if (_follow_playhead != yn) {
3764 if ((_follow_playhead = yn) == true) {
3766 reset_x_origin_to_follow_playhead ();
3773 Editor::toggle_stationary_playhead ()
3775 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3777 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3778 set_stationary_playhead (tact->get_active());
3783 Editor::set_stationary_playhead (bool yn)
3785 if (_stationary_playhead != yn) {
3786 if ((_stationary_playhead = yn) == true) {
3788 // FIXME need a 3.0 equivalent of this 2.X call
3789 // update_current_screen ();
3796 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3798 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3800 xfade->set_active (!xfade->active());
3805 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3807 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3809 xfade->set_follow_overlap (!xfade->following_overlap());
3814 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3816 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3822 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3826 switch (cew.run ()) {
3827 case RESPONSE_ACCEPT:
3834 PropertyChange all_crossfade_properties;
3835 all_crossfade_properties.add (ARDOUR::Properties::active);
3836 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3837 xfade->PropertyChanged (all_crossfade_properties);
3841 Editor::playlist_selector () const
3843 return *_playlist_selector;
3847 Editor::get_grid_type_as_beats (bool& success, framepos_t position)
3851 switch (_snap_type) {
3856 case SnapToBeatDiv32:
3859 case SnapToBeatDiv28:
3862 case SnapToBeatDiv24:
3865 case SnapToBeatDiv20:
3868 case SnapToBeatDiv16:
3871 case SnapToBeatDiv14:
3874 case SnapToBeatDiv12:
3877 case SnapToBeatDiv10:
3880 case SnapToBeatDiv8:
3883 case SnapToBeatDiv7:
3886 case SnapToBeatDiv6:
3889 case SnapToBeatDiv5:
3892 case SnapToBeatDiv4:
3895 case SnapToBeatDiv3:
3898 case SnapToBeatDiv2:
3904 return _session->tempo_map().meter_at (position).beats_per_bar();
3909 case SnapToTimecodeFrame:
3910 case SnapToTimecodeSeconds:
3911 case SnapToTimecodeMinutes:
3914 case SnapToRegionStart:
3915 case SnapToRegionEnd:
3916 case SnapToRegionSync:
3917 case SnapToRegionBoundary:
3927 Editor::get_nudge_distance (framepos_t pos, framecnt_t& next)
3931 ret = nudge_clock.current_duration (pos);
3932 next = ret + 1; /* XXXX fix me */
3938 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3940 ArdourDialog dialog (_("Playlist Deletion"));
3941 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3942 "If left alone, no audio files used by it will be cleaned.\n"
3943 "If deleted, audio files used by it alone by will cleaned."),
3946 dialog.set_position (WIN_POS_CENTER);
3947 dialog.get_vbox()->pack_start (label);
3951 dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT);
3952 dialog.add_button (_("Keep playlist"), RESPONSE_REJECT);
3953 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3955 switch (dialog.run ()) {
3956 case RESPONSE_ACCEPT:
3957 /* delete the playlist */
3961 case RESPONSE_REJECT:
3962 /* keep the playlist */
3974 Editor::audio_region_selection_covers (framepos_t where)
3976 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3977 if ((*a)->region()->covers (where)) {
3986 Editor::prepare_for_cleanup ()
3988 cut_buffer->clear_regions ();
3989 cut_buffer->clear_playlists ();
3991 selection->clear_regions ();
3992 selection->clear_playlists ();
3994 _regions->suspend_redisplay ();
3998 Editor::finish_cleanup ()
4000 _regions->resume_redisplay ();
4004 Editor::transport_loop_location()
4007 return _session->locations()->auto_loop_location();
4014 Editor::transport_punch_location()
4017 return _session->locations()->auto_punch_location();
4024 Editor::control_layout_scroll (GdkEventScroll* ev)
4026 if (Keyboard::some_magic_widget_has_focus()) {
4030 switch (ev->direction) {
4032 scroll_tracks_up_line ();
4036 case GDK_SCROLL_DOWN:
4037 scroll_tracks_down_line ();
4041 /* no left/right handling yet */
4049 Editor::session_state_saved (string)
4052 _snapshots->redisplay ();
4056 Editor::maximise_editing_space ()
4058 _mouse_mode_tearoff->set_visible (false);
4059 _tools_tearoff->set_visible (false);
4060 _zoom_tearoff->set_visible (false);
4062 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
4063 pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
4064 pre_maximal_editor_width = this->get_width ();
4065 pre_maximal_editor_height = this->get_height ();
4067 if (post_maximal_horizontal_pane_position == 0) {
4068 post_maximal_horizontal_pane_position = edit_pane.get_width();
4071 if (post_maximal_vertical_pane_position == 0) {
4072 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
4077 if (post_maximal_editor_width) {
4078 edit_pane.set_position (post_maximal_horizontal_pane_position -
4079 abs(post_maximal_editor_width - pre_maximal_editor_width));
4081 edit_pane.set_position (post_maximal_horizontal_pane_position);
4084 if (post_maximal_editor_height) {
4085 editor_summary_pane.set_position (post_maximal_vertical_pane_position -
4086 abs(post_maximal_editor_height - pre_maximal_editor_height));
4088 editor_summary_pane.set_position (post_maximal_vertical_pane_position);
4091 if (Config->get_keep_tearoffs()) {
4092 _mouse_mode_tearoff->set_visible (true);
4093 _tools_tearoff->set_visible (true);
4094 _zoom_tearoff->set_visible (true);
4099 Editor::restore_editing_space ()
4101 // user changed width/height of panes during fullscreen
4103 if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
4104 post_maximal_horizontal_pane_position = edit_pane.get_position();
4107 if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
4108 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
4113 _mouse_mode_tearoff->set_visible (true);
4114 _tools_tearoff->set_visible (true);
4115 _zoom_tearoff->set_visible (true);
4116 post_maximal_editor_width = this->get_width();
4117 post_maximal_editor_height = this->get_height();
4119 edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
4120 editor_summary_pane.set_position (pre_maximal_vertical_pane_position + abs(this->get_height() - pre_maximal_editor_height));
4124 * Make new playlists for a given track and also any others that belong
4125 * to the same active route group with the `edit' property.
4130 Editor::new_playlists (TimeAxisView* v)
4132 begin_reversible_command (_("new playlists"));
4133 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4134 _session->playlists->get (playlists);
4135 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4136 commit_reversible_command ();
4140 * Use a copy of the current playlist for a given track and also any others that belong
4141 * to the same active route group with the `edit' property.
4146 Editor::copy_playlists (TimeAxisView* v)
4148 begin_reversible_command (_("copy playlists"));
4149 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4150 _session->playlists->get (playlists);
4151 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4152 commit_reversible_command ();
4155 /** Clear the current playlist for a given track and also any others that belong
4156 * to the same active route group with the `edit' property.
4161 Editor::clear_playlists (TimeAxisView* v)
4163 begin_reversible_command (_("clear playlists"));
4164 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4165 _session->playlists->get (playlists);
4166 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4167 commit_reversible_command ();
4171 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4173 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4177 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4179 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4183 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4185 atv.clear_playlist ();
4189 Editor::on_key_press_event (GdkEventKey* ev)
4191 return key_press_focus_accelerator_handler (*this, ev);
4195 Editor::on_key_release_event (GdkEventKey* ev)
4197 return Gtk::Window::on_key_release_event (ev);
4198 // return key_press_focus_accelerator_handler (*this, ev);
4201 /** Queue up a change to the viewport x origin.
4202 * @param frame New x origin.
4205 Editor::reset_x_origin (framepos_t frame)
4207 queue_visual_change (frame);
4211 Editor::reset_y_origin (double y)
4213 queue_visual_change_y (y);
4217 Editor::reset_zoom (double fpu)
4219 queue_visual_change (fpu);
4223 Editor::reposition_and_zoom (framepos_t frame, double fpu)
4225 reset_x_origin (frame);
4228 if (!no_save_visual) {
4229 undo_visual_stack.push_back (current_visual_state(false));
4233 Editor::VisualState*
4234 Editor::current_visual_state (bool with_tracks)
4236 VisualState* vs = new VisualState;
4237 vs->y_position = vertical_adjustment.get_value();
4238 vs->frames_per_unit = frames_per_unit;
4239 vs->leftmost_frame = leftmost_frame;
4240 vs->zoom_focus = zoom_focus;
4243 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4244 vs->track_states.push_back (TAVState ((*i), &(*i)->get_state()));
4252 Editor::undo_visual_state ()
4254 if (undo_visual_stack.empty()) {
4258 redo_visual_stack.push_back (current_visual_state());
4260 VisualState* vs = undo_visual_stack.back();
4261 undo_visual_stack.pop_back();
4262 use_visual_state (*vs);
4266 Editor::redo_visual_state ()
4268 if (redo_visual_stack.empty()) {
4272 undo_visual_stack.push_back (current_visual_state());
4274 VisualState* vs = redo_visual_stack.back();
4275 redo_visual_stack.pop_back();
4276 use_visual_state (*vs);
4280 Editor::swap_visual_state ()
4282 if (undo_visual_stack.empty()) {
4283 redo_visual_state ();
4285 undo_visual_state ();
4290 Editor::use_visual_state (VisualState& vs)
4292 no_save_visual = true;
4294 _routes->suspend_redisplay ();
4296 vertical_adjustment.set_value (vs.y_position);
4298 set_zoom_focus (vs.zoom_focus);
4299 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4301 for (list<TAVState>::iterator i = vs.track_states.begin(); i != vs.track_states.end(); ++i) {
4302 TrackViewList::iterator t;
4304 /* check if the track still exists - it could have been deleted */
4306 if ((t = find (track_views.begin(), track_views.end(), i->first)) != track_views.end()) {
4307 (*t)->set_state (*(i->second), Stateful::loading_state_version);
4312 if (!vs.track_states.empty()) {
4313 _routes->update_visibility ();
4316 _routes->resume_redisplay ();
4318 no_save_visual = false;
4322 Editor::set_frames_per_unit (double fpu)
4324 /* this is the core function that controls the zoom level of the canvas. it is called
4325 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4328 if (fpu == frames_per_unit) {
4337 /* don't allow zooms that fit more than the maximum number
4338 of frames into an 800 pixel wide space.
4341 if (max_framepos / fpu < 800.0) {
4346 tempo_lines->tempo_map_changed();
4348 frames_per_unit = fpu;
4353 Editor::post_zoom ()
4355 // convert fpu to frame count
4357 framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width);
4359 if (frames_per_unit != zoom_range_clock.current_duration()) {
4360 zoom_range_clock.set (frames);
4363 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4364 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4365 (*i)->reshow_selection (selection->time);
4369 ZoomChanged (); /* EMIT_SIGNAL */
4371 //reset_scrolling_region ();
4373 if (playhead_cursor) {
4374 playhead_cursor->set_position (playhead_cursor->current_frame);
4377 refresh_location_display();
4378 _summary->set_overlays_dirty ();
4384 Editor::queue_visual_change (framepos_t where)
4386 pending_visual_change.add (VisualChange::TimeOrigin);
4387 pending_visual_change.time_origin = where;
4388 ensure_visual_change_idle_handler ();
4392 Editor::queue_visual_change (double fpu)
4394 pending_visual_change.add (VisualChange::ZoomLevel);
4395 pending_visual_change.frames_per_unit = fpu;
4397 ensure_visual_change_idle_handler ();
4401 Editor::queue_visual_change_y (double y)
4403 pending_visual_change.add (VisualChange::YOrigin);
4404 pending_visual_change.y_origin = y;
4406 ensure_visual_change_idle_handler ();
4410 Editor::ensure_visual_change_idle_handler ()
4412 if (pending_visual_change.idle_handler_id < 0) {
4413 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4418 Editor::_idle_visual_changer (void* arg)
4420 return static_cast<Editor*>(arg)->idle_visual_changer ();
4424 Editor::idle_visual_changer ()
4426 VisualChange::Type p = pending_visual_change.pending;
4427 pending_visual_change.pending = (VisualChange::Type) 0;
4429 double const last_time_origin = horizontal_position ();
4431 if (p & VisualChange::TimeOrigin) {
4432 /* This is a bit of a hack, but set_frames_per_unit
4433 below will (if called) end up with the
4434 CrossfadeViews looking at Editor::leftmost_frame,
4435 and if we're changing origin and zoom in the same
4436 operation it will be the wrong value unless we
4440 leftmost_frame = pending_visual_change.time_origin;
4443 if (p & VisualChange::ZoomLevel) {
4444 set_frames_per_unit (pending_visual_change.frames_per_unit);
4446 compute_fixed_ruler_scale ();
4447 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4448 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4449 update_tempo_based_rulers ();
4451 if (p & VisualChange::TimeOrigin) {
4452 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4454 if (p & VisualChange::YOrigin) {
4455 vertical_adjustment.set_value (pending_visual_change.y_origin);
4458 if (last_time_origin == horizontal_position ()) {
4459 /* changed signal not emitted */
4460 update_fixed_rulers ();
4461 redisplay_tempo (true);
4464 _summary->set_overlays_dirty ();
4466 pending_visual_change.idle_handler_id = -1;
4467 return 0; /* this is always a one-shot call */
4470 struct EditorOrderTimeAxisSorter {
4471 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4472 return a->order () < b->order ();
4477 Editor::sort_track_selection (TrackViewList* sel)
4479 EditorOrderTimeAxisSorter cmp;
4484 selection->tracks.sort (cmp);
4489 Editor::get_preferred_edit_position (bool ignore_playhead)
4492 framepos_t where = 0;
4493 EditPoint ep = _edit_point;
4495 if (entered_marker) {
4496 return entered_marker->position();
4499 if (ignore_playhead && ep == EditAtPlayhead) {
4500 ep = EditAtSelectedMarker;
4504 case EditAtPlayhead:
4505 where = _session->audible_frame();
4508 case EditAtSelectedMarker:
4509 if (!selection->markers.empty()) {
4511 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4514 where = loc->start();
4525 if (!mouse_frame (where, ignored)) {
4526 /* XXX not right but what can we do ? */
4537 Editor::set_loop_range (framepos_t start, framepos_t end, string cmd)
4539 if (!_session) return;
4541 begin_reversible_command (cmd);
4545 if ((tll = transport_loop_location()) == 0) {
4546 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop);
4547 XMLNode &before = _session->locations()->get_state();
4548 _session->locations()->add (loc, true);
4549 _session->set_auto_loop_location (loc);
4550 XMLNode &after = _session->locations()->get_state();
4551 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4553 XMLNode &before = tll->get_state();
4554 tll->set_hidden (false, this);
4555 tll->set (start, end);
4556 XMLNode &after = tll->get_state();
4557 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4560 commit_reversible_command ();
4564 Editor::set_punch_range (framepos_t start, framepos_t end, string cmd)
4566 if (!_session) return;
4568 begin_reversible_command (cmd);
4572 if ((tpl = transport_punch_location()) == 0) {
4573 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoPunch);
4574 XMLNode &before = _session->locations()->get_state();
4575 _session->locations()->add (loc, true);
4576 _session->set_auto_loop_location (loc);
4577 XMLNode &after = _session->locations()->get_state();
4578 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4581 XMLNode &before = tpl->get_state();
4582 tpl->set_hidden (false, this);
4583 tpl->set (start, end);
4584 XMLNode &after = tpl->get_state();
4585 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4588 commit_reversible_command ();
4591 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4592 * @param rs List to which found regions are added.
4593 * @param where Time to look at.
4594 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4597 Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4599 const TrackViewList* tracks;
4602 tracks = &track_views;
4607 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4608 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4610 boost::shared_ptr<Track> tr;
4611 boost::shared_ptr<Playlist> pl;
4613 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4615 Playlist::RegionList* regions = pl->regions_at (
4616 (framepos_t) floor ( (double)where * tr->speed()));
4618 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4619 RegionView* rv = rtv->view()->find_view (*i);
4632 Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4634 const TrackViewList* tracks;
4637 tracks = &track_views;
4642 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4643 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4645 boost::shared_ptr<Track> tr;
4646 boost::shared_ptr<Playlist> pl;
4648 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4650 Playlist::RegionList* regions = pl->regions_touched (
4651 (framepos_t) floor ( (double)where * tr->speed()), max_framepos);
4653 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4655 RegionView* rv = rtv->view()->find_view (*i);
4668 /** Get regions using the following conditions:
4669 * 1. If the edit point is `mouse':
4670 * if the mouse is over a selected region, or no region, return all selected regions.
4671 * if the mouse is over an unselected region, return just that region.
4672 * 2. For all other edit points:
4673 * return the selected regions AND those that are both under the edit position
4674 * AND on a selected track, or on a track which is in the same active edit-enabled route group
4675 * as a selected region.
4677 * The rationale here is that the mouse edit point is special in that its position describes
4678 * both a time and a track; the other edit modes only describe a time.
4680 * @param rs Returned region list.
4684 Editor::get_regions_from_selection_and_edit_point ()
4686 if (_edit_point == EditAtMouse) {
4687 if (entered_regionview == 0 || selection->regions.contains (entered_regionview)) {
4688 return selection->regions;
4691 rs.add (entered_regionview);
4696 /* We're using the edit point, but its not EditAtMouse */
4698 /* Start with selected regions */
4699 RegionSelection rs = selection->regions;
4701 TrackViewList tracks = selection->tracks;
4703 /* Tracks is currently the set of selected tracks; add any other tracks that
4704 have regions that are in the same edit-activated route group as one of
4707 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
4709 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4710 if (g && g->is_active() && g->is_edit()) {
4711 tracks.add (axis_views_from_routes (g->route_list()));
4716 if (!tracks.empty()) {
4717 /* now find regions that are at the edit position on those tracks */
4718 framepos_t const where = get_preferred_edit_position ();
4719 get_regions_at (rs, where, tracks);
4727 Editor::get_regions_from_selection_and_entered ()
4729 RegionSelection rs = selection->regions;
4731 if (rs.empty() && entered_regionview) {
4732 rs.add (entered_regionview);
4739 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4741 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4743 RouteTimeAxisView* tatv;
4745 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4747 boost::shared_ptr<Playlist> pl;
4748 vector<boost::shared_ptr<Region> > results;
4750 boost::shared_ptr<Track> tr;
4752 if ((tr = tatv->track()) == 0) {
4757 if ((pl = (tr->playlist())) != 0) {
4758 pl->get_region_list_equivalent_regions (region, results);
4761 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4762 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4763 regions.push_back (marv);
4772 Editor::show_rhythm_ferret ()
4774 if (rhythm_ferret == 0) {
4775 rhythm_ferret = new RhythmFerret(*this);
4778 rhythm_ferret->set_session (_session);
4779 rhythm_ferret->show ();
4780 rhythm_ferret->present ();
4784 Editor::first_idle ()
4786 MessageDialog* dialog = 0;
4788 if (track_views.size() > 1) {
4789 dialog = new MessageDialog (*this,
4790 string_compose (_("Please wait while %1 loads visual data"), PROGRAM_NAME),
4795 ARDOUR_UI::instance()->flush_pending ();
4798 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4802 // first idle adds route children (automation tracks), so we need to redisplay here
4803 _routes->redisplay ();
4811 Editor::_idle_resize (gpointer arg)
4813 return ((Editor*)arg)->idle_resize ();
4817 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4819 if (resize_idle_id < 0) {
4820 resize_idle_id = g_idle_add (_idle_resize, this);
4821 _pending_resize_amount = 0;
4824 /* make a note of the smallest resulting height, so that we can clamp the
4825 lower limit at TimeAxisView::hSmall */
4827 int32_t min_resulting = INT32_MAX;
4829 _pending_resize_amount += h;
4830 _pending_resize_view = view;
4832 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4834 if (selection->tracks.contains (_pending_resize_view)) {
4835 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4836 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4840 if (min_resulting < 0) {
4845 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4846 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4850 /** Handle pending resizing of tracks */
4852 Editor::idle_resize ()
4854 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4856 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4857 selection->tracks.contains (_pending_resize_view)) {
4859 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4860 if (*i != _pending_resize_view) {
4861 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4866 _pending_resize_amount = 0;
4868 _group_tabs->set_dirty ();
4869 resize_idle_id = -1;
4877 ENSURE_GUI_THREAD (*this, &Editor::located);
4879 playhead_cursor->set_position (_session->audible_frame ());
4880 if (_follow_playhead && !_pending_initial_locate) {
4881 reset_x_origin_to_follow_playhead ();
4884 _pending_locate_request = false;
4885 _pending_initial_locate = false;
4889 Editor::region_view_added (RegionView *)
4891 _summary->set_dirty ();
4895 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4897 TrackViewList::const_iterator j = track_views.begin ();
4898 while (j != track_views.end()) {
4899 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4900 if (rtv && rtv->route() == r) {
4911 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4915 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4916 TimeAxisView* tv = axis_view_from_route (*i);
4927 Editor::handle_new_route (RouteList& routes)
4929 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4931 RouteTimeAxisView *rtv;
4932 list<RouteTimeAxisView*> new_views;
4934 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4935 boost::shared_ptr<Route> route = (*x);
4937 if (route->is_hidden() || route->is_monitor()) {
4941 DataType dt = route->input()->default_type();
4943 if (dt == ARDOUR::DataType::AUDIO) {
4944 rtv = new AudioTimeAxisView (*this, _session, route, *track_canvas);
4945 } else if (dt == ARDOUR::DataType::MIDI) {
4946 rtv = new MidiTimeAxisView (*this, _session, route, *track_canvas);
4948 throw unknown_type();
4951 new_views.push_back (rtv);
4952 track_views.push_back (rtv);
4954 rtv->effective_gain_display ();
4956 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4959 _routes->routes_added (new_views);
4961 if (show_editor_mixer_when_tracks_arrive) {
4962 show_editor_mixer (true);
4965 editor_list_button.set_sensitive (true);
4967 _summary->set_dirty ();
4971 Editor::timeaxisview_deleted (TimeAxisView *tv)
4973 if (_session && _session->deletion_in_progress()) {
4974 /* the situation is under control */
4978 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
4980 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
4982 _routes->route_removed (tv);
4984 if (tv == entered_track) {
4988 TimeAxisView::Children c = tv->get_child_list ();
4989 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
4990 if (entered_track == i->get()) {
4995 /* remove it from the list of track views */
4997 TrackViewList::iterator i;
4999 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
5000 i = track_views.erase (i);
5003 /* update whatever the current mixer strip is displaying, if revelant */
5005 boost::shared_ptr<Route> route;
5008 route = rtav->route ();
5011 if (current_mixer_strip && current_mixer_strip->route() == route) {
5013 TimeAxisView* next_tv;
5015 if (track_views.empty()) {
5017 } else if (i == track_views.end()) {
5018 next_tv = track_views.front();
5025 set_selected_mixer_strip (*next_tv);
5027 /* make the editor mixer strip go away setting the
5028 * button to inactive (which also unticks the menu option)
5031 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
5037 Editor::hide_track_in_display (TimeAxisView* tv, bool /*temponly*/)
5039 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
5041 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
5042 // this will hide the mixer strip
5043 set_selected_mixer_strip (*tv);
5046 _routes->hide_track_in_display (*tv);
5050 Editor::sync_track_view_list_and_routes ()
5052 track_views = TrackViewList (_routes->views ());
5054 _summary->set_dirty ();
5055 _group_tabs->set_dirty ();
5057 return false; // do not call again (until needed)
5061 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
5063 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5068 /** Find a RouteTimeAxisView by the ID of its route */
5070 Editor::get_route_view_by_route_id (PBD::ID& id) const
5072 RouteTimeAxisView* v;
5074 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5075 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5076 if(v->route()->id() == id) {
5086 Editor::fit_route_group (RouteGroup *g)
5088 TrackViewList ts = axis_views_from_routes (g->route_list ());
5093 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5095 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5098 _session->cancel_audition ();
5102 if (_session->is_auditioning()) {
5103 _session->cancel_audition ();
5104 if (r == last_audition_region) {
5109 _session->audition_region (r);
5110 last_audition_region = r;
5115 Editor::hide_a_region (boost::shared_ptr<Region> r)
5117 r->set_hidden (true);
5121 Editor::show_a_region (boost::shared_ptr<Region> r)
5123 r->set_hidden (false);
5127 Editor::audition_region_from_region_list ()
5129 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5133 Editor::hide_region_from_region_list ()
5135 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5139 Editor::show_region_in_region_list ()
5141 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5145 Editor::step_edit_status_change (bool yn)
5148 start_step_editing ();
5150 stop_step_editing ();
5155 Editor::start_step_editing ()
5157 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5161 Editor::stop_step_editing ()
5163 step_edit_connection.disconnect ();
5167 Editor::check_step_edit ()
5169 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5170 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5172 mtv->check_step_edit ();
5176 return true; // do it again, till we stop
5180 Editor::horizontal_scroll_left_press ()
5182 ++_scroll_callbacks;
5184 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5185 /* delay the first auto-repeat */
5189 double x = leftmost_position() - current_page_frames() / 5;
5196 /* do hacky auto-repeat */
5197 if (!_scroll_connection.connected ()) {
5198 _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press), 100);
5199 _scroll_callbacks = 0;
5206 Editor::horizontal_scroll_left_release ()
5208 _scroll_connection.disconnect ();
5212 Editor::horizontal_scroll_right_press ()
5214 ++_scroll_callbacks;
5216 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5217 /* delay the first auto-repeat */
5221 reset_x_origin (leftmost_position() + current_page_frames() / 5);
5223 /* do hacky auto-repeat */
5224 if (!_scroll_connection.connected ()) {
5225 _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press), 100);
5226 _scroll_callbacks = 0;
5233 Editor::horizontal_scroll_right_release ()
5235 _scroll_connection.disconnect ();
5238 /** Queue a change for the Editor viewport x origin to follow the playhead */
5240 Editor::reset_x_origin_to_follow_playhead ()
5242 framepos_t const frame = playhead_cursor->current_frame;
5244 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5246 if (_session->transport_speed() < 0) {
5248 if (frame > (current_page_frames() / 2)) {
5249 center_screen (frame-(current_page_frames()/2));
5251 center_screen (current_page_frames()/2);
5256 if (frame < leftmost_frame) {
5259 if (_session->transport_rolling()) {
5260 /* rolling; end up with the playhead at the right of the page */
5261 l = frame - current_page_frames ();
5263 /* not rolling: end up with the playhead 3/4 of the way along the page */
5264 l = frame - (3 * current_page_frames() / 4);
5271 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5274 if (_session->transport_rolling()) {
5275 /* rolling: end up with the playhead on the left of the page */
5276 center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5278 /* not rolling: end up with the playhead 1/4 of the way along the page */
5279 center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5287 Editor::super_rapid_screen_update ()
5289 if (!_session || !_session->engine().running()) {
5293 /* METERING / MIXER STRIPS */
5295 /* update track meters, if required */
5296 if (is_mapped() && meters_running) {
5297 RouteTimeAxisView* rtv;
5298 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5299 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5300 rtv->fast_update ();
5305 /* and any current mixer strip */
5306 if (current_mixer_strip) {
5307 current_mixer_strip->fast_update ();
5310 /* PLAYHEAD AND VIEWPORT */
5312 framepos_t const frame = _session->audible_frame();
5314 /* There are a few reasons why we might not update the playhead / viewport stuff:
5316 * 1. we don't update things when there's a pending locate request, otherwise
5317 * when the editor requests a locate there is a chance that this method
5318 * will move the playhead before the locate request is processed, causing
5320 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5321 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5324 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5326 last_update_frame = frame;
5328 if (!_dragging_playhead) {
5329 playhead_cursor->set_position (frame);
5332 if (!_stationary_playhead) {
5334 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5335 reset_x_origin_to_follow_playhead ();
5340 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5344 // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5345 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5346 if (target <= 0.0) {
5349 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5350 target = (target * 0.15) + (current * 0.85);
5356 set_horizontal_position (current);
5365 Editor::session_going_away ()
5367 _have_idled = false;
5369 _session_connections.drop_connections ();
5371 super_rapid_screen_update_connection.disconnect ();
5373 selection->clear ();
5374 cut_buffer->clear ();
5376 clicked_regionview = 0;
5377 clicked_axisview = 0;
5378 clicked_routeview = 0;
5379 clicked_crossfadeview = 0;
5380 entered_regionview = 0;
5382 last_update_frame = 0;
5385 playhead_cursor->canvas_item.hide ();
5387 /* rip everything out of the list displays */
5391 _route_groups->clear ();
5393 /* do this first so that deleting a track doesn't reset cms to null
5394 and thus cause a leak.
5397 if (current_mixer_strip) {
5398 if (current_mixer_strip->get_parent() != 0) {
5399 global_hpacker.remove (*current_mixer_strip);
5401 delete current_mixer_strip;
5402 current_mixer_strip = 0;
5405 /* delete all trackviews */
5407 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5410 track_views.clear ();
5412 zoom_range_clock.set_session (0);
5413 nudge_clock.set_session (0);
5415 editor_list_button.set_active(false);
5416 editor_list_button.set_sensitive(false);
5418 /* clear tempo/meter rulers */
5419 remove_metric_marks ();
5421 clear_marker_display ();
5423 delete current_bbt_points;
5424 current_bbt_points = 0;
5426 /* get rid of any existing editor mixer strip */
5428 WindowTitle title(Glib::get_application_name());
5429 title += _("Editor");
5431 set_title (title.get_string());
5433 SessionHandlePtr::session_going_away ();
5438 Editor::show_editor_list (bool yn)
5441 the_notebook.show();
5443 the_notebook.hide();
5448 Editor::change_region_layering_order ()
5450 framepos_t const position = get_preferred_edit_position ();
5452 if (!clicked_routeview) {
5453 if (layering_order_editor) {
5454 layering_order_editor->hide ();
5459 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
5465 boost::shared_ptr<Playlist> pl = track->playlist();
5471 if (layering_order_editor == 0) {
5472 layering_order_editor = new RegionLayeringOrderEditor(*this);
5475 layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position);
5476 layering_order_editor->maybe_present ();
5480 Editor::update_region_layering_order_editor ()
5482 if (layering_order_editor && layering_order_editor->is_visible ()) {
5483 change_region_layering_order ();
5488 Editor::setup_fade_images ()
5490 _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear")));
5491 _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut")));
5492 _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut")));
5493 _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut")));
5494 _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut")));
5496 _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear")));
5497 _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut")));
5498 _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut")));
5499 _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut")));
5500 _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut")));
5504 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
5506 Editor::action_menu_item (std::string const & name)
5508 return *manage (editor_actions->get_action(name)->create_menu_item ());