fix math bug with numthreads computation
[ardour2.git] / gtk2_ardour / editor.cc
blob0b6929e86ccdea07b372408c4d701fb4dac304eb
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_edit_point_cursor = 0;
219 Gdk::Cursor* Editor::zoom_cursor = 0;
220 Gdk::Cursor* Editor::time_fx_cursor = 0;
221 Gdk::Cursor* Editor::fader_cursor = 0;
222 Gdk::Cursor* Editor::speaker_cursor = 0;
223 Gdk::Cursor* Editor::midi_pencil_cursor = 0;
224 Gdk::Cursor* Editor::midi_select_cursor = 0;
225 Gdk::Cursor* Editor::midi_resize_cursor = 0;
226 Gdk::Cursor* Editor::midi_erase_cursor = 0;
227 Gdk::Cursor* Editor::wait_cursor = 0;
228 Gdk::Cursor* Editor::timebar_cursor = 0;
229 Gdk::Cursor* Editor::transparent_cursor = 0;
231 void
232 show_me_the_size (Requisition* r, const char* what)
234 cerr << "size of " << what << " = " << r->width << " x " << r->height << endl;
237 #ifdef GTKOSX
238 static void
239 pane_size_watcher (Paned* pane)
241 /* if the handle of a pane vanishes into (at least) the tabs of a notebook,
242 it is no longer accessible. so stop that. this doesn't happen on X11,
243 just the quartz backend.
245 ugh.
248 int max_width_of_lhs = GTK_WIDGET(pane->gobj())->allocation.width - 25;
250 gint pos = pane->get_position ();
252 if (pos > max_width_of_lhs) {
253 pane->set_position (max_width_of_lhs);
256 #endif
258 Editor::Editor ()
259 : _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
261 /* time display buttons */
262 , minsec_label (_("Mins:Secs"))
263 , bbt_label (_("Bars:Beats"))
264 , timecode_label (_("Timecode"))
265 , frame_label (_("Samples"))
266 , tempo_label (_("Tempo"))
267 , meter_label (_("Meter"))
268 , mark_label (_("Location Markers"))
269 , range_mark_label (_("Range Markers"))
270 , transport_mark_label (_("Loop/Punch Ranges"))
271 , cd_mark_label (_("CD Markers"))
272 , edit_packer (4, 4, true)
274 /* the values here don't matter: layout widgets
275 reset them as needed.
278 , vertical_adjustment (0.0, 0.0, 10.0, 400.0)
280 /* tool bar related */
282 , zoom_range_clock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, false, true)
284 , toolbar_selection_clock_table (2,3)
286 , automation_mode_button (_("mode"))
287 , global_automation_button (_("automation"))
289 , midi_panic_button (_("Panic"))
291 #ifdef WITH_CMT
292 , image_socket_listener(0)
293 #endif
295 /* nudge */
297 , nudge_clock (X_("nudge"), false, X_("NudgeClock"), true, false, true)
298 , meters_running(false)
299 , _pending_locate_request (false)
300 , _pending_initial_locate (false)
303 constructed = false;
305 /* we are a singleton */
307 PublicEditor::_instance = this;
309 _have_idled = false;
311 selection = new Selection (this);
312 cut_buffer = new Selection (this);
314 clicked_regionview = 0;
315 clicked_axisview = 0;
316 clicked_routeview = 0;
317 clicked_crossfadeview = 0;
318 clicked_control_point = 0;
319 last_update_frame = 0;
320 _drags = new DragManager (this);
321 current_mixer_strip = 0;
322 current_bbt_points = 0;
323 tempo_lines = 0;
325 snap_type_strings = I18N (_snap_type_strings);
326 snap_mode_strings = I18N (_snap_mode_strings);
327 zoom_focus_strings = I18N (_zoom_focus_strings);
328 edit_point_strings = I18N (_edit_point_strings);
329 #ifdef USE_RUBBERBAND
330 rb_opt_strings = I18N (_rb_opt_strings);
331 #endif
333 snap_threshold = 5.0;
334 bbt_beat_subdivision = 4;
335 _canvas_width = 0;
336 _canvas_height = 0;
337 last_autoscroll_x = 0;
338 last_autoscroll_y = 0;
339 autoscroll_active = false;
340 autoscroll_timeout_tag = -1;
341 logo_item = 0;
343 analysis_window = 0;
345 current_interthread_info = 0;
346 _show_measures = true;
347 show_gain_after_trim = false;
348 verbose_cursor_on = true;
349 last_item_entered = 0;
350 last_item_entered_n = 0;
352 have_pending_keyboard_selection = false;
353 _follow_playhead = true;
354 _stationary_playhead = false;
355 _xfade_visibility = true;
356 editor_ruler_menu = 0;
357 no_ruler_shown_update = false;
358 marker_menu = 0;
359 session_range_marker_menu = 0;
360 range_marker_menu = 0;
361 marker_menu_item = 0;
362 tempo_or_meter_marker_menu = 0;
363 transport_marker_menu = 0;
364 new_transport_marker_menu = 0;
365 editor_mixer_strip_width = Wide;
366 show_editor_mixer_when_tracks_arrive = false;
367 region_edit_menu_split_multichannel_item = 0;
368 region_edit_menu_split_item = 0;
369 temp_location = 0;
370 leftmost_frame = 0;
371 current_stepping_trackview = 0;
372 entered_track = 0;
373 entered_regionview = 0;
374 entered_marker = 0;
375 clear_entered_track = false;
376 current_timefx = 0;
377 playhead_cursor = 0;
378 button_release_can_deselect = true;
379 _dragging_playhead = false;
380 _dragging_edit_point = false;
381 select_new_marker = false;
382 rhythm_ferret = 0;
383 _bundle_manager = 0;
384 for (ARDOUR::DataType::iterator i = ARDOUR::DataType::begin(); i != ARDOUR::DataType::end(); ++i) {
385 _global_port_matrix[*i] = 0;
387 allow_vertical_scroll = false;
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 || !_follow_playhead) {
984 return;
987 center_screen (frame);
988 playhead_cursor->set_position (frame);
991 void
992 Editor::center_screen (nframes64_t frame)
994 double page = _canvas_width * frames_per_unit;
996 /* if we're off the page, then scroll.
999 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
1000 center_screen_internal (frame, page);
1004 void
1005 Editor::center_screen_internal (nframes64_t frame, float page)
1007 page /= 2;
1009 if (frame > page) {
1010 frame -= (nframes64_t) page;
1011 } else {
1012 frame = 0;
1015 reset_x_origin (frame);
1019 void
1020 Editor::update_title ()
1022 ENSURE_GUI_THREAD (*this, &Editor::update_title)
1024 if (_session) {
1025 bool dirty = _session->dirty();
1027 string session_name;
1029 if (_session->snap_name() != _session->name()) {
1030 session_name = _session->snap_name();
1031 } else {
1032 session_name = _session->name();
1035 if (dirty) {
1036 session_name = "*" + session_name;
1039 WindowTitle title(session_name);
1040 title += Glib::get_application_name();
1041 set_title (title.get_string());
1045 void
1046 Editor::set_session (Session *t)
1048 SessionHandlePtr::set_session (t);
1050 if (!_session) {
1051 return;
1054 zoom_range_clock.set_session (_session);
1055 _playlist_selector->set_session (_session);
1056 nudge_clock.set_session (_session);
1057 _summary->set_session (_session);
1058 _group_tabs->set_session (_session);
1059 _route_groups->set_session (_session);
1060 _regions->set_session (_session);
1061 _snapshots->set_session (_session);
1062 _routes->set_session (_session);
1063 _locations->set_session (_session);
1065 if (rhythm_ferret) {
1066 rhythm_ferret->set_session (_session);
1069 if (analysis_window) {
1070 analysis_window->set_session (_session);
1073 if (sfbrowser) {
1074 sfbrowser->set_session (_session);
1077 compute_fixed_ruler_scale ();
1079 /* there are never any selected regions at startup */
1080 sensitize_the_right_region_actions (false);
1082 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1083 set_state (*node, Stateful::loading_state_version);
1085 /* catch up with the playhead */
1087 _session->request_locate (playhead_cursor->current_frame);
1088 _pending_initial_locate = true;
1090 update_title ();
1092 /* These signals can all be emitted by a non-GUI thread. Therefore the
1093 handlers for them must not attempt to directly interact with the GUI,
1094 but use Gtkmm2ext::UI::instance()->call_slot();
1097 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1098 _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context());
1099 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context());
1100 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1101 _session->TimecodeOffsetChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_just_timecode, this), gui_context());
1102 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context());
1103 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1104 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
1105 _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Editor::session_state_saved, this, _1), gui_context());
1106 _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context());
1107 _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&Editor::location_gone, this, _1), gui_context());
1108 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1109 _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display_s, this, _1), gui_context());
1110 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1112 if (Profile->get_sae()) {
1113 BBT_Time bbt;
1114 bbt.bars = 0;
1115 bbt.beats = 0;
1116 bbt.ticks = 120;
1117 nframes_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1);
1118 nudge_clock.set_mode(AudioClock::BBT);
1119 nudge_clock.set (pos, true, 0, AudioClock::BBT);
1121 } else {
1122 nudge_clock.set (_session->frame_rate() * 5, true, 0, AudioClock::Timecode); // default of 5 seconds
1125 playhead_cursor->canvas_item.show ();
1127 Location* loc = _session->locations()->auto_loop_location();
1128 if (loc == 0) {
1129 loc = new Location (0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1130 if (loc->start() == loc->end()) {
1131 loc->set_end (loc->start() + 1);
1133 _session->locations()->add (loc, false);
1134 _session->set_auto_loop_location (loc);
1135 } else {
1136 // force name
1137 loc->set_name (_("Loop"));
1140 loc = _session->locations()->auto_punch_location();
1141 if (loc == 0) {
1142 loc = new Location (0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1143 if (loc->start() == loc->end()) {
1144 loc->set_end (loc->start() + 1);
1146 _session->locations()->add (loc, false);
1147 _session->set_auto_punch_location (loc);
1148 } else {
1149 // force name
1150 loc->set_name (_("Punch"));
1153 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1154 Config->map_parameters (pc);
1155 _session->config.map_parameters (pc);
1157 refresh_location_display ();
1159 restore_ruler_visibility ();
1160 //tempo_map_changed (PropertyChange (0));
1161 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1163 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1164 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1167 super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
1168 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1171 switch (_snap_type) {
1172 case SnapToRegionStart:
1173 case SnapToRegionEnd:
1174 case SnapToRegionSync:
1175 case SnapToRegionBoundary:
1176 build_region_boundary_cache ();
1177 break;
1179 default:
1180 break;
1183 /* register for undo history */
1184 _session->register_with_memento_command_factory(_id, this);
1186 start_updating_meters ();
1189 void
1190 Editor::build_cursors ()
1192 using namespace Gdk;
1194 Gdk::Color mbg ("#000000" ); /* Black */
1195 Gdk::Color mfg ("#0000ff" ); /* Blue. */
1198 RefPtr<Bitmap> source, mask;
1199 source = Bitmap::create (mag_bits, mag_width, mag_height);
1200 mask = Bitmap::create (magmask_bits, mag_width, mag_height);
1201 zoom_cursor = new Gdk::Cursor (source, mask, mfg, mbg, mag_x_hot, mag_y_hot);
1204 Gdk::Color fbg ("#ffffff" );
1205 Gdk::Color ffg ("#000000" );
1208 RefPtr<Bitmap> source, mask;
1210 source = Bitmap::create (fader_cursor_bits, fader_cursor_width, fader_cursor_height);
1211 mask = Bitmap::create (fader_cursor_mask_bits, fader_cursor_width, fader_cursor_height);
1212 fader_cursor = new Gdk::Cursor (source, mask, ffg, fbg, fader_cursor_x_hot, fader_cursor_y_hot);
1216 RefPtr<Bitmap> source, mask;
1217 source = Bitmap::create (speaker_cursor_bits, speaker_cursor_width, speaker_cursor_height);
1218 mask = Bitmap::create (speaker_cursor_mask_bits, speaker_cursor_width, speaker_cursor_height);
1219 speaker_cursor = new Gdk::Cursor (source, mask, ffg, fbg, speaker_cursor_x_hot, speaker_cursor_y_hot);
1223 RefPtr<Bitmap> bits;
1224 char pix[4] = { 0, 0, 0, 0 };
1225 bits = Bitmap::create (pix, 2, 2);
1226 Gdk::Color c;
1227 transparent_cursor = new Gdk::Cursor (bits, bits, c, c, 0, 0);
1231 RefPtr<Bitmap> bits;
1232 char pix[4] = { 0, 0, 0, 0 };
1233 bits = Bitmap::create (pix, 2, 2);
1234 Gdk::Color c;
1235 transparent_cursor = new Gdk::Cursor (bits, bits, c, c, 0, 0);
1239 grabber_cursor = new Gdk::Cursor (HAND2);
1242 Glib::RefPtr<Gdk::Pixbuf> grabber_edit_point_pixbuf (::get_icon ("grabber_edit_point"));
1243 grabber_edit_point_cursor = new Gdk::Cursor (Gdk::Display::get_default(), grabber_edit_point_pixbuf, 5, 17);
1246 cross_hair_cursor = new Gdk::Cursor (CROSSHAIR);
1247 trimmer_cursor = new Gdk::Cursor (SB_H_DOUBLE_ARROW);
1250 Glib::RefPtr<Gdk::Pixbuf> apixbuf (::get_icon ("trim_left_cursor"));
1251 left_side_trim_cursor = new Gdk::Cursor (Gdk::Display::get_default(), apixbuf, 5, 11);
1255 Glib::RefPtr<Gdk::Pixbuf> apixbuf (::get_icon ("trim_right_cursor"));
1256 right_side_trim_cursor = new Gdk::Cursor (Gdk::Display::get_default(), apixbuf, 23, 11);
1260 Glib::RefPtr<Gdk::Pixbuf> apixbuf (::get_icon ("fade_in_cursor"));
1261 fade_in_cursor = new Gdk::Cursor (Gdk::Display::get_default(), apixbuf, 0, 40);
1265 Glib::RefPtr<Gdk::Pixbuf> apixbuf (::get_icon ("fade_out_cursor"));
1266 fade_out_cursor = new Gdk::Cursor (Gdk::Display::get_default(), apixbuf, 27, 40);
1269 selector_cursor = new Gdk::Cursor (XTERM);
1270 time_fx_cursor = new Gdk::Cursor (SIZING);
1271 wait_cursor = new Gdk::Cursor (WATCH);
1272 timebar_cursor = new Gdk::Cursor(LEFT_PTR);
1273 midi_pencil_cursor = new Gdk::Cursor (PENCIL);
1274 midi_select_cursor = new Gdk::Cursor (CENTER_PTR);
1275 midi_resize_cursor = new Gdk::Cursor (SIZING);
1276 midi_erase_cursor = new Gdk::Cursor (DRAPED_BOX);
1279 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1280 void
1281 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1283 using namespace Menu_Helpers;
1284 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1286 if (arv == 0) {
1287 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1288 /*NOTREACHED*/
1291 MenuList& items (fade_context_menu.items());
1293 items.clear ();
1295 switch (item_type) {
1296 case FadeInItem:
1297 case FadeInHandleItem:
1298 if (arv->audio_region()->fade_in_active()) {
1299 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1300 } else {
1301 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1304 items.push_back (SeparatorElem());
1306 if (Profile->get_sae()) {
1307 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear)));
1308 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast)));
1309 } else {
1310 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear)));
1311 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast)));
1312 items.push_back (MenuElem (_("Slow"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogB)));
1313 items.push_back (MenuElem (_("Fast"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogA)));
1314 items.push_back (MenuElem (_("Fastest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Slow)));
1317 break;
1319 case FadeOutItem:
1320 case FadeOutHandleItem:
1321 if (arv->audio_region()->fade_out_active()) {
1322 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1323 } else {
1324 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1327 items.push_back (SeparatorElem());
1329 if (Profile->get_sae()) {
1330 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear)));
1331 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow)));
1332 } else {
1333 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear)));
1334 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow)));
1335 items.push_back (MenuElem (_("Slow"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogA)));
1336 items.push_back (MenuElem (_("Fast"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogB)));
1337 items.push_back (MenuElem (_("Fastest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Fast)));
1340 break;
1342 default:
1343 fatal << _("programming error: ")
1344 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1345 << endmsg;
1346 /*NOTREACHED*/
1349 fade_context_menu.popup (button, time);
1352 void
1353 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection, nframes64_t frame)
1355 using namespace Menu_Helpers;
1356 Menu* (Editor::*build_menu_function)(nframes64_t);
1357 Menu *menu;
1359 switch (item_type) {
1360 case RegionItem:
1361 case RegionViewName:
1362 case RegionViewNameHighlight:
1363 case LeftFrameHandle:
1364 case RightFrameHandle:
1365 if (with_selection) {
1366 build_menu_function = &Editor::build_track_selection_context_menu;
1367 } else {
1368 build_menu_function = &Editor::build_track_region_context_menu;
1370 break;
1372 case SelectionItem:
1373 if (with_selection) {
1374 build_menu_function = &Editor::build_track_selection_context_menu;
1375 } else {
1376 build_menu_function = &Editor::build_track_context_menu;
1378 break;
1380 case CrossfadeViewItem:
1381 build_menu_function = &Editor::build_track_crossfade_context_menu;
1382 break;
1384 case StreamItem:
1385 if (clicked_routeview->track()) {
1386 build_menu_function = &Editor::build_track_context_menu;
1387 } else {
1388 build_menu_function = &Editor::build_track_bus_context_menu;
1390 break;
1392 default:
1393 /* probably shouldn't happen but if it does, we don't care */
1394 return;
1397 menu = (this->*build_menu_function)(frame);
1398 menu->set_name ("ArdourContextMenu");
1400 /* now handle specific situations */
1402 switch (item_type) {
1403 case RegionItem:
1404 case RegionViewName:
1405 case RegionViewNameHighlight:
1406 case LeftFrameHandle:
1407 case RightFrameHandle:
1408 if (!with_selection) {
1409 if (region_edit_menu_split_item) {
1410 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1411 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1412 } else {
1413 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1416 if (region_edit_menu_split_multichannel_item) {
1417 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1418 region_edit_menu_split_multichannel_item->set_sensitive (true);
1419 } else {
1420 region_edit_menu_split_multichannel_item->set_sensitive (false);
1424 break;
1426 case SelectionItem:
1427 break;
1429 case CrossfadeViewItem:
1430 break;
1432 case StreamItem:
1433 break;
1435 default:
1436 /* probably shouldn't happen but if it does, we don't care */
1437 return;
1440 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1442 /* Bounce to disk */
1444 using namespace Menu_Helpers;
1445 MenuList& edit_items = menu->items();
1447 edit_items.push_back (SeparatorElem());
1449 switch (clicked_routeview->audio_track()->freeze_state()) {
1450 case AudioTrack::NoFreeze:
1451 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1452 break;
1454 case AudioTrack::Frozen:
1455 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1456 break;
1458 case AudioTrack::UnFrozen:
1459 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1460 break;
1465 if (item_type == StreamItem && clicked_routeview) {
1466 clicked_routeview->build_underlay_menu(menu);
1469 menu->popup (button, time);
1472 Menu*
1473 Editor::build_track_context_menu (nframes64_t)
1475 using namespace Menu_Helpers;
1477 MenuList& edit_items = track_context_menu.items();
1478 edit_items.clear();
1480 add_dstream_context_items (edit_items);
1481 return &track_context_menu;
1484 Menu*
1485 Editor::build_track_bus_context_menu (nframes64_t)
1487 using namespace Menu_Helpers;
1489 MenuList& edit_items = track_context_menu.items();
1490 edit_items.clear();
1492 add_bus_context_items (edit_items);
1493 return &track_context_menu;
1496 Menu*
1497 Editor::build_track_region_context_menu (nframes64_t frame)
1499 using namespace Menu_Helpers;
1500 MenuList& edit_items = track_region_context_menu.items();
1501 edit_items.clear();
1503 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1505 if (rtv) {
1506 boost::shared_ptr<Track> tr;
1507 boost::shared_ptr<Playlist> pl;
1509 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
1510 Playlist::RegionList* regions = pl->regions_at ((nframes64_t) floor ( (double)frame * tr->speed()));
1512 if (selection->regions.size() > 1) {
1513 // there's already a multiple selection: just add a
1514 // single region context menu that will act on all
1515 // selected regions
1516 boost::shared_ptr<Region> dummy_region; // = NULL
1517 add_region_context_items (rtv->view(), dummy_region, edit_items);
1518 } else {
1519 for (Playlist::RegionList::reverse_iterator i = regions->rbegin(); i != regions->rend(); ++i) {
1520 add_region_context_items (rtv->view(), (*i), edit_items);
1524 delete regions;
1528 add_dstream_context_items (edit_items);
1530 return &track_region_context_menu;
1533 Menu*
1534 Editor::build_track_crossfade_context_menu (nframes64_t frame)
1536 using namespace Menu_Helpers;
1537 MenuList& edit_items = track_crossfade_context_menu.items();
1538 edit_items.clear ();
1540 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1542 if (atv) {
1543 boost::shared_ptr<Track> tr;
1544 boost::shared_ptr<Playlist> pl;
1545 boost::shared_ptr<AudioPlaylist> apl;
1547 if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1549 Playlist::RegionList* regions = pl->regions_at (frame);
1550 AudioPlaylist::Crossfades xfades;
1552 apl->crossfades_at (frame, xfades);
1554 bool many = xfades.size() > 1;
1556 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1557 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1560 if (selection->regions.size() > 1) {
1561 // there's already a multiple selection: just add a
1562 // single region context menu that will act on all
1563 // selected regions
1564 boost::shared_ptr<Region> dummy_region; // = NULL
1565 add_region_context_items (atv->audio_view(), dummy_region, edit_items);
1566 } else {
1567 for (Playlist::RegionList::reverse_iterator i = regions->rbegin(); i != regions->rend(); ++i) {
1568 add_region_context_items (atv->audio_view(), (*i), edit_items);
1571 delete regions;
1575 add_dstream_context_items (edit_items);
1577 return &track_crossfade_context_menu;
1580 void
1581 Editor::analyze_region_selection()
1583 if (analysis_window == 0) {
1584 analysis_window = new AnalysisWindow();
1586 if (_session != 0)
1587 analysis_window->set_session(_session);
1589 analysis_window->show_all();
1592 analysis_window->set_regionmode();
1593 analysis_window->analyze();
1595 analysis_window->present();
1598 void
1599 Editor::analyze_range_selection()
1601 if (analysis_window == 0) {
1602 analysis_window = new AnalysisWindow();
1604 if (_session != 0)
1605 analysis_window->set_session(_session);
1607 analysis_window->show_all();
1610 analysis_window->set_rangemode();
1611 analysis_window->analyze();
1613 analysis_window->present();
1616 Menu*
1617 Editor::build_track_selection_context_menu (nframes64_t)
1619 using namespace Menu_Helpers;
1620 MenuList& edit_items = track_selection_context_menu.items();
1621 edit_items.clear ();
1623 add_selection_context_items (edit_items);
1624 // edit_items.push_back (SeparatorElem());
1625 // add_dstream_context_items (edit_items);
1627 return &track_selection_context_menu;
1630 /** Add context menu items relevant to crossfades.
1631 * @param edit_items List to add the items to.
1633 void
1634 Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1636 using namespace Menu_Helpers;
1637 Menu *xfade_menu = manage (new Menu);
1638 MenuList& items = xfade_menu->items();
1639 xfade_menu->set_name ("ArdourContextMenu");
1640 string str;
1642 if (xfade->active()) {
1643 str = _("Mute");
1644 } else {
1645 str = _("Unmute");
1648 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1649 items.push_back (MenuElem (_("Edit"), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1651 if (xfade->can_follow_overlap()) {
1653 if (xfade->following_overlap()) {
1654 str = _("Convert to Short");
1655 } else {
1656 str = _("Convert to Full");
1659 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1662 if (many) {
1663 str = xfade->out()->name();
1664 str += "->";
1665 str += xfade->in()->name();
1666 } else {
1667 str = _("Crossfade");
1670 edit_items.push_back (MenuElem (str, *xfade_menu));
1671 edit_items.push_back (SeparatorElem());
1674 void
1675 Editor::xfade_edit_left_region ()
1677 if (clicked_crossfadeview) {
1678 clicked_crossfadeview->left_view.show_region_editor ();
1682 void
1683 Editor::xfade_edit_right_region ()
1685 if (clicked_crossfadeview) {
1686 clicked_crossfadeview->right_view.show_region_editor ();
1690 void
1691 Editor::add_region_context_items (StreamView* sv, boost::shared_ptr<Region> region, Menu_Helpers::MenuList& edit_items)
1693 using namespace Menu_Helpers;
1694 Gtk::MenuItem* foo_item;
1695 Menu *region_menu = manage (new Menu);
1696 MenuList& items = region_menu->items();
1697 region_menu->set_name ("ArdourContextMenu");
1699 boost::shared_ptr<AudioRegion> ar;
1700 boost::shared_ptr<MidiRegion> mr;
1702 if (region) {
1703 ar = boost::dynamic_pointer_cast<AudioRegion> (region);
1704 mr = boost::dynamic_pointer_cast<MidiRegion> (region);
1706 /* when this particular menu pops up, make the relevant region
1707 become selected.
1710 region_menu->signal_map_event().connect (
1711 sigc::bind (sigc::mem_fun(*this, &Editor::set_selected_regionview_from_map_event), sv, boost::weak_ptr<Region>(region)));
1713 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &Editor::rename_region)));
1714 if (mr) {
1715 items.push_back (MenuElem (_("List Editor..."), sigc::mem_fun(*this, &Editor::show_midi_list_editor)));
1717 items.push_back (MenuElem (_("Region Properties..."), sigc::mem_fun(*this, &Editor::edit_region)));
1720 items.push_back (MenuElem (_("Raise to Top Layer"), sigc::mem_fun(*this, &Editor::raise_region_to_top)));
1721 items.push_back (MenuElem (_("Lower to Bottom Layer"), sigc::mem_fun (*this, &Editor::lower_region_to_bottom)));
1722 items.push_back (SeparatorElem());
1723 items.push_back (MenuElem (_("Define Sync Point"), sigc::mem_fun(*this, &Editor::set_region_sync_from_edit_point)));
1724 if (_edit_point == EditAtMouse) {
1725 items.back ().set_sensitive (false);
1727 items.push_back (MenuElem (_("Remove Sync Point"), sigc::mem_fun(*this, &Editor::remove_region_sync)));
1728 items.push_back (SeparatorElem());
1730 items.push_back (MenuElem (_("Audition"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1731 items.push_back (MenuElem (_("Export..."), sigc::mem_fun(*this, &Editor::export_region)));
1732 items.push_back (MenuElem (_("Bounce"), sigc::mem_fun(*this, &Editor::bounce_region_selection)));
1734 if (ar) {
1735 items.push_back (MenuElem (_("Spectral Analysis..."), sigc::mem_fun(*this, &Editor::analyze_region_selection)));
1738 items.push_back (SeparatorElem());
1740 sigc::connection fooc;
1741 boost::shared_ptr<Region> region_to_check;
1743 if (region) {
1744 region_to_check = region;
1745 } else {
1746 region_to_check = selection->regions.front()->region();
1749 items.push_back (CheckMenuElem (_("Lock")));
1750 CheckMenuItem* region_lock_item = static_cast<CheckMenuItem*>(&items.back());
1751 if (region_to_check->locked()) {
1752 region_lock_item->set_active();
1754 region_lock_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_region_lock));
1756 items.push_back (CheckMenuElem (_("Glue to Bars and Beats")));
1757 CheckMenuItem* bbt_glue_item = static_cast<CheckMenuItem*>(&items.back());
1759 switch (region_to_check->positional_lock_style()) {
1760 case Region::MusicTime:
1761 bbt_glue_item->set_active (true);
1762 break;
1763 default:
1764 bbt_glue_item->set_active (false);
1765 break;
1768 bbt_glue_item->signal_activate().connect (sigc::mem_fun (*this, &Editor::toggle_region_lock_style));
1770 items.push_back (CheckMenuElem (_("Mute")));
1771 CheckMenuItem* region_mute_item = static_cast<CheckMenuItem*>(&items.back());
1772 fooc = region_mute_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_region_mute));
1773 if (region_to_check->muted()) {
1774 fooc.block (true);
1775 region_mute_item->set_active();
1776 fooc.block (false);
1779 items.push_back (MenuElem (_("Transpose..."), mem_fun(*this, &Editor::pitch_shift_regions)));
1781 if (!Profile->get_sae()) {
1782 items.push_back (CheckMenuElem (_("Opaque")));
1783 CheckMenuItem* region_opaque_item = static_cast<CheckMenuItem*>(&items.back());
1784 fooc = region_opaque_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_region_opaque));
1785 if (region_to_check->opaque()) {
1786 fooc.block (true);
1787 region_opaque_item->set_active();
1788 fooc.block (false);
1792 items.push_back (CheckMenuElem (_("Original Position"), sigc::mem_fun(*this, &Editor::naturalize)));
1793 if (region_to_check->at_natural_position()) {
1794 items.back().set_sensitive (false);
1797 items.push_back (SeparatorElem());
1799 if (ar) {
1801 RegionView* rv = sv->find_view (ar);
1802 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1804 if (!Profile->get_sae()) {
1805 items.push_back (MenuElem (_("Reset Envelope"), sigc::mem_fun(*this, &Editor::reset_region_gain_envelopes)));
1807 items.push_back (CheckMenuElem (_("Envelope Visible")));
1808 CheckMenuItem* region_envelope_visible_item = static_cast<CheckMenuItem*> (&items.back());
1809 fooc = region_envelope_visible_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_gain_envelope_visibility));
1810 if (arv->envelope_visible()) {
1811 fooc.block (true);
1812 region_envelope_visible_item->set_active (true);
1813 fooc.block (false);
1816 items.push_back (CheckMenuElem (_("Envelope Active")));
1817 CheckMenuItem* region_envelope_active_item = static_cast<CheckMenuItem*> (&items.back());
1818 fooc = region_envelope_active_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_gain_envelope_active));
1820 if (ar->envelope_active()) {
1821 fooc.block (true);
1822 region_envelope_active_item->set_active (true);
1823 fooc.block (false);
1826 items.push_back (SeparatorElem());
1829 items.push_back (MenuElem (_("Normalize..."), sigc::mem_fun(*this, &Editor::normalize_region)));
1830 if (ar->scale_amplitude() != 1) {
1831 items.push_back (MenuElem (_("Reset Gain"), sigc::mem_fun(*this, &Editor::reset_region_scale_amplitude)));
1834 } else if (mr) {
1835 items.push_back (MenuElem (_("Quantize"), sigc::mem_fun(*this, &Editor::quantize_region)));
1836 items.push_back (MenuElem (_("Fork"), sigc::mem_fun(*this, &Editor::fork_region)));
1837 items.push_back (SeparatorElem());
1840 items.push_back (MenuElem (_("Strip Silence..."), sigc::mem_fun (*this, &Editor::strip_region_silence)));
1841 items.push_back (MenuElem (_("Reverse"), sigc::mem_fun(*this, &Editor::reverse_region)));
1842 items.push_back (SeparatorElem());
1844 /* range related stuff */
1846 items.push_back (MenuElem (_("Add Single Range"), sigc::mem_fun (*this, &Editor::add_location_from_audio_region)));
1847 items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_locations_from_audio_region)));
1848 if (selection->regions.size() < 2) {
1849 items.back().set_sensitive (false);
1852 items.push_back (MenuElem (_("Set Range Selection"), sigc::mem_fun (*this, &Editor::set_selection_from_region)));
1853 items.push_back (SeparatorElem());
1855 /* Nudge region */
1857 Menu *nudge_menu = manage (new Menu());
1858 MenuList& nudge_items = nudge_menu->items();
1859 nudge_menu->set_name ("ArdourContextMenu");
1861 nudge_items.push_back (MenuElem (_("Nudge Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_forward), false, false))));
1862 nudge_items.push_back (MenuElem (_("Nudge Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_backward), false, false))));
1863 nudge_items.push_back (MenuElem (_("Nudge Forward by Capture Offset"), (sigc::mem_fun(*this, &Editor::nudge_forward_capture_offset))));
1864 nudge_items.push_back (MenuElem (_("Nudge Backward by Capture Offset"), (sigc::mem_fun(*this, &Editor::nudge_backward_capture_offset))));
1866 items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1867 items.push_back (SeparatorElem());
1869 Menu *trim_menu = manage (new Menu);
1870 MenuList& trim_items = trim_menu->items();
1871 trim_menu->set_name ("ArdourContextMenu");
1873 trim_items.push_back (MenuElem (_("Start to Edit Point"), sigc::mem_fun(*this, &Editor::trim_region_from_edit_point)));
1874 foo_item = &trim_items.back();
1875 if (_edit_point == EditAtMouse) {
1876 foo_item->set_sensitive (false);
1878 trim_items.push_back (MenuElem (_("Edit Point to End"), sigc::mem_fun(*this, &Editor::trim_region_to_edit_point)));
1879 foo_item = &trim_items.back();
1880 if (_edit_point == EditAtMouse) {
1881 foo_item->set_sensitive (false);
1883 trim_items.push_back (MenuElem (_("Trim to Loop"), sigc::mem_fun(*this, &Editor::trim_region_to_loop)));
1884 trim_items.push_back (MenuElem (_("Trim to Punch"), sigc::mem_fun(*this, &Editor::trim_region_to_punch)));
1886 items.push_back (MenuElem (_("Trim"), *trim_menu));
1887 items.push_back (SeparatorElem());
1889 items.push_back (MenuElem (_("Split"), (sigc::mem_fun(*this, &Editor::split))));
1890 region_edit_menu_split_item = &items.back();
1892 if (_edit_point == EditAtMouse) {
1893 region_edit_menu_split_item->set_sensitive (false);
1896 items.push_back (MenuElem (_("Make Mono Regions"), (sigc::mem_fun(*this, &Editor::split_multichannel_region))));
1897 region_edit_menu_split_multichannel_item = &items.back();
1899 items.push_back (MenuElem (_("Duplicate"), (sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false))));
1900 items.push_back (MenuElem (_("Multi-Duplicate..."), (sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), true))));
1901 items.push_back (MenuElem (_("Fill Track"), (sigc::mem_fun(*this, &Editor::region_fill_track))));
1902 items.push_back (SeparatorElem());
1903 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(*this, &Editor::remove_selected_regions)));
1905 /* OK, stick the region submenu at the top of the list, and then add
1906 the standard items.
1909 /* we have to hack up the region name because "_" has a special
1910 meaning for menu titles.
1913 string::size_type pos = 0;
1914 string menu_item_name = (region) ? region->name() : _("Selected Regions");
1916 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1917 menu_item_name.replace (pos, 1, "__");
1918 pos += 2;
1921 edit_items.push_back (MenuElem (menu_item_name, *region_menu));
1922 edit_items.push_back (SeparatorElem());
1925 /** Add context menu items relevant to selection ranges.
1926 * @param edit_items List to add the items to.
1928 void
1929 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1931 using namespace Menu_Helpers;
1933 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1934 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1936 edit_items.push_back (SeparatorElem());
1937 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1939 if (!selection->regions.empty()) {
1940 edit_items.push_back (SeparatorElem());
1941 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)));
1942 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)));
1945 edit_items.push_back (SeparatorElem());
1946 edit_items.push_back (MenuElem (_("Silence Range"), sigc::mem_fun(*this, &Editor::separate_region_from_selection)));
1947 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1949 edit_items.push_back (SeparatorElem());
1950 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1952 edit_items.push_back (SeparatorElem());
1953 edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1954 edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1956 edit_items.push_back (SeparatorElem());
1957 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1959 edit_items.push_back (SeparatorElem());
1960 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1961 edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1962 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1964 edit_items.push_back (SeparatorElem());
1965 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1966 edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1967 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1968 edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1969 edit_items.push_back (MenuElem (_("Export Range"), sigc::mem_fun(*this, &Editor::export_selection)));
1973 void
1974 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1976 using namespace Menu_Helpers;
1978 /* Playback */
1980 Menu *play_menu = manage (new Menu);
1981 MenuList& play_items = play_menu->items();
1982 play_menu->set_name ("ArdourContextMenu");
1984 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1985 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1986 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1987 play_items.push_back (SeparatorElem());
1988 play_items.push_back (MenuElem (_("Loop Region"), sigc::mem_fun(*this, &Editor::loop_selected_region)));
1990 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1992 /* Selection */
1994 Menu *select_menu = manage (new Menu);
1995 MenuList& select_items = select_menu->items();
1996 select_menu->set_name ("ArdourContextMenu");
1998 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1999 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
2000 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
2001 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
2002 select_items.push_back (SeparatorElem());
2003 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
2004 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
2005 select_items.push_back (SeparatorElem());
2006 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
2007 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
2008 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2009 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2010 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
2011 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
2012 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
2014 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2016 /* Cut-n-Paste */
2018 Menu *cutnpaste_menu = manage (new Menu);
2019 MenuList& cutnpaste_items = cutnpaste_menu->items();
2020 cutnpaste_menu->set_name ("ArdourContextMenu");
2022 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2023 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2024 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
2026 cutnpaste_items.push_back (SeparatorElem());
2028 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun(*this, &Editor::align), ARDOUR::SyncPoint)));
2029 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun(*this, &Editor::align_relative), ARDOUR::SyncPoint)));
2031 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
2033 /* Adding new material */
2035 edit_items.push_back (SeparatorElem());
2036 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
2037 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
2039 /* Nudge track */
2041 Menu *nudge_menu = manage (new Menu());
2042 MenuList& nudge_items = nudge_menu->items();
2043 nudge_menu->set_name ("ArdourContextMenu");
2045 edit_items.push_back (SeparatorElem());
2046 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2047 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2048 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2049 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2051 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2054 void
2055 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
2057 using namespace Menu_Helpers;
2059 /* Playback */
2061 Menu *play_menu = manage (new Menu);
2062 MenuList& play_items = play_menu->items();
2063 play_menu->set_name ("ArdourContextMenu");
2065 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
2066 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
2067 edit_items.push_back (MenuElem (_("Play"), *play_menu));
2069 /* Selection */
2071 Menu *select_menu = manage (new Menu);
2072 MenuList& select_items = select_menu->items();
2073 select_menu->set_name ("ArdourContextMenu");
2075 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2076 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
2077 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
2078 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
2079 select_items.push_back (SeparatorElem());
2080 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
2081 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
2082 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2083 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2085 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2087 /* Cut-n-Paste */
2089 Menu *cutnpaste_menu = manage (new Menu);
2090 MenuList& cutnpaste_items = cutnpaste_menu->items();
2091 cutnpaste_menu->set_name ("ArdourContextMenu");
2093 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2094 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2095 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
2097 Menu *nudge_menu = manage (new Menu());
2098 MenuList& nudge_items = nudge_menu->items();
2099 nudge_menu->set_name ("ArdourContextMenu");
2101 edit_items.push_back (SeparatorElem());
2102 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2103 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2104 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2105 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2107 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2110 SnapType
2111 Editor::snap_type() const
2113 return _snap_type;
2116 SnapMode
2117 Editor::snap_mode() const
2119 return _snap_mode;
2122 void
2123 Editor::set_snap_to (SnapType st)
2125 unsigned int snap_ind = (unsigned int)st;
2127 _snap_type = st;
2129 if (snap_ind > snap_type_strings.size() - 1) {
2130 snap_ind = 0;
2131 _snap_type = (SnapType)snap_ind;
2134 string str = snap_type_strings[snap_ind];
2136 if (str != snap_type_selector.get_active_text()) {
2137 snap_type_selector.set_active_text (str);
2140 instant_save ();
2142 switch (_snap_type) {
2143 case SnapToBeatDiv32:
2144 case SnapToBeatDiv28:
2145 case SnapToBeatDiv24:
2146 case SnapToBeatDiv16:
2147 case SnapToBeatDiv14:
2148 case SnapToBeatDiv12:
2149 case SnapToBeatDiv10:
2150 case SnapToBeatDiv8:
2151 case SnapToBeatDiv7:
2152 case SnapToBeatDiv6:
2153 case SnapToBeatDiv5:
2154 case SnapToBeatDiv4:
2155 case SnapToBeatDiv3:
2156 case SnapToBeatDiv2:
2157 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
2158 update_tempo_based_rulers ();
2159 break;
2161 case SnapToRegionStart:
2162 case SnapToRegionEnd:
2163 case SnapToRegionSync:
2164 case SnapToRegionBoundary:
2165 build_region_boundary_cache ();
2166 break;
2168 default:
2169 /* relax */
2170 break;
2173 SnapChanged (); /* EMIT SIGNAL */
2176 void
2177 Editor::set_snap_mode (SnapMode mode)
2179 _snap_mode = mode;
2180 string str = snap_mode_strings[(int)mode];
2182 if (str != snap_mode_selector.get_active_text ()) {
2183 snap_mode_selector.set_active_text (str);
2186 instant_save ();
2188 void
2189 Editor::set_edit_point_preference (EditPoint ep, bool force)
2191 bool changed = (_edit_point != ep);
2193 _edit_point = ep;
2194 string str = edit_point_strings[(int)ep];
2196 if (str != edit_point_selector.get_active_text ()) {
2197 edit_point_selector.set_active_text (str);
2200 set_canvas_cursor ();
2202 if (!force && !changed) {
2203 return;
2206 const char* action=NULL;
2208 switch (_edit_point) {
2209 case EditAtPlayhead:
2210 action = "edit-at-playhead";
2211 break;
2212 case EditAtSelectedMarker:
2213 action = "edit-at-marker";
2214 break;
2215 case EditAtMouse:
2216 action = "edit-at-mouse";
2217 break;
2220 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2221 if (act) {
2222 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2225 nframes64_t foo;
2226 bool in_track_canvas;
2228 if (!mouse_frame (foo, in_track_canvas)) {
2229 in_track_canvas = false;
2232 reset_canvas_action_sensitivity (in_track_canvas);
2234 instant_save ();
2238 Editor::set_state (const XMLNode& node, int /*version*/)
2240 const XMLProperty* prop;
2241 XMLNode* geometry;
2242 int x, y, xoff, yoff;
2243 Gdk::Geometry g;
2245 if ((prop = node.property ("id")) != 0) {
2246 _id = prop->value ();
2249 g.base_width = default_width;
2250 g.base_height = default_height;
2251 x = 1;
2252 y = 1;
2253 xoff = 0;
2254 yoff = 21;
2256 if ((geometry = find_named_node (node, "geometry")) != 0) {
2258 XMLProperty* prop;
2260 if ((prop = geometry->property("x_size")) == 0) {
2261 prop = geometry->property ("x-size");
2263 if (prop) {
2264 g.base_width = atoi(prop->value());
2266 if ((prop = geometry->property("y_size")) == 0) {
2267 prop = geometry->property ("y-size");
2269 if (prop) {
2270 g.base_height = atoi(prop->value());
2273 if ((prop = geometry->property ("x_pos")) == 0) {
2274 prop = geometry->property ("x-pos");
2276 if (prop) {
2277 x = atoi (prop->value());
2280 if ((prop = geometry->property ("y_pos")) == 0) {
2281 prop = geometry->property ("y-pos");
2283 if (prop) {
2284 y = atoi (prop->value());
2287 if ((prop = geometry->property ("x_off")) == 0) {
2288 prop = geometry->property ("x-off");
2290 if (prop) {
2291 xoff = atoi (prop->value());
2293 if ((prop = geometry->property ("y_off")) == 0) {
2294 prop = geometry->property ("y-off");
2296 if (prop) {
2297 yoff = atoi (prop->value());
2301 set_default_size (g.base_width, g.base_height);
2302 move (x, y);
2304 if (_session && (prop = node.property ("playhead"))) {
2305 nframes64_t pos;
2306 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2307 playhead_cursor->set_position (pos);
2308 } else {
2309 playhead_cursor->set_position (0);
2312 if ((prop = node.property ("mixer-width"))) {
2313 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2316 if ((prop = node.property ("zoom-focus"))) {
2317 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2320 if ((prop = node.property ("zoom"))) {
2321 reset_zoom (PBD::atof (prop->value()));
2324 if ((prop = node.property ("snap-to"))) {
2325 set_snap_to ((SnapType) atoi (prop->value()));
2328 if ((prop = node.property ("snap-mode"))) {
2329 set_snap_mode ((SnapMode) atoi (prop->value()));
2332 if ((prop = node.property ("mouse-mode"))) {
2333 MouseMode m = str2mousemode(prop->value());
2334 set_mouse_mode (m, true);
2335 } else {
2336 set_mouse_mode (MouseObject, true);
2339 if ((prop = node.property ("left-frame")) != 0){
2340 nframes64_t pos;
2341 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2342 reset_x_origin (pos);
2346 if ((prop = node.property ("y-origin")) != 0) {
2347 reset_y_origin (atof (prop->value ()));
2350 if ((prop = node.property ("internal-edit"))) {
2351 bool yn = string_is_affirmative (prop->value());
2352 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2353 if (act) {
2354 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2355 tact->set_active (!yn);
2356 tact->set_active (yn);
2360 if ((prop = node.property ("join-object-range"))) {
2361 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2364 if ((prop = node.property ("edit-point"))) {
2365 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2368 if ((prop = node.property ("show-measures"))) {
2369 bool yn = string_is_affirmative (prop->value());
2370 _show_measures = !yn;
2371 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2372 if (act) {
2373 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2374 /* do it twice to force the change */
2375 tact->set_active (!yn);
2376 tact->set_active (yn);
2380 if ((prop = node.property ("follow-playhead"))) {
2381 bool yn = string_is_affirmative (prop->value());
2382 set_follow_playhead (yn);
2383 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2384 if (act) {
2385 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2386 if (tact->get_active() != yn) {
2387 tact->set_active (yn);
2392 if ((prop = node.property ("stationary-playhead"))) {
2393 bool yn = (prop->value() == "yes");
2394 set_stationary_playhead (yn);
2395 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2396 if (act) {
2397 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2398 if (tact->get_active() != yn) {
2399 tact->set_active (yn);
2404 if ((prop = node.property ("region-list-sort-type"))) {
2405 RegionListSortType st;
2406 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2409 if ((prop = node.property ("xfades-visible"))) {
2410 bool yn = string_is_affirmative (prop->value());
2411 _xfade_visibility = !yn;
2412 // set_xfade_visibility (yn);
2415 if ((prop = node.property ("show-editor-mixer"))) {
2417 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2418 if (act) {
2420 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2421 bool yn = string_is_affirmative (prop->value());
2423 /* do it twice to force the change */
2425 tact->set_active (!yn);
2426 tact->set_active (yn);
2430 if ((prop = node.property ("show-editor-list"))) {
2432 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2433 assert(act);
2434 if (act) {
2436 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2437 bool yn = string_is_affirmative (prop->value());
2439 /* do it twice to force the change */
2441 tact->set_active (!yn);
2442 tact->set_active (yn);
2446 if ((prop = node.property (X_("editor-list-page")))) {
2447 the_notebook.set_current_page (atoi (prop->value ()));
2450 return 0;
2453 XMLNode&
2454 Editor::get_state ()
2456 XMLNode* node = new XMLNode ("Editor");
2457 char buf[32];
2459 _id.print (buf, sizeof (buf));
2460 node->add_property ("id", buf);
2462 if (is_realized()) {
2463 Glib::RefPtr<Gdk::Window> win = get_window();
2465 int x, y, xoff, yoff, width, height;
2466 win->get_root_origin(x, y);
2467 win->get_position(xoff, yoff);
2468 win->get_size(width, height);
2470 XMLNode* geometry = new XMLNode ("geometry");
2472 snprintf(buf, sizeof(buf), "%d", width);
2473 geometry->add_property("x-size", string(buf));
2474 snprintf(buf, sizeof(buf), "%d", height);
2475 geometry->add_property("y-size", string(buf));
2476 snprintf(buf, sizeof(buf), "%d", x);
2477 geometry->add_property("x-pos", string(buf));
2478 snprintf(buf, sizeof(buf), "%d", y);
2479 geometry->add_property("y-pos", string(buf));
2480 snprintf(buf, sizeof(buf), "%d", xoff);
2481 geometry->add_property("x-off", string(buf));
2482 snprintf(buf, sizeof(buf), "%d", yoff);
2483 geometry->add_property("y-off", string(buf));
2484 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2485 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2486 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2487 geometry->add_property("edit-vertical-pane-pos", string(buf));
2489 node->add_child_nocopy (*geometry);
2492 maybe_add_mixer_strip_width (*node);
2494 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2495 node->add_property ("zoom-focus", buf);
2496 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2497 node->add_property ("zoom", buf);
2498 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2499 node->add_property ("snap-to", buf);
2500 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2501 node->add_property ("snap-mode", buf);
2503 node->add_property ("edit-point", enum_2_string (_edit_point));
2505 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2506 node->add_property ("playhead", buf);
2507 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2508 node->add_property ("left-frame", buf);
2509 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2510 node->add_property ("y-origin", buf);
2512 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2513 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2514 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2515 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2516 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2517 node->add_property ("mouse-mode", enum2str(mouse_mode));
2518 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2519 node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2521 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2522 if (act) {
2523 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2524 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2527 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2528 if (act) {
2529 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2530 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2533 snprintf (buf, sizeof (buf), "%d", the_notebook.get_current_page ());
2534 node->add_property (X_("editor-list-page"), buf);
2536 return *node;
2541 /** @param y y offset from the top of all trackviews.
2542 * @return pair: TimeAxisView that y is over, layer index.
2543 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2544 * in stacked region display mode, otherwise 0.
2546 std::pair<TimeAxisView *, layer_t>
2547 Editor::trackview_by_y_position (double y)
2549 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2551 std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
2552 if (r.first) {
2553 return r;
2557 return std::make_pair ( (TimeAxisView *) 0, 0);
2560 /** Snap a position to the grid, if appropriate, taking into account current
2561 * grid settings and also the state of any snap modifier keys that may be pressed.
2562 * @param start Position to snap.
2563 * @param event Event to get current key modifier information from, or 0.
2565 void
2566 Editor::snap_to_with_modifier (nframes64_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2568 if (!_session || !event) {
2569 return;
2572 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2573 if (_snap_mode == SnapOff) {
2574 snap_to_internal (start, direction, for_mark);
2576 } else {
2577 if (_snap_mode != SnapOff) {
2578 snap_to_internal (start, direction, for_mark);
2583 void
2584 Editor::snap_to (nframes64_t& start, int32_t direction, bool for_mark)
2586 if (!_session || _snap_mode == SnapOff) {
2587 return;
2590 snap_to_internal (start, direction, for_mark);
2593 void
2594 Editor::timecode_snap_to_internal (nframes64_t& start, int32_t direction, bool /*for_mark*/)
2596 const nframes64_t one_timecode_second = (nframes64_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2597 nframes64_t one_timecode_minute = (nframes64_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2599 switch (_snap_type) {
2600 case SnapToTimecodeFrame:
2601 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2602 start = (nframes64_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2603 } else {
2604 start = (nframes64_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2606 break;
2608 case SnapToTimecodeSeconds:
2609 if (_session->timecode_offset_negative())
2611 start += _session->timecode_offset ();
2612 } else {
2613 start -= _session->timecode_offset ();
2615 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2616 start = (nframes64_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2617 } else {
2618 start = (nframes64_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2621 if (_session->timecode_offset_negative())
2623 start -= _session->timecode_offset ();
2624 } else {
2625 start += _session->timecode_offset ();
2627 break;
2629 case SnapToTimecodeMinutes:
2630 if (_session->timecode_offset_negative())
2632 start += _session->timecode_offset ();
2633 } else {
2634 start -= _session->timecode_offset ();
2636 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2637 start = (nframes64_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2638 } else {
2639 start = (nframes64_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2641 if (_session->timecode_offset_negative())
2643 start -= _session->timecode_offset ();
2644 } else {
2645 start += _session->timecode_offset ();
2647 break;
2648 default:
2649 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2650 /*NOTREACHED*/
2654 void
2655 Editor::snap_to_internal (nframes64_t& start, int32_t direction, bool for_mark)
2657 const nframes64_t one_second = _session->frame_rate();
2658 const nframes64_t one_minute = _session->frame_rate() * 60;
2659 nframes64_t presnap = start;
2660 nframes64_t before;
2661 nframes64_t after;
2663 switch (_snap_type) {
2664 case SnapToTimecodeFrame:
2665 case SnapToTimecodeSeconds:
2666 case SnapToTimecodeMinutes:
2667 return timecode_snap_to_internal (start, direction, for_mark);
2669 case SnapToCDFrame:
2670 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2671 start = (nframes64_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2672 } else {
2673 start = (nframes64_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2675 break;
2677 case SnapToSeconds:
2678 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2679 start = (nframes64_t) ceil ((double) start / one_second) * one_second;
2680 } else {
2681 start = (nframes64_t) floor ((double) start / one_second) * one_second;
2683 break;
2685 case SnapToMinutes:
2686 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2687 start = (nframes64_t) ceil ((double) start / one_minute) * one_minute;
2688 } else {
2689 start = (nframes64_t) floor ((double) start / one_minute) * one_minute;
2691 break;
2693 case SnapToBar:
2694 start = _session->tempo_map().round_to_bar (start, direction);
2695 break;
2697 case SnapToBeat:
2698 start = _session->tempo_map().round_to_beat (start, direction);
2699 break;
2701 case SnapToBeatDiv32:
2702 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2703 break;
2704 case SnapToBeatDiv28:
2705 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2706 break;
2707 case SnapToBeatDiv24:
2708 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2709 break;
2710 case SnapToBeatDiv16:
2711 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2712 break;
2713 case SnapToBeatDiv14:
2714 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2715 break;
2716 case SnapToBeatDiv12:
2717 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2718 break;
2719 case SnapToBeatDiv10:
2720 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2721 break;
2722 case SnapToBeatDiv8:
2723 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2724 break;
2725 case SnapToBeatDiv7:
2726 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2727 break;
2728 case SnapToBeatDiv6:
2729 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2730 break;
2731 case SnapToBeatDiv5:
2732 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2733 break;
2734 case SnapToBeatDiv4:
2735 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2736 break;
2737 case SnapToBeatDiv3:
2738 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2739 break;
2740 case SnapToBeatDiv2:
2741 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2742 break;
2744 case SnapToMark:
2745 if (for_mark) {
2746 return;
2749 _session->locations()->marks_either_side (start, before, after);
2751 if (before == max_frames) {
2752 start = after;
2753 } else if (after == max_frames) {
2754 start = before;
2755 } else if (before != max_frames && after != max_frames) {
2756 /* have before and after */
2757 if ((start - before) < (after - start)) {
2758 start = before;
2759 } else {
2760 start = after;
2764 break;
2766 case SnapToRegionStart:
2767 case SnapToRegionEnd:
2768 case SnapToRegionSync:
2769 case SnapToRegionBoundary:
2770 if (!region_boundary_cache.empty()) {
2772 vector<nframes64_t>::iterator prev = region_boundary_cache.end ();
2773 vector<nframes64_t>::iterator next = region_boundary_cache.end ();
2775 if (direction > 0) {
2776 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2777 } else {
2778 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2781 if (next != region_boundary_cache.begin ()) {
2782 prev = next;
2783 prev--;
2786 nframes64_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2787 nframes64_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2789 if (start > (p + n) / 2) {
2790 start = n;
2791 } else {
2792 start = p;
2795 break;
2798 switch (_snap_mode) {
2799 case SnapNormal:
2800 return;
2802 case SnapMagnetic:
2804 if (presnap > start) {
2805 if (presnap > (start + unit_to_frame(snap_threshold))) {
2806 start = presnap;
2809 } else if (presnap < start) {
2810 if (presnap < (start - unit_to_frame(snap_threshold))) {
2811 start = presnap;
2815 default:
2816 /* handled at entry */
2817 return;
2823 void
2824 Editor::setup_toolbar ()
2826 string pixmap_path;
2828 /* Mode Buttons (tool selection) */
2830 mouse_move_button.set_relief(Gtk::RELIEF_NONE);
2831 mouse_select_button.set_relief(Gtk::RELIEF_NONE);
2832 mouse_gain_button.set_relief(Gtk::RELIEF_NONE);
2833 mouse_zoom_button.set_relief(Gtk::RELIEF_NONE);
2834 mouse_timefx_button.set_relief(Gtk::RELIEF_NONE);
2835 mouse_audition_button.set_relief(Gtk::RELIEF_NONE);
2836 // internal_edit_button.set_relief(Gtk::RELIEF_NONE);
2837 join_object_range_button.set_relief(Gtk::RELIEF_NONE);
2839 HBox* mode_box = manage(new HBox);
2840 mode_box->set_border_width (2);
2841 mode_box->set_spacing(4);
2843 /* table containing mode buttons */
2845 HBox* mouse_mode_button_box = manage (new HBox ());
2847 if (Profile->get_sae()) {
2848 mouse_mode_button_box->pack_start (mouse_move_button);
2849 } else {
2850 mouse_mode_button_box->pack_start (mouse_move_button);
2851 mouse_mode_button_box->pack_start (join_object_range_button);
2852 mouse_mode_button_box->pack_start (mouse_select_button);
2855 mouse_mode_button_box->pack_start (mouse_zoom_button);
2857 if (!Profile->get_sae()) {
2858 mouse_mode_button_box->pack_start (mouse_gain_button);
2861 mouse_mode_button_box->pack_start (mouse_timefx_button);
2862 mouse_mode_button_box->pack_start (mouse_audition_button);
2863 mouse_mode_button_box->pack_start (internal_edit_button);
2865 vector<string> edit_mode_strings;
2866 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2867 if (!Profile->get_sae()) {
2868 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2870 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2872 edit_mode_selector.set_name ("EditModeSelector");
2873 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2874 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2876 mode_box->pack_start (edit_mode_selector);
2877 mode_box->pack_start (*mouse_mode_button_box);
2879 _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2880 _mouse_mode_tearoff->set_name ("MouseModeBase");
2881 _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2883 if (Profile->get_sae()) {
2884 _mouse_mode_tearoff->set_can_be_torn_off (false);
2887 _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2888 &_mouse_mode_tearoff->tearoff_window()));
2889 _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2890 &_mouse_mode_tearoff->tearoff_window(), 1));
2891 _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2892 &_mouse_mode_tearoff->tearoff_window()));
2893 _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2894 &_mouse_mode_tearoff->tearoff_window(), 1));
2896 mouse_move_button.set_mode (false);
2897 mouse_select_button.set_mode (false);
2898 mouse_gain_button.set_mode (false);
2899 mouse_zoom_button.set_mode (false);
2900 mouse_timefx_button.set_mode (false);
2901 mouse_audition_button.set_mode (false);
2902 join_object_range_button.set_mode (false);
2904 mouse_move_button.set_name ("MouseModeButton");
2905 mouse_select_button.set_name ("MouseModeButton");
2906 mouse_gain_button.set_name ("MouseModeButton");
2907 mouse_zoom_button.set_name ("MouseModeButton");
2908 mouse_timefx_button.set_name ("MouseModeButton");
2909 mouse_audition_button.set_name ("MouseModeButton");
2910 internal_edit_button.set_name ("MouseModeButton");
2911 join_object_range_button.set_name ("MouseModeButton");
2913 mouse_move_button.unset_flags (CAN_FOCUS);
2914 mouse_select_button.unset_flags (CAN_FOCUS);
2915 mouse_gain_button.unset_flags (CAN_FOCUS);
2916 mouse_zoom_button.unset_flags (CAN_FOCUS);
2917 mouse_timefx_button.unset_flags (CAN_FOCUS);
2918 mouse_audition_button.unset_flags (CAN_FOCUS);
2919 internal_edit_button.unset_flags (CAN_FOCUS);
2920 join_object_range_button.unset_flags (CAN_FOCUS);
2922 /* Zoom */
2924 zoom_box.set_spacing (1);
2925 zoom_box.set_border_width (0);
2927 zoom_in_button.set_name ("EditorTimeButton");
2928 zoom_in_button.set_image (*(manage (new Image (Stock::ZOOM_IN, Gtk::ICON_SIZE_MENU))));
2929 zoom_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), false));
2931 zoom_out_button.set_name ("EditorTimeButton");
2932 zoom_out_button.set_image (*(manage (new Image (Stock::ZOOM_OUT, Gtk::ICON_SIZE_MENU))));
2933 zoom_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), true));
2935 zoom_out_full_button.set_name ("EditorTimeButton");
2936 zoom_out_full_button.set_image (*(manage (new Image (Stock::ZOOM_100, Gtk::ICON_SIZE_MENU))));
2937 zoom_out_full_button.signal_clicked().connect (sigc::mem_fun(*this, &Editor::temporal_zoom_session));
2939 zoom_focus_selector.set_name ("ZoomFocusSelector");
2940 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
2941 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2943 zoom_box.pack_start (zoom_out_button, false, false);
2944 zoom_box.pack_start (zoom_in_button, false, false);
2945 zoom_box.pack_start (zoom_out_full_button, false, false);
2947 /* Track zoom buttons */
2948 tav_expand_button.set_name ("TrackHeightButton");
2949 tav_expand_button.set_size_request(-1,20);
2950 tav_expand_button.add (*(manage (new Image (::get_icon("tav_exp")))));
2951 tav_expand_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), true));
2953 tav_shrink_button.set_name ("TrackHeightButton");
2954 tav_shrink_button.set_size_request(-1,20);
2955 tav_shrink_button.add (*(manage (new Image (::get_icon("tav_shrink")))));
2956 tav_shrink_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), false));
2958 track_zoom_box.set_spacing (1);
2959 track_zoom_box.set_border_width (0);
2961 track_zoom_box.pack_start (tav_shrink_button, false, false);
2962 track_zoom_box.pack_start (tav_expand_button, false, false);
2964 HBox* zbc = manage (new HBox);
2965 zbc->pack_start (zoom_focus_selector, PACK_SHRINK);
2966 zoom_vbox.pack_start (*zbc, PACK_SHRINK);
2967 zoom_vbox.pack_start (zoom_box, PACK_SHRINK);
2968 zoom_vbox.pack_start (track_zoom_box, PACK_SHRINK);
2970 snap_box.set_spacing (1);
2971 snap_box.set_border_width (2);
2973 snap_type_selector.set_name ("SnapTypeSelector");
2974 set_popdown_strings (snap_type_selector, snap_type_strings, true);
2975 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2977 snap_mode_selector.set_name ("SnapModeSelector");
2978 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
2979 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2981 edit_point_selector.set_name ("EditPointSelector");
2982 set_popdown_strings (edit_point_selector, edit_point_strings, true);
2983 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2985 snap_box.pack_start (snap_mode_selector, false, false);
2986 snap_box.pack_start (snap_type_selector, false, false);
2987 snap_box.pack_start (edit_point_selector, false, false);
2989 /* Nudge */
2991 HBox *nudge_box = manage (new HBox);
2992 nudge_box->set_spacing(1);
2993 nudge_box->set_border_width (2);
2995 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2996 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2998 nudge_box->pack_start (nudge_backward_button, false, false);
2999 nudge_box->pack_start (nudge_forward_button, false, false);
3000 nudge_box->pack_start (nudge_clock, false, false);
3003 /* Pack everything in... */
3005 HBox* hbox = manage (new HBox);
3006 hbox->set_spacing(10);
3008 _tools_tearoff = manage (new TearOff (*hbox));
3009 _tools_tearoff->set_name ("MouseModeBase");
3010 _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
3012 if (Profile->get_sae()) {
3013 _tools_tearoff->set_can_be_torn_off (false);
3016 _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
3017 &_tools_tearoff->tearoff_window()));
3018 _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
3019 &_tools_tearoff->tearoff_window(), 0));
3020 _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
3021 &_tools_tearoff->tearoff_window()));
3022 _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
3023 &_tools_tearoff->tearoff_window(), 0));
3025 toolbar_hbox.set_spacing (10);
3026 toolbar_hbox.set_border_width (1);
3028 toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
3029 toolbar_hbox.pack_start (*_tools_tearoff, false, false);
3031 hbox->pack_start (snap_box, false, false);
3032 hbox->pack_start (*nudge_box, false, false);
3033 hbox->pack_start (panic_box, false, false);
3035 hbox->show_all ();
3037 toolbar_base.set_name ("ToolBarBase");
3038 toolbar_base.add (toolbar_hbox);
3040 toolbar_frame.set_shadow_type (SHADOW_OUT);
3041 toolbar_frame.set_name ("BaseFrame");
3042 toolbar_frame.add (toolbar_base);
3045 void
3046 Editor::setup_tooltips ()
3048 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
3049 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Gain Automation"));
3050 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
3051 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions"));
3052 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
3053 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
3054 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
3055 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: context-click for possible operations"));
3056 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
3057 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
3058 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
3059 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
3060 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
3061 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
3062 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
3063 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
3064 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
3065 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
3066 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
3067 ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
3068 ARDOUR_UI::instance()->set_tip (midi_panic_button, _("Send note off and reset controller messages on all MIDI channels"));
3071 void
3072 Editor::midi_panic ()
3074 cerr << "MIDI panic\n";
3076 if (_session) {
3077 _session->midi_panic();
3081 void
3082 Editor::setup_midi_toolbar ()
3084 RefPtr<Action> act;
3086 /* Midi sound notes */
3087 midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
3088 midi_sound_notes.set_relief(Gtk::RELIEF_NONE);
3089 midi_sound_notes.unset_flags (CAN_FOCUS);
3091 /* Panic */
3093 act = ActionManager::get_action (X_("MIDI"), X_("panic"));
3094 midi_panic_button.set_name("MidiPanicButton");
3095 act->connect_proxy (midi_panic_button);
3097 panic_box.pack_start (midi_sound_notes , true, true);
3098 panic_box.pack_start (midi_panic_button, true, true);
3102 Editor::convert_drop_to_paths (
3103 vector<ustring>& paths,
3104 const RefPtr<Gdk::DragContext>& /*context*/,
3105 gint /*x*/,
3106 gint /*y*/,
3107 const SelectionData& data,
3108 guint /*info*/,
3109 guint /*time*/)
3111 if (_session == 0) {
3112 return -1;
3115 vector<ustring> uris = data.get_uris();
3117 if (uris.empty()) {
3119 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3120 are actually URI lists. So do it by hand.
3123 if (data.get_target() != "text/plain") {
3124 return -1;
3127 /* Parse the "uri-list" format that Nautilus provides,
3128 where each pathname is delimited by \r\n.
3130 THERE MAY BE NO NULL TERMINATING CHAR!!!
3133 ustring txt = data.get_text();
3134 const char* p;
3135 const char* q;
3137 p = (const char *) malloc (txt.length() + 1);
3138 txt.copy ((char *) p, txt.length(), 0);
3139 ((char*)p)[txt.length()] = '\0';
3141 while (p)
3143 if (*p != '#')
3145 while (g_ascii_isspace (*p))
3146 p++;
3148 q = p;
3149 while (*q && (*q != '\n') && (*q != '\r')) {
3150 q++;
3153 if (q > p)
3155 q--;
3156 while (q > p && g_ascii_isspace (*q))
3157 q--;
3159 if (q > p)
3161 uris.push_back (ustring (p, q - p + 1));
3165 p = strchr (p, '\n');
3166 if (p)
3167 p++;
3170 free ((void*)p);
3172 if (uris.empty()) {
3173 return -1;
3177 for (vector<ustring>::iterator i = uris.begin(); i != uris.end(); ++i) {
3179 if ((*i).substr (0,7) == "file://") {
3181 ustring p = *i;
3182 PBD::url_decode (p);
3184 // scan forward past three slashes
3186 ustring::size_type slashcnt = 0;
3187 ustring::size_type n = 0;
3188 ustring::iterator x = p.begin();
3190 while (slashcnt < 3 && x != p.end()) {
3191 if ((*x) == '/') {
3192 slashcnt++;
3193 } else if (slashcnt == 3) {
3194 break;
3196 ++n;
3197 ++x;
3200 if (slashcnt != 3 || x == p.end()) {
3201 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3202 continue;
3205 paths.push_back (p.substr (n - 1));
3209 return 0;
3212 void
3213 Editor::new_tempo_section ()
3218 void
3219 Editor::map_transport_state ()
3221 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3223 if (_session && _session->transport_stopped()) {
3224 have_pending_keyboard_selection = false;
3227 update_loop_range_view (true);
3230 /* UNDO/REDO */
3232 Editor::State::State (PublicEditor const * e)
3234 selection = new Selection (e);
3237 Editor::State::~State ()
3239 delete selection;
3242 void
3243 Editor::begin_reversible_command (string name)
3245 if (_session) {
3246 _session->begin_reversible_command (name);
3250 void
3251 Editor::commit_reversible_command ()
3253 if (_session) {
3254 _session->commit_reversible_command ();
3258 void
3259 Editor::set_route_group_solo (Route& route, bool yn)
3261 RouteGroup *route_group;
3263 if ((route_group = route.route_group()) != 0) {
3264 route_group->apply (&Route::set_solo, yn, this);
3265 } else {
3266 route.set_solo (yn, this);
3270 void
3271 Editor::set_route_group_mute (Route& route, bool yn)
3273 RouteGroup *route_group = 0;
3275 if ((route_group = route.route_group()) != 0) {
3276 route_group->apply (&Route::set_mute, yn, this);
3277 } else {
3278 route.set_mute (yn, this);
3282 void
3283 Editor::history_changed ()
3285 string label;
3287 if (undo_action && _session) {
3288 if (_session->undo_depth() == 0) {
3289 label = _("Undo");
3290 } else {
3291 label = string_compose(_("Undo (%1)"), _session->next_undo());
3293 undo_action->property_label() = label;
3296 if (redo_action && _session) {
3297 if (_session->redo_depth() == 0) {
3298 label = _("Redo");
3299 } else {
3300 label = string_compose(_("Redo (%1)"), _session->next_redo());
3302 redo_action->property_label() = label;
3306 void
3307 Editor::duplicate_dialog (bool with_dialog)
3309 float times = 1.0f;
3311 if (mouse_mode == MouseRange) {
3312 if (selection->time.length() == 0) {
3313 return;
3317 RegionSelection rs;
3318 get_regions_for_action (rs);
3320 if (mouse_mode != MouseRange) {
3322 if (rs.empty()) {
3323 return;
3327 if (with_dialog) {
3329 ArdourDialog win (_("Duplicate"));
3330 Label label (_("Number of duplications:"));
3331 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3332 SpinButton spinner (adjustment, 0.0, 1);
3333 HBox hbox;
3335 win.get_vbox()->set_spacing (12);
3336 win.get_vbox()->pack_start (hbox);
3337 hbox.set_border_width (6);
3338 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3340 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3341 place, visually. so do this by hand.
3344 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3345 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3346 spinner.grab_focus();
3348 hbox.show ();
3349 label.show ();
3350 spinner.show ();
3352 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3353 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3354 win.set_default_response (RESPONSE_ACCEPT);
3356 win.set_position (WIN_POS_MOUSE);
3358 spinner.grab_focus ();
3360 switch (win.run ()) {
3361 case RESPONSE_ACCEPT:
3362 break;
3363 default:
3364 return;
3367 times = adjustment.get_value();
3370 if (mouse_mode == MouseRange) {
3371 duplicate_selection (times);
3372 } else {
3373 duplicate_some_regions (rs, times);
3377 void
3378 Editor::show_verbose_canvas_cursor ()
3380 verbose_canvas_cursor->raise_to_top();
3381 verbose_canvas_cursor->show();
3382 verbose_cursor_visible = true;
3385 void
3386 Editor::hide_verbose_canvas_cursor ()
3388 verbose_canvas_cursor->hide();
3389 verbose_cursor_visible = false;
3392 double
3393 Editor::clamp_verbose_cursor_x (double x)
3395 if (x < 0) {
3396 x = 0;
3397 } else {
3398 x = min (_canvas_width - 200.0, x);
3400 return x;
3403 double
3404 Editor::clamp_verbose_cursor_y (double y)
3406 if (y < canvas_timebars_vsize) {
3407 y = canvas_timebars_vsize;
3408 } else {
3409 y = min (_canvas_height - 50, y);
3411 return y;
3414 void
3415 Editor::show_verbose_canvas_cursor_with (const string & txt)
3417 verbose_canvas_cursor->property_text() = txt.c_str();
3419 int x, y;
3420 double wx, wy;
3422 track_canvas->get_pointer (x, y);
3423 track_canvas->window_to_world (x, y, wx, wy);
3425 /* don't get too close to the edge */
3426 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (wx);
3427 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (wy);
3429 show_verbose_canvas_cursor ();
3432 void
3433 Editor::set_verbose_canvas_cursor (const string & txt, double x, double y)
3435 verbose_canvas_cursor->property_text() = txt.c_str();
3436 /* don't get too close to the edge */
3437 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (x);
3438 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (y);
3441 void
3442 Editor::set_verbose_canvas_cursor_text (const string & txt)
3444 verbose_canvas_cursor->property_text() = txt.c_str();
3447 void
3448 Editor::set_edit_mode (EditMode m)
3450 Config->set_edit_mode (m);
3453 void
3454 Editor::cycle_edit_mode ()
3456 switch (Config->get_edit_mode()) {
3457 case Slide:
3458 if (Profile->get_sae()) {
3459 Config->set_edit_mode (Lock);
3460 } else {
3461 Config->set_edit_mode (Splice);
3463 break;
3464 case Splice:
3465 Config->set_edit_mode (Lock);
3466 break;
3467 case Lock:
3468 Config->set_edit_mode (Slide);
3469 break;
3473 void
3474 Editor::edit_mode_selection_done ()
3476 if (_session == 0) {
3477 return;
3480 string choice = edit_mode_selector.get_active_text();
3481 EditMode mode = Slide;
3483 if (choice == _("Splice Edit")) {
3484 mode = Splice;
3485 } else if (choice == _("Slide Edit")) {
3486 mode = Slide;
3487 } else if (choice == _("Lock Edit")) {
3488 mode = Lock;
3491 Config->set_edit_mode (mode);
3494 void
3495 Editor::snap_type_selection_done ()
3497 string choice = snap_type_selector.get_active_text();
3498 SnapType snaptype = SnapToBeat;
3500 if (choice == _("Beats/2")) {
3501 snaptype = SnapToBeatDiv2;
3502 } else if (choice == _("Beats/3")) {
3503 snaptype = SnapToBeatDiv3;
3504 } else if (choice == _("Beats/4")) {
3505 snaptype = SnapToBeatDiv4;
3506 } else if (choice == _("Beats/5")) {
3507 snaptype = SnapToBeatDiv5;
3508 } else if (choice == _("Beats/6")) {
3509 snaptype = SnapToBeatDiv6;
3510 } else if (choice == _("Beats/7")) {
3511 snaptype = SnapToBeatDiv7;
3512 } else if (choice == _("Beats/8")) {
3513 snaptype = SnapToBeatDiv8;
3514 } else if (choice == _("Beats/10")) {
3515 snaptype = SnapToBeatDiv10;
3516 } else if (choice == _("Beats/12")) {
3517 snaptype = SnapToBeatDiv12;
3518 } else if (choice == _("Beats/14")) {
3519 snaptype = SnapToBeatDiv14;
3520 } else if (choice == _("Beats/16")) {
3521 snaptype = SnapToBeatDiv16;
3522 } else if (choice == _("Beats/24")) {
3523 snaptype = SnapToBeatDiv24;
3524 } else if (choice == _("Beats/28")) {
3525 snaptype = SnapToBeatDiv28;
3526 } else if (choice == _("Beats/32")) {
3527 snaptype = SnapToBeatDiv32;
3528 } else if (choice == _("Beats")) {
3529 snaptype = SnapToBeat;
3530 } else if (choice == _("Bars")) {
3531 snaptype = SnapToBar;
3532 } else if (choice == _("Marks")) {
3533 snaptype = SnapToMark;
3534 } else if (choice == _("Region starts")) {
3535 snaptype = SnapToRegionStart;
3536 } else if (choice == _("Region ends")) {
3537 snaptype = SnapToRegionEnd;
3538 } else if (choice == _("Region bounds")) {
3539 snaptype = SnapToRegionBoundary;
3540 } else if (choice == _("Region syncs")) {
3541 snaptype = SnapToRegionSync;
3542 } else if (choice == _("CD Frames")) {
3543 snaptype = SnapToCDFrame;
3544 } else if (choice == _("Timecode Frames")) {
3545 snaptype = SnapToTimecodeFrame;
3546 } else if (choice == _("Timecode Seconds")) {
3547 snaptype = SnapToTimecodeSeconds;
3548 } else if (choice == _("Timecode Minutes")) {
3549 snaptype = SnapToTimecodeMinutes;
3550 } else if (choice == _("Seconds")) {
3551 snaptype = SnapToSeconds;
3552 } else if (choice == _("Minutes")) {
3553 snaptype = SnapToMinutes;
3556 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3557 if (ract) {
3558 ract->set_active ();
3562 void
3563 Editor::snap_mode_selection_done ()
3565 string choice = snap_mode_selector.get_active_text();
3566 SnapMode mode = SnapNormal;
3568 if (choice == _("No Grid")) {
3569 mode = SnapOff;
3570 } else if (choice == _("Grid")) {
3571 mode = SnapNormal;
3572 } else if (choice == _("Magnetic")) {
3573 mode = SnapMagnetic;
3576 RefPtr<RadioAction> ract = snap_mode_action (mode);
3578 if (ract) {
3579 ract->set_active (true);
3583 void
3584 Editor::cycle_edit_point (bool with_marker)
3586 switch (_edit_point) {
3587 case EditAtMouse:
3588 set_edit_point_preference (EditAtPlayhead);
3589 break;
3590 case EditAtPlayhead:
3591 if (with_marker) {
3592 set_edit_point_preference (EditAtSelectedMarker);
3593 } else {
3594 set_edit_point_preference (EditAtMouse);
3596 break;
3597 case EditAtSelectedMarker:
3598 set_edit_point_preference (EditAtMouse);
3599 break;
3603 void
3604 Editor::edit_point_selection_done ()
3606 string choice = edit_point_selector.get_active_text();
3607 EditPoint ep = EditAtSelectedMarker;
3609 if (choice == _("Marker")) {
3610 set_edit_point_preference (EditAtSelectedMarker);
3611 } else if (choice == _("Playhead")) {
3612 set_edit_point_preference (EditAtPlayhead);
3613 } else {
3614 set_edit_point_preference (EditAtMouse);
3617 RefPtr<RadioAction> ract = edit_point_action (ep);
3619 if (ract) {
3620 ract->set_active (true);
3624 void
3625 Editor::zoom_focus_selection_done ()
3627 string choice = zoom_focus_selector.get_active_text();
3628 ZoomFocus focus_type = ZoomFocusLeft;
3630 if (choice == _("Left")) {
3631 focus_type = ZoomFocusLeft;
3632 } else if (choice == _("Right")) {
3633 focus_type = ZoomFocusRight;
3634 } else if (choice == _("Center")) {
3635 focus_type = ZoomFocusCenter;
3636 } else if (choice == _("Playhead")) {
3637 focus_type = ZoomFocusPlayhead;
3638 } else if (choice == _("Mouse")) {
3639 focus_type = ZoomFocusMouse;
3640 } else if (choice == _("Edit point")) {
3641 focus_type = ZoomFocusEdit;
3644 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3646 if (ract) {
3647 ract->set_active ();
3651 gint
3652 Editor::edit_controls_button_release (GdkEventButton* ev)
3654 if (Keyboard::is_context_menu_event (ev)) {
3655 ARDOUR_UI::instance()->add_route (this);
3657 return TRUE;
3660 gint
3661 Editor::mouse_select_button_release (GdkEventButton* ev)
3663 /* this handles just right-clicks */
3665 if (ev->button != 3) {
3666 return false;
3669 return true;
3672 void
3673 Editor::set_zoom_focus (ZoomFocus f)
3675 string str = zoom_focus_strings[(int)f];
3677 if (str != zoom_focus_selector.get_active_text()) {
3678 zoom_focus_selector.set_active_text (str);
3681 if (zoom_focus != f) {
3682 zoom_focus = f;
3684 ZoomFocusChanged (); /* EMIT_SIGNAL */
3686 instant_save ();
3690 void
3691 Editor::ensure_float (Window& win)
3693 win.set_transient_for (*this);
3696 void
3697 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3699 /* recover or initialize pane positions. do this here rather than earlier because
3700 we don't want the positions to change the child allocations, which they seem to do.
3703 int pos;
3704 XMLProperty* prop;
3705 char buf[32];
3706 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3707 int width, height;
3709 enum Pane {
3710 Horizontal = 0x1,
3711 Vertical = 0x2
3714 static Pane done;
3716 XMLNode* geometry;
3718 width = default_width;
3719 height = default_height;
3721 if ((geometry = find_named_node (*node, "geometry")) != 0) {
3723 prop = geometry->property ("x-size");
3724 if (prop) {
3725 width = atoi (prop->value());
3727 prop = geometry->property ("y-size");
3728 if (prop) {
3729 height = atoi (prop->value());
3733 if (which == static_cast<Paned*> (&edit_pane)) {
3735 if (done & Horizontal) {
3736 return;
3739 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3740 /* initial allocation is 90% to canvas, 10% to notebook */
3741 pos = (int) floor (alloc.get_width() * 0.90f);
3742 snprintf (buf, sizeof(buf), "%d", pos);
3743 } else {
3744 pos = atoi (prop->value());
3747 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3748 edit_pane.set_position (pos);
3749 pre_maximal_horizontal_pane_position = pos;
3752 done = (Pane) (done | Horizontal);
3754 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3756 if (done & Vertical) {
3757 return;
3760 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3761 /* initial allocation is 90% to canvas, 10% to summary */
3762 pos = (int) floor (alloc.get_height() * 0.90f);
3763 snprintf (buf, sizeof(buf), "%d", pos);
3764 } else {
3765 pos = atoi (prop->value());
3768 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3769 editor_summary_pane.set_position (pos);
3770 pre_maximal_vertical_pane_position = pos;
3773 done = (Pane) (done | Vertical);
3777 void
3778 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3780 if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) {
3781 top_hbox.remove (toolbar_frame);
3785 void
3786 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3788 if (toolbar_frame.get_parent() == 0) {
3789 top_hbox.pack_end (toolbar_frame);
3793 void
3794 Editor::set_show_measures (bool yn)
3796 if (_show_measures != yn) {
3797 hide_measures ();
3799 if ((_show_measures = yn) == true) {
3800 if (tempo_lines)
3801 tempo_lines->show();
3802 draw_measures ();
3804 instant_save ();
3808 void
3809 Editor::toggle_follow_playhead ()
3811 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3812 if (act) {
3813 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3814 set_follow_playhead (tact->get_active());
3818 void
3819 Editor::set_follow_playhead (bool yn)
3821 if (_follow_playhead != yn) {
3822 if ((_follow_playhead = yn) == true) {
3823 /* catch up */
3824 reset_x_origin_to_follow_playhead ();
3826 instant_save ();
3830 void
3831 Editor::toggle_stationary_playhead ()
3833 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3834 if (act) {
3835 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3836 set_stationary_playhead (tact->get_active());
3840 void
3841 Editor::set_stationary_playhead (bool yn)
3843 if (_stationary_playhead != yn) {
3844 if ((_stationary_playhead = yn) == true) {
3845 /* catch up */
3846 // FIXME need a 3.0 equivalent of this 2.X call
3847 // update_current_screen ();
3849 instant_save ();
3853 void
3854 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3856 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3857 if (xfade) {
3858 xfade->set_active (!xfade->active());
3862 void
3863 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3865 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3866 if (xfade) {
3867 xfade->set_follow_overlap (!xfade->following_overlap());
3871 void
3872 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3874 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3876 if (!xfade) {
3877 return;
3880 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3882 ensure_float (cew);
3884 switch (cew.run ()) {
3885 case RESPONSE_ACCEPT:
3886 break;
3887 default:
3888 return;
3891 cew.apply ();
3892 PropertyChange all_crossfade_properties;
3893 all_crossfade_properties.add (ARDOUR::Properties::active);
3894 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3895 xfade->PropertyChanged (all_crossfade_properties);
3898 PlaylistSelector&
3899 Editor::playlist_selector () const
3901 return *_playlist_selector;
3904 Evoral::MusicalTime
3905 Editor::get_grid_type_as_beats (bool& success, nframes64_t position)
3907 success = true;
3909 switch (_snap_type) {
3910 case SnapToBeat:
3911 return 1.0;
3912 break;
3914 case SnapToBeatDiv32:
3915 return 1.0/32.0;
3916 break;
3917 case SnapToBeatDiv28:
3918 return 1.0/28.0;
3919 break;
3920 case SnapToBeatDiv24:
3921 return 1.0/24.0;
3922 break;
3923 case SnapToBeatDiv16:
3924 return 1.0/16.0;
3925 break;
3926 case SnapToBeatDiv14:
3927 return 1.0/14.0;
3928 break;
3929 case SnapToBeatDiv12:
3930 return 1.0/12.0;
3931 break;
3932 case SnapToBeatDiv10:
3933 return 1.0/10.0;
3934 break;
3935 case SnapToBeatDiv8:
3936 return 1.0/8.0;
3937 break;
3938 case SnapToBeatDiv7:
3939 return 1.0/7.0;
3940 break;
3941 case SnapToBeatDiv6:
3942 return 1.0/6.0;
3943 break;
3944 case SnapToBeatDiv5:
3945 return 1.0/5.0;
3946 break;
3947 case SnapToBeatDiv4:
3948 return 1.0/4.0;
3949 break;
3950 case SnapToBeatDiv3:
3951 return 1.0/3.0;
3952 break;
3953 case SnapToBeatDiv2:
3954 return 1.0/2.0;
3955 break;
3957 case SnapToBar:
3958 if (_session) {
3959 return _session->tempo_map().meter_at (position).beats_per_bar();
3961 break;
3963 case SnapToCDFrame:
3964 case SnapToTimecodeFrame:
3965 case SnapToTimecodeSeconds:
3966 case SnapToTimecodeMinutes:
3967 case SnapToSeconds:
3968 case SnapToMinutes:
3969 case SnapToRegionStart:
3970 case SnapToRegionEnd:
3971 case SnapToRegionSync:
3972 case SnapToRegionBoundary:
3973 default:
3974 success = false;
3975 break;
3978 return 0.0;
3981 nframes64_t
3982 Editor::get_nudge_distance (nframes64_t pos, nframes64_t& next)
3984 nframes64_t ret;
3986 ret = nudge_clock.current_duration (pos);
3987 next = ret + 1; /* XXXX fix me */
3989 return ret;
3993 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3995 ArdourDialog dialog (_("Playlist Deletion"));
3996 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3997 "If left alone, no audio files used by it will be cleaned.\n"
3998 "If deleted, audio files used by it alone by will cleaned."),
3999 pl->name()));
4001 dialog.set_position (WIN_POS_CENTER);
4002 dialog.get_vbox()->pack_start (label);
4004 label.show ();
4006 dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT);
4007 dialog.add_button (_("Keep playlist"), RESPONSE_REJECT);
4008 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
4010 switch (dialog.run ()) {
4011 case RESPONSE_ACCEPT:
4012 /* delete the playlist */
4013 return 0;
4014 break;
4016 case RESPONSE_REJECT:
4017 /* keep the playlist */
4018 return 1;
4019 break;
4021 default:
4022 break;
4025 return -1;
4028 bool
4029 Editor::audio_region_selection_covers (nframes64_t where)
4031 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
4032 if ((*a)->region()->covers (where)) {
4033 return true;
4037 return false;
4040 void
4041 Editor::prepare_for_cleanup ()
4043 cut_buffer->clear_regions ();
4044 cut_buffer->clear_playlists ();
4046 selection->clear_regions ();
4047 selection->clear_playlists ();
4049 _regions->suspend_redisplay ();
4052 void
4053 Editor::finish_cleanup ()
4055 _regions->resume_redisplay ();
4058 Location*
4059 Editor::transport_loop_location()
4061 if (_session) {
4062 return _session->locations()->auto_loop_location();
4063 } else {
4064 return 0;
4068 Location*
4069 Editor::transport_punch_location()
4071 if (_session) {
4072 return _session->locations()->auto_punch_location();
4073 } else {
4074 return 0;
4078 bool
4079 Editor::control_layout_scroll (GdkEventScroll* ev)
4081 if (Keyboard::some_magic_widget_has_focus()) {
4082 return false;
4085 switch (ev->direction) {
4086 case GDK_SCROLL_UP:
4087 scroll_tracks_up_line ();
4088 return true;
4089 break;
4091 case GDK_SCROLL_DOWN:
4092 scroll_tracks_down_line ();
4093 return true;
4095 default:
4096 /* no left/right handling yet */
4097 break;
4100 return false;
4103 void
4104 Editor::session_state_saved (string snap_name)
4106 ENSURE_GUI_THREAD (*this, &Editor::session_state_saved, snap_name);
4108 update_title ();
4109 _snapshots->redisplay ();
4112 void
4113 Editor::maximise_editing_space ()
4115 _mouse_mode_tearoff->set_visible (false);
4116 _tools_tearoff->set_visible (false);
4118 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
4119 pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
4120 pre_maximal_editor_width = this->get_width ();
4121 pre_maximal_editor_height = this->get_height ();
4123 if (post_maximal_horizontal_pane_position == 0) {
4124 post_maximal_horizontal_pane_position = edit_pane.get_width();
4127 if (post_maximal_vertical_pane_position == 0) {
4128 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
4131 fullscreen ();
4133 if (post_maximal_editor_width) {
4134 edit_pane.set_position (post_maximal_horizontal_pane_position -
4135 abs(post_maximal_editor_width - pre_maximal_editor_width));
4136 } else {
4137 edit_pane.set_position (post_maximal_horizontal_pane_position);
4140 if (post_maximal_editor_height) {
4141 editor_summary_pane.set_position (post_maximal_vertical_pane_position -
4142 abs(post_maximal_editor_height - pre_maximal_editor_height));
4143 } else {
4144 editor_summary_pane.set_position (post_maximal_vertical_pane_position);
4148 void
4149 Editor::restore_editing_space ()
4151 // user changed width/height of panes during fullscreen
4153 if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
4154 post_maximal_horizontal_pane_position = edit_pane.get_position();
4157 if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
4158 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
4161 unfullscreen();
4163 _mouse_mode_tearoff->set_visible (true);
4164 _tools_tearoff->set_visible (true);
4165 post_maximal_editor_width = this->get_width();
4166 post_maximal_editor_height = this->get_height();
4168 edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
4169 editor_summary_pane.set_position (pre_maximal_vertical_pane_position + abs(this->get_height() - pre_maximal_editor_height));
4173 * Make new playlists for a given track and also any others that belong
4174 * to the same active route group with the `edit' property.
4175 * @param v Track.
4178 void
4179 Editor::new_playlists (TimeAxisView* v)
4181 begin_reversible_command (_("new playlists"));
4182 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4183 _session->playlists->get (playlists);
4184 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4185 commit_reversible_command ();
4189 * Use a copy of the current playlist for a given track and also any others that belong
4190 * to the same active route group with the `edit' property.
4191 * @param v Track.
4194 void
4195 Editor::copy_playlists (TimeAxisView* v)
4197 begin_reversible_command (_("copy playlists"));
4198 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4199 _session->playlists->get (playlists);
4200 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4201 commit_reversible_command ();
4204 /** Clear the current playlist for a given track and also any others that belong
4205 * to the same active route group with the `edit' property.
4206 * @param v Track.
4209 void
4210 Editor::clear_playlists (TimeAxisView* v)
4212 begin_reversible_command (_("clear playlists"));
4213 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4214 _session->playlists->get (playlists);
4215 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4216 commit_reversible_command ();
4219 void
4220 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4222 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4225 void
4226 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4228 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4231 void
4232 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4234 atv.clear_playlist ();
4237 bool
4238 Editor::on_key_press_event (GdkEventKey* ev)
4240 return key_press_focus_accelerator_handler (*this, ev);
4243 bool
4244 Editor::on_key_release_event (GdkEventKey* ev)
4246 return Gtk::Window::on_key_release_event (ev);
4247 // return key_press_focus_accelerator_handler (*this, ev);
4250 /** Queue up a change to the viewport x origin.
4251 * @param frame New x origin.
4253 void
4254 Editor::reset_x_origin (nframes64_t frame)
4256 queue_visual_change (frame);
4259 void
4260 Editor::reset_y_origin (double y)
4262 queue_visual_change_y (y);
4265 void
4266 Editor::reset_zoom (double fpu)
4268 queue_visual_change (fpu);
4271 void
4272 Editor::reposition_and_zoom (nframes64_t frame, double fpu)
4274 reset_x_origin (frame);
4275 reset_zoom (fpu);
4277 if (!no_save_visual) {
4278 undo_visual_stack.push_back (current_visual_state(false));
4282 Editor::VisualState*
4283 Editor::current_visual_state (bool with_tracks)
4285 VisualState* vs = new VisualState;
4286 vs->y_position = vertical_adjustment.get_value();
4287 vs->frames_per_unit = frames_per_unit;
4288 vs->leftmost_frame = leftmost_frame;
4289 vs->zoom_focus = zoom_focus;
4291 if (with_tracks) {
4292 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4293 vs->track_states.push_back (TAVState ((*i), &(*i)->get_state()));
4297 return vs;
4300 void
4301 Editor::undo_visual_state ()
4303 if (undo_visual_stack.empty()) {
4304 return;
4307 redo_visual_stack.push_back (current_visual_state());
4309 VisualState* vs = undo_visual_stack.back();
4310 undo_visual_stack.pop_back();
4311 use_visual_state (*vs);
4314 void
4315 Editor::redo_visual_state ()
4317 if (redo_visual_stack.empty()) {
4318 return;
4321 undo_visual_stack.push_back (current_visual_state());
4323 VisualState* vs = redo_visual_stack.back();
4324 redo_visual_stack.pop_back();
4325 use_visual_state (*vs);
4328 void
4329 Editor::swap_visual_state ()
4331 if (undo_visual_stack.empty()) {
4332 redo_visual_state ();
4333 } else {
4334 undo_visual_state ();
4338 void
4339 Editor::use_visual_state (VisualState& vs)
4341 no_save_visual = true;
4343 _routes->suspend_redisplay ();
4345 vertical_adjustment.set_value (vs.y_position);
4347 set_zoom_focus (vs.zoom_focus);
4348 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4350 for (list<TAVState>::iterator i = vs.track_states.begin(); i != vs.track_states.end(); ++i) {
4351 TrackViewList::iterator t;
4353 /* check if the track still exists - it could have been deleted */
4355 if ((t = find (track_views.begin(), track_views.end(), i->first)) != track_views.end()) {
4356 (*t)->set_state (*(i->second), Stateful::loading_state_version);
4361 if (!vs.track_states.empty()) {
4362 _routes->update_visibility ();
4365 _routes->resume_redisplay ();
4367 no_save_visual = false;
4370 void
4371 Editor::set_frames_per_unit (double fpu)
4373 /* this is the core function that controls the zoom level of the canvas. it is called
4374 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4377 if (fpu == frames_per_unit) {
4378 return;
4381 if (fpu < 2.0) {
4382 fpu = 2.0;
4386 /* don't allow zooms that fit more than the maximum number
4387 of frames into an 800 pixel wide space.
4390 if (max_frames / fpu < 800.0) {
4391 return;
4394 if (tempo_lines)
4395 tempo_lines->tempo_map_changed();
4397 frames_per_unit = fpu;
4398 post_zoom ();
4401 void
4402 Editor::post_zoom ()
4404 // convert fpu to frame count
4406 nframes64_t frames = (nframes64_t) floor (frames_per_unit * _canvas_width);
4408 if (frames_per_unit != zoom_range_clock.current_duration()) {
4409 zoom_range_clock.set (frames);
4412 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4413 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4414 (*i)->reshow_selection (selection->time);
4418 ZoomChanged (); /* EMIT_SIGNAL */
4420 //reset_scrolling_region ();
4422 if (playhead_cursor) {
4423 playhead_cursor->set_position (playhead_cursor->current_frame);
4426 refresh_location_display();
4427 _summary->set_overlays_dirty ();
4429 instant_save ();
4432 void
4433 Editor::queue_visual_change (nframes64_t where)
4435 pending_visual_change.add (VisualChange::TimeOrigin);
4436 pending_visual_change.time_origin = where;
4437 ensure_visual_change_idle_handler ();
4440 void
4441 Editor::queue_visual_change (double fpu)
4443 pending_visual_change.add (VisualChange::ZoomLevel);
4444 pending_visual_change.frames_per_unit = fpu;
4446 ensure_visual_change_idle_handler ();
4449 void
4450 Editor::queue_visual_change_y (double y)
4452 pending_visual_change.add (VisualChange::YOrigin);
4453 pending_visual_change.y_origin = y;
4455 ensure_visual_change_idle_handler ();
4458 void
4459 Editor::ensure_visual_change_idle_handler ()
4461 if (pending_visual_change.idle_handler_id < 0) {
4462 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4467 Editor::_idle_visual_changer (void* arg)
4469 return static_cast<Editor*>(arg)->idle_visual_changer ();
4473 Editor::idle_visual_changer ()
4475 VisualChange::Type p = pending_visual_change.pending;
4476 pending_visual_change.pending = (VisualChange::Type) 0;
4478 double last_time_origin = horizontal_position ();
4480 if (p & VisualChange::ZoomLevel) {
4481 set_frames_per_unit (pending_visual_change.frames_per_unit);
4483 compute_fixed_ruler_scale ();
4484 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4485 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4486 update_tempo_based_rulers ();
4488 if (p & VisualChange::TimeOrigin) {
4489 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4491 if (p & VisualChange::YOrigin) {
4492 vertical_adjustment.set_value (pending_visual_change.y_origin);
4495 if (last_time_origin == horizontal_position ()) {
4496 /* changed signal not emitted */
4497 update_fixed_rulers ();
4498 redisplay_tempo (true);
4501 _summary->set_overlays_dirty ();
4503 pending_visual_change.idle_handler_id = -1;
4504 return 0; /* this is always a one-shot call */
4507 struct EditorOrderTimeAxisSorter {
4508 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4509 return a->order () < b->order ();
4513 void
4514 Editor::sort_track_selection (TrackViewList* sel)
4516 EditorOrderTimeAxisSorter cmp;
4518 if (sel) {
4519 sel->sort (cmp);
4520 } else {
4521 selection->tracks.sort (cmp);
4525 nframes64_t
4526 Editor::get_preferred_edit_position (bool ignore_playhead)
4528 bool ignored;
4529 nframes64_t where = 0;
4530 EditPoint ep = _edit_point;
4532 if (entered_marker) {
4533 return entered_marker->position();
4536 if (ignore_playhead && ep == EditAtPlayhead) {
4537 ep = EditAtSelectedMarker;
4540 switch (ep) {
4541 case EditAtPlayhead:
4542 where = _session->audible_frame();
4543 break;
4545 case EditAtSelectedMarker:
4546 if (!selection->markers.empty()) {
4547 bool is_start;
4548 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4549 if (loc) {
4550 if (is_start) {
4551 where = loc->start();
4552 } else {
4553 where = loc->end();
4555 break;
4558 /* fallthru */
4560 default:
4561 case EditAtMouse:
4562 if (!mouse_frame (where, ignored)) {
4563 /* XXX not right but what can we do ? */
4564 return 0;
4566 snap_to (where);
4567 break;
4570 return where;
4573 void
4574 Editor::set_loop_range (nframes64_t start, nframes64_t end, string cmd)
4576 if (!_session) return;
4578 begin_reversible_command (cmd);
4580 Location* tll;
4582 if ((tll = transport_loop_location()) == 0) {
4583 Location* loc = new Location (start, end, _("Loop"), Location::IsAutoLoop);
4584 XMLNode &before = _session->locations()->get_state();
4585 _session->locations()->add (loc, true);
4586 _session->set_auto_loop_location (loc);
4587 XMLNode &after = _session->locations()->get_state();
4588 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4589 } else {
4590 XMLNode &before = tll->get_state();
4591 tll->set_hidden (false, this);
4592 tll->set (start, end);
4593 XMLNode &after = tll->get_state();
4594 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4597 commit_reversible_command ();
4600 void
4601 Editor::set_punch_range (nframes64_t start, nframes64_t end, string cmd)
4603 if (!_session) return;
4605 begin_reversible_command (cmd);
4607 Location* tpl;
4609 if ((tpl = transport_punch_location()) == 0) {
4610 Location* loc = new Location (start, end, _("Loop"), Location::IsAutoPunch);
4611 XMLNode &before = _session->locations()->get_state();
4612 _session->locations()->add (loc, true);
4613 _session->set_auto_loop_location (loc);
4614 XMLNode &after = _session->locations()->get_state();
4615 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4617 else {
4618 XMLNode &before = tpl->get_state();
4619 tpl->set_hidden (false, this);
4620 tpl->set (start, end);
4621 XMLNode &after = tpl->get_state();
4622 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4625 commit_reversible_command ();
4628 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4629 * @param rs List to which found regions are added.
4630 * @param where Time to look at.
4631 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4633 void
4634 Editor::get_regions_at (RegionSelection& rs, nframes64_t where, const TrackViewList& ts) const
4636 const TrackViewList* tracks;
4638 if (ts.empty()) {
4639 tracks = &track_views;
4640 } else {
4641 tracks = &ts;
4644 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4645 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4646 if (rtv) {
4647 boost::shared_ptr<Track> tr;
4648 boost::shared_ptr<Playlist> pl;
4650 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4652 Playlist::RegionList* regions = pl->regions_at (
4653 (nframes64_t) floor ( (double)where * tr->speed()));
4655 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4656 RegionView* rv = rtv->view()->find_view (*i);
4657 if (rv) {
4658 rs.add (rv);
4662 delete regions;
4668 void
4669 Editor::get_regions_after (RegionSelection& rs, nframes64_t where, const TrackViewList& ts) const
4671 const TrackViewList* tracks;
4673 if (ts.empty()) {
4674 tracks = &track_views;
4675 } else {
4676 tracks = &ts;
4679 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4680 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4681 if (rtv) {
4682 boost::shared_ptr<Track> tr;
4683 boost::shared_ptr<Playlist> pl;
4685 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4687 Playlist::RegionList* regions = pl->regions_touched (
4688 (nframes64_t) floor ( (double)where * tr->speed()), max_frames);
4690 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4692 RegionView* rv = rtv->view()->find_view (*i);
4694 if (rv) {
4695 rs.push_back (rv);
4699 delete regions;
4705 /** Find all regions which are either:
4706 * - selected or
4707 * - the entered_regionview (if allow_entered == true) or
4708 * - under the preferred edit position AND on a selected track, or on a track
4709 * which is in the same active edit-enable route group as a selected region (if allow_edit_position == true)
4710 * @param rs Returned region list.
4711 * @param allow_entered true to include the entered_regionview in the list.
4713 void
4714 Editor::get_regions_for_action (RegionSelection& rs, bool allow_entered, bool allow_edit_position)
4716 /* Start with selected regions */
4717 rs = selection->regions;
4719 /* Add the entered_regionview, if requested */
4720 if (allow_entered && entered_regionview) {
4721 rs.add (entered_regionview);
4724 if (allow_edit_position) {
4726 TrackViewList tracks = selection->tracks;
4728 /* tracks is currently the set of selected tracks; add any other tracks that
4729 * have regions that are in the same edit-activated route group as one of
4730 * our regions */
4731 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
4733 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4734 if (g && g->is_active() && g->is_edit()) {
4735 tracks.add (axis_views_from_routes (g->route_list()));
4740 if (!tracks.empty()) {
4741 /* now find regions that are at the edit position on those tracks */
4742 nframes64_t const where = get_preferred_edit_position ();
4743 get_regions_at (rs, where, tracks);
4748 void
4749 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4751 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4753 RouteTimeAxisView* tatv;
4755 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4757 boost::shared_ptr<Playlist> pl;
4758 vector<boost::shared_ptr<Region> > results;
4759 RegionView* marv;
4760 boost::shared_ptr<Track> tr;
4762 if ((tr = tatv->track()) == 0) {
4763 /* bus */
4764 continue;
4767 if ((pl = (tr->playlist())) != 0) {
4768 pl->get_region_list_equivalent_regions (region, results);
4771 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4772 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4773 regions.push_back (marv);
4781 void
4782 Editor::show_rhythm_ferret ()
4784 if (rhythm_ferret == 0) {
4785 rhythm_ferret = new RhythmFerret(*this);
4788 rhythm_ferret->set_session (_session);
4789 rhythm_ferret->show ();
4790 rhythm_ferret->present ();
4793 void
4794 Editor::show_global_port_matrix (ARDOUR::DataType t)
4796 if (_global_port_matrix[t] == 0) {
4797 _global_port_matrix[t] = new GlobalPortMatrixWindow (_session, t);
4800 _global_port_matrix[t]->show ();
4803 void
4804 Editor::first_idle ()
4806 MessageDialog* dialog = 0;
4808 if (track_views.size() > 1) {
4809 dialog = new MessageDialog (*this,
4810 string_compose (_("Please wait while %1 loads visual data"), PROGRAM_NAME),
4811 true,
4812 Gtk::MESSAGE_INFO,
4813 Gtk::BUTTONS_NONE);
4814 dialog->present ();
4815 ARDOUR_UI::instance()->flush_pending ();
4818 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4819 (*t)->first_idle();
4822 // first idle adds route children (automation tracks), so we need to redisplay here
4823 _routes->redisplay ();
4825 delete dialog;
4827 _have_idled = true;
4830 gboolean
4831 Editor::_idle_resize (gpointer arg)
4833 return ((Editor*)arg)->idle_resize ();
4836 void
4837 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4839 if (resize_idle_id < 0) {
4840 resize_idle_id = g_idle_add (_idle_resize, this);
4841 _pending_resize_amount = 0;
4844 /* make a note of the smallest resulting height, so that we can clamp the
4845 lower limit at TimeAxisView::hSmall */
4847 int32_t min_resulting = INT32_MAX;
4849 _pending_resize_amount += h;
4850 _pending_resize_view = view;
4852 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4854 if (selection->tracks.contains (_pending_resize_view)) {
4855 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4856 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4860 if (min_resulting < 0) {
4861 min_resulting = 0;
4864 /* clamp */
4865 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4866 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4870 /** Handle pending resizing of tracks */
4871 bool
4872 Editor::idle_resize ()
4874 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4876 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4877 selection->tracks.contains (_pending_resize_view)) {
4879 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4880 if (*i != _pending_resize_view) {
4881 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4886 _pending_resize_amount = 0;
4887 flush_canvas ();
4888 _group_tabs->set_dirty ();
4889 resize_idle_id = -1;
4891 return false;
4894 void
4895 Editor::located ()
4897 ENSURE_GUI_THREAD (*this, &Editor::located);
4899 playhead_cursor->set_position (_session->audible_frame ());
4900 if (_follow_playhead && !_pending_initial_locate) {
4901 reset_x_origin_to_follow_playhead ();
4904 _pending_locate_request = false;
4905 _pending_initial_locate = false;
4908 void
4909 Editor::region_view_added (RegionView *)
4911 _summary->set_dirty ();
4914 TimeAxisView*
4915 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4917 TrackViewList::const_iterator j = track_views.begin ();
4918 while (j != track_views.end()) {
4919 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4920 if (rtv && rtv->route() == r) {
4921 return rtv;
4923 ++j;
4926 return 0;
4930 TrackViewList
4931 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4933 TrackViewList t;
4935 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4936 TimeAxisView* tv = axis_view_from_route (*i);
4937 if (tv) {
4938 t.push_back (tv);
4942 return t;
4946 void
4947 Editor::handle_new_route (RouteList& routes)
4949 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4951 RouteTimeAxisView *rtv;
4952 list<RouteTimeAxisView*> new_views;
4954 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4955 boost::shared_ptr<Route> route = (*x);
4957 if (route->is_hidden() || route->is_monitor()) {
4958 continue;
4961 DataType dt = route->input()->default_type();
4963 if (dt == ARDOUR::DataType::AUDIO) {
4964 rtv = new AudioTimeAxisView (*this, _session, route, *track_canvas);
4965 } else if (dt == ARDOUR::DataType::MIDI) {
4966 rtv = new MidiTimeAxisView (*this, _session, route, *track_canvas);
4967 } else {
4968 throw unknown_type();
4971 new_views.push_back (rtv);
4972 track_views.push_back (rtv);
4974 rtv->effective_gain_display ();
4976 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4979 _routes->routes_added (new_views);
4981 if (show_editor_mixer_when_tracks_arrive) {
4982 show_editor_mixer (true);
4985 editor_list_button.set_sensitive (true);
4987 _summary->set_dirty ();
4990 void
4991 Editor::timeaxisview_deleted (TimeAxisView *tv)
4993 if (_session && _session->deletion_in_progress()) {
4994 /* the situation is under control */
4995 return;
4998 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
5001 _routes->route_removed (tv);
5003 if (tv == entered_track) {
5004 entered_track = 0;
5007 /* remove it from the list of track views */
5009 TrackViewList::iterator i;
5011 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
5012 i = track_views.erase (i);
5015 /* update whatever the current mixer strip is displaying, if revelant */
5017 boost::shared_ptr<Route> route;
5018 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
5020 if (rtav) {
5021 route = rtav->route ();
5024 if (current_mixer_strip && current_mixer_strip->route() == route) {
5026 TimeAxisView* next_tv;
5028 if (track_views.empty()) {
5029 next_tv = 0;
5030 } else if (i == track_views.end()) {
5031 next_tv = track_views.front();
5032 } else {
5033 next_tv = (*i);
5037 if (next_tv) {
5038 set_selected_mixer_strip (*next_tv);
5039 } else {
5040 /* make the editor mixer strip go away setting the
5041 * button to inactive (which also unticks the menu option)
5044 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
5049 void
5050 Editor::hide_track_in_display (TimeAxisView* tv, bool /*temponly*/)
5052 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
5054 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
5055 // this will hide the mixer strip
5056 set_selected_mixer_strip (*tv);
5059 _routes->hide_track_in_display (*tv);
5062 bool
5063 Editor::sync_track_view_list_and_routes ()
5065 track_views = TrackViewList (_routes->views ());
5067 _summary->set_dirty ();
5068 _group_tabs->set_dirty ();
5070 return false; // do not call again (until needed)
5073 void
5074 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
5076 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5077 theslot (**i);
5081 RouteTimeAxisView*
5082 Editor::get_route_view_by_id (PBD::ID& id)
5084 RouteTimeAxisView* v;
5086 for(TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5087 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5088 if(v->route()->id() == id) {
5089 return v;
5094 return 0;
5097 void
5098 Editor::fit_route_group (RouteGroup *g)
5100 TrackViewList ts = axis_views_from_routes (g->route_list ());
5101 fit_tracks (ts);
5104 void
5105 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5107 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5109 if (r == 0) {
5110 _session->cancel_audition ();
5111 return;
5114 if (_session->is_auditioning()) {
5115 _session->cancel_audition ();
5116 if (r == last_audition_region) {
5117 return;
5121 _session->audition_region (r);
5122 last_audition_region = r;
5126 void
5127 Editor::hide_a_region (boost::shared_ptr<Region> r)
5129 r->set_hidden (true);
5132 void
5133 Editor::show_a_region (boost::shared_ptr<Region> r)
5135 r->set_hidden (false);
5138 void
5139 Editor::audition_region_from_region_list ()
5141 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5144 void
5145 Editor::hide_region_from_region_list ()
5147 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5150 void
5151 Editor::show_region_in_region_list ()
5153 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5156 void
5157 Editor::start_step_editing ()
5159 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5162 void
5163 Editor::stop_step_editing ()
5165 step_edit_connection.disconnect ();
5168 bool
5169 Editor::check_step_edit ()
5171 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5172 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5173 if (mtv) {
5174 mtv->check_step_edit ();
5178 return true; // do it again, till we stop
5181 bool
5182 Editor::horizontal_scroll_left_press ()
5184 ++_scroll_callbacks;
5186 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5187 /* delay the first auto-repeat */
5188 return true;
5191 double x = leftmost_position() - current_page_frames() / 5;
5192 if (x < 0) {
5193 x = 0;
5196 reset_x_origin (x);
5198 /* do hacky auto-repeat */
5199 if (!_scroll_connection.connected ()) {
5200 _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press), 100);
5201 _scroll_callbacks = 0;
5204 return true;
5207 void
5208 Editor::horizontal_scroll_left_release ()
5210 _scroll_connection.disconnect ();
5213 bool
5214 Editor::horizontal_scroll_right_press ()
5216 ++_scroll_callbacks;
5218 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5219 /* delay the first auto-repeat */
5220 return true;
5223 reset_x_origin (leftmost_position() + current_page_frames() / 5);
5225 /* do hacky auto-repeat */
5226 if (!_scroll_connection.connected ()) {
5227 _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press), 100);
5228 _scroll_callbacks = 0;
5231 return true;
5234 void
5235 Editor::horizontal_scroll_right_release ()
5237 _scroll_connection.disconnect ();
5240 /** Queue a change for the Editor viewport x origin to follow the playhead */
5241 void
5242 Editor::reset_x_origin_to_follow_playhead ()
5244 nframes64_t const frame = playhead_cursor->current_frame;
5246 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5248 if (_session->transport_speed() < 0) {
5250 if (frame > (current_page_frames() / 2)) {
5251 center_screen (frame-(current_page_frames()/2));
5252 } else {
5253 center_screen (current_page_frames()/2);
5256 } else {
5258 if (frame < leftmost_frame) {
5259 /* moving left */
5260 nframes64_t l = 0;
5261 if (_session->transport_rolling()) {
5262 /* rolling; end up with the playhead at the right of the page */
5263 l = frame - current_page_frames ();
5264 } else {
5265 /* not rolling: end up with the playhead 3/4 of the way along the page */
5266 l = frame - (3 * current_page_frames() / 4);
5269 if (l < 0) {
5270 l = 0;
5273 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5274 } else {
5275 /* moving right */
5276 if (_session->transport_rolling()) {
5277 /* rolling: end up with the playhead on the left of the page */
5278 center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5279 } else {
5280 /* not rolling: end up with the playhead 1/4 of the way along the page */
5281 center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5288 void
5289 Editor::super_rapid_screen_update ()
5291 if (!_session || !_session->engine().running()) {
5292 return;
5295 /* METERING / MIXER STRIPS */
5297 /* update track meters, if required */
5298 if (is_mapped() && meters_running) {
5299 RouteTimeAxisView* rtv;
5300 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5301 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5302 rtv->fast_update ();
5307 /* and any current mixer strip */
5308 if (current_mixer_strip) {
5309 current_mixer_strip->fast_update ();
5312 /* PLAYHEAD AND VIEWPORT */
5314 nframes64_t const frame = _session->audible_frame();
5316 /* There are a few reasons why we might not update the playhead / viewport stuff:
5318 * 1. we don't update things when there's a pending locate request, otherwise
5319 * when the editor requests a locate there is a chance that this method
5320 * will move the playhead before the locate request is processed, causing
5321 * a visual glitch.
5322 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5323 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5326 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5328 last_update_frame = frame;
5330 if (!_dragging_playhead) {
5331 playhead_cursor->set_position (frame);
5334 if (!_stationary_playhead) {
5336 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5337 reset_x_origin_to_follow_playhead ();
5340 } else {
5342 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5343 editor canvas
5345 #if 0
5346 // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5347 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5348 if (target <= 0.0) {
5349 target = 0.0;
5351 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5352 target = (target * 0.15) + (current * 0.85);
5353 } else {
5354 /* relax */
5357 current = target;
5358 set_horizontal_position (current);
5359 #endif
5366 void
5367 Editor::session_going_away ()
5369 _have_idled = false;
5371 _session_connections.drop_connections ();
5373 super_rapid_screen_update_connection.disconnect ();
5375 selection->clear ();
5376 cut_buffer->clear ();
5378 clicked_regionview = 0;
5379 clicked_axisview = 0;
5380 clicked_routeview = 0;
5381 clicked_crossfadeview = 0;
5382 entered_regionview = 0;
5383 entered_track = 0;
5384 last_update_frame = 0;
5385 _drags->abort ();
5387 playhead_cursor->canvas_item.hide ();
5389 /* rip everything out of the list displays */
5391 _regions->clear ();
5392 _routes->clear ();
5393 _route_groups->clear ();
5395 /* do this first so that deleting a track doesn't reset cms to null
5396 and thus cause a leak.
5399 if (current_mixer_strip) {
5400 if (current_mixer_strip->get_parent() != 0) {
5401 global_hpacker.remove (*current_mixer_strip);
5403 delete current_mixer_strip;
5404 current_mixer_strip = 0;
5407 /* delete all trackviews */
5409 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5410 delete *i;
5412 track_views.clear ();
5414 zoom_range_clock.set_session (0);
5415 nudge_clock.set_session (0);
5417 editor_list_button.set_active(false);
5418 editor_list_button.set_sensitive(false);
5420 /* clear tempo/meter rulers */
5421 remove_metric_marks ();
5422 hide_measures ();
5423 clear_marker_display ();
5425 delete current_bbt_points;
5426 current_bbt_points = 0;
5428 /* get rid of any existing editor mixer strip */
5430 WindowTitle title(Glib::get_application_name());
5431 title += _("Editor");
5433 set_title (title.get_string());
5435 SessionHandlePtr::session_going_away ();
5439 void
5440 Editor::show_editor_list (bool yn)
5442 if (yn) {
5443 the_notebook.show();
5444 } else {
5445 the_notebook.hide();