Remove erroneous assert which I added earlier.
[ardour2.git] / gtk2_ardour / editor.cc
blob89729800112f511e0d89d098d9f11ba5e4c2a7a2
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 #include <stdint.h>
23 #include <unistd.h>
24 #include <cstdlib>
25 #include <cmath>
26 #include <string>
27 #include <algorithm>
28 #include <map>
30 #include "ardour_ui.h"
32 * ardour_ui.h include was moved to the top of the list
33 * due to a conflicting definition of 'Style' between
34 * Apple's MacTypes.h and BarController.
37 #include <boost/none.hpp>
39 #include <sigc++/bind.h>
41 #include "pbd/convert.h"
42 #include "pbd/error.h"
43 #include "pbd/enumwriter.h"
44 #include "pbd/memento_command.h"
45 #include "pbd/unknown_type.h"
47 #include <glibmm/miscutils.h>
48 #include <gtkmm/image.h>
49 #include <gdkmm/color.h>
50 #include <gdkmm/bitmap.h>
52 #include "gtkmm2ext/bindings.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 "audio_clock.h"
81 #include "editor.h"
82 #include "debug.h"
83 #include "keyboard.h"
84 #include "marker.h"
85 #include "playlist_selector.h"
86 #include "audio_region_view.h"
87 #include "rgb_macros.h"
88 #include "selection.h"
89 #include "audio_streamview.h"
90 #include "time_axis_view.h"
91 #include "audio_time_axis.h"
92 #include "utils.h"
93 #include "crossfade_view.h"
94 #include "canvas-noevent-text.h"
95 #include "editing.h"
96 #include "public_editor.h"
97 #include "crossfade_edit.h"
98 #include "canvas_impl.h"
99 #include "actions.h"
100 #include "sfdb_ui.h"
101 #include "gui_thread.h"
102 #include "simpleline.h"
103 #include "rhythm_ferret.h"
104 #include "actions.h"
105 #include "tempo_lines.h"
106 #include "analysis_window.h"
107 #include "bundle_manager.h"
108 #include "global_port_matrix.h"
109 #include "editor_drag.h"
110 #include "editor_group_tabs.h"
111 #include "automation_time_axis.h"
112 #include "editor_routes.h"
113 #include "midi_time_axis.h"
114 #include "mixer_strip.h"
115 #include "editor_route_groups.h"
116 #include "editor_regions.h"
117 #include "editor_locations.h"
118 #include "editor_snapshots.h"
119 #include "editor_summary.h"
120 #include "region_layering_order_editor.h"
121 #include "mouse_cursors.h"
122 #include "editor_cursors.h"
124 #include "i18n.h"
126 #ifdef WITH_CMT
127 #include "imageframe_socket_handler.h"
128 #endif
130 using namespace std;
131 using namespace ARDOUR;
132 using namespace PBD;
133 using namespace Gtk;
134 using namespace Glib;
135 using namespace Gtkmm2ext;
136 using namespace Editing;
138 using PBD::internationalize;
139 using PBD::atoi;
140 using Gtkmm2ext::Keyboard;
142 const double Editor::timebar_height = 15.0;
144 static const gchar *_snap_type_strings[] = {
145 N_("CD Frames"),
146 N_("Timecode Frames"),
147 N_("Timecode Seconds"),
148 N_("Timecode Minutes"),
149 N_("Seconds"),
150 N_("Minutes"),
151 N_("Beats/32"),
152 N_("Beats/28"),
153 N_("Beats/24"),
154 N_("Beats/20"),
155 N_("Beats/16"),
156 N_("Beats/14"),
157 N_("Beats/12"),
158 N_("Beats/10"),
159 N_("Beats/8"),
160 N_("Beats/7"),
161 N_("Beats/6"),
162 N_("Beats/5"),
163 N_("Beats/4"),
164 N_("Beats/3"),
165 N_("Beats/2"),
166 N_("Beats"),
167 N_("Bars"),
168 N_("Marks"),
169 N_("Region starts"),
170 N_("Region ends"),
171 N_("Region syncs"),
172 N_("Region bounds"),
176 static const gchar *_snap_mode_strings[] = {
177 N_("No Grid"),
178 N_("Grid"),
179 N_("Magnetic"),
183 static const gchar *_edit_point_strings[] = {
184 N_("Playhead"),
185 N_("Marker"),
186 N_("Mouse"),
190 static const gchar *_zoom_focus_strings[] = {
191 N_("Left"),
192 N_("Right"),
193 N_("Center"),
194 N_("Playhead"),
195 N_("Mouse"),
196 N_("Edit point"),
200 #ifdef USE_RUBBERBAND
201 static const gchar *_rb_opt_strings[] = {
202 N_("Mushy"),
203 N_("Smooth"),
204 N_("Balanced multitimbral mixture"),
205 N_("Unpitched percussion with stable notes"),
206 N_("Crisp monophonic instrumental"),
207 N_("Unpitched solo percussion"),
208 N_("Resample without preserving pitch"),
211 #endif
213 void
214 show_me_the_size (Requisition* r, const char* what)
216 cerr << "size of " << what << " = " << r->width << " x " << r->height << endl;
219 #ifdef GTKOSX
220 static void
221 pane_size_watcher (Paned* pane)
223 /* if the handle of a pane vanishes into (at least) the tabs of a notebook,
224 it is no longer accessible. so stop that. this doesn't happen on X11,
225 just the quartz backend.
227 ugh.
230 int max_width_of_lhs = GTK_WIDGET(pane->gobj())->allocation.width - 25;
232 gint pos = pane->get_position ();
234 if (pos > max_width_of_lhs) {
235 pane->set_position (max_width_of_lhs);
238 #endif
240 Editor::Editor ()
241 : _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
243 /* time display buttons */
244 , minsec_label (_("Mins:Secs"))
245 , bbt_label (_("Bars:Beats"))
246 , timecode_label (_("Timecode"))
247 , samples_label (_("Samples"))
248 , tempo_label (_("Tempo"))
249 , meter_label (_("Meter"))
250 , mark_label (_("Location Markers"))
251 , range_mark_label (_("Range Markers"))
252 , transport_mark_label (_("Loop/Punch Ranges"))
253 , cd_mark_label (_("CD Markers"))
254 , edit_packer (4, 4, true)
256 /* the values here don't matter: layout widgets
257 reset them as needed.
260 , vertical_adjustment (0.0, 0.0, 10.0, 400.0)
262 /* tool bar related */
264 , zoom_range_clock (new AudioClock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, false, true))
266 , toolbar_selection_clock_table (2,3)
268 , automation_mode_button (_("mode"))
269 , global_automation_button (_("automation"))
271 , _toolbar_viewport (*manage (new Gtk::Adjustment (0, 0, 1e10)), *manage (new Gtk::Adjustment (0, 0, 1e10)))
272 , midi_panic_button (_("Panic"))
274 #ifdef WITH_CMT
275 , image_socket_listener(0)
276 #endif
278 /* nudge */
280 , nudge_clock (new AudioClock (X_("nudge"), false, X_("NudgeClock"), true, false, true))
281 , meters_running(false)
282 , _pending_locate_request (false)
283 , _pending_initial_locate (false)
284 , _last_cut_copy_source_track (0)
286 , _region_selection_change_updates_region_list (true)
288 constructed = false;
290 /* we are a singleton */
292 PublicEditor::_instance = this;
294 _have_idled = false;
296 selection = new Selection (this);
297 cut_buffer = new Selection (this);
299 clicked_regionview = 0;
300 clicked_axisview = 0;
301 clicked_routeview = 0;
302 clicked_crossfadeview = 0;
303 clicked_control_point = 0;
304 last_update_frame = 0;
305 pre_press_cursor = 0;
306 _drags = new DragManager (this);
307 current_mixer_strip = 0;
308 current_bbt_points = 0;
309 tempo_lines = 0;
311 snap_type_strings = I18N (_snap_type_strings);
312 snap_mode_strings = I18N (_snap_mode_strings);
313 zoom_focus_strings = I18N (_zoom_focus_strings);
314 edit_point_strings = I18N (_edit_point_strings);
315 #ifdef USE_RUBBERBAND
316 rb_opt_strings = I18N (_rb_opt_strings);
317 rb_current_opt = 4;
318 #endif
320 snap_threshold = 5.0;
321 bbt_beat_subdivision = 4;
322 _canvas_width = 0;
323 _canvas_height = 0;
324 last_autoscroll_x = 0;
325 last_autoscroll_y = 0;
326 autoscroll_active = false;
327 autoscroll_timeout_tag = -1;
328 logo_item = 0;
330 analysis_window = 0;
332 current_interthread_info = 0;
333 _show_measures = true;
334 show_gain_after_trim = false;
335 last_item_entered = 0;
337 have_pending_keyboard_selection = false;
338 _follow_playhead = true;
339 _stationary_playhead = false;
340 _xfade_visibility = true;
341 editor_ruler_menu = 0;
342 no_ruler_shown_update = false;
343 marker_menu = 0;
344 range_marker_menu = 0;
345 marker_menu_item = 0;
346 tempo_or_meter_marker_menu = 0;
347 transport_marker_menu = 0;
348 new_transport_marker_menu = 0;
349 editor_mixer_strip_width = Wide;
350 show_editor_mixer_when_tracks_arrive = false;
351 region_edit_menu_split_multichannel_item = 0;
352 region_edit_menu_split_item = 0;
353 temp_location = 0;
354 leftmost_frame = 0;
355 current_stepping_trackview = 0;
356 entered_track = 0;
357 entered_regionview = 0;
358 entered_marker = 0;
359 clear_entered_track = false;
360 current_timefx = 0;
361 playhead_cursor = 0;
362 button_release_can_deselect = true;
363 _dragging_playhead = false;
364 _dragging_edit_point = false;
365 select_new_marker = false;
366 rhythm_ferret = 0;
367 layering_order_editor = 0;
368 no_save_visual = false;
369 resize_idle_id = -1;
371 scrubbing_direction = 0;
373 sfbrowser = 0;
375 location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get();
376 location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get();
377 location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get();
378 location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get();
379 location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get();
381 _edit_point = EditAtMouse;
382 _internal_editing = false;
383 current_canvas_cursor = 0;
385 frames_per_unit = 2048; /* too early to use reset_zoom () */
387 _scroll_callbacks = 0;
389 zoom_focus = ZoomFocusLeft;
390 set_zoom_focus (ZoomFocusLeft);
391 zoom_range_clock->ValueChanged.connect (sigc::mem_fun(*this, &Editor::zoom_adjustment_changed));
393 bbt_label.set_name ("EditorTimeButton");
394 bbt_label.set_size_request (-1, (int)timebar_height);
395 bbt_label.set_alignment (1.0, 0.5);
396 bbt_label.set_padding (5,0);
397 bbt_label.hide ();
398 bbt_label.set_no_show_all();
399 minsec_label.set_name ("EditorTimeButton");
400 minsec_label.set_size_request (-1, (int)timebar_height);
401 minsec_label.set_alignment (1.0, 0.5);
402 minsec_label.set_padding (5,0);
403 minsec_label.hide ();
404 minsec_label.set_no_show_all();
405 timecode_label.set_name ("EditorTimeButton");
406 timecode_label.set_size_request (-1, (int)timebar_height);
407 timecode_label.set_alignment (1.0, 0.5);
408 timecode_label.set_padding (5,0);
409 timecode_label.hide ();
410 timecode_label.set_no_show_all();
411 samples_label.set_name ("EditorTimeButton");
412 samples_label.set_size_request (-1, (int)timebar_height);
413 samples_label.set_alignment (1.0, 0.5);
414 samples_label.set_padding (5,0);
415 samples_label.hide ();
416 samples_label.set_no_show_all();
418 tempo_label.set_name ("EditorTimeButton");
419 tempo_label.set_size_request (-1, (int)timebar_height);
420 tempo_label.set_alignment (1.0, 0.5);
421 tempo_label.set_padding (5,0);
422 tempo_label.hide();
423 tempo_label.set_no_show_all();
425 meter_label.set_name ("EditorTimeButton");
426 meter_label.set_size_request (-1, (int)timebar_height);
427 meter_label.set_alignment (1.0, 0.5);
428 meter_label.set_padding (5,0);
429 meter_label.hide();
430 meter_label.set_no_show_all();
432 mark_label.set_name ("EditorTimeButton");
433 mark_label.set_size_request (-1, (int)timebar_height);
434 mark_label.set_alignment (1.0, 0.5);
435 mark_label.set_padding (5,0);
436 mark_label.hide();
437 mark_label.set_no_show_all();
439 cd_mark_label.set_name ("EditorTimeButton");
440 cd_mark_label.set_size_request (-1, (int)timebar_height);
441 cd_mark_label.set_alignment (1.0, 0.5);
442 cd_mark_label.set_padding (5,0);
443 cd_mark_label.hide();
444 cd_mark_label.set_no_show_all();
446 range_mark_label.set_name ("EditorTimeButton");
447 range_mark_label.set_size_request (-1, (int)timebar_height);
448 range_mark_label.set_alignment (1.0, 0.5);
449 range_mark_label.set_padding (5,0);
450 range_mark_label.hide();
451 range_mark_label.set_no_show_all();
453 transport_mark_label.set_name ("EditorTimeButton");
454 transport_mark_label.set_size_request (-1, (int)timebar_height);
455 transport_mark_label.set_alignment (1.0, 0.5);
456 transport_mark_label.set_padding (5,0);
457 transport_mark_label.hide();
458 transport_mark_label.set_no_show_all();
460 initialize_rulers ();
461 initialize_canvas ();
463 _summary = new EditorSummary (this);
465 selection->TimeChanged.connect (sigc::mem_fun(*this, &Editor::time_selection_changed));
466 selection->TracksChanged.connect (sigc::mem_fun(*this, &Editor::track_selection_changed));
468 editor_regions_selection_changed_connection = selection->RegionsChanged.connect (sigc::mem_fun(*this, &Editor::region_selection_changed));
470 selection->PointsChanged.connect (sigc::mem_fun(*this, &Editor::point_selection_changed));
471 selection->MarkersChanged.connect (sigc::mem_fun(*this, &Editor::marker_selection_changed));
473 edit_controls_vbox.set_spacing (0);
474 vertical_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &Editor::tie_vertical_scrolling), true);
475 track_canvas->signal_map_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_map_handler));
477 HBox* h = manage (new HBox);
478 _group_tabs = new EditorGroupTabs (this);
479 h->pack_start (*_group_tabs, PACK_SHRINK);
480 h->pack_start (edit_controls_vbox);
481 controls_layout.add (*h);
483 controls_layout.set_name ("EditControlsBase");
484 controls_layout.add_events (Gdk::SCROLL_MASK);
485 controls_layout.signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::control_layout_scroll), false);
487 controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
488 controls_layout.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_release));
490 _cursors = new MouseCursors;
492 ArdourCanvas::Canvas* time_pad = manage(new ArdourCanvas::Canvas());
493 ArdourCanvas::SimpleLine* pad_line_1 = manage(new ArdourCanvas::SimpleLine(*time_pad->root(),
494 0.0, 1.0, 100.0, 1.0));
496 pad_line_1->property_color_rgba() = 0xFF0000FF;
497 pad_line_1->show();
499 time_pad->show();
501 time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2);
502 time_canvas_vbox.set_size_request (-1, -1);
504 ruler_label_event_box.add (ruler_label_vbox);
505 ruler_label_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
506 ruler_label_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
508 time_button_event_box.add (time_button_vbox);
509 time_button_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
510 time_button_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
512 /* these enable us to have a dedicated window (for cursor setting, etc.)
513 for the canvas areas.
516 track_canvas_event_box.add (*track_canvas);
518 time_canvas_event_box.add (time_canvas_vbox);
519 time_canvas_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
521 edit_packer.set_col_spacings (0);
522 edit_packer.set_row_spacings (0);
523 edit_packer.set_homogeneous (false);
524 edit_packer.set_border_width (0);
525 edit_packer.set_name ("EditorWindow");
527 /* labels for the rulers */
528 edit_packer.attach (ruler_label_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0);
529 /* labels for the marker "tracks" */
530 edit_packer.attach (time_button_event_box, 1, 2, 1, 2, FILL, SHRINK, 0, 0);
531 /* the rulers */
532 edit_packer.attach (time_canvas_event_box, 2, 3, 0, 1, FILL|EXPAND, FILL, 0, 0);
533 /* track controls */
534 edit_packer.attach (controls_layout, 0, 2, 2, 3, FILL, FILL|EXPAND, 0, 0);
535 /* main canvas */
536 edit_packer.attach (track_canvas_event_box, 2, 3, 1, 3, FILL|EXPAND, FILL|EXPAND, 0, 0);
538 bottom_hbox.set_border_width (2);
539 bottom_hbox.set_spacing (3);
541 _route_groups = new EditorRouteGroups (this);
542 _routes = new EditorRoutes (this);
543 _regions = new EditorRegions (this);
544 _snapshots = new EditorSnapshots (this);
545 _locations = new EditorLocations (this);
547 add_notebook_page (_("Regions"), _regions->widget ());
548 add_notebook_page (_("Tracks & Busses"), _routes->widget ());
549 add_notebook_page (_("Snapshots"), _snapshots->widget ());
550 add_notebook_page (_("Route Groups"), _route_groups->widget ());
551 add_notebook_page (_("Ranges & Marks"), _locations->widget ());
553 _the_notebook.set_show_tabs (true);
554 _the_notebook.set_scrollable (true);
555 _the_notebook.popup_disable ();
556 _the_notebook.set_tab_pos (Gtk::POS_RIGHT);
557 _the_notebook.show_all ();
559 post_maximal_editor_width = 0;
560 post_maximal_horizontal_pane_position = 0;
561 post_maximal_editor_height = 0;
562 post_maximal_vertical_pane_position = 0;
563 _notebook_shrunk = false;
565 editor_summary_pane.pack1(edit_packer);
567 Button* summary_arrows_left_left = manage (new Button);
568 summary_arrows_left_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
569 summary_arrows_left_left->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), LEFT)));
570 summary_arrows_left_left->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
572 Button* summary_arrows_left_right = manage (new Button);
573 summary_arrows_left_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
574 summary_arrows_left_right->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), RIGHT)));
575 summary_arrows_left_right->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
577 VBox* summary_arrows_left = manage (new VBox);
578 summary_arrows_left->pack_start (*summary_arrows_left_left);
579 summary_arrows_left->pack_start (*summary_arrows_left_right);
581 Button* summary_arrows_right_up = manage (new Button);
582 summary_arrows_right_up->add (*manage (new Arrow (ARROW_UP, SHADOW_NONE)));
583 summary_arrows_right_up->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), UP)));
584 summary_arrows_right_up->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
586 Button* summary_arrows_right_down = manage (new Button);
587 summary_arrows_right_down->add (*manage (new Arrow (ARROW_DOWN, SHADOW_NONE)));
588 summary_arrows_right_down->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), DOWN)));
589 summary_arrows_right_down->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
591 VBox* summary_arrows_right = manage (new VBox);
592 summary_arrows_right->pack_start (*summary_arrows_right_up);
593 summary_arrows_right->pack_start (*summary_arrows_right_down);
595 Frame* summary_frame = manage (new Frame);
596 summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN);
598 summary_frame->add (*_summary);
599 summary_frame->show ();
601 _summary_hbox.pack_start (*summary_arrows_left, false, false);
602 _summary_hbox.pack_start (*summary_frame, true, true);
603 _summary_hbox.pack_start (*summary_arrows_right, false, false);
605 editor_summary_pane.pack2 (_summary_hbox);
607 edit_pane.pack1 (editor_summary_pane, true, true);
608 edit_pane.pack2 (_the_notebook, false, true);
610 editor_summary_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun (*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&editor_summary_pane)));
612 /* XXX: editor_summary_pane might need similar special OS X treatment to the edit_pane */
614 edit_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&edit_pane)));
615 #ifdef GTKOSX
616 Glib::PropertyProxy<int> proxy = edit_pane.property_position();
617 proxy.signal_changed().connect (bind (sigc::ptr_fun (pane_size_watcher), static_cast<Paned*> (&edit_pane)));
618 #endif
619 top_hbox.pack_start (toolbar_frame);
621 HBox *hbox = manage (new HBox);
622 hbox->pack_start (edit_pane, true, true);
624 global_vpacker.pack_start (top_hbox, false, false);
625 global_vpacker.pack_start (*hbox, true, true);
627 global_hpacker.pack_start (global_vpacker, true, true);
629 set_name ("EditorWindow");
630 add_accel_group (ActionManager::ui_manager->get_accel_group());
632 status_bar_hpacker.show ();
634 vpacker.pack_end (status_bar_hpacker, false, false);
635 vpacker.pack_end (global_hpacker, true, true);
637 /* register actions now so that set_state() can find them and set toggles/checks etc */
639 register_actions ();
641 setup_toolbar ();
642 setup_midi_toolbar ();
644 _snap_type = SnapToBeat;
645 set_snap_to (_snap_type);
646 _snap_mode = SnapOff;
647 set_snap_mode (_snap_mode);
648 set_mouse_mode (MouseObject, true);
649 pre_internal_mouse_mode = MouseObject;
650 set_edit_point_preference (EditAtMouse, true);
652 _playlist_selector = new PlaylistSelector();
653 _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
655 RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), ui_bind (&Editor::catch_vanishing_regionview, this, _1), gui_context());
657 /* nudge stuff */
659 nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
660 nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
662 nudge_forward_button.set_name ("TransportButton");
663 nudge_backward_button.set_name ("TransportButton");
665 fade_context_menu.set_name ("ArdourContextMenu");
667 /* icons, titles, WM stuff */
669 list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
670 Glib::RefPtr<Gdk::Pixbuf> icon;
672 if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
673 window_icons.push_back (icon);
675 if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
676 window_icons.push_back (icon);
678 if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
679 window_icons.push_back (icon);
681 if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
682 window_icons.push_back (icon);
684 if (!window_icons.empty()) {
685 // set_icon_list (window_icons);
686 set_default_icon_list (window_icons);
689 WindowTitle title(Glib::get_application_name());
690 title += _("Editor");
691 set_title (title.get_string());
692 set_wmclass (X_("ardour_editor"), PROGRAM_NAME);
694 add (vpacker);
695 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
697 signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
698 signal_delete_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
700 /* allow external control surfaces/protocols to do various things */
702 ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context());
703 ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context());
704 ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context());
705 ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), ui_bind (&Editor::control_scroll, this, _1), gui_context());
706 BasicUI::AccessAction.connect (*this, invalidator (*this), ui_bind (&Editor::access_action, this, _1, _2), gui_context());
708 /* problematic: has to return a value and thus cannot be x-thread */
710 Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
712 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
714 TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
716 _ignore_region_action = false;
717 _last_region_menu_was_main = false;
718 _popup_region_menu_item = 0;
720 _show_marker_lines = false;
721 _over_region_trim_target = false;
723 /* Button bindings */
725 button_bindings = new Bindings;
727 XMLNode* node = button_settings();
728 if (node) {
729 for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) {
730 button_bindings->load (**i);
734 constructed = true;
735 instant_save ();
737 setup_fade_images ();
740 Editor::~Editor()
742 #ifdef WITH_CMT
743 if(image_socket_listener) {
744 if(image_socket_listener->is_connected())
746 image_socket_listener->close_connection() ;
749 delete image_socket_listener ;
750 image_socket_listener = 0 ;
752 #endif
754 delete button_bindings;
755 delete _routes;
756 delete _route_groups;
757 delete track_canvas;
758 delete _drags;
761 XMLNode*
762 Editor::button_settings () const
764 XMLNode* settings = ARDOUR_UI::instance()->editor_settings();
765 XMLNode* node = find_named_node (*settings, X_("Buttons"));
767 if (!node) {
768 cerr << "new empty Button node\n";
769 node = new XMLNode (X_("Buttons"));
772 return node;
775 void
776 Editor::add_toplevel_controls (Container& cont)
778 vpacker.pack_start (cont, false, false);
779 cont.show_all ();
782 void
783 Editor::catch_vanishing_regionview (RegionView *rv)
785 /* note: the selection will take care of the vanishing
786 audioregionview by itself.
789 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
790 _drags->abort ();
793 if (clicked_regionview == rv) {
794 clicked_regionview = 0;
797 if (entered_regionview == rv) {
798 set_entered_regionview (0);
801 if (!_all_region_actions_sensitized) {
802 sensitize_all_region_actions (true);
805 _over_region_trim_target = false;
808 void
809 Editor::set_entered_regionview (RegionView* rv)
811 if (rv == entered_regionview) {
812 return;
815 if (entered_regionview) {
816 entered_regionview->exited ();
819 if ((entered_regionview = rv) != 0) {
820 entered_regionview->entered (internal_editing ());
823 if (!_all_region_actions_sensitized && _last_region_menu_was_main) {
824 /* This RegionView entry might have changed what region actions
825 are allowed, so sensitize them all in case a key is pressed.
827 sensitize_all_region_actions (true);
831 void
832 Editor::set_entered_track (TimeAxisView* tav)
834 if (entered_track) {
835 entered_track->exited ();
838 if ((entered_track = tav) != 0) {
839 entered_track->entered ();
843 void
844 Editor::show_window ()
846 if (!is_visible ()) {
847 show_all ();
849 /* XXX: this is a bit unfortunate; it would probably
850 be nicer if we could just call show () above rather
851 than needing the show_all ()
854 /* re-hide stuff if necessary */
855 editor_list_button_toggled ();
856 parameter_changed ("show-summary");
857 parameter_changed ("show-group-tabs");
858 parameter_changed ("show-zoom-tools");
860 /* now reset all audio_time_axis heights, because widgets might need
861 to be re-hidden
864 TimeAxisView *tv;
866 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
867 tv = (static_cast<TimeAxisView*>(*i));
868 tv->reset_height ();
871 if (current_mixer_strip) {
872 current_mixer_strip->hide_things ();
876 present ();
879 void
880 Editor::instant_save ()
882 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
883 return;
886 if (_session) {
887 _session->add_instant_xml(get_state());
888 } else {
889 Config->add_instant_xml(get_state());
893 void
894 Editor::zoom_adjustment_changed ()
896 if (_session == 0) {
897 return;
900 double fpu = zoom_range_clock->current_duration() / _canvas_width;
902 if (fpu < 1.0) {
903 fpu = 1.0;
904 zoom_range_clock->set ((framepos_t) floor (fpu * _canvas_width));
905 } else if (fpu > _session->current_end_frame() / _canvas_width) {
906 fpu = _session->current_end_frame() / _canvas_width;
907 zoom_range_clock->set ((framepos_t) floor (fpu * _canvas_width));
910 temporal_zoom (fpu);
913 void
914 Editor::control_scroll (float fraction)
916 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
918 if (!_session) {
919 return;
922 double step = fraction * current_page_frames();
925 _control_scroll_target is an optional<T>
927 it acts like a pointer to an framepos_t, with
928 a operator conversion to boolean to check
929 that it has a value could possibly use
930 playhead_cursor->current_frame to store the
931 value and a boolean in the class to know
932 when it's out of date
935 if (!_control_scroll_target) {
936 _control_scroll_target = _session->transport_frame();
937 _dragging_playhead = true;
940 if ((fraction < 0.0f) && (*_control_scroll_target < (framepos_t) fabs(step))) {
941 *_control_scroll_target = 0;
942 } else if ((fraction > 0.0f) && (max_framepos - *_control_scroll_target < step)) {
943 *_control_scroll_target = max_framepos - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
944 } else {
945 *_control_scroll_target += (framepos_t) floor (step);
948 /* move visuals, we'll catch up with it later */
950 playhead_cursor->set_position (*_control_scroll_target);
951 UpdateAllTransportClocks (*_control_scroll_target);
953 if (*_control_scroll_target > (current_page_frames() / 2)) {
954 /* try to center PH in window */
955 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
956 } else {
957 reset_x_origin (0);
961 Now we do a timeout to actually bring the session to the right place
962 according to the playhead. This is to avoid reading disk buffers on every
963 call to control_scroll, which is driven by ScrollTimeline and therefore
964 probably by a control surface wheel which can generate lots of events.
966 /* cancel the existing timeout */
968 control_scroll_connection.disconnect ();
970 /* add the next timeout */
972 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
975 bool
976 Editor::deferred_control_scroll (framepos_t /*target*/)
978 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
979 // reset for next stream
980 _control_scroll_target = boost::none;
981 _dragging_playhead = false;
982 return false;
985 void
986 Editor::access_action (std::string action_group, std::string action_item)
988 if (!_session) {
989 return;
992 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
994 RefPtr<Action> act;
995 act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
997 if (act) {
998 act->activate();
1002 void
1003 Editor::on_realize ()
1005 Window::on_realize ();
1006 Realized ();
1009 void
1010 Editor::map_position_change (framepos_t frame)
1012 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, frame)
1014 if (_session == 0) {
1015 return;
1018 if (_follow_playhead) {
1019 center_screen (frame);
1022 playhead_cursor->set_position (frame);
1025 void
1026 Editor::center_screen (framepos_t frame)
1028 double page = _canvas_width * frames_per_unit;
1030 /* if we're off the page, then scroll.
1033 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
1034 center_screen_internal (frame, page);
1038 void
1039 Editor::center_screen_internal (framepos_t frame, float page)
1041 page /= 2;
1043 if (frame > page) {
1044 frame -= (framepos_t) page;
1045 } else {
1046 frame = 0;
1049 reset_x_origin (frame);
1053 void
1054 Editor::update_title ()
1056 ENSURE_GUI_THREAD (*this, &Editor::update_title)
1058 if (_session) {
1059 bool dirty = _session->dirty();
1061 string session_name;
1063 if (_session->snap_name() != _session->name()) {
1064 session_name = _session->snap_name();
1065 } else {
1066 session_name = _session->name();
1069 if (dirty) {
1070 session_name = "*" + session_name;
1073 WindowTitle title(session_name);
1074 title += Glib::get_application_name();
1075 set_title (title.get_string());
1079 void
1080 Editor::set_session (Session *t)
1082 SessionHandlePtr::set_session (t);
1084 if (!_session) {
1085 return;
1088 zoom_range_clock->set_session (_session);
1089 _playlist_selector->set_session (_session);
1090 nudge_clock->set_session (_session);
1091 _summary->set_session (_session);
1092 _group_tabs->set_session (_session);
1093 _route_groups->set_session (_session);
1094 _regions->set_session (_session);
1095 _snapshots->set_session (_session);
1096 _routes->set_session (_session);
1097 _locations->set_session (_session);
1099 if (rhythm_ferret) {
1100 rhythm_ferret->set_session (_session);
1103 if (analysis_window) {
1104 analysis_window->set_session (_session);
1107 if (sfbrowser) {
1108 sfbrowser->set_session (_session);
1111 compute_fixed_ruler_scale ();
1113 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1114 set_state (*node, Stateful::loading_state_version);
1116 /* catch up with the playhead */
1118 _session->request_locate (playhead_cursor->current_frame);
1119 _pending_initial_locate = true;
1121 update_title ();
1123 /* These signals can all be emitted by a non-GUI thread. Therefore the
1124 handlers for them must not attempt to directly interact with the GUI,
1125 but use Gtkmm2ext::UI::instance()->call_slot();
1128 _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), ui_bind(&Editor::step_edit_status_change, this, _1), gui_context());
1129 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1130 _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context());
1131 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context());
1132 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1133 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context());
1134 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1135 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
1136 _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Editor::session_state_saved, this, _1), gui_context());
1137 _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context());
1138 _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&Editor::location_gone, this, _1), gui_context());
1139 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1140 _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display, this), gui_context());
1141 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1143 if (Profile->get_sae()) {
1144 Timecode::BBT_Time bbt;
1145 bbt.bars = 0;
1146 bbt.beats = 0;
1147 bbt.ticks = 120;
1148 framepos_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1);
1149 nudge_clock->set_mode(AudioClock::BBT);
1150 nudge_clock->set (pos, true, 0, AudioClock::BBT);
1152 } else {
1153 nudge_clock->set (_session->frame_rate() * 5, true, 0, AudioClock::Timecode); // default of 5 seconds
1156 playhead_cursor->canvas_item.show ();
1158 Location* loc = _session->locations()->auto_loop_location();
1159 if (loc == 0) {
1160 loc = new Location (*_session, 0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1162 if (loc->start() == loc->end()) {
1163 loc->set_end (loc->start() + 1);
1166 _session->locations()->add (loc, false);
1167 _session->set_auto_loop_location (loc);
1168 } else {
1169 // force name
1170 loc->set_name (_("Loop"));
1173 loc = _session->locations()->auto_punch_location();
1175 if (loc == 0) {
1176 loc = new Location (*_session, 0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1178 if (loc->start() == loc->end()) {
1179 loc->set_end (loc->start() + 1);
1182 _session->locations()->add (loc, false);
1183 _session->set_auto_punch_location (loc);
1184 } else {
1185 // force name
1186 loc->set_name (_("Punch"));
1189 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1190 Config->map_parameters (pc);
1191 _session->config.map_parameters (pc);
1193 refresh_location_display ();
1195 restore_ruler_visibility ();
1196 //tempo_map_changed (PropertyChange (0));
1197 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1199 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1200 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1203 super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
1204 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1207 switch (_snap_type) {
1208 case SnapToRegionStart:
1209 case SnapToRegionEnd:
1210 case SnapToRegionSync:
1211 case SnapToRegionBoundary:
1212 build_region_boundary_cache ();
1213 break;
1215 default:
1216 break;
1219 /* register for undo history */
1220 _session->register_with_memento_command_factory(_id, this);
1222 ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated));
1224 start_updating_meters ();
1227 void
1228 Editor::action_pre_activated (Glib::RefPtr<Action> const & a)
1230 if (a->get_name() == "RegionMenu") {
1231 /* When the main menu's region menu is opened, we setup the actions so that they look right
1232 in the menu. I can't find a way of getting a signal when this menu is subsequently closed,
1233 so we resensitize all region actions when the entered regionview or the region selection
1234 changes. HOWEVER we can't always resensitize on entered_regionview change because that
1235 happens after the region context menu is opened. So we set a flag here, too.
1237 What a carry on :(
1239 sensitize_the_right_region_actions ();
1240 _last_region_menu_was_main = true;
1244 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1245 void
1246 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1248 using namespace Menu_Helpers;
1249 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1251 if (arv == 0) {
1252 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1253 /*NOTREACHED*/
1256 MenuList& items (fade_context_menu.items());
1258 items.clear ();
1260 switch (item_type) {
1261 case FadeInItem:
1262 case FadeInHandleItem:
1263 if (arv->audio_region()->fade_in_active()) {
1264 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1265 } else {
1266 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1269 items.push_back (SeparatorElem());
1271 if (Profile->get_sae()) {
1273 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)));
1274 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)));
1276 } else {
1278 items.push_back (
1279 ImageMenuElem (
1280 _("Linear"),
1281 *_fade_in_images[FadeLinear],
1282 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)
1286 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1288 items.push_back (
1289 ImageMenuElem (
1290 _("Slowest"),
1291 *_fade_in_images[FadeFast],
1292 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)
1295 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1297 items.push_back (
1298 ImageMenuElem (
1299 _("Slow"),
1300 *_fade_in_images[FadeLogB],
1301 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogB)
1304 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1306 items.push_back (
1307 ImageMenuElem (
1308 _("Fast"),
1309 *_fade_in_images[FadeLogA],
1310 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogA)
1313 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1315 items.push_back (
1316 ImageMenuElem (
1317 _("Fastest"),
1318 *_fade_in_images[FadeSlow],
1319 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow)
1322 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1325 break;
1327 case FadeOutItem:
1328 case FadeOutHandleItem:
1329 if (arv->audio_region()->fade_out_active()) {
1330 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1331 } else {
1332 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1335 items.push_back (SeparatorElem());
1337 if (Profile->get_sae()) {
1338 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)));
1339 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)));
1340 } else {
1342 items.push_back (
1343 ImageMenuElem (
1344 _("Linear"),
1345 *_fade_out_images[FadeLinear],
1346 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)
1350 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1352 items.push_back (
1353 ImageMenuElem (
1354 _("Slowest"),
1355 *_fade_out_images[FadeFast],
1356 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)
1359 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1361 items.push_back (
1362 ImageMenuElem (
1363 _("Slow"),
1364 *_fade_out_images[FadeLogB],
1365 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogA)
1368 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1370 items.push_back (
1371 ImageMenuElem (
1372 _("Fast"),
1373 *_fade_out_images[FadeLogA],
1374 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogB)
1377 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1379 items.push_back (
1380 ImageMenuElem (
1381 _("Fastest"),
1382 *_fade_out_images[FadeSlow],
1383 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast)
1386 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1389 break;
1391 default:
1392 fatal << _("programming error: ")
1393 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1394 << endmsg;
1395 /*NOTREACHED*/
1398 fade_context_menu.popup (button, time);
1401 void
1402 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1404 using namespace Menu_Helpers;
1405 Menu* (Editor::*build_menu_function)();
1406 Menu *menu;
1408 switch (item_type) {
1409 case RegionItem:
1410 case RegionViewName:
1411 case RegionViewNameHighlight:
1412 case LeftFrameHandle:
1413 case RightFrameHandle:
1414 if (with_selection) {
1415 build_menu_function = &Editor::build_track_selection_context_menu;
1416 } else {
1417 build_menu_function = &Editor::build_track_region_context_menu;
1419 break;
1421 case SelectionItem:
1422 if (with_selection) {
1423 build_menu_function = &Editor::build_track_selection_context_menu;
1424 } else {
1425 build_menu_function = &Editor::build_track_context_menu;
1427 break;
1429 case CrossfadeViewItem:
1430 build_menu_function = &Editor::build_track_crossfade_context_menu;
1431 break;
1433 case StreamItem:
1434 if (clicked_routeview->track()) {
1435 build_menu_function = &Editor::build_track_context_menu;
1436 } else {
1437 build_menu_function = &Editor::build_track_bus_context_menu;
1439 break;
1441 default:
1442 /* probably shouldn't happen but if it does, we don't care */
1443 return;
1446 menu = (this->*build_menu_function)();
1447 menu->set_name ("ArdourContextMenu");
1449 /* now handle specific situations */
1451 switch (item_type) {
1452 case RegionItem:
1453 case RegionViewName:
1454 case RegionViewNameHighlight:
1455 case LeftFrameHandle:
1456 case RightFrameHandle:
1457 if (!with_selection) {
1458 if (region_edit_menu_split_item) {
1459 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1460 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1461 } else {
1462 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1465 if (region_edit_menu_split_multichannel_item) {
1466 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1467 region_edit_menu_split_multichannel_item->set_sensitive (true);
1468 } else {
1469 region_edit_menu_split_multichannel_item->set_sensitive (false);
1473 break;
1475 case SelectionItem:
1476 break;
1478 case CrossfadeViewItem:
1479 break;
1481 case StreamItem:
1482 break;
1484 default:
1485 /* probably shouldn't happen but if it does, we don't care */
1486 return;
1489 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1491 /* Bounce to disk */
1493 using namespace Menu_Helpers;
1494 MenuList& edit_items = menu->items();
1496 edit_items.push_back (SeparatorElem());
1498 switch (clicked_routeview->audio_track()->freeze_state()) {
1499 case AudioTrack::NoFreeze:
1500 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1501 break;
1503 case AudioTrack::Frozen:
1504 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1505 break;
1507 case AudioTrack::UnFrozen:
1508 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1509 break;
1514 if (item_type == StreamItem && clicked_routeview) {
1515 clicked_routeview->build_underlay_menu(menu);
1518 /* When the region menu is opened, we setup the actions so that they look right
1519 in the menu.
1521 sensitize_the_right_region_actions ();
1522 _last_region_menu_was_main = false;
1524 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1525 menu->popup (button, time);
1528 Menu*
1529 Editor::build_track_context_menu ()
1531 using namespace Menu_Helpers;
1533 MenuList& edit_items = track_context_menu.items();
1534 edit_items.clear();
1536 add_dstream_context_items (edit_items);
1537 return &track_context_menu;
1540 Menu*
1541 Editor::build_track_bus_context_menu ()
1543 using namespace Menu_Helpers;
1545 MenuList& edit_items = track_context_menu.items();
1546 edit_items.clear();
1548 add_bus_context_items (edit_items);
1549 return &track_context_menu;
1552 Menu*
1553 Editor::build_track_region_context_menu ()
1555 using namespace Menu_Helpers;
1556 MenuList& edit_items = track_region_context_menu.items();
1557 edit_items.clear();
1559 /* we've just cleared the track region context menu, so the menu that these
1560 two items were on will have disappeared; stop them dangling.
1562 region_edit_menu_split_item = 0;
1563 region_edit_menu_split_multichannel_item = 0;
1565 /* we might try to use items that are currently attached to a crossfade menu,
1566 so clear that, too.
1568 track_crossfade_context_menu.items().clear ();
1570 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1572 if (rtv) {
1573 boost::shared_ptr<Track> tr;
1574 boost::shared_ptr<Playlist> pl;
1576 if ((tr = rtv->track())) {
1577 add_region_context_items (edit_items, tr);
1581 add_dstream_context_items (edit_items);
1583 return &track_region_context_menu;
1586 Menu*
1587 Editor::build_track_crossfade_context_menu ()
1589 using namespace Menu_Helpers;
1590 MenuList& edit_items = track_crossfade_context_menu.items();
1591 edit_items.clear ();
1593 /* we might try to use items that are currently attached to a crossfade menu,
1594 so clear that, too.
1596 track_region_context_menu.items().clear ();
1598 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1600 if (atv) {
1601 boost::shared_ptr<Track> tr;
1602 boost::shared_ptr<Playlist> pl;
1603 boost::shared_ptr<AudioPlaylist> apl;
1605 if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1607 AudioPlaylist::Crossfades xfades;
1608 framepos_t where;
1609 bool ignored;
1611 /* The xfade menu is a bit of a special case, as we always use the mouse position
1612 to decide whether or not to display it (rather than the edit point). No particularly
1613 strong reasons for this, other than it is a bit surprising to right-click on a xfade
1614 and not get a menu.
1616 mouse_frame (where, ignored);
1617 apl->crossfades_at (where, xfades);
1619 bool const many = xfades.size() > 1;
1621 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1622 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1625 add_region_context_items (edit_items, tr);
1629 add_dstream_context_items (edit_items);
1631 return &track_crossfade_context_menu;
1634 void
1635 Editor::analyze_region_selection ()
1637 if (analysis_window == 0) {
1638 analysis_window = new AnalysisWindow();
1640 if (_session != 0)
1641 analysis_window->set_session(_session);
1643 analysis_window->show_all();
1646 analysis_window->set_regionmode();
1647 analysis_window->analyze();
1649 analysis_window->present();
1652 void
1653 Editor::analyze_range_selection()
1655 if (analysis_window == 0) {
1656 analysis_window = new AnalysisWindow();
1658 if (_session != 0)
1659 analysis_window->set_session(_session);
1661 analysis_window->show_all();
1664 analysis_window->set_rangemode();
1665 analysis_window->analyze();
1667 analysis_window->present();
1670 Menu*
1671 Editor::build_track_selection_context_menu ()
1673 using namespace Menu_Helpers;
1674 MenuList& edit_items = track_selection_context_menu.items();
1675 edit_items.clear ();
1677 add_selection_context_items (edit_items);
1678 // edit_items.push_back (SeparatorElem());
1679 // add_dstream_context_items (edit_items);
1681 return &track_selection_context_menu;
1684 /** Add context menu items relevant to crossfades.
1685 * @param edit_items List to add the items to.
1687 void
1688 Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1690 using namespace Menu_Helpers;
1691 Menu *xfade_menu = manage (new Menu);
1692 MenuList& items = xfade_menu->items();
1693 xfade_menu->set_name ("ArdourContextMenu");
1694 string str;
1696 if (xfade->active()) {
1697 str = _("Mute");
1698 } else {
1699 str = _("Unmute");
1702 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1703 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1705 if (xfade->can_follow_overlap()) {
1707 if (xfade->following_overlap()) {
1708 str = _("Convert to Short");
1709 } else {
1710 str = _("Convert to Full");
1713 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1716 if (many) {
1717 str = xfade->out()->name();
1718 str += "->";
1719 str += xfade->in()->name();
1720 } else {
1721 str = _("Crossfade");
1724 edit_items.push_back (MenuElem (str, *xfade_menu));
1725 edit_items.push_back (SeparatorElem());
1728 void
1729 Editor::xfade_edit_left_region ()
1731 if (clicked_crossfadeview) {
1732 clicked_crossfadeview->left_view.show_region_editor ();
1736 void
1737 Editor::xfade_edit_right_region ()
1739 if (clicked_crossfadeview) {
1740 clicked_crossfadeview->right_view.show_region_editor ();
1744 void
1745 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::shared_ptr<Track> track)
1747 using namespace Menu_Helpers;
1749 /* OK, stick the region submenu at the top of the list, and then add
1750 the standard items.
1753 RegionSelection rs = get_regions_from_selection_and_entered ();
1755 string::size_type pos = 0;
1756 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1758 /* we have to hack up the region name because "_" has a special
1759 meaning for menu titles.
1762 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1763 menu_item_name.replace (pos, 1, "__");
1764 pos += 2;
1767 if (_popup_region_menu_item == 0) {
1768 _popup_region_menu_item = new MenuItem (menu_item_name);
1769 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1770 _popup_region_menu_item->show ();
1771 } else {
1772 _popup_region_menu_item->set_label (menu_item_name);
1775 /* Use the mouse position rather than the edit point to decide whether to show the `choose top region'
1776 dialogue. If we use the edit point it gets a bit messy because the user still has to click over
1777 *some* region in order to get the region context menu stuff to be displayed at all.
1780 framepos_t mouse;
1781 bool ignored;
1782 mouse_frame (mouse, ignored);
1784 edit_items.push_back (*_popup_region_menu_item);
1785 if (track->playlist()->count_regions_at (mouse) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1786 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region")->create_menu_item ()));
1788 edit_items.push_back (SeparatorElem());
1791 /** Add context menu items relevant to selection ranges.
1792 * @param edit_items List to add the items to.
1794 void
1795 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1797 using namespace Menu_Helpers;
1799 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1800 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1802 edit_items.push_back (SeparatorElem());
1803 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1805 edit_items.push_back (SeparatorElem());
1807 edit_items.push_back (
1808 MenuElem (
1809 _("Move Range Start to Previous Region Boundary"),
1810 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, false)
1814 edit_items.push_back (
1815 MenuElem (
1816 _("Move Range Start to Next Region Boundary"),
1817 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, true)
1821 edit_items.push_back (
1822 MenuElem (
1823 _("Move Range End to Previous Region Boundary"),
1824 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, false)
1828 edit_items.push_back (
1829 MenuElem (
1830 _("Move Range End to Next Region Boundary"),
1831 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, true)
1835 edit_items.push_back (SeparatorElem());
1836 edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection)));
1837 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1839 edit_items.push_back (SeparatorElem());
1840 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1842 edit_items.push_back (SeparatorElem());
1843 edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1844 edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1846 edit_items.push_back (SeparatorElem());
1847 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1849 edit_items.push_back (SeparatorElem());
1850 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1851 edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1852 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1854 edit_items.push_back (SeparatorElem());
1855 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1856 edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1857 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1858 edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1859 edit_items.push_back (MenuElem (_("Export Range"), sigc::mem_fun(*this, &Editor::export_selection)));
1863 void
1864 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1866 using namespace Menu_Helpers;
1868 /* Playback */
1870 Menu *play_menu = manage (new Menu);
1871 MenuList& play_items = play_menu->items();
1872 play_menu->set_name ("ArdourContextMenu");
1874 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1875 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1876 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1877 play_items.push_back (SeparatorElem());
1878 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
1880 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1882 /* Selection */
1884 Menu *select_menu = manage (new Menu);
1885 MenuList& select_items = select_menu->items();
1886 select_menu->set_name ("ArdourContextMenu");
1888 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1889 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1890 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1891 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1892 select_items.push_back (SeparatorElem());
1893 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
1894 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
1895 select_items.push_back (SeparatorElem());
1896 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1897 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1898 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1899 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1900 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
1901 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
1902 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
1904 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1906 /* Cut-n-Paste */
1908 Menu *cutnpaste_menu = manage (new Menu);
1909 MenuList& cutnpaste_items = cutnpaste_menu->items();
1910 cutnpaste_menu->set_name ("ArdourContextMenu");
1912 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1913 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1914 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1916 cutnpaste_items.push_back (SeparatorElem());
1918 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
1919 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
1921 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
1923 /* Adding new material */
1925 edit_items.push_back (SeparatorElem());
1926 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
1927 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
1929 /* Nudge track */
1931 Menu *nudge_menu = manage (new Menu());
1932 MenuList& nudge_items = nudge_menu->items();
1933 nudge_menu->set_name ("ArdourContextMenu");
1935 edit_items.push_back (SeparatorElem());
1936 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1937 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1938 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1939 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1941 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1944 void
1945 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
1947 using namespace Menu_Helpers;
1949 /* Playback */
1951 Menu *play_menu = manage (new Menu);
1952 MenuList& play_items = play_menu->items();
1953 play_menu->set_name ("ArdourContextMenu");
1955 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1956 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1957 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1959 /* Selection */
1961 Menu *select_menu = manage (new Menu);
1962 MenuList& select_items = select_menu->items();
1963 select_menu->set_name ("ArdourContextMenu");
1965 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1966 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1967 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1968 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1969 select_items.push_back (SeparatorElem());
1970 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1971 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1972 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1973 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1975 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1977 /* Cut-n-Paste */
1979 Menu *cutnpaste_menu = manage (new Menu);
1980 MenuList& cutnpaste_items = cutnpaste_menu->items();
1981 cutnpaste_menu->set_name ("ArdourContextMenu");
1983 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1984 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1985 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1987 Menu *nudge_menu = manage (new Menu());
1988 MenuList& nudge_items = nudge_menu->items();
1989 nudge_menu->set_name ("ArdourContextMenu");
1991 edit_items.push_back (SeparatorElem());
1992 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1993 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1994 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1995 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1997 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2000 SnapType
2001 Editor::snap_type() const
2003 return _snap_type;
2006 SnapMode
2007 Editor::snap_mode() const
2009 return _snap_mode;
2012 void
2013 Editor::set_snap_to (SnapType st)
2015 unsigned int snap_ind = (unsigned int)st;
2017 _snap_type = st;
2019 if (snap_ind > snap_type_strings.size() - 1) {
2020 snap_ind = 0;
2021 _snap_type = (SnapType)snap_ind;
2024 string str = snap_type_strings[snap_ind];
2026 if (str != snap_type_selector.get_active_text()) {
2027 snap_type_selector.set_active_text (str);
2030 instant_save ();
2032 switch (_snap_type) {
2033 case SnapToBeatDiv32:
2034 case SnapToBeatDiv28:
2035 case SnapToBeatDiv24:
2036 case SnapToBeatDiv20:
2037 case SnapToBeatDiv16:
2038 case SnapToBeatDiv14:
2039 case SnapToBeatDiv12:
2040 case SnapToBeatDiv10:
2041 case SnapToBeatDiv8:
2042 case SnapToBeatDiv7:
2043 case SnapToBeatDiv6:
2044 case SnapToBeatDiv5:
2045 case SnapToBeatDiv4:
2046 case SnapToBeatDiv3:
2047 case SnapToBeatDiv2:
2048 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
2049 update_tempo_based_rulers ();
2050 break;
2052 case SnapToRegionStart:
2053 case SnapToRegionEnd:
2054 case SnapToRegionSync:
2055 case SnapToRegionBoundary:
2056 build_region_boundary_cache ();
2057 break;
2059 default:
2060 /* relax */
2061 break;
2064 SnapChanged (); /* EMIT SIGNAL */
2067 void
2068 Editor::set_snap_mode (SnapMode mode)
2070 _snap_mode = mode;
2071 string str = snap_mode_strings[(int)mode];
2073 if (str != snap_mode_selector.get_active_text ()) {
2074 snap_mode_selector.set_active_text (str);
2077 instant_save ();
2079 void
2080 Editor::set_edit_point_preference (EditPoint ep, bool force)
2082 bool changed = (_edit_point != ep);
2084 _edit_point = ep;
2085 string str = edit_point_strings[(int)ep];
2087 if (str != edit_point_selector.get_active_text ()) {
2088 edit_point_selector.set_active_text (str);
2091 set_canvas_cursor ();
2093 if (!force && !changed) {
2094 return;
2097 const char* action=NULL;
2099 switch (_edit_point) {
2100 case EditAtPlayhead:
2101 action = "edit-at-playhead";
2102 break;
2103 case EditAtSelectedMarker:
2104 action = "edit-at-marker";
2105 break;
2106 case EditAtMouse:
2107 action = "edit-at-mouse";
2108 break;
2111 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2112 if (act) {
2113 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2116 framepos_t foo;
2117 bool in_track_canvas;
2119 if (!mouse_frame (foo, in_track_canvas)) {
2120 in_track_canvas = false;
2123 reset_canvas_action_sensitivity (in_track_canvas);
2125 instant_save ();
2129 Editor::set_state (const XMLNode& node, int /*version*/)
2131 const XMLProperty* prop;
2132 XMLNode* geometry;
2133 int x, y;
2134 Gdk::Geometry g;
2136 if ((prop = node.property ("id")) != 0) {
2137 _id = prop->value ();
2140 g.base_width = default_width;
2141 g.base_height = default_height;
2142 x = 1;
2143 y = 1;
2145 if ((geometry = find_named_node (node, "geometry")) != 0) {
2147 XMLProperty* prop;
2149 if ((prop = geometry->property("x_size")) == 0) {
2150 prop = geometry->property ("x-size");
2152 if (prop) {
2153 g.base_width = atoi(prop->value());
2155 if ((prop = geometry->property("y_size")) == 0) {
2156 prop = geometry->property ("y-size");
2158 if (prop) {
2159 g.base_height = atoi(prop->value());
2162 if ((prop = geometry->property ("x_pos")) == 0) {
2163 prop = geometry->property ("x-pos");
2165 if (prop) {
2166 x = atoi (prop->value());
2169 if ((prop = geometry->property ("y_pos")) == 0) {
2170 prop = geometry->property ("y-pos");
2172 if (prop) {
2173 y = atoi (prop->value());
2177 set_default_size (g.base_width, g.base_height);
2178 move (x, y);
2180 if (_session && (prop = node.property ("playhead"))) {
2181 framepos_t pos;
2182 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2183 playhead_cursor->set_position (pos);
2184 } else {
2185 playhead_cursor->set_position (0);
2188 if ((prop = node.property ("mixer-width"))) {
2189 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2192 if ((prop = node.property ("zoom-focus"))) {
2193 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2196 if ((prop = node.property ("zoom"))) {
2197 reset_zoom (PBD::atof (prop->value()));
2198 } else {
2199 reset_zoom (frames_per_unit);
2202 if ((prop = node.property ("snap-to"))) {
2203 set_snap_to ((SnapType) atoi (prop->value()));
2206 if ((prop = node.property ("snap-mode"))) {
2207 set_snap_mode ((SnapMode) atoi (prop->value()));
2210 if ((prop = node.property ("mouse-mode"))) {
2211 MouseMode m = str2mousemode(prop->value());
2212 set_mouse_mode (m, true);
2213 } else {
2214 set_mouse_mode (MouseObject, true);
2217 if ((prop = node.property ("left-frame")) != 0) {
2218 framepos_t pos;
2219 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2220 reset_x_origin (pos);
2224 if ((prop = node.property ("y-origin")) != 0) {
2225 reset_y_origin (atof (prop->value ()));
2228 if ((prop = node.property ("internal-edit"))) {
2229 bool yn = string_is_affirmative (prop->value());
2230 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2231 if (act) {
2232 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2233 tact->set_active (!yn);
2234 tact->set_active (yn);
2238 if ((prop = node.property ("join-object-range"))) {
2239 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2242 if ((prop = node.property ("edit-point"))) {
2243 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2246 if ((prop = node.property ("show-measures"))) {
2247 bool yn = string_is_affirmative (prop->value());
2248 _show_measures = yn;
2249 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2250 if (act) {
2251 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2252 /* do it twice to force the change */
2253 tact->set_active (!yn);
2254 tact->set_active (yn);
2258 if ((prop = node.property ("follow-playhead"))) {
2259 bool yn = string_is_affirmative (prop->value());
2260 set_follow_playhead (yn);
2261 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2262 if (act) {
2263 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2264 if (tact->get_active() != yn) {
2265 tact->set_active (yn);
2270 if ((prop = node.property ("stationary-playhead"))) {
2271 bool yn = (prop->value() == "yes");
2272 set_stationary_playhead (yn);
2273 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2274 if (act) {
2275 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2276 if (tact->get_active() != yn) {
2277 tact->set_active (yn);
2282 if ((prop = node.property ("region-list-sort-type"))) {
2283 RegionListSortType st;
2284 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2287 if ((prop = node.property ("xfades-visible"))) {
2288 bool yn = string_is_affirmative (prop->value());
2289 _xfade_visibility = !yn;
2290 // set_xfade_visibility (yn);
2293 if ((prop = node.property ("show-editor-mixer"))) {
2295 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2296 assert (act);
2298 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2299 bool yn = string_is_affirmative (prop->value());
2301 /* do it twice to force the change */
2303 tact->set_active (!yn);
2304 tact->set_active (yn);
2307 if ((prop = node.property ("show-editor-list"))) {
2309 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2310 assert (act);
2312 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2313 bool yn = string_is_affirmative (prop->value());
2315 /* do it twice to force the change */
2317 tact->set_active (!yn);
2318 tact->set_active (yn);
2321 if ((prop = node.property (X_("editor-list-page")))) {
2322 _the_notebook.set_current_page (atoi (prop->value ()));
2325 if ((prop = node.property (X_("show-marker-lines")))) {
2326 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2327 assert (act);
2328 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2329 bool yn = string_is_affirmative (prop->value ());
2331 tact->set_active (!yn);
2332 tact->set_active (yn);
2335 XMLNodeList children = node.children ();
2336 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2337 selection->set_state (**i, Stateful::current_state_version);
2338 _regions->set_state (**i);
2341 return 0;
2344 XMLNode&
2345 Editor::get_state ()
2347 XMLNode* node = new XMLNode ("Editor");
2348 char buf[32];
2350 _id.print (buf, sizeof (buf));
2351 node->add_property ("id", buf);
2353 if (is_realized()) {
2354 Glib::RefPtr<Gdk::Window> win = get_window();
2356 int x, y, width, height;
2357 win->get_root_origin(x, y);
2358 win->get_size(width, height);
2360 XMLNode* geometry = new XMLNode ("geometry");
2362 snprintf(buf, sizeof(buf), "%d", width);
2363 geometry->add_property("x-size", string(buf));
2364 snprintf(buf, sizeof(buf), "%d", height);
2365 geometry->add_property("y-size", string(buf));
2366 snprintf(buf, sizeof(buf), "%d", x);
2367 geometry->add_property("x-pos", string(buf));
2368 snprintf(buf, sizeof(buf), "%d", y);
2369 geometry->add_property("y-pos", string(buf));
2370 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2371 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2372 geometry->add_property("notebook-shrunk", _notebook_shrunk ? "1" : "0");
2373 snprintf(buf,sizeof(buf), "%d",pre_maximal_horizontal_pane_position);
2374 geometry->add_property("pre-maximal-horizontal-pane-position", string(buf));
2375 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2376 geometry->add_property("edit-vertical-pane-pos", string(buf));
2378 node->add_child_nocopy (*geometry);
2381 maybe_add_mixer_strip_width (*node);
2383 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2384 node->add_property ("zoom-focus", buf);
2385 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2386 node->add_property ("zoom", buf);
2387 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2388 node->add_property ("snap-to", buf);
2389 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2390 node->add_property ("snap-mode", buf);
2392 node->add_property ("edit-point", enum_2_string (_edit_point));
2394 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2395 node->add_property ("playhead", buf);
2396 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2397 node->add_property ("left-frame", buf);
2398 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2399 node->add_property ("y-origin", buf);
2401 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2402 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2403 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2404 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2405 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2406 node->add_property ("mouse-mode", enum2str(mouse_mode));
2407 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2408 node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2410 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2411 if (act) {
2412 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2413 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2416 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2417 if (act) {
2418 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2419 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2422 snprintf (buf, sizeof (buf), "%d", _the_notebook.get_current_page ());
2423 node->add_property (X_("editor-list-page"), buf);
2425 if (button_bindings) {
2426 XMLNode* bb = new XMLNode (X_("Buttons"));
2427 button_bindings->save (*bb);
2428 node->add_child_nocopy (*bb);
2431 node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no");
2433 node->add_child_nocopy (selection->get_state ());
2434 node->add_child_nocopy (_regions->get_state ());
2436 return *node;
2441 /** @param y y offset from the top of all trackviews.
2442 * @return pair: TimeAxisView that y is over, layer index.
2443 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2444 * in stacked region display mode, otherwise 0.
2446 std::pair<TimeAxisView *, layer_t>
2447 Editor::trackview_by_y_position (double y)
2449 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2451 std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
2452 if (r.first) {
2453 return r;
2457 return std::make_pair ( (TimeAxisView *) 0, 0);
2460 /** Snap a position to the grid, if appropriate, taking into account current
2461 * grid settings and also the state of any snap modifier keys that may be pressed.
2462 * @param start Position to snap.
2463 * @param event Event to get current key modifier information from, or 0.
2465 void
2466 Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2468 if (!_session || !event) {
2469 return;
2472 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2473 if (_snap_mode == SnapOff) {
2474 snap_to_internal (start, direction, for_mark);
2476 } else {
2477 if (_snap_mode != SnapOff) {
2478 snap_to_internal (start, direction, for_mark);
2483 void
2484 Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark)
2486 if (!_session || _snap_mode == SnapOff) {
2487 return;
2490 snap_to_internal (start, direction, for_mark);
2493 void
2494 Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /*for_mark*/)
2496 const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2497 framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2499 switch (_snap_type) {
2500 case SnapToTimecodeFrame:
2501 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2502 start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2503 } else {
2504 start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2506 break;
2508 case SnapToTimecodeSeconds:
2509 if (_session->config.get_timecode_offset_negative()) {
2510 start += _session->config.get_timecode_offset ();
2511 } else {
2512 start -= _session->config.get_timecode_offset ();
2514 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2515 start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2516 } else {
2517 start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2520 if (_session->config.get_timecode_offset_negative()) {
2521 start -= _session->config.get_timecode_offset ();
2522 } else {
2523 start += _session->config.get_timecode_offset ();
2525 break;
2527 case SnapToTimecodeMinutes:
2528 if (_session->config.get_timecode_offset_negative()) {
2529 start += _session->config.get_timecode_offset ();
2530 } else {
2531 start -= _session->config.get_timecode_offset ();
2533 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2534 start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2535 } else {
2536 start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2538 if (_session->config.get_timecode_offset_negative()) {
2539 start -= _session->config.get_timecode_offset ();
2540 } else {
2541 start += _session->config.get_timecode_offset ();
2543 break;
2544 default:
2545 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2546 /*NOTREACHED*/
2550 void
2551 Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark)
2553 const framepos_t one_second = _session->frame_rate();
2554 const framepos_t one_minute = _session->frame_rate() * 60;
2555 framepos_t presnap = start;
2556 framepos_t before;
2557 framepos_t after;
2559 switch (_snap_type) {
2560 case SnapToTimecodeFrame:
2561 case SnapToTimecodeSeconds:
2562 case SnapToTimecodeMinutes:
2563 return timecode_snap_to_internal (start, direction, for_mark);
2565 case SnapToCDFrame:
2566 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2567 start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2568 } else {
2569 start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2571 break;
2573 case SnapToSeconds:
2574 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2575 start = (framepos_t) ceil ((double) start / one_second) * one_second;
2576 } else {
2577 start = (framepos_t) floor ((double) start / one_second) * one_second;
2579 break;
2581 case SnapToMinutes:
2582 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2583 start = (framepos_t) ceil ((double) start / one_minute) * one_minute;
2584 } else {
2585 start = (framepos_t) floor ((double) start / one_minute) * one_minute;
2587 break;
2589 case SnapToBar:
2590 start = _session->tempo_map().round_to_bar (start, direction);
2591 break;
2593 case SnapToBeat:
2594 start = _session->tempo_map().round_to_beat (start, direction);
2595 break;
2597 case SnapToBeatDiv32:
2598 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2599 break;
2600 case SnapToBeatDiv28:
2601 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2602 break;
2603 case SnapToBeatDiv24:
2604 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2605 break;
2606 case SnapToBeatDiv20:
2607 start = _session->tempo_map().round_to_beat_subdivision (start, 20, direction);
2608 break;
2609 case SnapToBeatDiv16:
2610 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2611 break;
2612 case SnapToBeatDiv14:
2613 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2614 break;
2615 case SnapToBeatDiv12:
2616 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2617 break;
2618 case SnapToBeatDiv10:
2619 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2620 break;
2621 case SnapToBeatDiv8:
2622 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2623 break;
2624 case SnapToBeatDiv7:
2625 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2626 break;
2627 case SnapToBeatDiv6:
2628 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2629 break;
2630 case SnapToBeatDiv5:
2631 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2632 break;
2633 case SnapToBeatDiv4:
2634 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2635 break;
2636 case SnapToBeatDiv3:
2637 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2638 break;
2639 case SnapToBeatDiv2:
2640 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2641 break;
2643 case SnapToMark:
2644 if (for_mark) {
2645 return;
2648 _session->locations()->marks_either_side (start, before, after);
2650 if (before == max_framepos) {
2651 start = after;
2652 } else if (after == max_framepos) {
2653 start = before;
2654 } else if (before != max_framepos && after != max_framepos) {
2655 /* have before and after */
2656 if ((start - before) < (after - start)) {
2657 start = before;
2658 } else {
2659 start = after;
2663 break;
2665 case SnapToRegionStart:
2666 case SnapToRegionEnd:
2667 case SnapToRegionSync:
2668 case SnapToRegionBoundary:
2669 if (!region_boundary_cache.empty()) {
2671 vector<framepos_t>::iterator prev = region_boundary_cache.end ();
2672 vector<framepos_t>::iterator next = region_boundary_cache.end ();
2674 if (direction > 0) {
2675 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2676 } else {
2677 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2680 if (next != region_boundary_cache.begin ()) {
2681 prev = next;
2682 prev--;
2685 framepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2686 framepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2688 if (start > (p + n) / 2) {
2689 start = n;
2690 } else {
2691 start = p;
2694 break;
2697 switch (_snap_mode) {
2698 case SnapNormal:
2699 return;
2701 case SnapMagnetic:
2703 if (presnap > start) {
2704 if (presnap > (start + unit_to_frame(snap_threshold))) {
2705 start = presnap;
2708 } else if (presnap < start) {
2709 if (presnap < (start - unit_to_frame(snap_threshold))) {
2710 start = presnap;
2714 default:
2715 /* handled at entry */
2716 return;
2722 void
2723 Editor::setup_toolbar ()
2725 HBox* mode_box = manage(new HBox);
2726 mode_box->set_border_width (2);
2727 mode_box->set_spacing(4);
2729 /* table containing mode buttons */
2731 HBox* mouse_mode_button_box = manage (new HBox ());
2733 if (Profile->get_sae()) {
2734 mouse_mode_button_box->pack_start (mouse_move_button);
2735 } else {
2736 mouse_mode_button_box->pack_start (mouse_move_button);
2737 mouse_mode_button_box->pack_start (join_object_range_button);
2738 mouse_mode_button_box->pack_start (mouse_select_button);
2741 mouse_mode_button_box->pack_start (mouse_zoom_button);
2743 if (!Profile->get_sae()) {
2744 mouse_mode_button_box->pack_start (mouse_gain_button);
2747 mouse_mode_button_box->pack_start (mouse_timefx_button);
2748 mouse_mode_button_box->pack_start (mouse_audition_button);
2749 mouse_mode_button_box->pack_start (internal_edit_button);
2751 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2752 if (!Profile->get_sae()) {
2753 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2755 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2757 edit_mode_selector.set_name ("EditModeSelector");
2758 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2759 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2761 mode_box->pack_start (edit_mode_selector);
2762 mode_box->pack_start (*mouse_mode_button_box);
2764 _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2765 _mouse_mode_tearoff->set_name ("MouseModeBase");
2766 _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2768 if (Profile->get_sae()) {
2769 _mouse_mode_tearoff->set_can_be_torn_off (false);
2772 _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2773 &_mouse_mode_tearoff->tearoff_window()));
2774 _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2775 &_mouse_mode_tearoff->tearoff_window(), 1));
2776 _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2777 &_mouse_mode_tearoff->tearoff_window()));
2778 _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2779 &_mouse_mode_tearoff->tearoff_window(), 1));
2781 mouse_move_button.set_mode (false);
2782 mouse_select_button.set_mode (false);
2783 mouse_gain_button.set_mode (false);
2784 mouse_zoom_button.set_mode (false);
2785 mouse_timefx_button.set_mode (false);
2786 mouse_audition_button.set_mode (false);
2787 join_object_range_button.set_mode (false);
2789 mouse_move_button.set_name ("MouseModeButton");
2790 mouse_select_button.set_name ("MouseModeButton");
2791 mouse_gain_button.set_name ("MouseModeButton");
2792 mouse_zoom_button.set_name ("MouseModeButton");
2793 mouse_timefx_button.set_name ("MouseModeButton");
2794 mouse_audition_button.set_name ("MouseModeButton");
2795 internal_edit_button.set_name ("MouseModeButton");
2796 join_object_range_button.set_name ("MouseModeButton");
2798 mouse_move_button.unset_flags (CAN_FOCUS);
2799 mouse_select_button.unset_flags (CAN_FOCUS);
2800 mouse_gain_button.unset_flags (CAN_FOCUS);
2801 mouse_zoom_button.unset_flags (CAN_FOCUS);
2802 mouse_timefx_button.unset_flags (CAN_FOCUS);
2803 mouse_audition_button.unset_flags (CAN_FOCUS);
2804 internal_edit_button.unset_flags (CAN_FOCUS);
2805 join_object_range_button.unset_flags (CAN_FOCUS);
2807 /* Zoom */
2809 _zoom_box.set_spacing (1);
2810 _zoom_box.set_border_width (0);
2812 zoom_in_button.set_name ("EditorTimeButton");
2813 zoom_in_button.set_image (*(manage (new Image (::get_icon ("zoom_in")))));
2814 zoom_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), false));
2816 zoom_out_button.set_name ("EditorTimeButton");
2817 zoom_out_button.set_image (*(manage (new Image (::get_icon ("zoom_out")))));
2818 zoom_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), true));
2820 zoom_out_full_button.set_name ("EditorTimeButton");
2821 zoom_out_full_button.set_image (*(manage (new Image (::get_icon ("zoom_full")))));
2822 zoom_out_full_button.signal_clicked().connect (sigc::mem_fun(*this, &Editor::temporal_zoom_session));
2824 zoom_focus_selector.set_name ("ZoomFocusSelector");
2825 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
2826 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2828 _zoom_box.pack_start (zoom_out_button, false, false);
2829 _zoom_box.pack_start (zoom_in_button, false, false);
2830 _zoom_box.pack_start (zoom_out_full_button, false, false);
2832 _zoom_box.pack_start (zoom_focus_selector);
2834 /* Track zoom buttons */
2835 tav_expand_button.set_name ("TrackHeightButton");
2836 tav_expand_button.set_size_request (-1, 20);
2837 tav_expand_button.add (*(manage (new Image (::get_icon ("tav_exp")))));
2838 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("expand-tracks"));
2839 act->connect_proxy (tav_expand_button);
2841 tav_shrink_button.set_name ("TrackHeightButton");
2842 tav_shrink_button.set_size_request (-1, 20);
2843 tav_shrink_button.add (*(manage (new Image (::get_icon ("tav_shrink")))));
2844 act = ActionManager::get_action (X_("Editor"), X_("shrink-tracks"));
2845 act->connect_proxy (tav_shrink_button);
2847 _zoom_box.pack_start (tav_shrink_button);
2848 _zoom_box.pack_start (tav_expand_button);
2850 _zoom_tearoff = manage (new TearOff (_zoom_box));
2852 _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2853 &_zoom_tearoff->tearoff_window()));
2854 _zoom_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2855 &_zoom_tearoff->tearoff_window(), 0));
2856 _zoom_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2857 &_zoom_tearoff->tearoff_window()));
2858 _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2859 &_zoom_tearoff->tearoff_window(), 0));
2861 snap_box.set_spacing (1);
2862 snap_box.set_border_width (2);
2864 snap_type_selector.set_name ("SnapTypeSelector");
2865 set_popdown_strings (snap_type_selector, snap_type_strings, true);
2866 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2868 snap_mode_selector.set_name ("SnapModeSelector");
2869 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
2870 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2872 edit_point_selector.set_name ("EditPointSelector");
2873 set_popdown_strings (edit_point_selector, edit_point_strings, true);
2874 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2876 snap_box.pack_start (snap_mode_selector, false, false);
2877 snap_box.pack_start (snap_type_selector, false, false);
2878 snap_box.pack_start (edit_point_selector, false, false);
2880 /* Nudge */
2882 HBox *nudge_box = manage (new HBox);
2883 nudge_box->set_spacing(1);
2884 nudge_box->set_border_width (2);
2886 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2887 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2889 nudge_box->pack_start (nudge_backward_button, false, false);
2890 nudge_box->pack_start (nudge_forward_button, false, false);
2891 nudge_box->pack_start (*nudge_clock, false, false);
2894 /* Pack everything in... */
2896 HBox* hbox = manage (new HBox);
2897 hbox->set_spacing(10);
2899 _tools_tearoff = manage (new TearOff (*hbox));
2900 _tools_tearoff->set_name ("MouseModeBase");
2901 _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
2903 if (Profile->get_sae()) {
2904 _tools_tearoff->set_can_be_torn_off (false);
2907 _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2908 &_tools_tearoff->tearoff_window()));
2909 _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2910 &_tools_tearoff->tearoff_window(), 0));
2911 _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2912 &_tools_tearoff->tearoff_window()));
2913 _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2914 &_tools_tearoff->tearoff_window(), 0));
2916 toolbar_hbox.set_spacing (10);
2917 toolbar_hbox.set_border_width (1);
2919 toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
2920 toolbar_hbox.pack_start (*_zoom_tearoff, false, false);
2921 toolbar_hbox.pack_start (*_tools_tearoff, false, false);
2923 hbox->pack_start (snap_box, false, false);
2924 if (!Profile->get_small_screen()) {
2925 hbox->pack_start (*nudge_box, false, false);
2926 } else {
2927 ARDOUR_UI::instance()->editor_transport_box().pack_start (*nudge_box, false, false);
2929 hbox->pack_start (panic_box, false, false);
2931 hbox->show_all ();
2933 toolbar_base.set_name ("ToolBarBase");
2934 toolbar_base.add (toolbar_hbox);
2936 _toolbar_viewport.add (toolbar_base);
2937 /* stick to the required height but allow width to vary if there's not enough room */
2938 _toolbar_viewport.set_size_request (1, -1);
2940 toolbar_frame.set_shadow_type (SHADOW_OUT);
2941 toolbar_frame.set_name ("BaseFrame");
2942 toolbar_frame.add (_toolbar_viewport);
2944 DPIReset.connect (sigc::mem_fun (*this, &Editor::resize_text_widgets));
2947 void
2948 Editor::setup_tooltips ()
2950 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
2951 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain"));
2952 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
2953 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
2954 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2955 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
2956 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
2957 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
2958 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
2959 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
2960 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
2961 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
2962 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
2963 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
2964 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
2965 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
2966 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
2967 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
2968 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
2969 ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
2970 ARDOUR_UI::instance()->set_tip (midi_panic_button, _("Send note off and reset controller messages on all MIDI channels"));
2971 ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode"));
2974 void
2975 Editor::midi_panic ()
2977 cerr << "MIDI panic\n";
2979 if (_session) {
2980 _session->midi_panic();
2984 void
2985 Editor::setup_midi_toolbar ()
2987 RefPtr<Action> act;
2989 /* Midi sound notes */
2990 midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
2991 midi_sound_notes.unset_flags (CAN_FOCUS);
2993 /* Panic */
2995 act = ActionManager::get_action (X_("MIDI"), X_("panic"));
2996 midi_panic_button.set_name("MidiPanicButton");
2997 act->connect_proxy (midi_panic_button);
2999 panic_box.pack_start (midi_sound_notes , true, true);
3000 panic_box.pack_start (midi_panic_button, true, true);
3004 Editor::convert_drop_to_paths (
3005 vector<string>& paths,
3006 const RefPtr<Gdk::DragContext>& /*context*/,
3007 gint /*x*/,
3008 gint /*y*/,
3009 const SelectionData& data,
3010 guint /*info*/,
3011 guint /*time*/)
3013 if (_session == 0) {
3014 return -1;
3017 vector<string> uris = data.get_uris();
3019 if (uris.empty()) {
3021 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3022 are actually URI lists. So do it by hand.
3025 if (data.get_target() != "text/plain") {
3026 return -1;
3029 /* Parse the "uri-list" format that Nautilus provides,
3030 where each pathname is delimited by \r\n.
3032 THERE MAY BE NO NULL TERMINATING CHAR!!!
3035 string txt = data.get_text();
3036 const char* p;
3037 const char* q;
3039 p = (const char *) malloc (txt.length() + 1);
3040 txt.copy ((char *) p, txt.length(), 0);
3041 ((char*)p)[txt.length()] = '\0';
3043 while (p)
3045 if (*p != '#')
3047 while (g_ascii_isspace (*p))
3048 p++;
3050 q = p;
3051 while (*q && (*q != '\n') && (*q != '\r')) {
3052 q++;
3055 if (q > p)
3057 q--;
3058 while (q > p && g_ascii_isspace (*q))
3059 q--;
3061 if (q > p)
3063 uris.push_back (string (p, q - p + 1));
3067 p = strchr (p, '\n');
3068 if (p)
3069 p++;
3072 free ((void*)p);
3074 if (uris.empty()) {
3075 return -1;
3079 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3081 if ((*i).substr (0,7) == "file://") {
3083 string p = *i;
3084 PBD::url_decode (p);
3086 // scan forward past three slashes
3088 string::size_type slashcnt = 0;
3089 string::size_type n = 0;
3090 string::iterator x = p.begin();
3092 while (slashcnt < 3 && x != p.end()) {
3093 if ((*x) == '/') {
3094 slashcnt++;
3095 } else if (slashcnt == 3) {
3096 break;
3098 ++n;
3099 ++x;
3102 if (slashcnt != 3 || x == p.end()) {
3103 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3104 continue;
3107 paths.push_back (p.substr (n - 1));
3111 return 0;
3114 void
3115 Editor::new_tempo_section ()
3120 void
3121 Editor::map_transport_state ()
3123 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3125 if (_session && _session->transport_stopped()) {
3126 have_pending_keyboard_selection = false;
3129 update_loop_range_view (true);
3132 /* UNDO/REDO */
3134 Editor::State::State (PublicEditor const * e)
3136 selection = new Selection (e);
3139 Editor::State::~State ()
3141 delete selection;
3144 void
3145 Editor::begin_reversible_command (string name)
3147 if (_session) {
3148 _session->begin_reversible_command (name);
3152 void
3153 Editor::begin_reversible_command (GQuark q)
3155 if (_session) {
3156 _session->begin_reversible_command (q);
3160 void
3161 Editor::commit_reversible_command ()
3163 if (_session) {
3164 _session->commit_reversible_command ();
3168 void
3169 Editor::history_changed ()
3171 string label;
3173 if (undo_action && _session) {
3174 if (_session->undo_depth() == 0) {
3175 label = _("Undo");
3176 } else {
3177 label = string_compose(_("Undo (%1)"), _session->next_undo());
3179 undo_action->property_label() = label;
3182 if (redo_action && _session) {
3183 if (_session->redo_depth() == 0) {
3184 label = _("Redo");
3185 } else {
3186 label = string_compose(_("Redo (%1)"), _session->next_redo());
3188 redo_action->property_label() = label;
3192 void
3193 Editor::duplicate_dialog (bool with_dialog)
3195 float times = 1.0f;
3197 if (mouse_mode == MouseRange) {
3198 if (selection->time.length() == 0) {
3199 return;
3203 RegionSelection rs = get_regions_from_selection_and_entered ();
3205 if (mouse_mode != MouseRange && rs.empty()) {
3206 return;
3209 if (with_dialog) {
3211 ArdourDialog win (_("Duplicate"));
3212 Label label (_("Number of duplications:"));
3213 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3214 SpinButton spinner (adjustment, 0.0, 1);
3215 HBox hbox;
3217 win.get_vbox()->set_spacing (12);
3218 win.get_vbox()->pack_start (hbox);
3219 hbox.set_border_width (6);
3220 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3222 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3223 place, visually. so do this by hand.
3226 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3227 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3228 spinner.grab_focus();
3230 hbox.show ();
3231 label.show ();
3232 spinner.show ();
3234 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3235 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3236 win.set_default_response (RESPONSE_ACCEPT);
3238 win.set_position (WIN_POS_MOUSE);
3240 spinner.grab_focus ();
3242 switch (win.run ()) {
3243 case RESPONSE_ACCEPT:
3244 break;
3245 default:
3246 return;
3249 times = adjustment.get_value();
3252 if (mouse_mode == MouseRange) {
3253 duplicate_selection (times);
3254 } else {
3255 duplicate_some_regions (rs, times);
3259 void
3260 Editor::set_edit_mode (EditMode m)
3262 Config->set_edit_mode (m);
3265 void
3266 Editor::cycle_edit_mode ()
3268 switch (Config->get_edit_mode()) {
3269 case Slide:
3270 if (Profile->get_sae()) {
3271 Config->set_edit_mode (Lock);
3272 } else {
3273 Config->set_edit_mode (Splice);
3275 break;
3276 case Splice:
3277 Config->set_edit_mode (Lock);
3278 break;
3279 case Lock:
3280 Config->set_edit_mode (Slide);
3281 break;
3285 void
3286 Editor::edit_mode_selection_done ()
3288 string s = edit_mode_selector.get_active_text ();
3290 if (!s.empty()) {
3291 Config->set_edit_mode (string_to_edit_mode (s));
3295 void
3296 Editor::snap_type_selection_done ()
3298 string choice = snap_type_selector.get_active_text();
3299 SnapType snaptype = SnapToBeat;
3301 if (choice == _("Beats/2")) {
3302 snaptype = SnapToBeatDiv2;
3303 } else if (choice == _("Beats/3")) {
3304 snaptype = SnapToBeatDiv3;
3305 } else if (choice == _("Beats/4")) {
3306 snaptype = SnapToBeatDiv4;
3307 } else if (choice == _("Beats/5")) {
3308 snaptype = SnapToBeatDiv5;
3309 } else if (choice == _("Beats/6")) {
3310 snaptype = SnapToBeatDiv6;
3311 } else if (choice == _("Beats/7")) {
3312 snaptype = SnapToBeatDiv7;
3313 } else if (choice == _("Beats/8")) {
3314 snaptype = SnapToBeatDiv8;
3315 } else if (choice == _("Beats/10")) {
3316 snaptype = SnapToBeatDiv10;
3317 } else if (choice == _("Beats/12")) {
3318 snaptype = SnapToBeatDiv12;
3319 } else if (choice == _("Beats/14")) {
3320 snaptype = SnapToBeatDiv14;
3321 } else if (choice == _("Beats/16")) {
3322 snaptype = SnapToBeatDiv16;
3323 } else if (choice == _("Beats/20")) {
3324 snaptype = SnapToBeatDiv20;
3325 } else if (choice == _("Beats/24")) {
3326 snaptype = SnapToBeatDiv24;
3327 } else if (choice == _("Beats/28")) {
3328 snaptype = SnapToBeatDiv28;
3329 } else if (choice == _("Beats/32")) {
3330 snaptype = SnapToBeatDiv32;
3331 } else if (choice == _("Beats")) {
3332 snaptype = SnapToBeat;
3333 } else if (choice == _("Bars")) {
3334 snaptype = SnapToBar;
3335 } else if (choice == _("Marks")) {
3336 snaptype = SnapToMark;
3337 } else if (choice == _("Region starts")) {
3338 snaptype = SnapToRegionStart;
3339 } else if (choice == _("Region ends")) {
3340 snaptype = SnapToRegionEnd;
3341 } else if (choice == _("Region bounds")) {
3342 snaptype = SnapToRegionBoundary;
3343 } else if (choice == _("Region syncs")) {
3344 snaptype = SnapToRegionSync;
3345 } else if (choice == _("CD Frames")) {
3346 snaptype = SnapToCDFrame;
3347 } else if (choice == _("Timecode Frames")) {
3348 snaptype = SnapToTimecodeFrame;
3349 } else if (choice == _("Timecode Seconds")) {
3350 snaptype = SnapToTimecodeSeconds;
3351 } else if (choice == _("Timecode Minutes")) {
3352 snaptype = SnapToTimecodeMinutes;
3353 } else if (choice == _("Seconds")) {
3354 snaptype = SnapToSeconds;
3355 } else if (choice == _("Minutes")) {
3356 snaptype = SnapToMinutes;
3359 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3360 if (ract) {
3361 ract->set_active ();
3365 void
3366 Editor::snap_mode_selection_done ()
3368 string choice = snap_mode_selector.get_active_text();
3369 SnapMode mode = SnapNormal;
3371 if (choice == _("No Grid")) {
3372 mode = SnapOff;
3373 } else if (choice == _("Grid")) {
3374 mode = SnapNormal;
3375 } else if (choice == _("Magnetic")) {
3376 mode = SnapMagnetic;
3379 RefPtr<RadioAction> ract = snap_mode_action (mode);
3381 if (ract) {
3382 ract->set_active (true);
3386 void
3387 Editor::cycle_edit_point (bool with_marker)
3389 switch (_edit_point) {
3390 case EditAtMouse:
3391 set_edit_point_preference (EditAtPlayhead);
3392 break;
3393 case EditAtPlayhead:
3394 if (with_marker) {
3395 set_edit_point_preference (EditAtSelectedMarker);
3396 } else {
3397 set_edit_point_preference (EditAtMouse);
3399 break;
3400 case EditAtSelectedMarker:
3401 set_edit_point_preference (EditAtMouse);
3402 break;
3406 void
3407 Editor::edit_point_selection_done ()
3409 string choice = edit_point_selector.get_active_text();
3410 EditPoint ep = EditAtSelectedMarker;
3412 if (choice == _("Marker")) {
3413 set_edit_point_preference (EditAtSelectedMarker);
3414 } else if (choice == _("Playhead")) {
3415 set_edit_point_preference (EditAtPlayhead);
3416 } else {
3417 set_edit_point_preference (EditAtMouse);
3420 RefPtr<RadioAction> ract = edit_point_action (ep);
3422 if (ract) {
3423 ract->set_active (true);
3427 void
3428 Editor::zoom_focus_selection_done ()
3430 string choice = zoom_focus_selector.get_active_text();
3431 ZoomFocus focus_type = ZoomFocusLeft;
3433 if (choice == _("Left")) {
3434 focus_type = ZoomFocusLeft;
3435 } else if (choice == _("Right")) {
3436 focus_type = ZoomFocusRight;
3437 } else if (choice == _("Center")) {
3438 focus_type = ZoomFocusCenter;
3439 } else if (choice == _("Playhead")) {
3440 focus_type = ZoomFocusPlayhead;
3441 } else if (choice == _("Mouse")) {
3442 focus_type = ZoomFocusMouse;
3443 } else if (choice == _("Edit point")) {
3444 focus_type = ZoomFocusEdit;
3447 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3449 if (ract) {
3450 ract->set_active ();
3454 bool
3455 Editor::edit_controls_button_release (GdkEventButton* ev)
3457 if (Keyboard::is_context_menu_event (ev)) {
3458 ARDOUR_UI::instance()->add_route (this);
3459 } else if (ev->button == 1) {
3460 selection->clear_tracks ();
3463 return true;
3466 bool
3467 Editor::mouse_select_button_release (GdkEventButton* ev)
3469 /* this handles just right-clicks */
3471 if (ev->button != 3) {
3472 return false;
3475 return true;
3478 void
3479 Editor::set_zoom_focus (ZoomFocus f)
3481 string str = zoom_focus_strings[(int)f];
3483 if (str != zoom_focus_selector.get_active_text()) {
3484 zoom_focus_selector.set_active_text (str);
3487 if (zoom_focus != f) {
3488 zoom_focus = f;
3489 instant_save ();
3493 void
3494 Editor::ensure_float (Window& win)
3496 win.set_transient_for (*this);
3499 void
3500 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3502 /* recover or initialize pane positions. do this here rather than earlier because
3503 we don't want the positions to change the child allocations, which they seem to do.
3506 int pos;
3507 XMLProperty* prop;
3508 char buf[32];
3509 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3511 enum Pane {
3512 Horizontal = 0x1,
3513 Vertical = 0x2
3516 static Pane done;
3518 XMLNode* geometry = find_named_node (*node, "geometry");
3520 if (which == static_cast<Paned*> (&edit_pane)) {
3522 if (done & Horizontal) {
3523 return;
3526 if (geometry && (prop = geometry->property ("notebook-shrunk"))) {
3527 _notebook_shrunk = string_is_affirmative (prop->value ());
3530 if (geometry && (prop = geometry->property ("pre-maximal-horizontal-pane-position"))) {
3531 pre_maximal_horizontal_pane_position = atoi (prop->value ());
3534 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3535 /* initial allocation is 90% to canvas, 10% to notebook */
3536 pos = (int) floor (alloc.get_width() * 0.90f);
3537 snprintf (buf, sizeof(buf), "%d", pos);
3538 } else {
3539 pos = atoi (prop->value());
3542 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3543 edit_pane.set_position (pos);
3544 if (pre_maximal_horizontal_pane_position == 0) {
3545 pre_maximal_horizontal_pane_position = pos;
3549 done = (Pane) (done | Horizontal);
3551 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3553 if (done & Vertical) {
3554 return;
3557 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3558 /* initial allocation is 90% to canvas, 10% to summary */
3559 pos = (int) floor (alloc.get_height() * 0.90f);
3560 snprintf (buf, sizeof(buf), "%d", pos);
3561 } else {
3562 pos = atoi (prop->value());
3565 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3566 editor_summary_pane.set_position (pos);
3567 pre_maximal_vertical_pane_position = pos;
3570 done = (Pane) (done | Vertical);
3574 void
3575 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3577 if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) {
3578 top_hbox.remove (toolbar_frame);
3582 void
3583 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3585 if (toolbar_frame.get_parent() == 0) {
3586 top_hbox.pack_end (toolbar_frame);
3590 void
3591 Editor::set_show_measures (bool yn)
3593 if (_show_measures != yn) {
3594 hide_measures ();
3596 if ((_show_measures = yn) == true) {
3597 if (tempo_lines)
3598 tempo_lines->show();
3599 draw_measures ();
3601 instant_save ();
3605 void
3606 Editor::toggle_follow_playhead ()
3608 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3609 if (act) {
3610 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3611 set_follow_playhead (tact->get_active());
3615 /** @param yn true to follow playhead, otherwise false.
3616 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
3618 void
3619 Editor::set_follow_playhead (bool yn, bool catch_up)
3621 if (_follow_playhead != yn) {
3622 if ((_follow_playhead = yn) == true && catch_up) {
3623 /* catch up */
3624 reset_x_origin_to_follow_playhead ();
3626 instant_save ();
3630 void
3631 Editor::toggle_stationary_playhead ()
3633 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3634 if (act) {
3635 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3636 set_stationary_playhead (tact->get_active());
3640 void
3641 Editor::set_stationary_playhead (bool yn)
3643 if (_stationary_playhead != yn) {
3644 if ((_stationary_playhead = yn) == true) {
3645 /* catch up */
3646 // FIXME need a 3.0 equivalent of this 2.X call
3647 // update_current_screen ();
3649 instant_save ();
3653 void
3654 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3656 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3657 if (xfade) {
3658 xfade->set_active (!xfade->active());
3662 void
3663 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3665 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3666 if (xfade) {
3667 xfade->set_follow_overlap (!xfade->following_overlap());
3671 void
3672 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3674 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3676 if (!xfade) {
3677 return;
3680 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3682 ensure_float (cew);
3684 switch (cew.run ()) {
3685 case RESPONSE_ACCEPT:
3686 break;
3687 default:
3688 return;
3691 cew.apply ();
3692 PropertyChange all_crossfade_properties;
3693 all_crossfade_properties.add (ARDOUR::Properties::active);
3694 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3695 xfade->PropertyChanged (all_crossfade_properties);
3698 PlaylistSelector&
3699 Editor::playlist_selector () const
3701 return *_playlist_selector;
3704 Evoral::MusicalTime
3705 Editor::get_grid_type_as_beats (bool& success, framepos_t position)
3707 success = true;
3709 switch (_snap_type) {
3710 case SnapToBeat:
3711 return 1.0;
3712 break;
3714 case SnapToBeatDiv32:
3715 return 1.0/32.0;
3716 break;
3717 case SnapToBeatDiv28:
3718 return 1.0/28.0;
3719 break;
3720 case SnapToBeatDiv24:
3721 return 1.0/24.0;
3722 break;
3723 case SnapToBeatDiv20:
3724 return 1.0/20.0;
3725 break;
3726 case SnapToBeatDiv16:
3727 return 1.0/16.0;
3728 break;
3729 case SnapToBeatDiv14:
3730 return 1.0/14.0;
3731 break;
3732 case SnapToBeatDiv12:
3733 return 1.0/12.0;
3734 break;
3735 case SnapToBeatDiv10:
3736 return 1.0/10.0;
3737 break;
3738 case SnapToBeatDiv8:
3739 return 1.0/8.0;
3740 break;
3741 case SnapToBeatDiv7:
3742 return 1.0/7.0;
3743 break;
3744 case SnapToBeatDiv6:
3745 return 1.0/6.0;
3746 break;
3747 case SnapToBeatDiv5:
3748 return 1.0/5.0;
3749 break;
3750 case SnapToBeatDiv4:
3751 return 1.0/4.0;
3752 break;
3753 case SnapToBeatDiv3:
3754 return 1.0/3.0;
3755 break;
3756 case SnapToBeatDiv2:
3757 return 1.0/2.0;
3758 break;
3760 case SnapToBar:
3761 if (_session) {
3762 return _session->tempo_map().meter_at (position).beats_per_bar();
3764 break;
3766 case SnapToCDFrame:
3767 case SnapToTimecodeFrame:
3768 case SnapToTimecodeSeconds:
3769 case SnapToTimecodeMinutes:
3770 case SnapToSeconds:
3771 case SnapToMinutes:
3772 case SnapToRegionStart:
3773 case SnapToRegionEnd:
3774 case SnapToRegionSync:
3775 case SnapToRegionBoundary:
3776 default:
3777 success = false;
3778 break;
3781 return 0.0;
3784 framecnt_t
3785 Editor::get_nudge_distance (framepos_t pos, framecnt_t& next)
3787 framecnt_t ret;
3789 ret = nudge_clock->current_duration (pos);
3790 next = ret + 1; /* XXXX fix me */
3792 return ret;
3796 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3798 ArdourDialog dialog (_("Playlist Deletion"));
3799 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3800 "If it is kept, its audio files will not be cleaned.\n"
3801 "If it is deleted, audio files used by it alone will be cleaned."),
3802 pl->name()));
3804 dialog.set_position (WIN_POS_CENTER);
3805 dialog.get_vbox()->pack_start (label);
3807 label.show ();
3809 dialog.add_button (_("Delete Playlist"), RESPONSE_ACCEPT);
3810 dialog.add_button (_("Keep Playlist"), RESPONSE_REJECT);
3811 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3813 switch (dialog.run ()) {
3814 case RESPONSE_ACCEPT:
3815 /* delete the playlist */
3816 return 0;
3817 break;
3819 case RESPONSE_REJECT:
3820 /* keep the playlist */
3821 return 1;
3822 break;
3824 default:
3825 break;
3828 return -1;
3831 bool
3832 Editor::audio_region_selection_covers (framepos_t where)
3834 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3835 if ((*a)->region()->covers (where)) {
3836 return true;
3840 return false;
3843 void
3844 Editor::prepare_for_cleanup ()
3846 cut_buffer->clear_regions ();
3847 cut_buffer->clear_playlists ();
3849 selection->clear_regions ();
3850 selection->clear_playlists ();
3852 _regions->suspend_redisplay ();
3855 void
3856 Editor::finish_cleanup ()
3858 _regions->resume_redisplay ();
3861 Location*
3862 Editor::transport_loop_location()
3864 if (_session) {
3865 return _session->locations()->auto_loop_location();
3866 } else {
3867 return 0;
3871 Location*
3872 Editor::transport_punch_location()
3874 if (_session) {
3875 return _session->locations()->auto_punch_location();
3876 } else {
3877 return 0;
3881 bool
3882 Editor::control_layout_scroll (GdkEventScroll* ev)
3884 if (Keyboard::some_magic_widget_has_focus()) {
3885 return false;
3888 switch (ev->direction) {
3889 case GDK_SCROLL_UP:
3890 scroll_tracks_up_line ();
3891 return true;
3892 break;
3894 case GDK_SCROLL_DOWN:
3895 scroll_tracks_down_line ();
3896 return true;
3898 default:
3899 /* no left/right handling yet */
3900 break;
3903 return false;
3906 void
3907 Editor::session_state_saved (string)
3909 update_title ();
3910 _snapshots->redisplay ();
3913 void
3914 Editor::maximise_editing_space ()
3916 _mouse_mode_tearoff->set_visible (false);
3917 _tools_tearoff->set_visible (false);
3918 _zoom_tearoff->set_visible (false);
3920 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
3921 pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
3922 pre_maximal_editor_width = this->get_width ();
3923 pre_maximal_editor_height = this->get_height ();
3925 if (post_maximal_horizontal_pane_position == 0) {
3926 post_maximal_horizontal_pane_position = edit_pane.get_width();
3929 if (post_maximal_vertical_pane_position == 0) {
3930 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
3933 fullscreen ();
3935 if (post_maximal_editor_width) {
3936 edit_pane.set_position (post_maximal_horizontal_pane_position -
3937 abs(post_maximal_editor_width - pre_maximal_editor_width));
3938 } else {
3939 edit_pane.set_position (post_maximal_horizontal_pane_position);
3942 if (post_maximal_editor_height) {
3943 editor_summary_pane.set_position (post_maximal_vertical_pane_position -
3944 abs(post_maximal_editor_height - pre_maximal_editor_height));
3945 } else {
3946 editor_summary_pane.set_position (post_maximal_vertical_pane_position);
3949 if (Config->get_keep_tearoffs()) {
3950 _mouse_mode_tearoff->set_visible (true);
3951 _tools_tearoff->set_visible (true);
3952 if (Config->get_show_zoom_tools ()) {
3953 _zoom_tearoff->set_visible (true);
3959 void
3960 Editor::restore_editing_space ()
3962 // user changed width/height of panes during fullscreen
3964 if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
3965 post_maximal_horizontal_pane_position = edit_pane.get_position();
3968 if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
3969 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
3972 unfullscreen();
3974 _mouse_mode_tearoff->set_visible (true);
3975 _tools_tearoff->set_visible (true);
3976 if (Config->get_show_zoom_tools ()) {
3977 _zoom_tearoff->set_visible (true);
3979 post_maximal_editor_width = this->get_width();
3980 post_maximal_editor_height = this->get_height();
3982 edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
3983 editor_summary_pane.set_position (pre_maximal_vertical_pane_position + abs(this->get_height() - pre_maximal_editor_height));
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 (framepos_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 (framepos_t frame, double fpu)
4088 reset_x_origin (frame);
4089 reset_zoom (fpu);
4091 if (!no_save_visual) {
4092 undo_visual_stack.push_back (current_visual_state(false));
4096 Editor::VisualState*
4097 Editor::current_visual_state (bool with_tracks)
4099 VisualState* vs = new VisualState;
4100 vs->y_position = vertical_adjustment.get_value();
4101 vs->frames_per_unit = frames_per_unit;
4102 vs->leftmost_frame = leftmost_frame;
4103 vs->zoom_focus = zoom_focus;
4105 if (with_tracks) {
4106 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4107 vs->track_states.push_back (TAVState ((*i), &(*i)->get_state()));
4111 return vs;
4114 void
4115 Editor::undo_visual_state ()
4117 if (undo_visual_stack.empty()) {
4118 return;
4121 redo_visual_stack.push_back (current_visual_state());
4123 VisualState* vs = undo_visual_stack.back();
4124 undo_visual_stack.pop_back();
4125 use_visual_state (*vs);
4128 void
4129 Editor::redo_visual_state ()
4131 if (redo_visual_stack.empty()) {
4132 return;
4135 undo_visual_stack.push_back (current_visual_state());
4137 VisualState* vs = redo_visual_stack.back();
4138 redo_visual_stack.pop_back();
4139 use_visual_state (*vs);
4142 void
4143 Editor::swap_visual_state ()
4145 if (undo_visual_stack.empty()) {
4146 redo_visual_state ();
4147 } else {
4148 undo_visual_state ();
4152 void
4153 Editor::use_visual_state (VisualState& vs)
4155 no_save_visual = true;
4157 _routes->suspend_redisplay ();
4159 vertical_adjustment.set_value (vs.y_position);
4161 set_zoom_focus (vs.zoom_focus);
4162 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4164 for (list<TAVState>::iterator i = vs.track_states.begin(); i != vs.track_states.end(); ++i) {
4165 TrackViewList::iterator t;
4167 /* check if the track still exists - it could have been deleted */
4169 if ((t = find (track_views.begin(), track_views.end(), i->first)) != track_views.end()) {
4170 (*t)->set_state (*(i->second), Stateful::loading_state_version);
4175 if (!vs.track_states.empty()) {
4176 _routes->update_visibility ();
4179 _routes->resume_redisplay ();
4181 no_save_visual = false;
4184 void
4185 Editor::set_frames_per_unit (double fpu)
4187 /* this is the core function that controls the zoom level of the canvas. it is called
4188 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4191 if (fpu == frames_per_unit) {
4192 return;
4195 if (fpu < 2.0) {
4196 fpu = 2.0;
4200 /* don't allow zooms that fit more than the maximum number
4201 of frames into an 800 pixel wide space.
4204 if (max_framepos / fpu < 800.0) {
4205 return;
4208 if (tempo_lines)
4209 tempo_lines->tempo_map_changed();
4211 frames_per_unit = fpu;
4212 post_zoom ();
4215 void
4216 Editor::post_zoom ()
4218 // convert fpu to frame count
4220 framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width);
4222 if (frames_per_unit != zoom_range_clock->current_duration()) {
4223 zoom_range_clock->set (frames);
4226 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4227 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4228 (*i)->reshow_selection (selection->time);
4232 ZoomChanged (); /* EMIT_SIGNAL */
4234 //reset_scrolling_region ();
4236 if (playhead_cursor) {
4237 playhead_cursor->set_position (playhead_cursor->current_frame);
4240 refresh_location_display();
4241 _summary->set_overlays_dirty ();
4243 update_marker_labels ();
4245 instant_save ();
4248 void
4249 Editor::queue_visual_change (framepos_t where)
4251 pending_visual_change.add (VisualChange::TimeOrigin);
4252 pending_visual_change.time_origin = where;
4253 ensure_visual_change_idle_handler ();
4256 void
4257 Editor::queue_visual_change (double fpu)
4259 pending_visual_change.add (VisualChange::ZoomLevel);
4260 pending_visual_change.frames_per_unit = fpu;
4262 ensure_visual_change_idle_handler ();
4265 void
4266 Editor::queue_visual_change_y (double y)
4268 pending_visual_change.add (VisualChange::YOrigin);
4269 pending_visual_change.y_origin = y;
4271 ensure_visual_change_idle_handler ();
4274 void
4275 Editor::ensure_visual_change_idle_handler ()
4277 if (pending_visual_change.idle_handler_id < 0) {
4278 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4283 Editor::_idle_visual_changer (void* arg)
4285 return static_cast<Editor*>(arg)->idle_visual_changer ();
4289 Editor::idle_visual_changer ()
4291 VisualChange::Type p = pending_visual_change.pending;
4292 pending_visual_change.pending = (VisualChange::Type) 0;
4294 double const last_time_origin = horizontal_position ();
4296 if (p & VisualChange::TimeOrigin) {
4297 /* This is a bit of a hack, but set_frames_per_unit
4298 below will (if called) end up with the
4299 CrossfadeViews looking at Editor::leftmost_frame,
4300 and if we're changing origin and zoom in the same
4301 operation it will be the wrong value unless we
4302 update it here.
4305 leftmost_frame = pending_visual_change.time_origin;
4308 if (p & VisualChange::ZoomLevel) {
4309 set_frames_per_unit (pending_visual_change.frames_per_unit);
4311 compute_fixed_ruler_scale ();
4312 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4313 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4314 update_tempo_based_rulers ();
4316 if (p & VisualChange::TimeOrigin) {
4317 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4319 if (p & VisualChange::YOrigin) {
4320 vertical_adjustment.set_value (pending_visual_change.y_origin);
4323 if (last_time_origin == horizontal_position ()) {
4324 /* changed signal not emitted */
4325 update_fixed_rulers ();
4326 redisplay_tempo (true);
4329 _summary->set_overlays_dirty ();
4331 pending_visual_change.idle_handler_id = -1;
4332 return 0; /* this is always a one-shot call */
4335 struct EditorOrderTimeAxisSorter {
4336 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4337 return a->order () < b->order ();
4341 void
4342 Editor::sort_track_selection (TrackViewList* sel)
4344 EditorOrderTimeAxisSorter cmp;
4346 if (sel) {
4347 sel->sort (cmp);
4348 } else {
4349 selection->tracks.sort (cmp);
4353 framepos_t
4354 Editor::get_preferred_edit_position (bool ignore_playhead)
4356 bool ignored;
4357 framepos_t where = 0;
4358 EditPoint ep = _edit_point;
4360 if (entered_marker) {
4361 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position()));
4362 return entered_marker->position();
4365 if (ignore_playhead && ep == EditAtPlayhead) {
4366 ep = EditAtSelectedMarker;
4369 switch (ep) {
4370 case EditAtPlayhead:
4371 where = _session->audible_frame();
4372 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where));
4373 break;
4375 case EditAtSelectedMarker:
4376 if (!selection->markers.empty()) {
4377 bool is_start;
4378 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4379 if (loc) {
4380 if (is_start) {
4381 where = loc->start();
4382 } else {
4383 where = loc->end();
4385 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where));
4386 break;
4389 /* fallthru */
4391 default:
4392 case EditAtMouse:
4393 if (!mouse_frame (where, ignored)) {
4394 /* XXX not right but what can we do ? */
4395 return 0;
4397 snap_to (where);
4398 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where));
4399 break;
4402 return where;
4405 void
4406 Editor::set_loop_range (framepos_t start, framepos_t end, string cmd)
4408 if (!_session) return;
4410 begin_reversible_command (cmd);
4412 Location* tll;
4414 if ((tll = transport_loop_location()) == 0) {
4415 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop);
4416 XMLNode &before = _session->locations()->get_state();
4417 _session->locations()->add (loc, true);
4418 _session->set_auto_loop_location (loc);
4419 XMLNode &after = _session->locations()->get_state();
4420 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4421 } else {
4422 XMLNode &before = tll->get_state();
4423 tll->set_hidden (false, this);
4424 tll->set (start, end);
4425 XMLNode &after = tll->get_state();
4426 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4429 commit_reversible_command ();
4432 void
4433 Editor::set_punch_range (framepos_t start, framepos_t end, string cmd)
4435 if (!_session) return;
4437 begin_reversible_command (cmd);
4439 Location* tpl;
4441 if ((tpl = transport_punch_location()) == 0) {
4442 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoPunch);
4443 XMLNode &before = _session->locations()->get_state();
4444 _session->locations()->add (loc, true);
4445 _session->set_auto_loop_location (loc);
4446 XMLNode &after = _session->locations()->get_state();
4447 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4449 else {
4450 XMLNode &before = tpl->get_state();
4451 tpl->set_hidden (false, this);
4452 tpl->set (start, end);
4453 XMLNode &after = tpl->get_state();
4454 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4457 commit_reversible_command ();
4460 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4461 * @param rs List to which found regions are added.
4462 * @param where Time to look at.
4463 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4465 void
4466 Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4468 const TrackViewList* tracks;
4470 if (ts.empty()) {
4471 tracks = &track_views;
4472 } else {
4473 tracks = &ts;
4476 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4478 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4480 if (rtv) {
4481 boost::shared_ptr<Track> tr;
4482 boost::shared_ptr<Playlist> pl;
4484 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4486 Playlist::RegionList* regions = pl->regions_at (
4487 (framepos_t) floor ( (double) where * tr->speed()));
4489 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4490 RegionView* rv = rtv->view()->find_view (*i);
4491 if (rv) {
4492 rs.add (rv);
4496 delete regions;
4502 void
4503 Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4505 const TrackViewList* tracks;
4507 if (ts.empty()) {
4508 tracks = &track_views;
4509 } else {
4510 tracks = &ts;
4513 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4514 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4515 if (rtv) {
4516 boost::shared_ptr<Track> tr;
4517 boost::shared_ptr<Playlist> pl;
4519 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4521 Playlist::RegionList* regions = pl->regions_touched (
4522 (framepos_t) floor ( (double)where * tr->speed()), max_framepos);
4524 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4526 RegionView* rv = rtv->view()->find_view (*i);
4528 if (rv) {
4529 rs.push_back (rv);
4533 delete regions;
4539 /** Start with regions that are selected. Then add equivalent regions
4540 * on tracks in the same active edit-enabled route group as any of
4541 * the regions that we started with.
4544 RegionSelection
4545 Editor::get_regions_from_selection ()
4547 return get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
4550 /** Get regions using the following method:
4552 * Make an initial region list using the selected regions, unless
4553 * the edit point is `mouse' and the mouse is over an unselected
4554 * region. In this case, start with just that region.
4556 * Then, make an initial track list of the tracks that these
4557 * regions are on, and if the edit point is not `mouse', add the
4558 * selected tracks.
4560 * Look at this track list and add any other tracks that are on the
4561 * same active edit-enabled route group as one of the initial tracks.
4563 * Finally take the initial region list and add any regions that are
4564 * under the edit point on one of the tracks on the track list to get
4565 * the returned region list.
4567 * The rationale here is that the mouse edit point is special in that
4568 * its position describes both a time and a track; the other edit
4569 * modes only describe a time. Hence if the edit point is `mouse' we
4570 * ignore selected tracks, as we assume the user means something by
4571 * pointing at a particular track. Also in this case we take note of
4572 * the region directly under the edit point, as there is always just one
4573 * (rather than possibly several with non-mouse edit points).
4576 RegionSelection
4577 Editor::get_regions_from_selection_and_edit_point ()
4579 RegionSelection regions;
4581 if (_edit_point == EditAtMouse && entered_regionview && !selection->regions.contains (entered_regionview)) {
4582 regions.add (entered_regionview);
4583 } else {
4584 regions = selection->regions;
4587 TrackViewList tracks;
4589 if (_edit_point != EditAtMouse) {
4590 tracks = selection->tracks;
4593 /* Add any other tracks that have regions that are in the same
4594 edit-activated route group as one of our regions.
4596 for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) {
4598 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4600 if (g && g->is_active() && g->is_edit()) {
4601 tracks.add (axis_views_from_routes (g->route_list()));
4605 if (!tracks.empty()) {
4606 /* now find regions that are at the edit position on those tracks */
4607 framepos_t const where = get_preferred_edit_position ();
4608 get_regions_at (regions, where, tracks);
4611 return regions;
4614 /** Start with regions that are selected, or the entered regionview if none are selected.
4615 * Then add equivalent regions on tracks in the same active edit-enabled route group as any
4616 * of the regions that we started with.
4619 RegionSelection
4620 Editor::get_regions_from_selection_and_entered ()
4622 RegionSelection regions = selection->regions;
4624 if (regions.empty() && entered_regionview) {
4625 regions.add (entered_regionview);
4628 return get_equivalent_regions (regions, ARDOUR::Properties::edit.property_id);
4631 void
4632 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4634 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4636 RouteTimeAxisView* tatv;
4638 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4640 boost::shared_ptr<Playlist> pl;
4641 vector<boost::shared_ptr<Region> > results;
4642 RegionView* marv;
4643 boost::shared_ptr<Track> tr;
4645 if ((tr = tatv->track()) == 0) {
4646 /* bus */
4647 continue;
4650 if ((pl = (tr->playlist())) != 0) {
4651 pl->get_region_list_equivalent_regions (region, results);
4654 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4655 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4656 regions.push_back (marv);
4664 void
4665 Editor::show_rhythm_ferret ()
4667 if (rhythm_ferret == 0) {
4668 rhythm_ferret = new RhythmFerret(*this);
4671 rhythm_ferret->set_session (_session);
4672 rhythm_ferret->show ();
4673 rhythm_ferret->present ();
4676 void
4677 Editor::first_idle ()
4679 MessageDialog* dialog = 0;
4681 if (track_views.size() > 1) {
4682 dialog = new MessageDialog (*this,
4683 string_compose (_("Please wait while %1 loads visual data"), PROGRAM_NAME),
4684 true,
4685 Gtk::MESSAGE_INFO,
4686 Gtk::BUTTONS_NONE);
4687 dialog->present ();
4688 ARDOUR_UI::instance()->flush_pending ();
4691 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4692 (*t)->first_idle();
4695 // first idle adds route children (automation tracks), so we need to redisplay here
4696 _routes->redisplay ();
4698 delete dialog;
4700 _have_idled = true;
4703 gboolean
4704 Editor::_idle_resize (gpointer arg)
4706 return ((Editor*)arg)->idle_resize ();
4709 void
4710 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4712 if (resize_idle_id < 0) {
4713 resize_idle_id = g_idle_add (_idle_resize, this);
4714 _pending_resize_amount = 0;
4717 /* make a note of the smallest resulting height, so that we can clamp the
4718 lower limit at TimeAxisView::hSmall */
4720 int32_t min_resulting = INT32_MAX;
4722 _pending_resize_amount += h;
4723 _pending_resize_view = view;
4725 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4727 if (selection->tracks.contains (_pending_resize_view)) {
4728 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4729 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4733 if (min_resulting < 0) {
4734 min_resulting = 0;
4737 /* clamp */
4738 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4739 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4743 /** Handle pending resizing of tracks */
4744 bool
4745 Editor::idle_resize ()
4747 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4749 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4750 selection->tracks.contains (_pending_resize_view)) {
4752 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4753 if (*i != _pending_resize_view) {
4754 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4759 _pending_resize_amount = 0;
4760 flush_canvas ();
4761 _group_tabs->set_dirty ();
4762 resize_idle_id = -1;
4764 return false;
4767 void
4768 Editor::located ()
4770 ENSURE_GUI_THREAD (*this, &Editor::located);
4772 playhead_cursor->set_position (_session->audible_frame ());
4773 if (_follow_playhead && !_pending_initial_locate) {
4774 reset_x_origin_to_follow_playhead ();
4777 _pending_locate_request = false;
4778 _pending_initial_locate = false;
4781 void
4782 Editor::region_view_added (RegionView *)
4784 _summary->set_dirty ();
4787 void
4788 Editor::region_view_removed ()
4790 _summary->set_dirty ();
4793 TimeAxisView*
4794 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4796 TrackViewList::const_iterator j = track_views.begin ();
4797 while (j != track_views.end()) {
4798 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4799 if (rtv && rtv->route() == r) {
4800 return rtv;
4802 ++j;
4805 return 0;
4809 TrackViewList
4810 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4812 TrackViewList t;
4814 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4815 TimeAxisView* tv = axis_view_from_route (*i);
4816 if (tv) {
4817 t.push_back (tv);
4821 return t;
4825 void
4826 Editor::handle_new_route (RouteList& routes)
4828 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4830 RouteTimeAxisView *rtv;
4831 list<RouteTimeAxisView*> new_views;
4833 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4834 boost::shared_ptr<Route> route = (*x);
4836 if (route->is_hidden() || route->is_monitor()) {
4837 continue;
4840 DataType dt = route->input()->default_type();
4842 if (dt == ARDOUR::DataType::AUDIO) {
4843 rtv = new AudioTimeAxisView (*this, _session, route, *track_canvas);
4844 } else if (dt == ARDOUR::DataType::MIDI) {
4845 rtv = new MidiTimeAxisView (*this, _session, route, *track_canvas);
4846 } else {
4847 throw unknown_type();
4850 new_views.push_back (rtv);
4851 track_views.push_back (rtv);
4853 rtv->effective_gain_display ();
4855 if (internal_editing()) {
4856 rtv->enter_internal_edit_mode ();
4857 } else {
4858 rtv->leave_internal_edit_mode ();
4861 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4862 rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed));
4865 _routes->routes_added (new_views);
4866 _summary->routes_added (new_views);
4868 if (show_editor_mixer_when_tracks_arrive) {
4869 show_editor_mixer (true);
4872 editor_list_button.set_sensitive (true);
4875 void
4876 Editor::timeaxisview_deleted (TimeAxisView *tv)
4878 if (_session && _session->deletion_in_progress()) {
4879 /* the situation is under control */
4880 return;
4883 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
4885 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
4887 _routes->route_removed (tv);
4889 if (tv == entered_track) {
4890 entered_track = 0;
4893 TimeAxisView::Children c = tv->get_child_list ();
4894 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
4895 if (entered_track == i->get()) {
4896 entered_track = 0;
4900 /* remove it from the list of track views */
4902 TrackViewList::iterator i;
4904 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
4905 i = track_views.erase (i);
4908 /* update whatever the current mixer strip is displaying, if revelant */
4910 boost::shared_ptr<Route> route;
4912 if (rtav) {
4913 route = rtav->route ();
4916 if (current_mixer_strip && current_mixer_strip->route() == route) {
4918 TimeAxisView* next_tv;
4920 if (track_views.empty()) {
4921 next_tv = 0;
4922 } else if (i == track_views.end()) {
4923 next_tv = track_views.front();
4924 } else {
4925 next_tv = (*i);
4929 if (next_tv) {
4930 set_selected_mixer_strip (*next_tv);
4931 } else {
4932 /* make the editor mixer strip go away setting the
4933 * button to inactive (which also unticks the menu option)
4936 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
4941 void
4942 Editor::hide_track_in_display (TimeAxisView* tv, bool apply_to_selection)
4944 if (apply_to_selection) {
4945 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ) {
4947 TrackSelection::iterator j = i;
4948 ++j;
4950 hide_track_in_display (*i, false);
4952 i = j;
4954 } else {
4955 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
4957 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
4958 // this will hide the mixer strip
4959 set_selected_mixer_strip (*tv);
4962 _routes->hide_track_in_display (*tv);
4966 bool
4967 Editor::sync_track_view_list_and_routes ()
4969 track_views = TrackViewList (_routes->views ());
4971 _summary->set_dirty ();
4972 _group_tabs->set_dirty ();
4974 return false; // do not call again (until needed)
4977 void
4978 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
4980 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4981 theslot (**i);
4985 /** Find a RouteTimeAxisView by the ID of its route */
4986 RouteTimeAxisView*
4987 Editor::get_route_view_by_route_id (PBD::ID& id) const
4989 RouteTimeAxisView* v;
4991 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
4992 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
4993 if(v->route()->id() == id) {
4994 return v;
4999 return 0;
5002 void
5003 Editor::fit_route_group (RouteGroup *g)
5005 TrackViewList ts = axis_views_from_routes (g->route_list ());
5006 fit_tracks (ts);
5009 void
5010 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5012 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5014 if (r == 0) {
5015 _session->cancel_audition ();
5016 return;
5019 if (_session->is_auditioning()) {
5020 _session->cancel_audition ();
5021 if (r == last_audition_region) {
5022 return;
5026 _session->audition_region (r);
5027 last_audition_region = r;
5031 void
5032 Editor::hide_a_region (boost::shared_ptr<Region> r)
5034 r->set_hidden (true);
5037 void
5038 Editor::show_a_region (boost::shared_ptr<Region> r)
5040 r->set_hidden (false);
5043 void
5044 Editor::audition_region_from_region_list ()
5046 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5049 void
5050 Editor::hide_region_from_region_list ()
5052 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5055 void
5056 Editor::show_region_in_region_list ()
5058 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5061 void
5062 Editor::step_edit_status_change (bool yn)
5064 if (yn) {
5065 start_step_editing ();
5066 } else {
5067 stop_step_editing ();
5071 void
5072 Editor::start_step_editing ()
5074 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5077 void
5078 Editor::stop_step_editing ()
5080 step_edit_connection.disconnect ();
5083 bool
5084 Editor::check_step_edit ()
5086 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5087 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5088 if (mtv) {
5089 mtv->check_step_edit ();
5093 return true; // do it again, till we stop
5096 bool
5097 Editor::scroll_press (Direction dir)
5099 ++_scroll_callbacks;
5101 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5102 /* delay the first auto-repeat */
5103 return true;
5106 switch (dir) {
5107 case LEFT:
5108 scroll_backward (1);
5109 break;
5111 case RIGHT:
5112 scroll_forward (1);
5113 break;
5115 case UP:
5116 scroll_tracks_up_line ();
5117 break;
5119 case DOWN:
5120 scroll_tracks_down_line ();
5121 break;
5124 /* do hacky auto-repeat */
5125 if (!_scroll_connection.connected ()) {
5127 _scroll_connection = Glib::signal_timeout().connect (
5128 sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), dir), 100
5131 _scroll_callbacks = 0;
5134 return true;
5137 void
5138 Editor::scroll_release ()
5140 _scroll_connection.disconnect ();
5143 /** Queue a change for the Editor viewport x origin to follow the playhead */
5144 void
5145 Editor::reset_x_origin_to_follow_playhead ()
5147 framepos_t const frame = playhead_cursor->current_frame;
5149 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5151 if (_session->transport_speed() < 0) {
5153 if (frame > (current_page_frames() / 2)) {
5154 center_screen (frame-(current_page_frames()/2));
5155 } else {
5156 center_screen (current_page_frames()/2);
5159 } else {
5161 if (frame < leftmost_frame) {
5162 /* moving left */
5163 framepos_t l = 0;
5164 if (_session->transport_rolling()) {
5165 /* rolling; end up with the playhead at the right of the page */
5166 l = frame - current_page_frames ();
5167 } else {
5168 /* not rolling: end up with the playhead 3/4 of the way along the page */
5169 l = frame - (3 * current_page_frames() / 4);
5172 if (l < 0) {
5173 l = 0;
5176 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5177 } else {
5178 /* moving right */
5179 if (_session->transport_rolling()) {
5180 /* rolling: end up with the playhead on the left of the page */
5181 center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5182 } else {
5183 /* not rolling: end up with the playhead 1/4 of the way along the page */
5184 center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5191 void
5192 Editor::super_rapid_screen_update ()
5194 if (!_session || !_session->engine().running()) {
5195 return;
5198 /* METERING / MIXER STRIPS */
5200 /* update track meters, if required */
5201 if (is_mapped() && meters_running) {
5202 RouteTimeAxisView* rtv;
5203 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5204 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5205 rtv->fast_update ();
5210 /* and any current mixer strip */
5211 if (current_mixer_strip) {
5212 current_mixer_strip->fast_update ();
5215 /* PLAYHEAD AND VIEWPORT */
5217 framepos_t const frame = _session->audible_frame();
5219 /* There are a few reasons why we might not update the playhead / viewport stuff:
5221 * 1. we don't update things when there's a pending locate request, otherwise
5222 * when the editor requests a locate there is a chance that this method
5223 * will move the playhead before the locate request is processed, causing
5224 * a visual glitch.
5225 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5226 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5229 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5231 last_update_frame = frame;
5233 if (!_dragging_playhead) {
5234 playhead_cursor->set_position (frame);
5237 if (!_stationary_playhead) {
5239 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5240 reset_x_origin_to_follow_playhead ();
5243 } else {
5245 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5246 editor canvas
5248 #if 0
5249 // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5250 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5251 if (target <= 0.0) {
5252 target = 0.0;
5254 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5255 target = (target * 0.15) + (current * 0.85);
5256 } else {
5257 /* relax */
5260 current = target;
5261 set_horizontal_position (current);
5262 #endif
5269 void
5270 Editor::session_going_away ()
5272 _have_idled = false;
5274 _session_connections.drop_connections ();
5276 super_rapid_screen_update_connection.disconnect ();
5278 selection->clear ();
5279 cut_buffer->clear ();
5281 clicked_regionview = 0;
5282 clicked_axisview = 0;
5283 clicked_routeview = 0;
5284 clicked_crossfadeview = 0;
5285 entered_regionview = 0;
5286 entered_track = 0;
5287 last_update_frame = 0;
5288 _drags->abort ();
5290 playhead_cursor->canvas_item.hide ();
5292 /* rip everything out of the list displays */
5294 _regions->clear ();
5295 _routes->clear ();
5296 _route_groups->clear ();
5298 /* do this first so that deleting a track doesn't reset cms to null
5299 and thus cause a leak.
5302 if (current_mixer_strip) {
5303 if (current_mixer_strip->get_parent() != 0) {
5304 global_hpacker.remove (*current_mixer_strip);
5306 delete current_mixer_strip;
5307 current_mixer_strip = 0;
5310 /* delete all trackviews */
5312 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5313 delete *i;
5315 track_views.clear ();
5317 zoom_range_clock->set_session (0);
5318 nudge_clock->set_session (0);
5320 editor_list_button.set_active(false);
5321 editor_list_button.set_sensitive(false);
5323 /* clear tempo/meter rulers */
5324 remove_metric_marks ();
5325 hide_measures ();
5326 clear_marker_display ();
5328 delete current_bbt_points;
5329 current_bbt_points = 0;
5331 /* get rid of any existing editor mixer strip */
5333 WindowTitle title(Glib::get_application_name());
5334 title += _("Editor");
5336 set_title (title.get_string());
5338 SessionHandlePtr::session_going_away ();
5342 void
5343 Editor::show_editor_list (bool yn)
5345 if (yn) {
5346 _the_notebook.show ();
5347 } else {
5348 _the_notebook.hide ();
5352 void
5353 Editor::change_region_layering_order ()
5355 framepos_t const position = get_preferred_edit_position ();
5357 if (!clicked_routeview) {
5358 if (layering_order_editor) {
5359 layering_order_editor->hide ();
5361 return;
5364 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
5366 if (!track) {
5367 return;
5370 boost::shared_ptr<Playlist> pl = track->playlist();
5372 if (!pl) {
5373 return;
5376 if (layering_order_editor == 0) {
5377 layering_order_editor = new RegionLayeringOrderEditor(*this);
5380 layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position);
5381 layering_order_editor->maybe_present ();
5384 void
5385 Editor::update_region_layering_order_editor ()
5387 if (layering_order_editor && layering_order_editor->is_visible ()) {
5388 change_region_layering_order ();
5392 void
5393 Editor::setup_fade_images ()
5395 _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear")));
5396 _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut")));
5397 _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut")));
5398 _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut")));
5399 _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut")));
5401 _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear")));
5402 _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut")));
5403 _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut")));
5404 _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut")));
5405 _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut")));
5409 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
5410 Gtk::MenuItem&
5411 Editor::action_menu_item (std::string const & name)
5413 Glib::RefPtr<Action> a = editor_actions->get_action (name);
5414 assert (a);
5416 return *manage (a->create_menu_item ());
5419 void
5420 Editor::resize_text_widgets ()
5422 set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_FUDGE+10, 15);
5423 set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_FUDGE+10, 15);
5424 set_size_request_to_display_given_text (snap_type_selector, snap_type_strings, COMBO_FUDGE+10, 15);
5425 set_size_request_to_display_given_text (snap_mode_selector, snap_mode_strings, COMBO_FUDGE+10, 15);
5426 set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_FUDGE+10, 15);
5429 void
5430 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
5432 EventBox* b = manage (new EventBox);
5433 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
5434 Label* l = manage (new Label (name));
5435 l->set_angle (-90);
5436 b->add (*l);
5437 b->show_all ();
5438 _the_notebook.append_page (widget, *b);
5441 bool
5442 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
5444 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
5445 _the_notebook.set_current_page (_the_notebook.page_num (*page));
5448 if (ev->type == GDK_2BUTTON_PRESS) {
5450 /* double-click on a notebook tab shrinks or expands the notebook */
5452 if (_notebook_shrunk) {
5453 edit_pane.set_position (pre_maximal_horizontal_pane_position);
5454 _notebook_shrunk = false;
5455 } else {
5456 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
5457 edit_pane.set_position (edit_pane.get_position() + page->get_width());
5458 _notebook_shrunk = true;
5462 return true;