add left/right side trim cursors and use them for region trimming, as appropriate
[ardour2.git] / gtk2_ardour / editor.cc
blob92d3239e8a2b898f3711b4e9ef13118fec79095e
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"
118 #include "i18n.h"
120 #ifdef WITH_CMT
121 #include "imageframe_socket_handler.h"
122 #endif
124 using namespace std;
125 using namespace ARDOUR;
126 using namespace PBD;
127 using namespace Gtk;
128 using namespace Glib;
129 using namespace Gtkmm2ext;
130 using namespace Editing;
132 using PBD::internationalize;
133 using PBD::atoi;
134 using Gtkmm2ext::Keyboard;
136 const double Editor::timebar_height = 15.0;
138 #include "editor_xpms"
140 static const gchar *_snap_type_strings[] = {
141 N_("CD Frames"),
142 N_("Timecode Frames"),
143 N_("Timecode Seconds"),
144 N_("Timecode Minutes"),
145 N_("Seconds"),
146 N_("Minutes"),
147 N_("Beats/32"),
148 N_("Beats/28"),
149 N_("Beats/24"),
150 N_("Beats/16"),
151 N_("Beats/14"),
152 N_("Beats/12"),
153 N_("Beats/10"),
154 N_("Beats/8"),
155 N_("Beats/7"),
156 N_("Beats/6"),
157 N_("Beats/5"),
158 N_("Beats/4"),
159 N_("Beats/3"),
160 N_("Beats/2"),
161 N_("Beats"),
162 N_("Bars"),
163 N_("Marks"),
164 N_("Region starts"),
165 N_("Region ends"),
166 N_("Region syncs"),
167 N_("Region bounds"),
171 static const gchar *_snap_mode_strings[] = {
172 N_("No Grid"),
173 N_("Grid"),
174 N_("Magnetic"),
178 static const gchar *_edit_point_strings[] = {
179 N_("Playhead"),
180 N_("Marker"),
181 N_("Mouse"),
185 static const gchar *_zoom_focus_strings[] = {
186 N_("Left"),
187 N_("Right"),
188 N_("Center"),
189 N_("Playhead"),
190 N_("Mouse"),
191 N_("Edit point"),
195 #ifdef USE_RUBBERBAND
196 static const gchar *_rb_opt_strings[] = {
197 N_("Mushy"),
198 N_("Smooth"),
199 N_("Balanced multitimbral mixture"),
200 N_("Unpitched percussion with stable notes"),
201 N_("Crisp monophonic instrumental"),
202 N_("Unpitched solo percussion"),
205 #endif
207 /* Soundfile drag-n-drop */
209 Gdk::Cursor* Editor::cross_hair_cursor = 0;
210 Gdk::Cursor* Editor::selector_cursor = 0;
211 Gdk::Cursor* Editor::trimmer_cursor = 0;
212 Gdk::Cursor* Editor::left_side_trim_cursor = 0;
213 Gdk::Cursor* Editor::right_side_trim_cursor = 0;
214 Gdk::Cursor* Editor::grabber_cursor = 0;
215 Gdk::Cursor* Editor::grabber_edit_point_cursor = 0;
216 Gdk::Cursor* Editor::zoom_cursor = 0;
217 Gdk::Cursor* Editor::time_fx_cursor = 0;
218 Gdk::Cursor* Editor::fader_cursor = 0;
219 Gdk::Cursor* Editor::speaker_cursor = 0;
220 Gdk::Cursor* Editor::midi_pencil_cursor = 0;
221 Gdk::Cursor* Editor::midi_select_cursor = 0;
222 Gdk::Cursor* Editor::midi_resize_cursor = 0;
223 Gdk::Cursor* Editor::midi_erase_cursor = 0;
224 Gdk::Cursor* Editor::wait_cursor = 0;
225 Gdk::Cursor* Editor::timebar_cursor = 0;
226 Gdk::Cursor* Editor::transparent_cursor = 0;
228 void
229 show_me_the_size (Requisition* r, const char* what)
231 cerr << "size of " << what << " = " << r->width << " x " << r->height << endl;
234 #ifdef GTKOSX
235 static void
236 pane_size_watcher (Paned* pane)
238 /* if the handle of a pane vanishes into (at least) the tabs of a notebook,
239 it is no longer accessible. so stop that. this doesn't happen on X11,
240 just the quartz backend.
242 ugh.
245 int max_width_of_lhs = GTK_WIDGET(pane->gobj())->allocation.width - 25;
247 gint pos = pane->get_position ();
249 if (pos > max_width_of_lhs) {
250 pane->set_position (max_width_of_lhs);
253 #endif
255 Editor::Editor ()
256 : _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
258 /* time display buttons */
259 , minsec_label (_("Mins:Secs"))
260 , bbt_label (_("Bars:Beats"))
261 , timecode_label (_("Timecode"))
262 , frame_label (_("Samples"))
263 , tempo_label (_("Tempo"))
264 , meter_label (_("Meter"))
265 , mark_label (_("Location Markers"))
266 , range_mark_label (_("Range Markers"))
267 , transport_mark_label (_("Loop/Punch Ranges"))
268 , cd_mark_label (_("CD Markers"))
269 , edit_packer (4, 4, true)
271 /* the values here don't matter: layout widgets
272 reset them as needed.
275 , vertical_adjustment (0.0, 0.0, 10.0, 400.0)
277 /* tool bar related */
279 , zoom_range_clock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, false, true)
281 , toolbar_selection_clock_table (2,3)
283 , automation_mode_button (_("mode"))
284 , global_automation_button (_("automation"))
286 , midi_panic_button (_("Panic"))
288 #ifdef WITH_CMT
289 , image_socket_listener(0)
290 #endif
292 /* nudge */
294 , nudge_clock (X_("nudge"), false, X_("NudgeClock"), true, false, true)
295 , meters_running(false)
296 , _pending_locate_request (false)
297 , _pending_initial_locate (false)
300 constructed = false;
302 /* we are a singleton */
304 PublicEditor::_instance = this;
306 _have_idled = false;
308 selection = new Selection (this);
309 cut_buffer = new Selection (this);
311 clicked_regionview = 0;
312 clicked_axisview = 0;
313 clicked_routeview = 0;
314 clicked_crossfadeview = 0;
315 clicked_control_point = 0;
316 last_update_frame = 0;
317 _drags = new DragManager (this);
318 current_mixer_strip = 0;
319 current_bbt_points = 0;
320 tempo_lines = 0;
322 snap_type_strings = I18N (_snap_type_strings);
323 snap_mode_strings = I18N (_snap_mode_strings);
324 zoom_focus_strings = I18N (_zoom_focus_strings);
325 edit_point_strings = I18N (_edit_point_strings);
326 #ifdef USE_RUBBERBAND
327 rb_opt_strings = I18N (_rb_opt_strings);
328 #endif
330 snap_threshold = 5.0;
331 bbt_beat_subdivision = 4;
332 _canvas_width = 0;
333 _canvas_height = 0;
334 last_autoscroll_x = 0;
335 last_autoscroll_y = 0;
336 autoscroll_active = false;
337 autoscroll_timeout_tag = -1;
338 logo_item = 0;
340 analysis_window = 0;
342 current_interthread_info = 0;
343 _show_measures = true;
344 show_gain_after_trim = false;
345 verbose_cursor_on = true;
346 last_item_entered = 0;
347 last_item_entered_n = 0;
349 have_pending_keyboard_selection = false;
350 _follow_playhead = true;
351 _stationary_playhead = false;
352 _xfade_visibility = true;
353 editor_ruler_menu = 0;
354 no_ruler_shown_update = false;
355 marker_menu = 0;
356 session_range_marker_menu = 0;
357 range_marker_menu = 0;
358 marker_menu_item = 0;
359 tempo_or_meter_marker_menu = 0;
360 transport_marker_menu = 0;
361 new_transport_marker_menu = 0;
362 editor_mixer_strip_width = Wide;
363 show_editor_mixer_when_tracks_arrive = false;
364 region_edit_menu_split_multichannel_item = 0;
365 region_edit_menu_split_item = 0;
366 temp_location = 0;
367 leftmost_frame = 0;
368 current_stepping_trackview = 0;
369 entered_track = 0;
370 entered_regionview = 0;
371 entered_marker = 0;
372 clear_entered_track = false;
373 current_timefx = 0;
374 playhead_cursor = 0;
375 button_release_can_deselect = true;
376 _dragging_playhead = false;
377 _dragging_edit_point = false;
378 select_new_marker = false;
379 rhythm_ferret = 0;
380 _bundle_manager = 0;
381 for (ARDOUR::DataType::iterator i = ARDOUR::DataType::begin(); i != ARDOUR::DataType::end(); ++i) {
382 _global_port_matrix[*i] = 0;
384 allow_vertical_scroll = false;
385 no_save_visual = false;
386 resize_idle_id = -1;
388 scrubbing_direction = 0;
390 sfbrowser = 0;
392 location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get();
393 location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get();
394 location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get();
395 location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get();
396 location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get();
398 _edit_point = EditAtMouse;
399 _internal_editing = false;
400 current_canvas_cursor = 0;
402 frames_per_unit = 2048; /* too early to use reset_zoom () */
404 _scroll_callbacks = 0;
406 zoom_focus = ZoomFocusLeft;
407 set_zoom_focus (ZoomFocusLeft);
408 zoom_range_clock.ValueChanged.connect (sigc::mem_fun(*this, &Editor::zoom_adjustment_changed));
410 bbt_label.set_name ("EditorTimeButton");
411 bbt_label.set_size_request (-1, (int)timebar_height);
412 bbt_label.set_alignment (1.0, 0.5);
413 bbt_label.set_padding (5,0);
414 bbt_label.hide ();
415 bbt_label.set_no_show_all();
416 minsec_label.set_name ("EditorTimeButton");
417 minsec_label.set_size_request (-1, (int)timebar_height);
418 minsec_label.set_alignment (1.0, 0.5);
419 minsec_label.set_padding (5,0);
420 minsec_label.hide ();
421 minsec_label.set_no_show_all();
422 timecode_label.set_name ("EditorTimeButton");
423 timecode_label.set_size_request (-1, (int)timebar_height);
424 timecode_label.set_alignment (1.0, 0.5);
425 timecode_label.set_padding (5,0);
426 timecode_label.hide ();
427 timecode_label.set_no_show_all();
428 frame_label.set_name ("EditorTimeButton");
429 frame_label.set_size_request (-1, (int)timebar_height);
430 frame_label.set_alignment (1.0, 0.5);
431 frame_label.set_padding (5,0);
432 frame_label.hide ();
433 frame_label.set_no_show_all();
435 tempo_label.set_name ("EditorTimeButton");
436 tempo_label.set_size_request (-1, (int)timebar_height);
437 tempo_label.set_alignment (1.0, 0.5);
438 tempo_label.set_padding (5,0);
439 tempo_label.hide();
440 tempo_label.set_no_show_all();
441 meter_label.set_name ("EditorTimeButton");
442 meter_label.set_size_request (-1, (int)timebar_height);
443 meter_label.set_alignment (1.0, 0.5);
444 meter_label.set_padding (5,0);
445 meter_label.hide();
446 meter_label.set_no_show_all();
447 mark_label.set_name ("EditorTimeButton");
448 mark_label.set_size_request (-1, (int)timebar_height);
449 mark_label.set_alignment (1.0, 0.5);
450 mark_label.set_padding (5,0);
451 mark_label.hide();
452 mark_label.set_no_show_all();
453 cd_mark_label.set_name ("EditorTimeButton");
454 cd_mark_label.set_size_request (-1, (int)timebar_height);
455 cd_mark_label.set_alignment (1.0, 0.5);
456 cd_mark_label.set_padding (5,0);
457 cd_mark_label.hide();
458 cd_mark_label.set_no_show_all();
459 range_mark_label.set_name ("EditorTimeButton");
460 range_mark_label.set_size_request (-1, (int)timebar_height);
461 range_mark_label.set_alignment (1.0, 0.5);
462 range_mark_label.set_padding (5,0);
463 range_mark_label.hide();
464 range_mark_label.set_no_show_all();
465 transport_mark_label.set_name ("EditorTimeButton");
466 transport_mark_label.set_size_request (-1, (int)timebar_height);
467 transport_mark_label.set_alignment (1.0, 0.5);
468 transport_mark_label.set_padding (5,0);
469 transport_mark_label.hide();
470 transport_mark_label.set_no_show_all();
472 initialize_rulers ();
473 initialize_canvas ();
474 _summary = new EditorSummary (this);
476 selection->TimeChanged.connect (sigc::mem_fun(*this, &Editor::time_selection_changed));
477 selection->TracksChanged.connect (sigc::mem_fun(*this, &Editor::track_selection_changed));
478 editor_regions_selection_changed_connection = selection->RegionsChanged.connect (sigc::mem_fun(*this, &Editor::region_selection_changed));
479 selection->PointsChanged.connect (sigc::mem_fun(*this, &Editor::point_selection_changed));
480 selection->MarkersChanged.connect (sigc::mem_fun(*this, &Editor::marker_selection_changed));
482 edit_controls_vbox.set_spacing (0);
483 vertical_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &Editor::tie_vertical_scrolling), true);
484 track_canvas->signal_map_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_map_handler));
486 HBox* h = manage (new HBox);
487 _group_tabs = new EditorGroupTabs (this);
488 h->pack_start (*_group_tabs, PACK_SHRINK);
489 h->pack_start (edit_controls_vbox);
490 controls_layout.add (*h);
492 controls_layout.set_name ("EditControlsBase");
493 controls_layout.add_events (Gdk::SCROLL_MASK);
494 controls_layout.signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::control_layout_scroll), false);
496 controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
497 controls_layout.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_release));
498 controls_layout_size_request_connection = controls_layout.signal_size_request().connect (sigc::mem_fun (*this, &Editor::controls_layout_size_request));
500 build_cursors ();
502 ArdourCanvas::Canvas* time_pad = manage(new ArdourCanvas::Canvas());
503 ArdourCanvas::SimpleLine* pad_line_1 = manage(new ArdourCanvas::SimpleLine(*time_pad->root(),
504 0.0, 1.0, 100.0, 1.0));
505 pad_line_1->property_color_rgba() = 0xFF0000FF;
506 pad_line_1->show();
507 time_pad->show();
509 time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2);
510 time_canvas_vbox.set_size_request (-1, -1);
512 ruler_label_event_box.add (ruler_label_vbox);
513 ruler_label_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
514 ruler_label_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
516 time_button_event_box.add (time_button_vbox);
517 time_button_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
518 time_button_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
520 /* these enable us to have a dedicated window (for cursor setting, etc.)
521 for the canvas areas.
524 track_canvas_event_box.add (*track_canvas);
526 time_canvas_event_box.add (time_canvas_vbox);
527 time_canvas_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
529 edit_packer.set_col_spacings (0);
530 edit_packer.set_row_spacings (0);
531 edit_packer.set_homogeneous (false);
532 edit_packer.set_border_width (0);
533 edit_packer.set_name ("EditorWindow");
535 edit_packer.attach (zoom_vbox, 0, 1, 0, 2, SHRINK, FILL, 0, 0);
536 /* labels for the rulers */
537 edit_packer.attach (ruler_label_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0);
538 /* labels for the marker "tracks" */
539 edit_packer.attach (time_button_event_box, 1, 2, 1, 2, FILL, SHRINK, 0, 0);
540 /* the rulers */
541 edit_packer.attach (time_canvas_event_box, 2, 3, 0, 1, FILL|EXPAND, FILL, 0, 0);
542 /* track controls */
543 edit_packer.attach (controls_layout, 0, 2, 2, 3, FILL, FILL|EXPAND, 0, 0);
544 /* main canvas */
545 edit_packer.attach (track_canvas_event_box, 2, 3, 1, 3, FILL|EXPAND, FILL|EXPAND, 0, 0);
547 bottom_hbox.set_border_width (2);
548 bottom_hbox.set_spacing (3);
550 _route_groups = new EditorRouteGroups (this);
551 _routes = new EditorRoutes (this);
552 _regions = new EditorRegions (this);
553 _snapshots = new EditorSnapshots (this);
554 _locations = new EditorLocations (this);
556 Gtk::Label* nlabel;
558 nlabel = manage (new Label (_("Regions")));
559 nlabel->set_angle (-90);
560 the_notebook.append_page (_regions->widget (), *nlabel);
561 nlabel = manage (new Label (_("Tracks & Busses")));
562 nlabel->set_angle (-90);
563 the_notebook.append_page (_routes->widget (), *nlabel);
564 nlabel = manage (new Label (_("Snapshots")));
565 nlabel->set_angle (-90);
566 the_notebook.append_page (_snapshots->widget (), *nlabel);
567 nlabel = manage (new Label (_("Route Groups")));
568 nlabel->set_angle (-90);
569 the_notebook.append_page (_route_groups->widget (), *nlabel);
570 nlabel = manage (new Label (_("Ranges & Marks")));
571 nlabel->set_angle (-90);
572 the_notebook.append_page (_locations->widget (), *nlabel);
574 the_notebook.set_show_tabs (true);
575 the_notebook.set_scrollable (true);
576 the_notebook.popup_disable ();
577 the_notebook.set_tab_pos (Gtk::POS_RIGHT);
578 the_notebook.show_all ();
580 post_maximal_editor_width = 0;
581 post_maximal_horizontal_pane_position = 0;
582 post_maximal_editor_height = 0;
583 post_maximal_vertical_pane_position = 0;
585 editor_summary_pane.pack1(edit_packer);
587 Button* summary_arrows_left_left = manage (new Button);
588 summary_arrows_left_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
589 summary_arrows_left_left->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press)));
590 summary_arrows_left_left->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_release));
591 Button* summary_arrows_left_right = manage (new Button);
592 summary_arrows_left_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
593 summary_arrows_left_right->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press)));
594 summary_arrows_left_right->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_release));
595 VBox* summary_arrows_left = manage (new VBox);
596 summary_arrows_left->pack_start (*summary_arrows_left_left);
597 summary_arrows_left->pack_start (*summary_arrows_left_right);
599 Button* summary_arrows_right_left = manage (new Button);
600 summary_arrows_right_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
601 summary_arrows_right_left->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press)));
602 summary_arrows_right_left->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_release));
603 Button* summary_arrows_right_right = manage (new Button);
604 summary_arrows_right_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
605 summary_arrows_right_right->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press)));
606 summary_arrows_right_right->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_release));
607 VBox* summary_arrows_right = manage (new VBox);
608 summary_arrows_right->pack_start (*summary_arrows_right_left);
609 summary_arrows_right->pack_start (*summary_arrows_right_right);
611 Frame* summary_frame = manage (new Frame);
612 summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN);
613 summary_frame->add (*_summary);
614 summary_frame->show ();
616 _summary_hbox.pack_start (*summary_arrows_left, false, false);
617 _summary_hbox.pack_start (*summary_frame, true, true);
618 _summary_hbox.pack_start (*summary_arrows_right, false, false);
620 editor_summary_pane.pack2 (_summary_hbox);
622 edit_pane.pack1 (editor_summary_pane, true, true);
623 edit_pane.pack2 (the_notebook, false, true);
625 editor_summary_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun (*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&editor_summary_pane)));
627 /* XXX: editor_summary_pane might need similar special OS X treatment to the edit_pane */
629 edit_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&edit_pane)));
630 #ifdef GTKOSX
631 Glib::PropertyProxy<int> proxy = edit_pane.property_position();
632 proxy.signal_changed().connect (bind (sigc::ptr_fun (pane_size_watcher), static_cast<Paned*> (&edit_pane)));
633 #endif
634 top_hbox.pack_start (toolbar_frame, false, true);
636 HBox *hbox = manage (new HBox);
637 hbox->pack_start (edit_pane, true, true);
639 global_vpacker.pack_start (top_hbox, false, false);
640 global_vpacker.pack_start (*hbox, true, true);
642 global_hpacker.pack_start (global_vpacker, true, true);
644 set_name ("EditorWindow");
645 add_accel_group (ActionManager::ui_manager->get_accel_group());
647 status_bar_hpacker.show ();
649 vpacker.pack_end (status_bar_hpacker, false, false);
650 vpacker.pack_end (global_hpacker, true, true);
652 /* register actions now so that set_state() can find them and set toggles/checks etc */
654 register_actions ();
656 setup_toolbar ();
657 setup_midi_toolbar ();
659 _snap_type = SnapToBeat;
660 set_snap_to (_snap_type);
661 _snap_mode = SnapOff;
662 set_snap_mode (_snap_mode);
663 set_mouse_mode (MouseObject, true);
664 set_edit_point_preference (EditAtMouse, true);
666 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
667 set_state (*node, Stateful::loading_state_version);
669 _playlist_selector = new PlaylistSelector();
670 _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
672 RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), ui_bind (&Editor::catch_vanishing_regionview, this, _1), gui_context());
674 /* nudge stuff */
676 nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
677 nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
679 nudge_forward_button.set_name ("TransportButton");
680 nudge_backward_button.set_name ("TransportButton");
682 fade_context_menu.set_name ("ArdourContextMenu");
684 /* icons, titles, WM stuff */
686 list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
687 Glib::RefPtr<Gdk::Pixbuf> icon;
689 if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
690 window_icons.push_back (icon);
692 if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
693 window_icons.push_back (icon);
695 if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
696 window_icons.push_back (icon);
698 if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
699 window_icons.push_back (icon);
701 if (!window_icons.empty()) {
702 set_icon_list (window_icons);
703 set_default_icon_list (window_icons);
706 WindowTitle title(Glib::get_application_name());
707 title += _("Editor");
708 set_title (title.get_string());
709 set_wmclass (X_("ardour_editor"), "Ardour");
711 add (vpacker);
712 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
714 signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
715 signal_delete_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
717 /* allow external control surfaces/protocols to do various things */
719 ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context());
720 ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context());
721 ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context());
722 ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), ui_bind (&Editor::control_scroll, this, _1), gui_context());
723 BasicUI::AccessAction.connect (*this, invalidator (*this), ui_bind (&Editor::access_action, this, _1, _2), gui_context());
725 /* problematic: has to return a value and thus cannot be x-thread */
727 Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
729 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
731 TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
733 _last_normalization_value = 0;
735 constructed = true;
736 instant_save ();
739 Editor::~Editor()
741 #ifdef WITH_CMT
742 if(image_socket_listener) {
743 if(image_socket_listener->is_connected())
745 image_socket_listener->close_connection() ;
748 delete image_socket_listener ;
749 image_socket_listener = 0 ;
751 #endif
753 delete _routes;
754 delete _route_groups;
755 delete track_canvas;
756 delete _drags;
759 void
760 Editor::add_toplevel_controls (Container& cont)
762 vpacker.pack_start (cont, false, false);
763 cont.show_all ();
766 void
767 Editor::catch_vanishing_regionview (RegionView *rv)
769 /* note: the selection will take care of the vanishing
770 audioregionview by itself.
773 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
774 _drags->abort ();
777 if (clicked_regionview == rv) {
778 clicked_regionview = 0;
781 if (entered_regionview == rv) {
782 set_entered_regionview (0);
786 void
787 Editor::set_entered_regionview (RegionView* rv)
789 if (rv == entered_regionview) {
790 return;
793 if (entered_regionview) {
794 entered_regionview->exited ();
797 if ((entered_regionview = rv) != 0) {
798 entered_regionview->entered ();
802 void
803 Editor::set_entered_track (TimeAxisView* tav)
805 if (entered_track) {
806 entered_track->exited ();
809 if ((entered_track = tav) != 0) {
810 entered_track->entered ();
814 void
815 Editor::show_window ()
817 if (! is_visible ()) {
818 show_all ();
820 /* re-hide editor list if necessary */
821 editor_list_button_toggled ();
823 /* re-hide summary widget if necessary */
824 parameter_changed ("show-summary");
826 parameter_changed ("show-edit-group-tabs");
828 /* now reset all audio_time_axis heights, because widgets might need
829 to be re-hidden
832 TimeAxisView *tv;
834 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
835 tv = (static_cast<TimeAxisView*>(*i));
836 tv->reset_height ();
839 reset_zoom (frames_per_unit);
842 present ();
845 void
846 Editor::instant_save ()
848 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
849 return;
852 if (_session) {
853 _session->add_instant_xml(get_state());
854 } else {
855 Config->add_instant_xml(get_state());
859 void
860 Editor::zoom_adjustment_changed ()
862 if (_session == 0) {
863 return;
866 double fpu = zoom_range_clock.current_duration() / _canvas_width;
868 if (fpu < 1.0) {
869 fpu = 1.0;
870 zoom_range_clock.set ((nframes64_t) floor (fpu * _canvas_width));
871 } else if (fpu > _session->current_end_frame() / _canvas_width) {
872 fpu = _session->current_end_frame() / _canvas_width;
873 zoom_range_clock.set ((nframes64_t) floor (fpu * _canvas_width));
876 temporal_zoom (fpu);
879 void
880 Editor::control_scroll (float fraction)
882 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
884 if (!_session) {
885 return;
888 double step = fraction * current_page_frames();
891 _control_scroll_target is an optional<T>
893 it acts like a pointer to an nframes64_t, with
894 a operator conversion to boolean to check
895 that it has a value could possibly use
896 playhead_cursor->current_frame to store the
897 value and a boolean in the class to know
898 when it's out of date
901 if (!_control_scroll_target) {
902 _control_scroll_target = _session->transport_frame();
903 _dragging_playhead = true;
906 if ((fraction < 0.0f) && (*_control_scroll_target < (nframes64_t) fabs(step))) {
907 *_control_scroll_target = 0;
908 } else if ((fraction > 0.0f) && (max_frames - *_control_scroll_target < step)) {
909 *_control_scroll_target = max_frames - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
910 } else {
911 *_control_scroll_target += (nframes64_t) floor (step);
914 /* move visuals, we'll catch up with it later */
916 playhead_cursor->set_position (*_control_scroll_target);
917 UpdateAllTransportClocks (*_control_scroll_target);
919 if (*_control_scroll_target > (current_page_frames() / 2)) {
920 /* try to center PH in window */
921 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
922 } else {
923 reset_x_origin (0);
927 Now we do a timeout to actually bring the session to the right place
928 according to the playhead. This is to avoid reading disk buffers on every
929 call to control_scroll, which is driven by ScrollTimeline and therefore
930 probably by a control surface wheel which can generate lots of events.
932 /* cancel the existing timeout */
934 control_scroll_connection.disconnect ();
936 /* add the next timeout */
938 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
941 bool
942 Editor::deferred_control_scroll (nframes64_t /*target*/)
944 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
945 // reset for next stream
946 _control_scroll_target = boost::none;
947 _dragging_playhead = false;
948 return false;
951 void
952 Editor::access_action (std::string action_group, std::string action_item)
954 if (!_session) {
955 return;
958 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
960 RefPtr<Action> act;
961 act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
963 if (act) {
964 act->activate();
968 void
969 Editor::on_realize ()
971 Window::on_realize ();
972 Realized ();
975 void
976 Editor::map_position_change (nframes64_t frame)
978 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, frame)
980 if (_session == 0 || !_follow_playhead) {
981 return;
984 center_screen (frame);
985 playhead_cursor->set_position (frame);
988 void
989 Editor::center_screen (nframes64_t frame)
991 double page = _canvas_width * frames_per_unit;
993 /* if we're off the page, then scroll.
996 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
997 center_screen_internal (frame, page);
1001 void
1002 Editor::center_screen_internal (nframes64_t frame, float page)
1004 page /= 2;
1006 if (frame > page) {
1007 frame -= (nframes64_t) page;
1008 } else {
1009 frame = 0;
1012 reset_x_origin (frame);
1016 void
1017 Editor::update_title ()
1019 ENSURE_GUI_THREAD (*this, &Editor::update_title)
1021 if (_session) {
1022 bool dirty = _session->dirty();
1024 string session_name;
1026 if (_session->snap_name() != _session->name()) {
1027 session_name = _session->snap_name();
1028 } else {
1029 session_name = _session->name();
1032 if (dirty) {
1033 session_name = "*" + session_name;
1036 WindowTitle title(session_name);
1037 title += Glib::get_application_name();
1038 set_title (title.get_string());
1042 void
1043 Editor::set_session (Session *t)
1045 SessionHandlePtr::set_session (t);
1047 if (!_session) {
1048 return;
1051 zoom_range_clock.set_session (_session);
1052 _playlist_selector->set_session (_session);
1053 nudge_clock.set_session (_session);
1054 _summary->set_session (_session);
1055 _group_tabs->set_session (_session);
1056 _route_groups->set_session (_session);
1057 _regions->set_session (_session);
1058 _snapshots->set_session (_session);
1059 _routes->set_session (_session);
1060 _locations->set_session (_session);
1062 if (rhythm_ferret) {
1063 rhythm_ferret->set_session (_session);
1066 if (analysis_window) {
1067 analysis_window->set_session (_session);
1070 if (sfbrowser) {
1071 sfbrowser->set_session (_session);
1074 compute_fixed_ruler_scale ();
1076 /* there are never any selected regions at startup */
1077 sensitize_the_right_region_actions (false);
1079 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1080 set_state (*node, Stateful::loading_state_version);
1082 /* catch up with the playhead */
1084 _session->request_locate (playhead_cursor->current_frame);
1085 _pending_initial_locate = true;
1087 update_title ();
1089 /* These signals can all be emitted by a non-GUI thread. Therefore the
1090 handlers for them must not attempt to directly interact with the GUI,
1091 but use Gtkmm2ext::UI::instance()->call_slot();
1094 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1095 _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context());
1096 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context());
1097 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1098 _session->TimecodeOffsetChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_just_timecode, this), gui_context());
1099 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context());
1100 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1101 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
1102 _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Editor::session_state_saved, this, _1), gui_context());
1103 _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context());
1104 _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&Editor::location_gone, this, _1), gui_context());
1105 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1106 _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display_s, this, _1), gui_context());
1107 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1109 if (Profile->get_sae()) {
1110 BBT_Time bbt;
1111 bbt.bars = 0;
1112 bbt.beats = 0;
1113 bbt.ticks = 120;
1114 nframes_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1);
1115 nudge_clock.set_mode(AudioClock::BBT);
1116 nudge_clock.set (pos, true, 0, AudioClock::BBT);
1118 } else {
1119 nudge_clock.set (_session->frame_rate() * 5, true, 0, AudioClock::Timecode); // default of 5 seconds
1122 playhead_cursor->canvas_item.show ();
1124 Location* loc = _session->locations()->auto_loop_location();
1125 if (loc == 0) {
1126 loc = new Location (0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1127 if (loc->start() == loc->end()) {
1128 loc->set_end (loc->start() + 1);
1130 _session->locations()->add (loc, false);
1131 _session->set_auto_loop_location (loc);
1132 } else {
1133 // force name
1134 loc->set_name (_("Loop"));
1137 loc = _session->locations()->auto_punch_location();
1138 if (loc == 0) {
1139 loc = new Location (0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1140 if (loc->start() == loc->end()) {
1141 loc->set_end (loc->start() + 1);
1143 _session->locations()->add (loc, false);
1144 _session->set_auto_punch_location (loc);
1145 } else {
1146 // force name
1147 loc->set_name (_("Punch"));
1150 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1151 Config->map_parameters (pc);
1152 _session->config.map_parameters (pc);
1154 refresh_location_display ();
1156 restore_ruler_visibility ();
1157 //tempo_map_changed (PropertyChange (0));
1158 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1160 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1161 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1164 super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
1165 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1168 switch (_snap_type) {
1169 case SnapToRegionStart:
1170 case SnapToRegionEnd:
1171 case SnapToRegionSync:
1172 case SnapToRegionBoundary:
1173 build_region_boundary_cache ();
1174 break;
1176 default:
1177 break;
1180 /* register for undo history */
1181 _session->register_with_memento_command_factory(_id, this);
1183 start_updating_meters ();
1186 void
1187 Editor::build_cursors ()
1189 using namespace Gdk;
1191 Gdk::Color mbg ("#000000" ); /* Black */
1192 Gdk::Color mfg ("#0000ff" ); /* Blue. */
1195 RefPtr<Bitmap> source, mask;
1196 source = Bitmap::create (mag_bits, mag_width, mag_height);
1197 mask = Bitmap::create (magmask_bits, mag_width, mag_height);
1198 zoom_cursor = new Gdk::Cursor (source, mask, mfg, mbg, mag_x_hot, mag_y_hot);
1201 Gdk::Color fbg ("#ffffff" );
1202 Gdk::Color ffg ("#000000" );
1205 RefPtr<Bitmap> source, mask;
1207 source = Bitmap::create (fader_cursor_bits, fader_cursor_width, fader_cursor_height);
1208 mask = Bitmap::create (fader_cursor_mask_bits, fader_cursor_width, fader_cursor_height);
1209 fader_cursor = new Gdk::Cursor (source, mask, ffg, fbg, fader_cursor_x_hot, fader_cursor_y_hot);
1213 RefPtr<Bitmap> source, mask;
1214 source = Bitmap::create (speaker_cursor_bits, speaker_cursor_width, speaker_cursor_height);
1215 mask = Bitmap::create (speaker_cursor_mask_bits, speaker_cursor_width, speaker_cursor_height);
1216 speaker_cursor = new Gdk::Cursor (source, mask, ffg, fbg, speaker_cursor_x_hot, speaker_cursor_y_hot);
1220 RefPtr<Bitmap> bits;
1221 char pix[4] = { 0, 0, 0, 0 };
1222 bits = Bitmap::create (pix, 2, 2);
1223 Gdk::Color c;
1224 transparent_cursor = new Gdk::Cursor (bits, bits, c, c, 0, 0);
1228 RefPtr<Bitmap> bits;
1229 char pix[4] = { 0, 0, 0, 0 };
1230 bits = Bitmap::create (pix, 2, 2);
1231 Gdk::Color c;
1232 transparent_cursor = new Gdk::Cursor (bits, bits, c, c, 0, 0);
1236 grabber_cursor = new Gdk::Cursor (HAND2);
1239 Glib::RefPtr<Gdk::Pixbuf> grabber_edit_point_pixbuf (::get_icon ("grabber_edit_point"));
1240 grabber_edit_point_cursor = new Gdk::Cursor (Gdk::Display::get_default(), grabber_edit_point_pixbuf, 5, 17);
1243 cross_hair_cursor = new Gdk::Cursor (CROSSHAIR);
1244 trimmer_cursor = new Gdk::Cursor (SB_H_DOUBLE_ARROW);
1245 left_side_trim_cursor = new Gdk::Cursor (SB_LEFT_ARROW);
1246 right_side_trim_cursor = new Gdk::Cursor (SB_RIGHT_ARROW);
1247 selector_cursor = new Gdk::Cursor (XTERM);
1248 time_fx_cursor = new Gdk::Cursor (SIZING);
1249 wait_cursor = new Gdk::Cursor (WATCH);
1250 timebar_cursor = new Gdk::Cursor(LEFT_PTR);
1251 midi_pencil_cursor = new Gdk::Cursor (PENCIL);
1252 midi_select_cursor = new Gdk::Cursor (CENTER_PTR);
1253 midi_resize_cursor = new Gdk::Cursor (SIZING);
1254 midi_erase_cursor = new Gdk::Cursor (DRAPED_BOX);
1257 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1258 void
1259 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1261 using namespace Menu_Helpers;
1262 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1264 if (arv == 0) {
1265 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1266 /*NOTREACHED*/
1269 MenuList& items (fade_context_menu.items());
1271 items.clear ();
1273 switch (item_type) {
1274 case FadeInItem:
1275 case FadeInHandleItem:
1276 if (arv->audio_region()->fade_in_active()) {
1277 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1278 } else {
1279 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1282 items.push_back (SeparatorElem());
1284 if (Profile->get_sae()) {
1285 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear)));
1286 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast)));
1287 } else {
1288 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear)));
1289 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast)));
1290 items.push_back (MenuElem (_("Slow"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogB)));
1291 items.push_back (MenuElem (_("Fast"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogA)));
1292 items.push_back (MenuElem (_("Fastest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Slow)));
1295 break;
1297 case FadeOutItem:
1298 case FadeOutHandleItem:
1299 if (arv->audio_region()->fade_out_active()) {
1300 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1301 } else {
1302 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1305 items.push_back (SeparatorElem());
1307 if (Profile->get_sae()) {
1308 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear)));
1309 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow)));
1310 } else {
1311 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear)));
1312 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow)));
1313 items.push_back (MenuElem (_("Slow"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogA)));
1314 items.push_back (MenuElem (_("Fast"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogB)));
1315 items.push_back (MenuElem (_("Fastest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Fast)));
1318 break;
1320 default:
1321 fatal << _("programming error: ")
1322 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1323 << endmsg;
1324 /*NOTREACHED*/
1327 fade_context_menu.popup (button, time);
1330 void
1331 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection, nframes64_t frame)
1333 using namespace Menu_Helpers;
1334 Menu* (Editor::*build_menu_function)(nframes64_t);
1335 Menu *menu;
1337 switch (item_type) {
1338 case RegionItem:
1339 case RegionViewName:
1340 case RegionViewNameHighlight:
1341 case LeftFrameHandle:
1342 case RightFrameHandle:
1343 if (with_selection) {
1344 build_menu_function = &Editor::build_track_selection_context_menu;
1345 } else {
1346 build_menu_function = &Editor::build_track_region_context_menu;
1348 break;
1350 case SelectionItem:
1351 if (with_selection) {
1352 build_menu_function = &Editor::build_track_selection_context_menu;
1353 } else {
1354 build_menu_function = &Editor::build_track_context_menu;
1356 break;
1358 case CrossfadeViewItem:
1359 build_menu_function = &Editor::build_track_crossfade_context_menu;
1360 break;
1362 case StreamItem:
1363 if (clicked_routeview->track()) {
1364 build_menu_function = &Editor::build_track_context_menu;
1365 } else {
1366 build_menu_function = &Editor::build_track_bus_context_menu;
1368 break;
1370 default:
1371 /* probably shouldn't happen but if it does, we don't care */
1372 return;
1375 menu = (this->*build_menu_function)(frame);
1376 menu->set_name ("ArdourContextMenu");
1378 /* now handle specific situations */
1380 switch (item_type) {
1381 case RegionItem:
1382 case RegionViewName:
1383 case RegionViewNameHighlight:
1384 case LeftFrameHandle:
1385 case RightFrameHandle:
1386 if (!with_selection) {
1387 if (region_edit_menu_split_item) {
1388 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1389 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1390 } else {
1391 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1394 if (region_edit_menu_split_multichannel_item) {
1395 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1396 region_edit_menu_split_multichannel_item->set_sensitive (true);
1397 } else {
1398 region_edit_menu_split_multichannel_item->set_sensitive (false);
1402 break;
1404 case SelectionItem:
1405 break;
1407 case CrossfadeViewItem:
1408 break;
1410 case StreamItem:
1411 break;
1413 default:
1414 /* probably shouldn't happen but if it does, we don't care */
1415 return;
1418 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1420 /* Bounce to disk */
1422 using namespace Menu_Helpers;
1423 MenuList& edit_items = menu->items();
1425 edit_items.push_back (SeparatorElem());
1427 switch (clicked_routeview->audio_track()->freeze_state()) {
1428 case AudioTrack::NoFreeze:
1429 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1430 break;
1432 case AudioTrack::Frozen:
1433 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1434 break;
1436 case AudioTrack::UnFrozen:
1437 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1438 break;
1443 if (item_type == StreamItem && clicked_routeview) {
1444 clicked_routeview->build_underlay_menu(menu);
1447 menu->popup (button, time);
1450 Menu*
1451 Editor::build_track_context_menu (nframes64_t)
1453 using namespace Menu_Helpers;
1455 MenuList& edit_items = track_context_menu.items();
1456 edit_items.clear();
1458 add_dstream_context_items (edit_items);
1459 return &track_context_menu;
1462 Menu*
1463 Editor::build_track_bus_context_menu (nframes64_t)
1465 using namespace Menu_Helpers;
1467 MenuList& edit_items = track_context_menu.items();
1468 edit_items.clear();
1470 add_bus_context_items (edit_items);
1471 return &track_context_menu;
1474 Menu*
1475 Editor::build_track_region_context_menu (nframes64_t frame)
1477 using namespace Menu_Helpers;
1478 MenuList& edit_items = track_region_context_menu.items();
1479 edit_items.clear();
1481 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1483 if (rtv) {
1484 boost::shared_ptr<Track> tr;
1485 boost::shared_ptr<Playlist> pl;
1487 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
1488 Playlist::RegionList* regions = pl->regions_at ((nframes64_t) floor ( (double)frame * tr->speed()));
1490 if (selection->regions.size() > 1) {
1491 // there's already a multiple selection: just add a
1492 // single region context menu that will act on all
1493 // selected regions
1494 boost::shared_ptr<Region> dummy_region; // = NULL
1495 add_region_context_items (rtv->view(), dummy_region, edit_items);
1496 } else {
1497 for (Playlist::RegionList::reverse_iterator i = regions->rbegin(); i != regions->rend(); ++i) {
1498 add_region_context_items (rtv->view(), (*i), edit_items);
1502 delete regions;
1506 add_dstream_context_items (edit_items);
1508 return &track_region_context_menu;
1511 Menu*
1512 Editor::build_track_crossfade_context_menu (nframes64_t frame)
1514 using namespace Menu_Helpers;
1515 MenuList& edit_items = track_crossfade_context_menu.items();
1516 edit_items.clear ();
1518 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1520 if (atv) {
1521 boost::shared_ptr<Track> tr;
1522 boost::shared_ptr<Playlist> pl;
1523 boost::shared_ptr<AudioPlaylist> apl;
1525 if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1527 Playlist::RegionList* regions = pl->regions_at (frame);
1528 AudioPlaylist::Crossfades xfades;
1530 apl->crossfades_at (frame, xfades);
1532 bool many = xfades.size() > 1;
1534 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1535 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1538 if (selection->regions.size() > 1) {
1539 // there's already a multiple selection: just add a
1540 // single region context menu that will act on all
1541 // selected regions
1542 boost::shared_ptr<Region> dummy_region; // = NULL
1543 add_region_context_items (atv->audio_view(), dummy_region, edit_items);
1544 } else {
1545 for (Playlist::RegionList::reverse_iterator i = regions->rbegin(); i != regions->rend(); ++i) {
1546 add_region_context_items (atv->audio_view(), (*i), edit_items);
1549 delete regions;
1553 add_dstream_context_items (edit_items);
1555 return &track_crossfade_context_menu;
1558 void
1559 Editor::analyze_region_selection()
1561 if (analysis_window == 0) {
1562 analysis_window = new AnalysisWindow();
1564 if (_session != 0)
1565 analysis_window->set_session(_session);
1567 analysis_window->show_all();
1570 analysis_window->set_regionmode();
1571 analysis_window->analyze();
1573 analysis_window->present();
1576 void
1577 Editor::analyze_range_selection()
1579 if (analysis_window == 0) {
1580 analysis_window = new AnalysisWindow();
1582 if (_session != 0)
1583 analysis_window->set_session(_session);
1585 analysis_window->show_all();
1588 analysis_window->set_rangemode();
1589 analysis_window->analyze();
1591 analysis_window->present();
1594 Menu*
1595 Editor::build_track_selection_context_menu (nframes64_t)
1597 using namespace Menu_Helpers;
1598 MenuList& edit_items = track_selection_context_menu.items();
1599 edit_items.clear ();
1601 add_selection_context_items (edit_items);
1602 // edit_items.push_back (SeparatorElem());
1603 // add_dstream_context_items (edit_items);
1605 return &track_selection_context_menu;
1608 /** Add context menu items relevant to crossfades.
1609 * @param edit_items List to add the items to.
1611 void
1612 Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1614 using namespace Menu_Helpers;
1615 Menu *xfade_menu = manage (new Menu);
1616 MenuList& items = xfade_menu->items();
1617 xfade_menu->set_name ("ArdourContextMenu");
1618 string str;
1620 if (xfade->active()) {
1621 str = _("Mute");
1622 } else {
1623 str = _("Unmute");
1626 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1627 items.push_back (MenuElem (_("Edit"), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1629 if (xfade->can_follow_overlap()) {
1631 if (xfade->following_overlap()) {
1632 str = _("Convert to Short");
1633 } else {
1634 str = _("Convert to Full");
1637 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1640 if (many) {
1641 str = xfade->out()->name();
1642 str += "->";
1643 str += xfade->in()->name();
1644 } else {
1645 str = _("Crossfade");
1648 edit_items.push_back (MenuElem (str, *xfade_menu));
1649 edit_items.push_back (SeparatorElem());
1652 void
1653 Editor::xfade_edit_left_region ()
1655 if (clicked_crossfadeview) {
1656 clicked_crossfadeview->left_view.show_region_editor ();
1660 void
1661 Editor::xfade_edit_right_region ()
1663 if (clicked_crossfadeview) {
1664 clicked_crossfadeview->right_view.show_region_editor ();
1668 void
1669 Editor::add_region_context_items (StreamView* sv, boost::shared_ptr<Region> region, Menu_Helpers::MenuList& edit_items)
1671 using namespace Menu_Helpers;
1672 Gtk::MenuItem* foo_item;
1673 Menu *region_menu = manage (new Menu);
1674 MenuList& items = region_menu->items();
1675 region_menu->set_name ("ArdourContextMenu");
1677 boost::shared_ptr<AudioRegion> ar;
1678 boost::shared_ptr<MidiRegion> mr;
1680 if (region) {
1681 ar = boost::dynamic_pointer_cast<AudioRegion> (region);
1682 mr = boost::dynamic_pointer_cast<MidiRegion> (region);
1684 /* when this particular menu pops up, make the relevant region
1685 become selected.
1688 region_menu->signal_map_event().connect (
1689 sigc::bind (sigc::mem_fun(*this, &Editor::set_selected_regionview_from_map_event), sv, boost::weak_ptr<Region>(region)));
1691 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &Editor::rename_region)));
1692 if (mr) {
1693 items.push_back (MenuElem (_("List Editor..."), sigc::mem_fun(*this, &Editor::show_midi_list_editor)));
1695 items.push_back (MenuElem (_("Region Properties..."), sigc::mem_fun(*this, &Editor::edit_region)));
1698 items.push_back (MenuElem (_("Raise to Top Layer"), sigc::mem_fun(*this, &Editor::raise_region_to_top)));
1699 items.push_back (MenuElem (_("Lower to Bottom Layer"), sigc::mem_fun (*this, &Editor::lower_region_to_bottom)));
1700 items.push_back (SeparatorElem());
1701 items.push_back (MenuElem (_("Define Sync Point"), sigc::mem_fun(*this, &Editor::set_region_sync_from_edit_point)));
1702 if (_edit_point == EditAtMouse) {
1703 items.back ().set_sensitive (false);
1705 items.push_back (MenuElem (_("Remove Sync Point"), sigc::mem_fun(*this, &Editor::remove_region_sync)));
1706 items.push_back (SeparatorElem());
1708 items.push_back (MenuElem (_("Audition"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1709 items.push_back (MenuElem (_("Export..."), sigc::mem_fun(*this, &Editor::export_region)));
1710 items.push_back (MenuElem (_("Bounce"), sigc::mem_fun(*this, &Editor::bounce_region_selection)));
1712 if (ar) {
1713 items.push_back (MenuElem (_("Spectral Analysis..."), sigc::mem_fun(*this, &Editor::analyze_region_selection)));
1716 items.push_back (SeparatorElem());
1718 sigc::connection fooc;
1719 boost::shared_ptr<Region> region_to_check;
1721 if (region) {
1722 region_to_check = region;
1723 } else {
1724 region_to_check = selection->regions.front()->region();
1727 items.push_back (CheckMenuElem (_("Lock")));
1728 CheckMenuItem* region_lock_item = static_cast<CheckMenuItem*>(&items.back());
1729 if (region_to_check->locked()) {
1730 region_lock_item->set_active();
1732 region_lock_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_region_lock));
1734 items.push_back (CheckMenuElem (_("Glue to Bars and Beats")));
1735 CheckMenuItem* bbt_glue_item = static_cast<CheckMenuItem*>(&items.back());
1737 switch (region_to_check->positional_lock_style()) {
1738 case Region::MusicTime:
1739 bbt_glue_item->set_active (true);
1740 break;
1741 default:
1742 bbt_glue_item->set_active (false);
1743 break;
1746 bbt_glue_item->signal_activate().connect (sigc::mem_fun (*this, &Editor::toggle_region_lock_style));
1748 items.push_back (CheckMenuElem (_("Mute")));
1749 CheckMenuItem* region_mute_item = static_cast<CheckMenuItem*>(&items.back());
1750 fooc = region_mute_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_region_mute));
1751 if (region_to_check->muted()) {
1752 fooc.block (true);
1753 region_mute_item->set_active();
1754 fooc.block (false);
1757 items.push_back (MenuElem (_("Transpose..."), mem_fun(*this, &Editor::pitch_shift_regions)));
1759 if (!Profile->get_sae()) {
1760 items.push_back (CheckMenuElem (_("Opaque")));
1761 CheckMenuItem* region_opaque_item = static_cast<CheckMenuItem*>(&items.back());
1762 fooc = region_opaque_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_region_opaque));
1763 if (region_to_check->opaque()) {
1764 fooc.block (true);
1765 region_opaque_item->set_active();
1766 fooc.block (false);
1770 items.push_back (CheckMenuElem (_("Original Position"), sigc::mem_fun(*this, &Editor::naturalize)));
1771 if (region_to_check->at_natural_position()) {
1772 items.back().set_sensitive (false);
1775 items.push_back (SeparatorElem());
1777 if (ar) {
1779 RegionView* rv = sv->find_view (ar);
1780 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1782 if (!Profile->get_sae()) {
1783 items.push_back (MenuElem (_("Reset Envelope"), sigc::mem_fun(*this, &Editor::reset_region_gain_envelopes)));
1785 items.push_back (CheckMenuElem (_("Envelope Visible")));
1786 CheckMenuItem* region_envelope_visible_item = static_cast<CheckMenuItem*> (&items.back());
1787 fooc = region_envelope_visible_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_gain_envelope_visibility));
1788 if (arv->envelope_visible()) {
1789 fooc.block (true);
1790 region_envelope_visible_item->set_active (true);
1791 fooc.block (false);
1794 items.push_back (CheckMenuElem (_("Envelope Active")));
1795 CheckMenuItem* region_envelope_active_item = static_cast<CheckMenuItem*> (&items.back());
1796 fooc = region_envelope_active_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_gain_envelope_active));
1798 if (ar->envelope_active()) {
1799 fooc.block (true);
1800 region_envelope_active_item->set_active (true);
1801 fooc.block (false);
1804 items.push_back (SeparatorElem());
1807 items.push_back (MenuElem (_("Normalize..."), sigc::mem_fun(*this, &Editor::normalize_region)));
1808 if (ar->scale_amplitude() != 1) {
1809 items.push_back (MenuElem (_("Reset Gain"), sigc::mem_fun(*this, &Editor::reset_region_scale_amplitude)));
1812 } else if (mr) {
1813 items.push_back (MenuElem (_("Quantize"), sigc::mem_fun(*this, &Editor::quantize_region)));
1814 items.push_back (MenuElem (_("Fork"), sigc::mem_fun(*this, &Editor::fork_region)));
1815 items.push_back (SeparatorElem());
1818 items.push_back (MenuElem (_("Strip Silence..."), sigc::mem_fun (*this, &Editor::strip_region_silence)));
1819 items.push_back (MenuElem (_("Reverse"), sigc::mem_fun(*this, &Editor::reverse_region)));
1820 items.push_back (SeparatorElem());
1822 /* range related stuff */
1824 items.push_back (MenuElem (_("Add Single Range"), sigc::mem_fun (*this, &Editor::add_location_from_audio_region)));
1825 items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_locations_from_audio_region)));
1826 if (selection->regions.size() < 2) {
1827 items.back().set_sensitive (false);
1830 items.push_back (MenuElem (_("Set Range Selection"), sigc::mem_fun (*this, &Editor::set_selection_from_region)));
1831 items.push_back (SeparatorElem());
1833 /* Nudge region */
1835 Menu *nudge_menu = manage (new Menu());
1836 MenuList& nudge_items = nudge_menu->items();
1837 nudge_menu->set_name ("ArdourContextMenu");
1839 nudge_items.push_back (MenuElem (_("Nudge Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_forward), false, false))));
1840 nudge_items.push_back (MenuElem (_("Nudge Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_backward), false, false))));
1841 nudge_items.push_back (MenuElem (_("Nudge Forward by Capture Offset"), (sigc::mem_fun(*this, &Editor::nudge_forward_capture_offset))));
1842 nudge_items.push_back (MenuElem (_("Nudge Backward by Capture Offset"), (sigc::mem_fun(*this, &Editor::nudge_backward_capture_offset))));
1844 items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1845 items.push_back (SeparatorElem());
1847 Menu *trim_menu = manage (new Menu);
1848 MenuList& trim_items = trim_menu->items();
1849 trim_menu->set_name ("ArdourContextMenu");
1851 trim_items.push_back (MenuElem (_("Start to Edit Point"), sigc::mem_fun(*this, &Editor::trim_region_from_edit_point)));
1852 foo_item = &trim_items.back();
1853 if (_edit_point == EditAtMouse) {
1854 foo_item->set_sensitive (false);
1856 trim_items.push_back (MenuElem (_("Edit Point to End"), sigc::mem_fun(*this, &Editor::trim_region_to_edit_point)));
1857 foo_item = &trim_items.back();
1858 if (_edit_point == EditAtMouse) {
1859 foo_item->set_sensitive (false);
1861 trim_items.push_back (MenuElem (_("Trim to Loop"), sigc::mem_fun(*this, &Editor::trim_region_to_loop)));
1862 trim_items.push_back (MenuElem (_("Trim to Punch"), sigc::mem_fun(*this, &Editor::trim_region_to_punch)));
1864 items.push_back (MenuElem (_("Trim"), *trim_menu));
1865 items.push_back (SeparatorElem());
1867 items.push_back (MenuElem (_("Split"), (sigc::mem_fun(*this, &Editor::split))));
1868 region_edit_menu_split_item = &items.back();
1870 if (_edit_point == EditAtMouse) {
1871 region_edit_menu_split_item->set_sensitive (false);
1874 items.push_back (MenuElem (_("Make Mono Regions"), (sigc::mem_fun(*this, &Editor::split_multichannel_region))));
1875 region_edit_menu_split_multichannel_item = &items.back();
1877 items.push_back (MenuElem (_("Duplicate"), (sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false))));
1878 items.push_back (MenuElem (_("Multi-Duplicate..."), (sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), true))));
1879 items.push_back (MenuElem (_("Fill Track"), (sigc::mem_fun(*this, &Editor::region_fill_track))));
1880 items.push_back (SeparatorElem());
1881 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(*this, &Editor::remove_selected_regions)));
1883 /* OK, stick the region submenu at the top of the list, and then add
1884 the standard items.
1887 /* we have to hack up the region name because "_" has a special
1888 meaning for menu titles.
1891 string::size_type pos = 0;
1892 string menu_item_name = (region) ? region->name() : _("Selected Regions");
1894 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1895 menu_item_name.replace (pos, 1, "__");
1896 pos += 2;
1899 edit_items.push_back (MenuElem (menu_item_name, *region_menu));
1900 edit_items.push_back (SeparatorElem());
1903 /** Add context menu items relevant to selection ranges.
1904 * @param edit_items List to add the items to.
1906 void
1907 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1909 using namespace Menu_Helpers;
1911 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1912 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1914 edit_items.push_back (SeparatorElem());
1915 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1917 if (!selection->regions.empty()) {
1918 edit_items.push_back (SeparatorElem());
1919 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)));
1920 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)));
1923 edit_items.push_back (SeparatorElem());
1924 edit_items.push_back (MenuElem (_("Silence Range"), sigc::mem_fun(*this, &Editor::separate_region_from_selection)));
1925 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1927 edit_items.push_back (SeparatorElem());
1928 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1930 edit_items.push_back (SeparatorElem());
1931 edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1932 edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1934 edit_items.push_back (SeparatorElem());
1935 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1937 edit_items.push_back (SeparatorElem());
1938 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1939 edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1940 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1942 edit_items.push_back (SeparatorElem());
1943 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1944 edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1945 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1946 edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1947 edit_items.push_back (MenuElem (_("Export Range"), sigc::mem_fun(*this, &Editor::export_selection)));
1951 void
1952 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1954 using namespace Menu_Helpers;
1956 /* Playback */
1958 Menu *play_menu = manage (new Menu);
1959 MenuList& play_items = play_menu->items();
1960 play_menu->set_name ("ArdourContextMenu");
1962 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1963 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1964 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1965 play_items.push_back (SeparatorElem());
1966 play_items.push_back (MenuElem (_("Loop Region"), sigc::mem_fun(*this, &Editor::loop_selected_region)));
1968 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1970 /* Selection */
1972 Menu *select_menu = manage (new Menu);
1973 MenuList& select_items = select_menu->items();
1974 select_menu->set_name ("ArdourContextMenu");
1976 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1977 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1978 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1979 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1980 select_items.push_back (SeparatorElem());
1981 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
1982 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
1983 select_items.push_back (SeparatorElem());
1984 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1985 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1986 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1987 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1988 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
1989 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
1990 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
1992 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1994 /* Cut-n-Paste */
1996 Menu *cutnpaste_menu = manage (new Menu);
1997 MenuList& cutnpaste_items = cutnpaste_menu->items();
1998 cutnpaste_menu->set_name ("ArdourContextMenu");
2000 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2001 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2002 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
2004 cutnpaste_items.push_back (SeparatorElem());
2006 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun(*this, &Editor::align), ARDOUR::SyncPoint)));
2007 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun(*this, &Editor::align_relative), ARDOUR::SyncPoint)));
2009 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
2011 /* Adding new material */
2013 edit_items.push_back (SeparatorElem());
2014 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
2015 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
2017 /* Nudge track */
2019 Menu *nudge_menu = manage (new Menu());
2020 MenuList& nudge_items = nudge_menu->items();
2021 nudge_menu->set_name ("ArdourContextMenu");
2023 edit_items.push_back (SeparatorElem());
2024 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2025 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2026 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2027 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2029 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2032 void
2033 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
2035 using namespace Menu_Helpers;
2037 /* Playback */
2039 Menu *play_menu = manage (new Menu);
2040 MenuList& play_items = play_menu->items();
2041 play_menu->set_name ("ArdourContextMenu");
2043 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
2044 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
2045 edit_items.push_back (MenuElem (_("Play"), *play_menu));
2047 /* Selection */
2049 Menu *select_menu = manage (new Menu);
2050 MenuList& select_items = select_menu->items();
2051 select_menu->set_name ("ArdourContextMenu");
2053 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2054 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
2055 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
2056 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
2057 select_items.push_back (SeparatorElem());
2058 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
2059 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
2060 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2061 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2063 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2065 /* Cut-n-Paste */
2067 Menu *cutnpaste_menu = manage (new Menu);
2068 MenuList& cutnpaste_items = cutnpaste_menu->items();
2069 cutnpaste_menu->set_name ("ArdourContextMenu");
2071 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2072 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2073 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
2075 Menu *nudge_menu = manage (new Menu());
2076 MenuList& nudge_items = nudge_menu->items();
2077 nudge_menu->set_name ("ArdourContextMenu");
2079 edit_items.push_back (SeparatorElem());
2080 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2081 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2082 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2083 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2085 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2088 SnapType
2089 Editor::snap_type() const
2091 return _snap_type;
2094 SnapMode
2095 Editor::snap_mode() const
2097 return _snap_mode;
2100 void
2101 Editor::set_snap_to (SnapType st)
2103 unsigned int snap_ind = (unsigned int)st;
2105 _snap_type = st;
2107 if (snap_ind > snap_type_strings.size() - 1) {
2108 snap_ind = 0;
2109 _snap_type = (SnapType)snap_ind;
2112 string str = snap_type_strings[snap_ind];
2114 if (str != snap_type_selector.get_active_text()) {
2115 snap_type_selector.set_active_text (str);
2118 instant_save ();
2120 switch (_snap_type) {
2121 case SnapToBeatDiv32:
2122 case SnapToBeatDiv28:
2123 case SnapToBeatDiv24:
2124 case SnapToBeatDiv16:
2125 case SnapToBeatDiv14:
2126 case SnapToBeatDiv12:
2127 case SnapToBeatDiv10:
2128 case SnapToBeatDiv8:
2129 case SnapToBeatDiv7:
2130 case SnapToBeatDiv6:
2131 case SnapToBeatDiv5:
2132 case SnapToBeatDiv4:
2133 case SnapToBeatDiv3:
2134 case SnapToBeatDiv2:
2135 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
2136 update_tempo_based_rulers ();
2137 break;
2139 case SnapToRegionStart:
2140 case SnapToRegionEnd:
2141 case SnapToRegionSync:
2142 case SnapToRegionBoundary:
2143 build_region_boundary_cache ();
2144 break;
2146 default:
2147 /* relax */
2148 break;
2151 SnapChanged (); /* EMIT SIGNAL */
2154 void
2155 Editor::set_snap_mode (SnapMode mode)
2157 _snap_mode = mode;
2158 string str = snap_mode_strings[(int)mode];
2160 if (str != snap_mode_selector.get_active_text ()) {
2161 snap_mode_selector.set_active_text (str);
2164 instant_save ();
2166 void
2167 Editor::set_edit_point_preference (EditPoint ep, bool force)
2169 bool changed = (_edit_point != ep);
2171 _edit_point = ep;
2172 string str = edit_point_strings[(int)ep];
2174 if (str != edit_point_selector.get_active_text ()) {
2175 edit_point_selector.set_active_text (str);
2178 set_canvas_cursor ();
2180 if (!force && !changed) {
2181 return;
2184 const char* action=NULL;
2186 switch (_edit_point) {
2187 case EditAtPlayhead:
2188 action = "edit-at-playhead";
2189 break;
2190 case EditAtSelectedMarker:
2191 action = "edit-at-marker";
2192 break;
2193 case EditAtMouse:
2194 action = "edit-at-mouse";
2195 break;
2198 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2199 if (act) {
2200 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2203 nframes64_t foo;
2204 bool in_track_canvas;
2206 if (!mouse_frame (foo, in_track_canvas)) {
2207 in_track_canvas = false;
2210 reset_canvas_action_sensitivity (in_track_canvas);
2212 instant_save ();
2216 Editor::set_state (const XMLNode& node, int /*version*/)
2218 const XMLProperty* prop;
2219 XMLNode* geometry;
2220 int x, y, xoff, yoff;
2221 Gdk::Geometry g;
2223 if ((prop = node.property ("id")) != 0) {
2224 _id = prop->value ();
2227 g.base_width = default_width;
2228 g.base_height = default_height;
2229 x = 1;
2230 y = 1;
2231 xoff = 0;
2232 yoff = 21;
2234 if ((geometry = find_named_node (node, "geometry")) != 0) {
2236 XMLProperty* prop;
2238 if ((prop = geometry->property("x_size")) == 0) {
2239 prop = geometry->property ("x-size");
2241 if (prop) {
2242 g.base_width = atoi(prop->value());
2244 if ((prop = geometry->property("y_size")) == 0) {
2245 prop = geometry->property ("y-size");
2247 if (prop) {
2248 g.base_height = atoi(prop->value());
2251 if ((prop = geometry->property ("x_pos")) == 0) {
2252 prop = geometry->property ("x-pos");
2254 if (prop) {
2255 x = atoi (prop->value());
2258 if ((prop = geometry->property ("y_pos")) == 0) {
2259 prop = geometry->property ("y-pos");
2261 if (prop) {
2262 y = atoi (prop->value());
2265 if ((prop = geometry->property ("x_off")) == 0) {
2266 prop = geometry->property ("x-off");
2268 if (prop) {
2269 xoff = atoi (prop->value());
2271 if ((prop = geometry->property ("y_off")) == 0) {
2272 prop = geometry->property ("y-off");
2274 if (prop) {
2275 yoff = atoi (prop->value());
2279 set_default_size (g.base_width, g.base_height);
2280 move (x, y);
2282 if (_session && (prop = node.property ("playhead"))) {
2283 nframes64_t pos;
2284 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2285 playhead_cursor->set_position (pos);
2286 } else {
2287 playhead_cursor->set_position (0);
2290 if ((prop = node.property ("mixer-width"))) {
2291 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2294 if ((prop = node.property ("zoom-focus"))) {
2295 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2298 if ((prop = node.property ("zoom"))) {
2299 reset_zoom (PBD::atof (prop->value()));
2302 if ((prop = node.property ("snap-to"))) {
2303 set_snap_to ((SnapType) atoi (prop->value()));
2306 if ((prop = node.property ("snap-mode"))) {
2307 set_snap_mode ((SnapMode) atoi (prop->value()));
2310 if ((prop = node.property ("mouse-mode"))) {
2311 MouseMode m = str2mousemode(prop->value());
2312 set_mouse_mode (m, true);
2313 } else {
2314 set_mouse_mode (MouseObject, true);
2317 if ((prop = node.property ("left-frame")) != 0){
2318 nframes64_t pos;
2319 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2320 reset_x_origin (pos);
2324 if ((prop = node.property ("y-origin")) != 0) {
2325 reset_y_origin (atof (prop->value ()));
2328 if ((prop = node.property ("internal-edit"))) {
2329 bool yn = string_is_affirmative (prop->value());
2330 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2331 if (act) {
2332 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2333 tact->set_active (!yn);
2334 tact->set_active (yn);
2338 if ((prop = node.property ("join-object-range"))) {
2339 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2342 if ((prop = node.property ("edit-point"))) {
2343 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2346 if ((prop = node.property ("show-measures"))) {
2347 bool yn = string_is_affirmative (prop->value());
2348 _show_measures = !yn;
2349 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2350 if (act) {
2351 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2352 /* do it twice to force the change */
2353 tact->set_active (!yn);
2354 tact->set_active (yn);
2358 if ((prop = node.property ("follow-playhead"))) {
2359 bool yn = string_is_affirmative (prop->value());
2360 set_follow_playhead (yn);
2361 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2362 if (act) {
2363 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2364 if (tact->get_active() != yn) {
2365 tact->set_active (yn);
2370 if ((prop = node.property ("stationary-playhead"))) {
2371 bool yn = (prop->value() == "yes");
2372 set_stationary_playhead (yn);
2373 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2374 if (act) {
2375 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2376 if (tact->get_active() != yn) {
2377 tact->set_active (yn);
2382 if ((prop = node.property ("region-list-sort-type"))) {
2383 RegionListSortType st;
2384 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2387 if ((prop = node.property ("xfades-visible"))) {
2388 bool yn = string_is_affirmative (prop->value());
2389 _xfade_visibility = !yn;
2390 // set_xfade_visibility (yn);
2393 if ((prop = node.property ("show-editor-mixer"))) {
2395 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2396 if (act) {
2398 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2399 bool yn = string_is_affirmative (prop->value());
2401 /* do it twice to force the change */
2403 tact->set_active (!yn);
2404 tact->set_active (yn);
2408 if ((prop = node.property ("show-editor-list"))) {
2410 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2411 assert(act);
2412 if (act) {
2414 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2415 bool yn = string_is_affirmative (prop->value());
2417 /* do it twice to force the change */
2419 tact->set_active (!yn);
2420 tact->set_active (yn);
2424 if ((prop = node.property (X_("editor-list-page")))) {
2425 the_notebook.set_current_page (atoi (prop->value ()));
2428 return 0;
2431 XMLNode&
2432 Editor::get_state ()
2434 XMLNode* node = new XMLNode ("Editor");
2435 char buf[32];
2437 _id.print (buf, sizeof (buf));
2438 node->add_property ("id", buf);
2440 if (is_realized()) {
2441 Glib::RefPtr<Gdk::Window> win = get_window();
2443 int x, y, xoff, yoff, width, height;
2444 win->get_root_origin(x, y);
2445 win->get_position(xoff, yoff);
2446 win->get_size(width, height);
2448 XMLNode* geometry = new XMLNode ("geometry");
2450 snprintf(buf, sizeof(buf), "%d", width);
2451 geometry->add_property("x-size", string(buf));
2452 snprintf(buf, sizeof(buf), "%d", height);
2453 geometry->add_property("y-size", string(buf));
2454 snprintf(buf, sizeof(buf), "%d", x);
2455 geometry->add_property("x-pos", string(buf));
2456 snprintf(buf, sizeof(buf), "%d", y);
2457 geometry->add_property("y-pos", string(buf));
2458 snprintf(buf, sizeof(buf), "%d", xoff);
2459 geometry->add_property("x-off", string(buf));
2460 snprintf(buf, sizeof(buf), "%d", yoff);
2461 geometry->add_property("y-off", string(buf));
2462 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2463 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2464 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2465 geometry->add_property("edit-vertical-pane-pos", string(buf));
2467 node->add_child_nocopy (*geometry);
2470 maybe_add_mixer_strip_width (*node);
2472 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2473 node->add_property ("zoom-focus", buf);
2474 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2475 node->add_property ("zoom", buf);
2476 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2477 node->add_property ("snap-to", buf);
2478 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2479 node->add_property ("snap-mode", buf);
2481 node->add_property ("edit-point", enum_2_string (_edit_point));
2483 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2484 node->add_property ("playhead", buf);
2485 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2486 node->add_property ("left-frame", buf);
2487 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2488 node->add_property ("y-origin", buf);
2490 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2491 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2492 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2493 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2494 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2495 node->add_property ("mouse-mode", enum2str(mouse_mode));
2496 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2497 node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2499 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2500 if (act) {
2501 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2502 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2505 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2506 if (act) {
2507 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2508 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2511 snprintf (buf, sizeof (buf), "%d", the_notebook.get_current_page ());
2512 node->add_property (X_("editor-list-page"), buf);
2514 return *node;
2519 /** @param y y offset from the top of all trackviews.
2520 * @return pair: TimeAxisView that y is over, layer index.
2521 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2522 * in stacked region display mode, otherwise 0.
2524 std::pair<TimeAxisView *, layer_t>
2525 Editor::trackview_by_y_position (double y)
2527 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2529 std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
2530 if (r.first) {
2531 return r;
2535 return std::make_pair ( (TimeAxisView *) 0, 0);
2538 /** Snap a position to the grid, if appropriate, taking into account current
2539 * grid settings and also the state of any snap modifier keys that may be pressed.
2540 * @param start Position to snap.
2541 * @param event Event to get current key modifier information from, or 0.
2543 void
2544 Editor::snap_to_with_modifier (nframes64_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2546 if (!_session || !event) {
2547 return;
2550 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2551 if (_snap_mode == SnapOff) {
2552 snap_to_internal (start, direction, for_mark);
2554 } else {
2555 if (_snap_mode != SnapOff) {
2556 snap_to_internal (start, direction, for_mark);
2561 void
2562 Editor::snap_to (nframes64_t& start, int32_t direction, bool for_mark)
2564 if (!_session || _snap_mode == SnapOff) {
2565 return;
2568 snap_to_internal (start, direction, for_mark);
2571 void
2572 Editor::timecode_snap_to_internal (nframes64_t& start, int32_t direction, bool /*for_mark*/)
2574 const nframes64_t one_timecode_second = (nframes64_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2575 nframes64_t one_timecode_minute = (nframes64_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2577 switch (_snap_type) {
2578 case SnapToTimecodeFrame:
2579 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2580 start = (nframes64_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2581 } else {
2582 start = (nframes64_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2584 break;
2586 case SnapToTimecodeSeconds:
2587 if (_session->timecode_offset_negative())
2589 start += _session->timecode_offset ();
2590 } else {
2591 start -= _session->timecode_offset ();
2593 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2594 start = (nframes64_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2595 } else {
2596 start = (nframes64_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2599 if (_session->timecode_offset_negative())
2601 start -= _session->timecode_offset ();
2602 } else {
2603 start += _session->timecode_offset ();
2605 break;
2607 case SnapToTimecodeMinutes:
2608 if (_session->timecode_offset_negative())
2610 start += _session->timecode_offset ();
2611 } else {
2612 start -= _session->timecode_offset ();
2614 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2615 start = (nframes64_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2616 } else {
2617 start = (nframes64_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2619 if (_session->timecode_offset_negative())
2621 start -= _session->timecode_offset ();
2622 } else {
2623 start += _session->timecode_offset ();
2625 break;
2626 default:
2627 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2628 /*NOTREACHED*/
2632 void
2633 Editor::snap_to_internal (nframes64_t& start, int32_t direction, bool for_mark)
2635 const nframes64_t one_second = _session->frame_rate();
2636 const nframes64_t one_minute = _session->frame_rate() * 60;
2637 nframes64_t presnap = start;
2638 nframes64_t before;
2639 nframes64_t after;
2641 switch (_snap_type) {
2642 case SnapToTimecodeFrame:
2643 case SnapToTimecodeSeconds:
2644 case SnapToTimecodeMinutes:
2645 return timecode_snap_to_internal (start, direction, for_mark);
2647 case SnapToCDFrame:
2648 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2649 start = (nframes64_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2650 } else {
2651 start = (nframes64_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2653 break;
2655 case SnapToSeconds:
2656 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2657 start = (nframes64_t) ceil ((double) start / one_second) * one_second;
2658 } else {
2659 start = (nframes64_t) floor ((double) start / one_second) * one_second;
2661 break;
2663 case SnapToMinutes:
2664 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2665 start = (nframes64_t) ceil ((double) start / one_minute) * one_minute;
2666 } else {
2667 start = (nframes64_t) floor ((double) start / one_minute) * one_minute;
2669 break;
2671 case SnapToBar:
2672 start = _session->tempo_map().round_to_bar (start, direction);
2673 break;
2675 case SnapToBeat:
2676 start = _session->tempo_map().round_to_beat (start, direction);
2677 break;
2679 case SnapToBeatDiv32:
2680 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2681 break;
2682 case SnapToBeatDiv28:
2683 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2684 break;
2685 case SnapToBeatDiv24:
2686 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2687 break;
2688 case SnapToBeatDiv16:
2689 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2690 break;
2691 case SnapToBeatDiv14:
2692 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2693 break;
2694 case SnapToBeatDiv12:
2695 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2696 break;
2697 case SnapToBeatDiv10:
2698 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2699 break;
2700 case SnapToBeatDiv8:
2701 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2702 break;
2703 case SnapToBeatDiv7:
2704 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2705 break;
2706 case SnapToBeatDiv6:
2707 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2708 break;
2709 case SnapToBeatDiv5:
2710 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2711 break;
2712 case SnapToBeatDiv4:
2713 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2714 break;
2715 case SnapToBeatDiv3:
2716 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2717 break;
2718 case SnapToBeatDiv2:
2719 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2720 break;
2722 case SnapToMark:
2723 if (for_mark) {
2724 return;
2727 _session->locations()->marks_either_side (start, before, after);
2729 if (before == max_frames) {
2730 start = after;
2731 } else if (after == max_frames) {
2732 start = before;
2733 } else if (before != max_frames && after != max_frames) {
2734 /* have before and after */
2735 if ((start - before) < (after - start)) {
2736 start = before;
2737 } else {
2738 start = after;
2742 break;
2744 case SnapToRegionStart:
2745 case SnapToRegionEnd:
2746 case SnapToRegionSync:
2747 case SnapToRegionBoundary:
2748 if (!region_boundary_cache.empty()) {
2750 vector<nframes64_t>::iterator prev = region_boundary_cache.end ();
2751 vector<nframes64_t>::iterator next = region_boundary_cache.end ();
2753 if (direction > 0) {
2754 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2755 } else {
2756 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2759 if (next != region_boundary_cache.begin ()) {
2760 prev = next;
2761 prev--;
2764 nframes64_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2765 nframes64_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2767 if (start > (p + n) / 2) {
2768 start = n;
2769 } else {
2770 start = p;
2773 break;
2776 switch (_snap_mode) {
2777 case SnapNormal:
2778 return;
2780 case SnapMagnetic:
2782 if (presnap > start) {
2783 if (presnap > (start + unit_to_frame(snap_threshold))) {
2784 start = presnap;
2787 } else if (presnap < start) {
2788 if (presnap < (start - unit_to_frame(snap_threshold))) {
2789 start = presnap;
2793 default:
2794 /* handled at entry */
2795 return;
2801 void
2802 Editor::setup_toolbar ()
2804 string pixmap_path;
2806 /* Mode Buttons (tool selection) */
2808 mouse_move_button.set_relief(Gtk::RELIEF_NONE);
2809 mouse_select_button.set_relief(Gtk::RELIEF_NONE);
2810 mouse_gain_button.set_relief(Gtk::RELIEF_NONE);
2811 mouse_zoom_button.set_relief(Gtk::RELIEF_NONE);
2812 mouse_timefx_button.set_relief(Gtk::RELIEF_NONE);
2813 mouse_audition_button.set_relief(Gtk::RELIEF_NONE);
2814 // internal_edit_button.set_relief(Gtk::RELIEF_NONE);
2815 join_object_range_button.set_relief(Gtk::RELIEF_NONE);
2817 HBox* mode_box = manage(new HBox);
2818 mode_box->set_border_width (2);
2819 mode_box->set_spacing(4);
2821 /* table containing mode buttons */
2823 HBox* mouse_mode_button_box = manage (new HBox ());
2825 if (Profile->get_sae()) {
2826 mouse_mode_button_box->pack_start (mouse_move_button);
2827 } else {
2828 mouse_mode_button_box->pack_start (mouse_move_button);
2829 mouse_mode_button_box->pack_start (join_object_range_button);
2830 mouse_mode_button_box->pack_start (mouse_select_button);
2833 mouse_mode_button_box->pack_start (mouse_zoom_button);
2835 if (!Profile->get_sae()) {
2836 mouse_mode_button_box->pack_start (mouse_gain_button);
2839 mouse_mode_button_box->pack_start (mouse_timefx_button);
2840 mouse_mode_button_box->pack_start (mouse_audition_button);
2841 mouse_mode_button_box->pack_start (internal_edit_button);
2843 vector<string> edit_mode_strings;
2844 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2845 if (!Profile->get_sae()) {
2846 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2848 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2850 edit_mode_selector.set_name ("EditModeSelector");
2851 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2852 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2854 mode_box->pack_start (edit_mode_selector);
2855 mode_box->pack_start (*mouse_mode_button_box);
2857 _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2858 _mouse_mode_tearoff->set_name ("MouseModeBase");
2859 _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2861 if (Profile->get_sae()) {
2862 _mouse_mode_tearoff->set_can_be_torn_off (false);
2865 _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2866 &_mouse_mode_tearoff->tearoff_window()));
2867 _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2868 &_mouse_mode_tearoff->tearoff_window(), 1));
2869 _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2870 &_mouse_mode_tearoff->tearoff_window()));
2871 _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2872 &_mouse_mode_tearoff->tearoff_window(), 1));
2874 mouse_move_button.set_mode (false);
2875 mouse_select_button.set_mode (false);
2876 mouse_gain_button.set_mode (false);
2877 mouse_zoom_button.set_mode (false);
2878 mouse_timefx_button.set_mode (false);
2879 mouse_audition_button.set_mode (false);
2880 join_object_range_button.set_mode (false);
2882 mouse_move_button.set_name ("MouseModeButton");
2883 mouse_select_button.set_name ("MouseModeButton");
2884 mouse_gain_button.set_name ("MouseModeButton");
2885 mouse_zoom_button.set_name ("MouseModeButton");
2886 mouse_timefx_button.set_name ("MouseModeButton");
2887 mouse_audition_button.set_name ("MouseModeButton");
2888 internal_edit_button.set_name ("MouseModeButton");
2889 join_object_range_button.set_name ("MouseModeButton");
2891 mouse_move_button.unset_flags (CAN_FOCUS);
2892 mouse_select_button.unset_flags (CAN_FOCUS);
2893 mouse_gain_button.unset_flags (CAN_FOCUS);
2894 mouse_zoom_button.unset_flags (CAN_FOCUS);
2895 mouse_timefx_button.unset_flags (CAN_FOCUS);
2896 mouse_audition_button.unset_flags (CAN_FOCUS);
2897 internal_edit_button.unset_flags (CAN_FOCUS);
2898 join_object_range_button.unset_flags (CAN_FOCUS);
2900 /* Zoom */
2902 zoom_box.set_spacing (1);
2903 zoom_box.set_border_width (0);
2905 zoom_in_button.set_name ("EditorTimeButton");
2906 zoom_in_button.set_image (*(manage (new Image (Stock::ZOOM_IN, Gtk::ICON_SIZE_MENU))));
2907 zoom_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), false));
2909 zoom_out_button.set_name ("EditorTimeButton");
2910 zoom_out_button.set_image (*(manage (new Image (Stock::ZOOM_OUT, Gtk::ICON_SIZE_MENU))));
2911 zoom_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), true));
2913 zoom_out_full_button.set_name ("EditorTimeButton");
2914 zoom_out_full_button.set_image (*(manage (new Image (Stock::ZOOM_100, Gtk::ICON_SIZE_MENU))));
2915 zoom_out_full_button.signal_clicked().connect (sigc::mem_fun(*this, &Editor::temporal_zoom_session));
2917 zoom_focus_selector.set_name ("ZoomFocusSelector");
2918 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
2919 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2921 zoom_box.pack_start (zoom_out_button, false, false);
2922 zoom_box.pack_start (zoom_in_button, false, false);
2923 zoom_box.pack_start (zoom_out_full_button, false, false);
2925 /* Track zoom buttons */
2926 tav_expand_button.set_name ("TrackHeightButton");
2927 tav_expand_button.set_size_request(-1,20);
2928 tav_expand_button.add (*(manage (new Image (::get_icon("tav_exp")))));
2929 tav_expand_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), true));
2931 tav_shrink_button.set_name ("TrackHeightButton");
2932 tav_shrink_button.set_size_request(-1,20);
2933 tav_shrink_button.add (*(manage (new Image (::get_icon("tav_shrink")))));
2934 tav_shrink_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), false));
2936 track_zoom_box.set_spacing (1);
2937 track_zoom_box.set_border_width (0);
2939 track_zoom_box.pack_start (tav_shrink_button, false, false);
2940 track_zoom_box.pack_start (tav_expand_button, false, false);
2942 HBox* zbc = manage (new HBox);
2943 zbc->pack_start (zoom_focus_selector, PACK_SHRINK);
2944 zoom_vbox.pack_start (*zbc, PACK_SHRINK);
2945 zoom_vbox.pack_start (zoom_box, PACK_SHRINK);
2946 zoom_vbox.pack_start (track_zoom_box, PACK_SHRINK);
2948 snap_box.set_spacing (1);
2949 snap_box.set_border_width (2);
2951 snap_type_selector.set_name ("SnapTypeSelector");
2952 set_popdown_strings (snap_type_selector, snap_type_strings, true);
2953 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2955 snap_mode_selector.set_name ("SnapModeSelector");
2956 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
2957 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2959 edit_point_selector.set_name ("EditPointSelector");
2960 set_popdown_strings (edit_point_selector, edit_point_strings, true);
2961 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2963 snap_box.pack_start (snap_mode_selector, false, false);
2964 snap_box.pack_start (snap_type_selector, false, false);
2965 snap_box.pack_start (edit_point_selector, false, false);
2967 /* Nudge */
2969 HBox *nudge_box = manage (new HBox);
2970 nudge_box->set_spacing(1);
2971 nudge_box->set_border_width (2);
2973 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2974 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2976 nudge_box->pack_start (nudge_backward_button, false, false);
2977 nudge_box->pack_start (nudge_forward_button, false, false);
2978 nudge_box->pack_start (nudge_clock, false, false);
2981 /* Pack everything in... */
2983 HBox* hbox = manage (new HBox);
2984 hbox->set_spacing(10);
2986 _tools_tearoff = manage (new TearOff (*hbox));
2987 _tools_tearoff->set_name ("MouseModeBase");
2988 _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
2990 if (Profile->get_sae()) {
2991 _tools_tearoff->set_can_be_torn_off (false);
2994 _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2995 &_tools_tearoff->tearoff_window()));
2996 _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2997 &_tools_tearoff->tearoff_window(), 0));
2998 _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2999 &_tools_tearoff->tearoff_window()));
3000 _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
3001 &_tools_tearoff->tearoff_window(), 0));
3003 toolbar_hbox.set_spacing (10);
3004 toolbar_hbox.set_border_width (1);
3006 toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
3007 toolbar_hbox.pack_start (*_tools_tearoff, false, false);
3009 hbox->pack_start (snap_box, false, false);
3010 hbox->pack_start (*nudge_box, false, false);
3011 hbox->pack_start (panic_box, false, false);
3013 hbox->show_all ();
3015 toolbar_base.set_name ("ToolBarBase");
3016 toolbar_base.add (toolbar_hbox);
3018 toolbar_frame.set_shadow_type (SHADOW_OUT);
3019 toolbar_frame.set_name ("BaseFrame");
3020 toolbar_frame.add (toolbar_base);
3023 void
3024 Editor::setup_tooltips ()
3026 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
3027 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Gain Automation"));
3028 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
3029 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions"));
3030 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
3031 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
3032 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
3033 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: context-click for possible operations"));
3034 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
3035 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
3036 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
3037 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
3038 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
3039 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
3040 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
3041 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
3042 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
3043 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
3044 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
3045 ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
3046 ARDOUR_UI::instance()->set_tip (midi_panic_button, _("Send note off and reset controller messages on all MIDI channels"));
3049 void
3050 Editor::midi_panic ()
3052 cerr << "MIDI panic\n";
3054 if (_session) {
3055 _session->midi_panic();
3059 void
3060 Editor::setup_midi_toolbar ()
3062 RefPtr<Action> act;
3064 /* Midi sound notes */
3065 midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
3066 midi_sound_notes.set_relief(Gtk::RELIEF_NONE);
3067 midi_sound_notes.unset_flags (CAN_FOCUS);
3069 /* Panic */
3071 act = ActionManager::get_action (X_("MIDI"), X_("panic"));
3072 midi_panic_button.set_name("MidiPanicButton");
3073 act->connect_proxy (midi_panic_button);
3075 panic_box.pack_start (midi_sound_notes , true, true);
3076 panic_box.pack_start (midi_panic_button, true, true);
3080 Editor::convert_drop_to_paths (
3081 vector<ustring>& paths,
3082 const RefPtr<Gdk::DragContext>& /*context*/,
3083 gint /*x*/,
3084 gint /*y*/,
3085 const SelectionData& data,
3086 guint /*info*/,
3087 guint /*time*/)
3089 if (_session == 0) {
3090 return -1;
3093 vector<ustring> uris = data.get_uris();
3095 if (uris.empty()) {
3097 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3098 are actually URI lists. So do it by hand.
3101 if (data.get_target() != "text/plain") {
3102 return -1;
3105 /* Parse the "uri-list" format that Nautilus provides,
3106 where each pathname is delimited by \r\n.
3108 THERE MAY BE NO NULL TERMINATING CHAR!!!
3111 ustring txt = data.get_text();
3112 const char* p;
3113 const char* q;
3115 p = (const char *) malloc (txt.length() + 1);
3116 txt.copy ((char *) p, txt.length(), 0);
3117 ((char*)p)[txt.length()] = '\0';
3119 while (p)
3121 if (*p != '#')
3123 while (g_ascii_isspace (*p))
3124 p++;
3126 q = p;
3127 while (*q && (*q != '\n') && (*q != '\r')) {
3128 q++;
3131 if (q > p)
3133 q--;
3134 while (q > p && g_ascii_isspace (*q))
3135 q--;
3137 if (q > p)
3139 uris.push_back (ustring (p, q - p + 1));
3143 p = strchr (p, '\n');
3144 if (p)
3145 p++;
3148 free ((void*)p);
3150 if (uris.empty()) {
3151 return -1;
3155 for (vector<ustring>::iterator i = uris.begin(); i != uris.end(); ++i) {
3157 if ((*i).substr (0,7) == "file://") {
3159 ustring p = *i;
3160 PBD::url_decode (p);
3162 // scan forward past three slashes
3164 ustring::size_type slashcnt = 0;
3165 ustring::size_type n = 0;
3166 ustring::iterator x = p.begin();
3168 while (slashcnt < 3 && x != p.end()) {
3169 if ((*x) == '/') {
3170 slashcnt++;
3171 } else if (slashcnt == 3) {
3172 break;
3174 ++n;
3175 ++x;
3178 if (slashcnt != 3 || x == p.end()) {
3179 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3180 continue;
3183 paths.push_back (p.substr (n - 1));
3187 return 0;
3190 void
3191 Editor::new_tempo_section ()
3196 void
3197 Editor::map_transport_state ()
3199 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3201 if (_session && _session->transport_stopped()) {
3202 have_pending_keyboard_selection = false;
3205 update_loop_range_view (true);
3208 /* UNDO/REDO */
3210 Editor::State::State (PublicEditor const * e)
3212 selection = new Selection (e);
3215 Editor::State::~State ()
3217 delete selection;
3220 void
3221 Editor::begin_reversible_command (string name)
3223 if (_session) {
3224 _session->begin_reversible_command (name);
3228 void
3229 Editor::commit_reversible_command ()
3231 if (_session) {
3232 _session->commit_reversible_command ();
3236 void
3237 Editor::set_route_group_solo (Route& route, bool yn)
3239 RouteGroup *route_group;
3241 if ((route_group = route.route_group()) != 0) {
3242 route_group->apply (&Route::set_solo, yn, this);
3243 } else {
3244 route.set_solo (yn, this);
3248 void
3249 Editor::set_route_group_mute (Route& route, bool yn)
3251 RouteGroup *route_group = 0;
3253 if ((route_group = route.route_group()) != 0) {
3254 route_group->apply (&Route::set_mute, yn, this);
3255 } else {
3256 route.set_mute (yn, this);
3260 void
3261 Editor::history_changed ()
3263 string label;
3265 if (undo_action && _session) {
3266 if (_session->undo_depth() == 0) {
3267 label = _("Undo");
3268 } else {
3269 label = string_compose(_("Undo (%1)"), _session->next_undo());
3271 undo_action->property_label() = label;
3274 if (redo_action && _session) {
3275 if (_session->redo_depth() == 0) {
3276 label = _("Redo");
3277 } else {
3278 label = string_compose(_("Redo (%1)"), _session->next_redo());
3280 redo_action->property_label() = label;
3284 void
3285 Editor::duplicate_dialog (bool with_dialog)
3287 float times = 1.0f;
3289 if (mouse_mode == MouseRange) {
3290 if (selection->time.length() == 0) {
3291 return;
3295 RegionSelection rs;
3296 get_regions_for_action (rs);
3298 if (mouse_mode != MouseRange) {
3300 if (rs.empty()) {
3301 return;
3305 if (with_dialog) {
3307 ArdourDialog win (_("Duplicate"));
3308 Label label (_("Number of duplications:"));
3309 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3310 SpinButton spinner (adjustment, 0.0, 1);
3311 HBox hbox;
3313 win.get_vbox()->set_spacing (12);
3314 win.get_vbox()->pack_start (hbox);
3315 hbox.set_border_width (6);
3316 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3318 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3319 place, visually. so do this by hand.
3322 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3323 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3324 spinner.grab_focus();
3326 hbox.show ();
3327 label.show ();
3328 spinner.show ();
3330 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3331 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3332 win.set_default_response (RESPONSE_ACCEPT);
3334 win.set_position (WIN_POS_MOUSE);
3336 spinner.grab_focus ();
3338 switch (win.run ()) {
3339 case RESPONSE_ACCEPT:
3340 break;
3341 default:
3342 return;
3345 times = adjustment.get_value();
3348 if (mouse_mode == MouseRange) {
3349 duplicate_selection (times);
3350 } else {
3351 duplicate_some_regions (rs, times);
3355 void
3356 Editor::show_verbose_canvas_cursor ()
3358 verbose_canvas_cursor->raise_to_top();
3359 verbose_canvas_cursor->show();
3360 verbose_cursor_visible = true;
3363 void
3364 Editor::hide_verbose_canvas_cursor ()
3366 verbose_canvas_cursor->hide();
3367 verbose_cursor_visible = false;
3370 double
3371 Editor::clamp_verbose_cursor_x (double x)
3373 if (x < 0) {
3374 x = 0;
3375 } else {
3376 x = min (_canvas_width - 200.0, x);
3378 return x;
3381 double
3382 Editor::clamp_verbose_cursor_y (double y)
3384 if (y < canvas_timebars_vsize) {
3385 y = canvas_timebars_vsize;
3386 } else {
3387 y = min (_canvas_height - 50, y);
3389 return y;
3392 void
3393 Editor::show_verbose_canvas_cursor_with (const string & txt)
3395 verbose_canvas_cursor->property_text() = txt.c_str();
3397 int x, y;
3398 double wx, wy;
3400 track_canvas->get_pointer (x, y);
3401 track_canvas->window_to_world (x, y, wx, wy);
3403 /* don't get too close to the edge */
3404 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (wx);
3405 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (wy);
3407 show_verbose_canvas_cursor ();
3410 void
3411 Editor::set_verbose_canvas_cursor (const string & txt, double x, double y)
3413 verbose_canvas_cursor->property_text() = txt.c_str();
3414 /* don't get too close to the edge */
3415 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (x);
3416 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (y);
3419 void
3420 Editor::set_verbose_canvas_cursor_text (const string & txt)
3422 verbose_canvas_cursor->property_text() = txt.c_str();
3425 void
3426 Editor::set_edit_mode (EditMode m)
3428 Config->set_edit_mode (m);
3431 void
3432 Editor::cycle_edit_mode ()
3434 switch (Config->get_edit_mode()) {
3435 case Slide:
3436 if (Profile->get_sae()) {
3437 Config->set_edit_mode (Lock);
3438 } else {
3439 Config->set_edit_mode (Splice);
3441 break;
3442 case Splice:
3443 Config->set_edit_mode (Lock);
3444 break;
3445 case Lock:
3446 Config->set_edit_mode (Slide);
3447 break;
3451 void
3452 Editor::edit_mode_selection_done ()
3454 if (_session == 0) {
3455 return;
3458 string choice = edit_mode_selector.get_active_text();
3459 EditMode mode = Slide;
3461 if (choice == _("Splice Edit")) {
3462 mode = Splice;
3463 } else if (choice == _("Slide Edit")) {
3464 mode = Slide;
3465 } else if (choice == _("Lock Edit")) {
3466 mode = Lock;
3469 Config->set_edit_mode (mode);
3472 void
3473 Editor::snap_type_selection_done ()
3475 string choice = snap_type_selector.get_active_text();
3476 SnapType snaptype = SnapToBeat;
3478 if (choice == _("Beats/2")) {
3479 snaptype = SnapToBeatDiv2;
3480 } else if (choice == _("Beats/3")) {
3481 snaptype = SnapToBeatDiv3;
3482 } else if (choice == _("Beats/4")) {
3483 snaptype = SnapToBeatDiv4;
3484 } else if (choice == _("Beats/5")) {
3485 snaptype = SnapToBeatDiv5;
3486 } else if (choice == _("Beats/6")) {
3487 snaptype = SnapToBeatDiv6;
3488 } else if (choice == _("Beats/7")) {
3489 snaptype = SnapToBeatDiv7;
3490 } else if (choice == _("Beats/8")) {
3491 snaptype = SnapToBeatDiv8;
3492 } else if (choice == _("Beats/10")) {
3493 snaptype = SnapToBeatDiv10;
3494 } else if (choice == _("Beats/12")) {
3495 snaptype = SnapToBeatDiv12;
3496 } else if (choice == _("Beats/14")) {
3497 snaptype = SnapToBeatDiv14;
3498 } else if (choice == _("Beats/16")) {
3499 snaptype = SnapToBeatDiv16;
3500 } else if (choice == _("Beats/24")) {
3501 snaptype = SnapToBeatDiv24;
3502 } else if (choice == _("Beats/28")) {
3503 snaptype = SnapToBeatDiv28;
3504 } else if (choice == _("Beats/32")) {
3505 snaptype = SnapToBeatDiv32;
3506 } else if (choice == _("Beats")) {
3507 snaptype = SnapToBeat;
3508 } else if (choice == _("Bars")) {
3509 snaptype = SnapToBar;
3510 } else if (choice == _("Marks")) {
3511 snaptype = SnapToMark;
3512 } else if (choice == _("Region starts")) {
3513 snaptype = SnapToRegionStart;
3514 } else if (choice == _("Region ends")) {
3515 snaptype = SnapToRegionEnd;
3516 } else if (choice == _("Region bounds")) {
3517 snaptype = SnapToRegionBoundary;
3518 } else if (choice == _("Region syncs")) {
3519 snaptype = SnapToRegionSync;
3520 } else if (choice == _("CD Frames")) {
3521 snaptype = SnapToCDFrame;
3522 } else if (choice == _("Timecode Frames")) {
3523 snaptype = SnapToTimecodeFrame;
3524 } else if (choice == _("Timecode Seconds")) {
3525 snaptype = SnapToTimecodeSeconds;
3526 } else if (choice == _("Timecode Minutes")) {
3527 snaptype = SnapToTimecodeMinutes;
3528 } else if (choice == _("Seconds")) {
3529 snaptype = SnapToSeconds;
3530 } else if (choice == _("Minutes")) {
3531 snaptype = SnapToMinutes;
3534 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3535 if (ract) {
3536 ract->set_active ();
3540 void
3541 Editor::snap_mode_selection_done ()
3543 string choice = snap_mode_selector.get_active_text();
3544 SnapMode mode = SnapNormal;
3546 if (choice == _("No Grid")) {
3547 mode = SnapOff;
3548 } else if (choice == _("Grid")) {
3549 mode = SnapNormal;
3550 } else if (choice == _("Magnetic")) {
3551 mode = SnapMagnetic;
3554 RefPtr<RadioAction> ract = snap_mode_action (mode);
3556 if (ract) {
3557 ract->set_active (true);
3561 void
3562 Editor::cycle_edit_point (bool with_marker)
3564 switch (_edit_point) {
3565 case EditAtMouse:
3566 set_edit_point_preference (EditAtPlayhead);
3567 break;
3568 case EditAtPlayhead:
3569 if (with_marker) {
3570 set_edit_point_preference (EditAtSelectedMarker);
3571 } else {
3572 set_edit_point_preference (EditAtMouse);
3574 break;
3575 case EditAtSelectedMarker:
3576 set_edit_point_preference (EditAtMouse);
3577 break;
3581 void
3582 Editor::edit_point_selection_done ()
3584 string choice = edit_point_selector.get_active_text();
3585 EditPoint ep = EditAtSelectedMarker;
3587 if (choice == _("Marker")) {
3588 set_edit_point_preference (EditAtSelectedMarker);
3589 } else if (choice == _("Playhead")) {
3590 set_edit_point_preference (EditAtPlayhead);
3591 } else {
3592 set_edit_point_preference (EditAtMouse);
3595 RefPtr<RadioAction> ract = edit_point_action (ep);
3597 if (ract) {
3598 ract->set_active (true);
3602 void
3603 Editor::zoom_focus_selection_done ()
3605 string choice = zoom_focus_selector.get_active_text();
3606 ZoomFocus focus_type = ZoomFocusLeft;
3608 if (choice == _("Left")) {
3609 focus_type = ZoomFocusLeft;
3610 } else if (choice == _("Right")) {
3611 focus_type = ZoomFocusRight;
3612 } else if (choice == _("Center")) {
3613 focus_type = ZoomFocusCenter;
3614 } else if (choice == _("Playhead")) {
3615 focus_type = ZoomFocusPlayhead;
3616 } else if (choice == _("Mouse")) {
3617 focus_type = ZoomFocusMouse;
3618 } else if (choice == _("Edit point")) {
3619 focus_type = ZoomFocusEdit;
3622 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3624 if (ract) {
3625 ract->set_active ();
3629 gint
3630 Editor::edit_controls_button_release (GdkEventButton* ev)
3632 if (Keyboard::is_context_menu_event (ev)) {
3633 ARDOUR_UI::instance()->add_route (this);
3635 return TRUE;
3638 gint
3639 Editor::mouse_select_button_release (GdkEventButton* ev)
3641 /* this handles just right-clicks */
3643 if (ev->button != 3) {
3644 return false;
3647 return true;
3650 void
3651 Editor::set_zoom_focus (ZoomFocus f)
3653 string str = zoom_focus_strings[(int)f];
3655 if (str != zoom_focus_selector.get_active_text()) {
3656 zoom_focus_selector.set_active_text (str);
3659 if (zoom_focus != f) {
3660 zoom_focus = f;
3662 ZoomFocusChanged (); /* EMIT_SIGNAL */
3664 instant_save ();
3668 void
3669 Editor::ensure_float (Window& win)
3671 win.set_transient_for (*this);
3674 void
3675 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3677 /* recover or initialize pane positions. do this here rather than earlier because
3678 we don't want the positions to change the child allocations, which they seem to do.
3681 int pos;
3682 XMLProperty* prop;
3683 char buf[32];
3684 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3685 int width, height;
3687 enum Pane {
3688 Horizontal = 0x1,
3689 Vertical = 0x2
3692 static Pane done;
3694 XMLNode* geometry;
3696 width = default_width;
3697 height = default_height;
3699 if ((geometry = find_named_node (*node, "geometry")) != 0) {
3701 prop = geometry->property ("x-size");
3702 if (prop) {
3703 width = atoi (prop->value());
3705 prop = geometry->property ("y-size");
3706 if (prop) {
3707 height = atoi (prop->value());
3711 if (which == static_cast<Paned*> (&edit_pane)) {
3713 if (done & Horizontal) {
3714 return;
3717 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3718 /* initial allocation is 90% to canvas, 10% to notebook */
3719 pos = (int) floor (alloc.get_width() * 0.90f);
3720 snprintf (buf, sizeof(buf), "%d", pos);
3721 } else {
3722 pos = atoi (prop->value());
3725 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3726 edit_pane.set_position (pos);
3727 pre_maximal_horizontal_pane_position = pos;
3730 done = (Pane) (done | Horizontal);
3732 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3734 if (done & Vertical) {
3735 return;
3738 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3739 /* initial allocation is 90% to canvas, 10% to summary */
3740 pos = (int) floor (alloc.get_height() * 0.90f);
3741 snprintf (buf, sizeof(buf), "%d", pos);
3742 } else {
3743 pos = atoi (prop->value());
3746 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3747 editor_summary_pane.set_position (pos);
3748 pre_maximal_vertical_pane_position = pos;
3751 done = (Pane) (done | Vertical);
3755 void
3756 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3758 if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) {
3759 top_hbox.remove (toolbar_frame);
3763 void
3764 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3766 if (toolbar_frame.get_parent() == 0) {
3767 top_hbox.pack_end (toolbar_frame);
3771 void
3772 Editor::set_show_measures (bool yn)
3774 if (_show_measures != yn) {
3775 hide_measures ();
3777 if ((_show_measures = yn) == true) {
3778 if (tempo_lines)
3779 tempo_lines->show();
3780 draw_measures ();
3782 instant_save ();
3786 void
3787 Editor::toggle_follow_playhead ()
3789 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3790 if (act) {
3791 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3792 set_follow_playhead (tact->get_active());
3796 void
3797 Editor::set_follow_playhead (bool yn)
3799 if (_follow_playhead != yn) {
3800 if ((_follow_playhead = yn) == true) {
3801 /* catch up */
3802 reset_x_origin_to_follow_playhead ();
3804 instant_save ();
3808 void
3809 Editor::toggle_stationary_playhead ()
3811 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3812 if (act) {
3813 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3814 set_stationary_playhead (tact->get_active());
3818 void
3819 Editor::set_stationary_playhead (bool yn)
3821 if (_stationary_playhead != yn) {
3822 if ((_stationary_playhead = yn) == true) {
3823 /* catch up */
3824 // FIXME need a 3.0 equivalent of this 2.X call
3825 // update_current_screen ();
3827 instant_save ();
3831 void
3832 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3834 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3835 if (xfade) {
3836 xfade->set_active (!xfade->active());
3840 void
3841 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3843 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3844 if (xfade) {
3845 xfade->set_follow_overlap (!xfade->following_overlap());
3849 void
3850 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3852 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3854 if (!xfade) {
3855 return;
3858 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3860 ensure_float (cew);
3862 switch (cew.run ()) {
3863 case RESPONSE_ACCEPT:
3864 break;
3865 default:
3866 return;
3869 cew.apply ();
3870 PropertyChange all_crossfade_properties;
3871 all_crossfade_properties.add (ARDOUR::Properties::active);
3872 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3873 xfade->PropertyChanged (all_crossfade_properties);
3876 PlaylistSelector&
3877 Editor::playlist_selector () const
3879 return *_playlist_selector;
3882 Evoral::MusicalTime
3883 Editor::get_grid_type_as_beats (bool& success, nframes64_t position)
3885 success = true;
3887 switch (_snap_type) {
3888 case SnapToBeat:
3889 return 1.0;
3890 break;
3892 case SnapToBeatDiv32:
3893 return 1.0/32.0;
3894 break;
3895 case SnapToBeatDiv28:
3896 return 1.0/28.0;
3897 break;
3898 case SnapToBeatDiv24:
3899 return 1.0/24.0;
3900 break;
3901 case SnapToBeatDiv16:
3902 return 1.0/16.0;
3903 break;
3904 case SnapToBeatDiv14:
3905 return 1.0/14.0;
3906 break;
3907 case SnapToBeatDiv12:
3908 return 1.0/12.0;
3909 break;
3910 case SnapToBeatDiv10:
3911 return 1.0/10.0;
3912 break;
3913 case SnapToBeatDiv8:
3914 return 1.0/8.0;
3915 break;
3916 case SnapToBeatDiv7:
3917 return 1.0/7.0;
3918 break;
3919 case SnapToBeatDiv6:
3920 return 1.0/6.0;
3921 break;
3922 case SnapToBeatDiv5:
3923 return 1.0/5.0;
3924 break;
3925 case SnapToBeatDiv4:
3926 return 1.0/4.0;
3927 break;
3928 case SnapToBeatDiv3:
3929 return 1.0/3.0;
3930 break;
3931 case SnapToBeatDiv2:
3932 return 1.0/2.0;
3933 break;
3935 case SnapToBar:
3936 if (_session) {
3937 return _session->tempo_map().meter_at (position).beats_per_bar();
3939 break;
3941 case SnapToCDFrame:
3942 case SnapToTimecodeFrame:
3943 case SnapToTimecodeSeconds:
3944 case SnapToTimecodeMinutes:
3945 case SnapToSeconds:
3946 case SnapToMinutes:
3947 case SnapToRegionStart:
3948 case SnapToRegionEnd:
3949 case SnapToRegionSync:
3950 case SnapToRegionBoundary:
3951 default:
3952 success = false;
3953 break;
3956 return 0.0;
3959 nframes64_t
3960 Editor::get_nudge_distance (nframes64_t pos, nframes64_t& next)
3962 nframes64_t ret;
3964 ret = nudge_clock.current_duration (pos);
3965 next = ret + 1; /* XXXX fix me */
3967 return ret;
3971 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3973 ArdourDialog dialog (_("Playlist Deletion"));
3974 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3975 "If left alone, no audio files used by it will be cleaned.\n"
3976 "If deleted, audio files used by it alone by will cleaned."),
3977 pl->name()));
3979 dialog.set_position (WIN_POS_CENTER);
3980 dialog.get_vbox()->pack_start (label);
3982 label.show ();
3984 dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT);
3985 dialog.add_button (_("Keep playlist"), RESPONSE_REJECT);
3986 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3988 switch (dialog.run ()) {
3989 case RESPONSE_ACCEPT:
3990 /* delete the playlist */
3991 return 0;
3992 break;
3994 case RESPONSE_REJECT:
3995 /* keep the playlist */
3996 return 1;
3997 break;
3999 default:
4000 break;
4003 return -1;
4006 bool
4007 Editor::audio_region_selection_covers (nframes64_t where)
4009 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
4010 if ((*a)->region()->covers (where)) {
4011 return true;
4015 return false;
4018 void
4019 Editor::prepare_for_cleanup ()
4021 cut_buffer->clear_regions ();
4022 cut_buffer->clear_playlists ();
4024 selection->clear_regions ();
4025 selection->clear_playlists ();
4027 _regions->suspend_redisplay ();
4030 void
4031 Editor::finish_cleanup ()
4033 _regions->resume_redisplay ();
4036 Location*
4037 Editor::transport_loop_location()
4039 if (_session) {
4040 return _session->locations()->auto_loop_location();
4041 } else {
4042 return 0;
4046 Location*
4047 Editor::transport_punch_location()
4049 if (_session) {
4050 return _session->locations()->auto_punch_location();
4051 } else {
4052 return 0;
4056 bool
4057 Editor::control_layout_scroll (GdkEventScroll* ev)
4059 if (Keyboard::some_magic_widget_has_focus()) {
4060 return false;
4063 switch (ev->direction) {
4064 case GDK_SCROLL_UP:
4065 scroll_tracks_up_line ();
4066 return true;
4067 break;
4069 case GDK_SCROLL_DOWN:
4070 scroll_tracks_down_line ();
4071 return true;
4073 default:
4074 /* no left/right handling yet */
4075 break;
4078 return false;
4081 void
4082 Editor::session_state_saved (string snap_name)
4084 ENSURE_GUI_THREAD (*this, &Editor::session_state_saved, snap_name);
4086 update_title ();
4087 _snapshots->redisplay ();
4090 void
4091 Editor::maximise_editing_space ()
4093 _mouse_mode_tearoff->set_visible (false);
4094 _tools_tearoff->set_visible (false);
4096 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
4097 pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
4098 pre_maximal_editor_width = this->get_width ();
4099 pre_maximal_editor_height = this->get_height ();
4101 if (post_maximal_horizontal_pane_position == 0) {
4102 post_maximal_horizontal_pane_position = edit_pane.get_width();
4105 if (post_maximal_vertical_pane_position == 0) {
4106 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
4109 fullscreen ();
4111 if (post_maximal_editor_width) {
4112 edit_pane.set_position (post_maximal_horizontal_pane_position -
4113 abs(post_maximal_editor_width - pre_maximal_editor_width));
4114 } else {
4115 edit_pane.set_position (post_maximal_horizontal_pane_position);
4118 if (post_maximal_editor_height) {
4119 editor_summary_pane.set_position (post_maximal_vertical_pane_position -
4120 abs(post_maximal_editor_height - pre_maximal_editor_height));
4121 } else {
4122 editor_summary_pane.set_position (post_maximal_vertical_pane_position);
4126 void
4127 Editor::restore_editing_space ()
4129 // user changed width/height of panes during fullscreen
4131 if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
4132 post_maximal_horizontal_pane_position = edit_pane.get_position();
4135 if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
4136 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
4139 unfullscreen();
4141 _mouse_mode_tearoff->set_visible (true);
4142 _tools_tearoff->set_visible (true);
4143 post_maximal_editor_width = this->get_width();
4144 post_maximal_editor_height = this->get_height();
4146 edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
4147 editor_summary_pane.set_position (pre_maximal_vertical_pane_position + abs(this->get_height() - pre_maximal_editor_height));
4151 * Make new playlists for a given track and also any others that belong
4152 * to the same active route group with the `edit' property.
4153 * @param v Track.
4156 void
4157 Editor::new_playlists (TimeAxisView* v)
4159 begin_reversible_command (_("new playlists"));
4160 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4161 _session->playlists->get (playlists);
4162 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4163 commit_reversible_command ();
4167 * Use a copy of the current playlist for a given track and also any others that belong
4168 * to the same active route group with the `edit' property.
4169 * @param v Track.
4172 void
4173 Editor::copy_playlists (TimeAxisView* v)
4175 begin_reversible_command (_("copy playlists"));
4176 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4177 _session->playlists->get (playlists);
4178 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4179 commit_reversible_command ();
4182 /** Clear the current playlist for a given track and also any others that belong
4183 * to the same active route group with the `edit' property.
4184 * @param v Track.
4187 void
4188 Editor::clear_playlists (TimeAxisView* v)
4190 begin_reversible_command (_("clear playlists"));
4191 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4192 _session->playlists->get (playlists);
4193 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4194 commit_reversible_command ();
4197 void
4198 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4200 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4203 void
4204 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4206 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4209 void
4210 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4212 atv.clear_playlist ();
4215 bool
4216 Editor::on_key_press_event (GdkEventKey* ev)
4218 return key_press_focus_accelerator_handler (*this, ev);
4221 bool
4222 Editor::on_key_release_event (GdkEventKey* ev)
4224 return Gtk::Window::on_key_release_event (ev);
4225 // return key_press_focus_accelerator_handler (*this, ev);
4228 /** Queue up a change to the viewport x origin.
4229 * @param frame New x origin.
4231 void
4232 Editor::reset_x_origin (nframes64_t frame)
4234 queue_visual_change (frame);
4237 void
4238 Editor::reset_y_origin (double y)
4240 queue_visual_change_y (y);
4243 void
4244 Editor::reset_zoom (double fpu)
4246 queue_visual_change (fpu);
4249 void
4250 Editor::reposition_and_zoom (nframes64_t frame, double fpu)
4252 reset_x_origin (frame);
4253 reset_zoom (fpu);
4255 if (!no_save_visual) {
4256 undo_visual_stack.push_back (current_visual_state(false));
4260 Editor::VisualState*
4261 Editor::current_visual_state (bool with_tracks)
4263 VisualState* vs = new VisualState;
4264 vs->y_position = vertical_adjustment.get_value();
4265 vs->frames_per_unit = frames_per_unit;
4266 vs->leftmost_frame = leftmost_frame;
4267 vs->zoom_focus = zoom_focus;
4269 if (with_tracks) {
4270 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4271 vs->track_states.push_back (TAVState ((*i), &(*i)->get_state()));
4275 return vs;
4278 void
4279 Editor::undo_visual_state ()
4281 if (undo_visual_stack.empty()) {
4282 return;
4285 redo_visual_stack.push_back (current_visual_state());
4287 VisualState* vs = undo_visual_stack.back();
4288 undo_visual_stack.pop_back();
4289 use_visual_state (*vs);
4292 void
4293 Editor::redo_visual_state ()
4295 if (redo_visual_stack.empty()) {
4296 return;
4299 undo_visual_stack.push_back (current_visual_state());
4301 VisualState* vs = redo_visual_stack.back();
4302 redo_visual_stack.pop_back();
4303 use_visual_state (*vs);
4306 void
4307 Editor::swap_visual_state ()
4309 if (undo_visual_stack.empty()) {
4310 redo_visual_state ();
4311 } else {
4312 undo_visual_state ();
4316 void
4317 Editor::use_visual_state (VisualState& vs)
4319 no_save_visual = true;
4321 _routes->suspend_redisplay ();
4323 vertical_adjustment.set_value (vs.y_position);
4325 set_zoom_focus (vs.zoom_focus);
4326 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4328 for (list<TAVState>::iterator i = vs.track_states.begin(); i != vs.track_states.end(); ++i) {
4329 TrackViewList::iterator t;
4331 /* check if the track still exists - it could have been deleted */
4333 if ((t = find (track_views.begin(), track_views.end(), i->first)) != track_views.end()) {
4334 (*t)->set_state (*(i->second), Stateful::loading_state_version);
4339 if (!vs.track_states.empty()) {
4340 _routes->update_visibility ();
4343 _routes->resume_redisplay ();
4345 no_save_visual = false;
4348 void
4349 Editor::set_frames_per_unit (double fpu)
4351 /* this is the core function that controls the zoom level of the canvas. it is called
4352 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4355 if (fpu == frames_per_unit) {
4356 return;
4359 if (fpu < 2.0) {
4360 fpu = 2.0;
4364 /* don't allow zooms that fit more than the maximum number
4365 of frames into an 800 pixel wide space.
4368 if (max_frames / fpu < 800.0) {
4369 return;
4372 if (tempo_lines)
4373 tempo_lines->tempo_map_changed();
4375 frames_per_unit = fpu;
4376 post_zoom ();
4379 void
4380 Editor::post_zoom ()
4382 // convert fpu to frame count
4384 nframes64_t frames = (nframes64_t) floor (frames_per_unit * _canvas_width);
4386 if (frames_per_unit != zoom_range_clock.current_duration()) {
4387 zoom_range_clock.set (frames);
4390 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4391 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4392 (*i)->reshow_selection (selection->time);
4396 ZoomChanged (); /* EMIT_SIGNAL */
4398 //reset_scrolling_region ();
4400 if (playhead_cursor) {
4401 playhead_cursor->set_position (playhead_cursor->current_frame);
4404 refresh_location_display();
4405 _summary->set_overlays_dirty ();
4407 instant_save ();
4410 void
4411 Editor::queue_visual_change (nframes64_t where)
4413 pending_visual_change.add (VisualChange::TimeOrigin);
4414 pending_visual_change.time_origin = where;
4415 ensure_visual_change_idle_handler ();
4418 void
4419 Editor::queue_visual_change (double fpu)
4421 pending_visual_change.add (VisualChange::ZoomLevel);
4422 pending_visual_change.frames_per_unit = fpu;
4424 ensure_visual_change_idle_handler ();
4427 void
4428 Editor::queue_visual_change_y (double y)
4430 pending_visual_change.add (VisualChange::YOrigin);
4431 pending_visual_change.y_origin = y;
4433 ensure_visual_change_idle_handler ();
4436 void
4437 Editor::ensure_visual_change_idle_handler ()
4439 if (pending_visual_change.idle_handler_id < 0) {
4440 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4445 Editor::_idle_visual_changer (void* arg)
4447 return static_cast<Editor*>(arg)->idle_visual_changer ();
4451 Editor::idle_visual_changer ()
4453 VisualChange::Type p = pending_visual_change.pending;
4454 pending_visual_change.pending = (VisualChange::Type) 0;
4456 double last_time_origin = horizontal_position ();
4458 if (p & VisualChange::ZoomLevel) {
4459 set_frames_per_unit (pending_visual_change.frames_per_unit);
4461 compute_fixed_ruler_scale ();
4462 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4463 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4464 update_tempo_based_rulers ();
4466 if (p & VisualChange::TimeOrigin) {
4467 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4469 if (p & VisualChange::YOrigin) {
4470 vertical_adjustment.set_value (pending_visual_change.y_origin);
4473 if (last_time_origin == horizontal_position ()) {
4474 /* changed signal not emitted */
4475 update_fixed_rulers ();
4476 redisplay_tempo (true);
4479 _summary->set_overlays_dirty ();
4481 pending_visual_change.idle_handler_id = -1;
4482 return 0; /* this is always a one-shot call */
4485 struct EditorOrderTimeAxisSorter {
4486 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4487 return a->order () < b->order ();
4491 void
4492 Editor::sort_track_selection (TrackViewList* sel)
4494 EditorOrderTimeAxisSorter cmp;
4496 if (sel) {
4497 sel->sort (cmp);
4498 } else {
4499 selection->tracks.sort (cmp);
4503 nframes64_t
4504 Editor::get_preferred_edit_position (bool ignore_playhead)
4506 bool ignored;
4507 nframes64_t where = 0;
4508 EditPoint ep = _edit_point;
4510 if (entered_marker) {
4511 return entered_marker->position();
4514 if (ignore_playhead && ep == EditAtPlayhead) {
4515 ep = EditAtSelectedMarker;
4518 switch (ep) {
4519 case EditAtPlayhead:
4520 where = _session->audible_frame();
4521 break;
4523 case EditAtSelectedMarker:
4524 if (!selection->markers.empty()) {
4525 bool is_start;
4526 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4527 if (loc) {
4528 if (is_start) {
4529 where = loc->start();
4530 } else {
4531 where = loc->end();
4533 break;
4536 /* fallthru */
4538 default:
4539 case EditAtMouse:
4540 if (!mouse_frame (where, ignored)) {
4541 /* XXX not right but what can we do ? */
4542 return 0;
4544 snap_to (where);
4545 break;
4548 return where;
4551 void
4552 Editor::set_loop_range (nframes64_t start, nframes64_t end, string cmd)
4554 if (!_session) return;
4556 begin_reversible_command (cmd);
4558 Location* tll;
4560 if ((tll = transport_loop_location()) == 0) {
4561 Location* loc = new Location (start, end, _("Loop"), Location::IsAutoLoop);
4562 XMLNode &before = _session->locations()->get_state();
4563 _session->locations()->add (loc, true);
4564 _session->set_auto_loop_location (loc);
4565 XMLNode &after = _session->locations()->get_state();
4566 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4567 } else {
4568 XMLNode &before = tll->get_state();
4569 tll->set_hidden (false, this);
4570 tll->set (start, end);
4571 XMLNode &after = tll->get_state();
4572 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4575 commit_reversible_command ();
4578 void
4579 Editor::set_punch_range (nframes64_t start, nframes64_t end, string cmd)
4581 if (!_session) return;
4583 begin_reversible_command (cmd);
4585 Location* tpl;
4587 if ((tpl = transport_punch_location()) == 0) {
4588 Location* loc = new Location (start, end, _("Loop"), Location::IsAutoPunch);
4589 XMLNode &before = _session->locations()->get_state();
4590 _session->locations()->add (loc, true);
4591 _session->set_auto_loop_location (loc);
4592 XMLNode &after = _session->locations()->get_state();
4593 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4595 else {
4596 XMLNode &before = tpl->get_state();
4597 tpl->set_hidden (false, this);
4598 tpl->set (start, end);
4599 XMLNode &after = tpl->get_state();
4600 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4603 commit_reversible_command ();
4606 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4607 * @param rs List to which found regions are added.
4608 * @param where Time to look at.
4609 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4611 void
4612 Editor::get_regions_at (RegionSelection& rs, nframes64_t where, const TrackViewList& ts) const
4614 const TrackViewList* tracks;
4616 if (ts.empty()) {
4617 tracks = &track_views;
4618 } else {
4619 tracks = &ts;
4622 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4623 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4624 if (rtv) {
4625 boost::shared_ptr<Track> tr;
4626 boost::shared_ptr<Playlist> pl;
4628 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4630 Playlist::RegionList* regions = pl->regions_at (
4631 (nframes64_t) floor ( (double)where * tr->speed()));
4633 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4634 RegionView* rv = rtv->view()->find_view (*i);
4635 if (rv) {
4636 rs.add (rv);
4640 delete regions;
4646 void
4647 Editor::get_regions_after (RegionSelection& rs, nframes64_t where, const TrackViewList& ts) const
4649 const TrackViewList* tracks;
4651 if (ts.empty()) {
4652 tracks = &track_views;
4653 } else {
4654 tracks = &ts;
4657 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4658 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4659 if (rtv) {
4660 boost::shared_ptr<Track> tr;
4661 boost::shared_ptr<Playlist> pl;
4663 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4665 Playlist::RegionList* regions = pl->regions_touched (
4666 (nframes64_t) floor ( (double)where * tr->speed()), max_frames);
4668 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4670 RegionView* rv = rtv->view()->find_view (*i);
4672 if (rv) {
4673 rs.push_back (rv);
4677 delete regions;
4683 /** Find all regions which are either:
4684 * - selected or
4685 * - the entered_regionview (if allow_entered == true) or
4686 * - under the preferred edit position AND on a selected track, or on a track
4687 * which is in the same active edit-enable route group as a selected region (if allow_edit_position == true)
4688 * @param rs Returned region list.
4689 * @param allow_entered true to include the entered_regionview in the list.
4691 void
4692 Editor::get_regions_for_action (RegionSelection& rs, bool allow_entered, bool allow_edit_position)
4694 /* Start with selected regions */
4695 rs = selection->regions;
4697 /* Add the entered_regionview, if requested */
4698 if (allow_entered && entered_regionview) {
4699 rs.add (entered_regionview);
4702 if (allow_edit_position) {
4704 TrackViewList tracks = selection->tracks;
4706 /* tracks is currently the set of selected tracks; add any other tracks that
4707 * have regions that are in the same edit-activated route group as one of
4708 * our regions */
4709 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
4711 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4712 if (g && g->is_active() && g->is_edit()) {
4713 tracks.add (axis_views_from_routes (g->route_list()));
4718 if (!tracks.empty()) {
4719 /* now find regions that are at the edit position on those tracks */
4720 nframes64_t const where = get_preferred_edit_position ();
4721 get_regions_at (rs, where, tracks);
4726 void
4727 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4729 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4731 RouteTimeAxisView* tatv;
4733 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4735 boost::shared_ptr<Playlist> pl;
4736 vector<boost::shared_ptr<Region> > results;
4737 RegionView* marv;
4738 boost::shared_ptr<Track> tr;
4740 if ((tr = tatv->track()) == 0) {
4741 /* bus */
4742 continue;
4745 if ((pl = (tr->playlist())) != 0) {
4746 pl->get_region_list_equivalent_regions (region, results);
4749 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4750 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4751 regions.push_back (marv);
4759 void
4760 Editor::show_rhythm_ferret ()
4762 if (rhythm_ferret == 0) {
4763 rhythm_ferret = new RhythmFerret(*this);
4766 rhythm_ferret->set_session (_session);
4767 rhythm_ferret->show ();
4768 rhythm_ferret->present ();
4771 void
4772 Editor::show_global_port_matrix (ARDOUR::DataType t)
4774 if (_global_port_matrix[t] == 0) {
4775 _global_port_matrix[t] = new GlobalPortMatrixWindow (_session, t);
4778 _global_port_matrix[t]->show ();
4781 void
4782 Editor::first_idle ()
4784 MessageDialog* dialog = 0;
4786 if (track_views.size() > 1) {
4787 dialog = new MessageDialog (*this,
4788 string_compose (_("Please wait while %1 loads visual data"), PROGRAM_NAME),
4789 true,
4790 Gtk::MESSAGE_INFO,
4791 Gtk::BUTTONS_NONE);
4792 dialog->present ();
4793 ARDOUR_UI::instance()->flush_pending ();
4796 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4797 (*t)->first_idle();
4800 // first idle adds route children (automation tracks), so we need to redisplay here
4801 _routes->redisplay ();
4803 delete dialog;
4805 _have_idled = true;
4808 gboolean
4809 Editor::_idle_resize (gpointer arg)
4811 return ((Editor*)arg)->idle_resize ();
4814 void
4815 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4817 if (resize_idle_id < 0) {
4818 resize_idle_id = g_idle_add (_idle_resize, this);
4819 _pending_resize_amount = 0;
4822 /* make a note of the smallest resulting height, so that we can clamp the
4823 lower limit at TimeAxisView::hSmall */
4825 int32_t min_resulting = INT32_MAX;
4827 _pending_resize_amount += h;
4828 _pending_resize_view = view;
4830 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4832 if (selection->tracks.contains (_pending_resize_view)) {
4833 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4834 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4838 if (min_resulting < 0) {
4839 min_resulting = 0;
4842 /* clamp */
4843 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4844 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4848 /** Handle pending resizing of tracks */
4849 bool
4850 Editor::idle_resize ()
4852 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4854 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4855 selection->tracks.contains (_pending_resize_view)) {
4857 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4858 if (*i != _pending_resize_view) {
4859 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4864 _pending_resize_amount = 0;
4865 flush_canvas ();
4866 _group_tabs->set_dirty ();
4867 resize_idle_id = -1;
4869 return false;
4872 void
4873 Editor::located ()
4875 ENSURE_GUI_THREAD (*this, &Editor::located);
4877 playhead_cursor->set_position (_session->audible_frame ());
4878 if (_follow_playhead && !_pending_initial_locate) {
4879 reset_x_origin_to_follow_playhead ();
4882 _pending_locate_request = false;
4883 _pending_initial_locate = false;
4886 void
4887 Editor::region_view_added (RegionView *)
4889 _summary->set_dirty ();
4892 void
4893 Editor::streamview_height_changed ()
4895 _summary->set_dirty ();
4898 TimeAxisView*
4899 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4901 TrackViewList::const_iterator j = track_views.begin ();
4902 while (j != track_views.end()) {
4903 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4904 if (rtv && rtv->route() == r) {
4905 return rtv;
4907 ++j;
4910 return 0;
4914 TrackViewList
4915 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4917 TrackViewList t;
4919 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4920 TimeAxisView* tv = axis_view_from_route (*i);
4921 if (tv) {
4922 t.push_back (tv);
4926 return t;
4930 void
4931 Editor::handle_new_route (RouteList& routes)
4933 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4935 RouteTimeAxisView *rtv;
4936 list<RouteTimeAxisView*> new_views;
4938 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4939 boost::shared_ptr<Route> route = (*x);
4941 if (route->is_hidden() || route->is_monitor()) {
4942 continue;
4945 DataType dt = route->input()->default_type();
4947 if (dt == ARDOUR::DataType::AUDIO) {
4948 rtv = new AudioTimeAxisView (*this, _session, route, *track_canvas);
4949 } else if (dt == ARDOUR::DataType::MIDI) {
4950 rtv = new MidiTimeAxisView (*this, _session, route, *track_canvas);
4951 } else {
4952 throw unknown_type();
4955 new_views.push_back (rtv);
4956 track_views.push_back (rtv);
4958 rtv->effective_gain_display ();
4960 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4961 rtv->view()->HeightChanged.connect (sigc::mem_fun (*this, &Editor::streamview_height_changed));
4964 _routes->routes_added (new_views);
4966 if (show_editor_mixer_when_tracks_arrive) {
4967 show_editor_mixer (true);
4970 editor_list_button.set_sensitive (true);
4972 _summary->set_dirty ();
4975 void
4976 Editor::timeaxisview_deleted (TimeAxisView *tv)
4978 if (_session && _session->deletion_in_progress()) {
4979 /* the situation is under control */
4980 return;
4983 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
4986 _routes->route_removed (tv);
4988 if (tv == entered_track) {
4989 entered_track = 0;
4992 /* remove it from the list of track views */
4994 TrackViewList::iterator i;
4996 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
4997 i = track_views.erase (i);
5000 /* update whatever the current mixer strip is displaying, if revelant */
5002 boost::shared_ptr<Route> route;
5003 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
5005 if (rtav) {
5006 route = rtav->route ();
5009 if (current_mixer_strip && current_mixer_strip->route() == route) {
5011 TimeAxisView* next_tv;
5013 if (track_views.empty()) {
5014 next_tv = 0;
5015 } else if (i == track_views.end()) {
5016 next_tv = track_views.front();
5017 } else {
5018 next_tv = (*i);
5022 if (next_tv) {
5023 set_selected_mixer_strip (*next_tv);
5024 } else {
5025 /* make the editor mixer strip go away setting the
5026 * button to inactive (which also unticks the menu option)
5029 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
5034 void
5035 Editor::hide_track_in_display (TimeAxisView* tv, bool /*temponly*/)
5037 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
5039 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
5040 // this will hide the mixer strip
5041 set_selected_mixer_strip (*tv);
5044 _routes->hide_track_in_display (*tv);
5047 bool
5048 Editor::sync_track_view_list_and_routes ()
5050 track_views = TrackViewList (_routes->views ());
5052 _summary->set_dirty ();
5053 _group_tabs->set_dirty ();
5055 return false; // do not call again (until needed)
5058 void
5059 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
5061 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5062 theslot (**i);
5066 RouteTimeAxisView*
5067 Editor::get_route_view_by_id (PBD::ID& id)
5069 RouteTimeAxisView* v;
5071 for(TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5072 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5073 if(v->route()->id() == id) {
5074 return v;
5079 return 0;
5082 void
5083 Editor::fit_route_group (RouteGroup *g)
5085 TrackViewList ts = axis_views_from_routes (g->route_list ());
5086 fit_tracks (ts);
5089 void
5090 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5092 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5094 if (r == 0) {
5095 _session->cancel_audition ();
5096 return;
5099 if (_session->is_auditioning()) {
5100 _session->cancel_audition ();
5101 if (r == last_audition_region) {
5102 return;
5106 _session->audition_region (r);
5107 last_audition_region = r;
5111 void
5112 Editor::hide_a_region (boost::shared_ptr<Region> r)
5114 r->set_hidden (true);
5117 void
5118 Editor::show_a_region (boost::shared_ptr<Region> r)
5120 r->set_hidden (false);
5123 void
5124 Editor::audition_region_from_region_list ()
5126 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5129 void
5130 Editor::hide_region_from_region_list ()
5132 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5135 void
5136 Editor::show_region_in_region_list ()
5138 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5141 void
5142 Editor::start_step_editing ()
5144 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5147 void
5148 Editor::stop_step_editing ()
5150 step_edit_connection.disconnect ();
5153 bool
5154 Editor::check_step_edit ()
5156 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5157 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5158 if (mtv) {
5159 mtv->check_step_edit ();
5163 return true; // do it again, till we stop
5166 bool
5167 Editor::horizontal_scroll_left_press ()
5169 ++_scroll_callbacks;
5171 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5172 /* delay the first auto-repeat */
5173 return true;
5176 double x = leftmost_position() - current_page_frames() / 5;
5177 if (x < 0) {
5178 x = 0;
5181 reset_x_origin (x);
5183 /* do hacky auto-repeat */
5184 if (!_scroll_connection.connected ()) {
5185 _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press), 100);
5186 _scroll_callbacks = 0;
5189 return true;
5192 void
5193 Editor::horizontal_scroll_left_release ()
5195 _scroll_connection.disconnect ();
5198 bool
5199 Editor::horizontal_scroll_right_press ()
5201 ++_scroll_callbacks;
5203 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5204 /* delay the first auto-repeat */
5205 return true;
5208 reset_x_origin (leftmost_position() + current_page_frames() / 5);
5210 /* do hacky auto-repeat */
5211 if (!_scroll_connection.connected ()) {
5212 _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press), 100);
5213 _scroll_callbacks = 0;
5216 return true;
5219 void
5220 Editor::horizontal_scroll_right_release ()
5222 _scroll_connection.disconnect ();
5225 /** Queue a change for the Editor viewport x origin to follow the playhead */
5226 void
5227 Editor::reset_x_origin_to_follow_playhead ()
5229 nframes64_t const frame = playhead_cursor->current_frame;
5231 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5233 if (_session->transport_speed() < 0) {
5235 if (frame > (current_page_frames() / 2)) {
5236 center_screen (frame-(current_page_frames()/2));
5237 } else {
5238 center_screen (current_page_frames()/2);
5241 } else {
5243 if (frame < leftmost_frame) {
5244 /* moving left */
5245 nframes64_t l = 0;
5246 if (_session->transport_rolling()) {
5247 /* rolling; end up with the playhead at the right of the page */
5248 l = frame - current_page_frames ();
5249 } else {
5250 /* not rolling: end up with the playhead 3/4 of the way along the page */
5251 l = frame - (3 * current_page_frames() / 4);
5254 if (l < 0) {
5255 l = 0;
5258 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5259 } else {
5260 /* moving right */
5261 if (_session->transport_rolling()) {
5262 /* rolling: end up with the playhead on the left of the page */
5263 center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5264 } else {
5265 /* not rolling: end up with the playhead 1/4 of the way along the page */
5266 center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5273 void
5274 Editor::super_rapid_screen_update ()
5276 if (!_session || !_session->engine().running()) {
5277 return;
5280 /* METERING / MIXER STRIPS */
5282 /* update track meters, if required */
5283 if (is_mapped() && meters_running) {
5284 RouteTimeAxisView* rtv;
5285 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5286 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5287 rtv->fast_update ();
5292 /* and any current mixer strip */
5293 if (current_mixer_strip) {
5294 current_mixer_strip->fast_update ();
5297 /* PLAYHEAD AND VIEWPORT */
5299 nframes64_t const frame = _session->audible_frame();
5301 /* There are a few reasons why we might not update the playhead / viewport stuff:
5303 * 1. we don't update things when there's a pending locate request, otherwise
5304 * when the editor requests a locate there is a chance that this method
5305 * will move the playhead before the locate request is processed, causing
5306 * a visual glitch.
5307 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5308 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5311 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5313 last_update_frame = frame;
5315 if (!_dragging_playhead) {
5316 playhead_cursor->set_position (frame);
5319 if (!_stationary_playhead) {
5321 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5322 reset_x_origin_to_follow_playhead ();
5325 } else {
5327 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5328 editor canvas
5330 #if 0
5331 // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5332 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5333 if (target <= 0.0) {
5334 target = 0.0;
5336 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5337 target = (target * 0.15) + (current * 0.85);
5338 } else {
5339 /* relax */
5342 current = target;
5343 set_horizontal_position (current);
5344 #endif
5351 void
5352 Editor::session_going_away ()
5354 _have_idled = false;
5356 _session_connections.drop_connections ();
5358 super_rapid_screen_update_connection.disconnect ();
5360 selection->clear ();
5361 cut_buffer->clear ();
5363 clicked_regionview = 0;
5364 clicked_axisview = 0;
5365 clicked_routeview = 0;
5366 clicked_crossfadeview = 0;
5367 entered_regionview = 0;
5368 entered_track = 0;
5369 last_update_frame = 0;
5370 _drags->abort ();
5372 playhead_cursor->canvas_item.hide ();
5374 /* rip everything out of the list displays */
5376 _regions->clear ();
5377 _routes->clear ();
5378 _route_groups->clear ();
5380 /* do this first so that deleting a track doesn't reset cms to null
5381 and thus cause a leak.
5384 if (current_mixer_strip) {
5385 if (current_mixer_strip->get_parent() != 0) {
5386 global_hpacker.remove (*current_mixer_strip);
5388 delete current_mixer_strip;
5389 current_mixer_strip = 0;
5392 /* delete all trackviews */
5394 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5395 delete *i;
5397 track_views.clear ();
5399 zoom_range_clock.set_session (0);
5400 nudge_clock.set_session (0);
5402 editor_list_button.set_active(false);
5403 editor_list_button.set_sensitive(false);
5405 /* clear tempo/meter rulers */
5406 remove_metric_marks ();
5407 hide_measures ();
5408 clear_marker_display ();
5410 delete current_bbt_points;
5411 current_bbt_points = 0;
5413 /* get rid of any existing editor mixer strip */
5415 WindowTitle title(Glib::get_application_name());
5416 title += _("Editor");
5418 set_title (title.get_string());
5420 SessionHandlePtr::session_going_away ();
5424 void
5425 Editor::show_editor_list (bool yn)
5427 if (yn) {
5428 the_notebook.show();
5429 } else {
5430 the_notebook.hide();