Fix playhead behaviour on auto-return if follow playhead is not set.
[ArdourMidi.git] / gtk2_ardour / editor.cc
bloba9dbe9f7dd1629c15c1a58b523625cba3a86b734
1 /*
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
23 #include <stdint.h>
24 #include <unistd.h>
25 #include <cstdlib>
26 #include <cmath>
27 #include <string>
28 #include <algorithm>
29 #include <map>
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"
80 #include "editor.h"
81 #include "keyboard.h"
82 #include "marker.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"
90 #include "utils.h"
91 #include "crossfade_view.h"
92 #include "canvas-noevent-text.h"
93 #include "editing.h"
94 #include "public_editor.h"
95 #include "crossfade_edit.h"
96 #include "canvas_impl.h"
97 #include "actions.h"
98 #include "sfdb_ui.h"
99 #include "gui_thread.h"
100 #include "simpleline.h"
101 #include "rhythm_ferret.h"
102 #include "actions.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"
119 #include "i18n.h"
121 #ifdef WITH_CMT
122 #include "imageframe_socket_handler.h"
123 #endif
125 using namespace std;
126 using namespace ARDOUR;
127 using namespace PBD;
128 using namespace Gtk;
129 using namespace Glib;
130 using namespace Gtkmm2ext;
131 using namespace Editing;
133 using PBD::internationalize;
134 using PBD::atoi;
135 using Gtkmm2ext::Keyboard;
137 const double Editor::timebar_height = 15.0;
139 #include "editor_xpms"
141 static const gchar *_snap_type_strings[] = {
142 N_("CD Frames"),
143 N_("Timecode Frames"),
144 N_("Timecode Seconds"),
145 N_("Timecode Minutes"),
146 N_("Seconds"),
147 N_("Minutes"),
148 N_("Beats/32"),
149 N_("Beats/28"),
150 N_("Beats/24"),
151 N_("Beats/16"),
152 N_("Beats/14"),
153 N_("Beats/12"),
154 N_("Beats/10"),
155 N_("Beats/8"),
156 N_("Beats/7"),
157 N_("Beats/6"),
158 N_("Beats/5"),
159 N_("Beats/4"),
160 N_("Beats/3"),
161 N_("Beats/2"),
162 N_("Beats"),
163 N_("Bars"),
164 N_("Marks"),
165 N_("Region starts"),
166 N_("Region ends"),
167 N_("Region syncs"),
168 N_("Region bounds"),
172 static const gchar *_snap_mode_strings[] = {
173 N_("No Grid"),
174 N_("Grid"),
175 N_("Magnetic"),
179 static const gchar *_edit_point_strings[] = {
180 N_("Playhead"),
181 N_("Marker"),
182 N_("Mouse"),
186 static const gchar *_zoom_focus_strings[] = {
187 N_("Left"),
188 N_("Right"),
189 N_("Center"),
190 N_("Playhead"),
191 N_("Mouse"),
192 N_("Edit point"),
196 #ifdef USE_RUBBERBAND
197 static const gchar *_rb_opt_strings[] = {
198 N_("Mushy"),
199 N_("Smooth"),
200 N_("Balanced multitimbral mixture"),
201 N_("Unpitched percussion with stable notes"),
202 N_("Crisp monophonic instrumental"),
203 N_("Unpitched solo percussion"),
206 #endif
208 /* Soundfile drag-n-drop */
210 Gdk::Cursor* Editor::cross_hair_cursor = 0;
211 Gdk::Cursor* Editor::selector_cursor = 0;
212 Gdk::Cursor* Editor::trimmer_cursor = 0;
213 Gdk::Cursor* Editor::left_side_trim_cursor = 0;
214 Gdk::Cursor* Editor::right_side_trim_cursor = 0;
215 Gdk::Cursor* Editor::fade_in_cursor = 0;
216 Gdk::Cursor* Editor::fade_out_cursor = 0;
217 Gdk::Cursor* Editor::grabber_cursor = 0;
218 Gdk::Cursor* Editor::grabber_note_cursor = 0;
219 Gdk::Cursor* Editor::grabber_edit_point_cursor = 0;
220 Gdk::Cursor* Editor::zoom_cursor = 0;
221 Gdk::Cursor* Editor::time_fx_cursor = 0;
222 Gdk::Cursor* Editor::fader_cursor = 0;
223 Gdk::Cursor* Editor::speaker_cursor = 0;
224 Gdk::Cursor* Editor::midi_pencil_cursor = 0;
225 Gdk::Cursor* Editor::midi_select_cursor = 0;
226 Gdk::Cursor* Editor::midi_resize_cursor = 0;
227 Gdk::Cursor* Editor::midi_erase_cursor = 0;
228 Gdk::Cursor* Editor::wait_cursor = 0;
229 Gdk::Cursor* Editor::timebar_cursor = 0;
230 Gdk::Cursor* Editor::transparent_cursor = 0;
232 void
233 show_me_the_size (Requisition* r, const char* what)
235 cerr << "size of " << what << " = " << r->width << " x " << r->height << endl;
238 #ifdef GTKOSX
239 static void
240 pane_size_watcher (Paned* pane)
242 /* if the handle of a pane vanishes into (at least) the tabs of a notebook,
243 it is no longer accessible. so stop that. this doesn't happen on X11,
244 just the quartz backend.
246 ugh.
249 int max_width_of_lhs = GTK_WIDGET(pane->gobj())->allocation.width - 25;
251 gint pos = pane->get_position ();
253 if (pos > max_width_of_lhs) {
254 pane->set_position (max_width_of_lhs);
257 #endif
259 Editor::Editor ()
260 : _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
262 /* time display buttons */
263 , minsec_label (_("Mins:Secs"))
264 , bbt_label (_("Bars:Beats"))
265 , timecode_label (_("Timecode"))
266 , frame_label (_("Samples"))
267 , tempo_label (_("Tempo"))
268 , meter_label (_("Meter"))
269 , mark_label (_("Location Markers"))
270 , range_mark_label (_("Range Markers"))
271 , transport_mark_label (_("Loop/Punch Ranges"))
272 , cd_mark_label (_("CD Markers"))
273 , edit_packer (4, 4, true)
275 /* the values here don't matter: layout widgets
276 reset them as needed.
279 , vertical_adjustment (0.0, 0.0, 10.0, 400.0)
281 /* tool bar related */
283 , zoom_range_clock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, false, true)
285 , toolbar_selection_clock_table (2,3)
287 , automation_mode_button (_("mode"))
288 , global_automation_button (_("automation"))
290 , midi_panic_button (_("Panic"))
292 #ifdef WITH_CMT
293 , image_socket_listener(0)
294 #endif
296 /* nudge */
298 , nudge_clock (X_("nudge"), false, X_("NudgeClock"), true, false, true)
299 , meters_running(false)
300 , _pending_locate_request (false)
301 , _pending_initial_locate (false)
304 constructed = false;
306 /* we are a singleton */
308 PublicEditor::_instance = this;
310 _have_idled = false;
312 selection = new Selection (this);
313 cut_buffer = new Selection (this);
315 clicked_regionview = 0;
316 clicked_axisview = 0;
317 clicked_routeview = 0;
318 clicked_crossfadeview = 0;
319 clicked_control_point = 0;
320 last_update_frame = 0;
321 _drags = new DragManager (this);
322 current_mixer_strip = 0;
323 current_bbt_points = 0;
324 tempo_lines = 0;
326 snap_type_strings = I18N (_snap_type_strings);
327 snap_mode_strings = I18N (_snap_mode_strings);
328 zoom_focus_strings = I18N (_zoom_focus_strings);
329 edit_point_strings = I18N (_edit_point_strings);
330 #ifdef USE_RUBBERBAND
331 rb_opt_strings = I18N (_rb_opt_strings);
332 #endif
334 snap_threshold = 5.0;
335 bbt_beat_subdivision = 4;
336 _canvas_width = 0;
337 _canvas_height = 0;
338 last_autoscroll_x = 0;
339 last_autoscroll_y = 0;
340 autoscroll_active = false;
341 autoscroll_timeout_tag = -1;
342 logo_item = 0;
344 analysis_window = 0;
346 current_interthread_info = 0;
347 _show_measures = true;
348 show_gain_after_trim = false;
349 verbose_cursor_on = true;
350 last_item_entered = 0;
351 last_item_entered_n = 0;
353 have_pending_keyboard_selection = false;
354 _follow_playhead = true;
355 _stationary_playhead = false;
356 _xfade_visibility = true;
357 editor_ruler_menu = 0;
358 no_ruler_shown_update = false;
359 marker_menu = 0;
360 session_range_marker_menu = 0;
361 range_marker_menu = 0;
362 marker_menu_item = 0;
363 tempo_or_meter_marker_menu = 0;
364 transport_marker_menu = 0;
365 new_transport_marker_menu = 0;
366 editor_mixer_strip_width = Wide;
367 show_editor_mixer_when_tracks_arrive = false;
368 region_edit_menu_split_multichannel_item = 0;
369 region_edit_menu_split_item = 0;
370 temp_location = 0;
371 leftmost_frame = 0;
372 current_stepping_trackview = 0;
373 entered_track = 0;
374 entered_regionview = 0;
375 entered_marker = 0;
376 clear_entered_track = false;
377 current_timefx = 0;
378 playhead_cursor = 0;
379 button_release_can_deselect = true;
380 _dragging_playhead = false;
381 _dragging_edit_point = false;
382 select_new_marker = false;
383 rhythm_ferret = 0;
384 _bundle_manager = 0;
385 for (ARDOUR::DataType::iterator i = ARDOUR::DataType::begin(); i != ARDOUR::DataType::end(); ++i) {
386 _global_port_matrix[*i] = 0;
388 no_save_visual = false;
389 resize_idle_id = -1;
391 scrubbing_direction = 0;
393 sfbrowser = 0;
395 location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get();
396 location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get();
397 location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get();
398 location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get();
399 location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get();
401 _edit_point = EditAtMouse;
402 _internal_editing = false;
403 current_canvas_cursor = 0;
405 frames_per_unit = 2048; /* too early to use reset_zoom () */
407 _scroll_callbacks = 0;
409 zoom_focus = ZoomFocusLeft;
410 set_zoom_focus (ZoomFocusLeft);
411 zoom_range_clock.ValueChanged.connect (sigc::mem_fun(*this, &Editor::zoom_adjustment_changed));
413 bbt_label.set_name ("EditorTimeButton");
414 bbt_label.set_size_request (-1, (int)timebar_height);
415 bbt_label.set_alignment (1.0, 0.5);
416 bbt_label.set_padding (5,0);
417 bbt_label.hide ();
418 bbt_label.set_no_show_all();
419 minsec_label.set_name ("EditorTimeButton");
420 minsec_label.set_size_request (-1, (int)timebar_height);
421 minsec_label.set_alignment (1.0, 0.5);
422 minsec_label.set_padding (5,0);
423 minsec_label.hide ();
424 minsec_label.set_no_show_all();
425 timecode_label.set_name ("EditorTimeButton");
426 timecode_label.set_size_request (-1, (int)timebar_height);
427 timecode_label.set_alignment (1.0, 0.5);
428 timecode_label.set_padding (5,0);
429 timecode_label.hide ();
430 timecode_label.set_no_show_all();
431 frame_label.set_name ("EditorTimeButton");
432 frame_label.set_size_request (-1, (int)timebar_height);
433 frame_label.set_alignment (1.0, 0.5);
434 frame_label.set_padding (5,0);
435 frame_label.hide ();
436 frame_label.set_no_show_all();
438 tempo_label.set_name ("EditorTimeButton");
439 tempo_label.set_size_request (-1, (int)timebar_height);
440 tempo_label.set_alignment (1.0, 0.5);
441 tempo_label.set_padding (5,0);
442 tempo_label.hide();
443 tempo_label.set_no_show_all();
444 meter_label.set_name ("EditorTimeButton");
445 meter_label.set_size_request (-1, (int)timebar_height);
446 meter_label.set_alignment (1.0, 0.5);
447 meter_label.set_padding (5,0);
448 meter_label.hide();
449 meter_label.set_no_show_all();
450 mark_label.set_name ("EditorTimeButton");
451 mark_label.set_size_request (-1, (int)timebar_height);
452 mark_label.set_alignment (1.0, 0.5);
453 mark_label.set_padding (5,0);
454 mark_label.hide();
455 mark_label.set_no_show_all();
456 cd_mark_label.set_name ("EditorTimeButton");
457 cd_mark_label.set_size_request (-1, (int)timebar_height);
458 cd_mark_label.set_alignment (1.0, 0.5);
459 cd_mark_label.set_padding (5,0);
460 cd_mark_label.hide();
461 cd_mark_label.set_no_show_all();
462 range_mark_label.set_name ("EditorTimeButton");
463 range_mark_label.set_size_request (-1, (int)timebar_height);
464 range_mark_label.set_alignment (1.0, 0.5);
465 range_mark_label.set_padding (5,0);
466 range_mark_label.hide();
467 range_mark_label.set_no_show_all();
468 transport_mark_label.set_name ("EditorTimeButton");
469 transport_mark_label.set_size_request (-1, (int)timebar_height);
470 transport_mark_label.set_alignment (1.0, 0.5);
471 transport_mark_label.set_padding (5,0);
472 transport_mark_label.hide();
473 transport_mark_label.set_no_show_all();
475 initialize_rulers ();
476 initialize_canvas ();
477 _summary = new EditorSummary (this);
479 selection->TimeChanged.connect (sigc::mem_fun(*this, &Editor::time_selection_changed));
480 selection->TracksChanged.connect (sigc::mem_fun(*this, &Editor::track_selection_changed));
481 editor_regions_selection_changed_connection = selection->RegionsChanged.connect (sigc::mem_fun(*this, &Editor::region_selection_changed));
482 selection->PointsChanged.connect (sigc::mem_fun(*this, &Editor::point_selection_changed));
483 selection->MarkersChanged.connect (sigc::mem_fun(*this, &Editor::marker_selection_changed));
485 edit_controls_vbox.set_spacing (0);
486 vertical_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &Editor::tie_vertical_scrolling), true);
487 track_canvas->signal_map_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_map_handler));
489 HBox* h = manage (new HBox);
490 _group_tabs = new EditorGroupTabs (this);
491 h->pack_start (*_group_tabs, PACK_SHRINK);
492 h->pack_start (edit_controls_vbox);
493 controls_layout.add (*h);
495 controls_layout.set_name ("EditControlsBase");
496 controls_layout.add_events (Gdk::SCROLL_MASK);
497 controls_layout.signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::control_layout_scroll), false);
499 controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
500 controls_layout.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_release));
501 controls_layout_size_request_connection = controls_layout.signal_size_request().connect (sigc::mem_fun (*this, &Editor::controls_layout_size_request));
503 build_cursors ();
505 ArdourCanvas::Canvas* time_pad = manage(new ArdourCanvas::Canvas());
506 ArdourCanvas::SimpleLine* pad_line_1 = manage(new ArdourCanvas::SimpleLine(*time_pad->root(),
507 0.0, 1.0, 100.0, 1.0));
508 pad_line_1->property_color_rgba() = 0xFF0000FF;
509 pad_line_1->show();
510 time_pad->show();
512 time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2);
513 time_canvas_vbox.set_size_request (-1, -1);
515 ruler_label_event_box.add (ruler_label_vbox);
516 ruler_label_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
517 ruler_label_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
519 time_button_event_box.add (time_button_vbox);
520 time_button_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
521 time_button_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
523 /* these enable us to have a dedicated window (for cursor setting, etc.)
524 for the canvas areas.
527 track_canvas_event_box.add (*track_canvas);
529 time_canvas_event_box.add (time_canvas_vbox);
530 time_canvas_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
532 edit_packer.set_col_spacings (0);
533 edit_packer.set_row_spacings (0);
534 edit_packer.set_homogeneous (false);
535 edit_packer.set_border_width (0);
536 edit_packer.set_name ("EditorWindow");
538 edit_packer.attach (zoom_vbox, 0, 1, 0, 2, SHRINK, FILL, 0, 0);
539 /* labels for the rulers */
540 edit_packer.attach (ruler_label_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0);
541 /* labels for the marker "tracks" */
542 edit_packer.attach (time_button_event_box, 1, 2, 1, 2, FILL, SHRINK, 0, 0);
543 /* the rulers */
544 edit_packer.attach (time_canvas_event_box, 2, 3, 0, 1, FILL|EXPAND, FILL, 0, 0);
545 /* track controls */
546 edit_packer.attach (controls_layout, 0, 2, 2, 3, FILL, FILL|EXPAND, 0, 0);
547 /* main canvas */
548 edit_packer.attach (track_canvas_event_box, 2, 3, 1, 3, FILL|EXPAND, FILL|EXPAND, 0, 0);
550 bottom_hbox.set_border_width (2);
551 bottom_hbox.set_spacing (3);
553 _route_groups = new EditorRouteGroups (this);
554 _routes = new EditorRoutes (this);
555 _regions = new EditorRegions (this);
556 _snapshots = new EditorSnapshots (this);
557 _locations = new EditorLocations (this);
559 Gtk::Label* nlabel;
561 nlabel = manage (new Label (_("Regions")));
562 nlabel->set_angle (-90);
563 the_notebook.append_page (_regions->widget (), *nlabel);
564 nlabel = manage (new Label (_("Tracks & Busses")));
565 nlabel->set_angle (-90);
566 the_notebook.append_page (_routes->widget (), *nlabel);
567 nlabel = manage (new Label (_("Snapshots")));
568 nlabel->set_angle (-90);
569 the_notebook.append_page (_snapshots->widget (), *nlabel);
570 nlabel = manage (new Label (_("Route Groups")));
571 nlabel->set_angle (-90);
572 the_notebook.append_page (_route_groups->widget (), *nlabel);
573 nlabel = manage (new Label (_("Ranges & Marks")));
574 nlabel->set_angle (-90);
575 the_notebook.append_page (_locations->widget (), *nlabel);
577 the_notebook.set_show_tabs (true);
578 the_notebook.set_scrollable (true);
579 the_notebook.popup_disable ();
580 the_notebook.set_tab_pos (Gtk::POS_RIGHT);
581 the_notebook.show_all ();
583 post_maximal_editor_width = 0;
584 post_maximal_horizontal_pane_position = 0;
585 post_maximal_editor_height = 0;
586 post_maximal_vertical_pane_position = 0;
588 editor_summary_pane.pack1(edit_packer);
590 Button* summary_arrows_left_left = manage (new Button);
591 summary_arrows_left_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
592 summary_arrows_left_left->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press)));
593 summary_arrows_left_left->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_release));
594 Button* summary_arrows_left_right = manage (new Button);
595 summary_arrows_left_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
596 summary_arrows_left_right->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press)));
597 summary_arrows_left_right->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_release));
598 VBox* summary_arrows_left = manage (new VBox);
599 summary_arrows_left->pack_start (*summary_arrows_left_left);
600 summary_arrows_left->pack_start (*summary_arrows_left_right);
602 Button* summary_arrows_right_left = manage (new Button);
603 summary_arrows_right_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
604 summary_arrows_right_left->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press)));
605 summary_arrows_right_left->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_release));
606 Button* summary_arrows_right_right = manage (new Button);
607 summary_arrows_right_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
608 summary_arrows_right_right->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press)));
609 summary_arrows_right_right->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_release));
610 VBox* summary_arrows_right = manage (new VBox);
611 summary_arrows_right->pack_start (*summary_arrows_right_left);
612 summary_arrows_right->pack_start (*summary_arrows_right_right);
614 Frame* summary_frame = manage (new Frame);
615 summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN);
616 summary_frame->add (*_summary);
617 summary_frame->show ();
619 _summary_hbox.pack_start (*summary_arrows_left, false, false);
620 _summary_hbox.pack_start (*summary_frame, true, true);
621 _summary_hbox.pack_start (*summary_arrows_right, false, false);
623 editor_summary_pane.pack2 (_summary_hbox);
625 edit_pane.pack1 (editor_summary_pane, true, true);
626 edit_pane.pack2 (the_notebook, false, true);
628 editor_summary_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun (*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&editor_summary_pane)));
630 /* XXX: editor_summary_pane might need similar special OS X treatment to the edit_pane */
632 edit_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&edit_pane)));
633 #ifdef GTKOSX
634 Glib::PropertyProxy<int> proxy = edit_pane.property_position();
635 proxy.signal_changed().connect (bind (sigc::ptr_fun (pane_size_watcher), static_cast<Paned*> (&edit_pane)));
636 #endif
637 top_hbox.pack_start (toolbar_frame, false, true);
639 HBox *hbox = manage (new HBox);
640 hbox->pack_start (edit_pane, true, true);
642 global_vpacker.pack_start (top_hbox, false, false);
643 global_vpacker.pack_start (*hbox, true, true);
645 global_hpacker.pack_start (global_vpacker, true, true);
647 set_name ("EditorWindow");
648 add_accel_group (ActionManager::ui_manager->get_accel_group());
650 status_bar_hpacker.show ();
652 vpacker.pack_end (status_bar_hpacker, false, false);
653 vpacker.pack_end (global_hpacker, true, true);
655 /* register actions now so that set_state() can find them and set toggles/checks etc */
657 register_actions ();
659 setup_toolbar ();
660 setup_midi_toolbar ();
662 _snap_type = SnapToBeat;
663 set_snap_to (_snap_type);
664 _snap_mode = SnapOff;
665 set_snap_mode (_snap_mode);
666 set_mouse_mode (MouseObject, true);
667 set_edit_point_preference (EditAtMouse, true);
669 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
670 set_state (*node, Stateful::loading_state_version);
672 _playlist_selector = new PlaylistSelector();
673 _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
675 RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), ui_bind (&Editor::catch_vanishing_regionview, this, _1), gui_context());
677 /* nudge stuff */
679 nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
680 nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
682 nudge_forward_button.set_name ("TransportButton");
683 nudge_backward_button.set_name ("TransportButton");
685 fade_context_menu.set_name ("ArdourContextMenu");
687 /* icons, titles, WM stuff */
689 list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
690 Glib::RefPtr<Gdk::Pixbuf> icon;
692 if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
693 window_icons.push_back (icon);
695 if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
696 window_icons.push_back (icon);
698 if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
699 window_icons.push_back (icon);
701 if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
702 window_icons.push_back (icon);
704 if (!window_icons.empty()) {
705 set_icon_list (window_icons);
706 set_default_icon_list (window_icons);
709 WindowTitle title(Glib::get_application_name());
710 title += _("Editor");
711 set_title (title.get_string());
712 set_wmclass (X_("ardour_editor"), "Ardour");
714 add (vpacker);
715 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
717 signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
718 signal_delete_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
720 /* allow external control surfaces/protocols to do various things */
722 ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context());
723 ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context());
724 ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context());
725 ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), ui_bind (&Editor::control_scroll, this, _1), gui_context());
726 BasicUI::AccessAction.connect (*this, invalidator (*this), ui_bind (&Editor::access_action, this, _1, _2), gui_context());
728 /* problematic: has to return a value and thus cannot be x-thread */
730 Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
732 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
734 TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
736 _last_normalization_value = 0;
738 constructed = true;
739 instant_save ();
742 Editor::~Editor()
744 #ifdef WITH_CMT
745 if(image_socket_listener) {
746 if(image_socket_listener->is_connected())
748 image_socket_listener->close_connection() ;
751 delete image_socket_listener ;
752 image_socket_listener = 0 ;
754 #endif
756 delete _routes;
757 delete _route_groups;
758 delete track_canvas;
759 delete _drags;
762 void
763 Editor::add_toplevel_controls (Container& cont)
765 vpacker.pack_start (cont, false, false);
766 cont.show_all ();
769 void
770 Editor::catch_vanishing_regionview (RegionView *rv)
772 /* note: the selection will take care of the vanishing
773 audioregionview by itself.
776 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
777 _drags->abort ();
780 if (clicked_regionview == rv) {
781 clicked_regionview = 0;
784 if (entered_regionview == rv) {
785 set_entered_regionview (0);
789 void
790 Editor::set_entered_regionview (RegionView* rv)
792 if (rv == entered_regionview) {
793 return;
796 if (entered_regionview) {
797 entered_regionview->exited ();
800 if ((entered_regionview = rv) != 0) {
801 entered_regionview->entered ();
805 void
806 Editor::set_entered_track (TimeAxisView* tav)
808 if (entered_track) {
809 entered_track->exited ();
812 if ((entered_track = tav) != 0) {
813 entered_track->entered ();
817 void
818 Editor::show_window ()
820 if (! is_visible ()) {
821 show_all ();
823 /* re-hide editor list if necessary */
824 editor_list_button_toggled ();
826 /* re-hide summary widget if necessary */
827 parameter_changed ("show-summary");
829 parameter_changed ("show-edit-group-tabs");
831 /* now reset all audio_time_axis heights, because widgets might need
832 to be re-hidden
835 TimeAxisView *tv;
837 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
838 tv = (static_cast<TimeAxisView*>(*i));
839 tv->reset_height ();
842 reset_zoom (frames_per_unit);
845 present ();
848 void
849 Editor::instant_save ()
851 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
852 return;
855 if (_session) {
856 _session->add_instant_xml(get_state());
857 } else {
858 Config->add_instant_xml(get_state());
862 void
863 Editor::zoom_adjustment_changed ()
865 if (_session == 0) {
866 return;
869 double fpu = zoom_range_clock.current_duration() / _canvas_width;
871 if (fpu < 1.0) {
872 fpu = 1.0;
873 zoom_range_clock.set ((nframes64_t) floor (fpu * _canvas_width));
874 } else if (fpu > _session->current_end_frame() / _canvas_width) {
875 fpu = _session->current_end_frame() / _canvas_width;
876 zoom_range_clock.set ((nframes64_t) floor (fpu * _canvas_width));
879 temporal_zoom (fpu);
882 void
883 Editor::control_scroll (float fraction)
885 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
887 if (!_session) {
888 return;
891 double step = fraction * current_page_frames();
894 _control_scroll_target is an optional<T>
896 it acts like a pointer to an nframes64_t, with
897 a operator conversion to boolean to check
898 that it has a value could possibly use
899 playhead_cursor->current_frame to store the
900 value and a boolean in the class to know
901 when it's out of date
904 if (!_control_scroll_target) {
905 _control_scroll_target = _session->transport_frame();
906 _dragging_playhead = true;
909 if ((fraction < 0.0f) && (*_control_scroll_target < (nframes64_t) fabs(step))) {
910 *_control_scroll_target = 0;
911 } else if ((fraction > 0.0f) && (max_frames - *_control_scroll_target < step)) {
912 *_control_scroll_target = max_frames - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
913 } else {
914 *_control_scroll_target += (nframes64_t) floor (step);
917 /* move visuals, we'll catch up with it later */
919 playhead_cursor->set_position (*_control_scroll_target);
920 UpdateAllTransportClocks (*_control_scroll_target);
922 if (*_control_scroll_target > (current_page_frames() / 2)) {
923 /* try to center PH in window */
924 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
925 } else {
926 reset_x_origin (0);
930 Now we do a timeout to actually bring the session to the right place
931 according to the playhead. This is to avoid reading disk buffers on every
932 call to control_scroll, which is driven by ScrollTimeline and therefore
933 probably by a control surface wheel which can generate lots of events.
935 /* cancel the existing timeout */
937 control_scroll_connection.disconnect ();
939 /* add the next timeout */
941 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
944 bool
945 Editor::deferred_control_scroll (nframes64_t /*target*/)
947 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
948 // reset for next stream
949 _control_scroll_target = boost::none;
950 _dragging_playhead = false;
951 return false;
954 void
955 Editor::access_action (std::string action_group, std::string action_item)
957 if (!_session) {
958 return;
961 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
963 RefPtr<Action> act;
964 act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
966 if (act) {
967 act->activate();
971 void
972 Editor::on_realize ()
974 Window::on_realize ();
975 Realized ();
978 void
979 Editor::map_position_change (nframes64_t frame)
981 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, frame)
983 if (_session == 0) {
984 return;
987 if (_follow_playhead) {
988 center_screen (frame);
991 playhead_cursor->set_position (frame);
994 void
995 Editor::center_screen (nframes64_t frame)
997 double page = _canvas_width * frames_per_unit;
999 /* if we're off the page, then scroll.
1002 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
1003 center_screen_internal (frame, page);
1007 void
1008 Editor::center_screen_internal (nframes64_t frame, float page)
1010 page /= 2;
1012 if (frame > page) {
1013 frame -= (nframes64_t) page;
1014 } else {
1015 frame = 0;
1018 reset_x_origin (frame);
1022 void
1023 Editor::update_title ()
1025 ENSURE_GUI_THREAD (*this, &Editor::update_title)
1027 if (_session) {
1028 bool dirty = _session->dirty();
1030 string session_name;
1032 if (_session->snap_name() != _session->name()) {
1033 session_name = _session->snap_name();
1034 } else {
1035 session_name = _session->name();
1038 if (dirty) {
1039 session_name = "*" + session_name;
1042 WindowTitle title(session_name);
1043 title += Glib::get_application_name();
1044 set_title (title.get_string());
1048 void
1049 Editor::set_session (Session *t)
1051 SessionHandlePtr::set_session (t);
1053 if (!_session) {
1054 return;
1057 zoom_range_clock.set_session (_session);
1058 _playlist_selector->set_session (_session);
1059 nudge_clock.set_session (_session);
1060 _summary->set_session (_session);
1061 _group_tabs->set_session (_session);
1062 _route_groups->set_session (_session);
1063 _regions->set_session (_session);
1064 _snapshots->set_session (_session);
1065 _routes->set_session (_session);
1066 _locations->set_session (_session);
1068 if (rhythm_ferret) {
1069 rhythm_ferret->set_session (_session);
1072 if (analysis_window) {
1073 analysis_window->set_session (_session);
1076 if (sfbrowser) {
1077 sfbrowser->set_session (_session);
1080 compute_fixed_ruler_scale ();
1082 /* there are never any selected regions at startup */
1083 sensitize_the_right_region_actions (false);
1085 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1086 set_state (*node, Stateful::loading_state_version);
1088 /* catch up with the playhead */
1090 _session->request_locate (playhead_cursor->current_frame);
1091 _pending_initial_locate = true;
1093 update_title ();
1095 /* These signals can all be emitted by a non-GUI thread. Therefore the
1096 handlers for them must not attempt to directly interact with the GUI,
1097 but use Gtkmm2ext::UI::instance()->call_slot();
1100 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1101 _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context());
1102 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context());
1103 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1104 _session->TimecodeOffsetChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_just_timecode, this), gui_context());
1105 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context());
1106 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1107 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
1108 _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Editor::session_state_saved, this, _1), gui_context());
1109 _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context());
1110 _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&Editor::location_gone, this, _1), gui_context());
1111 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1112 _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display_s, this, _1), gui_context());
1113 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1115 if (Profile->get_sae()) {
1116 BBT_Time bbt;
1117 bbt.bars = 0;
1118 bbt.beats = 0;
1119 bbt.ticks = 120;
1120 nframes_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1);
1121 nudge_clock.set_mode(AudioClock::BBT);
1122 nudge_clock.set (pos, true, 0, AudioClock::BBT);
1124 } else {
1125 nudge_clock.set (_session->frame_rate() * 5, true, 0, AudioClock::Timecode); // default of 5 seconds
1128 playhead_cursor->canvas_item.show ();
1130 Location* loc = _session->locations()->auto_loop_location();
1131 if (loc == 0) {
1132 loc = new Location (0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1133 if (loc->start() == loc->end()) {
1134 loc->set_end (loc->start() + 1);
1136 _session->locations()->add (loc, false);
1137 _session->set_auto_loop_location (loc);
1138 } else {
1139 // force name
1140 loc->set_name (_("Loop"));
1143 loc = _session->locations()->auto_punch_location();
1144 if (loc == 0) {
1145 loc = new Location (0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1146 if (loc->start() == loc->end()) {
1147 loc->set_end (loc->start() + 1);
1149 _session->locations()->add (loc, false);
1150 _session->set_auto_punch_location (loc);
1151 } else {
1152 // force name
1153 loc->set_name (_("Punch"));
1156 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1157 Config->map_parameters (pc);
1158 _session->config.map_parameters (pc);
1160 refresh_location_display ();
1162 restore_ruler_visibility ();
1163 //tempo_map_changed (PropertyChange (0));
1164 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1166 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1167 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1170 super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
1171 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1174 switch (_snap_type) {
1175 case SnapToRegionStart:
1176 case SnapToRegionEnd:
1177 case SnapToRegionSync:
1178 case SnapToRegionBoundary:
1179 build_region_boundary_cache ();
1180 break;
1182 default:
1183 break;
1186 /* register for undo history */
1187 _session->register_with_memento_command_factory(_id, this);
1189 start_updating_meters ();
1192 void
1193 Editor::build_cursors ()
1195 using namespace Gdk;
1197 Gdk::Color mbg ("#000000" ); /* Black */
1198 Gdk::Color mfg ("#0000ff" ); /* Blue. */
1201 RefPtr<Bitmap> source, mask;
1202 source = Bitmap::create (mag_bits, mag_width, mag_height);
1203 mask = Bitmap::create (magmask_bits, mag_width, mag_height);
1204 zoom_cursor = new Gdk::Cursor (source, mask, mfg, mbg, mag_x_hot, mag_y_hot);
1207 Gdk::Color fbg ("#ffffff" );
1208 Gdk::Color ffg ("#000000" );
1211 RefPtr<Bitmap> source, mask;
1213 source = Bitmap::create (fader_cursor_bits, fader_cursor_width, fader_cursor_height);
1214 mask = Bitmap::create (fader_cursor_mask_bits, fader_cursor_width, fader_cursor_height);
1215 fader_cursor = new Gdk::Cursor (source, mask, ffg, fbg, fader_cursor_x_hot, fader_cursor_y_hot);
1219 RefPtr<Bitmap> source, mask;
1220 source = Bitmap::create (speaker_cursor_bits, speaker_cursor_width, speaker_cursor_height);
1221 mask = Bitmap::create (speaker_cursor_mask_bits, speaker_cursor_width, speaker_cursor_height);
1222 speaker_cursor = new Gdk::Cursor (source, mask, ffg, fbg, speaker_cursor_x_hot, speaker_cursor_y_hot);
1226 RefPtr<Bitmap> bits;
1227 char pix[4] = { 0, 0, 0, 0 };
1228 bits = Bitmap::create (pix, 2, 2);
1229 Gdk::Color c;
1230 transparent_cursor = new Gdk::Cursor (bits, bits, c, c, 0, 0);
1234 RefPtr<Bitmap> bits;
1235 char pix[4] = { 0, 0, 0, 0 };
1236 bits = Bitmap::create (pix, 2, 2);
1237 Gdk::Color c;
1238 transparent_cursor = new Gdk::Cursor (bits, bits, c, c, 0, 0);
1242 grabber_cursor = new Gdk::Cursor (HAND2);
1245 Glib::RefPtr<Gdk::Pixbuf> grabber_note_pixbuf (::get_icon ("grabber_note"));
1246 grabber_note_cursor = new Gdk::Cursor (Gdk::Display::get_default(), grabber_note_pixbuf, 5, 10);
1250 Glib::RefPtr<Gdk::Pixbuf> grabber_edit_point_pixbuf (::get_icon ("grabber_edit_point"));
1251 grabber_edit_point_cursor = new Gdk::Cursor (Gdk::Display::get_default(), grabber_edit_point_pixbuf, 5, 17);
1254 cross_hair_cursor = new Gdk::Cursor (CROSSHAIR);
1255 trimmer_cursor = new Gdk::Cursor (SB_H_DOUBLE_ARROW);
1258 Glib::RefPtr<Gdk::Pixbuf> apixbuf (::get_icon ("trim_left_cursor"));
1259 left_side_trim_cursor = new Gdk::Cursor (Gdk::Display::get_default(), apixbuf, 5, 11);
1263 Glib::RefPtr<Gdk::Pixbuf> apixbuf (::get_icon ("trim_right_cursor"));
1264 right_side_trim_cursor = new Gdk::Cursor (Gdk::Display::get_default(), apixbuf, 23, 11);
1268 Glib::RefPtr<Gdk::Pixbuf> apixbuf (::get_icon ("fade_in_cursor"));
1269 fade_in_cursor = new Gdk::Cursor (Gdk::Display::get_default(), apixbuf, 0, 40);
1273 Glib::RefPtr<Gdk::Pixbuf> apixbuf (::get_icon ("fade_out_cursor"));
1274 fade_out_cursor = new Gdk::Cursor (Gdk::Display::get_default(), apixbuf, 27, 40);
1277 selector_cursor = new Gdk::Cursor (XTERM);
1278 time_fx_cursor = new Gdk::Cursor (SIZING);
1279 wait_cursor = new Gdk::Cursor (WATCH);
1280 timebar_cursor = new Gdk::Cursor(LEFT_PTR);
1281 midi_pencil_cursor = new Gdk::Cursor (PENCIL);
1282 midi_select_cursor = new Gdk::Cursor (CENTER_PTR);
1283 midi_resize_cursor = new Gdk::Cursor (SIZING);
1284 midi_erase_cursor = new Gdk::Cursor (DRAPED_BOX);
1287 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1288 void
1289 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1291 using namespace Menu_Helpers;
1292 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1294 if (arv == 0) {
1295 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1296 /*NOTREACHED*/
1299 MenuList& items (fade_context_menu.items());
1301 items.clear ();
1303 switch (item_type) {
1304 case FadeInItem:
1305 case FadeInHandleItem:
1306 if (arv->audio_region()->fade_in_active()) {
1307 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1308 } else {
1309 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1312 items.push_back (SeparatorElem());
1314 if (Profile->get_sae()) {
1315 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear)));
1316 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast)));
1317 } else {
1318 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear)));
1319 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast)));
1320 items.push_back (MenuElem (_("Slow"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogB)));
1321 items.push_back (MenuElem (_("Fast"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogA)));
1322 items.push_back (MenuElem (_("Fastest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Slow)));
1325 break;
1327 case FadeOutItem:
1328 case FadeOutHandleItem:
1329 if (arv->audio_region()->fade_out_active()) {
1330 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1331 } else {
1332 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1335 items.push_back (SeparatorElem());
1337 if (Profile->get_sae()) {
1338 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear)));
1339 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow)));
1340 } else {
1341 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear)));
1342 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow)));
1343 items.push_back (MenuElem (_("Slow"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogA)));
1344 items.push_back (MenuElem (_("Fast"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogB)));
1345 items.push_back (MenuElem (_("Fastest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Fast)));
1348 break;
1350 default:
1351 fatal << _("programming error: ")
1352 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1353 << endmsg;
1354 /*NOTREACHED*/
1357 fade_context_menu.popup (button, time);
1360 void
1361 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection, nframes64_t frame)
1363 using namespace Menu_Helpers;
1364 Menu* (Editor::*build_menu_function)(nframes64_t);
1365 Menu *menu;
1367 switch (item_type) {
1368 case RegionItem:
1369 case RegionViewName:
1370 case RegionViewNameHighlight:
1371 case LeftFrameHandle:
1372 case RightFrameHandle:
1373 if (with_selection) {
1374 build_menu_function = &Editor::build_track_selection_context_menu;
1375 } else {
1376 build_menu_function = &Editor::build_track_region_context_menu;
1378 break;
1380 case SelectionItem:
1381 if (with_selection) {
1382 build_menu_function = &Editor::build_track_selection_context_menu;
1383 } else {
1384 build_menu_function = &Editor::build_track_context_menu;
1386 break;
1388 case CrossfadeViewItem:
1389 build_menu_function = &Editor::build_track_crossfade_context_menu;
1390 break;
1392 case StreamItem:
1393 if (clicked_routeview->track()) {
1394 build_menu_function = &Editor::build_track_context_menu;
1395 } else {
1396 build_menu_function = &Editor::build_track_bus_context_menu;
1398 break;
1400 default:
1401 /* probably shouldn't happen but if it does, we don't care */
1402 return;
1405 menu = (this->*build_menu_function)(frame);
1406 menu->set_name ("ArdourContextMenu");
1408 /* now handle specific situations */
1410 switch (item_type) {
1411 case RegionItem:
1412 case RegionViewName:
1413 case RegionViewNameHighlight:
1414 case LeftFrameHandle:
1415 case RightFrameHandle:
1416 if (!with_selection) {
1417 if (region_edit_menu_split_item) {
1418 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1419 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1420 } else {
1421 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1424 if (region_edit_menu_split_multichannel_item) {
1425 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1426 region_edit_menu_split_multichannel_item->set_sensitive (true);
1427 } else {
1428 region_edit_menu_split_multichannel_item->set_sensitive (false);
1432 break;
1434 case SelectionItem:
1435 break;
1437 case CrossfadeViewItem:
1438 break;
1440 case StreamItem:
1441 break;
1443 default:
1444 /* probably shouldn't happen but if it does, we don't care */
1445 return;
1448 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1450 /* Bounce to disk */
1452 using namespace Menu_Helpers;
1453 MenuList& edit_items = menu->items();
1455 edit_items.push_back (SeparatorElem());
1457 switch (clicked_routeview->audio_track()->freeze_state()) {
1458 case AudioTrack::NoFreeze:
1459 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1460 break;
1462 case AudioTrack::Frozen:
1463 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1464 break;
1466 case AudioTrack::UnFrozen:
1467 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1468 break;
1473 if (item_type == StreamItem && clicked_routeview) {
1474 clicked_routeview->build_underlay_menu(menu);
1477 menu->popup (button, time);
1480 Menu*
1481 Editor::build_track_context_menu (nframes64_t)
1483 using namespace Menu_Helpers;
1485 MenuList& edit_items = track_context_menu.items();
1486 edit_items.clear();
1488 add_dstream_context_items (edit_items);
1489 return &track_context_menu;
1492 Menu*
1493 Editor::build_track_bus_context_menu (nframes64_t)
1495 using namespace Menu_Helpers;
1497 MenuList& edit_items = track_context_menu.items();
1498 edit_items.clear();
1500 add_bus_context_items (edit_items);
1501 return &track_context_menu;
1504 Menu*
1505 Editor::build_track_region_context_menu (nframes64_t frame)
1507 using namespace Menu_Helpers;
1508 MenuList& edit_items = track_region_context_menu.items();
1509 edit_items.clear();
1511 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1513 if (rtv) {
1514 boost::shared_ptr<Track> tr;
1515 boost::shared_ptr<Playlist> pl;
1517 /* Don't offer a region submenu if we are in internal edit mode, as we don't select regions in this
1518 mode and so offering region context is somewhat confusing.
1520 if ((tr = rtv->track()) && ((pl = tr->playlist())) && !internal_editing()) {
1521 Playlist::RegionList* regions = pl->regions_at ((nframes64_t) floor ( (double)frame * tr->speed()));
1523 if (selection->regions.size() > 1) {
1524 // there's already a multiple selection: just add a
1525 // single region context menu that will act on all
1526 // selected regions
1527 boost::shared_ptr<Region> dummy_region; // = NULL
1528 add_region_context_items (rtv->view(), dummy_region, edit_items);
1529 } else {
1530 for (Playlist::RegionList::reverse_iterator i = regions->rbegin(); i != regions->rend(); ++i) {
1531 add_region_context_items (rtv->view(), (*i), edit_items);
1535 delete regions;
1539 add_dstream_context_items (edit_items);
1541 return &track_region_context_menu;
1544 Menu*
1545 Editor::build_track_crossfade_context_menu (nframes64_t frame)
1547 using namespace Menu_Helpers;
1548 MenuList& edit_items = track_crossfade_context_menu.items();
1549 edit_items.clear ();
1551 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1553 if (atv) {
1554 boost::shared_ptr<Track> tr;
1555 boost::shared_ptr<Playlist> pl;
1556 boost::shared_ptr<AudioPlaylist> apl;
1558 if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1560 Playlist::RegionList* regions = pl->regions_at (frame);
1561 AudioPlaylist::Crossfades xfades;
1563 apl->crossfades_at (frame, xfades);
1565 bool many = xfades.size() > 1;
1567 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1568 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1571 if (selection->regions.size() > 1) {
1572 // there's already a multiple selection: just add a
1573 // single region context menu that will act on all
1574 // selected regions
1575 boost::shared_ptr<Region> dummy_region; // = NULL
1576 add_region_context_items (atv->audio_view(), dummy_region, edit_items);
1577 } else {
1578 for (Playlist::RegionList::reverse_iterator i = regions->rbegin(); i != regions->rend(); ++i) {
1579 add_region_context_items (atv->audio_view(), (*i), edit_items);
1582 delete regions;
1586 add_dstream_context_items (edit_items);
1588 return &track_crossfade_context_menu;
1591 void
1592 Editor::analyze_region_selection()
1594 if (analysis_window == 0) {
1595 analysis_window = new AnalysisWindow();
1597 if (_session != 0)
1598 analysis_window->set_session(_session);
1600 analysis_window->show_all();
1603 analysis_window->set_regionmode();
1604 analysis_window->analyze();
1606 analysis_window->present();
1609 void
1610 Editor::analyze_range_selection()
1612 if (analysis_window == 0) {
1613 analysis_window = new AnalysisWindow();
1615 if (_session != 0)
1616 analysis_window->set_session(_session);
1618 analysis_window->show_all();
1621 analysis_window->set_rangemode();
1622 analysis_window->analyze();
1624 analysis_window->present();
1627 Menu*
1628 Editor::build_track_selection_context_menu (nframes64_t)
1630 using namespace Menu_Helpers;
1631 MenuList& edit_items = track_selection_context_menu.items();
1632 edit_items.clear ();
1634 add_selection_context_items (edit_items);
1635 // edit_items.push_back (SeparatorElem());
1636 // add_dstream_context_items (edit_items);
1638 return &track_selection_context_menu;
1641 /** Add context menu items relevant to crossfades.
1642 * @param edit_items List to add the items to.
1644 void
1645 Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1647 using namespace Menu_Helpers;
1648 Menu *xfade_menu = manage (new Menu);
1649 MenuList& items = xfade_menu->items();
1650 xfade_menu->set_name ("ArdourContextMenu");
1651 string str;
1653 if (xfade->active()) {
1654 str = _("Mute");
1655 } else {
1656 str = _("Unmute");
1659 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1660 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1662 if (xfade->can_follow_overlap()) {
1664 if (xfade->following_overlap()) {
1665 str = _("Convert to Short");
1666 } else {
1667 str = _("Convert to Full");
1670 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1673 if (many) {
1674 str = xfade->out()->name();
1675 str += "->";
1676 str += xfade->in()->name();
1677 } else {
1678 str = _("Crossfade");
1681 edit_items.push_back (MenuElem (str, *xfade_menu));
1682 edit_items.push_back (SeparatorElem());
1685 void
1686 Editor::xfade_edit_left_region ()
1688 if (clicked_crossfadeview) {
1689 clicked_crossfadeview->left_view.show_region_editor ();
1693 void
1694 Editor::xfade_edit_right_region ()
1696 if (clicked_crossfadeview) {
1697 clicked_crossfadeview->right_view.show_region_editor ();
1701 void
1702 Editor::add_region_context_items (StreamView* sv, boost::shared_ptr<Region> region, Menu_Helpers::MenuList& edit_items)
1704 using namespace Menu_Helpers;
1705 Gtk::MenuItem* foo_item;
1706 Menu *region_menu = manage (new Menu);
1707 MenuList& items = region_menu->items();
1708 region_menu->set_name ("ArdourContextMenu");
1710 boost::shared_ptr<AudioRegion> ar;
1711 boost::shared_ptr<MidiRegion> mr;
1713 if (region) {
1714 ar = boost::dynamic_pointer_cast<AudioRegion> (region);
1715 mr = boost::dynamic_pointer_cast<MidiRegion> (region);
1717 /* when this particular menu pops up, make the relevant region
1718 become selected.
1721 region_menu->signal_map_event().connect (
1722 sigc::bind (sigc::mem_fun(*this, &Editor::set_selected_regionview_from_map_event), sv, boost::weak_ptr<Region>(region)));
1724 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &Editor::rename_region)));
1725 if (mr) {
1726 items.push_back (MenuElem (_("List Editor..."), sigc::mem_fun(*this, &Editor::show_midi_list_editor)));
1728 items.push_back (MenuElem (_("Region Properties..."), sigc::mem_fun(*this, &Editor::edit_region)));
1731 items.push_back (MenuElem (_("Raise to Top Layer"), sigc::mem_fun(*this, &Editor::raise_region_to_top)));
1732 items.push_back (MenuElem (_("Lower to Bottom Layer"), sigc::mem_fun (*this, &Editor::lower_region_to_bottom)));
1733 items.push_back (SeparatorElem());
1734 items.push_back (MenuElem (_("Define Sync Point"), sigc::mem_fun(*this, &Editor::set_region_sync_from_edit_point)));
1735 if (_edit_point == EditAtMouse) {
1736 items.back ().set_sensitive (false);
1738 items.push_back (MenuElem (_("Remove Sync Point"), sigc::mem_fun(*this, &Editor::remove_region_sync)));
1739 items.push_back (SeparatorElem());
1741 items.push_back (MenuElem (_("Audition"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1742 items.push_back (MenuElem (_("Export..."), sigc::mem_fun(*this, &Editor::export_region)));
1743 items.push_back (MenuElem (_("Bounce"), sigc::mem_fun(*this, &Editor::bounce_region_selection)));
1745 if (ar) {
1746 items.push_back (MenuElem (_("Spectral Analysis..."), sigc::mem_fun(*this, &Editor::analyze_region_selection)));
1749 items.push_back (SeparatorElem());
1751 sigc::connection fooc;
1752 boost::shared_ptr<Region> region_to_check;
1754 if (region) {
1755 region_to_check = region;
1756 } else {
1757 region_to_check = selection->regions.front()->region();
1760 items.push_back (CheckMenuElem (_("Lock")));
1761 CheckMenuItem* region_lock_item = static_cast<CheckMenuItem*>(&items.back());
1762 if (region_to_check->locked()) {
1763 region_lock_item->set_active();
1765 region_lock_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_region_lock));
1767 items.push_back (CheckMenuElem (_("Glue to Bars and Beats")));
1768 CheckMenuItem* bbt_glue_item = static_cast<CheckMenuItem*>(&items.back());
1770 switch (region_to_check->position_lock_style()) {
1771 case MusicTime:
1772 bbt_glue_item->set_active (true);
1773 break;
1774 default:
1775 bbt_glue_item->set_active (false);
1776 break;
1779 bbt_glue_item->signal_activate().connect (sigc::mem_fun (*this, &Editor::toggle_region_lock_style));
1781 items.push_back (CheckMenuElem (_("Mute")));
1782 CheckMenuItem* region_mute_item = static_cast<CheckMenuItem*>(&items.back());
1783 fooc = region_mute_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_region_mute));
1784 if (region_to_check->muted()) {
1785 fooc.block (true);
1786 region_mute_item->set_active();
1787 fooc.block (false);
1790 items.push_back (MenuElem (_("Transpose..."), mem_fun(*this, &Editor::pitch_shift_regions)));
1792 if (!Profile->get_sae()) {
1793 items.push_back (CheckMenuElem (_("Opaque")));
1794 CheckMenuItem* region_opaque_item = static_cast<CheckMenuItem*>(&items.back());
1795 fooc = region_opaque_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_region_opaque));
1796 if (region_to_check->opaque()) {
1797 fooc.block (true);
1798 region_opaque_item->set_active();
1799 fooc.block (false);
1803 items.push_back (CheckMenuElem (_("Original Position"), sigc::mem_fun(*this, &Editor::naturalize)));
1804 if (region_to_check->at_natural_position()) {
1805 items.back().set_sensitive (false);
1808 items.push_back (SeparatorElem());
1810 if (ar) {
1812 RegionView* rv = sv->find_view (ar);
1813 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1815 if (!Profile->get_sae()) {
1816 items.push_back (MenuElem (_("Reset Envelope"), sigc::mem_fun(*this, &Editor::reset_region_gain_envelopes)));
1818 items.push_back (CheckMenuElem (_("Envelope Visible")));
1819 CheckMenuItem* region_envelope_visible_item = static_cast<CheckMenuItem*> (&items.back());
1820 fooc = region_envelope_visible_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_gain_envelope_visibility));
1821 if (arv->envelope_visible()) {
1822 fooc.block (true);
1823 region_envelope_visible_item->set_active (true);
1824 fooc.block (false);
1827 items.push_back (CheckMenuElem (_("Envelope Active")));
1828 CheckMenuItem* region_envelope_active_item = static_cast<CheckMenuItem*> (&items.back());
1829 fooc = region_envelope_active_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_gain_envelope_active));
1831 if (ar->envelope_active()) {
1832 fooc.block (true);
1833 region_envelope_active_item->set_active (true);
1834 fooc.block (false);
1837 items.push_back (SeparatorElem());
1840 items.push_back (MenuElem (_("Normalize..."), sigc::mem_fun(*this, &Editor::normalize_region)));
1841 if (ar->scale_amplitude() != 1) {
1842 items.push_back (MenuElem (_("Reset Gain"), sigc::mem_fun(*this, &Editor::reset_region_scale_amplitude)));
1845 } else if (mr) {
1846 items.push_back (MenuElem (_("Quantize"), sigc::mem_fun(*this, &Editor::quantize_region)));
1847 items.push_back (MenuElem (_("Fork"), sigc::mem_fun(*this, &Editor::fork_region)));
1848 items.push_back (SeparatorElem());
1851 items.push_back (MenuElem (_("Strip Silence..."), sigc::mem_fun (*this, &Editor::strip_region_silence)));
1852 items.push_back (MenuElem (_("Reverse"), sigc::mem_fun(*this, &Editor::reverse_region)));
1853 items.push_back (SeparatorElem());
1855 /* range related stuff */
1857 items.push_back (MenuElem (_("Add Single Range"), sigc::mem_fun (*this, &Editor::add_location_from_audio_region)));
1858 items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_locations_from_audio_region)));
1859 if (selection->regions.size() < 2) {
1860 items.back().set_sensitive (false);
1863 items.push_back (MenuElem (_("Set Range Selection"), sigc::mem_fun (*this, &Editor::set_selection_from_region)));
1864 items.push_back (SeparatorElem());
1866 /* Nudge region */
1868 Menu *nudge_menu = manage (new Menu());
1869 MenuList& nudge_items = nudge_menu->items();
1870 nudge_menu->set_name ("ArdourContextMenu");
1872 nudge_items.push_back (MenuElem (_("Nudge Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_forward), false, false))));
1873 nudge_items.push_back (MenuElem (_("Nudge Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_backward), false, false))));
1874 nudge_items.push_back (MenuElem (_("Nudge Forward by Capture Offset"), (sigc::mem_fun(*this, &Editor::nudge_forward_capture_offset))));
1875 nudge_items.push_back (MenuElem (_("Nudge Backward by Capture Offset"), (sigc::mem_fun(*this, &Editor::nudge_backward_capture_offset))));
1877 items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1878 items.push_back (SeparatorElem());
1880 Menu *trim_menu = manage (new Menu);
1881 MenuList& trim_items = trim_menu->items();
1882 trim_menu->set_name ("ArdourContextMenu");
1884 trim_items.push_back (MenuElem (_("Start to Edit Point"), sigc::mem_fun(*this, &Editor::trim_region_from_edit_point)));
1885 foo_item = &trim_items.back();
1886 if (_edit_point == EditAtMouse) {
1887 foo_item->set_sensitive (false);
1889 trim_items.push_back (MenuElem (_("Edit Point to End"), sigc::mem_fun(*this, &Editor::trim_region_to_edit_point)));
1890 foo_item = &trim_items.back();
1891 if (_edit_point == EditAtMouse) {
1892 foo_item->set_sensitive (false);
1894 trim_items.push_back (MenuElem (_("Trim to Loop"), sigc::mem_fun(*this, &Editor::trim_region_to_loop)));
1895 trim_items.push_back (MenuElem (_("Trim to Punch"), sigc::mem_fun(*this, &Editor::trim_region_to_punch)));
1897 items.push_back (MenuElem (_("Trim"), *trim_menu));
1898 items.push_back (SeparatorElem());
1900 items.push_back (MenuElem (_("Split"), (sigc::mem_fun(*this, &Editor::split))));
1901 region_edit_menu_split_item = &items.back();
1903 if (_edit_point == EditAtMouse) {
1904 region_edit_menu_split_item->set_sensitive (false);
1907 items.push_back (MenuElem (_("Make Mono Regions"), (sigc::mem_fun(*this, &Editor::split_multichannel_region))));
1908 region_edit_menu_split_multichannel_item = &items.back();
1910 items.push_back (MenuElem (_("Duplicate"), (sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false))));
1911 items.push_back (MenuElem (_("Multi-Duplicate..."), (sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), true))));
1912 items.push_back (MenuElem (_("Fill Track"), (sigc::mem_fun(*this, &Editor::region_fill_track))));
1913 items.push_back (SeparatorElem());
1914 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(*this, &Editor::remove_selected_regions)));
1916 /* OK, stick the region submenu at the top of the list, and then add
1917 the standard items.
1920 /* we have to hack up the region name because "_" has a special
1921 meaning for menu titles.
1924 string::size_type pos = 0;
1925 string menu_item_name = (region) ? region->name() : _("Selected Regions");
1927 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1928 menu_item_name.replace (pos, 1, "__");
1929 pos += 2;
1932 edit_items.push_back (MenuElem (menu_item_name, *region_menu));
1933 edit_items.push_back (SeparatorElem());
1936 /** Add context menu items relevant to selection ranges.
1937 * @param edit_items List to add the items to.
1939 void
1940 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1942 using namespace Menu_Helpers;
1944 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1945 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1947 edit_items.push_back (SeparatorElem());
1948 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1950 if (!selection->regions.empty()) {
1951 edit_items.push_back (SeparatorElem());
1952 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)));
1953 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)));
1956 edit_items.push_back (SeparatorElem());
1957 edit_items.push_back (MenuElem (_("Convert to region in-place"), mem_fun(*this, &Editor::separate_region_from_selection)));
1958 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1960 edit_items.push_back (SeparatorElem());
1961 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1963 edit_items.push_back (SeparatorElem());
1964 edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1965 edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1967 edit_items.push_back (SeparatorElem());
1968 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1970 edit_items.push_back (SeparatorElem());
1971 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1972 edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1973 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1975 edit_items.push_back (SeparatorElem());
1976 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1977 edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1978 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1979 edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1980 edit_items.push_back (MenuElem (_("Export Range"), sigc::mem_fun(*this, &Editor::export_selection)));
1984 void
1985 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1987 using namespace Menu_Helpers;
1989 /* Playback */
1991 Menu *play_menu = manage (new Menu);
1992 MenuList& play_items = play_menu->items();
1993 play_menu->set_name ("ArdourContextMenu");
1995 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1996 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1997 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1998 play_items.push_back (SeparatorElem());
1999 play_items.push_back (MenuElem (_("Loop Region"), sigc::mem_fun(*this, &Editor::loop_selected_region)));
2001 edit_items.push_back (MenuElem (_("Play"), *play_menu));
2003 /* Selection */
2005 Menu *select_menu = manage (new Menu);
2006 MenuList& select_items = select_menu->items();
2007 select_menu->set_name ("ArdourContextMenu");
2009 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2010 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
2011 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
2012 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
2013 select_items.push_back (SeparatorElem());
2014 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
2015 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
2016 select_items.push_back (SeparatorElem());
2017 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
2018 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
2019 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2020 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2021 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
2022 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
2023 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
2025 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2027 /* Cut-n-Paste */
2029 Menu *cutnpaste_menu = manage (new Menu);
2030 MenuList& cutnpaste_items = cutnpaste_menu->items();
2031 cutnpaste_menu->set_name ("ArdourContextMenu");
2033 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2034 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2035 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
2037 cutnpaste_items.push_back (SeparatorElem());
2039 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun(*this, &Editor::align), ARDOUR::SyncPoint)));
2040 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun(*this, &Editor::align_relative), ARDOUR::SyncPoint)));
2042 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
2044 /* Adding new material */
2046 edit_items.push_back (SeparatorElem());
2047 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
2048 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
2050 /* Nudge track */
2052 Menu *nudge_menu = manage (new Menu());
2053 MenuList& nudge_items = nudge_menu->items();
2054 nudge_menu->set_name ("ArdourContextMenu");
2056 edit_items.push_back (SeparatorElem());
2057 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2058 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2059 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2060 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2062 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2065 void
2066 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
2068 using namespace Menu_Helpers;
2070 /* Playback */
2072 Menu *play_menu = manage (new Menu);
2073 MenuList& play_items = play_menu->items();
2074 play_menu->set_name ("ArdourContextMenu");
2076 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
2077 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
2078 edit_items.push_back (MenuElem (_("Play"), *play_menu));
2080 /* Selection */
2082 Menu *select_menu = manage (new Menu);
2083 MenuList& select_items = select_menu->items();
2084 select_menu->set_name ("ArdourContextMenu");
2086 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2087 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
2088 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
2089 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
2090 select_items.push_back (SeparatorElem());
2091 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
2092 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
2093 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2094 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2096 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2098 /* Cut-n-Paste */
2100 Menu *cutnpaste_menu = manage (new Menu);
2101 MenuList& cutnpaste_items = cutnpaste_menu->items();
2102 cutnpaste_menu->set_name ("ArdourContextMenu");
2104 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2105 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2106 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
2108 Menu *nudge_menu = manage (new Menu());
2109 MenuList& nudge_items = nudge_menu->items();
2110 nudge_menu->set_name ("ArdourContextMenu");
2112 edit_items.push_back (SeparatorElem());
2113 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2114 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2115 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2116 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2118 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2121 SnapType
2122 Editor::snap_type() const
2124 return _snap_type;
2127 SnapMode
2128 Editor::snap_mode() const
2130 return _snap_mode;
2133 void
2134 Editor::set_snap_to (SnapType st)
2136 unsigned int snap_ind = (unsigned int)st;
2138 _snap_type = st;
2140 if (snap_ind > snap_type_strings.size() - 1) {
2141 snap_ind = 0;
2142 _snap_type = (SnapType)snap_ind;
2145 string str = snap_type_strings[snap_ind];
2147 if (str != snap_type_selector.get_active_text()) {
2148 snap_type_selector.set_active_text (str);
2151 instant_save ();
2153 switch (_snap_type) {
2154 case SnapToBeatDiv32:
2155 case SnapToBeatDiv28:
2156 case SnapToBeatDiv24:
2157 case SnapToBeatDiv16:
2158 case SnapToBeatDiv14:
2159 case SnapToBeatDiv12:
2160 case SnapToBeatDiv10:
2161 case SnapToBeatDiv8:
2162 case SnapToBeatDiv7:
2163 case SnapToBeatDiv6:
2164 case SnapToBeatDiv5:
2165 case SnapToBeatDiv4:
2166 case SnapToBeatDiv3:
2167 case SnapToBeatDiv2:
2168 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
2169 update_tempo_based_rulers ();
2170 break;
2172 case SnapToRegionStart:
2173 case SnapToRegionEnd:
2174 case SnapToRegionSync:
2175 case SnapToRegionBoundary:
2176 build_region_boundary_cache ();
2177 break;
2179 default:
2180 /* relax */
2181 break;
2184 SnapChanged (); /* EMIT SIGNAL */
2187 void
2188 Editor::set_snap_mode (SnapMode mode)
2190 _snap_mode = mode;
2191 string str = snap_mode_strings[(int)mode];
2193 if (str != snap_mode_selector.get_active_text ()) {
2194 snap_mode_selector.set_active_text (str);
2197 instant_save ();
2199 void
2200 Editor::set_edit_point_preference (EditPoint ep, bool force)
2202 bool changed = (_edit_point != ep);
2204 _edit_point = ep;
2205 string str = edit_point_strings[(int)ep];
2207 if (str != edit_point_selector.get_active_text ()) {
2208 edit_point_selector.set_active_text (str);
2211 set_canvas_cursor ();
2213 if (!force && !changed) {
2214 return;
2217 const char* action=NULL;
2219 switch (_edit_point) {
2220 case EditAtPlayhead:
2221 action = "edit-at-playhead";
2222 break;
2223 case EditAtSelectedMarker:
2224 action = "edit-at-marker";
2225 break;
2226 case EditAtMouse:
2227 action = "edit-at-mouse";
2228 break;
2231 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2232 if (act) {
2233 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2236 nframes64_t foo;
2237 bool in_track_canvas;
2239 if (!mouse_frame (foo, in_track_canvas)) {
2240 in_track_canvas = false;
2243 reset_canvas_action_sensitivity (in_track_canvas);
2245 instant_save ();
2249 Editor::set_state (const XMLNode& node, int /*version*/)
2251 const XMLProperty* prop;
2252 XMLNode* geometry;
2253 int x, y, xoff, yoff;
2254 Gdk::Geometry g;
2256 if ((prop = node.property ("id")) != 0) {
2257 _id = prop->value ();
2260 g.base_width = default_width;
2261 g.base_height = default_height;
2262 x = 1;
2263 y = 1;
2264 xoff = 0;
2265 yoff = 21;
2267 if ((geometry = find_named_node (node, "geometry")) != 0) {
2269 XMLProperty* prop;
2271 if ((prop = geometry->property("x_size")) == 0) {
2272 prop = geometry->property ("x-size");
2274 if (prop) {
2275 g.base_width = atoi(prop->value());
2277 if ((prop = geometry->property("y_size")) == 0) {
2278 prop = geometry->property ("y-size");
2280 if (prop) {
2281 g.base_height = atoi(prop->value());
2284 if ((prop = geometry->property ("x_pos")) == 0) {
2285 prop = geometry->property ("x-pos");
2287 if (prop) {
2288 x = atoi (prop->value());
2291 if ((prop = geometry->property ("y_pos")) == 0) {
2292 prop = geometry->property ("y-pos");
2294 if (prop) {
2295 y = atoi (prop->value());
2298 if ((prop = geometry->property ("x_off")) == 0) {
2299 prop = geometry->property ("x-off");
2301 if (prop) {
2302 xoff = atoi (prop->value());
2304 if ((prop = geometry->property ("y_off")) == 0) {
2305 prop = geometry->property ("y-off");
2307 if (prop) {
2308 yoff = atoi (prop->value());
2312 set_default_size (g.base_width, g.base_height);
2313 move (x, y);
2315 if (_session && (prop = node.property ("playhead"))) {
2316 nframes64_t pos;
2317 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2318 playhead_cursor->set_position (pos);
2319 } else {
2320 playhead_cursor->set_position (0);
2323 if ((prop = node.property ("mixer-width"))) {
2324 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2327 if ((prop = node.property ("zoom-focus"))) {
2328 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2331 if ((prop = node.property ("zoom"))) {
2332 reset_zoom (PBD::atof (prop->value()));
2335 if ((prop = node.property ("snap-to"))) {
2336 set_snap_to ((SnapType) atoi (prop->value()));
2339 if ((prop = node.property ("snap-mode"))) {
2340 set_snap_mode ((SnapMode) atoi (prop->value()));
2343 if ((prop = node.property ("mouse-mode"))) {
2344 MouseMode m = str2mousemode(prop->value());
2345 set_mouse_mode (m, true);
2346 } else {
2347 set_mouse_mode (MouseObject, true);
2350 if ((prop = node.property ("left-frame")) != 0){
2351 nframes64_t pos;
2352 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2353 reset_x_origin (pos);
2357 if ((prop = node.property ("y-origin")) != 0) {
2358 reset_y_origin (atof (prop->value ()));
2361 if ((prop = node.property ("internal-edit"))) {
2362 bool yn = string_is_affirmative (prop->value());
2363 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2364 if (act) {
2365 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2366 tact->set_active (!yn);
2367 tact->set_active (yn);
2371 if ((prop = node.property ("join-object-range"))) {
2372 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2375 if ((prop = node.property ("edit-point"))) {
2376 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2379 if ((prop = node.property ("show-measures"))) {
2380 bool yn = string_is_affirmative (prop->value());
2381 _show_measures = !yn;
2382 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2383 if (act) {
2384 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2385 /* do it twice to force the change */
2386 tact->set_active (!yn);
2387 tact->set_active (yn);
2391 if ((prop = node.property ("follow-playhead"))) {
2392 bool yn = string_is_affirmative (prop->value());
2393 set_follow_playhead (yn);
2394 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2395 if (act) {
2396 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2397 if (tact->get_active() != yn) {
2398 tact->set_active (yn);
2403 if ((prop = node.property ("stationary-playhead"))) {
2404 bool yn = (prop->value() == "yes");
2405 set_stationary_playhead (yn);
2406 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2407 if (act) {
2408 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2409 if (tact->get_active() != yn) {
2410 tact->set_active (yn);
2415 if ((prop = node.property ("region-list-sort-type"))) {
2416 RegionListSortType st;
2417 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2420 if ((prop = node.property ("xfades-visible"))) {
2421 bool yn = string_is_affirmative (prop->value());
2422 _xfade_visibility = !yn;
2423 // set_xfade_visibility (yn);
2426 if ((prop = node.property ("show-editor-mixer"))) {
2428 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2429 if (act) {
2431 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2432 bool yn = string_is_affirmative (prop->value());
2434 /* do it twice to force the change */
2436 tact->set_active (!yn);
2437 tact->set_active (yn);
2441 if ((prop = node.property ("show-editor-list"))) {
2443 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2444 assert(act);
2445 if (act) {
2447 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2448 bool yn = string_is_affirmative (prop->value());
2450 /* do it twice to force the change */
2452 tact->set_active (!yn);
2453 tact->set_active (yn);
2457 if ((prop = node.property (X_("editor-list-page")))) {
2458 the_notebook.set_current_page (atoi (prop->value ()));
2461 return 0;
2464 XMLNode&
2465 Editor::get_state ()
2467 XMLNode* node = new XMLNode ("Editor");
2468 char buf[32];
2470 _id.print (buf, sizeof (buf));
2471 node->add_property ("id", buf);
2473 if (is_realized()) {
2474 Glib::RefPtr<Gdk::Window> win = get_window();
2476 int x, y, xoff, yoff, width, height;
2477 win->get_root_origin(x, y);
2478 win->get_position(xoff, yoff);
2479 win->get_size(width, height);
2481 XMLNode* geometry = new XMLNode ("geometry");
2483 snprintf(buf, sizeof(buf), "%d", width);
2484 geometry->add_property("x-size", string(buf));
2485 snprintf(buf, sizeof(buf), "%d", height);
2486 geometry->add_property("y-size", string(buf));
2487 snprintf(buf, sizeof(buf), "%d", x);
2488 geometry->add_property("x-pos", string(buf));
2489 snprintf(buf, sizeof(buf), "%d", y);
2490 geometry->add_property("y-pos", string(buf));
2491 snprintf(buf, sizeof(buf), "%d", xoff);
2492 geometry->add_property("x-off", string(buf));
2493 snprintf(buf, sizeof(buf), "%d", yoff);
2494 geometry->add_property("y-off", string(buf));
2495 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2496 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2497 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2498 geometry->add_property("edit-vertical-pane-pos", string(buf));
2500 node->add_child_nocopy (*geometry);
2503 maybe_add_mixer_strip_width (*node);
2505 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2506 node->add_property ("zoom-focus", buf);
2507 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2508 node->add_property ("zoom", buf);
2509 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2510 node->add_property ("snap-to", buf);
2511 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2512 node->add_property ("snap-mode", buf);
2514 node->add_property ("edit-point", enum_2_string (_edit_point));
2516 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2517 node->add_property ("playhead", buf);
2518 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2519 node->add_property ("left-frame", buf);
2520 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2521 node->add_property ("y-origin", buf);
2523 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2524 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2525 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2526 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2527 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2528 node->add_property ("mouse-mode", enum2str(mouse_mode));
2529 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2530 node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2532 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2533 if (act) {
2534 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2535 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2538 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2539 if (act) {
2540 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2541 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2544 snprintf (buf, sizeof (buf), "%d", the_notebook.get_current_page ());
2545 node->add_property (X_("editor-list-page"), buf);
2547 return *node;
2552 /** @param y y offset from the top of all trackviews.
2553 * @return pair: TimeAxisView that y is over, layer index.
2554 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2555 * in stacked region display mode, otherwise 0.
2557 std::pair<TimeAxisView *, layer_t>
2558 Editor::trackview_by_y_position (double y)
2560 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2562 std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
2563 if (r.first) {
2564 return r;
2568 return std::make_pair ( (TimeAxisView *) 0, 0);
2571 /** Snap a position to the grid, if appropriate, taking into account current
2572 * grid settings and also the state of any snap modifier keys that may be pressed.
2573 * @param start Position to snap.
2574 * @param event Event to get current key modifier information from, or 0.
2576 void
2577 Editor::snap_to_with_modifier (nframes64_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2579 if (!_session || !event) {
2580 return;
2583 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2584 if (_snap_mode == SnapOff) {
2585 snap_to_internal (start, direction, for_mark);
2587 } else {
2588 if (_snap_mode != SnapOff) {
2589 snap_to_internal (start, direction, for_mark);
2594 void
2595 Editor::snap_to (nframes64_t& start, int32_t direction, bool for_mark)
2597 if (!_session || _snap_mode == SnapOff) {
2598 return;
2601 snap_to_internal (start, direction, for_mark);
2604 void
2605 Editor::timecode_snap_to_internal (nframes64_t& start, int32_t direction, bool /*for_mark*/)
2607 const nframes64_t one_timecode_second = (nframes64_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2608 nframes64_t one_timecode_minute = (nframes64_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2610 switch (_snap_type) {
2611 case SnapToTimecodeFrame:
2612 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2613 start = (nframes64_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2614 } else {
2615 start = (nframes64_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2617 break;
2619 case SnapToTimecodeSeconds:
2620 if (_session->timecode_offset_negative())
2622 start += _session->timecode_offset ();
2623 } else {
2624 start -= _session->timecode_offset ();
2626 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2627 start = (nframes64_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2628 } else {
2629 start = (nframes64_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2632 if (_session->timecode_offset_negative())
2634 start -= _session->timecode_offset ();
2635 } else {
2636 start += _session->timecode_offset ();
2638 break;
2640 case SnapToTimecodeMinutes:
2641 if (_session->timecode_offset_negative())
2643 start += _session->timecode_offset ();
2644 } else {
2645 start -= _session->timecode_offset ();
2647 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2648 start = (nframes64_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2649 } else {
2650 start = (nframes64_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2652 if (_session->timecode_offset_negative())
2654 start -= _session->timecode_offset ();
2655 } else {
2656 start += _session->timecode_offset ();
2658 break;
2659 default:
2660 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2661 /*NOTREACHED*/
2665 void
2666 Editor::snap_to_internal (nframes64_t& start, int32_t direction, bool for_mark)
2668 const nframes64_t one_second = _session->frame_rate();
2669 const nframes64_t one_minute = _session->frame_rate() * 60;
2670 nframes64_t presnap = start;
2671 nframes64_t before;
2672 nframes64_t after;
2674 switch (_snap_type) {
2675 case SnapToTimecodeFrame:
2676 case SnapToTimecodeSeconds:
2677 case SnapToTimecodeMinutes:
2678 return timecode_snap_to_internal (start, direction, for_mark);
2680 case SnapToCDFrame:
2681 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2682 start = (nframes64_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2683 } else {
2684 start = (nframes64_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2686 break;
2688 case SnapToSeconds:
2689 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2690 start = (nframes64_t) ceil ((double) start / one_second) * one_second;
2691 } else {
2692 start = (nframes64_t) floor ((double) start / one_second) * one_second;
2694 break;
2696 case SnapToMinutes:
2697 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2698 start = (nframes64_t) ceil ((double) start / one_minute) * one_minute;
2699 } else {
2700 start = (nframes64_t) floor ((double) start / one_minute) * one_minute;
2702 break;
2704 case SnapToBar:
2705 start = _session->tempo_map().round_to_bar (start, direction);
2706 break;
2708 case SnapToBeat:
2709 start = _session->tempo_map().round_to_beat (start, direction);
2710 break;
2712 case SnapToBeatDiv32:
2713 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2714 break;
2715 case SnapToBeatDiv28:
2716 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2717 break;
2718 case SnapToBeatDiv24:
2719 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2720 break;
2721 case SnapToBeatDiv16:
2722 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2723 break;
2724 case SnapToBeatDiv14:
2725 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2726 break;
2727 case SnapToBeatDiv12:
2728 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2729 break;
2730 case SnapToBeatDiv10:
2731 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2732 break;
2733 case SnapToBeatDiv8:
2734 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2735 break;
2736 case SnapToBeatDiv7:
2737 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2738 break;
2739 case SnapToBeatDiv6:
2740 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2741 break;
2742 case SnapToBeatDiv5:
2743 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2744 break;
2745 case SnapToBeatDiv4:
2746 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2747 break;
2748 case SnapToBeatDiv3:
2749 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2750 break;
2751 case SnapToBeatDiv2:
2752 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2753 break;
2755 case SnapToMark:
2756 if (for_mark) {
2757 return;
2760 _session->locations()->marks_either_side (start, before, after);
2762 if (before == max_frames) {
2763 start = after;
2764 } else if (after == max_frames) {
2765 start = before;
2766 } else if (before != max_frames && after != max_frames) {
2767 /* have before and after */
2768 if ((start - before) < (after - start)) {
2769 start = before;
2770 } else {
2771 start = after;
2775 break;
2777 case SnapToRegionStart:
2778 case SnapToRegionEnd:
2779 case SnapToRegionSync:
2780 case SnapToRegionBoundary:
2781 if (!region_boundary_cache.empty()) {
2783 vector<nframes64_t>::iterator prev = region_boundary_cache.end ();
2784 vector<nframes64_t>::iterator next = region_boundary_cache.end ();
2786 if (direction > 0) {
2787 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2788 } else {
2789 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2792 if (next != region_boundary_cache.begin ()) {
2793 prev = next;
2794 prev--;
2797 nframes64_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2798 nframes64_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2800 if (start > (p + n) / 2) {
2801 start = n;
2802 } else {
2803 start = p;
2806 break;
2809 switch (_snap_mode) {
2810 case SnapNormal:
2811 return;
2813 case SnapMagnetic:
2815 if (presnap > start) {
2816 if (presnap > (start + unit_to_frame(snap_threshold))) {
2817 start = presnap;
2820 } else if (presnap < start) {
2821 if (presnap < (start - unit_to_frame(snap_threshold))) {
2822 start = presnap;
2826 default:
2827 /* handled at entry */
2828 return;
2834 void
2835 Editor::setup_toolbar ()
2837 string pixmap_path;
2839 /* Mode Buttons (tool selection) */
2841 mouse_move_button.set_relief(Gtk::RELIEF_NONE);
2842 mouse_select_button.set_relief(Gtk::RELIEF_NONE);
2843 mouse_gain_button.set_relief(Gtk::RELIEF_NONE);
2844 mouse_zoom_button.set_relief(Gtk::RELIEF_NONE);
2845 mouse_timefx_button.set_relief(Gtk::RELIEF_NONE);
2846 mouse_audition_button.set_relief(Gtk::RELIEF_NONE);
2847 // internal_edit_button.set_relief(Gtk::RELIEF_NONE);
2848 join_object_range_button.set_relief(Gtk::RELIEF_NONE);
2850 HBox* mode_box = manage(new HBox);
2851 mode_box->set_border_width (2);
2852 mode_box->set_spacing(4);
2854 /* table containing mode buttons */
2856 HBox* mouse_mode_button_box = manage (new HBox ());
2858 if (Profile->get_sae()) {
2859 mouse_mode_button_box->pack_start (mouse_move_button);
2860 } else {
2861 mouse_mode_button_box->pack_start (mouse_move_button);
2862 mouse_mode_button_box->pack_start (join_object_range_button);
2863 mouse_mode_button_box->pack_start (mouse_select_button);
2866 mouse_mode_button_box->pack_start (mouse_zoom_button);
2868 if (!Profile->get_sae()) {
2869 mouse_mode_button_box->pack_start (mouse_gain_button);
2872 mouse_mode_button_box->pack_start (mouse_timefx_button);
2873 mouse_mode_button_box->pack_start (mouse_audition_button);
2874 mouse_mode_button_box->pack_start (internal_edit_button);
2876 vector<string> edit_mode_strings;
2877 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2878 if (!Profile->get_sae()) {
2879 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2881 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2883 edit_mode_selector.set_name ("EditModeSelector");
2884 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2885 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2887 mode_box->pack_start (edit_mode_selector);
2888 mode_box->pack_start (*mouse_mode_button_box);
2890 _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2891 _mouse_mode_tearoff->set_name ("MouseModeBase");
2892 _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2894 if (Profile->get_sae()) {
2895 _mouse_mode_tearoff->set_can_be_torn_off (false);
2898 _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2899 &_mouse_mode_tearoff->tearoff_window()));
2900 _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2901 &_mouse_mode_tearoff->tearoff_window(), 1));
2902 _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2903 &_mouse_mode_tearoff->tearoff_window()));
2904 _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2905 &_mouse_mode_tearoff->tearoff_window(), 1));
2907 mouse_move_button.set_mode (false);
2908 mouse_select_button.set_mode (false);
2909 mouse_gain_button.set_mode (false);
2910 mouse_zoom_button.set_mode (false);
2911 mouse_timefx_button.set_mode (false);
2912 mouse_audition_button.set_mode (false);
2913 join_object_range_button.set_mode (false);
2915 mouse_move_button.set_name ("MouseModeButton");
2916 mouse_select_button.set_name ("MouseModeButton");
2917 mouse_gain_button.set_name ("MouseModeButton");
2918 mouse_zoom_button.set_name ("MouseModeButton");
2919 mouse_timefx_button.set_name ("MouseModeButton");
2920 mouse_audition_button.set_name ("MouseModeButton");
2921 internal_edit_button.set_name ("MouseModeButton");
2922 join_object_range_button.set_name ("MouseModeButton");
2924 mouse_move_button.unset_flags (CAN_FOCUS);
2925 mouse_select_button.unset_flags (CAN_FOCUS);
2926 mouse_gain_button.unset_flags (CAN_FOCUS);
2927 mouse_zoom_button.unset_flags (CAN_FOCUS);
2928 mouse_timefx_button.unset_flags (CAN_FOCUS);
2929 mouse_audition_button.unset_flags (CAN_FOCUS);
2930 internal_edit_button.unset_flags (CAN_FOCUS);
2931 join_object_range_button.unset_flags (CAN_FOCUS);
2933 /* Zoom */
2935 zoom_box.set_spacing (1);
2936 zoom_box.set_border_width (0);
2938 zoom_in_button.set_name ("EditorTimeButton");
2939 zoom_in_button.set_image (*(manage (new Image (Stock::ZOOM_IN, Gtk::ICON_SIZE_MENU))));
2940 zoom_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), false));
2942 zoom_out_button.set_name ("EditorTimeButton");
2943 zoom_out_button.set_image (*(manage (new Image (Stock::ZOOM_OUT, Gtk::ICON_SIZE_MENU))));
2944 zoom_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), true));
2946 zoom_out_full_button.set_name ("EditorTimeButton");
2947 zoom_out_full_button.set_image (*(manage (new Image (Stock::ZOOM_100, Gtk::ICON_SIZE_MENU))));
2948 zoom_out_full_button.signal_clicked().connect (sigc::mem_fun(*this, &Editor::temporal_zoom_session));
2950 zoom_focus_selector.set_name ("ZoomFocusSelector");
2951 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
2952 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2954 zoom_box.pack_start (zoom_out_button, false, false);
2955 zoom_box.pack_start (zoom_in_button, false, false);
2956 zoom_box.pack_start (zoom_out_full_button, false, false);
2958 /* Track zoom buttons */
2959 tav_expand_button.set_name ("TrackHeightButton");
2960 tav_expand_button.set_size_request(-1,20);
2961 tav_expand_button.add (*(manage (new Image (::get_icon("tav_exp")))));
2962 tav_expand_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), true));
2964 tav_shrink_button.set_name ("TrackHeightButton");
2965 tav_shrink_button.set_size_request(-1,20);
2966 tav_shrink_button.add (*(manage (new Image (::get_icon("tav_shrink")))));
2967 tav_shrink_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), false));
2969 track_zoom_box.set_spacing (1);
2970 track_zoom_box.set_border_width (0);
2972 track_zoom_box.pack_start (tav_shrink_button, false, false);
2973 track_zoom_box.pack_start (tav_expand_button, false, false);
2975 HBox* zbc = manage (new HBox);
2976 zbc->pack_start (zoom_focus_selector, PACK_SHRINK);
2977 zoom_vbox.pack_start (*zbc, PACK_SHRINK);
2978 zoom_vbox.pack_start (zoom_box, PACK_SHRINK);
2979 zoom_vbox.pack_start (track_zoom_box, PACK_SHRINK);
2981 snap_box.set_spacing (1);
2982 snap_box.set_border_width (2);
2984 snap_type_selector.set_name ("SnapTypeSelector");
2985 set_popdown_strings (snap_type_selector, snap_type_strings, true);
2986 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2988 snap_mode_selector.set_name ("SnapModeSelector");
2989 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
2990 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2992 edit_point_selector.set_name ("EditPointSelector");
2993 set_popdown_strings (edit_point_selector, edit_point_strings, true);
2994 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2996 snap_box.pack_start (snap_mode_selector, false, false);
2997 snap_box.pack_start (snap_type_selector, false, false);
2998 snap_box.pack_start (edit_point_selector, false, false);
3000 /* Nudge */
3002 HBox *nudge_box = manage (new HBox);
3003 nudge_box->set_spacing(1);
3004 nudge_box->set_border_width (2);
3006 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
3007 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
3009 nudge_box->pack_start (nudge_backward_button, false, false);
3010 nudge_box->pack_start (nudge_forward_button, false, false);
3011 nudge_box->pack_start (nudge_clock, false, false);
3014 /* Pack everything in... */
3016 HBox* hbox = manage (new HBox);
3017 hbox->set_spacing(10);
3019 _tools_tearoff = manage (new TearOff (*hbox));
3020 _tools_tearoff->set_name ("MouseModeBase");
3021 _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
3023 if (Profile->get_sae()) {
3024 _tools_tearoff->set_can_be_torn_off (false);
3027 _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
3028 &_tools_tearoff->tearoff_window()));
3029 _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
3030 &_tools_tearoff->tearoff_window(), 0));
3031 _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
3032 &_tools_tearoff->tearoff_window()));
3033 _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
3034 &_tools_tearoff->tearoff_window(), 0));
3036 toolbar_hbox.set_spacing (10);
3037 toolbar_hbox.set_border_width (1);
3039 toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
3040 toolbar_hbox.pack_start (*_tools_tearoff, false, false);
3042 hbox->pack_start (snap_box, false, false);
3043 hbox->pack_start (*nudge_box, false, false);
3044 hbox->pack_start (panic_box, false, false);
3046 hbox->show_all ();
3048 toolbar_base.set_name ("ToolBarBase");
3049 toolbar_base.add (toolbar_hbox);
3051 toolbar_frame.set_shadow_type (SHADOW_OUT);
3052 toolbar_frame.set_name ("BaseFrame");
3053 toolbar_frame.add (toolbar_base);
3056 void
3057 Editor::setup_tooltips ()
3059 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
3060 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Gain Automation"));
3061 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
3062 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions"));
3063 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
3064 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
3065 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
3066 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: context-click for possible operations"));
3067 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
3068 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
3069 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
3070 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
3071 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
3072 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
3073 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
3074 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
3075 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
3076 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
3077 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
3078 ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
3079 ARDOUR_UI::instance()->set_tip (midi_panic_button, _("Send note off and reset controller messages on all MIDI channels"));
3082 void
3083 Editor::midi_panic ()
3085 cerr << "MIDI panic\n";
3087 if (_session) {
3088 _session->midi_panic();
3092 void
3093 Editor::setup_midi_toolbar ()
3095 RefPtr<Action> act;
3097 /* Midi sound notes */
3098 midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
3099 midi_sound_notes.set_relief(Gtk::RELIEF_NONE);
3100 midi_sound_notes.unset_flags (CAN_FOCUS);
3102 /* Panic */
3104 act = ActionManager::get_action (X_("MIDI"), X_("panic"));
3105 midi_panic_button.set_name("MidiPanicButton");
3106 act->connect_proxy (midi_panic_button);
3108 panic_box.pack_start (midi_sound_notes , true, true);
3109 panic_box.pack_start (midi_panic_button, true, true);
3113 Editor::convert_drop_to_paths (
3114 vector<ustring>& paths,
3115 const RefPtr<Gdk::DragContext>& /*context*/,
3116 gint /*x*/,
3117 gint /*y*/,
3118 const SelectionData& data,
3119 guint /*info*/,
3120 guint /*time*/)
3122 if (_session == 0) {
3123 return -1;
3126 vector<ustring> uris = data.get_uris();
3128 if (uris.empty()) {
3130 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3131 are actually URI lists. So do it by hand.
3134 if (data.get_target() != "text/plain") {
3135 return -1;
3138 /* Parse the "uri-list" format that Nautilus provides,
3139 where each pathname is delimited by \r\n.
3141 THERE MAY BE NO NULL TERMINATING CHAR!!!
3144 ustring txt = data.get_text();
3145 const char* p;
3146 const char* q;
3148 p = (const char *) malloc (txt.length() + 1);
3149 txt.copy ((char *) p, txt.length(), 0);
3150 ((char*)p)[txt.length()] = '\0';
3152 while (p)
3154 if (*p != '#')
3156 while (g_ascii_isspace (*p))
3157 p++;
3159 q = p;
3160 while (*q && (*q != '\n') && (*q != '\r')) {
3161 q++;
3164 if (q > p)
3166 q--;
3167 while (q > p && g_ascii_isspace (*q))
3168 q--;
3170 if (q > p)
3172 uris.push_back (ustring (p, q - p + 1));
3176 p = strchr (p, '\n');
3177 if (p)
3178 p++;
3181 free ((void*)p);
3183 if (uris.empty()) {
3184 return -1;
3188 for (vector<ustring>::iterator i = uris.begin(); i != uris.end(); ++i) {
3190 if ((*i).substr (0,7) == "file://") {
3192 ustring p = *i;
3193 PBD::url_decode (p);
3195 // scan forward past three slashes
3197 ustring::size_type slashcnt = 0;
3198 ustring::size_type n = 0;
3199 ustring::iterator x = p.begin();
3201 while (slashcnt < 3 && x != p.end()) {
3202 if ((*x) == '/') {
3203 slashcnt++;
3204 } else if (slashcnt == 3) {
3205 break;
3207 ++n;
3208 ++x;
3211 if (slashcnt != 3 || x == p.end()) {
3212 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3213 continue;
3216 paths.push_back (p.substr (n - 1));
3220 return 0;
3223 void
3224 Editor::new_tempo_section ()
3229 void
3230 Editor::map_transport_state ()
3232 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3234 if (_session && _session->transport_stopped()) {
3235 have_pending_keyboard_selection = false;
3238 update_loop_range_view (true);
3241 /* UNDO/REDO */
3243 Editor::State::State (PublicEditor const * e)
3245 selection = new Selection (e);
3248 Editor::State::~State ()
3250 delete selection;
3253 void
3254 Editor::begin_reversible_command (string name)
3256 if (_session) {
3257 _session->begin_reversible_command (name);
3261 void
3262 Editor::commit_reversible_command ()
3264 if (_session) {
3265 _session->commit_reversible_command ();
3269 void
3270 Editor::set_route_group_solo (Route& route, bool yn)
3272 RouteGroup *route_group;
3274 if ((route_group = route.route_group()) != 0) {
3275 route_group->apply (&Route::set_solo, yn, this);
3276 } else {
3277 route.set_solo (yn, this);
3281 void
3282 Editor::set_route_group_mute (Route& route, bool yn)
3284 RouteGroup *route_group = 0;
3286 if ((route_group = route.route_group()) != 0) {
3287 route_group->apply (&Route::set_mute, yn, this);
3288 } else {
3289 route.set_mute (yn, this);
3293 void
3294 Editor::history_changed ()
3296 string label;
3298 if (undo_action && _session) {
3299 if (_session->undo_depth() == 0) {
3300 label = _("Undo");
3301 } else {
3302 label = string_compose(_("Undo (%1)"), _session->next_undo());
3304 undo_action->property_label() = label;
3307 if (redo_action && _session) {
3308 if (_session->redo_depth() == 0) {
3309 label = _("Redo");
3310 } else {
3311 label = string_compose(_("Redo (%1)"), _session->next_redo());
3313 redo_action->property_label() = label;
3317 void
3318 Editor::duplicate_dialog (bool with_dialog)
3320 float times = 1.0f;
3322 if (mouse_mode == MouseRange) {
3323 if (selection->time.length() == 0) {
3324 return;
3328 RegionSelection rs;
3329 get_regions_for_action (rs);
3331 if (mouse_mode != MouseRange) {
3333 if (rs.empty()) {
3334 return;
3338 if (with_dialog) {
3340 ArdourDialog win (_("Duplicate"));
3341 Label label (_("Number of duplications:"));
3342 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3343 SpinButton spinner (adjustment, 0.0, 1);
3344 HBox hbox;
3346 win.get_vbox()->set_spacing (12);
3347 win.get_vbox()->pack_start (hbox);
3348 hbox.set_border_width (6);
3349 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3351 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3352 place, visually. so do this by hand.
3355 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3356 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3357 spinner.grab_focus();
3359 hbox.show ();
3360 label.show ();
3361 spinner.show ();
3363 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3364 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3365 win.set_default_response (RESPONSE_ACCEPT);
3367 win.set_position (WIN_POS_MOUSE);
3369 spinner.grab_focus ();
3371 switch (win.run ()) {
3372 case RESPONSE_ACCEPT:
3373 break;
3374 default:
3375 return;
3378 times = adjustment.get_value();
3381 if (mouse_mode == MouseRange) {
3382 duplicate_selection (times);
3383 } else {
3384 duplicate_some_regions (rs, times);
3388 void
3389 Editor::show_verbose_canvas_cursor ()
3391 verbose_canvas_cursor->raise_to_top();
3392 verbose_canvas_cursor->show();
3393 verbose_cursor_visible = true;
3396 void
3397 Editor::hide_verbose_canvas_cursor ()
3399 verbose_canvas_cursor->hide();
3400 verbose_cursor_visible = false;
3403 double
3404 Editor::clamp_verbose_cursor_x (double x)
3406 if (x < 0) {
3407 x = 0;
3408 } else {
3409 x = min (_canvas_width - 200.0, x);
3411 return x;
3414 double
3415 Editor::clamp_verbose_cursor_y (double y)
3417 if (y < canvas_timebars_vsize) {
3418 y = canvas_timebars_vsize;
3419 } else {
3420 y = min (_canvas_height - 50, y);
3422 return y;
3425 void
3426 Editor::show_verbose_canvas_cursor_with (const string & txt)
3428 verbose_canvas_cursor->property_text() = txt.c_str();
3430 int x, y;
3431 double wx, wy;
3433 track_canvas->get_pointer (x, y);
3434 track_canvas->window_to_world (x, y, wx, wy);
3436 /* don't get too close to the edge */
3437 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (wx);
3438 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (wy);
3440 show_verbose_canvas_cursor ();
3443 void
3444 Editor::set_verbose_canvas_cursor (const string & txt, double x, double y)
3446 verbose_canvas_cursor->property_text() = txt.c_str();
3447 /* don't get too close to the edge */
3448 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (x);
3449 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (y);
3452 void
3453 Editor::set_verbose_canvas_cursor_text (const string & txt)
3455 verbose_canvas_cursor->property_text() = txt.c_str();
3458 void
3459 Editor::set_edit_mode (EditMode m)
3461 Config->set_edit_mode (m);
3464 void
3465 Editor::cycle_edit_mode ()
3467 switch (Config->get_edit_mode()) {
3468 case Slide:
3469 if (Profile->get_sae()) {
3470 Config->set_edit_mode (Lock);
3471 } else {
3472 Config->set_edit_mode (Splice);
3474 break;
3475 case Splice:
3476 Config->set_edit_mode (Lock);
3477 break;
3478 case Lock:
3479 Config->set_edit_mode (Slide);
3480 break;
3484 void
3485 Editor::edit_mode_selection_done ()
3487 if (_session == 0) {
3488 return;
3491 string choice = edit_mode_selector.get_active_text();
3492 EditMode mode = Slide;
3494 if (choice == _("Splice Edit")) {
3495 mode = Splice;
3496 } else if (choice == _("Slide Edit")) {
3497 mode = Slide;
3498 } else if (choice == _("Lock Edit")) {
3499 mode = Lock;
3502 Config->set_edit_mode (mode);
3505 void
3506 Editor::snap_type_selection_done ()
3508 string choice = snap_type_selector.get_active_text();
3509 SnapType snaptype = SnapToBeat;
3511 if (choice == _("Beats/2")) {
3512 snaptype = SnapToBeatDiv2;
3513 } else if (choice == _("Beats/3")) {
3514 snaptype = SnapToBeatDiv3;
3515 } else if (choice == _("Beats/4")) {
3516 snaptype = SnapToBeatDiv4;
3517 } else if (choice == _("Beats/5")) {
3518 snaptype = SnapToBeatDiv5;
3519 } else if (choice == _("Beats/6")) {
3520 snaptype = SnapToBeatDiv6;
3521 } else if (choice == _("Beats/7")) {
3522 snaptype = SnapToBeatDiv7;
3523 } else if (choice == _("Beats/8")) {
3524 snaptype = SnapToBeatDiv8;
3525 } else if (choice == _("Beats/10")) {
3526 snaptype = SnapToBeatDiv10;
3527 } else if (choice == _("Beats/12")) {
3528 snaptype = SnapToBeatDiv12;
3529 } else if (choice == _("Beats/14")) {
3530 snaptype = SnapToBeatDiv14;
3531 } else if (choice == _("Beats/16")) {
3532 snaptype = SnapToBeatDiv16;
3533 } else if (choice == _("Beats/24")) {
3534 snaptype = SnapToBeatDiv24;
3535 } else if (choice == _("Beats/28")) {
3536 snaptype = SnapToBeatDiv28;
3537 } else if (choice == _("Beats/32")) {
3538 snaptype = SnapToBeatDiv32;
3539 } else if (choice == _("Beats")) {
3540 snaptype = SnapToBeat;
3541 } else if (choice == _("Bars")) {
3542 snaptype = SnapToBar;
3543 } else if (choice == _("Marks")) {
3544 snaptype = SnapToMark;
3545 } else if (choice == _("Region starts")) {
3546 snaptype = SnapToRegionStart;
3547 } else if (choice == _("Region ends")) {
3548 snaptype = SnapToRegionEnd;
3549 } else if (choice == _("Region bounds")) {
3550 snaptype = SnapToRegionBoundary;
3551 } else if (choice == _("Region syncs")) {
3552 snaptype = SnapToRegionSync;
3553 } else if (choice == _("CD Frames")) {
3554 snaptype = SnapToCDFrame;
3555 } else if (choice == _("Timecode Frames")) {
3556 snaptype = SnapToTimecodeFrame;
3557 } else if (choice == _("Timecode Seconds")) {
3558 snaptype = SnapToTimecodeSeconds;
3559 } else if (choice == _("Timecode Minutes")) {
3560 snaptype = SnapToTimecodeMinutes;
3561 } else if (choice == _("Seconds")) {
3562 snaptype = SnapToSeconds;
3563 } else if (choice == _("Minutes")) {
3564 snaptype = SnapToMinutes;
3567 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3568 if (ract) {
3569 ract->set_active ();
3573 void
3574 Editor::snap_mode_selection_done ()
3576 string choice = snap_mode_selector.get_active_text();
3577 SnapMode mode = SnapNormal;
3579 if (choice == _("No Grid")) {
3580 mode = SnapOff;
3581 } else if (choice == _("Grid")) {
3582 mode = SnapNormal;
3583 } else if (choice == _("Magnetic")) {
3584 mode = SnapMagnetic;
3587 RefPtr<RadioAction> ract = snap_mode_action (mode);
3589 if (ract) {
3590 ract->set_active (true);
3594 void
3595 Editor::cycle_edit_point (bool with_marker)
3597 switch (_edit_point) {
3598 case EditAtMouse:
3599 set_edit_point_preference (EditAtPlayhead);
3600 break;
3601 case EditAtPlayhead:
3602 if (with_marker) {
3603 set_edit_point_preference (EditAtSelectedMarker);
3604 } else {
3605 set_edit_point_preference (EditAtMouse);
3607 break;
3608 case EditAtSelectedMarker:
3609 set_edit_point_preference (EditAtMouse);
3610 break;
3614 void
3615 Editor::edit_point_selection_done ()
3617 string choice = edit_point_selector.get_active_text();
3618 EditPoint ep = EditAtSelectedMarker;
3620 if (choice == _("Marker")) {
3621 set_edit_point_preference (EditAtSelectedMarker);
3622 } else if (choice == _("Playhead")) {
3623 set_edit_point_preference (EditAtPlayhead);
3624 } else {
3625 set_edit_point_preference (EditAtMouse);
3628 RefPtr<RadioAction> ract = edit_point_action (ep);
3630 if (ract) {
3631 ract->set_active (true);
3635 void
3636 Editor::zoom_focus_selection_done ()
3638 string choice = zoom_focus_selector.get_active_text();
3639 ZoomFocus focus_type = ZoomFocusLeft;
3641 if (choice == _("Left")) {
3642 focus_type = ZoomFocusLeft;
3643 } else if (choice == _("Right")) {
3644 focus_type = ZoomFocusRight;
3645 } else if (choice == _("Center")) {
3646 focus_type = ZoomFocusCenter;
3647 } else if (choice == _("Playhead")) {
3648 focus_type = ZoomFocusPlayhead;
3649 } else if (choice == _("Mouse")) {
3650 focus_type = ZoomFocusMouse;
3651 } else if (choice == _("Edit point")) {
3652 focus_type = ZoomFocusEdit;
3655 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3657 if (ract) {
3658 ract->set_active ();
3662 gint
3663 Editor::edit_controls_button_release (GdkEventButton* ev)
3665 if (Keyboard::is_context_menu_event (ev)) {
3666 ARDOUR_UI::instance()->add_route (this);
3668 return TRUE;
3671 gint
3672 Editor::mouse_select_button_release (GdkEventButton* ev)
3674 /* this handles just right-clicks */
3676 if (ev->button != 3) {
3677 return false;
3680 return true;
3683 void
3684 Editor::set_zoom_focus (ZoomFocus f)
3686 string str = zoom_focus_strings[(int)f];
3688 if (str != zoom_focus_selector.get_active_text()) {
3689 zoom_focus_selector.set_active_text (str);
3692 if (zoom_focus != f) {
3693 zoom_focus = f;
3695 ZoomFocusChanged (); /* EMIT_SIGNAL */
3697 instant_save ();
3701 void
3702 Editor::ensure_float (Window& win)
3704 win.set_transient_for (*this);
3707 void
3708 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3710 /* recover or initialize pane positions. do this here rather than earlier because
3711 we don't want the positions to change the child allocations, which they seem to do.
3714 int pos;
3715 XMLProperty* prop;
3716 char buf[32];
3717 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3718 int width, height;
3720 enum Pane {
3721 Horizontal = 0x1,
3722 Vertical = 0x2
3725 static Pane done;
3727 XMLNode* geometry;
3729 width = default_width;
3730 height = default_height;
3732 if ((geometry = find_named_node (*node, "geometry")) != 0) {
3734 prop = geometry->property ("x-size");
3735 if (prop) {
3736 width = atoi (prop->value());
3738 prop = geometry->property ("y-size");
3739 if (prop) {
3740 height = atoi (prop->value());
3744 if (which == static_cast<Paned*> (&edit_pane)) {
3746 if (done & Horizontal) {
3747 return;
3750 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3751 /* initial allocation is 90% to canvas, 10% to notebook */
3752 pos = (int) floor (alloc.get_width() * 0.90f);
3753 snprintf (buf, sizeof(buf), "%d", pos);
3754 } else {
3755 pos = atoi (prop->value());
3758 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3759 edit_pane.set_position (pos);
3760 pre_maximal_horizontal_pane_position = pos;
3763 done = (Pane) (done | Horizontal);
3765 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3767 if (done & Vertical) {
3768 return;
3771 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3772 /* initial allocation is 90% to canvas, 10% to summary */
3773 pos = (int) floor (alloc.get_height() * 0.90f);
3774 snprintf (buf, sizeof(buf), "%d", pos);
3775 } else {
3776 pos = atoi (prop->value());
3779 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3780 editor_summary_pane.set_position (pos);
3781 pre_maximal_vertical_pane_position = pos;
3784 done = (Pane) (done | Vertical);
3788 void
3789 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3791 if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) {
3792 top_hbox.remove (toolbar_frame);
3796 void
3797 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3799 if (toolbar_frame.get_parent() == 0) {
3800 top_hbox.pack_end (toolbar_frame);
3804 void
3805 Editor::set_show_measures (bool yn)
3807 if (_show_measures != yn) {
3808 hide_measures ();
3810 if ((_show_measures = yn) == true) {
3811 if (tempo_lines)
3812 tempo_lines->show();
3813 draw_measures ();
3815 instant_save ();
3819 void
3820 Editor::toggle_follow_playhead ()
3822 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3823 if (act) {
3824 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3825 set_follow_playhead (tact->get_active());
3829 void
3830 Editor::set_follow_playhead (bool yn)
3832 if (_follow_playhead != yn) {
3833 if ((_follow_playhead = yn) == true) {
3834 /* catch up */
3835 reset_x_origin_to_follow_playhead ();
3837 instant_save ();
3841 void
3842 Editor::toggle_stationary_playhead ()
3844 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3845 if (act) {
3846 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3847 set_stationary_playhead (tact->get_active());
3851 void
3852 Editor::set_stationary_playhead (bool yn)
3854 if (_stationary_playhead != yn) {
3855 if ((_stationary_playhead = yn) == true) {
3856 /* catch up */
3857 // FIXME need a 3.0 equivalent of this 2.X call
3858 // update_current_screen ();
3860 instant_save ();
3864 void
3865 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3867 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3868 if (xfade) {
3869 xfade->set_active (!xfade->active());
3873 void
3874 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3876 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3877 if (xfade) {
3878 xfade->set_follow_overlap (!xfade->following_overlap());
3882 void
3883 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3885 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3887 if (!xfade) {
3888 return;
3891 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3893 ensure_float (cew);
3895 switch (cew.run ()) {
3896 case RESPONSE_ACCEPT:
3897 break;
3898 default:
3899 return;
3902 cew.apply ();
3903 PropertyChange all_crossfade_properties;
3904 all_crossfade_properties.add (ARDOUR::Properties::active);
3905 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3906 xfade->PropertyChanged (all_crossfade_properties);
3909 PlaylistSelector&
3910 Editor::playlist_selector () const
3912 return *_playlist_selector;
3915 Evoral::MusicalTime
3916 Editor::get_grid_type_as_beats (bool& success, nframes64_t position)
3918 success = true;
3920 switch (_snap_type) {
3921 case SnapToBeat:
3922 return 1.0;
3923 break;
3925 case SnapToBeatDiv32:
3926 return 1.0/32.0;
3927 break;
3928 case SnapToBeatDiv28:
3929 return 1.0/28.0;
3930 break;
3931 case SnapToBeatDiv24:
3932 return 1.0/24.0;
3933 break;
3934 case SnapToBeatDiv16:
3935 return 1.0/16.0;
3936 break;
3937 case SnapToBeatDiv14:
3938 return 1.0/14.0;
3939 break;
3940 case SnapToBeatDiv12:
3941 return 1.0/12.0;
3942 break;
3943 case SnapToBeatDiv10:
3944 return 1.0/10.0;
3945 break;
3946 case SnapToBeatDiv8:
3947 return 1.0/8.0;
3948 break;
3949 case SnapToBeatDiv7:
3950 return 1.0/7.0;
3951 break;
3952 case SnapToBeatDiv6:
3953 return 1.0/6.0;
3954 break;
3955 case SnapToBeatDiv5:
3956 return 1.0/5.0;
3957 break;
3958 case SnapToBeatDiv4:
3959 return 1.0/4.0;
3960 break;
3961 case SnapToBeatDiv3:
3962 return 1.0/3.0;
3963 break;
3964 case SnapToBeatDiv2:
3965 return 1.0/2.0;
3966 break;
3968 case SnapToBar:
3969 if (_session) {
3970 return _session->tempo_map().meter_at (position).beats_per_bar();
3972 break;
3974 case SnapToCDFrame:
3975 case SnapToTimecodeFrame:
3976 case SnapToTimecodeSeconds:
3977 case SnapToTimecodeMinutes:
3978 case SnapToSeconds:
3979 case SnapToMinutes:
3980 case SnapToRegionStart:
3981 case SnapToRegionEnd:
3982 case SnapToRegionSync:
3983 case SnapToRegionBoundary:
3984 default:
3985 success = false;
3986 break;
3989 return 0.0;
3992 nframes64_t
3993 Editor::get_nudge_distance (nframes64_t pos, nframes64_t& next)
3995 nframes64_t ret;
3997 ret = nudge_clock.current_duration (pos);
3998 next = ret + 1; /* XXXX fix me */
4000 return ret;
4004 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
4006 ArdourDialog dialog (_("Playlist Deletion"));
4007 Label label (string_compose (_("Playlist %1 is currently unused.\n"
4008 "If left alone, no audio files used by it will be cleaned.\n"
4009 "If deleted, audio files used by it alone by will cleaned."),
4010 pl->name()));
4012 dialog.set_position (WIN_POS_CENTER);
4013 dialog.get_vbox()->pack_start (label);
4015 label.show ();
4017 dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT);
4018 dialog.add_button (_("Keep playlist"), RESPONSE_REJECT);
4019 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
4021 switch (dialog.run ()) {
4022 case RESPONSE_ACCEPT:
4023 /* delete the playlist */
4024 return 0;
4025 break;
4027 case RESPONSE_REJECT:
4028 /* keep the playlist */
4029 return 1;
4030 break;
4032 default:
4033 break;
4036 return -1;
4039 bool
4040 Editor::audio_region_selection_covers (nframes64_t where)
4042 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
4043 if ((*a)->region()->covers (where)) {
4044 return true;
4048 return false;
4051 void
4052 Editor::prepare_for_cleanup ()
4054 cut_buffer->clear_regions ();
4055 cut_buffer->clear_playlists ();
4057 selection->clear_regions ();
4058 selection->clear_playlists ();
4060 _regions->suspend_redisplay ();
4063 void
4064 Editor::finish_cleanup ()
4066 _regions->resume_redisplay ();
4069 Location*
4070 Editor::transport_loop_location()
4072 if (_session) {
4073 return _session->locations()->auto_loop_location();
4074 } else {
4075 return 0;
4079 Location*
4080 Editor::transport_punch_location()
4082 if (_session) {
4083 return _session->locations()->auto_punch_location();
4084 } else {
4085 return 0;
4089 bool
4090 Editor::control_layout_scroll (GdkEventScroll* ev)
4092 if (Keyboard::some_magic_widget_has_focus()) {
4093 return false;
4096 switch (ev->direction) {
4097 case GDK_SCROLL_UP:
4098 scroll_tracks_up_line ();
4099 return true;
4100 break;
4102 case GDK_SCROLL_DOWN:
4103 scroll_tracks_down_line ();
4104 return true;
4106 default:
4107 /* no left/right handling yet */
4108 break;
4111 return false;
4114 void
4115 Editor::session_state_saved (string snap_name)
4117 ENSURE_GUI_THREAD (*this, &Editor::session_state_saved, snap_name);
4119 update_title ();
4120 _snapshots->redisplay ();
4123 void
4124 Editor::maximise_editing_space ()
4126 _mouse_mode_tearoff->set_visible (false);
4127 _tools_tearoff->set_visible (false);
4129 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
4130 pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
4131 pre_maximal_editor_width = this->get_width ();
4132 pre_maximal_editor_height = this->get_height ();
4134 if (post_maximal_horizontal_pane_position == 0) {
4135 post_maximal_horizontal_pane_position = edit_pane.get_width();
4138 if (post_maximal_vertical_pane_position == 0) {
4139 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
4142 fullscreen ();
4144 if (post_maximal_editor_width) {
4145 edit_pane.set_position (post_maximal_horizontal_pane_position -
4146 abs(post_maximal_editor_width - pre_maximal_editor_width));
4147 } else {
4148 edit_pane.set_position (post_maximal_horizontal_pane_position);
4151 if (post_maximal_editor_height) {
4152 editor_summary_pane.set_position (post_maximal_vertical_pane_position -
4153 abs(post_maximal_editor_height - pre_maximal_editor_height));
4154 } else {
4155 editor_summary_pane.set_position (post_maximal_vertical_pane_position);
4159 void
4160 Editor::restore_editing_space ()
4162 // user changed width/height of panes during fullscreen
4164 if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
4165 post_maximal_horizontal_pane_position = edit_pane.get_position();
4168 if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
4169 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
4172 unfullscreen();
4174 _mouse_mode_tearoff->set_visible (true);
4175 _tools_tearoff->set_visible (true);
4176 post_maximal_editor_width = this->get_width();
4177 post_maximal_editor_height = this->get_height();
4179 edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
4180 editor_summary_pane.set_position (pre_maximal_vertical_pane_position + abs(this->get_height() - pre_maximal_editor_height));
4184 * Make new playlists for a given track and also any others that belong
4185 * to the same active route group with the `edit' property.
4186 * @param v Track.
4189 void
4190 Editor::new_playlists (TimeAxisView* v)
4192 begin_reversible_command (_("new playlists"));
4193 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4194 _session->playlists->get (playlists);
4195 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4196 commit_reversible_command ();
4200 * Use a copy of the current playlist for a given track and also any others that belong
4201 * to the same active route group with the `edit' property.
4202 * @param v Track.
4205 void
4206 Editor::copy_playlists (TimeAxisView* v)
4208 begin_reversible_command (_("copy playlists"));
4209 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4210 _session->playlists->get (playlists);
4211 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4212 commit_reversible_command ();
4215 /** Clear the current playlist for a given track and also any others that belong
4216 * to the same active route group with the `edit' property.
4217 * @param v Track.
4220 void
4221 Editor::clear_playlists (TimeAxisView* v)
4223 begin_reversible_command (_("clear playlists"));
4224 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4225 _session->playlists->get (playlists);
4226 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4227 commit_reversible_command ();
4230 void
4231 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4233 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4236 void
4237 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4239 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4242 void
4243 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4245 atv.clear_playlist ();
4248 bool
4249 Editor::on_key_press_event (GdkEventKey* ev)
4251 return key_press_focus_accelerator_handler (*this, ev);
4254 bool
4255 Editor::on_key_release_event (GdkEventKey* ev)
4257 return Gtk::Window::on_key_release_event (ev);
4258 // return key_press_focus_accelerator_handler (*this, ev);
4261 /** Queue up a change to the viewport x origin.
4262 * @param frame New x origin.
4264 void
4265 Editor::reset_x_origin (nframes64_t frame)
4267 queue_visual_change (frame);
4270 void
4271 Editor::reset_y_origin (double y)
4273 queue_visual_change_y (y);
4276 void
4277 Editor::reset_zoom (double fpu)
4279 queue_visual_change (fpu);
4282 void
4283 Editor::reposition_and_zoom (nframes64_t frame, double fpu)
4285 reset_x_origin (frame);
4286 reset_zoom (fpu);
4288 if (!no_save_visual) {
4289 undo_visual_stack.push_back (current_visual_state(false));
4293 Editor::VisualState*
4294 Editor::current_visual_state (bool with_tracks)
4296 VisualState* vs = new VisualState;
4297 vs->y_position = vertical_adjustment.get_value();
4298 vs->frames_per_unit = frames_per_unit;
4299 vs->leftmost_frame = leftmost_frame;
4300 vs->zoom_focus = zoom_focus;
4302 if (with_tracks) {
4303 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4304 vs->track_states.push_back (TAVState ((*i), &(*i)->get_state()));
4308 return vs;
4311 void
4312 Editor::undo_visual_state ()
4314 if (undo_visual_stack.empty()) {
4315 return;
4318 redo_visual_stack.push_back (current_visual_state());
4320 VisualState* vs = undo_visual_stack.back();
4321 undo_visual_stack.pop_back();
4322 use_visual_state (*vs);
4325 void
4326 Editor::redo_visual_state ()
4328 if (redo_visual_stack.empty()) {
4329 return;
4332 undo_visual_stack.push_back (current_visual_state());
4334 VisualState* vs = redo_visual_stack.back();
4335 redo_visual_stack.pop_back();
4336 use_visual_state (*vs);
4339 void
4340 Editor::swap_visual_state ()
4342 if (undo_visual_stack.empty()) {
4343 redo_visual_state ();
4344 } else {
4345 undo_visual_state ();
4349 void
4350 Editor::use_visual_state (VisualState& vs)
4352 no_save_visual = true;
4354 _routes->suspend_redisplay ();
4356 vertical_adjustment.set_value (vs.y_position);
4358 set_zoom_focus (vs.zoom_focus);
4359 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4361 for (list<TAVState>::iterator i = vs.track_states.begin(); i != vs.track_states.end(); ++i) {
4362 TrackViewList::iterator t;
4364 /* check if the track still exists - it could have been deleted */
4366 if ((t = find (track_views.begin(), track_views.end(), i->first)) != track_views.end()) {
4367 (*t)->set_state (*(i->second), Stateful::loading_state_version);
4372 if (!vs.track_states.empty()) {
4373 _routes->update_visibility ();
4376 _routes->resume_redisplay ();
4378 no_save_visual = false;
4381 void
4382 Editor::set_frames_per_unit (double fpu)
4384 /* this is the core function that controls the zoom level of the canvas. it is called
4385 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4388 if (fpu == frames_per_unit) {
4389 return;
4392 if (fpu < 2.0) {
4393 fpu = 2.0;
4397 /* don't allow zooms that fit more than the maximum number
4398 of frames into an 800 pixel wide space.
4401 if (max_frames / fpu < 800.0) {
4402 return;
4405 if (tempo_lines)
4406 tempo_lines->tempo_map_changed();
4408 frames_per_unit = fpu;
4409 post_zoom ();
4412 void
4413 Editor::post_zoom ()
4415 // convert fpu to frame count
4417 nframes64_t frames = (nframes64_t) floor (frames_per_unit * _canvas_width);
4419 if (frames_per_unit != zoom_range_clock.current_duration()) {
4420 zoom_range_clock.set (frames);
4423 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4424 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4425 (*i)->reshow_selection (selection->time);
4429 ZoomChanged (); /* EMIT_SIGNAL */
4431 //reset_scrolling_region ();
4433 if (playhead_cursor) {
4434 playhead_cursor->set_position (playhead_cursor->current_frame);
4437 refresh_location_display();
4438 _summary->set_overlays_dirty ();
4440 instant_save ();
4443 void
4444 Editor::queue_visual_change (nframes64_t where)
4446 pending_visual_change.add (VisualChange::TimeOrigin);
4447 pending_visual_change.time_origin = where;
4448 ensure_visual_change_idle_handler ();
4451 void
4452 Editor::queue_visual_change (double fpu)
4454 pending_visual_change.add (VisualChange::ZoomLevel);
4455 pending_visual_change.frames_per_unit = fpu;
4457 ensure_visual_change_idle_handler ();
4460 void
4461 Editor::queue_visual_change_y (double y)
4463 pending_visual_change.add (VisualChange::YOrigin);
4464 pending_visual_change.y_origin = y;
4466 ensure_visual_change_idle_handler ();
4469 void
4470 Editor::ensure_visual_change_idle_handler ()
4472 if (pending_visual_change.idle_handler_id < 0) {
4473 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4478 Editor::_idle_visual_changer (void* arg)
4480 return static_cast<Editor*>(arg)->idle_visual_changer ();
4484 Editor::idle_visual_changer ()
4486 VisualChange::Type p = pending_visual_change.pending;
4487 pending_visual_change.pending = (VisualChange::Type) 0;
4489 double last_time_origin = horizontal_position ();
4491 if (p & VisualChange::ZoomLevel) {
4492 set_frames_per_unit (pending_visual_change.frames_per_unit);
4494 compute_fixed_ruler_scale ();
4495 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4496 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4497 update_tempo_based_rulers ();
4499 if (p & VisualChange::TimeOrigin) {
4500 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4502 if (p & VisualChange::YOrigin) {
4503 vertical_adjustment.set_value (pending_visual_change.y_origin);
4506 if (last_time_origin == horizontal_position ()) {
4507 /* changed signal not emitted */
4508 update_fixed_rulers ();
4509 redisplay_tempo (true);
4512 _summary->set_overlays_dirty ();
4514 pending_visual_change.idle_handler_id = -1;
4515 return 0; /* this is always a one-shot call */
4518 struct EditorOrderTimeAxisSorter {
4519 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4520 return a->order () < b->order ();
4524 void
4525 Editor::sort_track_selection (TrackViewList* sel)
4527 EditorOrderTimeAxisSorter cmp;
4529 if (sel) {
4530 sel->sort (cmp);
4531 } else {
4532 selection->tracks.sort (cmp);
4536 nframes64_t
4537 Editor::get_preferred_edit_position (bool ignore_playhead)
4539 bool ignored;
4540 nframes64_t where = 0;
4541 EditPoint ep = _edit_point;
4543 if (entered_marker) {
4544 return entered_marker->position();
4547 if (ignore_playhead && ep == EditAtPlayhead) {
4548 ep = EditAtSelectedMarker;
4551 switch (ep) {
4552 case EditAtPlayhead:
4553 where = _session->audible_frame();
4554 break;
4556 case EditAtSelectedMarker:
4557 if (!selection->markers.empty()) {
4558 bool is_start;
4559 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4560 if (loc) {
4561 if (is_start) {
4562 where = loc->start();
4563 } else {
4564 where = loc->end();
4566 break;
4569 /* fallthru */
4571 default:
4572 case EditAtMouse:
4573 if (!mouse_frame (where, ignored)) {
4574 /* XXX not right but what can we do ? */
4575 return 0;
4577 snap_to (where);
4578 break;
4581 return where;
4584 void
4585 Editor::set_loop_range (nframes64_t start, nframes64_t end, string cmd)
4587 if (!_session) return;
4589 begin_reversible_command (cmd);
4591 Location* tll;
4593 if ((tll = transport_loop_location()) == 0) {
4594 Location* loc = new Location (start, end, _("Loop"), Location::IsAutoLoop);
4595 XMLNode &before = _session->locations()->get_state();
4596 _session->locations()->add (loc, true);
4597 _session->set_auto_loop_location (loc);
4598 XMLNode &after = _session->locations()->get_state();
4599 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4600 } else {
4601 XMLNode &before = tll->get_state();
4602 tll->set_hidden (false, this);
4603 tll->set (start, end);
4604 XMLNode &after = tll->get_state();
4605 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4608 commit_reversible_command ();
4611 void
4612 Editor::set_punch_range (nframes64_t start, nframes64_t end, string cmd)
4614 if (!_session) return;
4616 begin_reversible_command (cmd);
4618 Location* tpl;
4620 if ((tpl = transport_punch_location()) == 0) {
4621 Location* loc = new Location (start, end, _("Loop"), Location::IsAutoPunch);
4622 XMLNode &before = _session->locations()->get_state();
4623 _session->locations()->add (loc, true);
4624 _session->set_auto_loop_location (loc);
4625 XMLNode &after = _session->locations()->get_state();
4626 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4628 else {
4629 XMLNode &before = tpl->get_state();
4630 tpl->set_hidden (false, this);
4631 tpl->set (start, end);
4632 XMLNode &after = tpl->get_state();
4633 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4636 commit_reversible_command ();
4639 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4640 * @param rs List to which found regions are added.
4641 * @param where Time to look at.
4642 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4644 void
4645 Editor::get_regions_at (RegionSelection& rs, nframes64_t where, const TrackViewList& ts) const
4647 const TrackViewList* tracks;
4649 if (ts.empty()) {
4650 tracks = &track_views;
4651 } else {
4652 tracks = &ts;
4655 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4656 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4657 if (rtv) {
4658 boost::shared_ptr<Track> tr;
4659 boost::shared_ptr<Playlist> pl;
4661 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4663 Playlist::RegionList* regions = pl->regions_at (
4664 (nframes64_t) floor ( (double)where * tr->speed()));
4666 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4667 RegionView* rv = rtv->view()->find_view (*i);
4668 if (rv) {
4669 rs.add (rv);
4673 delete regions;
4679 void
4680 Editor::get_regions_after (RegionSelection& rs, nframes64_t where, const TrackViewList& ts) const
4682 const TrackViewList* tracks;
4684 if (ts.empty()) {
4685 tracks = &track_views;
4686 } else {
4687 tracks = &ts;
4690 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4691 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4692 if (rtv) {
4693 boost::shared_ptr<Track> tr;
4694 boost::shared_ptr<Playlist> pl;
4696 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4698 Playlist::RegionList* regions = pl->regions_touched (
4699 (nframes64_t) floor ( (double)where * tr->speed()), max_frames);
4701 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4703 RegionView* rv = rtv->view()->find_view (*i);
4705 if (rv) {
4706 rs.push_back (rv);
4710 delete regions;
4716 /** Find all regions which are either:
4717 * - selected or
4718 * - the entered_regionview (if allow_entered == true) or
4719 * - under the preferred edit position AND on a selected track, or on a track
4720 * which is in the same active edit-enable route group as a selected region (if allow_edit_position == true)
4721 * @param rs Returned region list.
4722 * @param allow_entered true to include the entered_regionview in the list.
4724 void
4725 Editor::get_regions_for_action (RegionSelection& rs, bool allow_entered, bool allow_edit_position)
4727 /* Start with selected regions */
4728 rs = selection->regions;
4730 /* Add the entered_regionview, if requested */
4731 if (allow_entered && entered_regionview) {
4732 rs.add (entered_regionview);
4735 if (allow_edit_position) {
4737 TrackViewList tracks = selection->tracks;
4739 /* tracks is currently the set of selected tracks; add any other tracks that
4740 * have regions that are in the same edit-activated route group as one of
4741 * our regions */
4742 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
4744 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4745 if (g && g->is_active() && g->is_edit()) {
4746 tracks.add (axis_views_from_routes (g->route_list()));
4751 if (!tracks.empty()) {
4752 /* now find regions that are at the edit position on those tracks */
4753 nframes64_t const where = get_preferred_edit_position ();
4754 get_regions_at (rs, where, tracks);
4759 void
4760 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4762 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4764 RouteTimeAxisView* tatv;
4766 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4768 boost::shared_ptr<Playlist> pl;
4769 vector<boost::shared_ptr<Region> > results;
4770 RegionView* marv;
4771 boost::shared_ptr<Track> tr;
4773 if ((tr = tatv->track()) == 0) {
4774 /* bus */
4775 continue;
4778 if ((pl = (tr->playlist())) != 0) {
4779 pl->get_region_list_equivalent_regions (region, results);
4782 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4783 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4784 regions.push_back (marv);
4792 void
4793 Editor::show_rhythm_ferret ()
4795 if (rhythm_ferret == 0) {
4796 rhythm_ferret = new RhythmFerret(*this);
4799 rhythm_ferret->set_session (_session);
4800 rhythm_ferret->show ();
4801 rhythm_ferret->present ();
4804 void
4805 Editor::show_global_port_matrix (ARDOUR::DataType t)
4807 if (_global_port_matrix[t] == 0) {
4808 _global_port_matrix[t] = new GlobalPortMatrixWindow (_session, t);
4811 _global_port_matrix[t]->show ();
4814 void
4815 Editor::first_idle ()
4817 MessageDialog* dialog = 0;
4819 if (track_views.size() > 1) {
4820 dialog = new MessageDialog (*this,
4821 string_compose (_("Please wait while %1 loads visual data"), PROGRAM_NAME),
4822 true,
4823 Gtk::MESSAGE_INFO,
4824 Gtk::BUTTONS_NONE);
4825 dialog->present ();
4826 ARDOUR_UI::instance()->flush_pending ();
4829 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4830 (*t)->first_idle();
4833 // first idle adds route children (automation tracks), so we need to redisplay here
4834 _routes->redisplay ();
4836 delete dialog;
4838 _have_idled = true;
4841 gboolean
4842 Editor::_idle_resize (gpointer arg)
4844 return ((Editor*)arg)->idle_resize ();
4847 void
4848 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4850 if (resize_idle_id < 0) {
4851 resize_idle_id = g_idle_add (_idle_resize, this);
4852 _pending_resize_amount = 0;
4855 /* make a note of the smallest resulting height, so that we can clamp the
4856 lower limit at TimeAxisView::hSmall */
4858 int32_t min_resulting = INT32_MAX;
4860 _pending_resize_amount += h;
4861 _pending_resize_view = view;
4863 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4865 if (selection->tracks.contains (_pending_resize_view)) {
4866 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4867 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4871 if (min_resulting < 0) {
4872 min_resulting = 0;
4875 /* clamp */
4876 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4877 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4881 /** Handle pending resizing of tracks */
4882 bool
4883 Editor::idle_resize ()
4885 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4887 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4888 selection->tracks.contains (_pending_resize_view)) {
4890 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4891 if (*i != _pending_resize_view) {
4892 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4897 _pending_resize_amount = 0;
4898 flush_canvas ();
4899 _group_tabs->set_dirty ();
4900 resize_idle_id = -1;
4902 return false;
4905 void
4906 Editor::located ()
4908 ENSURE_GUI_THREAD (*this, &Editor::located);
4910 playhead_cursor->set_position (_session->audible_frame ());
4911 if (_follow_playhead && !_pending_initial_locate) {
4912 reset_x_origin_to_follow_playhead ();
4915 _pending_locate_request = false;
4916 _pending_initial_locate = false;
4919 void
4920 Editor::region_view_added (RegionView *)
4922 _summary->set_dirty ();
4925 TimeAxisView*
4926 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4928 TrackViewList::const_iterator j = track_views.begin ();
4929 while (j != track_views.end()) {
4930 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4931 if (rtv && rtv->route() == r) {
4932 return rtv;
4934 ++j;
4937 return 0;
4941 TrackViewList
4942 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4944 TrackViewList t;
4946 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4947 TimeAxisView* tv = axis_view_from_route (*i);
4948 if (tv) {
4949 t.push_back (tv);
4953 return t;
4957 void
4958 Editor::handle_new_route (RouteList& routes)
4960 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4962 RouteTimeAxisView *rtv;
4963 list<RouteTimeAxisView*> new_views;
4965 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4966 boost::shared_ptr<Route> route = (*x);
4968 if (route->is_hidden() || route->is_monitor()) {
4969 continue;
4972 DataType dt = route->input()->default_type();
4974 if (dt == ARDOUR::DataType::AUDIO) {
4975 rtv = new AudioTimeAxisView (*this, _session, route, *track_canvas);
4976 } else if (dt == ARDOUR::DataType::MIDI) {
4977 rtv = new MidiTimeAxisView (*this, _session, route, *track_canvas);
4978 } else {
4979 throw unknown_type();
4982 new_views.push_back (rtv);
4983 track_views.push_back (rtv);
4985 rtv->effective_gain_display ();
4987 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4990 _routes->routes_added (new_views);
4992 if (show_editor_mixer_when_tracks_arrive) {
4993 show_editor_mixer (true);
4996 editor_list_button.set_sensitive (true);
4998 _summary->set_dirty ();
5001 void
5002 Editor::timeaxisview_deleted (TimeAxisView *tv)
5004 if (_session && _session->deletion_in_progress()) {
5005 /* the situation is under control */
5006 return;
5009 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
5012 _routes->route_removed (tv);
5014 if (tv == entered_track) {
5015 entered_track = 0;
5018 /* remove it from the list of track views */
5020 TrackViewList::iterator i;
5022 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
5023 i = track_views.erase (i);
5026 /* update whatever the current mixer strip is displaying, if revelant */
5028 boost::shared_ptr<Route> route;
5029 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
5031 if (rtav) {
5032 route = rtav->route ();
5035 if (current_mixer_strip && current_mixer_strip->route() == route) {
5037 TimeAxisView* next_tv;
5039 if (track_views.empty()) {
5040 next_tv = 0;
5041 } else if (i == track_views.end()) {
5042 next_tv = track_views.front();
5043 } else {
5044 next_tv = (*i);
5048 if (next_tv) {
5049 set_selected_mixer_strip (*next_tv);
5050 } else {
5051 /* make the editor mixer strip go away setting the
5052 * button to inactive (which also unticks the menu option)
5055 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
5060 void
5061 Editor::hide_track_in_display (TimeAxisView* tv, bool /*temponly*/)
5063 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
5065 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
5066 // this will hide the mixer strip
5067 set_selected_mixer_strip (*tv);
5070 _routes->hide_track_in_display (*tv);
5073 bool
5074 Editor::sync_track_view_list_and_routes ()
5076 track_views = TrackViewList (_routes->views ());
5078 _summary->set_dirty ();
5079 _group_tabs->set_dirty ();
5081 return false; // do not call again (until needed)
5084 void
5085 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
5087 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5088 theslot (**i);
5092 RouteTimeAxisView*
5093 Editor::get_route_view_by_id (PBD::ID& id)
5095 RouteTimeAxisView* v;
5097 for(TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5098 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5099 if(v->route()->id() == id) {
5100 return v;
5105 return 0;
5108 void
5109 Editor::fit_route_group (RouteGroup *g)
5111 TrackViewList ts = axis_views_from_routes (g->route_list ());
5112 fit_tracks (ts);
5115 void
5116 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5118 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5120 if (r == 0) {
5121 _session->cancel_audition ();
5122 return;
5125 if (_session->is_auditioning()) {
5126 _session->cancel_audition ();
5127 if (r == last_audition_region) {
5128 return;
5132 _session->audition_region (r);
5133 last_audition_region = r;
5137 void
5138 Editor::hide_a_region (boost::shared_ptr<Region> r)
5140 r->set_hidden (true);
5143 void
5144 Editor::show_a_region (boost::shared_ptr<Region> r)
5146 r->set_hidden (false);
5149 void
5150 Editor::audition_region_from_region_list ()
5152 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5155 void
5156 Editor::hide_region_from_region_list ()
5158 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5161 void
5162 Editor::show_region_in_region_list ()
5164 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5167 void
5168 Editor::start_step_editing ()
5170 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5173 void
5174 Editor::stop_step_editing ()
5176 step_edit_connection.disconnect ();
5179 bool
5180 Editor::check_step_edit ()
5182 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5183 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5184 if (mtv) {
5185 mtv->check_step_edit ();
5189 return true; // do it again, till we stop
5192 bool
5193 Editor::horizontal_scroll_left_press ()
5195 ++_scroll_callbacks;
5197 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5198 /* delay the first auto-repeat */
5199 return true;
5202 double x = leftmost_position() - current_page_frames() / 5;
5203 if (x < 0) {
5204 x = 0;
5207 reset_x_origin (x);
5209 /* do hacky auto-repeat */
5210 if (!_scroll_connection.connected ()) {
5211 _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press), 100);
5212 _scroll_callbacks = 0;
5215 return true;
5218 void
5219 Editor::horizontal_scroll_left_release ()
5221 _scroll_connection.disconnect ();
5224 bool
5225 Editor::horizontal_scroll_right_press ()
5227 ++_scroll_callbacks;
5229 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5230 /* delay the first auto-repeat */
5231 return true;
5234 reset_x_origin (leftmost_position() + current_page_frames() / 5);
5236 /* do hacky auto-repeat */
5237 if (!_scroll_connection.connected ()) {
5238 _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press), 100);
5239 _scroll_callbacks = 0;
5242 return true;
5245 void
5246 Editor::horizontal_scroll_right_release ()
5248 _scroll_connection.disconnect ();
5251 /** Queue a change for the Editor viewport x origin to follow the playhead */
5252 void
5253 Editor::reset_x_origin_to_follow_playhead ()
5255 nframes64_t const frame = playhead_cursor->current_frame;
5257 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5259 if (_session->transport_speed() < 0) {
5261 if (frame > (current_page_frames() / 2)) {
5262 center_screen (frame-(current_page_frames()/2));
5263 } else {
5264 center_screen (current_page_frames()/2);
5267 } else {
5269 if (frame < leftmost_frame) {
5270 /* moving left */
5271 nframes64_t l = 0;
5272 if (_session->transport_rolling()) {
5273 /* rolling; end up with the playhead at the right of the page */
5274 l = frame - current_page_frames ();
5275 } else {
5276 /* not rolling: end up with the playhead 3/4 of the way along the page */
5277 l = frame - (3 * current_page_frames() / 4);
5280 if (l < 0) {
5281 l = 0;
5284 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5285 } else {
5286 /* moving right */
5287 if (_session->transport_rolling()) {
5288 /* rolling: end up with the playhead on the left of the page */
5289 center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5290 } else {
5291 /* not rolling: end up with the playhead 1/4 of the way along the page */
5292 center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5299 void
5300 Editor::super_rapid_screen_update ()
5302 if (!_session || !_session->engine().running()) {
5303 return;
5306 /* METERING / MIXER STRIPS */
5308 /* update track meters, if required */
5309 if (is_mapped() && meters_running) {
5310 RouteTimeAxisView* rtv;
5311 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5312 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5313 rtv->fast_update ();
5318 /* and any current mixer strip */
5319 if (current_mixer_strip) {
5320 current_mixer_strip->fast_update ();
5323 /* PLAYHEAD AND VIEWPORT */
5325 nframes64_t const frame = _session->audible_frame();
5327 /* There are a few reasons why we might not update the playhead / viewport stuff:
5329 * 1. we don't update things when there's a pending locate request, otherwise
5330 * when the editor requests a locate there is a chance that this method
5331 * will move the playhead before the locate request is processed, causing
5332 * a visual glitch.
5333 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5334 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5337 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5339 last_update_frame = frame;
5341 if (!_dragging_playhead) {
5342 playhead_cursor->set_position (frame);
5345 if (!_stationary_playhead) {
5347 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5348 reset_x_origin_to_follow_playhead ();
5351 } else {
5353 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5354 editor canvas
5356 #if 0
5357 // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5358 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5359 if (target <= 0.0) {
5360 target = 0.0;
5362 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5363 target = (target * 0.15) + (current * 0.85);
5364 } else {
5365 /* relax */
5368 current = target;
5369 set_horizontal_position (current);
5370 #endif
5377 void
5378 Editor::session_going_away ()
5380 _have_idled = false;
5382 _session_connections.drop_connections ();
5384 super_rapid_screen_update_connection.disconnect ();
5386 selection->clear ();
5387 cut_buffer->clear ();
5389 clicked_regionview = 0;
5390 clicked_axisview = 0;
5391 clicked_routeview = 0;
5392 clicked_crossfadeview = 0;
5393 entered_regionview = 0;
5394 entered_track = 0;
5395 last_update_frame = 0;
5396 _drags->abort ();
5398 playhead_cursor->canvas_item.hide ();
5400 /* rip everything out of the list displays */
5402 _regions->clear ();
5403 _routes->clear ();
5404 _route_groups->clear ();
5406 /* do this first so that deleting a track doesn't reset cms to null
5407 and thus cause a leak.
5410 if (current_mixer_strip) {
5411 if (current_mixer_strip->get_parent() != 0) {
5412 global_hpacker.remove (*current_mixer_strip);
5414 delete current_mixer_strip;
5415 current_mixer_strip = 0;
5418 /* delete all trackviews */
5420 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5421 delete *i;
5423 track_views.clear ();
5425 zoom_range_clock.set_session (0);
5426 nudge_clock.set_session (0);
5428 editor_list_button.set_active(false);
5429 editor_list_button.set_sensitive(false);
5431 /* clear tempo/meter rulers */
5432 remove_metric_marks ();
5433 hide_measures ();
5434 clear_marker_display ();
5436 delete current_bbt_points;
5437 current_bbt_points = 0;
5439 /* get rid of any existing editor mixer strip */
5441 WindowTitle title(Glib::get_application_name());
5442 title += _("Editor");
5444 set_title (title.get_string());
5446 SessionHandlePtr::session_going_away ();
5450 void
5451 Editor::show_editor_list (bool yn)
5453 if (yn) {
5454 the_notebook.show();
5455 } else {
5456 the_notebook.hide();