how about that ... a monitor/main section .. GUI is still unfinished .. several small...
[ardour2.git] / gtk2_ardour / editor.cc
blob1c56abb47d64269043f86a812f6b87069f64cd45
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/16"),
149 N_("Beats/8"),
150 N_("Beats/4"),
151 N_("Beats/3"),
152 N_("Beats"),
153 N_("Bars"),
154 N_("Marks"),
155 N_("Region starts"),
156 N_("Region ends"),
157 N_("Region syncs"),
158 N_("Region bounds"),
162 static const gchar *_snap_mode_strings[] = {
163 N_("No Grid"),
164 N_("Grid"),
165 N_("Magnetic"),
169 static const gchar *_edit_point_strings[] = {
170 N_("Playhead"),
171 N_("Marker"),
172 N_("Mouse"),
176 static const gchar *_zoom_focus_strings[] = {
177 N_("Left"),
178 N_("Right"),
179 N_("Center"),
180 N_("Playhead"),
181 N_("Mouse"),
182 N_("Edit point"),
186 #ifdef USE_RUBBERBAND
187 static const gchar *_rb_opt_strings[] = {
188 N_("Mushy"),
189 N_("Smooth"),
190 N_("Balanced multitimbral mixture"),
191 N_("Unpitched percussion with stable notes"),
192 N_("Crisp monophonic instrumental"),
193 N_("Unpitched solo percussion"),
196 #endif
198 /* Soundfile drag-n-drop */
200 Gdk::Cursor* Editor::cross_hair_cursor = 0;
201 Gdk::Cursor* Editor::selector_cursor = 0;
202 Gdk::Cursor* Editor::trimmer_cursor = 0;
203 Gdk::Cursor* Editor::grabber_cursor = 0;
204 Gdk::Cursor* Editor::grabber_edit_point_cursor = 0;
205 Gdk::Cursor* Editor::zoom_cursor = 0;
206 Gdk::Cursor* Editor::time_fx_cursor = 0;
207 Gdk::Cursor* Editor::fader_cursor = 0;
208 Gdk::Cursor* Editor::speaker_cursor = 0;
209 Gdk::Cursor* Editor::midi_pencil_cursor = 0;
210 Gdk::Cursor* Editor::midi_select_cursor = 0;
211 Gdk::Cursor* Editor::midi_resize_cursor = 0;
212 Gdk::Cursor* Editor::midi_erase_cursor = 0;
213 Gdk::Cursor* Editor::wait_cursor = 0;
214 Gdk::Cursor* Editor::timebar_cursor = 0;
215 Gdk::Cursor* Editor::transparent_cursor = 0;
217 void
218 show_me_the_size (Requisition* r, const char* what)
220 cerr << "size of " << what << " = " << r->width << " x " << r->height << endl;
223 Editor::Editor ()
224 : _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
226 /* time display buttons */
227 , minsec_label (_("Mins:Secs"))
228 , bbt_label (_("Bars:Beats"))
229 , timecode_label (_("Timecode"))
230 , frame_label (_("Samples"))
231 , tempo_label (_("Tempo"))
232 , meter_label (_("Meter"))
233 , mark_label (_("Location Markers"))
234 , range_mark_label (_("Range Markers"))
235 , transport_mark_label (_("Loop/Punch Ranges"))
236 , cd_mark_label (_("CD Markers"))
237 , edit_packer (4, 4, true)
239 /* the values here don't matter: layout widgets
240 reset them as needed.
243 , vertical_adjustment (0.0, 0.0, 10.0, 400.0)
244 , horizontal_adjustment (0.0, 0.0, 20.0, 1200.0)
246 /* tool bar related */
248 , zoom_range_clock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, false, true)
250 , toolbar_selection_clock_table (2,3)
252 , automation_mode_button (_("mode"))
253 , global_automation_button (_("automation"))
255 , midi_panic_button (_("Panic"))
257 #ifdef WITH_CMT
258 , image_socket_listener(0)
259 #endif
261 /* nudge */
263 , nudge_clock (X_("nudge"), false, X_("NudgeClock"), true, false, true)
264 , meters_running(false)
265 , _pending_locate_request (false)
266 , _pending_initial_locate (false)
269 constructed = false;
271 /* we are a singleton */
273 PublicEditor::_instance = this;
275 _have_idled = false;
277 selection = new Selection (this);
278 cut_buffer = new Selection (this);
280 clicked_regionview = 0;
281 clicked_axisview = 0;
282 clicked_routeview = 0;
283 clicked_crossfadeview = 0;
284 clicked_control_point = 0;
285 last_update_frame = 0;
286 _drags = new DragManager (this);
287 current_mixer_strip = 0;
288 current_bbt_points = 0;
289 tempo_lines = 0;
291 snap_type_strings = I18N (_snap_type_strings);
292 snap_mode_strings = I18N (_snap_mode_strings);
293 zoom_focus_strings = I18N (_zoom_focus_strings);
294 edit_point_strings = I18N (_edit_point_strings);
295 #ifdef USE_RUBBERBAND
296 rb_opt_strings = I18N (_rb_opt_strings);
297 #endif
299 snap_threshold = 5.0;
300 bbt_beat_subdivision = 4;
301 _canvas_width = 0;
302 _canvas_height = 0;
303 last_autoscroll_x = 0;
304 last_autoscroll_y = 0;
305 autoscroll_active = false;
306 autoscroll_timeout_tag = -1;
307 interthread_progress_window = 0;
308 logo_item = 0;
310 analysis_window = 0;
312 current_interthread_info = 0;
313 _show_measures = true;
314 _show_waveforms_recording = true;
315 show_gain_after_trim = false;
316 verbose_cursor_on = true;
317 last_item_entered = 0;
318 last_item_entered_n = 0;
320 have_pending_keyboard_selection = false;
321 _follow_playhead = true;
322 _xfade_visibility = true;
323 editor_ruler_menu = 0;
324 no_ruler_shown_update = false;
325 marker_menu = 0;
326 start_end_marker_menu = 0;
327 range_marker_menu = 0;
328 marker_menu_item = 0;
329 tm_marker_menu = 0;
330 transport_marker_menu = 0;
331 new_transport_marker_menu = 0;
332 editor_mixer_strip_width = Wide;
333 show_editor_mixer_when_tracks_arrive = false;
334 region_edit_menu_split_multichannel_item = 0;
335 region_edit_menu_split_item = 0;
336 temp_location = 0;
337 leftmost_frame = 0;
338 current_stepping_trackview = 0;
339 entered_track = 0;
340 entered_regionview = 0;
341 entered_marker = 0;
342 clear_entered_track = false;
343 _new_regionviews_show_envelope = false;
344 current_timefx = 0;
345 playhead_cursor = 0;
346 button_release_can_deselect = true;
347 _dragging_playhead = false;
348 _dragging_edit_point = false;
349 select_new_marker = false;
350 rhythm_ferret = 0;
351 _bundle_manager = 0;
352 for (ARDOUR::DataType::iterator i = ARDOUR::DataType::begin(); i != ARDOUR::DataType::end(); ++i) {
353 _global_port_matrix[*i] = 0;
355 allow_vertical_scroll = false;
356 no_save_visual = false;
357 resize_idle_id = -1;
359 scrubbing_direction = 0;
361 sfbrowser = 0;
363 location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get();
364 location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get();
365 location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get();
366 location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get();
367 location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get();
369 _edit_point = EditAtMouse;
370 _internal_editing = false;
371 current_canvas_cursor = 0;
373 frames_per_unit = 2048; /* too early to use reset_zoom () */
375 zoom_focus = ZoomFocusLeft;
376 set_zoom_focus (ZoomFocusLeft);
377 zoom_range_clock.ValueChanged.connect (sigc::mem_fun(*this, &Editor::zoom_adjustment_changed));
379 bbt_label.set_name ("EditorTimeButton");
380 bbt_label.set_size_request (-1, (int)timebar_height);
381 bbt_label.set_alignment (1.0, 0.5);
382 bbt_label.set_padding (5,0);
383 bbt_label.hide ();
384 bbt_label.set_no_show_all();
385 minsec_label.set_name ("EditorTimeButton");
386 minsec_label.set_size_request (-1, (int)timebar_height);
387 minsec_label.set_alignment (1.0, 0.5);
388 minsec_label.set_padding (5,0);
389 minsec_label.hide ();
390 minsec_label.set_no_show_all();
391 timecode_label.set_name ("EditorTimeButton");
392 timecode_label.set_size_request (-1, (int)timebar_height);
393 timecode_label.set_alignment (1.0, 0.5);
394 timecode_label.set_padding (5,0);
395 timecode_label.hide ();
396 timecode_label.set_no_show_all();
397 frame_label.set_name ("EditorTimeButton");
398 frame_label.set_size_request (-1, (int)timebar_height);
399 frame_label.set_alignment (1.0, 0.5);
400 frame_label.set_padding (5,0);
401 frame_label.hide ();
402 frame_label.set_no_show_all();
404 tempo_label.set_name ("EditorTimeButton");
405 tempo_label.set_size_request (-1, (int)timebar_height);
406 tempo_label.set_alignment (1.0, 0.5);
407 tempo_label.set_padding (5,0);
408 tempo_label.hide();
409 tempo_label.set_no_show_all();
410 meter_label.set_name ("EditorTimeButton");
411 meter_label.set_size_request (-1, (int)timebar_height);
412 meter_label.set_alignment (1.0, 0.5);
413 meter_label.set_padding (5,0);
414 meter_label.hide();
415 meter_label.set_no_show_all();
416 mark_label.set_name ("EditorTimeButton");
417 mark_label.set_size_request (-1, (int)timebar_height);
418 mark_label.set_alignment (1.0, 0.5);
419 mark_label.set_padding (5,0);
420 mark_label.hide();
421 mark_label.set_no_show_all();
422 cd_mark_label.set_name ("EditorTimeButton");
423 cd_mark_label.set_size_request (-1, (int)timebar_height);
424 cd_mark_label.set_alignment (1.0, 0.5);
425 cd_mark_label.set_padding (5,0);
426 cd_mark_label.hide();
427 cd_mark_label.set_no_show_all();
428 range_mark_label.set_name ("EditorTimeButton");
429 range_mark_label.set_size_request (-1, (int)timebar_height);
430 range_mark_label.set_alignment (1.0, 0.5);
431 range_mark_label.set_padding (5,0);
432 range_mark_label.hide();
433 range_mark_label.set_no_show_all();
434 transport_mark_label.set_name ("EditorTimeButton");
435 transport_mark_label.set_size_request (-1, (int)timebar_height);
436 transport_mark_label.set_alignment (1.0, 0.5);
437 transport_mark_label.set_padding (5,0);
438 transport_mark_label.hide();
439 transport_mark_label.set_no_show_all();
441 initialize_rulers ();
442 initialize_canvas ();
443 _summary = new EditorSummary (this);
445 selection->TimeChanged.connect (sigc::mem_fun(*this, &Editor::time_selection_changed));
446 selection->TracksChanged.connect (sigc::mem_fun(*this, &Editor::track_selection_changed));
447 editor_regions_selection_changed_connection = selection->RegionsChanged.connect (sigc::mem_fun(*this, &Editor::region_selection_changed));
448 selection->PointsChanged.connect (sigc::mem_fun(*this, &Editor::point_selection_changed));
449 selection->MarkersChanged.connect (sigc::mem_fun(*this, &Editor::marker_selection_changed));
451 edit_controls_vbox.set_spacing (0);
452 horizontal_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &Editor::scroll_canvas_horizontally), false);
453 vertical_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &Editor::tie_vertical_scrolling), true);
454 track_canvas->signal_map_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_map_handler));
456 HBox* h = manage (new HBox);
457 _group_tabs = new EditorGroupTabs (this);
458 h->pack_start (*_group_tabs, PACK_SHRINK);
459 h->pack_start (edit_controls_vbox);
460 controls_layout.add (*h);
462 controls_layout.set_name ("EditControlsBase");
463 controls_layout.add_events (Gdk::SCROLL_MASK);
464 controls_layout.signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::control_layout_scroll), false);
466 controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
467 controls_layout.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_release));
468 controls_layout_size_request_connection = controls_layout.signal_size_request().connect (sigc::mem_fun (*this, &Editor::controls_layout_size_request));
470 build_cursors ();
472 ArdourCanvas::Canvas* time_pad = manage(new ArdourCanvas::Canvas());
473 ArdourCanvas::SimpleLine* pad_line_1 = manage(new ArdourCanvas::SimpleLine(*time_pad->root(),
474 0.0, 1.0, 100.0, 1.0));
475 pad_line_1->property_color_rgba() = 0xFF0000FF;
476 pad_line_1->show();
477 time_pad->show();
479 time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2);
480 time_canvas_vbox.set_size_request (-1, -1);
482 ruler_label_event_box.add (ruler_label_vbox);
483 ruler_label_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
484 ruler_label_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
486 time_button_event_box.add (time_button_vbox);
487 time_button_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
488 time_button_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
490 /* these enable us to have a dedicated window (for cursor setting, etc.)
491 for the canvas areas.
494 track_canvas_event_box.add (*track_canvas);
496 time_canvas_event_box.add (time_canvas_vbox);
497 time_canvas_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
499 edit_packer.set_col_spacings (0);
500 edit_packer.set_row_spacings (0);
501 edit_packer.set_homogeneous (false);
502 edit_packer.set_border_width (0);
503 edit_packer.set_name ("EditorWindow");
505 edit_packer.attach (zoom_vbox, 0, 1, 0, 2, SHRINK, FILL, 0, 0);
506 /* labels for the rulers */
507 edit_packer.attach (ruler_label_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0);
508 /* labels for the marker "tracks" */
509 edit_packer.attach (time_button_event_box, 1, 2, 1, 2, FILL, SHRINK, 0, 0);
510 /* the rulers */
511 edit_packer.attach (time_canvas_event_box, 2, 3, 0, 1, FILL|EXPAND, FILL, 0, 0);
512 /* track controls */
513 edit_packer.attach (controls_layout, 0, 2, 2, 3, FILL, FILL|EXPAND, 0, 0);
514 /* main canvas */
515 edit_packer.attach (track_canvas_event_box, 2, 3, 1, 3, FILL|EXPAND, FILL|EXPAND, 0, 0);
517 bottom_hbox.set_border_width (2);
518 bottom_hbox.set_spacing (3);
520 _route_groups = new EditorRouteGroups (this);
521 _routes = new EditorRoutes (this);
522 _regions = new EditorRegions (this);
523 _snapshots = new EditorSnapshots (this);
524 _locations = new EditorLocations (this);
526 Gtk::Label* nlabel;
528 nlabel = manage (new Label (_("Regions")));
529 nlabel->set_angle (-90);
530 the_notebook.append_page (_regions->widget (), *nlabel);
531 nlabel = manage (new Label (_("Tracks/Busses")));
532 nlabel->set_angle (-90);
533 the_notebook.append_page (_routes->widget (), *nlabel);
534 nlabel = manage (new Label (_("Snapshots")));
535 nlabel->set_angle (-90);
536 the_notebook.append_page (_snapshots->widget (), *nlabel);
537 nlabel = manage (new Label (_("Route Groups")));
538 nlabel->set_angle (-90);
539 the_notebook.append_page (_route_groups->widget (), *nlabel);
540 nlabel = manage (new Label (_("Ranges & Marks")));
541 nlabel->set_angle (-90);
542 the_notebook.append_page (_locations->widget (), *nlabel);
544 the_notebook.set_show_tabs (true);
545 the_notebook.set_scrollable (true);
546 the_notebook.popup_disable ();
547 the_notebook.set_tab_pos (Gtk::POS_RIGHT);
548 the_notebook.show_all ();
550 post_maximal_editor_width = 0;
551 post_maximal_pane_position = 0;
553 VPaned *editor_summary_pane = manage(new VPaned());
554 editor_summary_pane->pack1(edit_packer);
556 Button* summary_arrows_left_left = manage (new Button);
557 summary_arrows_left_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
558 summary_arrows_left_left->signal_clicked().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left));
559 Button* summary_arrows_left_right = manage (new Button);
560 summary_arrows_left_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
561 summary_arrows_left_right->signal_clicked().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right));
562 VBox* summary_arrows_left = manage (new VBox);
563 summary_arrows_left->pack_start (*summary_arrows_left_left);
564 summary_arrows_left->pack_start (*summary_arrows_left_right);
566 Button* summary_arrows_right_left = manage (new Button);
567 summary_arrows_right_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
568 summary_arrows_right_left->signal_clicked().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left));
569 Button* summary_arrows_right_right = manage (new Button);
570 summary_arrows_right_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
571 summary_arrows_right_right->signal_clicked().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right));
572 VBox* summary_arrows_right = manage (new VBox);
573 summary_arrows_right->pack_start (*summary_arrows_right_left);
574 summary_arrows_right->pack_start (*summary_arrows_right_right);
576 Frame* summary_frame = manage (new Frame);
577 summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN);
578 summary_frame->add (*_summary);
579 summary_frame->show ();
581 _summary_hbox.pack_start (*summary_arrows_left, false, false);
582 _summary_hbox.pack_start (*summary_frame, true, true);
583 _summary_hbox.pack_start (*summary_arrows_right, false, false);
585 editor_summary_pane->pack2 (_summary_hbox);
587 edit_pane.pack1 (*editor_summary_pane, true, true);
588 edit_pane.pack2 (the_notebook, false, true);
590 edit_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&edit_pane)));
592 top_hbox.pack_start (toolbar_frame, false, true);
594 HBox *hbox = manage (new HBox);
595 hbox->pack_start (edit_pane, true, true);
597 global_vpacker.pack_start (top_hbox, false, false);
598 global_vpacker.pack_start (*hbox, true, true);
600 global_hpacker.pack_start (global_vpacker, true, true);
602 set_name ("EditorWindow");
603 add_accel_group (ActionManager::ui_manager->get_accel_group());
605 status_bar_hpacker.show ();
607 vpacker.pack_end (status_bar_hpacker, false, false);
608 vpacker.pack_end (global_hpacker, true, true);
610 /* register actions now so that set_state() can find them and set toggles/checks etc */
612 register_actions ();
614 setup_toolbar ();
615 setup_midi_toolbar ();
617 _snap_type = SnapToBeat;
618 set_snap_to (_snap_type);
619 _snap_mode = SnapOff;
620 set_snap_mode (_snap_mode);
621 set_mouse_mode (MouseObject, true);
622 set_edit_point_preference (EditAtMouse, true);
624 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
625 set_state (*node, Stateful::loading_state_version);
627 _playlist_selector = new PlaylistSelector();
628 _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
630 RegionView::RegionViewGoingAway.connect (*this, ui_bind (&Editor::catch_vanishing_regionview, this, _1), gui_context());
632 /* nudge stuff */
634 nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
635 nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
637 nudge_forward_button.set_name ("TransportButton");
638 nudge_backward_button.set_name ("TransportButton");
640 fade_context_menu.set_name ("ArdourContextMenu");
642 /* icons, titles, WM stuff */
644 list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
645 Glib::RefPtr<Gdk::Pixbuf> icon;
647 if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
648 window_icons.push_back (icon);
650 if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
651 window_icons.push_back (icon);
653 if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
654 window_icons.push_back (icon);
656 if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
657 window_icons.push_back (icon);
659 if (!window_icons.empty()) {
660 set_icon_list (window_icons);
661 set_default_icon_list (window_icons);
664 WindowTitle title(Glib::get_application_name());
665 title += _("Editor");
666 set_title (title.get_string());
667 set_wmclass (X_("ardour_editor"), "Ardour");
669 add (vpacker);
670 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
672 signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
673 signal_delete_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
675 /* allow external control surfaces/protocols to do various things */
677 ControlProtocol::ZoomToSession.connect (*this, boost::bind (&Editor::temporal_zoom_session, this), gui_context());
678 ControlProtocol::ZoomIn.connect (*this, boost::bind (&Editor::temporal_zoom_step, this, false), gui_context());
679 ControlProtocol::ZoomOut.connect (*this, boost::bind (&Editor::temporal_zoom_step, this, true), gui_context());
680 ControlProtocol::ScrollTimeline.connect (*this, ui_bind (&Editor::control_scroll, this, _1), gui_context());
681 BasicUI::AccessAction.connect (*this, ui_bind (&Editor::access_action, this, _1, _2), gui_context());
683 /* problematic: has to return a value and thus cannot be x-thread */
685 Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
687 Config->ParameterChanged.connect (*this, ui_bind (&Editor::parameter_changed, this, _1), gui_context());
689 TimeAxisView::CatchDeletion.connect (*this, ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
691 _last_normalization_value = 0;
693 constructed = true;
694 instant_save ();
697 Editor::~Editor()
699 #ifdef WITH_CMT
700 if(image_socket_listener) {
701 if(image_socket_listener->is_connected())
703 image_socket_listener->close_connection() ;
706 delete image_socket_listener ;
707 image_socket_listener = 0 ;
709 #endif
711 delete _routes;
712 delete _route_groups;
713 delete track_canvas;
714 delete _drags;
717 void
718 Editor::add_toplevel_controls (Container& cont)
720 vpacker.pack_start (cont, false, false);
721 cont.show_all ();
724 void
725 Editor::catch_vanishing_regionview (RegionView *rv)
727 /* note: the selection will take care of the vanishing
728 audioregionview by itself.
731 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
732 _drags->abort ();
735 if (clicked_regionview == rv) {
736 clicked_regionview = 0;
739 if (entered_regionview == rv) {
740 set_entered_regionview (0);
744 void
745 Editor::set_entered_regionview (RegionView* rv)
747 if (rv == entered_regionview) {
748 return;
751 if (entered_regionview) {
752 entered_regionview->exited ();
755 if ((entered_regionview = rv) != 0) {
756 entered_regionview->entered ();
760 void
761 Editor::set_entered_track (TimeAxisView* tav)
763 if (entered_track) {
764 entered_track->exited ();
767 if ((entered_track = tav) != 0) {
768 entered_track->entered ();
772 void
773 Editor::show_window ()
775 if (! is_visible ()) {
776 show_all ();
778 /* re-hide editor list if necessary */
779 editor_list_button_toggled ();
781 /* re-hide summary widget if necessary */
782 parameter_changed ("show-summary");
784 parameter_changed ("show-edit-group-tabs");
786 /* now reset all audio_time_axis heights, because widgets might need
787 to be re-hidden
790 TimeAxisView *tv;
792 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
793 tv = (static_cast<TimeAxisView*>(*i));
794 tv->reset_height ();
797 reset_zoom (frames_per_unit);
800 present ();
803 void
804 Editor::instant_save ()
806 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
807 return;
810 if (_session) {
811 _session->add_instant_xml(get_state());
812 } else {
813 Config->add_instant_xml(get_state());
817 void
818 Editor::zoom_adjustment_changed ()
820 if (_session == 0) {
821 return;
824 double fpu = zoom_range_clock.current_duration() / _canvas_width;
826 if (fpu < 1.0) {
827 fpu = 1.0;
828 zoom_range_clock.set ((nframes64_t) floor (fpu * _canvas_width));
829 } else if (fpu > _session->current_end_frame() / _canvas_width) {
830 fpu = _session->current_end_frame() / _canvas_width;
831 zoom_range_clock.set ((nframes64_t) floor (fpu * _canvas_width));
834 temporal_zoom (fpu);
837 void
838 Editor::control_scroll (float fraction)
840 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
842 if (!_session) {
843 return;
846 double step = fraction * current_page_frames();
849 _control_scroll_target is an optional<T>
851 it acts like a pointer to an nframes64_t, with
852 a operator conversion to boolean to check
853 that it has a value could possibly use
854 playhead_cursor->current_frame to store the
855 value and a boolean in the class to know
856 when it's out of date
859 if (!_control_scroll_target) {
860 _control_scroll_target = _session->transport_frame();
861 _dragging_playhead = true;
864 if ((fraction < 0.0f) && (*_control_scroll_target < (nframes64_t) fabs(step))) {
865 *_control_scroll_target = 0;
866 } else if ((fraction > 0.0f) && (max_frames - *_control_scroll_target < step)) {
867 *_control_scroll_target = max_frames - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
868 } else {
869 *_control_scroll_target += (nframes64_t) floor (step);
872 /* move visuals, we'll catch up with it later */
874 playhead_cursor->set_position (*_control_scroll_target);
875 UpdateAllTransportClocks (*_control_scroll_target);
877 if (*_control_scroll_target > (current_page_frames() / 2)) {
878 /* try to center PH in window */
879 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
880 } else {
881 reset_x_origin (0);
885 Now we do a timeout to actually bring the session to the right place
886 according to the playhead. This is to avoid reading disk buffers on every
887 call to control_scroll, which is driven by ScrollTimeline and therefore
888 probably by a control surface wheel which can generate lots of events.
890 /* cancel the existing timeout */
892 control_scroll_connection.disconnect ();
894 /* add the next timeout */
896 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
899 bool
900 Editor::deferred_control_scroll (nframes64_t /*target*/)
902 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
903 // reset for next stream
904 _control_scroll_target = boost::none;
905 _dragging_playhead = false;
906 return false;
909 void
910 Editor::access_action (std::string action_group, std::string action_item)
912 if (!_session) {
913 return;
916 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
918 RefPtr<Action> act;
919 act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
921 if (act) {
922 act->activate();
926 void
927 Editor::on_realize ()
929 Window::on_realize ();
930 Realized ();
933 void
934 Editor::map_position_change (nframes64_t frame)
936 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, frame)
938 if (_session == 0 || !_follow_playhead) {
939 return;
942 center_screen (frame);
943 playhead_cursor->set_position (frame);
946 void
947 Editor::center_screen (nframes64_t frame)
949 double page = _canvas_width * frames_per_unit;
951 /* if we're off the page, then scroll.
954 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
955 center_screen_internal (frame, page);
959 void
960 Editor::center_screen_internal (nframes64_t frame, float page)
962 page /= 2;
964 if (frame > page) {
965 frame -= (nframes64_t) page;
966 } else {
967 frame = 0;
970 reset_x_origin (frame);
973 void
974 Editor::handle_new_duration ()
976 if (!_session) {
977 return;
980 ENSURE_GUI_THREAD (*this, &Editor::handle_new_duration)
981 nframes64_t new_end = _session->current_end_frame() + (nframes64_t) floorf (current_page_frames() * 0.10f);
983 horizontal_adjustment.set_upper (new_end / frames_per_unit);
984 horizontal_adjustment.set_page_size (current_page_frames()/frames_per_unit);
986 if (horizontal_adjustment.get_value() + _canvas_width > horizontal_adjustment.get_upper()) {
987 horizontal_adjustment.set_value (horizontal_adjustment.get_upper() - _canvas_width);
989 //cerr << "Editor::handle_new_duration () called ha v:l:u:ps:lcf = " << horizontal_adjustment.get_value() << ":" << horizontal_adjustment.get_lower() << ":" << horizontal_adjustment.get_upper() << ":" << horizontal_adjustment.get_page_size() << ":" << endl;//DEBUG
992 void
993 Editor::update_title ()
995 ENSURE_GUI_THREAD (*this, &Editor::update_title)
997 if (_session) {
998 bool dirty = _session->dirty();
1000 string session_name;
1002 if (_session->snap_name() != _session->name()) {
1003 session_name = _session->snap_name();
1004 } else {
1005 session_name = _session->name();
1008 if (dirty) {
1009 session_name = "*" + session_name;
1012 WindowTitle title(session_name);
1013 title += Glib::get_application_name();
1014 set_title (title.get_string());
1018 void
1019 Editor::set_session (Session *t)
1021 SessionHandlePtr::set_session (t);
1023 if (!_session) {
1024 return;
1027 zoom_range_clock.set_session (_session);
1028 _playlist_selector->set_session (_session);
1029 nudge_clock.set_session (_session);
1030 _summary->set_session (_session);
1031 _group_tabs->set_session (_session);
1032 _route_groups->set_session (_session);
1033 _regions->set_session (_session);
1034 _snapshots->set_session (_session);
1035 _routes->set_session (_session);
1036 _locations->set_session (_session);
1038 if (rhythm_ferret) {
1039 rhythm_ferret->set_session (_session);
1042 if (analysis_window) {
1043 analysis_window->set_session (_session);
1046 if (sfbrowser) {
1047 sfbrowser->set_session (_session);
1050 compute_fixed_ruler_scale ();
1052 /* there are never any selected regions at startup */
1053 sensitize_the_right_region_actions (false);
1055 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1056 set_state (*node, Stateful::loading_state_version);
1058 /* catch up with the playhead */
1060 _session->request_locate (playhead_cursor->current_frame);
1061 _pending_initial_locate = true;
1063 update_title ();
1065 /* These signals can all be emitted by a non-GUI thread. Therefore the
1066 handlers for them must not attempt to directly interact with the GUI,
1067 but use Gtkmm2ext::UI::instance()->call_slot();
1070 _session->TransportStateChange.connect (_session_connections, boost::bind (&Editor::map_transport_state, this), gui_context());
1071 _session->PositionChanged.connect (_session_connections, ui_bind (&Editor::map_position_change, this, _1), gui_context());
1072 _session->RouteAdded.connect (_session_connections, ui_bind (&Editor::handle_new_route, this, _1), gui_context());
1073 _session->DurationChanged.connect (_session_connections, boost::bind (&Editor::handle_new_duration, this), gui_context());
1074 _session->DirtyChanged.connect (_session_connections, boost::bind (&Editor::update_title, this), gui_context());
1075 _session->TimecodeOffsetChanged.connect (_session_connections, boost::bind (&Editor::update_just_timecode, this), gui_context());
1076 _session->tempo_map().PropertyChanged.connect (_session_connections, ui_bind (&Editor::tempo_map_changed, this, _1), gui_context());
1077 _session->Located.connect (_session_connections, boost::bind (&Editor::located, this), gui_context());
1078 _session->config.ParameterChanged.connect (_session_connections, ui_bind (&Editor::parameter_changed, this, _1), gui_context());
1079 _session->StateSaved.connect (_session_connections, ui_bind (&Editor::session_state_saved, this, _1), gui_context());
1080 _session->locations()->added.connect (_session_connections, ui_bind (&Editor::add_new_location, this, _1), gui_context());
1081 _session->locations()->removed.connect (_session_connections, ui_bind (&Editor::location_gone, this, _1), gui_context());
1082 _session->locations()->changed.connect (_session_connections, boost::bind (&Editor::refresh_location_display, this), gui_context());
1083 _session->locations()->StateChanged.connect (_session_connections, ui_bind (&Editor::refresh_location_display_s, this, _1), gui_context());
1084 _session->locations()->end_location()->changed.connect (_session_connections, ui_bind (&Editor::end_location_changed, this, _1), gui_context());
1085 _session->history().Changed.connect (_session_connections, boost::bind (&Editor::history_changed, this), gui_context());
1087 if (Profile->get_sae()) {
1088 BBT_Time bbt;
1089 bbt.bars = 0;
1090 bbt.beats = 0;
1091 bbt.ticks = 120;
1092 nframes_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1);
1093 nudge_clock.set_mode(AudioClock::BBT);
1094 nudge_clock.set (pos, true, 0, AudioClock::BBT);
1096 } else {
1097 nudge_clock.set (_session->frame_rate() * 5, true, 0, AudioClock::Timecode); // default of 5 seconds
1100 playhead_cursor->canvas_item.show ();
1102 Location* loc = _session->locations()->auto_loop_location();
1103 if (loc == 0) {
1104 loc = new Location (0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1105 if (loc->start() == loc->end()) {
1106 loc->set_end (loc->start() + 1);
1108 _session->locations()->add (loc, false);
1109 _session->set_auto_loop_location (loc);
1110 } else {
1111 // force name
1112 loc->set_name (_("Loop"));
1115 loc = _session->locations()->auto_punch_location();
1116 if (loc == 0) {
1117 loc = new Location (0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1118 if (loc->start() == loc->end()) {
1119 loc->set_end (loc->start() + 1);
1121 _session->locations()->add (loc, false);
1122 _session->set_auto_punch_location (loc);
1123 } else {
1124 // force name
1125 loc->set_name (_("Punch"));
1128 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1129 Config->map_parameters (pc);
1130 _session->config.map_parameters (pc);
1132 refresh_location_display ();
1133 handle_new_duration ();
1135 restore_ruler_visibility ();
1136 //tempo_map_changed (PropertyChange (0));
1137 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1139 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1140 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1143 super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
1144 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1147 switch (_snap_type) {
1148 case SnapToRegionStart:
1149 case SnapToRegionEnd:
1150 case SnapToRegionSync:
1151 case SnapToRegionBoundary:
1152 build_region_boundary_cache ();
1153 break;
1155 default:
1156 break;
1159 /* register for undo history */
1160 _session->register_with_memento_command_factory(_id, this);
1162 start_updating_meters ();
1165 void
1166 Editor::build_cursors ()
1168 using namespace Gdk;
1170 Gdk::Color mbg ("#000000" ); /* Black */
1171 Gdk::Color mfg ("#0000ff" ); /* Blue. */
1174 RefPtr<Bitmap> source, mask;
1175 source = Bitmap::create (mag_bits, mag_width, mag_height);
1176 mask = Bitmap::create (magmask_bits, mag_width, mag_height);
1177 zoom_cursor = new Gdk::Cursor (source, mask, mfg, mbg, mag_x_hot, mag_y_hot);
1180 Gdk::Color fbg ("#ffffff" );
1181 Gdk::Color ffg ("#000000" );
1184 RefPtr<Bitmap> source, mask;
1186 source = Bitmap::create (fader_cursor_bits, fader_cursor_width, fader_cursor_height);
1187 mask = Bitmap::create (fader_cursor_mask_bits, fader_cursor_width, fader_cursor_height);
1188 fader_cursor = new Gdk::Cursor (source, mask, ffg, fbg, fader_cursor_x_hot, fader_cursor_y_hot);
1192 RefPtr<Bitmap> source, mask;
1193 source = Bitmap::create (speaker_cursor_bits, speaker_cursor_width, speaker_cursor_height);
1194 mask = Bitmap::create (speaker_cursor_mask_bits, speaker_cursor_width, speaker_cursor_height);
1195 speaker_cursor = new Gdk::Cursor (source, mask, ffg, fbg, speaker_cursor_x_hot, speaker_cursor_y_hot);
1199 RefPtr<Bitmap> bits;
1200 char pix[4] = { 0, 0, 0, 0 };
1201 bits = Bitmap::create (pix, 2, 2);
1202 Gdk::Color c;
1203 transparent_cursor = new Gdk::Cursor (bits, bits, c, c, 0, 0);
1207 RefPtr<Bitmap> bits;
1208 char pix[4] = { 0, 0, 0, 0 };
1209 bits = Bitmap::create (pix, 2, 2);
1210 Gdk::Color c;
1211 transparent_cursor = new Gdk::Cursor (bits, bits, c, c, 0, 0);
1215 grabber_cursor = new Gdk::Cursor (HAND2);
1218 Glib::RefPtr<Gdk::Pixbuf> grabber_edit_point_pixbuf (::get_icon ("grabber_edit_point"));
1219 grabber_edit_point_cursor = new Gdk::Cursor (Gdk::Display::get_default(), grabber_edit_point_pixbuf, 5, 17);
1222 cross_hair_cursor = new Gdk::Cursor (CROSSHAIR);
1223 trimmer_cursor = new Gdk::Cursor (SB_H_DOUBLE_ARROW);
1224 selector_cursor = new Gdk::Cursor (XTERM);
1225 time_fx_cursor = new Gdk::Cursor (SIZING);
1226 wait_cursor = new Gdk::Cursor (WATCH);
1227 timebar_cursor = new Gdk::Cursor(LEFT_PTR);
1228 midi_pencil_cursor = new Gdk::Cursor (PENCIL);
1229 midi_select_cursor = new Gdk::Cursor (CENTER_PTR);
1230 midi_resize_cursor = new Gdk::Cursor (SIZING);
1231 midi_erase_cursor = new Gdk::Cursor (DRAPED_BOX);
1234 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1235 void
1236 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1238 using namespace Menu_Helpers;
1239 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1241 if (arv == 0) {
1242 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1243 /*NOTREACHED*/
1246 MenuList& items (fade_context_menu.items());
1248 items.clear ();
1250 switch (item_type) {
1251 case FadeInItem:
1252 case FadeInHandleItem:
1253 if (arv->audio_region()->fade_in_active()) {
1254 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1255 } else {
1256 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1259 items.push_back (SeparatorElem());
1261 if (Profile->get_sae()) {
1262 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear)));
1263 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast)));
1264 } else {
1265 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear)));
1266 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast)));
1267 items.push_back (MenuElem (_("Slow"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogB)));
1268 items.push_back (MenuElem (_("Fast"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogA)));
1269 items.push_back (MenuElem (_("Fastest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Slow)));
1272 break;
1274 case FadeOutItem:
1275 case FadeOutHandleItem:
1276 if (arv->audio_region()->fade_out_active()) {
1277 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1278 } else {
1279 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_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_out_shape), AudioRegion::Linear)));
1286 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow)));
1287 } else {
1288 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear)));
1289 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow)));
1290 items.push_back (MenuElem (_("Slow"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogA)));
1291 items.push_back (MenuElem (_("Fast"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogB)));
1292 items.push_back (MenuElem (_("Fastest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Fast)));
1295 break;
1297 default:
1298 fatal << _("programming error: ")
1299 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1300 << endmsg;
1301 /*NOTREACHED*/
1304 fade_context_menu.popup (button, time);
1307 void
1308 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection, nframes64_t frame)
1310 using namespace Menu_Helpers;
1311 Menu* (Editor::*build_menu_function)(nframes64_t);
1312 Menu *menu;
1314 switch (item_type) {
1315 case RegionItem:
1316 case RegionViewName:
1317 case RegionViewNameHighlight:
1318 if (with_selection) {
1319 build_menu_function = &Editor::build_track_selection_context_menu;
1320 } else {
1321 build_menu_function = &Editor::build_track_region_context_menu;
1323 break;
1325 case SelectionItem:
1326 if (with_selection) {
1327 build_menu_function = &Editor::build_track_selection_context_menu;
1328 } else {
1329 build_menu_function = &Editor::build_track_context_menu;
1331 break;
1333 case CrossfadeViewItem:
1334 build_menu_function = &Editor::build_track_crossfade_context_menu;
1335 break;
1337 case StreamItem:
1338 if (clicked_routeview->get_diskstream()) {
1339 build_menu_function = &Editor::build_track_context_menu;
1340 } else {
1341 build_menu_function = &Editor::build_track_bus_context_menu;
1343 break;
1345 default:
1346 /* probably shouldn't happen but if it does, we don't care */
1347 return;
1350 menu = (this->*build_menu_function)(frame);
1351 menu->set_name ("ArdourContextMenu");
1353 /* now handle specific situations */
1355 switch (item_type) {
1356 case RegionItem:
1357 case RegionViewName:
1358 case RegionViewNameHighlight:
1359 if (!with_selection) {
1360 if (region_edit_menu_split_item) {
1361 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1362 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1363 } else {
1364 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1368 if (region_edit_menu_split_multichannel_item) {
1369 if (clicked_regionview && clicked_regionview->region().n_channels() > 1) {
1370 // GTK2FIX find the action, change its sensitivity
1371 // region_edit_menu_split_multichannel_item->set_sensitive (true);
1372 } else {
1373 // GTK2FIX see above
1374 // region_edit_menu_split_multichannel_item->set_sensitive (false);
1378 break;
1380 case SelectionItem:
1381 break;
1383 case CrossfadeViewItem:
1384 break;
1386 case StreamItem:
1387 break;
1389 default:
1390 /* probably shouldn't happen but if it does, we don't care */
1391 return;
1394 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1396 /* Bounce to disk */
1398 using namespace Menu_Helpers;
1399 MenuList& edit_items = menu->items();
1401 edit_items.push_back (SeparatorElem());
1403 switch (clicked_routeview->audio_track()->freeze_state()) {
1404 case AudioTrack::NoFreeze:
1405 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1406 break;
1408 case AudioTrack::Frozen:
1409 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1410 break;
1412 case AudioTrack::UnFrozen:
1413 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1414 break;
1419 if (item_type == StreamItem && clicked_routeview) {
1420 clicked_routeview->build_underlay_menu(menu);
1423 menu->popup (button, time);
1426 Menu*
1427 Editor::build_track_context_menu (nframes64_t)
1429 using namespace Menu_Helpers;
1431 MenuList& edit_items = track_context_menu.items();
1432 edit_items.clear();
1434 add_dstream_context_items (edit_items);
1435 return &track_context_menu;
1438 Menu*
1439 Editor::build_track_bus_context_menu (nframes64_t)
1441 using namespace Menu_Helpers;
1443 MenuList& edit_items = track_context_menu.items();
1444 edit_items.clear();
1446 add_bus_context_items (edit_items);
1447 return &track_context_menu;
1450 Menu*
1451 Editor::build_track_region_context_menu (nframes64_t frame)
1453 using namespace Menu_Helpers;
1454 MenuList& edit_items = track_region_context_menu.items();
1455 edit_items.clear();
1457 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1459 if (rtv) {
1460 boost::shared_ptr<Diskstream> ds;
1461 boost::shared_ptr<Playlist> pl;
1463 if ((ds = rtv->get_diskstream()) && ((pl = ds->playlist()))) {
1464 Playlist::RegionList* regions = pl->regions_at ((nframes64_t) floor ( (double)frame * ds->speed()));
1466 if (selection->regions.size() > 1) {
1467 // there's already a multiple selection: just add a
1468 // single region context menu that will act on all
1469 // selected regions
1470 boost::shared_ptr<Region> dummy_region; // = NULL
1471 add_region_context_items (rtv->view(), dummy_region, edit_items);
1472 } else {
1473 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
1474 add_region_context_items (rtv->view(), (*i), edit_items);
1478 delete regions;
1482 add_dstream_context_items (edit_items);
1484 return &track_region_context_menu;
1487 Menu*
1488 Editor::build_track_crossfade_context_menu (nframes64_t frame)
1490 using namespace Menu_Helpers;
1491 MenuList& edit_items = track_crossfade_context_menu.items();
1492 edit_items.clear ();
1494 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1496 if (atv) {
1497 boost::shared_ptr<Diskstream> ds;
1498 boost::shared_ptr<Playlist> pl;
1499 boost::shared_ptr<AudioPlaylist> apl;
1501 if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1503 Playlist::RegionList* regions = pl->regions_at (frame);
1504 AudioPlaylist::Crossfades xfades;
1506 apl->crossfades_at (frame, xfades);
1508 bool many = xfades.size() > 1;
1510 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1511 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1514 if (selection->regions.size() > 1) {
1515 // there's already a multiple selection: just add a
1516 // single region context menu that will act on all
1517 // selected regions
1518 boost::shared_ptr<Region> dummy_region; // = NULL
1519 add_region_context_items (atv->audio_view(), dummy_region, edit_items);
1520 } else {
1521 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
1522 add_region_context_items (atv->audio_view(), (*i), edit_items);
1525 delete regions;
1529 add_dstream_context_items (edit_items);
1531 return &track_crossfade_context_menu;
1534 void
1535 Editor::analyze_region_selection()
1537 if (analysis_window == 0) {
1538 analysis_window = new AnalysisWindow();
1540 if (_session != 0)
1541 analysis_window->set_session(_session);
1543 analysis_window->show_all();
1546 analysis_window->set_regionmode();
1547 analysis_window->analyze();
1549 analysis_window->present();
1552 void
1553 Editor::analyze_range_selection()
1555 if (analysis_window == 0) {
1556 analysis_window = new AnalysisWindow();
1558 if (_session != 0)
1559 analysis_window->set_session(_session);
1561 analysis_window->show_all();
1564 analysis_window->set_rangemode();
1565 analysis_window->analyze();
1567 analysis_window->present();
1570 Menu*
1571 Editor::build_track_selection_context_menu (nframes64_t)
1573 using namespace Menu_Helpers;
1574 MenuList& edit_items = track_selection_context_menu.items();
1575 edit_items.clear ();
1577 add_selection_context_items (edit_items);
1578 // edit_items.push_back (SeparatorElem());
1579 // add_dstream_context_items (edit_items);
1581 return &track_selection_context_menu;
1584 /** Add context menu items relevant to crossfades.
1585 * @param edit_items List to add the items to.
1587 void
1588 Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1590 using namespace Menu_Helpers;
1591 Menu *xfade_menu = manage (new Menu);
1592 MenuList& items = xfade_menu->items();
1593 xfade_menu->set_name ("ArdourContextMenu");
1594 string str;
1596 if (xfade->active()) {
1597 str = _("Mute");
1598 } else {
1599 str = _("Unmute");
1602 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1603 items.push_back (MenuElem (_("Edit"), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1605 if (xfade->can_follow_overlap()) {
1607 if (xfade->following_overlap()) {
1608 str = _("Convert to Short");
1609 } else {
1610 str = _("Convert to Full");
1613 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1616 if (many) {
1617 str = xfade->out()->name();
1618 str += "->";
1619 str += xfade->in()->name();
1620 } else {
1621 str = _("Crossfade");
1624 edit_items.push_back (MenuElem (str, *xfade_menu));
1625 edit_items.push_back (SeparatorElem());
1628 void
1629 Editor::xfade_edit_left_region ()
1631 if (clicked_crossfadeview) {
1632 clicked_crossfadeview->left_view.show_region_editor ();
1636 void
1637 Editor::xfade_edit_right_region ()
1639 if (clicked_crossfadeview) {
1640 clicked_crossfadeview->right_view.show_region_editor ();
1644 void
1645 Editor::add_region_context_items (StreamView* sv, boost::shared_ptr<Region> region, Menu_Helpers::MenuList& edit_items)
1647 using namespace Menu_Helpers;
1648 Gtk::MenuItem* foo_item;
1649 Menu *region_menu = manage (new Menu);
1650 MenuList& items = region_menu->items();
1651 region_menu->set_name ("ArdourContextMenu");
1653 boost::shared_ptr<AudioRegion> ar;
1654 boost::shared_ptr<MidiRegion> mr;
1656 if (region) {
1657 ar = boost::dynamic_pointer_cast<AudioRegion> (region);
1658 mr = boost::dynamic_pointer_cast<MidiRegion> (region);
1660 /* when this particular menu pops up, make the relevant region
1661 become selected.
1664 region_menu->signal_map_event().connect (
1665 sigc::bind (sigc::mem_fun(*this, &Editor::set_selected_regionview_from_map_event), sv, boost::weak_ptr<Region>(region)));
1667 items.push_back (MenuElem (_("Rename..."), sigc::mem_fun(*this, &Editor::rename_region)));
1668 if (mr && internal_editing()) {
1669 items.push_back (MenuElem (_("List editor..."), sigc::mem_fun(*this, &Editor::show_midi_list_editor)));
1670 } else {
1671 items.push_back (MenuElem (_("Region Properties..."), sigc::mem_fun(*this, &Editor::edit_region)));
1675 items.push_back (MenuElem (_("Raise to Top Layer"), sigc::mem_fun(*this, &Editor::raise_region_to_top)));
1676 items.push_back (MenuElem (_("Lower to Bottom Layer"), sigc::mem_fun (*this, &Editor::lower_region_to_bottom)));
1677 items.push_back (SeparatorElem());
1678 items.push_back (MenuElem (_("Define Sync Point"), sigc::mem_fun(*this, &Editor::set_region_sync_from_edit_point)));
1679 if (_edit_point == EditAtMouse) {
1680 items.back ().set_sensitive (false);
1682 items.push_back (MenuElem (_("Remove Sync Point"), sigc::mem_fun(*this, &Editor::remove_region_sync)));
1683 items.push_back (SeparatorElem());
1685 items.push_back (MenuElem (_("Audition"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1686 items.push_back (MenuElem (_("Export"), sigc::mem_fun(*this, &Editor::export_region)));
1687 items.push_back (MenuElem (_("Bounce"), sigc::mem_fun(*this, &Editor::bounce_region_selection)));
1689 if (ar) {
1690 items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_region_selection)));
1693 items.push_back (SeparatorElem());
1695 sigc::connection fooc;
1696 boost::shared_ptr<Region> region_to_check;
1698 if (region) {
1699 region_to_check = region;
1700 } else {
1701 region_to_check = selection->regions.front()->region();
1704 items.push_back (CheckMenuElem (_("Lock")));
1705 CheckMenuItem* region_lock_item = static_cast<CheckMenuItem*>(&items.back());
1706 if (region_to_check->locked()) {
1707 region_lock_item->set_active();
1709 region_lock_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_region_lock));
1711 items.push_back (CheckMenuElem (_("Glue to Bars and Beats")));
1712 CheckMenuItem* bbt_glue_item = static_cast<CheckMenuItem*>(&items.back());
1714 switch (region_to_check->positional_lock_style()) {
1715 case Region::MusicTime:
1716 bbt_glue_item->set_active (true);
1717 break;
1718 default:
1719 bbt_glue_item->set_active (false);
1720 break;
1723 bbt_glue_item->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &Editor::set_region_lock_style), Region::MusicTime));
1725 items.push_back (CheckMenuElem (_("Mute")));
1726 CheckMenuItem* region_mute_item = static_cast<CheckMenuItem*>(&items.back());
1727 fooc = region_mute_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_region_mute));
1728 if (region_to_check->muted()) {
1729 fooc.block (true);
1730 region_mute_item->set_active();
1731 fooc.block (false);
1734 if (!Profile->get_sae()) {
1735 items.push_back (CheckMenuElem (_("Opaque")));
1736 CheckMenuItem* region_opaque_item = static_cast<CheckMenuItem*>(&items.back());
1737 fooc = region_opaque_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_region_opaque));
1738 if (region_to_check->opaque()) {
1739 fooc.block (true);
1740 region_opaque_item->set_active();
1741 fooc.block (false);
1745 items.push_back (CheckMenuElem (_("Original Position"), sigc::mem_fun(*this, &Editor::naturalize)));
1746 if (region_to_check->at_natural_position()) {
1747 items.back().set_sensitive (false);
1750 items.push_back (SeparatorElem());
1752 if (ar) {
1754 RegionView* rv = sv->find_view (ar);
1755 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1757 if (!Profile->get_sae()) {
1758 items.push_back (MenuElem (_("Reset Envelope"), sigc::mem_fun(*this, &Editor::reset_region_gain_envelopes)));
1760 items.push_back (CheckMenuElem (_("Envelope Visible")));
1761 CheckMenuItem* region_envelope_visible_item = static_cast<CheckMenuItem*> (&items.back());
1762 fooc = region_envelope_visible_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_gain_envelope_visibility));
1763 if (arv->envelope_visible()) {
1764 fooc.block (true);
1765 region_envelope_visible_item->set_active (true);
1766 fooc.block (false);
1769 items.push_back (CheckMenuElem (_("Envelope Active")));
1770 CheckMenuItem* region_envelope_active_item = static_cast<CheckMenuItem*> (&items.back());
1771 fooc = region_envelope_active_item->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_gain_envelope_active));
1773 if (ar->envelope_active()) {
1774 fooc.block (true);
1775 region_envelope_active_item->set_active (true);
1776 fooc.block (false);
1779 items.push_back (SeparatorElem());
1782 items.push_back (MenuElem (_("Normalize"), sigc::mem_fun(*this, &Editor::normalize_region)));
1783 if (ar->scale_amplitude() != 1) {
1784 items.push_back (MenuElem (_("Reset Gain"), sigc::mem_fun(*this, &Editor::reset_region_scale_amplitude)));
1787 } else if (mr) {
1788 items.push_back (MenuElem (_("Quantize"), sigc::mem_fun(*this, &Editor::quantize_region)));
1789 items.push_back (SeparatorElem());
1792 items.push_back (MenuElem (_("Strip Silence..."), sigc::mem_fun (*this, &Editor::strip_region_silence)));
1793 items.push_back (MenuElem (_("Reverse"), sigc::mem_fun(*this, &Editor::reverse_region)));
1794 items.push_back (SeparatorElem());
1796 /* range related stuff */
1798 items.push_back (MenuElem (_("Add Single Range"), sigc::mem_fun (*this, &Editor::add_location_from_audio_region)));
1799 items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_locations_from_audio_region)));
1800 if (selection->regions.size() < 2) {
1801 items.back().set_sensitive (false);
1804 items.push_back (MenuElem (_("Set Range Selection"), sigc::mem_fun (*this, &Editor::set_selection_from_region)));
1805 items.push_back (SeparatorElem());
1807 /* Nudge region */
1809 Menu *nudge_menu = manage (new Menu());
1810 MenuList& nudge_items = nudge_menu->items();
1811 nudge_menu->set_name ("ArdourContextMenu");
1813 nudge_items.push_back (MenuElem (_("Nudge Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_forward), false, false))));
1814 nudge_items.push_back (MenuElem (_("Nudge Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_backward), false, false))));
1815 nudge_items.push_back (MenuElem (_("Nudge Forward by Capture Offset"), (sigc::mem_fun(*this, &Editor::nudge_forward_capture_offset))));
1816 nudge_items.push_back (MenuElem (_("Nudge Backward by Capture Offset"), (sigc::mem_fun(*this, &Editor::nudge_backward_capture_offset))));
1818 items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1819 items.push_back (SeparatorElem());
1821 Menu *trim_menu = manage (new Menu);
1822 MenuList& trim_items = trim_menu->items();
1823 trim_menu->set_name ("ArdourContextMenu");
1825 trim_items.push_back (MenuElem (_("Start to Edit Point"), sigc::mem_fun(*this, &Editor::trim_region_from_edit_point)));
1826 foo_item = &trim_items.back();
1827 if (_edit_point == EditAtMouse) {
1828 foo_item->set_sensitive (false);
1830 trim_items.push_back (MenuElem (_("Edit Point to End"), sigc::mem_fun(*this, &Editor::trim_region_to_edit_point)));
1831 foo_item = &trim_items.back();
1832 if (_edit_point == EditAtMouse) {
1833 foo_item->set_sensitive (false);
1835 trim_items.push_back (MenuElem (_("Trim to Loop"), sigc::mem_fun(*this, &Editor::trim_region_to_loop)));
1836 trim_items.push_back (MenuElem (_("Trim to Punch"), sigc::mem_fun(*this, &Editor::trim_region_to_punch)));
1838 items.push_back (MenuElem (_("Trim"), *trim_menu));
1839 items.push_back (SeparatorElem());
1841 items.push_back (MenuElem (_("Split"), (sigc::mem_fun(*this, &Editor::split))));
1842 region_edit_menu_split_item = &items.back();
1844 if (_edit_point == EditAtMouse) {
1845 region_edit_menu_split_item->set_sensitive (false);
1848 items.push_back (MenuElem (_("Make Mono Regions"), (sigc::mem_fun(*this, &Editor::split_multichannel_region))));
1849 region_edit_menu_split_multichannel_item = &items.back();
1851 items.push_back (MenuElem (_("Duplicate"), (sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false))));
1852 items.push_back (MenuElem (_("Multi-Duplicate"), (sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), true))));
1853 items.push_back (MenuElem (_("Fill Track"), (sigc::mem_fun(*this, &Editor::region_fill_track))));
1854 items.push_back (SeparatorElem());
1855 items.push_back (MenuElem (_("Remove"), sigc::mem_fun(*this, &Editor::remove_selected_regions)));
1857 /* OK, stick the region submenu at the top of the list, and then add
1858 the standard items.
1861 /* we have to hack up the region name because "_" has a special
1862 meaning for menu titles.
1865 string::size_type pos = 0;
1866 string menu_item_name = (region) ? region->name() : _("Selected Regions");
1868 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1869 menu_item_name.replace (pos, 1, "__");
1870 pos += 2;
1873 edit_items.push_back (MenuElem (menu_item_name, *region_menu));
1874 edit_items.push_back (SeparatorElem());
1877 /** Add context menu items relevant to selection ranges.
1878 * @param edit_items List to add the items to.
1880 void
1881 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1883 using namespace Menu_Helpers;
1885 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1886 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1888 edit_items.push_back (SeparatorElem());
1889 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1891 if (!selection->regions.empty()) {
1892 edit_items.push_back (SeparatorElem());
1893 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)));
1894 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)));
1897 edit_items.push_back (SeparatorElem());
1898 edit_items.push_back (MenuElem (_("Silence Range"), sigc::mem_fun(*this, &Editor::separate_region_from_selection)));
1899 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1901 edit_items.push_back (SeparatorElem());
1902 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1904 edit_items.push_back (SeparatorElem());
1905 edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1906 edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1908 edit_items.push_back (SeparatorElem());
1909 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1911 edit_items.push_back (SeparatorElem());
1912 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1913 edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1914 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1916 edit_items.push_back (SeparatorElem());
1917 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1918 edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1919 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1920 edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1921 edit_items.push_back (MenuElem (_("Export Range"), sigc::mem_fun(*this, &Editor::export_range)));
1925 void
1926 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1928 using namespace Menu_Helpers;
1930 /* Playback */
1932 Menu *play_menu = manage (new Menu);
1933 MenuList& play_items = play_menu->items();
1934 play_menu->set_name ("ArdourContextMenu");
1936 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1937 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1938 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1939 play_items.push_back (SeparatorElem());
1940 play_items.push_back (MenuElem (_("Loop Region"), sigc::mem_fun(*this, &Editor::loop_selected_region)));
1942 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1944 /* Selection */
1946 Menu *select_menu = manage (new Menu);
1947 MenuList& select_items = select_menu->items();
1948 select_menu->set_name ("ArdourContextMenu");
1950 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1951 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1952 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1953 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1954 select_items.push_back (SeparatorElem());
1955 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
1956 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
1957 select_items.push_back (SeparatorElem());
1958 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1959 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1960 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1961 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1962 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
1963 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
1964 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
1966 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1968 /* Cut-n-Paste */
1970 Menu *cutnpaste_menu = manage (new Menu);
1971 MenuList& cutnpaste_items = cutnpaste_menu->items();
1972 cutnpaste_menu->set_name ("ArdourContextMenu");
1974 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1975 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1976 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1978 cutnpaste_items.push_back (SeparatorElem());
1980 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun(*this, &Editor::align), ARDOUR::SyncPoint)));
1981 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun(*this, &Editor::align_relative), ARDOUR::SyncPoint)));
1983 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
1985 /* Adding new material */
1987 edit_items.push_back (SeparatorElem());
1988 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
1989 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
1991 /* Nudge track */
1993 Menu *nudge_menu = manage (new Menu());
1994 MenuList& nudge_items = nudge_menu->items();
1995 nudge_menu->set_name ("ArdourContextMenu");
1997 edit_items.push_back (SeparatorElem());
1998 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1999 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2000 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2001 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2003 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2006 void
2007 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
2009 using namespace Menu_Helpers;
2011 /* Playback */
2013 Menu *play_menu = manage (new Menu);
2014 MenuList& play_items = play_menu->items();
2015 play_menu->set_name ("ArdourContextMenu");
2017 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
2018 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
2019 edit_items.push_back (MenuElem (_("Play"), *play_menu));
2021 /* Selection */
2023 Menu *select_menu = manage (new Menu);
2024 MenuList& select_items = select_menu->items();
2025 select_menu->set_name ("ArdourContextMenu");
2027 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2028 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
2029 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
2030 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
2031 select_items.push_back (SeparatorElem());
2032 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
2033 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
2034 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2035 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2037 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2039 /* Cut-n-Paste */
2041 Menu *cutnpaste_menu = manage (new Menu);
2042 MenuList& cutnpaste_items = cutnpaste_menu->items();
2043 cutnpaste_menu->set_name ("ArdourContextMenu");
2045 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2046 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2047 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
2049 Menu *nudge_menu = manage (new Menu());
2050 MenuList& nudge_items = nudge_menu->items();
2051 nudge_menu->set_name ("ArdourContextMenu");
2053 edit_items.push_back (SeparatorElem());
2054 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2055 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2056 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2057 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2059 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2062 SnapType
2063 Editor::snap_type() const
2065 return _snap_type;
2068 SnapMode
2069 Editor::snap_mode() const
2071 return _snap_mode;
2074 void
2075 Editor::set_snap_to (SnapType st)
2077 unsigned int snap_ind = (unsigned int)st;
2079 _snap_type = st;
2081 if (snap_ind > snap_type_strings.size() - 1) {
2082 snap_ind = 0;
2083 _snap_type = (SnapType)snap_ind;
2086 string str = snap_type_strings[snap_ind];
2088 if (str != snap_type_selector.get_active_text()) {
2089 snap_type_selector.set_active_text (str);
2092 instant_save ();
2094 switch (_snap_type) {
2095 case SnapToAThirtysecondBeat:
2096 case SnapToASixteenthBeat:
2097 case SnapToAEighthBeat:
2098 case SnapToAQuarterBeat:
2099 case SnapToAThirdBeat:
2100 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
2101 update_tempo_based_rulers ();
2102 break;
2104 case SnapToRegionStart:
2105 case SnapToRegionEnd:
2106 case SnapToRegionSync:
2107 case SnapToRegionBoundary:
2108 build_region_boundary_cache ();
2109 break;
2111 default:
2112 /* relax */
2113 break;
2117 void
2118 Editor::set_snap_mode (SnapMode mode)
2120 _snap_mode = mode;
2121 string str = snap_mode_strings[(int)mode];
2123 if (str != snap_mode_selector.get_active_text ()) {
2124 snap_mode_selector.set_active_text (str);
2127 instant_save ();
2129 void
2130 Editor::set_edit_point_preference (EditPoint ep, bool force)
2132 bool changed = (_edit_point != ep);
2134 _edit_point = ep;
2135 string str = edit_point_strings[(int)ep];
2137 if (str != edit_point_selector.get_active_text ()) {
2138 edit_point_selector.set_active_text (str);
2141 set_canvas_cursor ();
2143 if (!force && !changed) {
2144 return;
2147 const char* action=NULL;
2149 switch (_edit_point) {
2150 case EditAtPlayhead:
2151 action = "edit-at-playhead";
2152 break;
2153 case EditAtSelectedMarker:
2154 action = "edit-at-marker";
2155 break;
2156 case EditAtMouse:
2157 action = "edit-at-mouse";
2158 break;
2161 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2162 if (act) {
2163 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2166 nframes64_t foo;
2167 bool in_track_canvas;
2169 if (!mouse_frame (foo, in_track_canvas)) {
2170 in_track_canvas = false;
2173 reset_canvas_action_sensitivity (in_track_canvas);
2175 instant_save ();
2179 Editor::set_state (const XMLNode& node, int /*version*/)
2181 const XMLProperty* prop;
2182 XMLNode* geometry;
2183 int x, y, xoff, yoff;
2184 Gdk::Geometry g;
2186 if ((prop = node.property ("id")) != 0) {
2187 _id = prop->value ();
2190 g.base_width = default_width;
2191 g.base_height = default_height;
2192 x = 1;
2193 y = 1;
2194 xoff = 0;
2195 yoff = 21;
2197 if ((geometry = find_named_node (node, "geometry")) != 0) {
2199 XMLProperty* prop;
2201 if ((prop = geometry->property("x_size")) == 0) {
2202 prop = geometry->property ("x-size");
2204 if (prop) {
2205 g.base_width = atoi(prop->value());
2207 if ((prop = geometry->property("y_size")) == 0) {
2208 prop = geometry->property ("y-size");
2210 if (prop) {
2211 g.base_height = atoi(prop->value());
2214 if ((prop = geometry->property ("x_pos")) == 0) {
2215 prop = geometry->property ("x-pos");
2217 if (prop) {
2218 x = atoi (prop->value());
2221 if ((prop = geometry->property ("y_pos")) == 0) {
2222 prop = geometry->property ("y-pos");
2224 if (prop) {
2225 y = atoi (prop->value());
2228 if ((prop = geometry->property ("x_off")) == 0) {
2229 prop = geometry->property ("x-off");
2231 if (prop) {
2232 xoff = atoi (prop->value());
2234 if ((prop = geometry->property ("y_off")) == 0) {
2235 prop = geometry->property ("y-off");
2237 if (prop) {
2238 yoff = atoi (prop->value());
2242 set_default_size (g.base_width, g.base_height);
2243 move (x, y);
2245 if (_session && (prop = node.property ("playhead"))) {
2246 nframes64_t pos = atol (prop->value().c_str());
2247 playhead_cursor->set_position (pos);
2248 } else {
2249 playhead_cursor->set_position (0);
2252 if ((prop = node.property ("mixer-width"))) {
2253 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2256 if ((prop = node.property ("zoom-focus"))) {
2257 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2260 if ((prop = node.property ("zoom"))) {
2261 reset_zoom (PBD::atof (prop->value()));
2264 if ((prop = node.property ("snap-to"))) {
2265 set_snap_to ((SnapType) atoi (prop->value()));
2268 if ((prop = node.property ("snap-mode"))) {
2269 set_snap_mode ((SnapMode) atoi (prop->value()));
2272 if ((prop = node.property ("mouse-mode"))) {
2273 MouseMode m = str2mousemode(prop->value());
2274 set_mouse_mode (m, true);
2275 } else {
2276 set_mouse_mode (MouseObject, true);
2279 if ((prop = node.property ("left-frame")) != 0){
2280 nframes64_t pos;
2281 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2282 reset_x_origin (pos);
2286 if ((prop = node.property ("y-origin")) != 0) {
2287 reset_y_origin (atof (prop->value ()));
2290 if ((prop = node.property ("internal-edit"))) {
2291 bool yn = string_is_affirmative (prop->value());
2292 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2293 if (act) {
2294 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2295 tact->set_active (!yn);
2296 tact->set_active (yn);
2300 if ((prop = node.property ("join-object-range"))) {
2301 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2304 if ((prop = node.property ("edit-point"))) {
2305 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2308 if ((prop = node.property ("show-waveforms-recording"))) {
2309 bool yn = string_is_affirmative (prop->value());
2310 _show_waveforms_recording = !yn;
2311 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleWaveformsWhileRecording"));
2312 if (act) {
2313 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2314 /* do it twice to force the change */
2315 tact->set_active (!yn);
2316 tact->set_active (yn);
2320 if ((prop = node.property ("show-measures"))) {
2321 bool yn = string_is_affirmative (prop->value());
2322 _show_measures = !yn;
2323 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2324 if (act) {
2325 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2326 /* do it twice to force the change */
2327 tact->set_active (!yn);
2328 tact->set_active (yn);
2332 if ((prop = node.property ("follow-playhead"))) {
2333 bool yn = string_is_affirmative (prop->value());
2334 set_follow_playhead (yn);
2335 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2336 if (act) {
2337 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2338 if (tact->get_active() != yn) {
2339 tact->set_active (yn);
2344 if ((prop = node.property ("region-list-sort-type"))) {
2345 RegionListSortType st;
2346 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2349 if ((prop = node.property ("xfades-visible"))) {
2350 bool yn = string_is_affirmative (prop->value());
2351 _xfade_visibility = !yn;
2352 // set_xfade_visibility (yn);
2355 if ((prop = node.property ("show-editor-mixer"))) {
2357 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2358 if (act) {
2360 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2361 bool yn = string_is_affirmative (prop->value());
2363 /* do it twice to force the change */
2365 tact->set_active (!yn);
2366 tact->set_active (yn);
2370 if ((prop = node.property ("show-editor-list"))) {
2372 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2373 assert(act);
2374 if (act) {
2376 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2377 bool yn = string_is_affirmative (prop->value());
2379 /* do it twice to force the change */
2381 tact->set_active (!yn);
2382 tact->set_active (yn);
2387 return 0;
2390 XMLNode&
2391 Editor::get_state ()
2393 XMLNode* node = new XMLNode ("Editor");
2394 char buf[32];
2396 _id.print (buf, sizeof (buf));
2397 node->add_property ("id", buf);
2399 if (is_realized()) {
2400 Glib::RefPtr<Gdk::Window> win = get_window();
2402 int x, y, xoff, yoff, width, height;
2403 win->get_root_origin(x, y);
2404 win->get_position(xoff, yoff);
2405 win->get_size(width, height);
2407 XMLNode* geometry = new XMLNode ("geometry");
2409 snprintf(buf, sizeof(buf), "%d", width);
2410 geometry->add_property("x-size", string(buf));
2411 snprintf(buf, sizeof(buf), "%d", height);
2412 geometry->add_property("y-size", string(buf));
2413 snprintf(buf, sizeof(buf), "%d", x);
2414 geometry->add_property("x-pos", string(buf));
2415 snprintf(buf, sizeof(buf), "%d", y);
2416 geometry->add_property("y-pos", string(buf));
2417 snprintf(buf, sizeof(buf), "%d", xoff);
2418 geometry->add_property("x-off", string(buf));
2419 snprintf(buf, sizeof(buf), "%d", yoff);
2420 geometry->add_property("y-off", string(buf));
2421 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2422 geometry->add_property("edit_pane_pos", string(buf));
2424 node->add_child_nocopy (*geometry);
2427 maybe_add_mixer_strip_width (*node);
2429 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2430 node->add_property ("zoom-focus", buf);
2431 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2432 node->add_property ("zoom", buf);
2433 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2434 node->add_property ("snap-to", buf);
2435 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2436 node->add_property ("snap-mode", buf);
2438 node->add_property ("edit-point", enum_2_string (_edit_point));
2440 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2441 node->add_property ("playhead", buf);
2442 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2443 node->add_property ("left-frame", buf);
2444 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2445 node->add_property ("y-origin", buf);
2447 node->add_property ("show-waveforms-recording", _show_waveforms_recording ? "yes" : "no");
2448 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2449 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2450 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2451 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2452 node->add_property ("mouse-mode", enum2str(mouse_mode));
2453 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2454 node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2456 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2457 if (act) {
2458 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2459 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2462 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2463 if (act) {
2464 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2465 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2468 return *node;
2473 /** @param y y offset from the top of all trackviews.
2474 * @return pair: TimeAxisView that y is over, layer index.
2475 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2476 * in stacked region display mode, otherwise 0.
2478 std::pair<TimeAxisView *, layer_t>
2479 Editor::trackview_by_y_position (double y)
2481 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2483 std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
2484 if (r.first) {
2485 return r;
2489 return std::make_pair ( (TimeAxisView *) 0, 0);
2492 /** Snap a position to the grid, if appropriate, taking into account current
2493 * grid settings and also the state of any snap modifier keys that may be pressed.
2494 * @param start Position to snap.
2495 * @param event Event to get current key modifier information from, or 0.
2497 void
2498 Editor::snap_to_with_modifier (nframes64_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2500 if (!_session || !event) {
2501 return;
2504 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2505 if (_snap_mode == SnapOff) {
2506 snap_to_internal (start, direction, for_mark);
2508 } else {
2509 if (_snap_mode != SnapOff) {
2510 snap_to_internal (start, direction, for_mark);
2515 void
2516 Editor::snap_to (nframes64_t& start, int32_t direction, bool for_mark)
2518 if (!_session || _snap_mode == SnapOff) {
2519 return;
2522 snap_to_internal (start, direction, for_mark);
2525 void
2526 Editor::timecode_snap_to_internal (nframes64_t& start, int32_t direction, bool /*for_mark*/)
2528 const nframes64_t one_timecode_second = (nframes64_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2529 nframes64_t one_timecode_minute = (nframes64_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2531 switch (_snap_type) {
2532 case SnapToTimecodeFrame:
2533 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2534 start = (nframes64_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2535 } else {
2536 start = (nframes64_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2538 break;
2540 case SnapToTimecodeSeconds:
2541 if (_session->timecode_offset_negative())
2543 start += _session->timecode_offset ();
2544 } else {
2545 start -= _session->timecode_offset ();
2547 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2548 start = (nframes64_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2549 } else {
2550 start = (nframes64_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2553 if (_session->timecode_offset_negative())
2555 start -= _session->timecode_offset ();
2556 } else {
2557 start += _session->timecode_offset ();
2559 break;
2561 case SnapToTimecodeMinutes:
2562 if (_session->timecode_offset_negative())
2564 start += _session->timecode_offset ();
2565 } else {
2566 start -= _session->timecode_offset ();
2568 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2569 start = (nframes64_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2570 } else {
2571 start = (nframes64_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2573 if (_session->timecode_offset_negative())
2575 start -= _session->timecode_offset ();
2576 } else {
2577 start += _session->timecode_offset ();
2579 break;
2580 default:
2581 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2582 /*NOTREACHED*/
2586 void
2587 Editor::snap_to_internal (nframes64_t& start, int32_t direction, bool for_mark)
2589 const nframes64_t one_second = _session->frame_rate();
2590 const nframes64_t one_minute = _session->frame_rate() * 60;
2591 nframes64_t presnap = start;
2592 nframes64_t before;
2593 nframes64_t after;
2595 switch (_snap_type) {
2596 case SnapToTimecodeFrame:
2597 case SnapToTimecodeSeconds:
2598 case SnapToTimecodeMinutes:
2599 return timecode_snap_to_internal (start, direction, for_mark);
2601 case SnapToCDFrame:
2602 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2603 start = (nframes64_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2604 } else {
2605 start = (nframes64_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2607 break;
2609 case SnapToSeconds:
2610 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2611 start = (nframes64_t) ceil ((double) start / one_second) * one_second;
2612 } else {
2613 start = (nframes64_t) floor ((double) start / one_second) * one_second;
2615 break;
2617 case SnapToMinutes:
2618 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2619 start = (nframes64_t) ceil ((double) start / one_minute) * one_minute;
2620 } else {
2621 start = (nframes64_t) floor ((double) start / one_minute) * one_minute;
2623 break;
2625 case SnapToBar:
2626 start = _session->tempo_map().round_to_bar (start, direction);
2627 break;
2629 case SnapToBeat:
2630 start = _session->tempo_map().round_to_beat (start, direction);
2631 break;
2633 case SnapToAThirtysecondBeat:
2634 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2635 break;
2637 case SnapToASixteenthBeat:
2638 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2639 break;
2641 case SnapToAEighthBeat:
2642 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2643 break;
2645 case SnapToAQuarterBeat:
2646 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2647 break;
2649 case SnapToAThirdBeat:
2650 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2651 break;
2653 case SnapToMark:
2654 if (for_mark) {
2655 return;
2658 _session->locations()->marks_either_side (start, before, after);
2660 if (before == max_frames) {
2661 start = after;
2662 } else if (after == max_frames) {
2663 start = before;
2664 } else if (before != max_frames && after != max_frames) {
2665 /* have before and after */
2666 if ((start - before) < (after - start)) {
2667 start = before;
2668 } else {
2669 start = after;
2673 break;
2675 case SnapToRegionStart:
2676 case SnapToRegionEnd:
2677 case SnapToRegionSync:
2678 case SnapToRegionBoundary:
2679 if (!region_boundary_cache.empty()) {
2681 vector<nframes64_t>::iterator prev = region_boundary_cache.end ();
2682 vector<nframes64_t>::iterator next = region_boundary_cache.end ();
2684 if (direction > 0) {
2685 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2686 } else {
2687 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2690 if (next != region_boundary_cache.begin ()) {
2691 prev = next;
2692 prev--;
2695 nframes64_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2696 nframes64_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2698 if (start > (p + n) / 2) {
2699 start = n;
2700 } else {
2701 start = p;
2704 break;
2707 switch (_snap_mode) {
2708 case SnapNormal:
2709 return;
2711 case SnapMagnetic:
2713 if (presnap > start) {
2714 if (presnap > (start + unit_to_frame(snap_threshold))) {
2715 start = presnap;
2718 } else if (presnap < start) {
2719 if (presnap < (start - unit_to_frame(snap_threshold))) {
2720 start = presnap;
2724 default:
2725 /* handled at entry */
2726 return;
2732 void
2733 Editor::setup_toolbar ()
2735 string pixmap_path;
2737 /* Mode Buttons (tool selection) */
2739 mouse_move_button.set_relief(Gtk::RELIEF_NONE);
2740 mouse_select_button.set_relief(Gtk::RELIEF_NONE);
2741 mouse_gain_button.set_relief(Gtk::RELIEF_NONE);
2742 mouse_zoom_button.set_relief(Gtk::RELIEF_NONE);
2743 mouse_timefx_button.set_relief(Gtk::RELIEF_NONE);
2744 mouse_audition_button.set_relief(Gtk::RELIEF_NONE);
2745 // internal_edit_button.set_relief(Gtk::RELIEF_NONE);
2746 join_object_range_button.set_relief(Gtk::RELIEF_NONE);
2748 HBox* mode_box = manage(new HBox);
2749 mode_box->set_border_width (2);
2750 mode_box->set_spacing(4);
2752 /* table containing mode buttons */
2754 HBox* mouse_mode_button_box = manage (new HBox ());
2756 if (Profile->get_sae()) {
2757 mouse_mode_button_box->pack_start (mouse_move_button);
2758 } else {
2759 mouse_mode_button_box->pack_start (mouse_move_button);
2760 mouse_mode_button_box->pack_start (join_object_range_button);
2761 mouse_mode_button_box->pack_start (mouse_select_button);
2764 mouse_mode_button_box->pack_start (mouse_zoom_button);
2766 if (!Profile->get_sae()) {
2767 mouse_mode_button_box->pack_start (mouse_gain_button);
2770 mouse_mode_button_box->pack_start (mouse_timefx_button);
2771 mouse_mode_button_box->pack_start (mouse_audition_button);
2772 mouse_mode_button_box->pack_start (internal_edit_button);
2774 vector<string> edit_mode_strings;
2775 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2776 if (!Profile->get_sae()) {
2777 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2779 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2781 edit_mode_selector.set_name ("EditModeSelector");
2782 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2783 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2785 mode_box->pack_start (edit_mode_selector);
2786 mode_box->pack_start (*mouse_mode_button_box);
2788 mouse_mode_tearoff = manage (new TearOff (*mode_box));
2789 mouse_mode_tearoff->set_name ("MouseModeBase");
2790 mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &mouse_mode_tearoff->tearoff_window()), false);
2792 if (Profile->get_sae()) {
2793 mouse_mode_tearoff->set_can_be_torn_off (false);
2796 mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2797 &mouse_mode_tearoff->tearoff_window()));
2798 mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2799 &mouse_mode_tearoff->tearoff_window(), 1));
2800 mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2801 &mouse_mode_tearoff->tearoff_window()));
2802 mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2803 &mouse_mode_tearoff->tearoff_window(), 1));
2805 mouse_move_button.set_mode (false);
2806 mouse_select_button.set_mode (false);
2807 mouse_gain_button.set_mode (false);
2808 mouse_zoom_button.set_mode (false);
2809 mouse_timefx_button.set_mode (false);
2810 mouse_audition_button.set_mode (false);
2811 join_object_range_button.set_mode (false);
2813 mouse_move_button.set_name ("MouseModeButton");
2814 mouse_select_button.set_name ("MouseModeButton");
2815 mouse_gain_button.set_name ("MouseModeButton");
2816 mouse_zoom_button.set_name ("MouseModeButton");
2817 mouse_timefx_button.set_name ("MouseModeButton");
2818 mouse_audition_button.set_name ("MouseModeButton");
2819 internal_edit_button.set_name ("MouseModeButton");
2820 join_object_range_button.set_name ("MouseModeButton");
2822 mouse_move_button.unset_flags (CAN_FOCUS);
2823 mouse_select_button.unset_flags (CAN_FOCUS);
2824 mouse_gain_button.unset_flags (CAN_FOCUS);
2825 mouse_zoom_button.unset_flags (CAN_FOCUS);
2826 mouse_timefx_button.unset_flags (CAN_FOCUS);
2827 mouse_audition_button.unset_flags (CAN_FOCUS);
2828 internal_edit_button.unset_flags (CAN_FOCUS);
2829 join_object_range_button.unset_flags (CAN_FOCUS);
2831 /* Zoom */
2833 zoom_box.set_spacing (1);
2834 zoom_box.set_border_width (0);
2836 zoom_in_button.set_name ("EditorTimeButton");
2837 zoom_in_button.set_image (*(manage (new Image (Stock::ZOOM_IN, Gtk::ICON_SIZE_MENU))));
2838 zoom_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), false));
2840 zoom_out_button.set_name ("EditorTimeButton");
2841 zoom_out_button.set_image (*(manage (new Image (Stock::ZOOM_OUT, Gtk::ICON_SIZE_MENU))));
2842 zoom_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), true));
2844 zoom_out_full_button.set_name ("EditorTimeButton");
2845 zoom_out_full_button.set_image (*(manage (new Image (Stock::ZOOM_100, Gtk::ICON_SIZE_MENU))));
2846 zoom_out_full_button.signal_clicked().connect (sigc::mem_fun(*this, &Editor::temporal_zoom_session));
2848 zoom_focus_selector.set_name ("ZoomFocusSelector");
2849 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
2850 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2852 zoom_box.pack_start (zoom_out_button, false, false);
2853 zoom_box.pack_start (zoom_in_button, false, false);
2854 zoom_box.pack_start (zoom_out_full_button, false, false);
2856 /* Track zoom buttons */
2857 tav_expand_button.set_name ("TrackHeightButton");
2858 tav_expand_button.set_size_request(-1,20);
2859 tav_expand_button.add (*(manage (new Image (::get_icon("tav_exp")))));
2860 tav_expand_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), true));
2862 tav_shrink_button.set_name ("TrackHeightButton");
2863 tav_shrink_button.set_size_request(-1,20);
2864 tav_shrink_button.add (*(manage (new Image (::get_icon("tav_shrink")))));
2865 tav_shrink_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), false));
2867 track_zoom_box.set_spacing (1);
2868 track_zoom_box.set_border_width (0);
2870 track_zoom_box.pack_start (tav_shrink_button, false, false);
2871 track_zoom_box.pack_start (tav_expand_button, false, false);
2873 HBox* zbc = manage (new HBox);
2874 zbc->pack_start (zoom_focus_selector, PACK_SHRINK);
2875 zoom_vbox.pack_start (*zbc, PACK_SHRINK);
2876 zoom_vbox.pack_start (zoom_box, PACK_SHRINK);
2877 zoom_vbox.pack_start (track_zoom_box, PACK_SHRINK);
2879 snap_box.set_spacing (1);
2880 snap_box.set_border_width (2);
2882 snap_type_selector.set_name ("SnapTypeSelector");
2883 set_popdown_strings (snap_type_selector, snap_type_strings, true);
2884 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2886 snap_mode_selector.set_name ("SnapModeSelector");
2887 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
2888 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2890 edit_point_selector.set_name ("EditPointSelector");
2891 set_popdown_strings (edit_point_selector, edit_point_strings, true);
2892 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2894 snap_box.pack_start (snap_mode_selector, false, false);
2895 snap_box.pack_start (snap_type_selector, false, false);
2896 snap_box.pack_start (edit_point_selector, false, false);
2898 /* Nudge */
2900 HBox *nudge_box = manage (new HBox);
2901 nudge_box->set_spacing(1);
2902 nudge_box->set_border_width (2);
2904 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2905 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2907 nudge_box->pack_start (nudge_backward_button, false, false);
2908 nudge_box->pack_start (nudge_forward_button, false, false);
2909 nudge_box->pack_start (nudge_clock, false, false);
2912 /* Pack everything in... */
2914 HBox* hbox = manage (new HBox);
2915 hbox->set_spacing(10);
2917 tools_tearoff = manage (new TearOff (*hbox));
2918 tools_tearoff->set_name ("MouseModeBase");
2919 tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &tools_tearoff->tearoff_window()), false);
2921 if (Profile->get_sae()) {
2922 tools_tearoff->set_can_be_torn_off (false);
2925 tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2926 &tools_tearoff->tearoff_window()));
2927 tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2928 &tools_tearoff->tearoff_window(), 0));
2929 tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2930 &tools_tearoff->tearoff_window()));
2931 tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2932 &tools_tearoff->tearoff_window(), 0));
2934 toolbar_hbox.set_spacing (10);
2935 toolbar_hbox.set_border_width (1);
2937 toolbar_hbox.pack_start (*mouse_mode_tearoff, false, false);
2938 toolbar_hbox.pack_start (*tools_tearoff, false, false);
2940 hbox->pack_start (snap_box, false, false);
2941 hbox->pack_start (*nudge_box, false, false);
2942 hbox->pack_start (panic_box, false, false);
2944 hbox->show_all ();
2946 toolbar_base.set_name ("ToolBarBase");
2947 toolbar_base.add (toolbar_hbox);
2949 toolbar_frame.set_shadow_type (SHADOW_OUT);
2950 toolbar_frame.set_name ("BaseFrame");
2951 toolbar_frame.add (toolbar_base);
2954 void
2955 Editor::setup_tooltips ()
2957 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
2958 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Select/Move Ranges"));
2959 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Gain Automation"));
2960 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
2961 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions"));
2962 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2963 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
2964 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
2965 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: context-click for possible operations"));
2966 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
2967 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
2968 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
2969 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
2970 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
2971 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
2972 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
2973 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
2974 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
2975 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
2976 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
2977 ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
2978 ARDOUR_UI::instance()->set_tip (midi_panic_button, _("Send note off and reset controller messages on all MIDI channels"));
2981 void
2982 Editor::midi_panic ()
2984 cerr << "MIDI panic\n";
2986 if (_session) {
2987 _session->midi_panic();
2991 void
2992 Editor::setup_midi_toolbar ()
2994 RefPtr<Action> act;
2996 /* Midi sound notes */
2997 midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
2998 midi_sound_notes.set_relief(Gtk::RELIEF_NONE);
2999 midi_sound_notes.unset_flags (CAN_FOCUS);
3001 /* Panic */
3003 act = ActionManager::get_action (X_("MIDI"), X_("panic"));
3004 midi_panic_button.set_name("MidiPanicButton");
3005 act->connect_proxy (midi_panic_button);
3007 panic_box.pack_start (midi_sound_notes , true, true);
3008 panic_box.pack_start (midi_panic_button, true, true);
3012 Editor::convert_drop_to_paths (
3013 vector<ustring>& paths,
3014 const RefPtr<Gdk::DragContext>& /*context*/,
3015 gint /*x*/,
3016 gint /*y*/,
3017 const SelectionData& data,
3018 guint /*info*/,
3019 guint /*time*/)
3021 if (_session == 0) {
3022 return -1;
3025 vector<ustring> uris = data.get_uris();
3027 if (uris.empty()) {
3029 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3030 are actually URI lists. So do it by hand.
3033 if (data.get_target() != "text/plain") {
3034 return -1;
3037 /* Parse the "uri-list" format that Nautilus provides,
3038 where each pathname is delimited by \r\n.
3040 THERE MAY BE NO NULL TERMINATING CHAR!!!
3043 ustring txt = data.get_text();
3044 const char* p;
3045 const char* q;
3047 p = (const char *) malloc (txt.length() + 1);
3048 txt.copy ((char *) p, txt.length(), 0);
3049 ((char*)p)[txt.length()] = '\0';
3051 while (p)
3053 if (*p != '#')
3055 while (g_ascii_isspace (*p))
3056 p++;
3058 q = p;
3059 while (*q && (*q != '\n') && (*q != '\r')) {
3060 q++;
3063 if (q > p)
3065 q--;
3066 while (q > p && g_ascii_isspace (*q))
3067 q--;
3069 if (q > p)
3071 uris.push_back (ustring (p, q - p + 1));
3075 p = strchr (p, '\n');
3076 if (p)
3077 p++;
3080 free ((void*)p);
3082 if (uris.empty()) {
3083 return -1;
3087 for (vector<ustring>::iterator i = uris.begin(); i != uris.end(); ++i) {
3089 if ((*i).substr (0,7) == "file://") {
3091 ustring p = *i;
3092 PBD::url_decode (p);
3094 // scan forward past three slashes
3096 ustring::size_type slashcnt = 0;
3097 ustring::size_type n = 0;
3098 ustring::iterator x = p.begin();
3100 while (slashcnt < 3 && x != p.end()) {
3101 if ((*x) == '/') {
3102 slashcnt++;
3103 } else if (slashcnt == 3) {
3104 break;
3106 ++n;
3107 ++x;
3110 if (slashcnt != 3 || x == p.end()) {
3111 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3112 continue;
3115 paths.push_back (p.substr (n - 1));
3119 return 0;
3122 void
3123 Editor::new_tempo_section ()
3128 void
3129 Editor::map_transport_state ()
3131 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3133 if (_session->transport_stopped()) {
3134 have_pending_keyboard_selection = false;
3137 update_loop_range_view (true);
3140 /* UNDO/REDO */
3142 Editor::State::State (PublicEditor const * e)
3144 selection = new Selection (e);
3147 Editor::State::~State ()
3149 delete selection;
3152 void
3153 Editor::begin_reversible_command (string name)
3155 if (_session) {
3156 _session->begin_reversible_command (name);
3160 void
3161 Editor::commit_reversible_command ()
3163 if (_session) {
3164 _session->commit_reversible_command ();
3168 void
3169 Editor::set_route_group_solo (Route& route, bool yn)
3171 RouteGroup *route_group;
3173 if ((route_group = route.route_group()) != 0) {
3174 route_group->apply (&Route::set_solo, yn, this);
3175 } else {
3176 route.set_solo (yn, this);
3180 void
3181 Editor::set_route_group_mute (Route& route, bool yn)
3183 RouteGroup *route_group = 0;
3185 if ((route_group = route.route_group()) != 0) {
3186 route_group->apply (&Route::set_mute, yn, this);
3187 } else {
3188 route.set_mute (yn, this);
3192 void
3193 Editor::history_changed ()
3195 string label;
3197 if (undo_action && _session) {
3198 if (_session->undo_depth() == 0) {
3199 label = _("Undo");
3200 } else {
3201 label = string_compose(_("Undo (%1)"), _session->next_undo());
3203 undo_action->property_label() = label;
3206 if (redo_action && _session) {
3207 if (_session->redo_depth() == 0) {
3208 label = _("Redo");
3209 } else {
3210 label = string_compose(_("Redo (%1)"), _session->next_redo());
3212 redo_action->property_label() = label;
3216 void
3217 Editor::duplicate_dialog (bool with_dialog)
3219 float times = 1.0f;
3221 if (mouse_mode == MouseRange) {
3222 if (selection->time.length() == 0) {
3223 return;
3227 RegionSelection rs;
3228 get_regions_for_action (rs);
3230 if (mouse_mode != MouseRange) {
3232 if (rs.empty()) {
3233 return;
3237 if (with_dialog) {
3239 ArdourDialog win ("Duplicate");
3240 Label label (_("Number of Duplications:"));
3241 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3242 SpinButton spinner (adjustment, 0.0, 1);
3243 HBox hbox;
3245 win.get_vbox()->set_spacing (12);
3246 win.get_vbox()->pack_start (hbox);
3247 hbox.set_border_width (6);
3248 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3250 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3251 place, visually. so do this by hand.
3254 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3255 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3256 spinner.grab_focus();
3258 hbox.show ();
3259 label.show ();
3260 spinner.show ();
3262 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3263 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3264 win.set_default_response (RESPONSE_ACCEPT);
3266 win.set_position (WIN_POS_MOUSE);
3268 spinner.grab_focus ();
3270 switch (win.run ()) {
3271 case RESPONSE_ACCEPT:
3272 break;
3273 default:
3274 return;
3277 times = adjustment.get_value();
3280 if (mouse_mode == MouseRange) {
3281 duplicate_selection (times);
3282 } else {
3283 duplicate_some_regions (rs, times);
3287 void
3288 Editor::show_verbose_canvas_cursor ()
3290 verbose_canvas_cursor->raise_to_top();
3291 verbose_canvas_cursor->show();
3292 verbose_cursor_visible = true;
3295 void
3296 Editor::hide_verbose_canvas_cursor ()
3298 verbose_canvas_cursor->hide();
3299 verbose_cursor_visible = false;
3302 double
3303 Editor::clamp_verbose_cursor_x (double x)
3305 if (x < 0) {
3306 x = 0;
3307 } else {
3308 x = min (_canvas_width - 200.0, x);
3310 return x;
3313 double
3314 Editor::clamp_verbose_cursor_y (double y)
3316 if (y < canvas_timebars_vsize) {
3317 y = canvas_timebars_vsize;
3318 } else {
3319 y = min (_canvas_height - 50, y);
3321 return y;
3324 void
3325 Editor::show_verbose_canvas_cursor_with (const string & txt)
3327 verbose_canvas_cursor->property_text() = txt.c_str();
3329 int x, y;
3330 double wx, wy;
3332 track_canvas->get_pointer (x, y);
3333 track_canvas->window_to_world (x, y, wx, wy);
3335 /* don't get too close to the edge */
3336 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (wx);
3337 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (wy);
3339 show_verbose_canvas_cursor ();
3342 void
3343 Editor::set_verbose_canvas_cursor (const string & txt, double x, double y)
3345 verbose_canvas_cursor->property_text() = txt.c_str();
3346 /* don't get too close to the edge */
3347 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (x);
3348 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (y);
3351 void
3352 Editor::set_verbose_canvas_cursor_text (const string & txt)
3354 verbose_canvas_cursor->property_text() = txt.c_str();
3357 void
3358 Editor::set_edit_mode (EditMode m)
3360 Config->set_edit_mode (m);
3363 void
3364 Editor::cycle_edit_mode ()
3366 switch (Config->get_edit_mode()) {
3367 case Slide:
3368 if (Profile->get_sae()) {
3369 Config->set_edit_mode (Lock);
3370 } else {
3371 Config->set_edit_mode (Splice);
3373 break;
3374 case Splice:
3375 Config->set_edit_mode (Lock);
3376 break;
3377 case Lock:
3378 Config->set_edit_mode (Slide);
3379 break;
3383 void
3384 Editor::edit_mode_selection_done ()
3386 if (_session == 0) {
3387 return;
3390 string choice = edit_mode_selector.get_active_text();
3391 EditMode mode = Slide;
3393 if (choice == _("Splice Edit")) {
3394 mode = Splice;
3395 } else if (choice == _("Slide Edit")) {
3396 mode = Slide;
3397 } else if (choice == _("Lock Edit")) {
3398 mode = Lock;
3401 Config->set_edit_mode (mode);
3404 void
3405 Editor::snap_type_selection_done ()
3407 string choice = snap_type_selector.get_active_text();
3408 SnapType snaptype = SnapToBeat;
3410 if (choice == _("Beats/3")) {
3411 snaptype = SnapToAThirdBeat;
3412 } else if (choice == _("Beats/4")) {
3413 snaptype = SnapToAQuarterBeat;
3414 } else if (choice == _("Beats/8")) {
3415 snaptype = SnapToAEighthBeat;
3416 } else if (choice == _("Beats/16")) {
3417 snaptype = SnapToASixteenthBeat;
3418 } else if (choice == _("Beats/32")) {
3419 snaptype = SnapToAThirtysecondBeat;
3420 } else if (choice == _("Beats")) {
3421 snaptype = SnapToBeat;
3422 } else if (choice == _("Bars")) {
3423 snaptype = SnapToBar;
3424 } else if (choice == _("Marks")) {
3425 snaptype = SnapToMark;
3426 } else if (choice == _("Region starts")) {
3427 snaptype = SnapToRegionStart;
3428 } else if (choice == _("Region ends")) {
3429 snaptype = SnapToRegionEnd;
3430 } else if (choice == _("Region bounds")) {
3431 snaptype = SnapToRegionBoundary;
3432 } else if (choice == _("Region syncs")) {
3433 snaptype = SnapToRegionSync;
3434 } else if (choice == _("CD Frames")) {
3435 snaptype = SnapToCDFrame;
3436 } else if (choice == _("Timecode Frames")) {
3437 snaptype = SnapToTimecodeFrame;
3438 } else if (choice == _("Timecode Seconds")) {
3439 snaptype = SnapToTimecodeSeconds;
3440 } else if (choice == _("Timecode Minutes")) {
3441 snaptype = SnapToTimecodeMinutes;
3442 } else if (choice == _("Seconds")) {
3443 snaptype = SnapToSeconds;
3444 } else if (choice == _("Minutes")) {
3445 snaptype = SnapToMinutes;
3448 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3449 if (ract) {
3450 ract->set_active ();
3454 void
3455 Editor::snap_mode_selection_done ()
3457 string choice = snap_mode_selector.get_active_text();
3458 SnapMode mode = SnapNormal;
3460 if (choice == _("No Grid")) {
3461 mode = SnapOff;
3462 } else if (choice == _("Grid")) {
3463 mode = SnapNormal;
3464 } else if (choice == _("Magnetic")) {
3465 mode = SnapMagnetic;
3468 RefPtr<RadioAction> ract = snap_mode_action (mode);
3470 if (ract) {
3471 ract->set_active (true);
3475 void
3476 Editor::cycle_edit_point (bool with_marker)
3478 switch (_edit_point) {
3479 case EditAtMouse:
3480 set_edit_point_preference (EditAtPlayhead);
3481 break;
3482 case EditAtPlayhead:
3483 if (with_marker) {
3484 set_edit_point_preference (EditAtSelectedMarker);
3485 } else {
3486 set_edit_point_preference (EditAtMouse);
3488 break;
3489 case EditAtSelectedMarker:
3490 set_edit_point_preference (EditAtMouse);
3491 break;
3495 void
3496 Editor::edit_point_selection_done ()
3498 string choice = edit_point_selector.get_active_text();
3499 EditPoint ep = EditAtSelectedMarker;
3501 if (choice == _("Marker")) {
3502 set_edit_point_preference (EditAtSelectedMarker);
3503 } else if (choice == _("Playhead")) {
3504 set_edit_point_preference (EditAtPlayhead);
3505 } else {
3506 set_edit_point_preference (EditAtMouse);
3509 RefPtr<RadioAction> ract = edit_point_action (ep);
3511 if (ract) {
3512 ract->set_active (true);
3516 void
3517 Editor::zoom_focus_selection_done ()
3519 string choice = zoom_focus_selector.get_active_text();
3520 ZoomFocus focus_type = ZoomFocusLeft;
3522 if (choice == _("Left")) {
3523 focus_type = ZoomFocusLeft;
3524 } else if (choice == _("Right")) {
3525 focus_type = ZoomFocusRight;
3526 } else if (choice == _("Center")) {
3527 focus_type = ZoomFocusCenter;
3528 } else if (choice == _("Playhead")) {
3529 focus_type = ZoomFocusPlayhead;
3530 } else if (choice == _("Mouse")) {
3531 focus_type = ZoomFocusMouse;
3532 } else if (choice == _("Edit point")) {
3533 focus_type = ZoomFocusEdit;
3536 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3538 if (ract) {
3539 ract->set_active ();
3543 gint
3544 Editor::edit_controls_button_release (GdkEventButton* ev)
3546 if (Keyboard::is_context_menu_event (ev)) {
3547 ARDOUR_UI::instance()->add_route (this);
3549 return TRUE;
3552 gint
3553 Editor::mouse_select_button_release (GdkEventButton* ev)
3555 /* this handles just right-clicks */
3557 if (ev->button != 3) {
3558 return false;
3561 return true;
3564 void
3565 Editor::set_zoom_focus (ZoomFocus f)
3567 string str = zoom_focus_strings[(int)f];
3569 if (str != zoom_focus_selector.get_active_text()) {
3570 zoom_focus_selector.set_active_text (str);
3573 if (zoom_focus != f) {
3574 zoom_focus = f;
3576 ZoomFocusChanged (); /* EMIT_SIGNAL */
3578 instant_save ();
3582 void
3583 Editor::ensure_float (Window& win)
3585 win.set_transient_for (*this);
3588 void
3589 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3591 /* recover or initialize pane positions. do this here rather than earlier because
3592 we don't want the positions to change the child allocations, which they seem to do.
3595 int pos;
3596 XMLProperty* prop;
3597 char buf[32];
3598 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3599 int width, height;
3600 static int32_t done;
3601 XMLNode* geometry;
3603 width = default_width;
3604 height = default_height;
3606 if ((geometry = find_named_node (*node, "geometry")) != 0) {
3608 if ((prop = geometry->property ("x_size")) == 0) {
3609 prop = geometry->property ("x-size");
3611 if (prop) {
3612 width = atoi (prop->value());
3614 if ((prop = geometry->property ("y_size")) == 0) {
3615 prop = geometry->property ("y-size");
3617 if (prop) {
3618 height = atoi (prop->value());
3622 if (which == static_cast<Paned*> (&edit_pane)) {
3624 if (done) {
3625 return;
3628 if (!geometry || (prop = geometry->property ("edit-pane-pos")) == 0) {
3629 /* initial allocation is 90% to canvas, 10% to notebook */
3630 pos = (int) floor (alloc.get_width() * 0.90f);
3631 snprintf (buf, sizeof(buf), "%d", pos);
3632 } else {
3633 pos = atoi (prop->value());
3636 if ((done = GTK_WIDGET(edit_pane.gobj())->allocation.width > pos)) {
3637 edit_pane.set_position (pos);
3638 pre_maximal_pane_position = pos;
3643 void
3644 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3646 cerr << "remove tearoff\n";
3648 if (tools_tearoff->torn_off() &&
3649 mouse_mode_tearoff->torn_off()) {
3650 top_hbox.remove (toolbar_frame);
3654 void
3655 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3657 cerr << "reattach tearoff\n";
3658 if (toolbar_frame.get_parent() == 0) {
3659 top_hbox.pack_end (toolbar_frame);
3663 void
3664 Editor::set_show_measures (bool yn)
3666 if (_show_measures != yn) {
3667 hide_measures ();
3669 if ((_show_measures = yn) == true) {
3670 if (tempo_lines)
3671 tempo_lines->show();
3672 draw_measures ();
3674 instant_save ();
3678 void
3679 Editor::toggle_follow_playhead ()
3681 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3682 if (act) {
3683 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3684 set_follow_playhead (tact->get_active());
3688 void
3689 Editor::set_follow_playhead (bool yn)
3691 if (_follow_playhead != yn) {
3692 if ((_follow_playhead = yn) == true) {
3693 /* catch up */
3694 reset_x_origin_to_follow_playhead ();
3696 instant_save ();
3700 void
3701 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3703 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3704 if (xfade) {
3705 xfade->set_active (!xfade->active());
3709 void
3710 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3712 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3713 if (xfade) {
3714 xfade->set_follow_overlap (!xfade->following_overlap());
3718 void
3719 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3721 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3723 if (!xfade) {
3724 return;
3727 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3729 ensure_float (cew);
3731 switch (cew.run ()) {
3732 case RESPONSE_ACCEPT:
3733 break;
3734 default:
3735 return;
3738 cew.apply ();
3739 PropertyChange all_crossfade_properties;
3740 all_crossfade_properties.add (ARDOUR::Properties::active);
3741 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3742 xfade->PropertyChanged (all_crossfade_properties);
3745 PlaylistSelector&
3746 Editor::playlist_selector () const
3748 return *_playlist_selector;
3751 Evoral::MusicalTime
3752 Editor::get_grid_type_as_beats (bool& success, nframes64_t position)
3754 success = true;
3756 switch (_snap_type) {
3757 case SnapToBeat:
3758 return 1.0;
3759 break;
3761 case SnapToAThirtysecondBeat:
3762 return 1.0/32.0;
3763 break;
3765 case SnapToASixteenthBeat:
3766 return 1.0/16.0;
3767 break;
3769 case SnapToAEighthBeat:
3770 return 1.0/8.0;
3771 break;
3773 case SnapToAQuarterBeat:
3774 return 1.0/4.0;
3775 break;
3777 case SnapToAThirdBeat:
3778 return 1.0/3.0;
3779 break;
3781 case SnapToBar:
3782 if (_session) {
3783 return _session->tempo_map().meter_at (position).beats_per_bar();
3785 break;
3787 case SnapToCDFrame:
3788 case SnapToTimecodeFrame:
3789 case SnapToTimecodeSeconds:
3790 case SnapToTimecodeMinutes:
3791 case SnapToSeconds:
3792 case SnapToMinutes:
3793 case SnapToRegionStart:
3794 case SnapToRegionEnd:
3795 case SnapToRegionSync:
3796 case SnapToRegionBoundary:
3797 default:
3798 success = false;
3799 break;
3802 return 0.0;
3805 nframes64_t
3806 Editor::get_nudge_distance (nframes64_t pos, nframes64_t& next)
3808 nframes64_t ret;
3810 ret = nudge_clock.current_duration (pos);
3811 next = ret + 1; /* XXXX fix me */
3813 return ret;
3816 void
3817 Editor::end_location_changed (Location* location)
3819 ENSURE_GUI_THREAD (*this, &Editor::end_location_changed, location)
3820 //reset_scrolling_region ();
3821 nframes64_t session_span = location->start() + (nframes64_t) floorf (current_page_frames() * 0.10f);
3822 horizontal_adjustment.set_upper (session_span / frames_per_unit);
3826 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3828 ArdourDialog dialog ("playlist deletion dialog");
3829 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3830 "If left alone, no audio files used by it will be cleaned.\n"
3831 "If deleted, audio files used by it alone by will cleaned."),
3832 pl->name()));
3834 dialog.set_position (WIN_POS_CENTER);
3835 dialog.get_vbox()->pack_start (label);
3837 label.show ();
3839 dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT);
3840 dialog.add_button (_("Keep playlist"), RESPONSE_REJECT);
3841 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3843 switch (dialog.run ()) {
3844 case RESPONSE_ACCEPT:
3845 /* delete the playlist */
3846 return 0;
3847 break;
3849 case RESPONSE_REJECT:
3850 /* keep the playlist */
3851 return 1;
3852 break;
3854 default:
3855 break;
3858 return -1;
3861 bool
3862 Editor::audio_region_selection_covers (nframes64_t where)
3864 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3865 if ((*a)->region()->covers (where)) {
3866 return true;
3870 return false;
3873 void
3874 Editor::prepare_for_cleanup ()
3876 cut_buffer->clear_regions ();
3877 cut_buffer->clear_playlists ();
3879 selection->clear_regions ();
3880 selection->clear_playlists ();
3882 _regions->suspend_redisplay ();
3885 void
3886 Editor::finish_cleanup ()
3888 _regions->resume_redisplay ();
3891 Location*
3892 Editor::transport_loop_location()
3894 if (_session) {
3895 return _session->locations()->auto_loop_location();
3896 } else {
3897 return 0;
3901 Location*
3902 Editor::transport_punch_location()
3904 if (_session) {
3905 return _session->locations()->auto_punch_location();
3906 } else {
3907 return 0;
3911 bool
3912 Editor::control_layout_scroll (GdkEventScroll* ev)
3914 if (Keyboard::some_magic_widget_has_focus()) {
3915 return false;
3918 switch (ev->direction) {
3919 case GDK_SCROLL_UP:
3920 scroll_tracks_up_line ();
3921 return true;
3922 break;
3924 case GDK_SCROLL_DOWN:
3925 scroll_tracks_down_line ();
3926 return true;
3928 default:
3929 /* no left/right handling yet */
3930 break;
3933 return false;
3936 void
3937 Editor::session_state_saved (string snap_name)
3939 ENSURE_GUI_THREAD (*this, &Editor::session_state_saved, snap_name);
3941 update_title ();
3942 _snapshots->redisplay ();
3945 void
3946 Editor::maximise_editing_space ()
3948 mouse_mode_tearoff->set_visible (false);
3949 tools_tearoff->set_visible (false);
3951 pre_maximal_pane_position = edit_pane.get_position();
3952 pre_maximal_editor_width = this->get_width();
3954 if(post_maximal_pane_position == 0) {
3955 post_maximal_pane_position = edit_pane.get_width();
3958 fullscreen();
3960 if(post_maximal_editor_width) {
3961 edit_pane.set_position (post_maximal_pane_position -
3962 abs(post_maximal_editor_width - pre_maximal_editor_width));
3963 } else {
3964 edit_pane.set_position (post_maximal_pane_position);
3968 void
3969 Editor::restore_editing_space ()
3971 // user changed width of pane during fullscreen
3973 if(post_maximal_pane_position != edit_pane.get_position()) {
3974 post_maximal_pane_position = edit_pane.get_position();
3977 unfullscreen();
3979 mouse_mode_tearoff->set_visible (true);
3980 tools_tearoff->set_visible (true);
3981 post_maximal_editor_width = this->get_width();
3983 edit_pane.set_position (pre_maximal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
3987 * Make new playlists for a given track and also any others that belong
3988 * to the same active route group with the `edit' property.
3989 * @param v Track.
3992 void
3993 Editor::new_playlists (TimeAxisView* v)
3995 begin_reversible_command (_("new playlists"));
3996 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
3997 _session->playlists->get (playlists);
3998 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
3999 commit_reversible_command ();
4003 * Use a copy of the current playlist for a given track and also any others that belong
4004 * to the same active route group with the `edit' property.
4005 * @param v Track.
4008 void
4009 Editor::copy_playlists (TimeAxisView* v)
4011 begin_reversible_command (_("copy playlists"));
4012 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4013 _session->playlists->get (playlists);
4014 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4015 commit_reversible_command ();
4018 /** Clear the current playlist for a given track and also any others that belong
4019 * to the same active route group with the `edit' property.
4020 * @param v Track.
4023 void
4024 Editor::clear_playlists (TimeAxisView* v)
4026 begin_reversible_command (_("clear playlists"));
4027 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4028 _session->playlists->get (playlists);
4029 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4030 commit_reversible_command ();
4033 void
4034 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4036 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4039 void
4040 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4042 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4045 void
4046 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4048 atv.clear_playlist ();
4051 bool
4052 Editor::on_key_press_event (GdkEventKey* ev)
4054 return key_press_focus_accelerator_handler (*this, ev);
4057 bool
4058 Editor::on_key_release_event (GdkEventKey* ev)
4060 return Gtk::Window::on_key_release_event (ev);
4061 // return key_press_focus_accelerator_handler (*this, ev);
4064 /** Queue up a change to the viewport x origin.
4065 * @param frame New x origin.
4067 void
4068 Editor::reset_x_origin (nframes64_t frame)
4070 queue_visual_change (frame);
4073 void
4074 Editor::reset_y_origin (double y)
4076 queue_visual_change_y (y);
4079 void
4080 Editor::reset_zoom (double fpu)
4082 queue_visual_change (fpu);
4085 void
4086 Editor::reposition_and_zoom (nframes64_t frame, double fpu)
4088 //cerr << "Editor::reposition_and_zoom () called ha v:l:u:ps:fpu = " << horizontal_adjustment.get_value() << ":" << horizontal_adjustment.get_lower() << ":" << horizontal_adjustment.get_upper() << ":" << horizontal_adjustment.get_page_size() << ":" << frames_per_unit << endl;//DEBUG
4089 reset_x_origin (frame);
4090 reset_zoom (fpu);
4092 if (!no_save_visual) {
4093 undo_visual_stack.push_back (current_visual_state(false));
4097 Editor::VisualState*
4098 Editor::current_visual_state (bool with_tracks)
4100 VisualState* vs = new VisualState;
4101 vs->y_position = vertical_adjustment.get_value();
4102 vs->frames_per_unit = frames_per_unit;
4103 vs->leftmost_frame = leftmost_frame;
4104 vs->zoom_focus = zoom_focus;
4106 if (with_tracks) {
4107 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4108 vs->track_states.push_back (TAVState ((*i), &(*i)->get_state()));
4112 return vs;
4115 void
4116 Editor::undo_visual_state ()
4118 if (undo_visual_stack.empty()) {
4119 return;
4122 VisualState* vs = undo_visual_stack.back();
4123 undo_visual_stack.pop_back();
4124 use_visual_state (*vs);
4125 redo_visual_stack.push_back (vs);
4128 void
4129 Editor::redo_visual_state ()
4131 if (redo_visual_stack.empty()) {
4132 return;
4135 VisualState* vs = redo_visual_stack.back();
4136 redo_visual_stack.pop_back();
4137 use_visual_state (*vs);
4138 undo_visual_stack.push_back (vs);
4141 void
4142 Editor::swap_visual_state ()
4144 if (undo_visual_stack.empty()) {
4145 redo_visual_state ();
4146 } else {
4147 undo_visual_state ();
4151 void
4152 Editor::use_visual_state (VisualState& vs)
4154 no_save_visual = true;
4156 _routes->suspend_redisplay ();
4158 vertical_adjustment.set_value (vs.y_position);
4160 set_zoom_focus (vs.zoom_focus);
4161 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4163 for (list<TAVState>::iterator i = vs.track_states.begin(); i != vs.track_states.end(); ++i) {
4164 TrackViewList::iterator t;
4166 /* check if the track still exists - it could have been deleted */
4168 if ((t = find (track_views.begin(), track_views.end(), i->first)) != track_views.end()) {
4169 (*t)->set_state (*(i->second), Stateful::loading_state_version);
4174 if (!vs.track_states.empty()) {
4175 _routes->update_visibility ();
4178 _routes->resume_redisplay ();
4180 no_save_visual = false;
4183 void
4184 Editor::set_frames_per_unit (double fpu)
4186 /* this is the core function that controls the zoom level of the canvas. it is called
4187 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4190 if (fpu == frames_per_unit) {
4191 return;
4194 if (fpu < 2.0) {
4195 fpu = 2.0;
4199 /* don't allow zooms that fit more than the maximum number
4200 of frames into an 800 pixel wide space.
4203 if (max_frames / fpu < 800.0) {
4204 return;
4207 if (tempo_lines)
4208 tempo_lines->tempo_map_changed();
4210 frames_per_unit = fpu;
4211 post_zoom ();
4214 void
4215 Editor::post_zoom ()
4217 nframes64_t cef = 0;
4219 // convert fpu to frame count
4221 nframes64_t frames = (nframes64_t) floor (frames_per_unit * _canvas_width);
4223 if (frames_per_unit != zoom_range_clock.current_duration()) {
4224 zoom_range_clock.set (frames);
4227 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4228 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4229 (*i)->reshow_selection (selection->time);
4233 leftmost_frame = (nframes64_t) floor (horizontal_adjustment.get_value() * frames_per_unit);
4235 ZoomChanged (); /* EMIT_SIGNAL */
4237 if (_session) {
4238 cef = _session->current_end_frame() + (current_page_frames() / 10);// Add a little extra so we can see the end marker
4240 horizontal_adjustment.set_upper (cef / frames_per_unit);
4242 //reset_scrolling_region ();
4244 if (playhead_cursor) {
4245 playhead_cursor->set_position (playhead_cursor->current_frame);
4248 refresh_location_display();
4249 _summary->set_overlays_dirty ();
4251 instant_save ();
4254 void
4255 Editor::queue_visual_change (nframes64_t where)
4257 pending_visual_change.add (VisualChange::TimeOrigin);
4259 /* if we're moving beyond the end, make sure the upper limit of the horizontal adjustment
4260 can reach.
4263 if (_session && (where > _session->current_end_frame())) {
4264 horizontal_adjustment.set_upper ((where + current_page_frames()) / frames_per_unit);
4267 pending_visual_change.time_origin = where;
4269 ensure_visual_change_idle_handler ();
4272 void
4273 Editor::queue_visual_change (double fpu)
4275 pending_visual_change.add (VisualChange::ZoomLevel);
4276 pending_visual_change.frames_per_unit = fpu;
4278 ensure_visual_change_idle_handler ();
4281 void
4282 Editor::queue_visual_change_y (double y)
4284 pending_visual_change.add (VisualChange::YOrigin);
4285 pending_visual_change.y_origin = y;
4287 ensure_visual_change_idle_handler ();
4290 void
4291 Editor::ensure_visual_change_idle_handler ()
4293 if (pending_visual_change.idle_handler_id < 0) {
4294 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4299 Editor::_idle_visual_changer (void* arg)
4301 return static_cast<Editor*>(arg)->idle_visual_changer ();
4305 Editor::idle_visual_changer ()
4307 VisualChange::Type p = pending_visual_change.pending;
4308 pending_visual_change.pending = (VisualChange::Type) 0;
4310 double last_time_origin = horizontal_adjustment.get_value();
4312 if (p & VisualChange::ZoomLevel) {
4313 set_frames_per_unit (pending_visual_change.frames_per_unit);
4315 compute_fixed_ruler_scale ();
4316 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4317 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4318 update_tempo_based_rulers ();
4320 if (p & VisualChange::TimeOrigin) {
4321 horizontal_adjustment.set_value (pending_visual_change.time_origin / frames_per_unit);
4323 if (p & VisualChange::YOrigin) {
4324 vertical_adjustment.set_value (pending_visual_change.y_origin);
4327 if (last_time_origin == horizontal_adjustment.get_value()) {
4328 /* changed signal not emitted */
4329 update_fixed_rulers ();
4330 redisplay_tempo (true);
4333 _summary->set_overlays_dirty ();
4335 // cerr << "Editor::idle_visual_changer () called ha v:l:u:ps:fpu = " << horizontal_adjustment.get_value() << ":" << horizontal_adjustment.get_lower() << ":" << horizontal_adjustment.get_upper() << ":" << horizontal_adjustment.get_page_size() << ":" << frames_per_unit << endl;//DEBUG
4336 pending_visual_change.idle_handler_id = -1;
4337 return 0; /* this is always a one-shot call */
4340 struct EditorOrderTimeAxisSorter {
4341 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4342 return a->order () < b->order ();
4346 void
4347 Editor::sort_track_selection (TrackViewList* sel)
4349 EditorOrderTimeAxisSorter cmp;
4351 if (sel) {
4352 sel->sort (cmp);
4353 } else {
4354 selection->tracks.sort (cmp);
4358 nframes64_t
4359 Editor::get_preferred_edit_position (bool ignore_playhead)
4361 bool ignored;
4362 nframes64_t where = 0;
4363 EditPoint ep = _edit_point;
4365 if (entered_marker) {
4366 return entered_marker->position();
4369 if (ignore_playhead && ep == EditAtPlayhead) {
4370 ep = EditAtSelectedMarker;
4373 switch (ep) {
4374 case EditAtPlayhead:
4375 where = _session->audible_frame();
4376 break;
4378 case EditAtSelectedMarker:
4379 if (!selection->markers.empty()) {
4380 bool is_start;
4381 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4382 if (loc) {
4383 if (is_start) {
4384 where = loc->start();
4385 } else {
4386 where = loc->end();
4388 break;
4391 /* fallthru */
4393 default:
4394 case EditAtMouse:
4395 if (!mouse_frame (where, ignored)) {
4396 /* XXX not right but what can we do ? */
4397 return 0;
4399 snap_to (where);
4400 break;
4403 return where;
4406 void
4407 Editor::set_loop_range (nframes64_t start, nframes64_t end, string cmd)
4409 if (!_session) return;
4411 begin_reversible_command (cmd);
4413 Location* tll;
4415 if ((tll = transport_loop_location()) == 0) {
4416 Location* loc = new Location (start, end, _("Loop"), Location::IsAutoLoop);
4417 XMLNode &before = _session->locations()->get_state();
4418 _session->locations()->add (loc, true);
4419 _session->set_auto_loop_location (loc);
4420 XMLNode &after = _session->locations()->get_state();
4421 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4422 } else {
4423 XMLNode &before = tll->get_state();
4424 tll->set_hidden (false, this);
4425 tll->set (start, end);
4426 XMLNode &after = tll->get_state();
4427 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4430 commit_reversible_command ();
4433 void
4434 Editor::set_punch_range (nframes64_t start, nframes64_t end, string cmd)
4436 if (!_session) return;
4438 begin_reversible_command (cmd);
4440 Location* tpl;
4442 if ((tpl = transport_punch_location()) == 0) {
4443 Location* loc = new Location (start, end, _("Loop"), Location::IsAutoPunch);
4444 XMLNode &before = _session->locations()->get_state();
4445 _session->locations()->add (loc, true);
4446 _session->set_auto_loop_location (loc);
4447 XMLNode &after = _session->locations()->get_state();
4448 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4450 else {
4451 XMLNode &before = tpl->get_state();
4452 tpl->set_hidden (false, this);
4453 tpl->set (start, end);
4454 XMLNode &after = tpl->get_state();
4455 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4458 commit_reversible_command ();
4461 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4462 * @param rs List to which found regions are added.
4463 * @param where Time to look at.
4464 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4466 void
4467 Editor::get_regions_at (RegionSelection& rs, nframes64_t where, const TrackViewList& ts) const
4469 const TrackViewList* tracks;
4471 if (ts.empty()) {
4472 tracks = &track_views;
4473 } else {
4474 tracks = &ts;
4477 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4478 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4479 if (rtv) {
4480 boost::shared_ptr<Diskstream> ds;
4481 boost::shared_ptr<Playlist> pl;
4483 if ((ds = rtv->get_diskstream()) && ((pl = ds->playlist()))) {
4485 Playlist::RegionList* regions = pl->regions_at (
4486 (nframes64_t) floor ( (double)where * ds->speed()));
4488 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4489 RegionView* rv = rtv->view()->find_view (*i);
4490 if (rv) {
4491 rs.add (rv);
4495 delete regions;
4501 void
4502 Editor::get_regions_after (RegionSelection& rs, nframes64_t where, const TrackViewList& ts) const
4504 const TrackViewList* tracks;
4506 if (ts.empty()) {
4507 tracks = &track_views;
4508 } else {
4509 tracks = &ts;
4512 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4513 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4514 if (rtv) {
4515 boost::shared_ptr<Diskstream> ds;
4516 boost::shared_ptr<Playlist> pl;
4518 if ((ds = rtv->get_diskstream()) && ((pl = ds->playlist()))) {
4520 Playlist::RegionList* regions = pl->regions_touched (
4521 (nframes64_t) floor ( (double)where * ds->speed()), max_frames);
4523 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4525 RegionView* rv = rtv->view()->find_view (*i);
4527 if (rv) {
4528 rs.push_back (rv);
4532 delete regions;
4538 /** Find all regions which are either:
4539 * - selected or
4540 * - the entered_regionview (if allow_entered == true) or
4541 * - under the preferred edit position AND on a selected track, or on a track
4542 * which is in the same active edit-enable route group as a selected region (if allow_edit_position == true)
4543 * @param rs Returned region list.
4544 * @param allow_entered true to include the entered_regionview in the list.
4546 void
4547 Editor::get_regions_for_action (RegionSelection& rs, bool allow_entered, bool allow_edit_position)
4549 /* Start with selected regions */
4550 rs = selection->regions;
4552 /* Add the entered_regionview, if requested */
4553 if (allow_entered && entered_regionview) {
4554 rs.add (entered_regionview);
4557 if (allow_edit_position) {
4559 TrackViewList tracks = selection->tracks;
4561 /* tracks is currently the set of selected tracks; add any other tracks that
4562 * have regions that are in the same edit-activated route group as one of
4563 * our regions */
4564 for (RegionSelection::iterator i = rs.begin (); i != rs.end(); ++i) {
4566 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4567 if (g && g->is_active() && g->is_edit()) {
4568 tracks.add (axis_views_from_routes (g->route_list()));
4573 if (!tracks.empty()) {
4574 /* now find regions that are at the edit position on those tracks */
4575 nframes64_t const where = get_preferred_edit_position ();
4576 get_regions_at (rs, where, tracks);
4581 void
4582 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4584 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4586 RouteTimeAxisView* tatv;
4588 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4590 boost::shared_ptr<Playlist> pl;
4591 vector<boost::shared_ptr<Region> > results;
4592 RegionView* marv;
4593 boost::shared_ptr<Diskstream> ds;
4595 if ((ds = tatv->get_diskstream()) == 0) {
4596 /* bus */
4597 continue;
4600 if ((pl = (ds->playlist())) != 0) {
4601 pl->get_region_list_equivalent_regions (region, results);
4604 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4605 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4606 regions.push_back (marv);
4614 void
4615 Editor::show_rhythm_ferret ()
4617 if (rhythm_ferret == 0) {
4618 rhythm_ferret = new RhythmFerret(*this);
4621 rhythm_ferret->set_session (_session);
4622 rhythm_ferret->show ();
4623 rhythm_ferret->present ();
4626 void
4627 Editor::show_global_port_matrix (ARDOUR::DataType t)
4629 if (_global_port_matrix[t] == 0) {
4630 _global_port_matrix[t] = new GlobalPortMatrixWindow (_session, t);
4633 _global_port_matrix[t]->show ();
4636 void
4637 Editor::first_idle ()
4639 MessageDialog* dialog = 0;
4641 if (track_views.size() > 1) {
4642 dialog = new MessageDialog (*this,
4643 _("Please wait while Ardour loads visual data"),
4644 true,
4645 Gtk::MESSAGE_INFO,
4646 Gtk::BUTTONS_NONE);
4647 dialog->present ();
4648 ARDOUR_UI::instance()->flush_pending ();
4651 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4652 (*t)->first_idle();
4655 // first idle adds route children (automation tracks), so we need to redisplay here
4656 _routes->redisplay ();
4658 delete dialog;
4660 _have_idled = true;
4663 static gboolean
4664 _idle_resizer (gpointer arg)
4666 return ((Editor*)arg)->idle_resize ();
4669 void
4670 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4672 if (resize_idle_id < 0) {
4673 resize_idle_id = g_idle_add (_idle_resizer, this);
4674 _pending_resize_amount = 0;
4677 /* make a note of the smallest resulting height, so that we can clamp the
4678 lower limit at TimeAxisView::hSmall */
4680 int32_t min_resulting = INT32_MAX;
4682 _pending_resize_amount += h;
4683 _pending_resize_view = view;
4685 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4687 if (selection->tracks.contains (_pending_resize_view)) {
4688 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4689 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4693 if (min_resulting < 0) {
4694 min_resulting = 0;
4697 /* clamp */
4698 if (uint32_t (min_resulting) < TimeAxisView::hSmall) {
4699 _pending_resize_amount += TimeAxisView::hSmall - min_resulting;
4703 /** Handle pending resizing of tracks */
4704 bool
4705 Editor::idle_resize ()
4707 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4709 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4710 selection->tracks.contains (_pending_resize_view)) {
4712 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4713 if (*i != _pending_resize_view) {
4714 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4719 flush_canvas ();
4720 _group_tabs->set_dirty ();
4721 resize_idle_id = -1;
4723 return false;
4726 void
4727 Editor::located ()
4729 ENSURE_GUI_THREAD (*this, &Editor::located);
4731 playhead_cursor->set_position (_session->audible_frame ());
4732 if (_follow_playhead && !_pending_initial_locate) {
4733 reset_x_origin_to_follow_playhead ();
4736 _pending_locate_request = false;
4737 _pending_initial_locate = false;
4740 void
4741 Editor::region_view_added (RegionView *)
4743 _summary->set_dirty ();
4746 void
4747 Editor::streamview_height_changed ()
4749 _summary->set_dirty ();
4752 TimeAxisView*
4753 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4755 TrackViewList::const_iterator j = track_views.begin ();
4756 while (j != track_views.end()) {
4757 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4758 if (rtv && rtv->route() == r) {
4759 return rtv;
4761 ++j;
4764 return 0;
4768 TrackViewList
4769 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4771 TrackViewList t;
4773 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4774 TimeAxisView* tv = axis_view_from_route (*i);
4775 if (tv) {
4776 t.push_back (tv);
4780 return t;
4784 void
4785 Editor::handle_new_route (RouteList& routes)
4787 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4789 RouteTimeAxisView *rtv;
4790 list<RouteTimeAxisView*> new_views;
4792 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4793 boost::shared_ptr<Route> route = (*x);
4795 if (route->is_hidden() || route->is_control()) {
4796 continue;
4799 DataType dt = route->input()->default_type();
4801 if (dt == ARDOUR::DataType::AUDIO) {
4802 rtv = new AudioTimeAxisView (*this, _session, route, *track_canvas);
4803 } else if (dt == ARDOUR::DataType::MIDI) {
4804 rtv = new MidiTimeAxisView (*this, _session, route, *track_canvas);
4805 } else {
4806 throw unknown_type();
4809 new_views.push_back (rtv);
4810 track_views.push_back (rtv);
4812 rtv->effective_gain_display ();
4814 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4815 rtv->view()->HeightChanged.connect (sigc::mem_fun (*this, &Editor::streamview_height_changed));
4818 _routes->routes_added (new_views);
4820 if (show_editor_mixer_when_tracks_arrive) {
4821 show_editor_mixer (true);
4824 editor_list_button.set_sensitive (true);
4826 _summary->set_dirty ();
4829 void
4830 Editor::timeaxisview_deleted (TimeAxisView *tv)
4832 if (_session && _session->deletion_in_progress()) {
4833 /* the situation is under control */
4834 return;
4837 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
4840 _routes->route_removed (tv);
4842 if (tv == entered_track) {
4843 entered_track = 0;
4846 /* remove it from the list of track views */
4848 TrackViewList::iterator i;
4850 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
4851 i = track_views.erase (i);
4854 /* update whatever the current mixer strip is displaying, if revelant */
4856 boost::shared_ptr<Route> route;
4857 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
4859 if (rtav) {
4860 route = rtav->route ();
4863 if (current_mixer_strip && current_mixer_strip->route() == route) {
4865 TimeAxisView* next_tv;
4867 if (track_views.empty()) {
4868 next_tv = 0;
4869 } else if (i == track_views.end()) {
4870 next_tv = track_views.front();
4871 } else {
4872 next_tv = (*i);
4876 if (next_tv) {
4877 set_selected_mixer_strip (*next_tv);
4878 } else {
4879 /* make the editor mixer strip go away setting the
4880 * button to inactive (which also unticks the menu option)
4883 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
4888 void
4889 Editor::hide_track_in_display (TimeAxisView& tv, bool /*temponly*/)
4891 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (&tv);
4893 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
4894 // this will hide the mixer strip
4895 set_selected_mixer_strip (tv);
4898 _routes->hide_track_in_display (tv);
4901 bool
4902 Editor::sync_track_view_list_and_routes ()
4904 track_views = TrackViewList (_routes->views ());
4906 _summary->set_dirty ();
4907 _group_tabs->set_dirty ();
4909 return false; // do not call again (until needed)
4912 void
4913 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
4915 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4916 theslot (**i);
4920 RouteTimeAxisView*
4921 Editor::get_route_view_by_id (PBD::ID& id)
4923 RouteTimeAxisView* v;
4925 for(TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4926 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
4927 if(v->route()->id() == id) {
4928 return v;
4933 return 0;
4936 void
4937 Editor::fit_route_group (RouteGroup *g)
4939 TrackViewList ts = axis_views_from_routes (g->route_list ());
4940 fit_tracks (ts);
4943 void
4944 Editor::consider_auditioning (boost::shared_ptr<Region> region)
4946 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
4948 if (r == 0) {
4949 _session->cancel_audition ();
4950 return;
4953 if (_session->is_auditioning()) {
4954 _session->cancel_audition ();
4955 if (r == last_audition_region) {
4956 return;
4960 _session->audition_region (r);
4961 last_audition_region = r;
4965 void
4966 Editor::hide_a_region (boost::shared_ptr<Region> r)
4968 r->set_hidden (true);
4971 void
4972 Editor::remove_a_region (boost::shared_ptr<Region> r)
4974 // _session->remove_region_from_region_list (r);
4977 void
4978 Editor::audition_region_from_region_list ()
4980 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
4983 void
4984 Editor::hide_region_from_region_list ()
4986 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
4989 void
4990 Editor::start_step_editing ()
4992 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
4995 void
4996 Editor::stop_step_editing ()
4998 step_edit_connection.disconnect ();
5001 bool
5002 Editor::check_step_edit ()
5004 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5005 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5006 if (mtv) {
5007 mtv->check_step_edit ();
5011 return true; // do it again, till we stop
5014 void
5015 Editor::horizontal_scroll_left ()
5017 double x = leftmost_position() - current_page_frames() / 5;
5018 if (x < 0) {
5019 x = 0;
5022 reset_x_origin (x);
5025 void
5026 Editor::horizontal_scroll_right ()
5028 reset_x_origin (leftmost_position() + current_page_frames() / 5);
5031 /** Queue a change for the Editor viewport x origin to follow the playhead */
5032 void
5033 Editor::reset_x_origin_to_follow_playhead ()
5035 nframes64_t const frame = playhead_cursor->current_frame;
5037 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5039 if (_session->transport_speed() < 0) {
5041 if (frame > (current_page_frames() / 2)) {
5042 center_screen (frame-(current_page_frames()/2));
5043 } else {
5044 center_screen (current_page_frames()/2);
5047 } else {
5049 if (frame < leftmost_frame) {
5050 /* moving left */
5051 nframes64_t l = 0;
5052 if (_session->transport_rolling()) {
5053 /* rolling; end up with the playhead at the right of the page */
5054 l = frame - current_page_frames ();
5055 } else {
5056 /* not rolling: end up with the playhead 3/4 of the way along the page */
5057 l = frame - (3 * current_page_frames() / 4);
5060 if (l < 0) {
5061 l = 0;
5064 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5065 } else {
5066 /* moving right */
5067 if (_session->transport_rolling()) {
5068 /* rolling: end up with the playhead on the left of the page */
5069 center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5070 } else {
5071 /* not rolling: end up with the playhead 1/4 of the way along the page */
5072 center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5079 void
5080 Editor::super_rapid_screen_update ()
5082 if (!_session || !_session->engine().running()) {
5083 return;
5086 /* METERING / MIXER STRIPS */
5088 /* update track meters, if required */
5089 if (is_mapped() && meters_running) {
5090 RouteTimeAxisView* rtv;
5091 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5092 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5093 rtv->fast_update ();
5098 /* and any current mixer strip */
5099 if (current_mixer_strip) {
5100 current_mixer_strip->fast_update ();
5103 /* PLAYHEAD AND VIEWPORT */
5105 nframes64_t const frame = _session->audible_frame();
5107 /* There are a few reasons why we might not update the playhead / viewport stuff:
5109 * 1. we don't update things when there's a pending locate request, otherwise
5110 * when the editor requests a locate there is a chance that this method
5111 * will move the playhead before the locate request is processed, causing
5112 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5113 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5116 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5118 last_update_frame = frame;
5120 if (!_dragging_playhead) {
5121 playhead_cursor->set_position (frame);
5124 #undef CONTINUOUS_SCROLL
5125 #ifndef CONTINUOUS_SCROLL
5127 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5128 reset_x_origin_to_follow_playhead ();
5131 #else // CONTINUOUS_SCROLL
5133 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5134 editor canvas
5137 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5138 if (target <= 0.0) {
5139 target = 0.0;
5141 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5142 target = (target * 0.15) + (current * 0.85);
5143 } else {
5144 /* relax */
5147 current = target;
5148 horizontal_adjustment.set_value (current);
5150 #endif // CONTINUOUS_SCROLL
5156 void
5157 Editor::session_going_away ()
5159 _have_idled = false;
5161 _session_connections.drop_connections ();
5163 super_rapid_screen_update_connection.disconnect ();
5165 selection->clear ();
5166 cut_buffer->clear ();
5168 clicked_regionview = 0;
5169 clicked_axisview = 0;
5170 clicked_routeview = 0;
5171 clicked_crossfadeview = 0;
5172 entered_regionview = 0;
5173 entered_track = 0;
5174 last_update_frame = 0;
5175 _drags->abort ();
5177 playhead_cursor->canvas_item.hide ();
5179 /* rip everything out of the list displays */
5181 _regions->clear ();
5182 _routes->clear ();
5183 _route_groups->clear ();
5185 /* do this first so that deleting a track doesn't reset cms to null
5186 and thus cause a leak.
5189 if (current_mixer_strip) {
5190 if (current_mixer_strip->get_parent() != 0) {
5191 global_hpacker.remove (*current_mixer_strip);
5193 delete current_mixer_strip;
5194 current_mixer_strip = 0;
5197 /* delete all trackviews */
5199 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5200 delete *i;
5202 track_views.clear ();
5204 zoom_range_clock.set_session (0);
5205 nudge_clock.set_session (0);
5207 editor_list_button.set_active(false);
5208 editor_list_button.set_sensitive(false);
5210 /* clear tempo/meter rulers */
5211 remove_metric_marks ();
5212 hide_measures ();
5213 clear_marker_display ();
5215 delete current_bbt_points;
5216 current_bbt_points = 0;
5218 /* get rid of any existing editor mixer strip */
5220 WindowTitle title(Glib::get_application_name());
5221 title += _("Editor");
5223 set_title (title.get_string());
5225 SessionHandlePtr::session_going_away ();
5229 void
5230 Editor::show_editor_list (bool yn)
5232 if (yn) {
5233 the_notebook.show();
5234 } else {
5235 the_notebook.hide();