ignore unpaired noteoff's when writing part of a MidiModel to a new source. in realit...
[ardour2.git] / gtk2_ardour / editor.cc
blob3e5a1724684f85da3e3bd771c13527ccc60d9d5b
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, xoff, yoff;
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;
2144 xoff = 0;
2145 yoff = 21;
2147 if ((geometry = find_named_node (node, "geometry")) != 0) {
2149 XMLProperty* prop;
2151 if ((prop = geometry->property("x_size")) == 0) {
2152 prop = geometry->property ("x-size");
2154 if (prop) {
2155 g.base_width = atoi(prop->value());
2157 if ((prop = geometry->property("y_size")) == 0) {
2158 prop = geometry->property ("y-size");
2160 if (prop) {
2161 g.base_height = atoi(prop->value());
2164 if ((prop = geometry->property ("x_pos")) == 0) {
2165 prop = geometry->property ("x-pos");
2167 if (prop) {
2168 x = atoi (prop->value());
2171 if ((prop = geometry->property ("y_pos")) == 0) {
2172 prop = geometry->property ("y-pos");
2174 if (prop) {
2175 y = atoi (prop->value());
2178 if ((prop = geometry->property ("x_off")) == 0) {
2179 prop = geometry->property ("x-off");
2181 if (prop) {
2182 xoff = atoi (prop->value());
2184 if ((prop = geometry->property ("y_off")) == 0) {
2185 prop = geometry->property ("y-off");
2187 if (prop) {
2188 yoff = atoi (prop->value());
2192 set_default_size (g.base_width, g.base_height);
2193 move (x, y);
2195 if (_session && (prop = node.property ("playhead"))) {
2196 framepos_t pos;
2197 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2198 playhead_cursor->set_position (pos);
2199 } else {
2200 playhead_cursor->set_position (0);
2203 if ((prop = node.property ("mixer-width"))) {
2204 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2207 if ((prop = node.property ("zoom-focus"))) {
2208 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2211 if ((prop = node.property ("zoom"))) {
2212 reset_zoom (PBD::atof (prop->value()));
2213 } else {
2214 reset_zoom (frames_per_unit);
2217 if ((prop = node.property ("snap-to"))) {
2218 set_snap_to ((SnapType) atoi (prop->value()));
2221 if ((prop = node.property ("snap-mode"))) {
2222 set_snap_mode ((SnapMode) atoi (prop->value()));
2225 if ((prop = node.property ("mouse-mode"))) {
2226 MouseMode m = str2mousemode(prop->value());
2227 set_mouse_mode (m, true);
2228 } else {
2229 set_mouse_mode (MouseObject, true);
2232 if ((prop = node.property ("left-frame")) != 0) {
2233 framepos_t pos;
2234 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2235 reset_x_origin (pos);
2239 if ((prop = node.property ("y-origin")) != 0) {
2240 reset_y_origin (atof (prop->value ()));
2243 if ((prop = node.property ("internal-edit"))) {
2244 bool yn = string_is_affirmative (prop->value());
2245 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2246 if (act) {
2247 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2248 tact->set_active (!yn);
2249 tact->set_active (yn);
2253 if ((prop = node.property ("join-object-range"))) {
2254 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2257 if ((prop = node.property ("edit-point"))) {
2258 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2261 if ((prop = node.property ("show-measures"))) {
2262 bool yn = string_is_affirmative (prop->value());
2263 _show_measures = yn;
2264 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2265 if (act) {
2266 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2267 /* do it twice to force the change */
2268 tact->set_active (!yn);
2269 tact->set_active (yn);
2273 if ((prop = node.property ("follow-playhead"))) {
2274 bool yn = string_is_affirmative (prop->value());
2275 set_follow_playhead (yn);
2276 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2277 if (act) {
2278 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2279 if (tact->get_active() != yn) {
2280 tact->set_active (yn);
2285 if ((prop = node.property ("stationary-playhead"))) {
2286 bool yn = (prop->value() == "yes");
2287 set_stationary_playhead (yn);
2288 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2289 if (act) {
2290 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2291 if (tact->get_active() != yn) {
2292 tact->set_active (yn);
2297 if ((prop = node.property ("region-list-sort-type"))) {
2298 RegionListSortType st;
2299 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2302 if ((prop = node.property ("xfades-visible"))) {
2303 bool yn = string_is_affirmative (prop->value());
2304 _xfade_visibility = !yn;
2305 // set_xfade_visibility (yn);
2308 if ((prop = node.property ("show-editor-mixer"))) {
2310 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2311 assert (act);
2313 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2314 bool yn = string_is_affirmative (prop->value());
2316 /* do it twice to force the change */
2318 tact->set_active (!yn);
2319 tact->set_active (yn);
2322 if ((prop = node.property ("show-editor-list"))) {
2324 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2325 assert (act);
2327 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2328 bool yn = string_is_affirmative (prop->value());
2330 /* do it twice to force the change */
2332 tact->set_active (!yn);
2333 tact->set_active (yn);
2336 if ((prop = node.property (X_("editor-list-page")))) {
2337 _the_notebook.set_current_page (atoi (prop->value ()));
2340 if ((prop = node.property (X_("show-marker-lines")))) {
2341 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2342 assert (act);
2343 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2344 bool yn = string_is_affirmative (prop->value ());
2346 tact->set_active (!yn);
2347 tact->set_active (yn);
2350 XMLNodeList children = node.children ();
2351 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2352 selection->set_state (**i, Stateful::current_state_version);
2353 _regions->set_state (**i);
2356 return 0;
2359 XMLNode&
2360 Editor::get_state ()
2362 XMLNode* node = new XMLNode ("Editor");
2363 char buf[32];
2365 _id.print (buf, sizeof (buf));
2366 node->add_property ("id", buf);
2368 if (is_realized()) {
2369 Glib::RefPtr<Gdk::Window> win = get_window();
2371 int x, y, xoff, yoff, width, height;
2372 win->get_root_origin(x, y);
2373 win->get_position(xoff, yoff);
2374 win->get_size(width, height);
2376 XMLNode* geometry = new XMLNode ("geometry");
2378 snprintf(buf, sizeof(buf), "%d", width);
2379 geometry->add_property("x-size", string(buf));
2380 snprintf(buf, sizeof(buf), "%d", height);
2381 geometry->add_property("y-size", string(buf));
2382 snprintf(buf, sizeof(buf), "%d", x);
2383 geometry->add_property("x-pos", string(buf));
2384 snprintf(buf, sizeof(buf), "%d", y);
2385 geometry->add_property("y-pos", string(buf));
2386 snprintf(buf, sizeof(buf), "%d", xoff);
2387 geometry->add_property("x-off", string(buf));
2388 snprintf(buf, sizeof(buf), "%d", yoff);
2389 geometry->add_property("y-off", string(buf));
2390 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2391 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2392 geometry->add_property("notebook-shrunk", _notebook_shrunk ? "1" : "0");
2393 snprintf(buf,sizeof(buf), "%d",pre_maximal_horizontal_pane_position);
2394 geometry->add_property("pre-maximal-horizontal-pane-position", string(buf));
2395 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2396 geometry->add_property("edit-vertical-pane-pos", string(buf));
2398 node->add_child_nocopy (*geometry);
2401 maybe_add_mixer_strip_width (*node);
2403 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2404 node->add_property ("zoom-focus", buf);
2405 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2406 node->add_property ("zoom", buf);
2407 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2408 node->add_property ("snap-to", buf);
2409 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2410 node->add_property ("snap-mode", buf);
2412 node->add_property ("edit-point", enum_2_string (_edit_point));
2414 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2415 node->add_property ("playhead", buf);
2416 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2417 node->add_property ("left-frame", buf);
2418 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2419 node->add_property ("y-origin", buf);
2421 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2422 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2423 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2424 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2425 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2426 node->add_property ("mouse-mode", enum2str(mouse_mode));
2427 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2428 node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2430 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2431 if (act) {
2432 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2433 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2436 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2437 if (act) {
2438 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2439 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2442 snprintf (buf, sizeof (buf), "%d", _the_notebook.get_current_page ());
2443 node->add_property (X_("editor-list-page"), buf);
2445 if (button_bindings) {
2446 XMLNode* bb = new XMLNode (X_("Buttons"));
2447 button_bindings->save (*bb);
2448 node->add_child_nocopy (*bb);
2451 node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no");
2453 node->add_child_nocopy (selection->get_state ());
2454 node->add_child_nocopy (_regions->get_state ());
2456 return *node;
2461 /** @param y y offset from the top of all trackviews.
2462 * @return pair: TimeAxisView that y is over, layer index.
2463 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2464 * in stacked region display mode, otherwise 0.
2466 std::pair<TimeAxisView *, layer_t>
2467 Editor::trackview_by_y_position (double y)
2469 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2471 std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
2472 if (r.first) {
2473 return r;
2477 return std::make_pair ( (TimeAxisView *) 0, 0);
2480 /** Snap a position to the grid, if appropriate, taking into account current
2481 * grid settings and also the state of any snap modifier keys that may be pressed.
2482 * @param start Position to snap.
2483 * @param event Event to get current key modifier information from, or 0.
2485 void
2486 Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2488 if (!_session || !event) {
2489 return;
2492 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2493 if (_snap_mode == SnapOff) {
2494 snap_to_internal (start, direction, for_mark);
2496 } else {
2497 if (_snap_mode != SnapOff) {
2498 snap_to_internal (start, direction, for_mark);
2503 void
2504 Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark)
2506 if (!_session || _snap_mode == SnapOff) {
2507 return;
2510 snap_to_internal (start, direction, for_mark);
2513 void
2514 Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /*for_mark*/)
2516 const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2517 framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2519 switch (_snap_type) {
2520 case SnapToTimecodeFrame:
2521 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2522 start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2523 } else {
2524 start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2526 break;
2528 case SnapToTimecodeSeconds:
2529 if (_session->config.get_timecode_offset_negative()) {
2530 start += _session->config.get_timecode_offset ();
2531 } else {
2532 start -= _session->config.get_timecode_offset ();
2534 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2535 start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2536 } else {
2537 start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2540 if (_session->config.get_timecode_offset_negative()) {
2541 start -= _session->config.get_timecode_offset ();
2542 } else {
2543 start += _session->config.get_timecode_offset ();
2545 break;
2547 case SnapToTimecodeMinutes:
2548 if (_session->config.get_timecode_offset_negative()) {
2549 start += _session->config.get_timecode_offset ();
2550 } else {
2551 start -= _session->config.get_timecode_offset ();
2553 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2554 start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2555 } else {
2556 start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2558 if (_session->config.get_timecode_offset_negative()) {
2559 start -= _session->config.get_timecode_offset ();
2560 } else {
2561 start += _session->config.get_timecode_offset ();
2563 break;
2564 default:
2565 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2566 /*NOTREACHED*/
2570 void
2571 Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark)
2573 const framepos_t one_second = _session->frame_rate();
2574 const framepos_t one_minute = _session->frame_rate() * 60;
2575 framepos_t presnap = start;
2576 framepos_t before;
2577 framepos_t after;
2579 switch (_snap_type) {
2580 case SnapToTimecodeFrame:
2581 case SnapToTimecodeSeconds:
2582 case SnapToTimecodeMinutes:
2583 return timecode_snap_to_internal (start, direction, for_mark);
2585 case SnapToCDFrame:
2586 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2587 start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2588 } else {
2589 start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2591 break;
2593 case SnapToSeconds:
2594 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2595 start = (framepos_t) ceil ((double) start / one_second) * one_second;
2596 } else {
2597 start = (framepos_t) floor ((double) start / one_second) * one_second;
2599 break;
2601 case SnapToMinutes:
2602 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2603 start = (framepos_t) ceil ((double) start / one_minute) * one_minute;
2604 } else {
2605 start = (framepos_t) floor ((double) start / one_minute) * one_minute;
2607 break;
2609 case SnapToBar:
2610 start = _session->tempo_map().round_to_bar (start, direction);
2611 break;
2613 case SnapToBeat:
2614 start = _session->tempo_map().round_to_beat (start, direction);
2615 break;
2617 case SnapToBeatDiv32:
2618 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2619 break;
2620 case SnapToBeatDiv28:
2621 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2622 break;
2623 case SnapToBeatDiv24:
2624 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2625 break;
2626 case SnapToBeatDiv20:
2627 start = _session->tempo_map().round_to_beat_subdivision (start, 20, direction);
2628 break;
2629 case SnapToBeatDiv16:
2630 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2631 break;
2632 case SnapToBeatDiv14:
2633 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2634 break;
2635 case SnapToBeatDiv12:
2636 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2637 break;
2638 case SnapToBeatDiv10:
2639 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2640 break;
2641 case SnapToBeatDiv8:
2642 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2643 break;
2644 case SnapToBeatDiv7:
2645 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2646 break;
2647 case SnapToBeatDiv6:
2648 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2649 break;
2650 case SnapToBeatDiv5:
2651 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2652 break;
2653 case SnapToBeatDiv4:
2654 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2655 break;
2656 case SnapToBeatDiv3:
2657 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2658 break;
2659 case SnapToBeatDiv2:
2660 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2661 break;
2663 case SnapToMark:
2664 if (for_mark) {
2665 return;
2668 _session->locations()->marks_either_side (start, before, after);
2670 if (before == max_framepos) {
2671 start = after;
2672 } else if (after == max_framepos) {
2673 start = before;
2674 } else if (before != max_framepos && after != max_framepos) {
2675 /* have before and after */
2676 if ((start - before) < (after - start)) {
2677 start = before;
2678 } else {
2679 start = after;
2683 break;
2685 case SnapToRegionStart:
2686 case SnapToRegionEnd:
2687 case SnapToRegionSync:
2688 case SnapToRegionBoundary:
2689 if (!region_boundary_cache.empty()) {
2691 vector<framepos_t>::iterator prev = region_boundary_cache.end ();
2692 vector<framepos_t>::iterator next = region_boundary_cache.end ();
2694 if (direction > 0) {
2695 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2696 } else {
2697 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2700 if (next != region_boundary_cache.begin ()) {
2701 prev = next;
2702 prev--;
2705 framepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2706 framepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2708 if (start > (p + n) / 2) {
2709 start = n;
2710 } else {
2711 start = p;
2714 break;
2717 switch (_snap_mode) {
2718 case SnapNormal:
2719 return;
2721 case SnapMagnetic:
2723 if (presnap > start) {
2724 if (presnap > (start + unit_to_frame(snap_threshold))) {
2725 start = presnap;
2728 } else if (presnap < start) {
2729 if (presnap < (start - unit_to_frame(snap_threshold))) {
2730 start = presnap;
2734 default:
2735 /* handled at entry */
2736 return;
2742 void
2743 Editor::setup_toolbar ()
2745 HBox* mode_box = manage(new HBox);
2746 mode_box->set_border_width (2);
2747 mode_box->set_spacing(4);
2749 /* table containing mode buttons */
2751 HBox* mouse_mode_button_box = manage (new HBox ());
2753 if (Profile->get_sae()) {
2754 mouse_mode_button_box->pack_start (mouse_move_button);
2755 } else {
2756 mouse_mode_button_box->pack_start (mouse_move_button);
2757 mouse_mode_button_box->pack_start (join_object_range_button);
2758 mouse_mode_button_box->pack_start (mouse_select_button);
2761 mouse_mode_button_box->pack_start (mouse_zoom_button);
2763 if (!Profile->get_sae()) {
2764 mouse_mode_button_box->pack_start (mouse_gain_button);
2767 mouse_mode_button_box->pack_start (mouse_timefx_button);
2768 mouse_mode_button_box->pack_start (mouse_audition_button);
2769 mouse_mode_button_box->pack_start (internal_edit_button);
2771 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2772 if (!Profile->get_sae()) {
2773 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2775 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2777 edit_mode_selector.set_name ("EditModeSelector");
2778 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2779 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2781 mode_box->pack_start (edit_mode_selector);
2782 mode_box->pack_start (*mouse_mode_button_box);
2784 _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2785 _mouse_mode_tearoff->set_name ("MouseModeBase");
2786 _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2788 if (Profile->get_sae()) {
2789 _mouse_mode_tearoff->set_can_be_torn_off (false);
2792 _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2793 &_mouse_mode_tearoff->tearoff_window()));
2794 _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2795 &_mouse_mode_tearoff->tearoff_window(), 1));
2796 _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2797 &_mouse_mode_tearoff->tearoff_window()));
2798 _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2799 &_mouse_mode_tearoff->tearoff_window(), 1));
2801 mouse_move_button.set_mode (false);
2802 mouse_select_button.set_mode (false);
2803 mouse_gain_button.set_mode (false);
2804 mouse_zoom_button.set_mode (false);
2805 mouse_timefx_button.set_mode (false);
2806 mouse_audition_button.set_mode (false);
2807 join_object_range_button.set_mode (false);
2809 mouse_move_button.set_name ("MouseModeButton");
2810 mouse_select_button.set_name ("MouseModeButton");
2811 mouse_gain_button.set_name ("MouseModeButton");
2812 mouse_zoom_button.set_name ("MouseModeButton");
2813 mouse_timefx_button.set_name ("MouseModeButton");
2814 mouse_audition_button.set_name ("MouseModeButton");
2815 internal_edit_button.set_name ("MouseModeButton");
2816 join_object_range_button.set_name ("MouseModeButton");
2818 mouse_move_button.unset_flags (CAN_FOCUS);
2819 mouse_select_button.unset_flags (CAN_FOCUS);
2820 mouse_gain_button.unset_flags (CAN_FOCUS);
2821 mouse_zoom_button.unset_flags (CAN_FOCUS);
2822 mouse_timefx_button.unset_flags (CAN_FOCUS);
2823 mouse_audition_button.unset_flags (CAN_FOCUS);
2824 internal_edit_button.unset_flags (CAN_FOCUS);
2825 join_object_range_button.unset_flags (CAN_FOCUS);
2827 /* Zoom */
2829 _zoom_box.set_spacing (1);
2830 _zoom_box.set_border_width (0);
2832 zoom_in_button.set_name ("EditorTimeButton");
2833 zoom_in_button.set_image (*(manage (new Image (::get_icon ("zoom_in")))));
2834 zoom_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), false));
2836 zoom_out_button.set_name ("EditorTimeButton");
2837 zoom_out_button.set_image (*(manage (new Image (::get_icon ("zoom_out")))));
2838 zoom_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), true));
2840 zoom_out_full_button.set_name ("EditorTimeButton");
2841 zoom_out_full_button.set_image (*(manage (new Image (::get_icon ("zoom_full")))));
2842 zoom_out_full_button.signal_clicked().connect (sigc::mem_fun(*this, &Editor::temporal_zoom_session));
2844 zoom_focus_selector.set_name ("ZoomFocusSelector");
2845 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
2846 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2848 _zoom_box.pack_start (zoom_out_button, false, false);
2849 _zoom_box.pack_start (zoom_in_button, false, false);
2850 _zoom_box.pack_start (zoom_out_full_button, false, false);
2852 _zoom_box.pack_start (zoom_focus_selector);
2854 /* Track zoom buttons */
2855 tav_expand_button.set_name ("TrackHeightButton");
2856 tav_expand_button.set_size_request (-1, 20);
2857 tav_expand_button.add (*(manage (new Image (::get_icon ("tav_exp")))));
2858 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("expand-tracks"));
2859 act->connect_proxy (tav_expand_button);
2861 tav_shrink_button.set_name ("TrackHeightButton");
2862 tav_shrink_button.set_size_request (-1, 20);
2863 tav_shrink_button.add (*(manage (new Image (::get_icon ("tav_shrink")))));
2864 act = ActionManager::get_action (X_("Editor"), X_("shrink-tracks"));
2865 act->connect_proxy (tav_shrink_button);
2867 _zoom_box.pack_start (tav_shrink_button);
2868 _zoom_box.pack_start (tav_expand_button);
2870 _zoom_tearoff = manage (new TearOff (_zoom_box));
2872 _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2873 &_zoom_tearoff->tearoff_window()));
2874 _zoom_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2875 &_zoom_tearoff->tearoff_window(), 0));
2876 _zoom_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2877 &_zoom_tearoff->tearoff_window()));
2878 _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2879 &_zoom_tearoff->tearoff_window(), 0));
2881 snap_box.set_spacing (1);
2882 snap_box.set_border_width (2);
2884 snap_type_selector.set_name ("SnapTypeSelector");
2885 set_popdown_strings (snap_type_selector, snap_type_strings, true);
2886 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2888 snap_mode_selector.set_name ("SnapModeSelector");
2889 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
2890 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2892 edit_point_selector.set_name ("EditPointSelector");
2893 set_popdown_strings (edit_point_selector, edit_point_strings, true);
2894 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2896 snap_box.pack_start (snap_mode_selector, false, false);
2897 snap_box.pack_start (snap_type_selector, false, false);
2898 snap_box.pack_start (edit_point_selector, false, false);
2900 /* Nudge */
2902 HBox *nudge_box = manage (new HBox);
2903 nudge_box->set_spacing(1);
2904 nudge_box->set_border_width (2);
2906 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2907 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2909 nudge_box->pack_start (nudge_backward_button, false, false);
2910 nudge_box->pack_start (nudge_forward_button, false, false);
2911 nudge_box->pack_start (*nudge_clock, false, false);
2914 /* Pack everything in... */
2916 HBox* hbox = manage (new HBox);
2917 hbox->set_spacing(10);
2919 _tools_tearoff = manage (new TearOff (*hbox));
2920 _tools_tearoff->set_name ("MouseModeBase");
2921 _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
2923 if (Profile->get_sae()) {
2924 _tools_tearoff->set_can_be_torn_off (false);
2927 _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2928 &_tools_tearoff->tearoff_window()));
2929 _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2930 &_tools_tearoff->tearoff_window(), 0));
2931 _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2932 &_tools_tearoff->tearoff_window()));
2933 _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2934 &_tools_tearoff->tearoff_window(), 0));
2936 toolbar_hbox.set_spacing (10);
2937 toolbar_hbox.set_border_width (1);
2939 toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
2940 toolbar_hbox.pack_start (*_zoom_tearoff, false, false);
2941 toolbar_hbox.pack_start (*_tools_tearoff, false, false);
2943 hbox->pack_start (snap_box, false, false);
2944 if (!Profile->get_small_screen()) {
2945 hbox->pack_start (*nudge_box, false, false);
2946 } else {
2947 ARDOUR_UI::instance()->editor_transport_box().pack_start (*nudge_box, false, false);
2949 hbox->pack_start (panic_box, false, false);
2951 hbox->show_all ();
2953 toolbar_base.set_name ("ToolBarBase");
2954 toolbar_base.add (toolbar_hbox);
2956 _toolbar_viewport.add (toolbar_base);
2957 /* stick to the required height but allow width to vary if there's not enough room */
2958 _toolbar_viewport.set_size_request (1, -1);
2960 toolbar_frame.set_shadow_type (SHADOW_OUT);
2961 toolbar_frame.set_name ("BaseFrame");
2962 toolbar_frame.add (_toolbar_viewport);
2964 DPIReset.connect (sigc::mem_fun (*this, &Editor::resize_text_widgets));
2967 void
2968 Editor::setup_tooltips ()
2970 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
2971 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain"));
2972 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
2973 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
2974 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2975 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
2976 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
2977 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
2978 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
2979 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
2980 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
2981 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
2982 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
2983 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
2984 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
2985 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
2986 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
2987 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
2988 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
2989 ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
2990 ARDOUR_UI::instance()->set_tip (midi_panic_button, _("Send note off and reset controller messages on all MIDI channels"));
2991 ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode"));
2994 void
2995 Editor::midi_panic ()
2997 cerr << "MIDI panic\n";
2999 if (_session) {
3000 _session->midi_panic();
3004 void
3005 Editor::setup_midi_toolbar ()
3007 RefPtr<Action> act;
3009 /* Midi sound notes */
3010 midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
3011 midi_sound_notes.unset_flags (CAN_FOCUS);
3013 /* Panic */
3015 act = ActionManager::get_action (X_("MIDI"), X_("panic"));
3016 midi_panic_button.set_name("MidiPanicButton");
3017 act->connect_proxy (midi_panic_button);
3019 panic_box.pack_start (midi_sound_notes , true, true);
3020 panic_box.pack_start (midi_panic_button, true, true);
3024 Editor::convert_drop_to_paths (
3025 vector<string>& paths,
3026 const RefPtr<Gdk::DragContext>& /*context*/,
3027 gint /*x*/,
3028 gint /*y*/,
3029 const SelectionData& data,
3030 guint /*info*/,
3031 guint /*time*/)
3033 if (_session == 0) {
3034 return -1;
3037 vector<string> uris = data.get_uris();
3039 if (uris.empty()) {
3041 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3042 are actually URI lists. So do it by hand.
3045 if (data.get_target() != "text/plain") {
3046 return -1;
3049 /* Parse the "uri-list" format that Nautilus provides,
3050 where each pathname is delimited by \r\n.
3052 THERE MAY BE NO NULL TERMINATING CHAR!!!
3055 string txt = data.get_text();
3056 const char* p;
3057 const char* q;
3059 p = (const char *) malloc (txt.length() + 1);
3060 txt.copy ((char *) p, txt.length(), 0);
3061 ((char*)p)[txt.length()] = '\0';
3063 while (p)
3065 if (*p != '#')
3067 while (g_ascii_isspace (*p))
3068 p++;
3070 q = p;
3071 while (*q && (*q != '\n') && (*q != '\r')) {
3072 q++;
3075 if (q > p)
3077 q--;
3078 while (q > p && g_ascii_isspace (*q))
3079 q--;
3081 if (q > p)
3083 uris.push_back (string (p, q - p + 1));
3087 p = strchr (p, '\n');
3088 if (p)
3089 p++;
3092 free ((void*)p);
3094 if (uris.empty()) {
3095 return -1;
3099 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3101 if ((*i).substr (0,7) == "file://") {
3103 string p = *i;
3104 PBD::url_decode (p);
3106 // scan forward past three slashes
3108 string::size_type slashcnt = 0;
3109 string::size_type n = 0;
3110 string::iterator x = p.begin();
3112 while (slashcnt < 3 && x != p.end()) {
3113 if ((*x) == '/') {
3114 slashcnt++;
3115 } else if (slashcnt == 3) {
3116 break;
3118 ++n;
3119 ++x;
3122 if (slashcnt != 3 || x == p.end()) {
3123 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3124 continue;
3127 paths.push_back (p.substr (n - 1));
3131 return 0;
3134 void
3135 Editor::new_tempo_section ()
3140 void
3141 Editor::map_transport_state ()
3143 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3145 if (_session && _session->transport_stopped()) {
3146 have_pending_keyboard_selection = false;
3149 update_loop_range_view (true);
3152 /* UNDO/REDO */
3154 Editor::State::State (PublicEditor const * e)
3156 selection = new Selection (e);
3159 Editor::State::~State ()
3161 delete selection;
3164 void
3165 Editor::begin_reversible_command (string name)
3167 if (_session) {
3168 _session->begin_reversible_command (name);
3172 void
3173 Editor::begin_reversible_command (GQuark q)
3175 if (_session) {
3176 _session->begin_reversible_command (q);
3180 void
3181 Editor::commit_reversible_command ()
3183 if (_session) {
3184 _session->commit_reversible_command ();
3188 void
3189 Editor::history_changed ()
3191 string label;
3193 if (undo_action && _session) {
3194 if (_session->undo_depth() == 0) {
3195 label = _("Undo");
3196 } else {
3197 label = string_compose(_("Undo (%1)"), _session->next_undo());
3199 undo_action->property_label() = label;
3202 if (redo_action && _session) {
3203 if (_session->redo_depth() == 0) {
3204 label = _("Redo");
3205 } else {
3206 label = string_compose(_("Redo (%1)"), _session->next_redo());
3208 redo_action->property_label() = label;
3212 void
3213 Editor::duplicate_dialog (bool with_dialog)
3215 float times = 1.0f;
3217 if (mouse_mode == MouseRange) {
3218 if (selection->time.length() == 0) {
3219 return;
3223 RegionSelection rs = get_regions_from_selection_and_entered ();
3225 if (mouse_mode != MouseRange && rs.empty()) {
3226 return;
3229 if (with_dialog) {
3231 ArdourDialog win (_("Duplicate"));
3232 Label label (_("Number of duplications:"));
3233 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3234 SpinButton spinner (adjustment, 0.0, 1);
3235 HBox hbox;
3237 win.get_vbox()->set_spacing (12);
3238 win.get_vbox()->pack_start (hbox);
3239 hbox.set_border_width (6);
3240 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3242 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3243 place, visually. so do this by hand.
3246 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3247 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3248 spinner.grab_focus();
3250 hbox.show ();
3251 label.show ();
3252 spinner.show ();
3254 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3255 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3256 win.set_default_response (RESPONSE_ACCEPT);
3258 win.set_position (WIN_POS_MOUSE);
3260 spinner.grab_focus ();
3262 switch (win.run ()) {
3263 case RESPONSE_ACCEPT:
3264 break;
3265 default:
3266 return;
3269 times = adjustment.get_value();
3272 if (mouse_mode == MouseRange) {
3273 duplicate_selection (times);
3274 } else {
3275 duplicate_some_regions (rs, times);
3279 void
3280 Editor::set_edit_mode (EditMode m)
3282 Config->set_edit_mode (m);
3285 void
3286 Editor::cycle_edit_mode ()
3288 switch (Config->get_edit_mode()) {
3289 case Slide:
3290 if (Profile->get_sae()) {
3291 Config->set_edit_mode (Lock);
3292 } else {
3293 Config->set_edit_mode (Splice);
3295 break;
3296 case Splice:
3297 Config->set_edit_mode (Lock);
3298 break;
3299 case Lock:
3300 Config->set_edit_mode (Slide);
3301 break;
3305 void
3306 Editor::edit_mode_selection_done ()
3308 string s = edit_mode_selector.get_active_text ();
3310 if (!s.empty()) {
3311 Config->set_edit_mode (string_to_edit_mode (s));
3315 void
3316 Editor::snap_type_selection_done ()
3318 string choice = snap_type_selector.get_active_text();
3319 SnapType snaptype = SnapToBeat;
3321 if (choice == _("Beats/2")) {
3322 snaptype = SnapToBeatDiv2;
3323 } else if (choice == _("Beats/3")) {
3324 snaptype = SnapToBeatDiv3;
3325 } else if (choice == _("Beats/4")) {
3326 snaptype = SnapToBeatDiv4;
3327 } else if (choice == _("Beats/5")) {
3328 snaptype = SnapToBeatDiv5;
3329 } else if (choice == _("Beats/6")) {
3330 snaptype = SnapToBeatDiv6;
3331 } else if (choice == _("Beats/7")) {
3332 snaptype = SnapToBeatDiv7;
3333 } else if (choice == _("Beats/8")) {
3334 snaptype = SnapToBeatDiv8;
3335 } else if (choice == _("Beats/10")) {
3336 snaptype = SnapToBeatDiv10;
3337 } else if (choice == _("Beats/12")) {
3338 snaptype = SnapToBeatDiv12;
3339 } else if (choice == _("Beats/14")) {
3340 snaptype = SnapToBeatDiv14;
3341 } else if (choice == _("Beats/16")) {
3342 snaptype = SnapToBeatDiv16;
3343 } else if (choice == _("Beats/20")) {
3344 snaptype = SnapToBeatDiv20;
3345 } else if (choice == _("Beats/24")) {
3346 snaptype = SnapToBeatDiv24;
3347 } else if (choice == _("Beats/28")) {
3348 snaptype = SnapToBeatDiv28;
3349 } else if (choice == _("Beats/32")) {
3350 snaptype = SnapToBeatDiv32;
3351 } else if (choice == _("Beats")) {
3352 snaptype = SnapToBeat;
3353 } else if (choice == _("Bars")) {
3354 snaptype = SnapToBar;
3355 } else if (choice == _("Marks")) {
3356 snaptype = SnapToMark;
3357 } else if (choice == _("Region starts")) {
3358 snaptype = SnapToRegionStart;
3359 } else if (choice == _("Region ends")) {
3360 snaptype = SnapToRegionEnd;
3361 } else if (choice == _("Region bounds")) {
3362 snaptype = SnapToRegionBoundary;
3363 } else if (choice == _("Region syncs")) {
3364 snaptype = SnapToRegionSync;
3365 } else if (choice == _("CD Frames")) {
3366 snaptype = SnapToCDFrame;
3367 } else if (choice == _("Timecode Frames")) {
3368 snaptype = SnapToTimecodeFrame;
3369 } else if (choice == _("Timecode Seconds")) {
3370 snaptype = SnapToTimecodeSeconds;
3371 } else if (choice == _("Timecode Minutes")) {
3372 snaptype = SnapToTimecodeMinutes;
3373 } else if (choice == _("Seconds")) {
3374 snaptype = SnapToSeconds;
3375 } else if (choice == _("Minutes")) {
3376 snaptype = SnapToMinutes;
3379 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3380 if (ract) {
3381 ract->set_active ();
3385 void
3386 Editor::snap_mode_selection_done ()
3388 string choice = snap_mode_selector.get_active_text();
3389 SnapMode mode = SnapNormal;
3391 if (choice == _("No Grid")) {
3392 mode = SnapOff;
3393 } else if (choice == _("Grid")) {
3394 mode = SnapNormal;
3395 } else if (choice == _("Magnetic")) {
3396 mode = SnapMagnetic;
3399 RefPtr<RadioAction> ract = snap_mode_action (mode);
3401 if (ract) {
3402 ract->set_active (true);
3406 void
3407 Editor::cycle_edit_point (bool with_marker)
3409 switch (_edit_point) {
3410 case EditAtMouse:
3411 set_edit_point_preference (EditAtPlayhead);
3412 break;
3413 case EditAtPlayhead:
3414 if (with_marker) {
3415 set_edit_point_preference (EditAtSelectedMarker);
3416 } else {
3417 set_edit_point_preference (EditAtMouse);
3419 break;
3420 case EditAtSelectedMarker:
3421 set_edit_point_preference (EditAtMouse);
3422 break;
3426 void
3427 Editor::edit_point_selection_done ()
3429 string choice = edit_point_selector.get_active_text();
3430 EditPoint ep = EditAtSelectedMarker;
3432 if (choice == _("Marker")) {
3433 set_edit_point_preference (EditAtSelectedMarker);
3434 } else if (choice == _("Playhead")) {
3435 set_edit_point_preference (EditAtPlayhead);
3436 } else {
3437 set_edit_point_preference (EditAtMouse);
3440 RefPtr<RadioAction> ract = edit_point_action (ep);
3442 if (ract) {
3443 ract->set_active (true);
3447 void
3448 Editor::zoom_focus_selection_done ()
3450 string choice = zoom_focus_selector.get_active_text();
3451 ZoomFocus focus_type = ZoomFocusLeft;
3453 if (choice == _("Left")) {
3454 focus_type = ZoomFocusLeft;
3455 } else if (choice == _("Right")) {
3456 focus_type = ZoomFocusRight;
3457 } else if (choice == _("Center")) {
3458 focus_type = ZoomFocusCenter;
3459 } else if (choice == _("Playhead")) {
3460 focus_type = ZoomFocusPlayhead;
3461 } else if (choice == _("Mouse")) {
3462 focus_type = ZoomFocusMouse;
3463 } else if (choice == _("Edit point")) {
3464 focus_type = ZoomFocusEdit;
3467 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3469 if (ract) {
3470 ract->set_active ();
3474 bool
3475 Editor::edit_controls_button_release (GdkEventButton* ev)
3477 if (Keyboard::is_context_menu_event (ev)) {
3478 ARDOUR_UI::instance()->add_route (this);
3479 } else if (ev->button == 1) {
3480 selection->clear_tracks ();
3483 return true;
3486 bool
3487 Editor::mouse_select_button_release (GdkEventButton* ev)
3489 /* this handles just right-clicks */
3491 if (ev->button != 3) {
3492 return false;
3495 return true;
3498 void
3499 Editor::set_zoom_focus (ZoomFocus f)
3501 string str = zoom_focus_strings[(int)f];
3503 if (str != zoom_focus_selector.get_active_text()) {
3504 zoom_focus_selector.set_active_text (str);
3507 if (zoom_focus != f) {
3508 zoom_focus = f;
3509 instant_save ();
3513 void
3514 Editor::ensure_float (Window& win)
3516 win.set_transient_for (*this);
3519 void
3520 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3522 /* recover or initialize pane positions. do this here rather than earlier because
3523 we don't want the positions to change the child allocations, which they seem to do.
3526 int pos;
3527 XMLProperty* prop;
3528 char buf[32];
3529 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3530 int width, height;
3532 enum Pane {
3533 Horizontal = 0x1,
3534 Vertical = 0x2
3537 static Pane done;
3539 XMLNode* geometry;
3541 width = default_width;
3542 height = default_height;
3544 if ((geometry = find_named_node (*node, "geometry")) != 0) {
3546 prop = geometry->property ("x-size");
3547 if (prop) {
3548 width = atoi (prop->value());
3550 prop = geometry->property ("y-size");
3551 if (prop) {
3552 height = atoi (prop->value());
3556 if (which == static_cast<Paned*> (&edit_pane)) {
3558 if (done & Horizontal) {
3559 return;
3562 if (geometry && (prop = geometry->property ("notebook-shrunk"))) {
3563 _notebook_shrunk = string_is_affirmative (prop->value ());
3566 if (geometry && (prop = geometry->property ("pre-maximal-horizontal-pane-position"))) {
3567 pre_maximal_horizontal_pane_position = atoi (prop->value ());
3570 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3571 /* initial allocation is 90% to canvas, 10% to notebook */
3572 pos = (int) floor (alloc.get_width() * 0.90f);
3573 snprintf (buf, sizeof(buf), "%d", pos);
3574 } else {
3575 pos = atoi (prop->value());
3578 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3579 edit_pane.set_position (pos);
3580 if (pre_maximal_horizontal_pane_position == 0) {
3581 pre_maximal_horizontal_pane_position = pos;
3585 done = (Pane) (done | Horizontal);
3587 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3589 if (done & Vertical) {
3590 return;
3593 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3594 /* initial allocation is 90% to canvas, 10% to summary */
3595 pos = (int) floor (alloc.get_height() * 0.90f);
3596 snprintf (buf, sizeof(buf), "%d", pos);
3597 } else {
3598 pos = atoi (prop->value());
3601 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3602 editor_summary_pane.set_position (pos);
3603 pre_maximal_vertical_pane_position = pos;
3606 done = (Pane) (done | Vertical);
3610 void
3611 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3613 if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) {
3614 top_hbox.remove (toolbar_frame);
3618 void
3619 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3621 if (toolbar_frame.get_parent() == 0) {
3622 top_hbox.pack_end (toolbar_frame);
3626 void
3627 Editor::set_show_measures (bool yn)
3629 if (_show_measures != yn) {
3630 hide_measures ();
3632 if ((_show_measures = yn) == true) {
3633 if (tempo_lines)
3634 tempo_lines->show();
3635 draw_measures ();
3637 instant_save ();
3641 void
3642 Editor::toggle_follow_playhead ()
3644 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3645 if (act) {
3646 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3647 set_follow_playhead (tact->get_active());
3651 /** @param yn true to follow playhead, otherwise false.
3652 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
3654 void
3655 Editor::set_follow_playhead (bool yn, bool catch_up)
3657 if (_follow_playhead != yn) {
3658 if ((_follow_playhead = yn) == true && catch_up) {
3659 /* catch up */
3660 reset_x_origin_to_follow_playhead ();
3662 instant_save ();
3666 void
3667 Editor::toggle_stationary_playhead ()
3669 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3670 if (act) {
3671 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3672 set_stationary_playhead (tact->get_active());
3676 void
3677 Editor::set_stationary_playhead (bool yn)
3679 if (_stationary_playhead != yn) {
3680 if ((_stationary_playhead = yn) == true) {
3681 /* catch up */
3682 // FIXME need a 3.0 equivalent of this 2.X call
3683 // update_current_screen ();
3685 instant_save ();
3689 void
3690 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3692 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3693 if (xfade) {
3694 xfade->set_active (!xfade->active());
3698 void
3699 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3701 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3702 if (xfade) {
3703 xfade->set_follow_overlap (!xfade->following_overlap());
3707 void
3708 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3710 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3712 if (!xfade) {
3713 return;
3716 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3718 ensure_float (cew);
3720 switch (cew.run ()) {
3721 case RESPONSE_ACCEPT:
3722 break;
3723 default:
3724 return;
3727 cew.apply ();
3728 PropertyChange all_crossfade_properties;
3729 all_crossfade_properties.add (ARDOUR::Properties::active);
3730 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3731 xfade->PropertyChanged (all_crossfade_properties);
3734 PlaylistSelector&
3735 Editor::playlist_selector () const
3737 return *_playlist_selector;
3740 Evoral::MusicalTime
3741 Editor::get_grid_type_as_beats (bool& success, framepos_t position)
3743 success = true;
3745 switch (_snap_type) {
3746 case SnapToBeat:
3747 return 1.0;
3748 break;
3750 case SnapToBeatDiv32:
3751 return 1.0/32.0;
3752 break;
3753 case SnapToBeatDiv28:
3754 return 1.0/28.0;
3755 break;
3756 case SnapToBeatDiv24:
3757 return 1.0/24.0;
3758 break;
3759 case SnapToBeatDiv20:
3760 return 1.0/20.0;
3761 break;
3762 case SnapToBeatDiv16:
3763 return 1.0/16.0;
3764 break;
3765 case SnapToBeatDiv14:
3766 return 1.0/14.0;
3767 break;
3768 case SnapToBeatDiv12:
3769 return 1.0/12.0;
3770 break;
3771 case SnapToBeatDiv10:
3772 return 1.0/10.0;
3773 break;
3774 case SnapToBeatDiv8:
3775 return 1.0/8.0;
3776 break;
3777 case SnapToBeatDiv7:
3778 return 1.0/7.0;
3779 break;
3780 case SnapToBeatDiv6:
3781 return 1.0/6.0;
3782 break;
3783 case SnapToBeatDiv5:
3784 return 1.0/5.0;
3785 break;
3786 case SnapToBeatDiv4:
3787 return 1.0/4.0;
3788 break;
3789 case SnapToBeatDiv3:
3790 return 1.0/3.0;
3791 break;
3792 case SnapToBeatDiv2:
3793 return 1.0/2.0;
3794 break;
3796 case SnapToBar:
3797 if (_session) {
3798 return _session->tempo_map().meter_at (position).beats_per_bar();
3800 break;
3802 case SnapToCDFrame:
3803 case SnapToTimecodeFrame:
3804 case SnapToTimecodeSeconds:
3805 case SnapToTimecodeMinutes:
3806 case SnapToSeconds:
3807 case SnapToMinutes:
3808 case SnapToRegionStart:
3809 case SnapToRegionEnd:
3810 case SnapToRegionSync:
3811 case SnapToRegionBoundary:
3812 default:
3813 success = false;
3814 break;
3817 return 0.0;
3820 framecnt_t
3821 Editor::get_nudge_distance (framepos_t pos, framecnt_t& next)
3823 framecnt_t ret;
3825 ret = nudge_clock->current_duration (pos);
3826 next = ret + 1; /* XXXX fix me */
3828 return ret;
3832 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3834 ArdourDialog dialog (_("Playlist Deletion"));
3835 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3836 "If it is kept, its audio files will not be cleaned.\n"
3837 "If it is deleted, audio files used by it alone will be cleaned."),
3838 pl->name()));
3840 dialog.set_position (WIN_POS_CENTER);
3841 dialog.get_vbox()->pack_start (label);
3843 label.show ();
3845 dialog.add_button (_("Delete Playlist"), RESPONSE_ACCEPT);
3846 dialog.add_button (_("Keep Playlist"), RESPONSE_REJECT);
3847 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3849 switch (dialog.run ()) {
3850 case RESPONSE_ACCEPT:
3851 /* delete the playlist */
3852 return 0;
3853 break;
3855 case RESPONSE_REJECT:
3856 /* keep the playlist */
3857 return 1;
3858 break;
3860 default:
3861 break;
3864 return -1;
3867 bool
3868 Editor::audio_region_selection_covers (framepos_t where)
3870 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3871 if ((*a)->region()->covers (where)) {
3872 return true;
3876 return false;
3879 void
3880 Editor::prepare_for_cleanup ()
3882 cut_buffer->clear_regions ();
3883 cut_buffer->clear_playlists ();
3885 selection->clear_regions ();
3886 selection->clear_playlists ();
3888 _regions->suspend_redisplay ();
3891 void
3892 Editor::finish_cleanup ()
3894 _regions->resume_redisplay ();
3897 Location*
3898 Editor::transport_loop_location()
3900 if (_session) {
3901 return _session->locations()->auto_loop_location();
3902 } else {
3903 return 0;
3907 Location*
3908 Editor::transport_punch_location()
3910 if (_session) {
3911 return _session->locations()->auto_punch_location();
3912 } else {
3913 return 0;
3917 bool
3918 Editor::control_layout_scroll (GdkEventScroll* ev)
3920 if (Keyboard::some_magic_widget_has_focus()) {
3921 return false;
3924 switch (ev->direction) {
3925 case GDK_SCROLL_UP:
3926 scroll_tracks_up_line ();
3927 return true;
3928 break;
3930 case GDK_SCROLL_DOWN:
3931 scroll_tracks_down_line ();
3932 return true;
3934 default:
3935 /* no left/right handling yet */
3936 break;
3939 return false;
3942 void
3943 Editor::session_state_saved (string)
3945 update_title ();
3946 _snapshots->redisplay ();
3949 void
3950 Editor::maximise_editing_space ()
3952 _mouse_mode_tearoff->set_visible (false);
3953 _tools_tearoff->set_visible (false);
3954 _zoom_tearoff->set_visible (false);
3956 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
3957 pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
3958 pre_maximal_editor_width = this->get_width ();
3959 pre_maximal_editor_height = this->get_height ();
3961 if (post_maximal_horizontal_pane_position == 0) {
3962 post_maximal_horizontal_pane_position = edit_pane.get_width();
3965 if (post_maximal_vertical_pane_position == 0) {
3966 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
3969 fullscreen ();
3971 if (post_maximal_editor_width) {
3972 edit_pane.set_position (post_maximal_horizontal_pane_position -
3973 abs(post_maximal_editor_width - pre_maximal_editor_width));
3974 } else {
3975 edit_pane.set_position (post_maximal_horizontal_pane_position);
3978 if (post_maximal_editor_height) {
3979 editor_summary_pane.set_position (post_maximal_vertical_pane_position -
3980 abs(post_maximal_editor_height - pre_maximal_editor_height));
3981 } else {
3982 editor_summary_pane.set_position (post_maximal_vertical_pane_position);
3985 if (Config->get_keep_tearoffs()) {
3986 _mouse_mode_tearoff->set_visible (true);
3987 _tools_tearoff->set_visible (true);
3988 if (Config->get_show_zoom_tools ()) {
3989 _zoom_tearoff->set_visible (true);
3995 void
3996 Editor::restore_editing_space ()
3998 // user changed width/height of panes during fullscreen
4000 if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
4001 post_maximal_horizontal_pane_position = edit_pane.get_position();
4004 if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
4005 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
4008 unfullscreen();
4010 _mouse_mode_tearoff->set_visible (true);
4011 _tools_tearoff->set_visible (true);
4012 if (Config->get_show_zoom_tools ()) {
4013 _zoom_tearoff->set_visible (true);
4015 post_maximal_editor_width = this->get_width();
4016 post_maximal_editor_height = this->get_height();
4018 edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
4019 editor_summary_pane.set_position (pre_maximal_vertical_pane_position + abs(this->get_height() - pre_maximal_editor_height));
4023 * Make new playlists for a given track and also any others that belong
4024 * to the same active route group with the `edit' property.
4025 * @param v Track.
4028 void
4029 Editor::new_playlists (TimeAxisView* v)
4031 begin_reversible_command (_("new playlists"));
4032 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4033 _session->playlists->get (playlists);
4034 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4035 commit_reversible_command ();
4039 * Use a copy of the current playlist for a given track and also any others that belong
4040 * to the same active route group with the `edit' property.
4041 * @param v Track.
4044 void
4045 Editor::copy_playlists (TimeAxisView* v)
4047 begin_reversible_command (_("copy playlists"));
4048 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4049 _session->playlists->get (playlists);
4050 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4051 commit_reversible_command ();
4054 /** Clear the current playlist for a given track and also any others that belong
4055 * to the same active route group with the `edit' property.
4056 * @param v Track.
4059 void
4060 Editor::clear_playlists (TimeAxisView* v)
4062 begin_reversible_command (_("clear playlists"));
4063 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4064 _session->playlists->get (playlists);
4065 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4066 commit_reversible_command ();
4069 void
4070 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4072 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4075 void
4076 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4078 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4081 void
4082 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4084 atv.clear_playlist ();
4087 bool
4088 Editor::on_key_press_event (GdkEventKey* ev)
4090 return key_press_focus_accelerator_handler (*this, ev);
4093 bool
4094 Editor::on_key_release_event (GdkEventKey* ev)
4096 return Gtk::Window::on_key_release_event (ev);
4097 // return key_press_focus_accelerator_handler (*this, ev);
4100 /** Queue up a change to the viewport x origin.
4101 * @param frame New x origin.
4103 void
4104 Editor::reset_x_origin (framepos_t frame)
4106 queue_visual_change (frame);
4109 void
4110 Editor::reset_y_origin (double y)
4112 queue_visual_change_y (y);
4115 void
4116 Editor::reset_zoom (double fpu)
4118 queue_visual_change (fpu);
4121 void
4122 Editor::reposition_and_zoom (framepos_t frame, double fpu)
4124 reset_x_origin (frame);
4125 reset_zoom (fpu);
4127 if (!no_save_visual) {
4128 undo_visual_stack.push_back (current_visual_state(false));
4132 Editor::VisualState*
4133 Editor::current_visual_state (bool with_tracks)
4135 VisualState* vs = new VisualState;
4136 vs->y_position = vertical_adjustment.get_value();
4137 vs->frames_per_unit = frames_per_unit;
4138 vs->leftmost_frame = leftmost_frame;
4139 vs->zoom_focus = zoom_focus;
4141 if (with_tracks) {
4142 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4143 vs->track_states.push_back (TAVState ((*i), &(*i)->get_state()));
4147 return vs;
4150 void
4151 Editor::undo_visual_state ()
4153 if (undo_visual_stack.empty()) {
4154 return;
4157 redo_visual_stack.push_back (current_visual_state());
4159 VisualState* vs = undo_visual_stack.back();
4160 undo_visual_stack.pop_back();
4161 use_visual_state (*vs);
4164 void
4165 Editor::redo_visual_state ()
4167 if (redo_visual_stack.empty()) {
4168 return;
4171 undo_visual_stack.push_back (current_visual_state());
4173 VisualState* vs = redo_visual_stack.back();
4174 redo_visual_stack.pop_back();
4175 use_visual_state (*vs);
4178 void
4179 Editor::swap_visual_state ()
4181 if (undo_visual_stack.empty()) {
4182 redo_visual_state ();
4183 } else {
4184 undo_visual_state ();
4188 void
4189 Editor::use_visual_state (VisualState& vs)
4191 no_save_visual = true;
4193 _routes->suspend_redisplay ();
4195 vertical_adjustment.set_value (vs.y_position);
4197 set_zoom_focus (vs.zoom_focus);
4198 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4200 for (list<TAVState>::iterator i = vs.track_states.begin(); i != vs.track_states.end(); ++i) {
4201 TrackViewList::iterator t;
4203 /* check if the track still exists - it could have been deleted */
4205 if ((t = find (track_views.begin(), track_views.end(), i->first)) != track_views.end()) {
4206 (*t)->set_state (*(i->second), Stateful::loading_state_version);
4211 if (!vs.track_states.empty()) {
4212 _routes->update_visibility ();
4215 _routes->resume_redisplay ();
4217 no_save_visual = false;
4220 void
4221 Editor::set_frames_per_unit (double fpu)
4223 /* this is the core function that controls the zoom level of the canvas. it is called
4224 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4227 if (fpu == frames_per_unit) {
4228 return;
4231 if (fpu < 2.0) {
4232 fpu = 2.0;
4236 /* don't allow zooms that fit more than the maximum number
4237 of frames into an 800 pixel wide space.
4240 if (max_framepos / fpu < 800.0) {
4241 return;
4244 if (tempo_lines)
4245 tempo_lines->tempo_map_changed();
4247 frames_per_unit = fpu;
4248 post_zoom ();
4251 void
4252 Editor::post_zoom ()
4254 // convert fpu to frame count
4256 framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width);
4258 if (frames_per_unit != zoom_range_clock->current_duration()) {
4259 zoom_range_clock->set (frames);
4262 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4263 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4264 (*i)->reshow_selection (selection->time);
4268 ZoomChanged (); /* EMIT_SIGNAL */
4270 //reset_scrolling_region ();
4272 if (playhead_cursor) {
4273 playhead_cursor->set_position (playhead_cursor->current_frame);
4276 refresh_location_display();
4277 _summary->set_overlays_dirty ();
4279 update_marker_labels ();
4281 instant_save ();
4284 void
4285 Editor::queue_visual_change (framepos_t where)
4287 pending_visual_change.add (VisualChange::TimeOrigin);
4288 pending_visual_change.time_origin = where;
4289 ensure_visual_change_idle_handler ();
4292 void
4293 Editor::queue_visual_change (double fpu)
4295 pending_visual_change.add (VisualChange::ZoomLevel);
4296 pending_visual_change.frames_per_unit = fpu;
4298 ensure_visual_change_idle_handler ();
4301 void
4302 Editor::queue_visual_change_y (double y)
4304 pending_visual_change.add (VisualChange::YOrigin);
4305 pending_visual_change.y_origin = y;
4307 ensure_visual_change_idle_handler ();
4310 void
4311 Editor::ensure_visual_change_idle_handler ()
4313 if (pending_visual_change.idle_handler_id < 0) {
4314 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4319 Editor::_idle_visual_changer (void* arg)
4321 return static_cast<Editor*>(arg)->idle_visual_changer ();
4325 Editor::idle_visual_changer ()
4327 VisualChange::Type p = pending_visual_change.pending;
4328 pending_visual_change.pending = (VisualChange::Type) 0;
4330 double const last_time_origin = horizontal_position ();
4332 if (p & VisualChange::TimeOrigin) {
4333 /* This is a bit of a hack, but set_frames_per_unit
4334 below will (if called) end up with the
4335 CrossfadeViews looking at Editor::leftmost_frame,
4336 and if we're changing origin and zoom in the same
4337 operation it will be the wrong value unless we
4338 update it here.
4341 leftmost_frame = pending_visual_change.time_origin;
4344 if (p & VisualChange::ZoomLevel) {
4345 set_frames_per_unit (pending_visual_change.frames_per_unit);
4347 compute_fixed_ruler_scale ();
4348 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4349 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4350 update_tempo_based_rulers ();
4352 if (p & VisualChange::TimeOrigin) {
4353 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4355 if (p & VisualChange::YOrigin) {
4356 vertical_adjustment.set_value (pending_visual_change.y_origin);
4359 if (last_time_origin == horizontal_position ()) {
4360 /* changed signal not emitted */
4361 update_fixed_rulers ();
4362 redisplay_tempo (true);
4365 _summary->set_overlays_dirty ();
4367 pending_visual_change.idle_handler_id = -1;
4368 return 0; /* this is always a one-shot call */
4371 struct EditorOrderTimeAxisSorter {
4372 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4373 return a->order () < b->order ();
4377 void
4378 Editor::sort_track_selection (TrackViewList* sel)
4380 EditorOrderTimeAxisSorter cmp;
4382 if (sel) {
4383 sel->sort (cmp);
4384 } else {
4385 selection->tracks.sort (cmp);
4389 framepos_t
4390 Editor::get_preferred_edit_position (bool ignore_playhead)
4392 bool ignored;
4393 framepos_t where = 0;
4394 EditPoint ep = _edit_point;
4396 if (entered_marker) {
4397 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position()));
4398 return entered_marker->position();
4401 if (ignore_playhead && ep == EditAtPlayhead) {
4402 ep = EditAtSelectedMarker;
4405 switch (ep) {
4406 case EditAtPlayhead:
4407 where = _session->audible_frame();
4408 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where));
4409 break;
4411 case EditAtSelectedMarker:
4412 if (!selection->markers.empty()) {
4413 bool is_start;
4414 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4415 if (loc) {
4416 if (is_start) {
4417 where = loc->start();
4418 } else {
4419 where = loc->end();
4421 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where));
4422 break;
4425 /* fallthru */
4427 default:
4428 case EditAtMouse:
4429 if (!mouse_frame (where, ignored)) {
4430 /* XXX not right but what can we do ? */
4431 return 0;
4433 snap_to (where);
4434 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where));
4435 break;
4438 return where;
4441 void
4442 Editor::set_loop_range (framepos_t start, framepos_t end, string cmd)
4444 if (!_session) return;
4446 begin_reversible_command (cmd);
4448 Location* tll;
4450 if ((tll = transport_loop_location()) == 0) {
4451 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop);
4452 XMLNode &before = _session->locations()->get_state();
4453 _session->locations()->add (loc, true);
4454 _session->set_auto_loop_location (loc);
4455 XMLNode &after = _session->locations()->get_state();
4456 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4457 } else {
4458 XMLNode &before = tll->get_state();
4459 tll->set_hidden (false, this);
4460 tll->set (start, end);
4461 XMLNode &after = tll->get_state();
4462 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4465 commit_reversible_command ();
4468 void
4469 Editor::set_punch_range (framepos_t start, framepos_t end, string cmd)
4471 if (!_session) return;
4473 begin_reversible_command (cmd);
4475 Location* tpl;
4477 if ((tpl = transport_punch_location()) == 0) {
4478 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoPunch);
4479 XMLNode &before = _session->locations()->get_state();
4480 _session->locations()->add (loc, true);
4481 _session->set_auto_loop_location (loc);
4482 XMLNode &after = _session->locations()->get_state();
4483 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4485 else {
4486 XMLNode &before = tpl->get_state();
4487 tpl->set_hidden (false, this);
4488 tpl->set (start, end);
4489 XMLNode &after = tpl->get_state();
4490 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4493 commit_reversible_command ();
4496 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4497 * @param rs List to which found regions are added.
4498 * @param where Time to look at.
4499 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4501 void
4502 Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4504 const TrackViewList* tracks;
4506 if (ts.empty()) {
4507 tracks = &track_views;
4508 } else {
4509 tracks = &ts;
4512 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4514 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4516 if (rtv) {
4517 boost::shared_ptr<Track> tr;
4518 boost::shared_ptr<Playlist> pl;
4520 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4522 Playlist::RegionList* regions = pl->regions_at (
4523 (framepos_t) floor ( (double) where * tr->speed()));
4525 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4526 RegionView* rv = rtv->view()->find_view (*i);
4527 if (rv) {
4528 rs.add (rv);
4532 delete regions;
4538 void
4539 Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4541 const TrackViewList* tracks;
4543 if (ts.empty()) {
4544 tracks = &track_views;
4545 } else {
4546 tracks = &ts;
4549 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4550 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4551 if (rtv) {
4552 boost::shared_ptr<Track> tr;
4553 boost::shared_ptr<Playlist> pl;
4555 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4557 Playlist::RegionList* regions = pl->regions_touched (
4558 (framepos_t) floor ( (double)where * tr->speed()), max_framepos);
4560 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4562 RegionView* rv = rtv->view()->find_view (*i);
4564 if (rv) {
4565 rs.push_back (rv);
4569 delete regions;
4575 /** Start with regions that are selected. Then add equivalent regions
4576 * on tracks in the same active edit-enabled route group as any of
4577 * the regions that we started with.
4580 RegionSelection
4581 Editor::get_regions_from_selection ()
4583 return get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
4586 /** Get regions using the following method:
4588 * Make an initial region list using the selected regions, unless
4589 * the edit point is `mouse' and the mouse is over an unselected
4590 * region. In this case, start with just that region.
4592 * Then, make an initial track list of the tracks that these
4593 * regions are on, and if the edit point is not `mouse', add the
4594 * selected tracks.
4596 * Look at this track list and add any other tracks that are on the
4597 * same active edit-enabled route group as one of the initial tracks.
4599 * Finally take the initial region list and add any regions that are
4600 * under the edit point on one of the tracks on the track list to get
4601 * the returned region list.
4603 * The rationale here is that the mouse edit point is special in that
4604 * its position describes both a time and a track; the other edit
4605 * modes only describe a time. Hence if the edit point is `mouse' we
4606 * ignore selected tracks, as we assume the user means something by
4607 * pointing at a particular track. Also in this case we take note of
4608 * the region directly under the edit point, as there is always just one
4609 * (rather than possibly several with non-mouse edit points).
4612 RegionSelection
4613 Editor::get_regions_from_selection_and_edit_point ()
4615 RegionSelection regions;
4617 if (_edit_point == EditAtMouse && entered_regionview && !selection->regions.contains (entered_regionview)) {
4618 regions.add (entered_regionview);
4619 } else {
4620 regions = selection->regions;
4623 TrackViewList tracks;
4625 if (_edit_point != EditAtMouse) {
4626 tracks = selection->tracks;
4629 /* Add any other tracks that have regions that are in the same
4630 edit-activated route group as one of our regions.
4632 for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) {
4634 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4636 if (g && g->is_active() && g->is_edit()) {
4637 tracks.add (axis_views_from_routes (g->route_list()));
4641 if (!tracks.empty()) {
4642 /* now find regions that are at the edit position on those tracks */
4643 framepos_t const where = get_preferred_edit_position ();
4644 get_regions_at (regions, where, tracks);
4647 return regions;
4650 /** Start with regions that are selected, or the entered regionview if none are selected.
4651 * Then add equivalent regions on tracks in the same active edit-enabled route group as any
4652 * of the regions that we started with.
4655 RegionSelection
4656 Editor::get_regions_from_selection_and_entered ()
4658 RegionSelection regions = selection->regions;
4660 if (regions.empty() && entered_regionview) {
4661 regions.add (entered_regionview);
4664 return get_equivalent_regions (regions, ARDOUR::Properties::edit.property_id);
4667 void
4668 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4670 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4672 RouteTimeAxisView* tatv;
4674 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4676 boost::shared_ptr<Playlist> pl;
4677 vector<boost::shared_ptr<Region> > results;
4678 RegionView* marv;
4679 boost::shared_ptr<Track> tr;
4681 if ((tr = tatv->track()) == 0) {
4682 /* bus */
4683 continue;
4686 if ((pl = (tr->playlist())) != 0) {
4687 pl->get_region_list_equivalent_regions (region, results);
4690 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4691 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4692 regions.push_back (marv);
4700 void
4701 Editor::show_rhythm_ferret ()
4703 if (rhythm_ferret == 0) {
4704 rhythm_ferret = new RhythmFerret(*this);
4707 rhythm_ferret->set_session (_session);
4708 rhythm_ferret->show ();
4709 rhythm_ferret->present ();
4712 void
4713 Editor::first_idle ()
4715 MessageDialog* dialog = 0;
4717 if (track_views.size() > 1) {
4718 dialog = new MessageDialog (*this,
4719 string_compose (_("Please wait while %1 loads visual data"), PROGRAM_NAME),
4720 true,
4721 Gtk::MESSAGE_INFO,
4722 Gtk::BUTTONS_NONE);
4723 dialog->present ();
4724 ARDOUR_UI::instance()->flush_pending ();
4727 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4728 (*t)->first_idle();
4731 // first idle adds route children (automation tracks), so we need to redisplay here
4732 _routes->redisplay ();
4734 delete dialog;
4736 _have_idled = true;
4739 gboolean
4740 Editor::_idle_resize (gpointer arg)
4742 return ((Editor*)arg)->idle_resize ();
4745 void
4746 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4748 if (resize_idle_id < 0) {
4749 resize_idle_id = g_idle_add (_idle_resize, this);
4750 _pending_resize_amount = 0;
4753 /* make a note of the smallest resulting height, so that we can clamp the
4754 lower limit at TimeAxisView::hSmall */
4756 int32_t min_resulting = INT32_MAX;
4758 _pending_resize_amount += h;
4759 _pending_resize_view = view;
4761 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4763 if (selection->tracks.contains (_pending_resize_view)) {
4764 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4765 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4769 if (min_resulting < 0) {
4770 min_resulting = 0;
4773 /* clamp */
4774 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4775 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4779 /** Handle pending resizing of tracks */
4780 bool
4781 Editor::idle_resize ()
4783 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4785 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4786 selection->tracks.contains (_pending_resize_view)) {
4788 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4789 if (*i != _pending_resize_view) {
4790 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4795 _pending_resize_amount = 0;
4796 flush_canvas ();
4797 _group_tabs->set_dirty ();
4798 resize_idle_id = -1;
4800 return false;
4803 void
4804 Editor::located ()
4806 ENSURE_GUI_THREAD (*this, &Editor::located);
4808 playhead_cursor->set_position (_session->audible_frame ());
4809 if (_follow_playhead && !_pending_initial_locate) {
4810 reset_x_origin_to_follow_playhead ();
4813 _pending_locate_request = false;
4814 _pending_initial_locate = false;
4817 void
4818 Editor::region_view_added (RegionView *)
4820 _summary->set_dirty ();
4823 void
4824 Editor::region_view_removed ()
4826 _summary->set_dirty ();
4829 TimeAxisView*
4830 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4832 TrackViewList::const_iterator j = track_views.begin ();
4833 while (j != track_views.end()) {
4834 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4835 if (rtv && rtv->route() == r) {
4836 return rtv;
4838 ++j;
4841 return 0;
4845 TrackViewList
4846 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4848 TrackViewList t;
4850 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4851 TimeAxisView* tv = axis_view_from_route (*i);
4852 if (tv) {
4853 t.push_back (tv);
4857 return t;
4861 void
4862 Editor::handle_new_route (RouteList& routes)
4864 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4866 RouteTimeAxisView *rtv;
4867 list<RouteTimeAxisView*> new_views;
4869 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4870 boost::shared_ptr<Route> route = (*x);
4872 if (route->is_hidden() || route->is_monitor()) {
4873 continue;
4876 DataType dt = route->input()->default_type();
4878 if (dt == ARDOUR::DataType::AUDIO) {
4879 rtv = new AudioTimeAxisView (*this, _session, route, *track_canvas);
4880 } else if (dt == ARDOUR::DataType::MIDI) {
4881 rtv = new MidiTimeAxisView (*this, _session, route, *track_canvas);
4882 } else {
4883 throw unknown_type();
4886 new_views.push_back (rtv);
4887 track_views.push_back (rtv);
4889 rtv->effective_gain_display ();
4891 if (internal_editing()) {
4892 rtv->enter_internal_edit_mode ();
4893 } else {
4894 rtv->leave_internal_edit_mode ();
4897 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4898 rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed));
4901 _routes->routes_added (new_views);
4902 _summary->routes_added (new_views);
4904 if (show_editor_mixer_when_tracks_arrive) {
4905 show_editor_mixer (true);
4908 editor_list_button.set_sensitive (true);
4911 void
4912 Editor::timeaxisview_deleted (TimeAxisView *tv)
4914 if (_session && _session->deletion_in_progress()) {
4915 /* the situation is under control */
4916 return;
4919 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
4921 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
4923 _routes->route_removed (tv);
4925 if (tv == entered_track) {
4926 entered_track = 0;
4929 TimeAxisView::Children c = tv->get_child_list ();
4930 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
4931 if (entered_track == i->get()) {
4932 entered_track = 0;
4936 /* remove it from the list of track views */
4938 TrackViewList::iterator i;
4940 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
4941 i = track_views.erase (i);
4944 /* update whatever the current mixer strip is displaying, if revelant */
4946 boost::shared_ptr<Route> route;
4948 if (rtav) {
4949 route = rtav->route ();
4952 if (current_mixer_strip && current_mixer_strip->route() == route) {
4954 TimeAxisView* next_tv;
4956 if (track_views.empty()) {
4957 next_tv = 0;
4958 } else if (i == track_views.end()) {
4959 next_tv = track_views.front();
4960 } else {
4961 next_tv = (*i);
4965 if (next_tv) {
4966 set_selected_mixer_strip (*next_tv);
4967 } else {
4968 /* make the editor mixer strip go away setting the
4969 * button to inactive (which also unticks the menu option)
4972 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
4977 void
4978 Editor::hide_track_in_display (TimeAxisView* tv, bool apply_to_selection)
4980 if (apply_to_selection) {
4981 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ) {
4983 TrackSelection::iterator j = i;
4984 ++j;
4986 hide_track_in_display (*i, false);
4988 i = j;
4990 } else {
4991 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
4993 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
4994 // this will hide the mixer strip
4995 set_selected_mixer_strip (*tv);
4998 _routes->hide_track_in_display (*tv);
5002 bool
5003 Editor::sync_track_view_list_and_routes ()
5005 track_views = TrackViewList (_routes->views ());
5007 _summary->set_dirty ();
5008 _group_tabs->set_dirty ();
5010 return false; // do not call again (until needed)
5013 void
5014 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
5016 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5017 theslot (**i);
5021 /** Find a RouteTimeAxisView by the ID of its route */
5022 RouteTimeAxisView*
5023 Editor::get_route_view_by_route_id (PBD::ID& id) const
5025 RouteTimeAxisView* v;
5027 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5028 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5029 if(v->route()->id() == id) {
5030 return v;
5035 return 0;
5038 void
5039 Editor::fit_route_group (RouteGroup *g)
5041 TrackViewList ts = axis_views_from_routes (g->route_list ());
5042 fit_tracks (ts);
5045 void
5046 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5048 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5050 if (r == 0) {
5051 _session->cancel_audition ();
5052 return;
5055 if (_session->is_auditioning()) {
5056 _session->cancel_audition ();
5057 if (r == last_audition_region) {
5058 return;
5062 _session->audition_region (r);
5063 last_audition_region = r;
5067 void
5068 Editor::hide_a_region (boost::shared_ptr<Region> r)
5070 r->set_hidden (true);
5073 void
5074 Editor::show_a_region (boost::shared_ptr<Region> r)
5076 r->set_hidden (false);
5079 void
5080 Editor::audition_region_from_region_list ()
5082 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5085 void
5086 Editor::hide_region_from_region_list ()
5088 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5091 void
5092 Editor::show_region_in_region_list ()
5094 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5097 void
5098 Editor::step_edit_status_change (bool yn)
5100 if (yn) {
5101 start_step_editing ();
5102 } else {
5103 stop_step_editing ();
5107 void
5108 Editor::start_step_editing ()
5110 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5113 void
5114 Editor::stop_step_editing ()
5116 step_edit_connection.disconnect ();
5119 bool
5120 Editor::check_step_edit ()
5122 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5123 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5124 if (mtv) {
5125 mtv->check_step_edit ();
5129 return true; // do it again, till we stop
5132 bool
5133 Editor::scroll_press (Direction dir)
5135 ++_scroll_callbacks;
5137 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5138 /* delay the first auto-repeat */
5139 return true;
5142 switch (dir) {
5143 case LEFT:
5144 scroll_backward (1);
5145 break;
5147 case RIGHT:
5148 scroll_forward (1);
5149 break;
5151 case UP:
5152 scroll_tracks_up_line ();
5153 break;
5155 case DOWN:
5156 scroll_tracks_down_line ();
5157 break;
5160 /* do hacky auto-repeat */
5161 if (!_scroll_connection.connected ()) {
5163 _scroll_connection = Glib::signal_timeout().connect (
5164 sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), dir), 100
5167 _scroll_callbacks = 0;
5170 return true;
5173 void
5174 Editor::scroll_release ()
5176 _scroll_connection.disconnect ();
5179 /** Queue a change for the Editor viewport x origin to follow the playhead */
5180 void
5181 Editor::reset_x_origin_to_follow_playhead ()
5183 framepos_t const frame = playhead_cursor->current_frame;
5185 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5187 if (_session->transport_speed() < 0) {
5189 if (frame > (current_page_frames() / 2)) {
5190 center_screen (frame-(current_page_frames()/2));
5191 } else {
5192 center_screen (current_page_frames()/2);
5195 } else {
5197 if (frame < leftmost_frame) {
5198 /* moving left */
5199 framepos_t l = 0;
5200 if (_session->transport_rolling()) {
5201 /* rolling; end up with the playhead at the right of the page */
5202 l = frame - current_page_frames ();
5203 } else {
5204 /* not rolling: end up with the playhead 3/4 of the way along the page */
5205 l = frame - (3 * current_page_frames() / 4);
5208 if (l < 0) {
5209 l = 0;
5212 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5213 } else {
5214 /* moving right */
5215 if (_session->transport_rolling()) {
5216 /* rolling: end up with the playhead on the left of the page */
5217 center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5218 } else {
5219 /* not rolling: end up with the playhead 1/4 of the way along the page */
5220 center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5227 void
5228 Editor::super_rapid_screen_update ()
5230 if (!_session || !_session->engine().running()) {
5231 return;
5234 /* METERING / MIXER STRIPS */
5236 /* update track meters, if required */
5237 if (is_mapped() && meters_running) {
5238 RouteTimeAxisView* rtv;
5239 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5240 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5241 rtv->fast_update ();
5246 /* and any current mixer strip */
5247 if (current_mixer_strip) {
5248 current_mixer_strip->fast_update ();
5251 /* PLAYHEAD AND VIEWPORT */
5253 framepos_t const frame = _session->audible_frame();
5255 /* There are a few reasons why we might not update the playhead / viewport stuff:
5257 * 1. we don't update things when there's a pending locate request, otherwise
5258 * when the editor requests a locate there is a chance that this method
5259 * will move the playhead before the locate request is processed, causing
5260 * a visual glitch.
5261 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5262 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5265 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5267 last_update_frame = frame;
5269 if (!_dragging_playhead) {
5270 playhead_cursor->set_position (frame);
5273 if (!_stationary_playhead) {
5275 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5276 reset_x_origin_to_follow_playhead ();
5279 } else {
5281 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5282 editor canvas
5284 #if 0
5285 // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5286 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5287 if (target <= 0.0) {
5288 target = 0.0;
5290 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5291 target = (target * 0.15) + (current * 0.85);
5292 } else {
5293 /* relax */
5296 current = target;
5297 set_horizontal_position (current);
5298 #endif
5305 void
5306 Editor::session_going_away ()
5308 _have_idled = false;
5310 _session_connections.drop_connections ();
5312 super_rapid_screen_update_connection.disconnect ();
5314 selection->clear ();
5315 cut_buffer->clear ();
5317 clicked_regionview = 0;
5318 clicked_axisview = 0;
5319 clicked_routeview = 0;
5320 clicked_crossfadeview = 0;
5321 entered_regionview = 0;
5322 entered_track = 0;
5323 last_update_frame = 0;
5324 _drags->abort ();
5326 playhead_cursor->canvas_item.hide ();
5328 /* rip everything out of the list displays */
5330 _regions->clear ();
5331 _routes->clear ();
5332 _route_groups->clear ();
5334 /* do this first so that deleting a track doesn't reset cms to null
5335 and thus cause a leak.
5338 if (current_mixer_strip) {
5339 if (current_mixer_strip->get_parent() != 0) {
5340 global_hpacker.remove (*current_mixer_strip);
5342 delete current_mixer_strip;
5343 current_mixer_strip = 0;
5346 /* delete all trackviews */
5348 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5349 delete *i;
5351 track_views.clear ();
5353 zoom_range_clock->set_session (0);
5354 nudge_clock->set_session (0);
5356 editor_list_button.set_active(false);
5357 editor_list_button.set_sensitive(false);
5359 /* clear tempo/meter rulers */
5360 remove_metric_marks ();
5361 hide_measures ();
5362 clear_marker_display ();
5364 delete current_bbt_points;
5365 current_bbt_points = 0;
5367 /* get rid of any existing editor mixer strip */
5369 WindowTitle title(Glib::get_application_name());
5370 title += _("Editor");
5372 set_title (title.get_string());
5374 SessionHandlePtr::session_going_away ();
5378 void
5379 Editor::show_editor_list (bool yn)
5381 if (yn) {
5382 _the_notebook.show ();
5383 } else {
5384 _the_notebook.hide ();
5388 void
5389 Editor::change_region_layering_order ()
5391 framepos_t const position = get_preferred_edit_position ();
5393 if (!clicked_routeview) {
5394 if (layering_order_editor) {
5395 layering_order_editor->hide ();
5397 return;
5400 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
5402 if (!track) {
5403 return;
5406 boost::shared_ptr<Playlist> pl = track->playlist();
5408 if (!pl) {
5409 return;
5412 if (layering_order_editor == 0) {
5413 layering_order_editor = new RegionLayeringOrderEditor(*this);
5416 layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position);
5417 layering_order_editor->maybe_present ();
5420 void
5421 Editor::update_region_layering_order_editor ()
5423 if (layering_order_editor && layering_order_editor->is_visible ()) {
5424 change_region_layering_order ();
5428 void
5429 Editor::setup_fade_images ()
5431 _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear")));
5432 _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut")));
5433 _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut")));
5434 _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut")));
5435 _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut")));
5437 _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear")));
5438 _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut")));
5439 _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut")));
5440 _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut")));
5441 _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut")));
5445 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
5446 Gtk::MenuItem&
5447 Editor::action_menu_item (std::string const & name)
5449 Glib::RefPtr<Action> a = editor_actions->get_action (name);
5450 assert (a);
5452 return *manage (a->create_menu_item ());
5455 void
5456 Editor::resize_text_widgets ()
5458 set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_FUDGE+10, 15);
5459 set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_FUDGE+10, 15);
5460 set_size_request_to_display_given_text (snap_type_selector, snap_type_strings, COMBO_FUDGE+10, 15);
5461 set_size_request_to_display_given_text (snap_mode_selector, snap_mode_strings, COMBO_FUDGE+10, 15);
5462 set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_FUDGE+10, 15);
5465 void
5466 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
5468 EventBox* b = manage (new EventBox);
5469 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
5470 Label* l = manage (new Label (name));
5471 l->set_angle (-90);
5472 b->add (*l);
5473 b->show_all ();
5474 _the_notebook.append_page (widget, *b);
5477 bool
5478 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
5480 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
5481 _the_notebook.set_current_page (_the_notebook.page_num (*page));
5484 if (ev->type == GDK_2BUTTON_PRESS) {
5486 /* double-click on a notebook tab shrinks or expands the notebook */
5488 if (_notebook_shrunk) {
5489 edit_pane.set_position (pre_maximal_horizontal_pane_position);
5490 _notebook_shrunk = false;
5491 } else {
5492 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
5493 edit_pane.set_position (edit_pane.get_position() + page->get_width());
5494 _notebook_shrunk = true;
5498 return true;