when leaving internal edit mode, if the current mode is range ("draw") and it wasn...
[ardour2.git] / gtk2_ardour / editor.cc
blob62897bda74fdfbd2adbcf3791a9732fe776ad494
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 "editor.h"
81 #include "debug.h"
82 #include "keyboard.h"
83 #include "marker.h"
84 #include "playlist_selector.h"
85 #include "audio_region_view.h"
86 #include "rgb_macros.h"
87 #include "selection.h"
88 #include "audio_streamview.h"
89 #include "time_axis_view.h"
90 #include "audio_time_axis.h"
91 #include "utils.h"
92 #include "crossfade_view.h"
93 #include "canvas-noevent-text.h"
94 #include "editing.h"
95 #include "public_editor.h"
96 #include "crossfade_edit.h"
97 #include "canvas_impl.h"
98 #include "actions.h"
99 #include "sfdb_ui.h"
100 #include "gui_thread.h"
101 #include "simpleline.h"
102 #include "rhythm_ferret.h"
103 #include "actions.h"
104 #include "tempo_lines.h"
105 #include "analysis_window.h"
106 #include "bundle_manager.h"
107 #include "global_port_matrix.h"
108 #include "editor_drag.h"
109 #include "editor_group_tabs.h"
110 #include "automation_time_axis.h"
111 #include "editor_routes.h"
112 #include "midi_time_axis.h"
113 #include "mixer_strip.h"
114 #include "editor_route_groups.h"
115 #include "editor_regions.h"
116 #include "editor_locations.h"
117 #include "editor_snapshots.h"
118 #include "editor_summary.h"
119 #include "region_layering_order_editor.h"
120 #include "mouse_cursors.h"
121 #include "editor_cursors.h"
123 #include "i18n.h"
125 #ifdef WITH_CMT
126 #include "imageframe_socket_handler.h"
127 #endif
129 using namespace std;
130 using namespace ARDOUR;
131 using namespace PBD;
132 using namespace Gtk;
133 using namespace Glib;
134 using namespace Gtkmm2ext;
135 using namespace Editing;
137 using PBD::internationalize;
138 using PBD::atoi;
139 using Gtkmm2ext::Keyboard;
141 const double Editor::timebar_height = 15.0;
143 static const gchar *_snap_type_strings[] = {
144 N_("CD Frames"),
145 N_("Timecode Frames"),
146 N_("Timecode Seconds"),
147 N_("Timecode Minutes"),
148 N_("Seconds"),
149 N_("Minutes"),
150 N_("Beats/32"),
151 N_("Beats/28"),
152 N_("Beats/24"),
153 N_("Beats/20"),
154 N_("Beats/16"),
155 N_("Beats/14"),
156 N_("Beats/12"),
157 N_("Beats/10"),
158 N_("Beats/8"),
159 N_("Beats/7"),
160 N_("Beats/6"),
161 N_("Beats/5"),
162 N_("Beats/4"),
163 N_("Beats/3"),
164 N_("Beats/2"),
165 N_("Beats"),
166 N_("Bars"),
167 N_("Marks"),
168 N_("Region starts"),
169 N_("Region ends"),
170 N_("Region syncs"),
171 N_("Region bounds"),
175 static const gchar *_snap_mode_strings[] = {
176 N_("No Grid"),
177 N_("Grid"),
178 N_("Magnetic"),
182 static const gchar *_edit_point_strings[] = {
183 N_("Playhead"),
184 N_("Marker"),
185 N_("Mouse"),
189 static const gchar *_zoom_focus_strings[] = {
190 N_("Left"),
191 N_("Right"),
192 N_("Center"),
193 N_("Playhead"),
194 N_("Mouse"),
195 N_("Edit point"),
199 #ifdef USE_RUBBERBAND
200 static const gchar *_rb_opt_strings[] = {
201 N_("Mushy"),
202 N_("Smooth"),
203 N_("Balanced multitimbral mixture"),
204 N_("Unpitched percussion with stable notes"),
205 N_("Crisp monophonic instrumental"),
206 N_("Unpitched solo percussion"),
207 N_("Resample without preserving pitch"),
210 #endif
212 void
213 show_me_the_size (Requisition* r, const char* what)
215 cerr << "size of " << what << " = " << r->width << " x " << r->height << endl;
218 #ifdef GTKOSX
219 static void
220 pane_size_watcher (Paned* pane)
222 /* if the handle of a pane vanishes into (at least) the tabs of a notebook,
223 it is no longer accessible. so stop that. this doesn't happen on X11,
224 just the quartz backend.
226 ugh.
229 int max_width_of_lhs = GTK_WIDGET(pane->gobj())->allocation.width - 25;
231 gint pos = pane->get_position ();
233 if (pos > max_width_of_lhs) {
234 pane->set_position (max_width_of_lhs);
237 #endif
239 Editor::Editor ()
240 : _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
242 /* time display buttons */
243 , minsec_label (_("Mins:Secs"))
244 , bbt_label (_("Bars:Beats"))
245 , timecode_label (_("Timecode"))
246 , samples_label (_("Samples"))
247 , tempo_label (_("Tempo"))
248 , meter_label (_("Meter"))
249 , mark_label (_("Location Markers"))
250 , range_mark_label (_("Range Markers"))
251 , transport_mark_label (_("Loop/Punch Ranges"))
252 , cd_mark_label (_("CD Markers"))
253 , edit_packer (4, 4, true)
255 /* the values here don't matter: layout widgets
256 reset them as needed.
259 , vertical_adjustment (0.0, 0.0, 10.0, 400.0)
261 /* tool bar related */
263 , zoom_range_clock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, false, true)
265 , toolbar_selection_clock_table (2,3)
267 , automation_mode_button (_("mode"))
268 , global_automation_button (_("automation"))
270 , _toolbar_viewport (*manage (new Gtk::Adjustment (0, 0, 1e10)), *manage (new Gtk::Adjustment (0, 0, 1e10)))
271 , midi_panic_button (_("Panic"))
273 #ifdef WITH_CMT
274 , image_socket_listener(0)
275 #endif
277 /* nudge */
279 , nudge_clock (X_("nudge"), false, X_("NudgeClock"), true, false, true)
280 , meters_running(false)
281 , _pending_locate_request (false)
282 , _pending_initial_locate (false)
283 , _last_cut_copy_source_track (0)
285 , _region_selection_change_updates_region_list (true)
287 constructed = false;
289 /* we are a singleton */
291 PublicEditor::_instance = this;
293 _have_idled = false;
295 selection = new Selection (this);
296 cut_buffer = new Selection (this);
298 clicked_regionview = 0;
299 clicked_axisview = 0;
300 clicked_routeview = 0;
301 clicked_crossfadeview = 0;
302 clicked_control_point = 0;
303 last_update_frame = 0;
304 pre_press_cursor = 0;
305 _drags = new DragManager (this);
306 current_mixer_strip = 0;
307 current_bbt_points = 0;
308 tempo_lines = 0;
310 snap_type_strings = I18N (_snap_type_strings);
311 snap_mode_strings = I18N (_snap_mode_strings);
312 zoom_focus_strings = I18N (_zoom_focus_strings);
313 edit_point_strings = I18N (_edit_point_strings);
314 #ifdef USE_RUBBERBAND
315 rb_opt_strings = I18N (_rb_opt_strings);
316 rb_current_opt = 4;
317 #endif
319 snap_threshold = 5.0;
320 bbt_beat_subdivision = 4;
321 _canvas_width = 0;
322 _canvas_height = 0;
323 last_autoscroll_x = 0;
324 last_autoscroll_y = 0;
325 autoscroll_active = false;
326 autoscroll_timeout_tag = -1;
327 logo_item = 0;
329 analysis_window = 0;
331 current_interthread_info = 0;
332 _show_measures = true;
333 show_gain_after_trim = false;
334 verbose_cursor_on = true;
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 _bundle_manager = 0;
369 no_save_visual = false;
370 resize_idle_id = -1;
372 scrubbing_direction = 0;
374 sfbrowser = 0;
376 location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get();
377 location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get();
378 location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get();
379 location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get();
380 location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get();
382 _edit_point = EditAtMouse;
383 _internal_editing = false;
384 current_canvas_cursor = 0;
386 frames_per_unit = 2048; /* too early to use reset_zoom () */
388 _scroll_callbacks = 0;
390 zoom_focus = ZoomFocusLeft;
391 set_zoom_focus (ZoomFocusLeft);
392 zoom_range_clock.ValueChanged.connect (sigc::mem_fun(*this, &Editor::zoom_adjustment_changed));
394 bbt_label.set_name ("EditorTimeButton");
395 bbt_label.set_size_request (-1, (int)timebar_height);
396 bbt_label.set_alignment (1.0, 0.5);
397 bbt_label.set_padding (5,0);
398 bbt_label.hide ();
399 bbt_label.set_no_show_all();
400 minsec_label.set_name ("EditorTimeButton");
401 minsec_label.set_size_request (-1, (int)timebar_height);
402 minsec_label.set_alignment (1.0, 0.5);
403 minsec_label.set_padding (5,0);
404 minsec_label.hide ();
405 minsec_label.set_no_show_all();
406 timecode_label.set_name ("EditorTimeButton");
407 timecode_label.set_size_request (-1, (int)timebar_height);
408 timecode_label.set_alignment (1.0, 0.5);
409 timecode_label.set_padding (5,0);
410 timecode_label.hide ();
411 timecode_label.set_no_show_all();
412 samples_label.set_name ("EditorTimeButton");
413 samples_label.set_size_request (-1, (int)timebar_height);
414 samples_label.set_alignment (1.0, 0.5);
415 samples_label.set_padding (5,0);
416 samples_label.hide ();
417 samples_label.set_no_show_all();
419 tempo_label.set_name ("EditorTimeButton");
420 tempo_label.set_size_request (-1, (int)timebar_height);
421 tempo_label.set_alignment (1.0, 0.5);
422 tempo_label.set_padding (5,0);
423 tempo_label.hide();
424 tempo_label.set_no_show_all();
426 meter_label.set_name ("EditorTimeButton");
427 meter_label.set_size_request (-1, (int)timebar_height);
428 meter_label.set_alignment (1.0, 0.5);
429 meter_label.set_padding (5,0);
430 meter_label.hide();
431 meter_label.set_no_show_all();
433 mark_label.set_name ("EditorTimeButton");
434 mark_label.set_size_request (-1, (int)timebar_height);
435 mark_label.set_alignment (1.0, 0.5);
436 mark_label.set_padding (5,0);
437 mark_label.hide();
438 mark_label.set_no_show_all();
440 cd_mark_label.set_name ("EditorTimeButton");
441 cd_mark_label.set_size_request (-1, (int)timebar_height);
442 cd_mark_label.set_alignment (1.0, 0.5);
443 cd_mark_label.set_padding (5,0);
444 cd_mark_label.hide();
445 cd_mark_label.set_no_show_all();
447 range_mark_label.set_name ("EditorTimeButton");
448 range_mark_label.set_size_request (-1, (int)timebar_height);
449 range_mark_label.set_alignment (1.0, 0.5);
450 range_mark_label.set_padding (5,0);
451 range_mark_label.hide();
452 range_mark_label.set_no_show_all();
454 transport_mark_label.set_name ("EditorTimeButton");
455 transport_mark_label.set_size_request (-1, (int)timebar_height);
456 transport_mark_label.set_alignment (1.0, 0.5);
457 transport_mark_label.set_padding (5,0);
458 transport_mark_label.hide();
459 transport_mark_label.set_no_show_all();
461 initialize_rulers ();
462 initialize_canvas ();
464 _summary = new EditorSummary (this);
466 selection->TimeChanged.connect (sigc::mem_fun(*this, &Editor::time_selection_changed));
467 selection->TracksChanged.connect (sigc::mem_fun(*this, &Editor::track_selection_changed));
469 editor_regions_selection_changed_connection = selection->RegionsChanged.connect (sigc::mem_fun(*this, &Editor::region_selection_changed));
471 selection->PointsChanged.connect (sigc::mem_fun(*this, &Editor::point_selection_changed));
472 selection->MarkersChanged.connect (sigc::mem_fun(*this, &Editor::marker_selection_changed));
474 edit_controls_vbox.set_spacing (0);
475 vertical_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &Editor::tie_vertical_scrolling), true);
476 track_canvas->signal_map_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_map_handler));
478 HBox* h = manage (new HBox);
479 _group_tabs = new EditorGroupTabs (this);
480 h->pack_start (*_group_tabs, PACK_SHRINK);
481 h->pack_start (edit_controls_vbox);
482 controls_layout.add (*h);
484 controls_layout.set_name ("EditControlsBase");
485 controls_layout.add_events (Gdk::SCROLL_MASK);
486 controls_layout.signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::control_layout_scroll), false);
488 controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
489 controls_layout.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_release));
491 _cursors = new MouseCursors;
493 ArdourCanvas::Canvas* time_pad = manage(new ArdourCanvas::Canvas());
494 ArdourCanvas::SimpleLine* pad_line_1 = manage(new ArdourCanvas::SimpleLine(*time_pad->root(),
495 0.0, 1.0, 100.0, 1.0));
497 pad_line_1->property_color_rgba() = 0xFF0000FF;
498 pad_line_1->show();
500 time_pad->show();
502 time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2);
503 time_canvas_vbox.set_size_request (-1, -1);
505 ruler_label_event_box.add (ruler_label_vbox);
506 ruler_label_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
507 ruler_label_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
509 time_button_event_box.add (time_button_vbox);
510 time_button_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
511 time_button_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
513 /* these enable us to have a dedicated window (for cursor setting, etc.)
514 for the canvas areas.
517 track_canvas_event_box.add (*track_canvas);
519 time_canvas_event_box.add (time_canvas_vbox);
520 time_canvas_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
522 edit_packer.set_col_spacings (0);
523 edit_packer.set_row_spacings (0);
524 edit_packer.set_homogeneous (false);
525 edit_packer.set_border_width (0);
526 edit_packer.set_name ("EditorWindow");
528 /* labels for the rulers */
529 edit_packer.attach (ruler_label_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0);
530 /* labels for the marker "tracks" */
531 edit_packer.attach (time_button_event_box, 1, 2, 1, 2, FILL, SHRINK, 0, 0);
532 /* the rulers */
533 edit_packer.attach (time_canvas_event_box, 2, 3, 0, 1, FILL|EXPAND, FILL, 0, 0);
534 /* track controls */
535 edit_packer.attach (controls_layout, 0, 2, 2, 3, FILL, FILL|EXPAND, 0, 0);
536 /* main canvas */
537 edit_packer.attach (track_canvas_event_box, 2, 3, 1, 3, FILL|EXPAND, FILL|EXPAND, 0, 0);
539 bottom_hbox.set_border_width (2);
540 bottom_hbox.set_spacing (3);
542 _route_groups = new EditorRouteGroups (this);
543 _routes = new EditorRoutes (this);
544 _regions = new EditorRegions (this);
545 _snapshots = new EditorSnapshots (this);
546 _locations = new EditorLocations (this);
548 add_notebook_page (_("Regions"), _regions->widget ());
549 add_notebook_page (_("Tracks & Busses"), _routes->widget ());
550 add_notebook_page (_("Snapshots"), _snapshots->widget ());
551 add_notebook_page (_("Route Groups"), _route_groups->widget ());
552 add_notebook_page (_("Ranges & Marks"), _locations->widget ());
554 _the_notebook.set_show_tabs (true);
555 _the_notebook.set_scrollable (true);
556 _the_notebook.popup_disable ();
557 _the_notebook.set_tab_pos (Gtk::POS_RIGHT);
558 _the_notebook.show_all ();
560 post_maximal_editor_width = 0;
561 post_maximal_horizontal_pane_position = 0;
562 post_maximal_editor_height = 0;
563 post_maximal_vertical_pane_position = 0;
564 _notebook_shrunk = false;
566 editor_summary_pane.pack1(edit_packer);
568 Button* summary_arrows_left_left = manage (new Button);
569 summary_arrows_left_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
570 summary_arrows_left_left->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), LEFT)));
571 summary_arrows_left_left->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
573 Button* summary_arrows_left_right = manage (new Button);
574 summary_arrows_left_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
575 summary_arrows_left_right->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), RIGHT)));
576 summary_arrows_left_right->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
578 VBox* summary_arrows_left = manage (new VBox);
579 summary_arrows_left->pack_start (*summary_arrows_left_left);
580 summary_arrows_left->pack_start (*summary_arrows_left_right);
582 Button* summary_arrows_right_up = manage (new Button);
583 summary_arrows_right_up->add (*manage (new Arrow (ARROW_UP, SHADOW_NONE)));
584 summary_arrows_right_up->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), UP)));
585 summary_arrows_right_up->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
587 Button* summary_arrows_right_down = manage (new Button);
588 summary_arrows_right_down->add (*manage (new Arrow (ARROW_DOWN, SHADOW_NONE)));
589 summary_arrows_right_down->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), DOWN)));
590 summary_arrows_right_down->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
592 VBox* summary_arrows_right = manage (new VBox);
593 summary_arrows_right->pack_start (*summary_arrows_right_up);
594 summary_arrows_right->pack_start (*summary_arrows_right_down);
596 Frame* summary_frame = manage (new Frame);
597 summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN);
599 summary_frame->add (*_summary);
600 summary_frame->show ();
602 _summary_hbox.pack_start (*summary_arrows_left, false, false);
603 _summary_hbox.pack_start (*summary_frame, true, true);
604 _summary_hbox.pack_start (*summary_arrows_right, false, false);
606 editor_summary_pane.pack2 (_summary_hbox);
608 edit_pane.pack1 (editor_summary_pane, true, true);
609 edit_pane.pack2 (_the_notebook, false, true);
611 editor_summary_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun (*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&editor_summary_pane)));
613 /* XXX: editor_summary_pane might need similar special OS X treatment to the edit_pane */
615 edit_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&edit_pane)));
616 #ifdef GTKOSX
617 Glib::PropertyProxy<int> proxy = edit_pane.property_position();
618 proxy.signal_changed().connect (bind (sigc::ptr_fun (pane_size_watcher), static_cast<Paned*> (&edit_pane)));
619 #endif
620 top_hbox.pack_start (toolbar_frame);
622 HBox *hbox = manage (new HBox);
623 hbox->pack_start (edit_pane, true, true);
625 global_vpacker.pack_start (top_hbox, false, false);
626 global_vpacker.pack_start (*hbox, true, true);
628 global_hpacker.pack_start (global_vpacker, true, true);
630 set_name ("EditorWindow");
631 add_accel_group (ActionManager::ui_manager->get_accel_group());
633 status_bar_hpacker.show ();
635 vpacker.pack_end (status_bar_hpacker, false, false);
636 vpacker.pack_end (global_hpacker, true, true);
638 /* register actions now so that set_state() can find them and set toggles/checks etc */
640 register_actions ();
642 setup_toolbar ();
643 setup_midi_toolbar ();
645 _snap_type = SnapToBeat;
646 set_snap_to (_snap_type);
647 _snap_mode = SnapOff;
648 set_snap_mode (_snap_mode);
649 set_mouse_mode (MouseObject, true);
650 pre_internal_mouse_mode = MouseObject;
651 set_edit_point_preference (EditAtMouse, true);
653 _playlist_selector = new PlaylistSelector();
654 _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
656 RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), ui_bind (&Editor::catch_vanishing_regionview, this, _1), gui_context());
658 /* nudge stuff */
660 nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
661 nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
663 nudge_forward_button.set_name ("TransportButton");
664 nudge_backward_button.set_name ("TransportButton");
666 fade_context_menu.set_name ("ArdourContextMenu");
668 /* icons, titles, WM stuff */
670 list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
671 Glib::RefPtr<Gdk::Pixbuf> icon;
673 if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
674 window_icons.push_back (icon);
676 if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
677 window_icons.push_back (icon);
679 if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
680 window_icons.push_back (icon);
682 if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
683 window_icons.push_back (icon);
685 if (!window_icons.empty()) {
686 set_icon_list (window_icons);
687 set_default_icon_list (window_icons);
690 WindowTitle title(Glib::get_application_name());
691 title += _("Editor");
692 set_title (title.get_string());
693 set_wmclass (X_("ardour_editor"), PROGRAM_NAME);
695 add (vpacker);
696 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
698 signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
699 signal_delete_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
701 /* allow external control surfaces/protocols to do various things */
703 ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context());
704 ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context());
705 ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context());
706 ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), ui_bind (&Editor::control_scroll, this, _1), gui_context());
707 BasicUI::AccessAction.connect (*this, invalidator (*this), ui_bind (&Editor::access_action, this, _1, _2), gui_context());
709 /* problematic: has to return a value and thus cannot be x-thread */
711 Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
713 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
715 TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
717 _ignore_region_action = false;
718 _last_region_menu_was_main = false;
719 _popup_region_menu_item = 0;
721 _show_marker_lines = false;
722 _over_region_trim_target = false;
724 /* Button bindings */
726 button_bindings = new Bindings;
728 XMLNode* node = button_settings();
729 if (node) {
730 for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) {
731 button_bindings->load (**i);
735 constructed = true;
736 instant_save ();
738 setup_fade_images ();
741 Editor::~Editor()
743 #ifdef WITH_CMT
744 if(image_socket_listener) {
745 if(image_socket_listener->is_connected())
747 image_socket_listener->close_connection() ;
750 delete image_socket_listener ;
751 image_socket_listener = 0 ;
753 #endif
755 delete button_bindings;
756 delete _routes;
757 delete _route_groups;
758 delete track_canvas;
759 delete _drags;
762 XMLNode*
763 Editor::button_settings () const
765 XMLNode* settings = ARDOUR_UI::instance()->editor_settings();
766 XMLNode* node = find_named_node (*settings, X_("Buttons"));
768 if (!node) {
769 cerr << "new empty Button node\n";
770 node = new XMLNode (X_("Buttons"));
773 return node;
776 void
777 Editor::add_toplevel_controls (Container& cont)
779 vpacker.pack_start (cont, false, false);
780 cont.show_all ();
783 void
784 Editor::catch_vanishing_regionview (RegionView *rv)
786 /* note: the selection will take care of the vanishing
787 audioregionview by itself.
790 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
791 _drags->abort ();
794 if (clicked_regionview == rv) {
795 clicked_regionview = 0;
798 if (entered_regionview == rv) {
799 set_entered_regionview (0);
802 if (!_all_region_actions_sensitized) {
803 sensitize_all_region_actions (true);
807 void
808 Editor::set_entered_regionview (RegionView* rv)
810 if (rv == entered_regionview) {
811 return;
814 if (entered_regionview) {
815 entered_regionview->exited ();
818 if ((entered_regionview = rv) != 0) {
819 entered_regionview->entered (internal_editing ());
822 if (!_all_region_actions_sensitized && _last_region_menu_was_main) {
823 /* This RegionView entry might have changed what region actions
824 are allowed, so sensitize them all in case a key is pressed.
826 sensitize_all_region_actions (true);
830 void
831 Editor::set_entered_track (TimeAxisView* tav)
833 if (entered_track) {
834 entered_track->exited ();
837 if ((entered_track = tav) != 0) {
838 entered_track->entered ();
842 void
843 Editor::show_window ()
845 if (!is_visible ()) {
846 show_all ();
848 /* XXX: this is a bit unfortunate; it would probably
849 be nicer if we could just call show () above rather
850 than needing the show_all ()
853 /* re-hide stuff if necessary */
854 editor_list_button_toggled ();
855 parameter_changed ("show-summary");
856 parameter_changed ("show-group-tabs");
857 parameter_changed ("show-zoom-tools");
859 /* now reset all audio_time_axis heights, because widgets might need
860 to be re-hidden
863 TimeAxisView *tv;
865 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
866 tv = (static_cast<TimeAxisView*>(*i));
867 tv->reset_height ();
870 if (current_mixer_strip) {
871 current_mixer_strip->hide_things ();
875 present ();
878 void
879 Editor::instant_save ()
881 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
882 return;
885 if (_session) {
886 _session->add_instant_xml(get_state());
887 } else {
888 Config->add_instant_xml(get_state());
892 void
893 Editor::zoom_adjustment_changed ()
895 if (_session == 0) {
896 return;
899 double fpu = zoom_range_clock.current_duration() / _canvas_width;
901 if (fpu < 1.0) {
902 fpu = 1.0;
903 zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width));
904 } else if (fpu > _session->current_end_frame() / _canvas_width) {
905 fpu = _session->current_end_frame() / _canvas_width;
906 zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width));
909 temporal_zoom (fpu);
912 void
913 Editor::control_scroll (float fraction)
915 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
917 if (!_session) {
918 return;
921 double step = fraction * current_page_frames();
924 _control_scroll_target is an optional<T>
926 it acts like a pointer to an framepos_t, with
927 a operator conversion to boolean to check
928 that it has a value could possibly use
929 playhead_cursor->current_frame to store the
930 value and a boolean in the class to know
931 when it's out of date
934 if (!_control_scroll_target) {
935 _control_scroll_target = _session->transport_frame();
936 _dragging_playhead = true;
939 if ((fraction < 0.0f) && (*_control_scroll_target < (framepos_t) fabs(step))) {
940 *_control_scroll_target = 0;
941 } else if ((fraction > 0.0f) && (max_framepos - *_control_scroll_target < step)) {
942 *_control_scroll_target = max_framepos - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
943 } else {
944 *_control_scroll_target += (framepos_t) floor (step);
947 /* move visuals, we'll catch up with it later */
949 playhead_cursor->set_position (*_control_scroll_target);
950 UpdateAllTransportClocks (*_control_scroll_target);
952 if (*_control_scroll_target > (current_page_frames() / 2)) {
953 /* try to center PH in window */
954 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
955 } else {
956 reset_x_origin (0);
960 Now we do a timeout to actually bring the session to the right place
961 according to the playhead. This is to avoid reading disk buffers on every
962 call to control_scroll, which is driven by ScrollTimeline and therefore
963 probably by a control surface wheel which can generate lots of events.
965 /* cancel the existing timeout */
967 control_scroll_connection.disconnect ();
969 /* add the next timeout */
971 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
974 bool
975 Editor::deferred_control_scroll (framepos_t /*target*/)
977 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
978 // reset for next stream
979 _control_scroll_target = boost::none;
980 _dragging_playhead = false;
981 return false;
984 void
985 Editor::access_action (std::string action_group, std::string action_item)
987 if (!_session) {
988 return;
991 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
993 RefPtr<Action> act;
994 act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
996 if (act) {
997 act->activate();
1001 void
1002 Editor::on_realize ()
1004 Window::on_realize ();
1005 Realized ();
1008 void
1009 Editor::map_position_change (framepos_t frame)
1011 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, frame)
1013 if (_session == 0) {
1014 return;
1017 if (_follow_playhead) {
1018 center_screen (frame);
1021 playhead_cursor->set_position (frame);
1024 void
1025 Editor::center_screen (framepos_t frame)
1027 double page = _canvas_width * frames_per_unit;
1029 /* if we're off the page, then scroll.
1032 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
1033 center_screen_internal (frame, page);
1037 void
1038 Editor::center_screen_internal (framepos_t frame, float page)
1040 page /= 2;
1042 if (frame > page) {
1043 frame -= (framepos_t) page;
1044 } else {
1045 frame = 0;
1048 reset_x_origin (frame);
1052 void
1053 Editor::update_title ()
1055 ENSURE_GUI_THREAD (*this, &Editor::update_title)
1057 if (_session) {
1058 bool dirty = _session->dirty();
1060 string session_name;
1062 if (_session->snap_name() != _session->name()) {
1063 session_name = _session->snap_name();
1064 } else {
1065 session_name = _session->name();
1068 if (dirty) {
1069 session_name = "*" + session_name;
1072 WindowTitle title(session_name);
1073 title += Glib::get_application_name();
1074 set_title (title.get_string());
1078 void
1079 Editor::set_session (Session *t)
1081 SessionHandlePtr::set_session (t);
1083 if (!_session) {
1084 return;
1087 zoom_range_clock.set_session (_session);
1088 _playlist_selector->set_session (_session);
1089 nudge_clock.set_session (_session);
1090 _summary->set_session (_session);
1091 _group_tabs->set_session (_session);
1092 _route_groups->set_session (_session);
1093 _regions->set_session (_session);
1094 _snapshots->set_session (_session);
1095 _routes->set_session (_session);
1096 _locations->set_session (_session);
1098 if (rhythm_ferret) {
1099 rhythm_ferret->set_session (_session);
1102 if (analysis_window) {
1103 analysis_window->set_session (_session);
1106 if (sfbrowser) {
1107 sfbrowser->set_session (_session);
1110 compute_fixed_ruler_scale ();
1112 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1113 set_state (*node, Stateful::loading_state_version);
1115 /* catch up with the playhead */
1117 _session->request_locate (playhead_cursor->current_frame);
1118 _pending_initial_locate = true;
1120 update_title ();
1122 /* These signals can all be emitted by a non-GUI thread. Therefore the
1123 handlers for them must not attempt to directly interact with the GUI,
1124 but use Gtkmm2ext::UI::instance()->call_slot();
1127 _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), ui_bind(&Editor::step_edit_status_change, this, _1), gui_context());
1128 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1129 _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context());
1130 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context());
1131 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1132 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context());
1133 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1134 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
1135 _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Editor::session_state_saved, this, _1), gui_context());
1136 _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context());
1137 _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&Editor::location_gone, this, _1), gui_context());
1138 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1139 _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display_s, this, _1), gui_context());
1140 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1142 if (Profile->get_sae()) {
1143 Timecode::BBT_Time bbt;
1144 bbt.bars = 0;
1145 bbt.beats = 0;
1146 bbt.ticks = 120;
1147 framepos_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1);
1148 nudge_clock.set_mode(AudioClock::BBT);
1149 nudge_clock.set (pos, true, 0, AudioClock::BBT);
1151 } else {
1152 nudge_clock.set (_session->frame_rate() * 5, true, 0, AudioClock::Timecode); // default of 5 seconds
1155 playhead_cursor->canvas_item.show ();
1157 Location* loc = _session->locations()->auto_loop_location();
1158 if (loc == 0) {
1159 loc = new Location (*_session, 0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1161 if (loc->start() == loc->end()) {
1162 loc->set_end (loc->start() + 1);
1165 _session->locations()->add (loc, false);
1166 _session->set_auto_loop_location (loc);
1167 } else {
1168 // force name
1169 loc->set_name (_("Loop"));
1172 loc = _session->locations()->auto_punch_location();
1174 if (loc == 0) {
1175 loc = new Location (*_session, 0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1177 if (loc->start() == loc->end()) {
1178 loc->set_end (loc->start() + 1);
1181 _session->locations()->add (loc, false);
1182 _session->set_auto_punch_location (loc);
1183 } else {
1184 // force name
1185 loc->set_name (_("Punch"));
1188 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1189 Config->map_parameters (pc);
1190 _session->config.map_parameters (pc);
1192 refresh_location_display ();
1194 restore_ruler_visibility ();
1195 //tempo_map_changed (PropertyChange (0));
1196 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1198 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1199 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1202 super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
1203 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1206 switch (_snap_type) {
1207 case SnapToRegionStart:
1208 case SnapToRegionEnd:
1209 case SnapToRegionSync:
1210 case SnapToRegionBoundary:
1211 build_region_boundary_cache ();
1212 break;
1214 default:
1215 break;
1218 /* register for undo history */
1219 _session->register_with_memento_command_factory(_id, this);
1221 ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated));
1223 start_updating_meters ();
1226 void
1227 Editor::action_pre_activated (Glib::RefPtr<Action> const & a)
1229 if (a->get_name() == "RegionMenu") {
1230 /* When the main menu's region menu is opened, we setup the actions so that they look right
1231 in the menu. I can't find a way of getting a signal when this menu is subsequently closed,
1232 so we resensitize all region actions when the entered regionview or the region selection
1233 changes. HOWEVER we can't always resensitize on entered_regionview change because that
1234 happens after the region context menu is opened. So we set a flag here, too.
1236 What a carry on :(
1238 sensitize_the_right_region_actions ();
1239 _last_region_menu_was_main = true;
1243 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1244 void
1245 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1247 using namespace Menu_Helpers;
1248 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1250 if (arv == 0) {
1251 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1252 /*NOTREACHED*/
1255 MenuList& items (fade_context_menu.items());
1257 items.clear ();
1259 switch (item_type) {
1260 case FadeInItem:
1261 case FadeInHandleItem:
1262 if (arv->audio_region()->fade_in_active()) {
1263 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1264 } else {
1265 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1268 items.push_back (SeparatorElem());
1270 if (Profile->get_sae()) {
1272 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)));
1273 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)));
1275 } else {
1277 items.push_back (
1278 ImageMenuElem (
1279 _("Linear"),
1280 *_fade_in_images[FadeLinear],
1281 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)
1285 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1287 items.push_back (
1288 ImageMenuElem (
1289 _("Slowest"),
1290 *_fade_in_images[FadeFast],
1291 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)
1294 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1296 items.push_back (
1297 ImageMenuElem (
1298 _("Slow"),
1299 *_fade_in_images[FadeLogB],
1300 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogB)
1303 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1305 items.push_back (
1306 ImageMenuElem (
1307 _("Fast"),
1308 *_fade_in_images[FadeLogA],
1309 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogA)
1312 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1314 items.push_back (
1315 ImageMenuElem (
1316 _("Fastest"),
1317 *_fade_in_images[FadeSlow],
1318 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow)
1321 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1324 break;
1326 case FadeOutItem:
1327 case FadeOutHandleItem:
1328 if (arv->audio_region()->fade_out_active()) {
1329 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1330 } else {
1331 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1334 items.push_back (SeparatorElem());
1336 if (Profile->get_sae()) {
1337 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)));
1338 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)));
1339 } else {
1341 items.push_back (
1342 ImageMenuElem (
1343 _("Linear"),
1344 *_fade_out_images[FadeLinear],
1345 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)
1349 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1351 items.push_back (
1352 ImageMenuElem (
1353 _("Slowest"),
1354 *_fade_out_images[FadeFast],
1355 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)
1358 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1360 items.push_back (
1361 ImageMenuElem (
1362 _("Slow"),
1363 *_fade_out_images[FadeLogB],
1364 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogA)
1367 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1369 items.push_back (
1370 ImageMenuElem (
1371 _("Fast"),
1372 *_fade_out_images[FadeLogA],
1373 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogB)
1376 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1378 items.push_back (
1379 ImageMenuElem (
1380 _("Fastest"),
1381 *_fade_out_images[FadeSlow],
1382 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast)
1385 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1388 break;
1390 default:
1391 fatal << _("programming error: ")
1392 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1393 << endmsg;
1394 /*NOTREACHED*/
1397 fade_context_menu.popup (button, time);
1400 void
1401 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1403 using namespace Menu_Helpers;
1404 Menu* (Editor::*build_menu_function)();
1405 Menu *menu;
1407 switch (item_type) {
1408 case RegionItem:
1409 case RegionViewName:
1410 case RegionViewNameHighlight:
1411 case LeftFrameHandle:
1412 case RightFrameHandle:
1413 if (with_selection) {
1414 build_menu_function = &Editor::build_track_selection_context_menu;
1415 } else {
1416 build_menu_function = &Editor::build_track_region_context_menu;
1418 break;
1420 case SelectionItem:
1421 if (with_selection) {
1422 build_menu_function = &Editor::build_track_selection_context_menu;
1423 } else {
1424 build_menu_function = &Editor::build_track_context_menu;
1426 break;
1428 case CrossfadeViewItem:
1429 build_menu_function = &Editor::build_track_crossfade_context_menu;
1430 break;
1432 case StreamItem:
1433 if (clicked_routeview->track()) {
1434 build_menu_function = &Editor::build_track_context_menu;
1435 } else {
1436 build_menu_function = &Editor::build_track_bus_context_menu;
1438 break;
1440 default:
1441 /* probably shouldn't happen but if it does, we don't care */
1442 return;
1445 menu = (this->*build_menu_function)();
1446 menu->set_name ("ArdourContextMenu");
1448 /* now handle specific situations */
1450 switch (item_type) {
1451 case RegionItem:
1452 case RegionViewName:
1453 case RegionViewNameHighlight:
1454 case LeftFrameHandle:
1455 case RightFrameHandle:
1456 if (!with_selection) {
1457 if (region_edit_menu_split_item) {
1458 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1459 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1460 } else {
1461 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1464 if (region_edit_menu_split_multichannel_item) {
1465 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1466 region_edit_menu_split_multichannel_item->set_sensitive (true);
1467 } else {
1468 region_edit_menu_split_multichannel_item->set_sensitive (false);
1472 break;
1474 case SelectionItem:
1475 break;
1477 case CrossfadeViewItem:
1478 break;
1480 case StreamItem:
1481 break;
1483 default:
1484 /* probably shouldn't happen but if it does, we don't care */
1485 return;
1488 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1490 /* Bounce to disk */
1492 using namespace Menu_Helpers;
1493 MenuList& edit_items = menu->items();
1495 edit_items.push_back (SeparatorElem());
1497 switch (clicked_routeview->audio_track()->freeze_state()) {
1498 case AudioTrack::NoFreeze:
1499 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1500 break;
1502 case AudioTrack::Frozen:
1503 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1504 break;
1506 case AudioTrack::UnFrozen:
1507 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1508 break;
1513 if (item_type == StreamItem && clicked_routeview) {
1514 clicked_routeview->build_underlay_menu(menu);
1517 /* When the region menu is opened, we setup the actions so that they look right
1518 in the menu.
1520 sensitize_the_right_region_actions ();
1521 _last_region_menu_was_main = false;
1523 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1524 menu->popup (button, time);
1527 Menu*
1528 Editor::build_track_context_menu ()
1530 using namespace Menu_Helpers;
1532 MenuList& edit_items = track_context_menu.items();
1533 edit_items.clear();
1535 add_dstream_context_items (edit_items);
1536 return &track_context_menu;
1539 Menu*
1540 Editor::build_track_bus_context_menu ()
1542 using namespace Menu_Helpers;
1544 MenuList& edit_items = track_context_menu.items();
1545 edit_items.clear();
1547 add_bus_context_items (edit_items);
1548 return &track_context_menu;
1551 Menu*
1552 Editor::build_track_region_context_menu ()
1554 using namespace Menu_Helpers;
1555 MenuList& edit_items = track_region_context_menu.items();
1556 edit_items.clear();
1558 /* we've just cleared the track region context menu, so the menu that these
1559 two items were on will have disappeared; stop them dangling.
1561 region_edit_menu_split_item = 0;
1562 region_edit_menu_split_multichannel_item = 0;
1564 /* we might try to use items that are currently attached to a crossfade menu,
1565 so clear that, too.
1567 track_crossfade_context_menu.items().clear ();
1569 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1571 if (rtv) {
1572 boost::shared_ptr<Track> tr;
1573 boost::shared_ptr<Playlist> pl;
1575 if ((tr = rtv->track())) {
1576 add_region_context_items (edit_items, tr);
1580 add_dstream_context_items (edit_items);
1582 return &track_region_context_menu;
1585 Menu*
1586 Editor::build_track_crossfade_context_menu ()
1588 using namespace Menu_Helpers;
1589 MenuList& edit_items = track_crossfade_context_menu.items();
1590 edit_items.clear ();
1592 /* we might try to use items that are currently attached to a crossfade menu,
1593 so clear that, too.
1595 track_region_context_menu.items().clear ();
1597 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1599 if (atv) {
1600 boost::shared_ptr<Track> tr;
1601 boost::shared_ptr<Playlist> pl;
1602 boost::shared_ptr<AudioPlaylist> apl;
1604 if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1606 AudioPlaylist::Crossfades xfades;
1607 framepos_t where;
1608 bool ignored;
1610 /* The xfade menu is a bit of a special case, as we always use the mouse position
1611 to decide whether or not to display it (rather than the edit point). No particularly
1612 strong reasons for this, other than it is a bit surprising to right-click on a xfade
1613 and not get a menu.
1615 mouse_frame (where, ignored);
1616 apl->crossfades_at (where, xfades);
1618 bool const many = xfades.size() > 1;
1620 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1621 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1624 add_region_context_items (edit_items, tr);
1628 add_dstream_context_items (edit_items);
1630 return &track_crossfade_context_menu;
1633 void
1634 Editor::analyze_region_selection ()
1636 if (analysis_window == 0) {
1637 analysis_window = new AnalysisWindow();
1639 if (_session != 0)
1640 analysis_window->set_session(_session);
1642 analysis_window->show_all();
1645 analysis_window->set_regionmode();
1646 analysis_window->analyze();
1648 analysis_window->present();
1651 void
1652 Editor::analyze_range_selection()
1654 if (analysis_window == 0) {
1655 analysis_window = new AnalysisWindow();
1657 if (_session != 0)
1658 analysis_window->set_session(_session);
1660 analysis_window->show_all();
1663 analysis_window->set_rangemode();
1664 analysis_window->analyze();
1666 analysis_window->present();
1669 Menu*
1670 Editor::build_track_selection_context_menu ()
1672 using namespace Menu_Helpers;
1673 MenuList& edit_items = track_selection_context_menu.items();
1674 edit_items.clear ();
1676 add_selection_context_items (edit_items);
1677 // edit_items.push_back (SeparatorElem());
1678 // add_dstream_context_items (edit_items);
1680 return &track_selection_context_menu;
1683 /** Add context menu items relevant to crossfades.
1684 * @param edit_items List to add the items to.
1686 void
1687 Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1689 using namespace Menu_Helpers;
1690 Menu *xfade_menu = manage (new Menu);
1691 MenuList& items = xfade_menu->items();
1692 xfade_menu->set_name ("ArdourContextMenu");
1693 string str;
1695 if (xfade->active()) {
1696 str = _("Mute");
1697 } else {
1698 str = _("Unmute");
1701 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1702 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1704 if (xfade->can_follow_overlap()) {
1706 if (xfade->following_overlap()) {
1707 str = _("Convert to Short");
1708 } else {
1709 str = _("Convert to Full");
1712 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1715 if (many) {
1716 str = xfade->out()->name();
1717 str += "->";
1718 str += xfade->in()->name();
1719 } else {
1720 str = _("Crossfade");
1723 edit_items.push_back (MenuElem (str, *xfade_menu));
1724 edit_items.push_back (SeparatorElem());
1727 void
1728 Editor::xfade_edit_left_region ()
1730 if (clicked_crossfadeview) {
1731 clicked_crossfadeview->left_view.show_region_editor ();
1735 void
1736 Editor::xfade_edit_right_region ()
1738 if (clicked_crossfadeview) {
1739 clicked_crossfadeview->right_view.show_region_editor ();
1743 void
1744 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::shared_ptr<Track> track)
1746 using namespace Menu_Helpers;
1748 /* OK, stick the region submenu at the top of the list, and then add
1749 the standard items.
1752 RegionSelection rs = get_regions_from_selection_and_entered ();
1754 string::size_type pos = 0;
1755 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1757 /* we have to hack up the region name because "_" has a special
1758 meaning for menu titles.
1761 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1762 menu_item_name.replace (pos, 1, "__");
1763 pos += 2;
1766 if (_popup_region_menu_item == 0) {
1767 _popup_region_menu_item = new MenuItem (menu_item_name);
1768 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1769 _popup_region_menu_item->show ();
1770 } else {
1771 _popup_region_menu_item->set_label (menu_item_name);
1774 /* Use the mouse position rather than the edit point to decide whether to show the `choose top region'
1775 dialogue. If we use the edit point it gets a bit messy because the user still has to click over
1776 *some* region in order to get the region context menu stuff to be displayed at all.
1779 framepos_t mouse;
1780 bool ignored;
1781 mouse_frame (mouse, ignored);
1783 edit_items.push_back (*_popup_region_menu_item);
1784 if (track->playlist()->count_regions_at (mouse) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1785 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region")->create_menu_item ()));
1787 edit_items.push_back (SeparatorElem());
1790 /** Add context menu items relevant to selection ranges.
1791 * @param edit_items List to add the items to.
1793 void
1794 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1796 using namespace Menu_Helpers;
1798 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1799 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1801 edit_items.push_back (SeparatorElem());
1802 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1804 if (!selection->regions.empty()) {
1805 edit_items.push_back (SeparatorElem());
1806 edit_items.push_back (MenuElem (_("Extend Range to End of Region"), sigc::bind (sigc::mem_fun(*this, &Editor::extend_selection_to_end_of_region), false)));
1807 edit_items.push_back (MenuElem (_("Extend Range to Start of Region"), sigc::bind (sigc::mem_fun(*this, &Editor::extend_selection_to_start_of_region), false)));
1810 edit_items.push_back (SeparatorElem());
1811 edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection)));
1812 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1814 edit_items.push_back (SeparatorElem());
1815 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1817 edit_items.push_back (SeparatorElem());
1818 edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1819 edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1821 edit_items.push_back (SeparatorElem());
1822 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1824 edit_items.push_back (SeparatorElem());
1825 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1826 edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1827 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1829 edit_items.push_back (SeparatorElem());
1830 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1831 edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1832 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1833 edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1834 edit_items.push_back (MenuElem (_("Export Range"), sigc::mem_fun(*this, &Editor::export_selection)));
1838 void
1839 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1841 using namespace Menu_Helpers;
1843 /* Playback */
1845 Menu *play_menu = manage (new Menu);
1846 MenuList& play_items = play_menu->items();
1847 play_menu->set_name ("ArdourContextMenu");
1849 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1850 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1851 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1852 play_items.push_back (SeparatorElem());
1853 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
1855 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1857 /* Selection */
1859 Menu *select_menu = manage (new Menu);
1860 MenuList& select_items = select_menu->items();
1861 select_menu->set_name ("ArdourContextMenu");
1863 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1864 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1865 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1866 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1867 select_items.push_back (SeparatorElem());
1868 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
1869 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
1870 select_items.push_back (SeparatorElem());
1871 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1872 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1873 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1874 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1875 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
1876 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
1877 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
1879 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1881 /* Cut-n-Paste */
1883 Menu *cutnpaste_menu = manage (new Menu);
1884 MenuList& cutnpaste_items = cutnpaste_menu->items();
1885 cutnpaste_menu->set_name ("ArdourContextMenu");
1887 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1888 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1889 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1891 cutnpaste_items.push_back (SeparatorElem());
1893 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
1894 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
1896 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
1898 /* Adding new material */
1900 edit_items.push_back (SeparatorElem());
1901 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
1902 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
1904 /* Nudge track */
1906 Menu *nudge_menu = manage (new Menu());
1907 MenuList& nudge_items = nudge_menu->items();
1908 nudge_menu->set_name ("ArdourContextMenu");
1910 edit_items.push_back (SeparatorElem());
1911 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1912 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1913 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1914 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1916 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1919 void
1920 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
1922 using namespace Menu_Helpers;
1924 /* Playback */
1926 Menu *play_menu = manage (new Menu);
1927 MenuList& play_items = play_menu->items();
1928 play_menu->set_name ("ArdourContextMenu");
1930 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1931 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1932 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1934 /* Selection */
1936 Menu *select_menu = manage (new Menu);
1937 MenuList& select_items = select_menu->items();
1938 select_menu->set_name ("ArdourContextMenu");
1940 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1941 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1942 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1943 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1944 select_items.push_back (SeparatorElem());
1945 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1946 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1947 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1948 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1950 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1952 /* Cut-n-Paste */
1954 Menu *cutnpaste_menu = manage (new Menu);
1955 MenuList& cutnpaste_items = cutnpaste_menu->items();
1956 cutnpaste_menu->set_name ("ArdourContextMenu");
1958 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1959 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1960 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1962 Menu *nudge_menu = manage (new Menu());
1963 MenuList& nudge_items = nudge_menu->items();
1964 nudge_menu->set_name ("ArdourContextMenu");
1966 edit_items.push_back (SeparatorElem());
1967 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1968 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1969 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1970 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1972 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1975 SnapType
1976 Editor::snap_type() const
1978 return _snap_type;
1981 SnapMode
1982 Editor::snap_mode() const
1984 return _snap_mode;
1987 void
1988 Editor::set_snap_to (SnapType st)
1990 unsigned int snap_ind = (unsigned int)st;
1992 _snap_type = st;
1994 if (snap_ind > snap_type_strings.size() - 1) {
1995 snap_ind = 0;
1996 _snap_type = (SnapType)snap_ind;
1999 string str = snap_type_strings[snap_ind];
2001 if (str != snap_type_selector.get_active_text()) {
2002 snap_type_selector.set_active_text (str);
2005 instant_save ();
2007 switch (_snap_type) {
2008 case SnapToBeatDiv32:
2009 case SnapToBeatDiv28:
2010 case SnapToBeatDiv24:
2011 case SnapToBeatDiv20:
2012 case SnapToBeatDiv16:
2013 case SnapToBeatDiv14:
2014 case SnapToBeatDiv12:
2015 case SnapToBeatDiv10:
2016 case SnapToBeatDiv8:
2017 case SnapToBeatDiv7:
2018 case SnapToBeatDiv6:
2019 case SnapToBeatDiv5:
2020 case SnapToBeatDiv4:
2021 case SnapToBeatDiv3:
2022 case SnapToBeatDiv2:
2023 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
2024 update_tempo_based_rulers ();
2025 break;
2027 case SnapToRegionStart:
2028 case SnapToRegionEnd:
2029 case SnapToRegionSync:
2030 case SnapToRegionBoundary:
2031 build_region_boundary_cache ();
2032 break;
2034 default:
2035 /* relax */
2036 break;
2039 SnapChanged (); /* EMIT SIGNAL */
2042 void
2043 Editor::set_snap_mode (SnapMode mode)
2045 _snap_mode = mode;
2046 string str = snap_mode_strings[(int)mode];
2048 if (str != snap_mode_selector.get_active_text ()) {
2049 snap_mode_selector.set_active_text (str);
2052 instant_save ();
2054 void
2055 Editor::set_edit_point_preference (EditPoint ep, bool force)
2057 bool changed = (_edit_point != ep);
2059 _edit_point = ep;
2060 string str = edit_point_strings[(int)ep];
2062 if (str != edit_point_selector.get_active_text ()) {
2063 edit_point_selector.set_active_text (str);
2066 set_canvas_cursor ();
2068 if (!force && !changed) {
2069 return;
2072 const char* action=NULL;
2074 switch (_edit_point) {
2075 case EditAtPlayhead:
2076 action = "edit-at-playhead";
2077 break;
2078 case EditAtSelectedMarker:
2079 action = "edit-at-marker";
2080 break;
2081 case EditAtMouse:
2082 action = "edit-at-mouse";
2083 break;
2086 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2087 if (act) {
2088 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2091 framepos_t foo;
2092 bool in_track_canvas;
2094 if (!mouse_frame (foo, in_track_canvas)) {
2095 in_track_canvas = false;
2098 reset_canvas_action_sensitivity (in_track_canvas);
2100 instant_save ();
2104 Editor::set_state (const XMLNode& node, int /*version*/)
2106 const XMLProperty* prop;
2107 XMLNode* geometry;
2108 int x, y, xoff, yoff;
2109 Gdk::Geometry g;
2111 if ((prop = node.property ("id")) != 0) {
2112 _id = prop->value ();
2115 g.base_width = default_width;
2116 g.base_height = default_height;
2117 x = 1;
2118 y = 1;
2119 xoff = 0;
2120 yoff = 21;
2122 if ((geometry = find_named_node (node, "geometry")) != 0) {
2124 XMLProperty* prop;
2126 if ((prop = geometry->property("x_size")) == 0) {
2127 prop = geometry->property ("x-size");
2129 if (prop) {
2130 g.base_width = atoi(prop->value());
2132 if ((prop = geometry->property("y_size")) == 0) {
2133 prop = geometry->property ("y-size");
2135 if (prop) {
2136 g.base_height = atoi(prop->value());
2139 if ((prop = geometry->property ("x_pos")) == 0) {
2140 prop = geometry->property ("x-pos");
2142 if (prop) {
2143 x = atoi (prop->value());
2146 if ((prop = geometry->property ("y_pos")) == 0) {
2147 prop = geometry->property ("y-pos");
2149 if (prop) {
2150 y = atoi (prop->value());
2153 if ((prop = geometry->property ("x_off")) == 0) {
2154 prop = geometry->property ("x-off");
2156 if (prop) {
2157 xoff = atoi (prop->value());
2159 if ((prop = geometry->property ("y_off")) == 0) {
2160 prop = geometry->property ("y-off");
2162 if (prop) {
2163 yoff = atoi (prop->value());
2167 set_default_size (g.base_width, g.base_height);
2168 move (x, y);
2170 if (_session && (prop = node.property ("playhead"))) {
2171 framepos_t pos;
2172 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2173 playhead_cursor->set_position (pos);
2174 } else {
2175 playhead_cursor->set_position (0);
2178 if ((prop = node.property ("mixer-width"))) {
2179 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2182 if ((prop = node.property ("zoom-focus"))) {
2183 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2186 if ((prop = node.property ("zoom"))) {
2187 reset_zoom (PBD::atof (prop->value()));
2188 } else {
2189 reset_zoom (frames_per_unit);
2192 if ((prop = node.property ("snap-to"))) {
2193 set_snap_to ((SnapType) atoi (prop->value()));
2196 if ((prop = node.property ("snap-mode"))) {
2197 set_snap_mode ((SnapMode) atoi (prop->value()));
2200 if ((prop = node.property ("mouse-mode"))) {
2201 MouseMode m = str2mousemode(prop->value());
2202 set_mouse_mode (m, true);
2203 } else {
2204 set_mouse_mode (MouseObject, true);
2207 if ((prop = node.property ("left-frame")) != 0) {
2208 framepos_t pos;
2209 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2210 reset_x_origin (pos);
2214 if ((prop = node.property ("y-origin")) != 0) {
2215 reset_y_origin (atof (prop->value ()));
2218 if ((prop = node.property ("internal-edit"))) {
2219 bool yn = string_is_affirmative (prop->value());
2220 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2221 if (act) {
2222 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2223 tact->set_active (!yn);
2224 tact->set_active (yn);
2228 if ((prop = node.property ("join-object-range"))) {
2229 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2232 if ((prop = node.property ("edit-point"))) {
2233 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2236 if ((prop = node.property ("show-measures"))) {
2237 bool yn = string_is_affirmative (prop->value());
2238 _show_measures = yn;
2239 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2240 if (act) {
2241 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2242 /* do it twice to force the change */
2243 tact->set_active (!yn);
2244 tact->set_active (yn);
2248 if ((prop = node.property ("follow-playhead"))) {
2249 bool yn = string_is_affirmative (prop->value());
2250 set_follow_playhead (yn);
2251 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2252 if (act) {
2253 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2254 if (tact->get_active() != yn) {
2255 tact->set_active (yn);
2260 if ((prop = node.property ("stationary-playhead"))) {
2261 bool yn = (prop->value() == "yes");
2262 set_stationary_playhead (yn);
2263 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2264 if (act) {
2265 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2266 if (tact->get_active() != yn) {
2267 tact->set_active (yn);
2272 if ((prop = node.property ("region-list-sort-type"))) {
2273 RegionListSortType st;
2274 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2277 if ((prop = node.property ("xfades-visible"))) {
2278 bool yn = string_is_affirmative (prop->value());
2279 _xfade_visibility = !yn;
2280 // set_xfade_visibility (yn);
2283 if ((prop = node.property ("show-editor-mixer"))) {
2285 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2286 assert (act);
2288 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2289 bool yn = string_is_affirmative (prop->value());
2291 /* do it twice to force the change */
2293 tact->set_active (!yn);
2294 tact->set_active (yn);
2297 if ((prop = node.property ("show-editor-list"))) {
2299 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2300 assert (act);
2302 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2303 bool yn = string_is_affirmative (prop->value());
2305 /* do it twice to force the change */
2307 tact->set_active (!yn);
2308 tact->set_active (yn);
2311 if ((prop = node.property (X_("editor-list-page")))) {
2312 _the_notebook.set_current_page (atoi (prop->value ()));
2315 if ((prop = node.property (X_("show-marker-lines")))) {
2316 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2317 assert (act);
2318 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2319 bool yn = string_is_affirmative (prop->value ());
2321 tact->set_active (!yn);
2322 tact->set_active (yn);
2325 XMLNodeList children = node.children ();
2326 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2327 selection->set_state (**i, Stateful::current_state_version);
2328 _regions->set_state (**i);
2331 return 0;
2334 XMLNode&
2335 Editor::get_state ()
2337 XMLNode* node = new XMLNode ("Editor");
2338 char buf[32];
2340 _id.print (buf, sizeof (buf));
2341 node->add_property ("id", buf);
2343 if (is_realized()) {
2344 Glib::RefPtr<Gdk::Window> win = get_window();
2346 int x, y, xoff, yoff, width, height;
2347 win->get_root_origin(x, y);
2348 win->get_position(xoff, yoff);
2349 win->get_size(width, height);
2351 XMLNode* geometry = new XMLNode ("geometry");
2353 snprintf(buf, sizeof(buf), "%d", width);
2354 geometry->add_property("x-size", string(buf));
2355 snprintf(buf, sizeof(buf), "%d", height);
2356 geometry->add_property("y-size", string(buf));
2357 snprintf(buf, sizeof(buf), "%d", x);
2358 geometry->add_property("x-pos", string(buf));
2359 snprintf(buf, sizeof(buf), "%d", y);
2360 geometry->add_property("y-pos", string(buf));
2361 snprintf(buf, sizeof(buf), "%d", xoff);
2362 geometry->add_property("x-off", string(buf));
2363 snprintf(buf, sizeof(buf), "%d", yoff);
2364 geometry->add_property("y-off", string(buf));
2365 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2366 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2367 geometry->add_property("notebook-shrunk", _notebook_shrunk ? "1" : "0");
2368 snprintf(buf,sizeof(buf), "%d",pre_maximal_horizontal_pane_position);
2369 geometry->add_property("pre-maximal-horizontal-pane-position", string(buf));
2370 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2371 geometry->add_property("edit-vertical-pane-pos", string(buf));
2373 node->add_child_nocopy (*geometry);
2376 maybe_add_mixer_strip_width (*node);
2378 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2379 node->add_property ("zoom-focus", buf);
2380 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2381 node->add_property ("zoom", buf);
2382 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2383 node->add_property ("snap-to", buf);
2384 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2385 node->add_property ("snap-mode", buf);
2387 node->add_property ("edit-point", enum_2_string (_edit_point));
2389 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2390 node->add_property ("playhead", buf);
2391 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2392 node->add_property ("left-frame", buf);
2393 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2394 node->add_property ("y-origin", buf);
2396 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2397 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2398 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2399 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2400 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2401 node->add_property ("mouse-mode", enum2str(mouse_mode));
2402 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2403 node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2405 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2406 if (act) {
2407 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2408 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2411 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2412 if (act) {
2413 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2414 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2417 snprintf (buf, sizeof (buf), "%d", _the_notebook.get_current_page ());
2418 node->add_property (X_("editor-list-page"), buf);
2420 if (button_bindings) {
2421 XMLNode* bb = new XMLNode (X_("Buttons"));
2422 button_bindings->save (*bb);
2423 node->add_child_nocopy (*bb);
2426 node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no");
2428 node->add_child_nocopy (selection->get_state ());
2429 node->add_child_nocopy (_regions->get_state ());
2431 return *node;
2436 /** @param y y offset from the top of all trackviews.
2437 * @return pair: TimeAxisView that y is over, layer index.
2438 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2439 * in stacked region display mode, otherwise 0.
2441 std::pair<TimeAxisView *, layer_t>
2442 Editor::trackview_by_y_position (double y)
2444 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2446 std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
2447 if (r.first) {
2448 return r;
2452 return std::make_pair ( (TimeAxisView *) 0, 0);
2455 /** Snap a position to the grid, if appropriate, taking into account current
2456 * grid settings and also the state of any snap modifier keys that may be pressed.
2457 * @param start Position to snap.
2458 * @param event Event to get current key modifier information from, or 0.
2460 void
2461 Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2463 if (!_session || !event) {
2464 return;
2467 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2468 if (_snap_mode == SnapOff) {
2469 snap_to_internal (start, direction, for_mark);
2471 } else {
2472 if (_snap_mode != SnapOff) {
2473 snap_to_internal (start, direction, for_mark);
2478 void
2479 Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark)
2481 if (!_session || _snap_mode == SnapOff) {
2482 return;
2485 snap_to_internal (start, direction, for_mark);
2488 void
2489 Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /*for_mark*/)
2491 const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2492 framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2494 switch (_snap_type) {
2495 case SnapToTimecodeFrame:
2496 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2497 start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2498 } else {
2499 start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2501 break;
2503 case SnapToTimecodeSeconds:
2504 if (_session->config.get_timecode_offset_negative()) {
2505 start += _session->config.get_timecode_offset ();
2506 } else {
2507 start -= _session->config.get_timecode_offset ();
2509 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2510 start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2511 } else {
2512 start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2515 if (_session->config.get_timecode_offset_negative()) {
2516 start -= _session->config.get_timecode_offset ();
2517 } else {
2518 start += _session->config.get_timecode_offset ();
2520 break;
2522 case SnapToTimecodeMinutes:
2523 if (_session->config.get_timecode_offset_negative()) {
2524 start += _session->config.get_timecode_offset ();
2525 } else {
2526 start -= _session->config.get_timecode_offset ();
2528 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2529 start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2530 } else {
2531 start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2533 if (_session->config.get_timecode_offset_negative()) {
2534 start -= _session->config.get_timecode_offset ();
2535 } else {
2536 start += _session->config.get_timecode_offset ();
2538 break;
2539 default:
2540 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2541 /*NOTREACHED*/
2545 void
2546 Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark)
2548 const framepos_t one_second = _session->frame_rate();
2549 const framepos_t one_minute = _session->frame_rate() * 60;
2550 framepos_t presnap = start;
2551 framepos_t before;
2552 framepos_t after;
2554 switch (_snap_type) {
2555 case SnapToTimecodeFrame:
2556 case SnapToTimecodeSeconds:
2557 case SnapToTimecodeMinutes:
2558 return timecode_snap_to_internal (start, direction, for_mark);
2560 case SnapToCDFrame:
2561 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2562 start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2563 } else {
2564 start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2566 break;
2568 case SnapToSeconds:
2569 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2570 start = (framepos_t) ceil ((double) start / one_second) * one_second;
2571 } else {
2572 start = (framepos_t) floor ((double) start / one_second) * one_second;
2574 break;
2576 case SnapToMinutes:
2577 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2578 start = (framepos_t) ceil ((double) start / one_minute) * one_minute;
2579 } else {
2580 start = (framepos_t) floor ((double) start / one_minute) * one_minute;
2582 break;
2584 case SnapToBar:
2585 start = _session->tempo_map().round_to_bar (start, direction);
2586 break;
2588 case SnapToBeat:
2589 start = _session->tempo_map().round_to_beat (start, direction);
2590 break;
2592 case SnapToBeatDiv32:
2593 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2594 break;
2595 case SnapToBeatDiv28:
2596 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2597 break;
2598 case SnapToBeatDiv24:
2599 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2600 break;
2601 case SnapToBeatDiv20:
2602 start = _session->tempo_map().round_to_beat_subdivision (start, 20, direction);
2603 break;
2604 case SnapToBeatDiv16:
2605 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2606 break;
2607 case SnapToBeatDiv14:
2608 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2609 break;
2610 case SnapToBeatDiv12:
2611 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2612 break;
2613 case SnapToBeatDiv10:
2614 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2615 break;
2616 case SnapToBeatDiv8:
2617 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2618 break;
2619 case SnapToBeatDiv7:
2620 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2621 break;
2622 case SnapToBeatDiv6:
2623 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2624 break;
2625 case SnapToBeatDiv5:
2626 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2627 break;
2628 case SnapToBeatDiv4:
2629 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2630 break;
2631 case SnapToBeatDiv3:
2632 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2633 break;
2634 case SnapToBeatDiv2:
2635 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2636 break;
2638 case SnapToMark:
2639 if (for_mark) {
2640 return;
2643 _session->locations()->marks_either_side (start, before, after);
2645 if (before == max_framepos) {
2646 start = after;
2647 } else if (after == max_framepos) {
2648 start = before;
2649 } else if (before != max_framepos && after != max_framepos) {
2650 /* have before and after */
2651 if ((start - before) < (after - start)) {
2652 start = before;
2653 } else {
2654 start = after;
2658 break;
2660 case SnapToRegionStart:
2661 case SnapToRegionEnd:
2662 case SnapToRegionSync:
2663 case SnapToRegionBoundary:
2664 if (!region_boundary_cache.empty()) {
2666 vector<framepos_t>::iterator prev = region_boundary_cache.end ();
2667 vector<framepos_t>::iterator next = region_boundary_cache.end ();
2669 if (direction > 0) {
2670 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2671 } else {
2672 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2675 if (next != region_boundary_cache.begin ()) {
2676 prev = next;
2677 prev--;
2680 framepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2681 framepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2683 if (start > (p + n) / 2) {
2684 start = n;
2685 } else {
2686 start = p;
2689 break;
2692 switch (_snap_mode) {
2693 case SnapNormal:
2694 return;
2696 case SnapMagnetic:
2698 if (presnap > start) {
2699 if (presnap > (start + unit_to_frame(snap_threshold))) {
2700 start = presnap;
2703 } else if (presnap < start) {
2704 if (presnap < (start - unit_to_frame(snap_threshold))) {
2705 start = presnap;
2709 default:
2710 /* handled at entry */
2711 return;
2717 void
2718 Editor::setup_toolbar ()
2720 string pixmap_path;
2722 /* Mode Buttons (tool selection) */
2724 mouse_move_button.set_relief(Gtk::RELIEF_NONE);
2725 mouse_select_button.set_relief(Gtk::RELIEF_NONE);
2726 mouse_gain_button.set_relief(Gtk::RELIEF_NONE);
2727 mouse_zoom_button.set_relief(Gtk::RELIEF_NONE);
2728 mouse_timefx_button.set_relief(Gtk::RELIEF_NONE);
2729 mouse_audition_button.set_relief(Gtk::RELIEF_NONE);
2730 // internal_edit_button.set_relief(Gtk::RELIEF_NONE);
2731 join_object_range_button.set_relief(Gtk::RELIEF_NONE);
2733 HBox* mode_box = manage(new HBox);
2734 mode_box->set_border_width (2);
2735 mode_box->set_spacing(4);
2737 /* table containing mode buttons */
2739 HBox* mouse_mode_button_box = manage (new HBox ());
2741 if (Profile->get_sae()) {
2742 mouse_mode_button_box->pack_start (mouse_move_button);
2743 } else {
2744 mouse_mode_button_box->pack_start (mouse_move_button);
2745 mouse_mode_button_box->pack_start (join_object_range_button);
2746 mouse_mode_button_box->pack_start (mouse_select_button);
2749 mouse_mode_button_box->pack_start (mouse_zoom_button);
2751 if (!Profile->get_sae()) {
2752 mouse_mode_button_box->pack_start (mouse_gain_button);
2755 mouse_mode_button_box->pack_start (mouse_timefx_button);
2756 mouse_mode_button_box->pack_start (mouse_audition_button);
2757 mouse_mode_button_box->pack_start (internal_edit_button);
2759 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2760 if (!Profile->get_sae()) {
2761 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2763 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2765 edit_mode_selector.set_name ("EditModeSelector");
2766 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2767 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2769 mode_box->pack_start (edit_mode_selector);
2770 mode_box->pack_start (*mouse_mode_button_box);
2772 _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2773 _mouse_mode_tearoff->set_name ("MouseModeBase");
2774 _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2776 if (Profile->get_sae()) {
2777 _mouse_mode_tearoff->set_can_be_torn_off (false);
2780 _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2781 &_mouse_mode_tearoff->tearoff_window()));
2782 _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2783 &_mouse_mode_tearoff->tearoff_window(), 1));
2784 _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2785 &_mouse_mode_tearoff->tearoff_window()));
2786 _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2787 &_mouse_mode_tearoff->tearoff_window(), 1));
2789 mouse_move_button.set_mode (false);
2790 mouse_select_button.set_mode (false);
2791 mouse_gain_button.set_mode (false);
2792 mouse_zoom_button.set_mode (false);
2793 mouse_timefx_button.set_mode (false);
2794 mouse_audition_button.set_mode (false);
2795 join_object_range_button.set_mode (false);
2797 mouse_move_button.set_name ("MouseModeButton");
2798 mouse_select_button.set_name ("MouseModeButton");
2799 mouse_gain_button.set_name ("MouseModeButton");
2800 mouse_zoom_button.set_name ("MouseModeButton");
2801 mouse_timefx_button.set_name ("MouseModeButton");
2802 mouse_audition_button.set_name ("MouseModeButton");
2803 internal_edit_button.set_name ("MouseModeButton");
2804 join_object_range_button.set_name ("MouseModeButton");
2806 mouse_move_button.unset_flags (CAN_FOCUS);
2807 mouse_select_button.unset_flags (CAN_FOCUS);
2808 mouse_gain_button.unset_flags (CAN_FOCUS);
2809 mouse_zoom_button.unset_flags (CAN_FOCUS);
2810 mouse_timefx_button.unset_flags (CAN_FOCUS);
2811 mouse_audition_button.unset_flags (CAN_FOCUS);
2812 internal_edit_button.unset_flags (CAN_FOCUS);
2813 join_object_range_button.unset_flags (CAN_FOCUS);
2815 /* Zoom */
2817 _zoom_box.set_spacing (1);
2818 _zoom_box.set_border_width (0);
2820 zoom_in_button.set_name ("EditorTimeButton");
2821 zoom_in_button.set_image (*(manage (new Image (::get_icon ("zoom_in")))));
2822 zoom_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), false));
2824 zoom_out_button.set_name ("EditorTimeButton");
2825 zoom_out_button.set_image (*(manage (new Image (::get_icon ("zoom_out")))));
2826 zoom_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), true));
2828 zoom_out_full_button.set_name ("EditorTimeButton");
2829 zoom_out_full_button.set_image (*(manage (new Image (::get_icon ("zoom_full")))));
2830 zoom_out_full_button.signal_clicked().connect (sigc::mem_fun(*this, &Editor::temporal_zoom_session));
2832 zoom_focus_selector.set_name ("ZoomFocusSelector");
2833 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
2834 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2836 _zoom_box.pack_start (zoom_out_button, false, false);
2837 _zoom_box.pack_start (zoom_in_button, false, false);
2838 _zoom_box.pack_start (zoom_out_full_button, false, false);
2840 _zoom_box.pack_start (zoom_focus_selector);
2842 /* Track zoom buttons */
2843 tav_expand_button.set_name ("TrackHeightButton");
2844 tav_expand_button.set_size_request (-1, 20);
2845 tav_expand_button.add (*(manage (new Image (::get_icon ("tav_exp")))));
2846 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("expand-tracks"));
2847 act->connect_proxy (tav_expand_button);
2849 tav_shrink_button.set_name ("TrackHeightButton");
2850 tav_shrink_button.set_size_request (-1, 20);
2851 tav_shrink_button.add (*(manage (new Image (::get_icon ("tav_shrink")))));
2852 act = ActionManager::get_action (X_("Editor"), X_("shrink-tracks"));
2853 act->connect_proxy (tav_shrink_button);
2855 _zoom_box.pack_start (tav_shrink_button);
2856 _zoom_box.pack_start (tav_expand_button);
2858 _zoom_tearoff = manage (new TearOff (_zoom_box));
2860 _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2861 &_zoom_tearoff->tearoff_window()));
2862 _zoom_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2863 &_zoom_tearoff->tearoff_window(), 0));
2864 _zoom_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2865 &_zoom_tearoff->tearoff_window()));
2866 _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2867 &_zoom_tearoff->tearoff_window(), 0));
2869 snap_box.set_spacing (1);
2870 snap_box.set_border_width (2);
2872 snap_type_selector.set_name ("SnapTypeSelector");
2873 set_popdown_strings (snap_type_selector, snap_type_strings, true);
2874 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2876 snap_mode_selector.set_name ("SnapModeSelector");
2877 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
2878 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2880 edit_point_selector.set_name ("EditPointSelector");
2881 set_popdown_strings (edit_point_selector, edit_point_strings, true);
2882 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2884 snap_box.pack_start (snap_mode_selector, false, false);
2885 snap_box.pack_start (snap_type_selector, false, false);
2886 snap_box.pack_start (edit_point_selector, false, false);
2888 /* Nudge */
2890 HBox *nudge_box = manage (new HBox);
2891 nudge_box->set_spacing(1);
2892 nudge_box->set_border_width (2);
2894 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2895 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2897 nudge_box->pack_start (nudge_backward_button, false, false);
2898 nudge_box->pack_start (nudge_forward_button, false, false);
2899 nudge_box->pack_start (nudge_clock, false, false);
2902 /* Pack everything in... */
2904 HBox* hbox = manage (new HBox);
2905 hbox->set_spacing(10);
2907 _tools_tearoff = manage (new TearOff (*hbox));
2908 _tools_tearoff->set_name ("MouseModeBase");
2909 _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
2911 if (Profile->get_sae()) {
2912 _tools_tearoff->set_can_be_torn_off (false);
2915 _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2916 &_tools_tearoff->tearoff_window()));
2917 _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2918 &_tools_tearoff->tearoff_window(), 0));
2919 _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2920 &_tools_tearoff->tearoff_window()));
2921 _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2922 &_tools_tearoff->tearoff_window(), 0));
2924 toolbar_hbox.set_spacing (10);
2925 toolbar_hbox.set_border_width (1);
2927 toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
2928 toolbar_hbox.pack_start (*_zoom_tearoff, false, false);
2929 toolbar_hbox.pack_start (*_tools_tearoff, false, false);
2931 hbox->pack_start (snap_box, false, false);
2932 hbox->pack_start (*nudge_box, false, false);
2933 hbox->pack_start (panic_box, false, false);
2935 hbox->show_all ();
2937 toolbar_base.set_name ("ToolBarBase");
2938 toolbar_base.add (toolbar_hbox);
2940 _toolbar_viewport.add (toolbar_base);
2941 /* stick to the required height but allow width to vary if there's not enough room */
2942 _toolbar_viewport.set_size_request (1, -1);
2944 toolbar_frame.set_shadow_type (SHADOW_OUT);
2945 toolbar_frame.set_name ("BaseFrame");
2946 toolbar_frame.add (_toolbar_viewport);
2948 DPIReset.connect (sigc::mem_fun (*this, &Editor::resize_text_widgets));
2951 void
2952 Editor::setup_tooltips ()
2954 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
2955 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain"));
2956 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
2957 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
2958 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2959 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
2960 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
2961 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
2962 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
2963 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
2964 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
2965 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
2966 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
2967 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
2968 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
2969 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
2970 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
2971 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
2972 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
2973 ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
2974 ARDOUR_UI::instance()->set_tip (midi_panic_button, _("Send note off and reset controller messages on all MIDI channels"));
2975 ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode"));
2978 void
2979 Editor::midi_panic ()
2981 cerr << "MIDI panic\n";
2983 if (_session) {
2984 _session->midi_panic();
2988 void
2989 Editor::setup_midi_toolbar ()
2991 RefPtr<Action> act;
2993 /* Midi sound notes */
2994 midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
2995 midi_sound_notes.set_relief(Gtk::RELIEF_NONE);
2996 midi_sound_notes.unset_flags (CAN_FOCUS);
2998 /* Panic */
3000 act = ActionManager::get_action (X_("MIDI"), X_("panic"));
3001 midi_panic_button.set_name("MidiPanicButton");
3002 act->connect_proxy (midi_panic_button);
3004 panic_box.pack_start (midi_sound_notes , true, true);
3005 panic_box.pack_start (midi_panic_button, true, true);
3009 Editor::convert_drop_to_paths (
3010 vector<string>& paths,
3011 const RefPtr<Gdk::DragContext>& /*context*/,
3012 gint /*x*/,
3013 gint /*y*/,
3014 const SelectionData& data,
3015 guint /*info*/,
3016 guint /*time*/)
3018 if (_session == 0) {
3019 return -1;
3022 vector<string> uris = data.get_uris();
3024 if (uris.empty()) {
3026 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3027 are actually URI lists. So do it by hand.
3030 if (data.get_target() != "text/plain") {
3031 return -1;
3034 /* Parse the "uri-list" format that Nautilus provides,
3035 where each pathname is delimited by \r\n.
3037 THERE MAY BE NO NULL TERMINATING CHAR!!!
3040 string txt = data.get_text();
3041 const char* p;
3042 const char* q;
3044 p = (const char *) malloc (txt.length() + 1);
3045 txt.copy ((char *) p, txt.length(), 0);
3046 ((char*)p)[txt.length()] = '\0';
3048 while (p)
3050 if (*p != '#')
3052 while (g_ascii_isspace (*p))
3053 p++;
3055 q = p;
3056 while (*q && (*q != '\n') && (*q != '\r')) {
3057 q++;
3060 if (q > p)
3062 q--;
3063 while (q > p && g_ascii_isspace (*q))
3064 q--;
3066 if (q > p)
3068 uris.push_back (string (p, q - p + 1));
3072 p = strchr (p, '\n');
3073 if (p)
3074 p++;
3077 free ((void*)p);
3079 if (uris.empty()) {
3080 return -1;
3084 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3086 if ((*i).substr (0,7) == "file://") {
3088 string p = *i;
3089 PBD::url_decode (p);
3091 // scan forward past three slashes
3093 string::size_type slashcnt = 0;
3094 string::size_type n = 0;
3095 string::iterator x = p.begin();
3097 while (slashcnt < 3 && x != p.end()) {
3098 if ((*x) == '/') {
3099 slashcnt++;
3100 } else if (slashcnt == 3) {
3101 break;
3103 ++n;
3104 ++x;
3107 if (slashcnt != 3 || x == p.end()) {
3108 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3109 continue;
3112 paths.push_back (p.substr (n - 1));
3116 return 0;
3119 void
3120 Editor::new_tempo_section ()
3125 void
3126 Editor::map_transport_state ()
3128 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3130 if (_session && _session->transport_stopped()) {
3131 have_pending_keyboard_selection = false;
3134 update_loop_range_view (true);
3137 /* UNDO/REDO */
3139 Editor::State::State (PublicEditor const * e)
3141 selection = new Selection (e);
3144 Editor::State::~State ()
3146 delete selection;
3149 void
3150 Editor::begin_reversible_command (string name)
3152 if (_session) {
3153 _session->begin_reversible_command (name);
3157 void
3158 Editor::begin_reversible_command (GQuark q)
3160 if (_session) {
3161 _session->begin_reversible_command (q);
3165 void
3166 Editor::commit_reversible_command ()
3168 if (_session) {
3169 _session->commit_reversible_command ();
3173 void
3174 Editor::history_changed ()
3176 string label;
3178 if (undo_action && _session) {
3179 if (_session->undo_depth() == 0) {
3180 label = _("Undo");
3181 } else {
3182 label = string_compose(_("Undo (%1)"), _session->next_undo());
3184 undo_action->property_label() = label;
3187 if (redo_action && _session) {
3188 if (_session->redo_depth() == 0) {
3189 label = _("Redo");
3190 } else {
3191 label = string_compose(_("Redo (%1)"), _session->next_redo());
3193 redo_action->property_label() = label;
3197 void
3198 Editor::duplicate_dialog (bool with_dialog)
3200 float times = 1.0f;
3202 if (mouse_mode == MouseRange) {
3203 if (selection->time.length() == 0) {
3204 return;
3208 RegionSelection rs = get_regions_from_selection_and_entered ();
3210 if (mouse_mode != MouseRange && rs.empty()) {
3211 return;
3214 if (with_dialog) {
3216 ArdourDialog win (_("Duplicate"));
3217 Label label (_("Number of duplications:"));
3218 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3219 SpinButton spinner (adjustment, 0.0, 1);
3220 HBox hbox;
3222 win.get_vbox()->set_spacing (12);
3223 win.get_vbox()->pack_start (hbox);
3224 hbox.set_border_width (6);
3225 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3227 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3228 place, visually. so do this by hand.
3231 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3232 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3233 spinner.grab_focus();
3235 hbox.show ();
3236 label.show ();
3237 spinner.show ();
3239 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3240 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3241 win.set_default_response (RESPONSE_ACCEPT);
3243 win.set_position (WIN_POS_MOUSE);
3245 spinner.grab_focus ();
3247 switch (win.run ()) {
3248 case RESPONSE_ACCEPT:
3249 break;
3250 default:
3251 return;
3254 times = adjustment.get_value();
3257 if (mouse_mode == MouseRange) {
3258 duplicate_selection (times);
3259 } else {
3260 duplicate_some_regions (rs, times);
3264 void
3265 Editor::show_verbose_canvas_cursor ()
3267 verbose_canvas_cursor->raise_to_top();
3268 verbose_canvas_cursor->show();
3269 verbose_cursor_visible = true;
3272 void
3273 Editor::hide_verbose_canvas_cursor ()
3275 verbose_canvas_cursor->hide();
3276 verbose_cursor_visible = false;
3279 double
3280 Editor::clamp_verbose_cursor_x (double x)
3282 if (x < 0) {
3283 x = 0;
3284 } else {
3285 x = min (_canvas_width - 200.0, x);
3287 return x;
3290 double
3291 Editor::clamp_verbose_cursor_y (double y)
3293 if (y < canvas_timebars_vsize) {
3294 y = canvas_timebars_vsize;
3295 } else {
3296 y = min (_canvas_height - 50, y);
3298 return y;
3301 void
3302 Editor::show_verbose_canvas_cursor_with (const string & txt, int32_t xoffset, int32_t yoffset)
3304 verbose_canvas_cursor->property_text() = txt.c_str();
3306 int x, y;
3307 double wx, wy;
3309 track_canvas->get_pointer (x, y);
3310 track_canvas->window_to_world (x, y, wx, wy);
3312 wx += xoffset;
3313 wy += yoffset;
3315 /* don't get too close to the edge */
3316 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (wx);
3317 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (wy);
3319 show_verbose_canvas_cursor ();
3322 void
3323 Editor::set_verbose_canvas_cursor (const string & txt, double x, double y)
3325 verbose_canvas_cursor->property_text() = txt.c_str();
3326 /* don't get too close to the edge */
3327 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (x);
3328 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (y);
3331 void
3332 Editor::set_verbose_canvas_cursor_text (const string & txt)
3334 verbose_canvas_cursor->property_text() = txt.c_str();
3337 void
3338 Editor::set_edit_mode (EditMode m)
3340 Config->set_edit_mode (m);
3343 void
3344 Editor::cycle_edit_mode ()
3346 switch (Config->get_edit_mode()) {
3347 case Slide:
3348 if (Profile->get_sae()) {
3349 Config->set_edit_mode (Lock);
3350 } else {
3351 Config->set_edit_mode (Splice);
3353 break;
3354 case Splice:
3355 Config->set_edit_mode (Lock);
3356 break;
3357 case Lock:
3358 Config->set_edit_mode (Slide);
3359 break;
3363 void
3364 Editor::edit_mode_selection_done ()
3366 string s = edit_mode_selector.get_active_text ();
3368 if (!s.empty()) {
3369 Config->set_edit_mode (string_to_edit_mode (s));
3373 void
3374 Editor::snap_type_selection_done ()
3376 string choice = snap_type_selector.get_active_text();
3377 SnapType snaptype = SnapToBeat;
3379 if (choice == _("Beats/2")) {
3380 snaptype = SnapToBeatDiv2;
3381 } else if (choice == _("Beats/3")) {
3382 snaptype = SnapToBeatDiv3;
3383 } else if (choice == _("Beats/4")) {
3384 snaptype = SnapToBeatDiv4;
3385 } else if (choice == _("Beats/5")) {
3386 snaptype = SnapToBeatDiv5;
3387 } else if (choice == _("Beats/6")) {
3388 snaptype = SnapToBeatDiv6;
3389 } else if (choice == _("Beats/7")) {
3390 snaptype = SnapToBeatDiv7;
3391 } else if (choice == _("Beats/8")) {
3392 snaptype = SnapToBeatDiv8;
3393 } else if (choice == _("Beats/10")) {
3394 snaptype = SnapToBeatDiv10;
3395 } else if (choice == _("Beats/12")) {
3396 snaptype = SnapToBeatDiv12;
3397 } else if (choice == _("Beats/14")) {
3398 snaptype = SnapToBeatDiv14;
3399 } else if (choice == _("Beats/16")) {
3400 snaptype = SnapToBeatDiv16;
3401 } else if (choice == _("Beats/20")) {
3402 snaptype = SnapToBeatDiv20;
3403 } else if (choice == _("Beats/24")) {
3404 snaptype = SnapToBeatDiv24;
3405 } else if (choice == _("Beats/28")) {
3406 snaptype = SnapToBeatDiv28;
3407 } else if (choice == _("Beats/32")) {
3408 snaptype = SnapToBeatDiv32;
3409 } else if (choice == _("Beats")) {
3410 snaptype = SnapToBeat;
3411 } else if (choice == _("Bars")) {
3412 snaptype = SnapToBar;
3413 } else if (choice == _("Marks")) {
3414 snaptype = SnapToMark;
3415 } else if (choice == _("Region starts")) {
3416 snaptype = SnapToRegionStart;
3417 } else if (choice == _("Region ends")) {
3418 snaptype = SnapToRegionEnd;
3419 } else if (choice == _("Region bounds")) {
3420 snaptype = SnapToRegionBoundary;
3421 } else if (choice == _("Region syncs")) {
3422 snaptype = SnapToRegionSync;
3423 } else if (choice == _("CD Frames")) {
3424 snaptype = SnapToCDFrame;
3425 } else if (choice == _("Timecode Frames")) {
3426 snaptype = SnapToTimecodeFrame;
3427 } else if (choice == _("Timecode Seconds")) {
3428 snaptype = SnapToTimecodeSeconds;
3429 } else if (choice == _("Timecode Minutes")) {
3430 snaptype = SnapToTimecodeMinutes;
3431 } else if (choice == _("Seconds")) {
3432 snaptype = SnapToSeconds;
3433 } else if (choice == _("Minutes")) {
3434 snaptype = SnapToMinutes;
3437 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3438 if (ract) {
3439 ract->set_active ();
3443 void
3444 Editor::snap_mode_selection_done ()
3446 string choice = snap_mode_selector.get_active_text();
3447 SnapMode mode = SnapNormal;
3449 if (choice == _("No Grid")) {
3450 mode = SnapOff;
3451 } else if (choice == _("Grid")) {
3452 mode = SnapNormal;
3453 } else if (choice == _("Magnetic")) {
3454 mode = SnapMagnetic;
3457 RefPtr<RadioAction> ract = snap_mode_action (mode);
3459 if (ract) {
3460 ract->set_active (true);
3464 void
3465 Editor::cycle_edit_point (bool with_marker)
3467 switch (_edit_point) {
3468 case EditAtMouse:
3469 set_edit_point_preference (EditAtPlayhead);
3470 break;
3471 case EditAtPlayhead:
3472 if (with_marker) {
3473 set_edit_point_preference (EditAtSelectedMarker);
3474 } else {
3475 set_edit_point_preference (EditAtMouse);
3477 break;
3478 case EditAtSelectedMarker:
3479 set_edit_point_preference (EditAtMouse);
3480 break;
3484 void
3485 Editor::edit_point_selection_done ()
3487 string choice = edit_point_selector.get_active_text();
3488 EditPoint ep = EditAtSelectedMarker;
3490 if (choice == _("Marker")) {
3491 set_edit_point_preference (EditAtSelectedMarker);
3492 } else if (choice == _("Playhead")) {
3493 set_edit_point_preference (EditAtPlayhead);
3494 } else {
3495 set_edit_point_preference (EditAtMouse);
3498 RefPtr<RadioAction> ract = edit_point_action (ep);
3500 if (ract) {
3501 ract->set_active (true);
3505 void
3506 Editor::zoom_focus_selection_done ()
3508 string choice = zoom_focus_selector.get_active_text();
3509 ZoomFocus focus_type = ZoomFocusLeft;
3511 if (choice == _("Left")) {
3512 focus_type = ZoomFocusLeft;
3513 } else if (choice == _("Right")) {
3514 focus_type = ZoomFocusRight;
3515 } else if (choice == _("Center")) {
3516 focus_type = ZoomFocusCenter;
3517 } else if (choice == _("Playhead")) {
3518 focus_type = ZoomFocusPlayhead;
3519 } else if (choice == _("Mouse")) {
3520 focus_type = ZoomFocusMouse;
3521 } else if (choice == _("Edit point")) {
3522 focus_type = ZoomFocusEdit;
3525 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3527 if (ract) {
3528 ract->set_active ();
3532 gint
3533 Editor::edit_controls_button_release (GdkEventButton* ev)
3535 if (Keyboard::is_context_menu_event (ev)) {
3536 ARDOUR_UI::instance()->add_route (this);
3538 return TRUE;
3541 gint
3542 Editor::mouse_select_button_release (GdkEventButton* ev)
3544 /* this handles just right-clicks */
3546 if (ev->button != 3) {
3547 return false;
3550 return true;
3553 void
3554 Editor::set_zoom_focus (ZoomFocus f)
3556 string str = zoom_focus_strings[(int)f];
3558 if (str != zoom_focus_selector.get_active_text()) {
3559 zoom_focus_selector.set_active_text (str);
3562 if (zoom_focus != f) {
3563 zoom_focus = f;
3565 ZoomFocusChanged (); /* EMIT_SIGNAL */
3567 instant_save ();
3571 void
3572 Editor::ensure_float (Window& win)
3574 win.set_transient_for (*this);
3577 void
3578 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3580 /* recover or initialize pane positions. do this here rather than earlier because
3581 we don't want the positions to change the child allocations, which they seem to do.
3584 int pos;
3585 XMLProperty* prop;
3586 char buf[32];
3587 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3588 int width, height;
3590 enum Pane {
3591 Horizontal = 0x1,
3592 Vertical = 0x2
3595 static Pane done;
3597 XMLNode* geometry;
3599 width = default_width;
3600 height = default_height;
3602 if ((geometry = find_named_node (*node, "geometry")) != 0) {
3604 prop = geometry->property ("x-size");
3605 if (prop) {
3606 width = atoi (prop->value());
3608 prop = geometry->property ("y-size");
3609 if (prop) {
3610 height = atoi (prop->value());
3614 if (which == static_cast<Paned*> (&edit_pane)) {
3616 if (done & Horizontal) {
3617 return;
3620 if (geometry && (prop = geometry->property ("notebook-shrunk"))) {
3621 _notebook_shrunk = string_is_affirmative (prop->value ());
3624 if (geometry && (prop = geometry->property ("pre-maximal-horizontal-pane-position"))) {
3625 pre_maximal_horizontal_pane_position = atoi (prop->value ());
3628 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3629 /* initial allocation is 90% to canvas, 10% to notebook */
3630 pos = (int) floor (alloc.get_width() * 0.90f);
3631 snprintf (buf, sizeof(buf), "%d", pos);
3632 } else {
3633 pos = atoi (prop->value());
3636 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3637 edit_pane.set_position (pos);
3638 if (pre_maximal_horizontal_pane_position == 0) {
3639 pre_maximal_horizontal_pane_position = pos;
3643 done = (Pane) (done | Horizontal);
3645 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3647 if (done & Vertical) {
3648 return;
3651 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3652 /* initial allocation is 90% to canvas, 10% to summary */
3653 pos = (int) floor (alloc.get_height() * 0.90f);
3654 snprintf (buf, sizeof(buf), "%d", pos);
3655 } else {
3656 pos = atoi (prop->value());
3659 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3660 editor_summary_pane.set_position (pos);
3661 pre_maximal_vertical_pane_position = pos;
3664 done = (Pane) (done | Vertical);
3668 void
3669 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3671 if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) {
3672 top_hbox.remove (toolbar_frame);
3676 void
3677 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3679 if (toolbar_frame.get_parent() == 0) {
3680 top_hbox.pack_end (toolbar_frame);
3684 void
3685 Editor::set_show_measures (bool yn)
3687 if (_show_measures != yn) {
3688 hide_measures ();
3690 if ((_show_measures = yn) == true) {
3691 if (tempo_lines)
3692 tempo_lines->show();
3693 draw_measures ();
3695 instant_save ();
3699 void
3700 Editor::toggle_follow_playhead ()
3702 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3703 if (act) {
3704 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3705 set_follow_playhead (tact->get_active());
3709 /** @param yn true to follow playhead, otherwise false.
3710 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
3712 void
3713 Editor::set_follow_playhead (bool yn, bool catch_up)
3715 if (_follow_playhead != yn) {
3716 if ((_follow_playhead = yn) == true && catch_up) {
3717 /* catch up */
3718 reset_x_origin_to_follow_playhead ();
3720 instant_save ();
3724 void
3725 Editor::toggle_stationary_playhead ()
3727 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3728 if (act) {
3729 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3730 set_stationary_playhead (tact->get_active());
3734 void
3735 Editor::set_stationary_playhead (bool yn)
3737 if (_stationary_playhead != yn) {
3738 if ((_stationary_playhead = yn) == true) {
3739 /* catch up */
3740 // FIXME need a 3.0 equivalent of this 2.X call
3741 // update_current_screen ();
3743 instant_save ();
3747 void
3748 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3750 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3751 if (xfade) {
3752 xfade->set_active (!xfade->active());
3756 void
3757 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3759 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3760 if (xfade) {
3761 xfade->set_follow_overlap (!xfade->following_overlap());
3765 void
3766 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3768 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3770 if (!xfade) {
3771 return;
3774 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3776 ensure_float (cew);
3778 switch (cew.run ()) {
3779 case RESPONSE_ACCEPT:
3780 break;
3781 default:
3782 return;
3785 cew.apply ();
3786 PropertyChange all_crossfade_properties;
3787 all_crossfade_properties.add (ARDOUR::Properties::active);
3788 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3789 xfade->PropertyChanged (all_crossfade_properties);
3792 PlaylistSelector&
3793 Editor::playlist_selector () const
3795 return *_playlist_selector;
3798 Evoral::MusicalTime
3799 Editor::get_grid_type_as_beats (bool& success, framepos_t position)
3801 success = true;
3803 switch (_snap_type) {
3804 case SnapToBeat:
3805 return 1.0;
3806 break;
3808 case SnapToBeatDiv32:
3809 return 1.0/32.0;
3810 break;
3811 case SnapToBeatDiv28:
3812 return 1.0/28.0;
3813 break;
3814 case SnapToBeatDiv24:
3815 return 1.0/24.0;
3816 break;
3817 case SnapToBeatDiv20:
3818 return 1.0/20.0;
3819 break;
3820 case SnapToBeatDiv16:
3821 return 1.0/16.0;
3822 break;
3823 case SnapToBeatDiv14:
3824 return 1.0/14.0;
3825 break;
3826 case SnapToBeatDiv12:
3827 return 1.0/12.0;
3828 break;
3829 case SnapToBeatDiv10:
3830 return 1.0/10.0;
3831 break;
3832 case SnapToBeatDiv8:
3833 return 1.0/8.0;
3834 break;
3835 case SnapToBeatDiv7:
3836 return 1.0/7.0;
3837 break;
3838 case SnapToBeatDiv6:
3839 return 1.0/6.0;
3840 break;
3841 case SnapToBeatDiv5:
3842 return 1.0/5.0;
3843 break;
3844 case SnapToBeatDiv4:
3845 return 1.0/4.0;
3846 break;
3847 case SnapToBeatDiv3:
3848 return 1.0/3.0;
3849 break;
3850 case SnapToBeatDiv2:
3851 return 1.0/2.0;
3852 break;
3854 case SnapToBar:
3855 if (_session) {
3856 return _session->tempo_map().meter_at (position).beats_per_bar();
3858 break;
3860 case SnapToCDFrame:
3861 case SnapToTimecodeFrame:
3862 case SnapToTimecodeSeconds:
3863 case SnapToTimecodeMinutes:
3864 case SnapToSeconds:
3865 case SnapToMinutes:
3866 case SnapToRegionStart:
3867 case SnapToRegionEnd:
3868 case SnapToRegionSync:
3869 case SnapToRegionBoundary:
3870 default:
3871 success = false;
3872 break;
3875 return 0.0;
3878 framecnt_t
3879 Editor::get_nudge_distance (framepos_t pos, framecnt_t& next)
3881 framecnt_t ret;
3883 ret = nudge_clock.current_duration (pos);
3884 next = ret + 1; /* XXXX fix me */
3886 return ret;
3890 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3892 ArdourDialog dialog (_("Playlist Deletion"));
3893 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3894 "If left alone, no audio files used by it will be cleaned.\n"
3895 "If deleted, audio files used by it alone by will cleaned."),
3896 pl->name()));
3898 dialog.set_position (WIN_POS_CENTER);
3899 dialog.get_vbox()->pack_start (label);
3901 label.show ();
3903 dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT);
3904 dialog.add_button (_("Keep playlist"), RESPONSE_REJECT);
3905 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3907 switch (dialog.run ()) {
3908 case RESPONSE_ACCEPT:
3909 /* delete the playlist */
3910 return 0;
3911 break;
3913 case RESPONSE_REJECT:
3914 /* keep the playlist */
3915 return 1;
3916 break;
3918 default:
3919 break;
3922 return -1;
3925 bool
3926 Editor::audio_region_selection_covers (framepos_t where)
3928 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3929 if ((*a)->region()->covers (where)) {
3930 return true;
3934 return false;
3937 void
3938 Editor::prepare_for_cleanup ()
3940 cut_buffer->clear_regions ();
3941 cut_buffer->clear_playlists ();
3943 selection->clear_regions ();
3944 selection->clear_playlists ();
3946 _regions->suspend_redisplay ();
3949 void
3950 Editor::finish_cleanup ()
3952 _regions->resume_redisplay ();
3955 Location*
3956 Editor::transport_loop_location()
3958 if (_session) {
3959 return _session->locations()->auto_loop_location();
3960 } else {
3961 return 0;
3965 Location*
3966 Editor::transport_punch_location()
3968 if (_session) {
3969 return _session->locations()->auto_punch_location();
3970 } else {
3971 return 0;
3975 bool
3976 Editor::control_layout_scroll (GdkEventScroll* ev)
3978 if (Keyboard::some_magic_widget_has_focus()) {
3979 return false;
3982 switch (ev->direction) {
3983 case GDK_SCROLL_UP:
3984 scroll_tracks_up_line ();
3985 return true;
3986 break;
3988 case GDK_SCROLL_DOWN:
3989 scroll_tracks_down_line ();
3990 return true;
3992 default:
3993 /* no left/right handling yet */
3994 break;
3997 return false;
4000 void
4001 Editor::session_state_saved (string)
4003 update_title ();
4004 _snapshots->redisplay ();
4007 void
4008 Editor::maximise_editing_space ()
4010 _mouse_mode_tearoff->set_visible (false);
4011 _tools_tearoff->set_visible (false);
4012 _zoom_tearoff->set_visible (false);
4014 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
4015 pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
4016 pre_maximal_editor_width = this->get_width ();
4017 pre_maximal_editor_height = this->get_height ();
4019 if (post_maximal_horizontal_pane_position == 0) {
4020 post_maximal_horizontal_pane_position = edit_pane.get_width();
4023 if (post_maximal_vertical_pane_position == 0) {
4024 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
4027 fullscreen ();
4029 if (post_maximal_editor_width) {
4030 edit_pane.set_position (post_maximal_horizontal_pane_position -
4031 abs(post_maximal_editor_width - pre_maximal_editor_width));
4032 } else {
4033 edit_pane.set_position (post_maximal_horizontal_pane_position);
4036 if (post_maximal_editor_height) {
4037 editor_summary_pane.set_position (post_maximal_vertical_pane_position -
4038 abs(post_maximal_editor_height - pre_maximal_editor_height));
4039 } else {
4040 editor_summary_pane.set_position (post_maximal_vertical_pane_position);
4043 if (Config->get_keep_tearoffs()) {
4044 _mouse_mode_tearoff->set_visible (true);
4045 _tools_tearoff->set_visible (true);
4046 if (Config->get_show_zoom_tools ()) {
4047 _zoom_tearoff->set_visible (true);
4053 void
4054 Editor::restore_editing_space ()
4056 // user changed width/height of panes during fullscreen
4058 if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
4059 post_maximal_horizontal_pane_position = edit_pane.get_position();
4062 if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
4063 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
4066 unfullscreen();
4068 _mouse_mode_tearoff->set_visible (true);
4069 _tools_tearoff->set_visible (true);
4070 if (Config->get_show_zoom_tools ()) {
4071 _zoom_tearoff->set_visible (true);
4073 post_maximal_editor_width = this->get_width();
4074 post_maximal_editor_height = this->get_height();
4076 edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
4077 editor_summary_pane.set_position (pre_maximal_vertical_pane_position + abs(this->get_height() - pre_maximal_editor_height));
4081 * Make new playlists for a given track and also any others that belong
4082 * to the same active route group with the `edit' property.
4083 * @param v Track.
4086 void
4087 Editor::new_playlists (TimeAxisView* v)
4089 begin_reversible_command (_("new playlists"));
4090 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4091 _session->playlists->get (playlists);
4092 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4093 commit_reversible_command ();
4097 * Use a copy of the current playlist for a given track and also any others that belong
4098 * to the same active route group with the `edit' property.
4099 * @param v Track.
4102 void
4103 Editor::copy_playlists (TimeAxisView* v)
4105 begin_reversible_command (_("copy playlists"));
4106 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4107 _session->playlists->get (playlists);
4108 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4109 commit_reversible_command ();
4112 /** Clear the current playlist for a given track and also any others that belong
4113 * to the same active route group with the `edit' property.
4114 * @param v Track.
4117 void
4118 Editor::clear_playlists (TimeAxisView* v)
4120 begin_reversible_command (_("clear playlists"));
4121 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4122 _session->playlists->get (playlists);
4123 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4124 commit_reversible_command ();
4127 void
4128 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4130 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4133 void
4134 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4136 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4139 void
4140 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4142 atv.clear_playlist ();
4145 bool
4146 Editor::on_key_press_event (GdkEventKey* ev)
4148 return key_press_focus_accelerator_handler (*this, ev);
4151 bool
4152 Editor::on_key_release_event (GdkEventKey* ev)
4154 return Gtk::Window::on_key_release_event (ev);
4155 // return key_press_focus_accelerator_handler (*this, ev);
4158 /** Queue up a change to the viewport x origin.
4159 * @param frame New x origin.
4161 void
4162 Editor::reset_x_origin (framepos_t frame)
4164 queue_visual_change (frame);
4167 void
4168 Editor::reset_y_origin (double y)
4170 queue_visual_change_y (y);
4173 void
4174 Editor::reset_zoom (double fpu)
4176 queue_visual_change (fpu);
4179 void
4180 Editor::reposition_and_zoom (framepos_t frame, double fpu)
4182 reset_x_origin (frame);
4183 reset_zoom (fpu);
4185 if (!no_save_visual) {
4186 undo_visual_stack.push_back (current_visual_state(false));
4190 Editor::VisualState*
4191 Editor::current_visual_state (bool with_tracks)
4193 VisualState* vs = new VisualState;
4194 vs->y_position = vertical_adjustment.get_value();
4195 vs->frames_per_unit = frames_per_unit;
4196 vs->leftmost_frame = leftmost_frame;
4197 vs->zoom_focus = zoom_focus;
4199 if (with_tracks) {
4200 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4201 vs->track_states.push_back (TAVState ((*i), &(*i)->get_state()));
4205 return vs;
4208 void
4209 Editor::undo_visual_state ()
4211 if (undo_visual_stack.empty()) {
4212 return;
4215 redo_visual_stack.push_back (current_visual_state());
4217 VisualState* vs = undo_visual_stack.back();
4218 undo_visual_stack.pop_back();
4219 use_visual_state (*vs);
4222 void
4223 Editor::redo_visual_state ()
4225 if (redo_visual_stack.empty()) {
4226 return;
4229 undo_visual_stack.push_back (current_visual_state());
4231 VisualState* vs = redo_visual_stack.back();
4232 redo_visual_stack.pop_back();
4233 use_visual_state (*vs);
4236 void
4237 Editor::swap_visual_state ()
4239 if (undo_visual_stack.empty()) {
4240 redo_visual_state ();
4241 } else {
4242 undo_visual_state ();
4246 void
4247 Editor::use_visual_state (VisualState& vs)
4249 no_save_visual = true;
4251 _routes->suspend_redisplay ();
4253 vertical_adjustment.set_value (vs.y_position);
4255 set_zoom_focus (vs.zoom_focus);
4256 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4258 for (list<TAVState>::iterator i = vs.track_states.begin(); i != vs.track_states.end(); ++i) {
4259 TrackViewList::iterator t;
4261 /* check if the track still exists - it could have been deleted */
4263 if ((t = find (track_views.begin(), track_views.end(), i->first)) != track_views.end()) {
4264 (*t)->set_state (*(i->second), Stateful::loading_state_version);
4269 if (!vs.track_states.empty()) {
4270 _routes->update_visibility ();
4273 _routes->resume_redisplay ();
4275 no_save_visual = false;
4278 void
4279 Editor::set_frames_per_unit (double fpu)
4281 /* this is the core function that controls the zoom level of the canvas. it is called
4282 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4285 if (fpu == frames_per_unit) {
4286 return;
4289 if (fpu < 2.0) {
4290 fpu = 2.0;
4294 /* don't allow zooms that fit more than the maximum number
4295 of frames into an 800 pixel wide space.
4298 if (max_framepos / fpu < 800.0) {
4299 return;
4302 if (tempo_lines)
4303 tempo_lines->tempo_map_changed();
4305 frames_per_unit = fpu;
4306 post_zoom ();
4309 void
4310 Editor::post_zoom ()
4312 // convert fpu to frame count
4314 framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width);
4316 if (frames_per_unit != zoom_range_clock.current_duration()) {
4317 zoom_range_clock.set (frames);
4320 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4321 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4322 (*i)->reshow_selection (selection->time);
4326 ZoomChanged (); /* EMIT_SIGNAL */
4328 //reset_scrolling_region ();
4330 if (playhead_cursor) {
4331 playhead_cursor->set_position (playhead_cursor->current_frame);
4334 refresh_location_display();
4335 _summary->set_overlays_dirty ();
4337 update_marker_labels ();
4339 instant_save ();
4342 void
4343 Editor::queue_visual_change (framepos_t where)
4345 pending_visual_change.add (VisualChange::TimeOrigin);
4346 pending_visual_change.time_origin = where;
4347 ensure_visual_change_idle_handler ();
4350 void
4351 Editor::queue_visual_change (double fpu)
4353 pending_visual_change.add (VisualChange::ZoomLevel);
4354 pending_visual_change.frames_per_unit = fpu;
4356 ensure_visual_change_idle_handler ();
4359 void
4360 Editor::queue_visual_change_y (double y)
4362 pending_visual_change.add (VisualChange::YOrigin);
4363 pending_visual_change.y_origin = y;
4365 ensure_visual_change_idle_handler ();
4368 void
4369 Editor::ensure_visual_change_idle_handler ()
4371 if (pending_visual_change.idle_handler_id < 0) {
4372 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4377 Editor::_idle_visual_changer (void* arg)
4379 return static_cast<Editor*>(arg)->idle_visual_changer ();
4383 Editor::idle_visual_changer ()
4385 VisualChange::Type p = pending_visual_change.pending;
4386 pending_visual_change.pending = (VisualChange::Type) 0;
4388 double const last_time_origin = horizontal_position ();
4390 if (p & VisualChange::TimeOrigin) {
4391 /* This is a bit of a hack, but set_frames_per_unit
4392 below will (if called) end up with the
4393 CrossfadeViews looking at Editor::leftmost_frame,
4394 and if we're changing origin and zoom in the same
4395 operation it will be the wrong value unless we
4396 update it here.
4399 leftmost_frame = pending_visual_change.time_origin;
4402 if (p & VisualChange::ZoomLevel) {
4403 set_frames_per_unit (pending_visual_change.frames_per_unit);
4405 compute_fixed_ruler_scale ();
4406 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4407 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4408 update_tempo_based_rulers ();
4410 if (p & VisualChange::TimeOrigin) {
4411 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4413 if (p & VisualChange::YOrigin) {
4414 vertical_adjustment.set_value (pending_visual_change.y_origin);
4417 if (last_time_origin == horizontal_position ()) {
4418 /* changed signal not emitted */
4419 update_fixed_rulers ();
4420 redisplay_tempo (true);
4423 _summary->set_overlays_dirty ();
4425 pending_visual_change.idle_handler_id = -1;
4426 return 0; /* this is always a one-shot call */
4429 struct EditorOrderTimeAxisSorter {
4430 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4431 return a->order () < b->order ();
4435 void
4436 Editor::sort_track_selection (TrackViewList* sel)
4438 EditorOrderTimeAxisSorter cmp;
4440 if (sel) {
4441 sel->sort (cmp);
4442 } else {
4443 selection->tracks.sort (cmp);
4447 framepos_t
4448 Editor::get_preferred_edit_position (bool ignore_playhead)
4450 bool ignored;
4451 framepos_t where = 0;
4452 EditPoint ep = _edit_point;
4454 if (entered_marker) {
4455 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position()));
4456 return entered_marker->position();
4459 if (ignore_playhead && ep == EditAtPlayhead) {
4460 ep = EditAtSelectedMarker;
4463 switch (ep) {
4464 case EditAtPlayhead:
4465 where = _session->audible_frame();
4466 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where));
4467 break;
4469 case EditAtSelectedMarker:
4470 if (!selection->markers.empty()) {
4471 bool is_start;
4472 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4473 if (loc) {
4474 if (is_start) {
4475 where = loc->start();
4476 } else {
4477 where = loc->end();
4479 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where));
4480 break;
4483 /* fallthru */
4485 default:
4486 case EditAtMouse:
4487 if (!mouse_frame (where, ignored)) {
4488 /* XXX not right but what can we do ? */
4489 return 0;
4491 snap_to (where);
4492 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where));
4493 break;
4496 return where;
4499 void
4500 Editor::set_loop_range (framepos_t start, framepos_t end, string cmd)
4502 if (!_session) return;
4504 begin_reversible_command (cmd);
4506 Location* tll;
4508 if ((tll = transport_loop_location()) == 0) {
4509 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop);
4510 XMLNode &before = _session->locations()->get_state();
4511 _session->locations()->add (loc, true);
4512 _session->set_auto_loop_location (loc);
4513 XMLNode &after = _session->locations()->get_state();
4514 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4515 } else {
4516 XMLNode &before = tll->get_state();
4517 tll->set_hidden (false, this);
4518 tll->set (start, end);
4519 XMLNode &after = tll->get_state();
4520 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4523 commit_reversible_command ();
4526 void
4527 Editor::set_punch_range (framepos_t start, framepos_t end, string cmd)
4529 if (!_session) return;
4531 begin_reversible_command (cmd);
4533 Location* tpl;
4535 if ((tpl = transport_punch_location()) == 0) {
4536 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoPunch);
4537 XMLNode &before = _session->locations()->get_state();
4538 _session->locations()->add (loc, true);
4539 _session->set_auto_loop_location (loc);
4540 XMLNode &after = _session->locations()->get_state();
4541 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4543 else {
4544 XMLNode &before = tpl->get_state();
4545 tpl->set_hidden (false, this);
4546 tpl->set (start, end);
4547 XMLNode &after = tpl->get_state();
4548 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4551 commit_reversible_command ();
4554 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4555 * @param rs List to which found regions are added.
4556 * @param where Time to look at.
4557 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4559 void
4560 Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4562 const TrackViewList* tracks;
4564 if (ts.empty()) {
4565 tracks = &track_views;
4566 } else {
4567 tracks = &ts;
4570 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4572 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4574 if (rtv) {
4575 boost::shared_ptr<Track> tr;
4576 boost::shared_ptr<Playlist> pl;
4578 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4580 Playlist::RegionList* regions = pl->regions_at (
4581 (framepos_t) floor ( (double) where * tr->speed()));
4583 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4584 RegionView* rv = rtv->view()->find_view (*i);
4585 if (rv) {
4586 rs.add (rv);
4590 delete regions;
4596 void
4597 Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4599 const TrackViewList* tracks;
4601 if (ts.empty()) {
4602 tracks = &track_views;
4603 } else {
4604 tracks = &ts;
4607 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4608 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4609 if (rtv) {
4610 boost::shared_ptr<Track> tr;
4611 boost::shared_ptr<Playlist> pl;
4613 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4615 Playlist::RegionList* regions = pl->regions_touched (
4616 (framepos_t) floor ( (double)where * tr->speed()), max_framepos);
4618 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4620 RegionView* rv = rtv->view()->find_view (*i);
4622 if (rv) {
4623 rs.push_back (rv);
4627 delete regions;
4633 /** Start with regions that are selected. Then add equivalent regions
4634 * on tracks in the same active edit-enabled route group as any of
4635 * the regions that we started with.
4638 RegionSelection
4639 Editor::get_regions_from_selection ()
4641 return get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
4644 /** Get regions using the following method:
4646 * Make an initial region list using the selected regions, unless
4647 * the edit point is `mouse' and the mouse is over an unselected
4648 * region. In this case, start with just that region.
4650 * Then, make an initial track list of the tracks that these
4651 * regions are on, and if the edit point is not `mouse', add the
4652 * selected tracks.
4654 * Look at this track list and add any other tracks that are on the
4655 * same active edit-enabled route group as one of the initial tracks.
4657 * Finally take the initial region list and add any regions that are
4658 * under the edit point on one of the tracks on the track list to get
4659 * the returned region list.
4661 * The rationale here is that the mouse edit point is special in that
4662 * its position describes both a time and a track; the other edit
4663 * modes only describe a time. Hence if the edit point is `mouse' we
4664 * ignore selected tracks, as we assume the user means something by
4665 * pointing at a particular track. Also in this case we take note of
4666 * the region directly under the edit point, as there is always just one
4667 * (rather than possibly several with non-mouse edit points).
4670 RegionSelection
4671 Editor::get_regions_from_selection_and_edit_point ()
4673 RegionSelection regions;
4675 if (_edit_point == EditAtMouse && entered_regionview && !selection->regions.contains (entered_regionview)) {
4676 regions.add (entered_regionview);
4677 } else {
4678 regions = selection->regions;
4681 TrackViewList tracks;
4683 if (_edit_point != EditAtMouse) {
4684 tracks = selection->tracks;
4687 /* Add any other tracks that have regions that are in the same
4688 edit-activated route group as one of our regions.
4690 for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) {
4692 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4694 if (g && g->is_active() && g->is_edit()) {
4695 tracks.add (axis_views_from_routes (g->route_list()));
4699 if (!tracks.empty()) {
4700 /* now find regions that are at the edit position on those tracks */
4701 framepos_t const where = get_preferred_edit_position ();
4702 get_regions_at (regions, where, tracks);
4705 return regions;
4708 /** Start with regions that are selected, or the entered regionview if none are selected.
4709 * Then add equivalent regions on tracks in the same active edit-enabled route group as any
4710 * of the regions that we started with.
4713 RegionSelection
4714 Editor::get_regions_from_selection_and_entered ()
4716 RegionSelection regions = selection->regions;
4718 if (regions.empty() && entered_regionview) {
4719 regions.add (entered_regionview);
4722 return get_equivalent_regions (regions, ARDOUR::Properties::edit.property_id);
4725 void
4726 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4728 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4730 RouteTimeAxisView* tatv;
4732 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4734 boost::shared_ptr<Playlist> pl;
4735 vector<boost::shared_ptr<Region> > results;
4736 RegionView* marv;
4737 boost::shared_ptr<Track> tr;
4739 if ((tr = tatv->track()) == 0) {
4740 /* bus */
4741 continue;
4744 if ((pl = (tr->playlist())) != 0) {
4745 pl->get_region_list_equivalent_regions (region, results);
4748 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4749 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4750 regions.push_back (marv);
4758 void
4759 Editor::show_rhythm_ferret ()
4761 if (rhythm_ferret == 0) {
4762 rhythm_ferret = new RhythmFerret(*this);
4765 rhythm_ferret->set_session (_session);
4766 rhythm_ferret->show ();
4767 rhythm_ferret->present ();
4770 void
4771 Editor::first_idle ()
4773 MessageDialog* dialog = 0;
4775 if (track_views.size() > 1) {
4776 dialog = new MessageDialog (*this,
4777 string_compose (_("Please wait while %1 loads visual data"), PROGRAM_NAME),
4778 true,
4779 Gtk::MESSAGE_INFO,
4780 Gtk::BUTTONS_NONE);
4781 dialog->present ();
4782 ARDOUR_UI::instance()->flush_pending ();
4785 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4786 (*t)->first_idle();
4789 // first idle adds route children (automation tracks), so we need to redisplay here
4790 _routes->redisplay ();
4792 delete dialog;
4794 _have_idled = true;
4797 gboolean
4798 Editor::_idle_resize (gpointer arg)
4800 return ((Editor*)arg)->idle_resize ();
4803 void
4804 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4806 if (resize_idle_id < 0) {
4807 resize_idle_id = g_idle_add (_idle_resize, this);
4808 _pending_resize_amount = 0;
4811 /* make a note of the smallest resulting height, so that we can clamp the
4812 lower limit at TimeAxisView::hSmall */
4814 int32_t min_resulting = INT32_MAX;
4816 _pending_resize_amount += h;
4817 _pending_resize_view = view;
4819 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4821 if (selection->tracks.contains (_pending_resize_view)) {
4822 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4823 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4827 if (min_resulting < 0) {
4828 min_resulting = 0;
4831 /* clamp */
4832 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4833 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4837 /** Handle pending resizing of tracks */
4838 bool
4839 Editor::idle_resize ()
4841 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4843 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4844 selection->tracks.contains (_pending_resize_view)) {
4846 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4847 if (*i != _pending_resize_view) {
4848 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4853 _pending_resize_amount = 0;
4854 flush_canvas ();
4855 _group_tabs->set_dirty ();
4856 resize_idle_id = -1;
4858 return false;
4861 void
4862 Editor::located ()
4864 ENSURE_GUI_THREAD (*this, &Editor::located);
4866 playhead_cursor->set_position (_session->audible_frame ());
4867 if (_follow_playhead && !_pending_initial_locate) {
4868 reset_x_origin_to_follow_playhead ();
4871 _pending_locate_request = false;
4872 _pending_initial_locate = false;
4875 void
4876 Editor::region_view_added (RegionView *)
4878 _summary->set_dirty ();
4881 void
4882 Editor::region_view_removed ()
4884 _summary->set_dirty ();
4887 TimeAxisView*
4888 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4890 TrackViewList::const_iterator j = track_views.begin ();
4891 while (j != track_views.end()) {
4892 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4893 if (rtv && rtv->route() == r) {
4894 return rtv;
4896 ++j;
4899 return 0;
4903 TrackViewList
4904 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4906 TrackViewList t;
4908 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4909 TimeAxisView* tv = axis_view_from_route (*i);
4910 if (tv) {
4911 t.push_back (tv);
4915 return t;
4919 void
4920 Editor::handle_new_route (RouteList& routes)
4922 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4924 RouteTimeAxisView *rtv;
4925 list<RouteTimeAxisView*> new_views;
4927 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4928 boost::shared_ptr<Route> route = (*x);
4930 if (route->is_hidden() || route->is_monitor()) {
4931 continue;
4934 DataType dt = route->input()->default_type();
4936 if (dt == ARDOUR::DataType::AUDIO) {
4937 rtv = new AudioTimeAxisView (*this, _session, route, *track_canvas);
4938 } else if (dt == ARDOUR::DataType::MIDI) {
4939 rtv = new MidiTimeAxisView (*this, _session, route, *track_canvas);
4940 } else {
4941 throw unknown_type();
4944 new_views.push_back (rtv);
4945 track_views.push_back (rtv);
4947 rtv->effective_gain_display ();
4949 if (internal_editing()) {
4950 rtv->enter_internal_edit_mode ();
4951 } else {
4952 rtv->leave_internal_edit_mode ();
4955 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4956 rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed));
4959 _routes->routes_added (new_views);
4960 _summary->routes_added (new_views);
4962 if (show_editor_mixer_when_tracks_arrive) {
4963 show_editor_mixer (true);
4966 editor_list_button.set_sensitive (true);
4969 void
4970 Editor::timeaxisview_deleted (TimeAxisView *tv)
4972 if (_session && _session->deletion_in_progress()) {
4973 /* the situation is under control */
4974 return;
4977 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
4979 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
4981 _routes->route_removed (tv);
4983 if (tv == entered_track) {
4984 entered_track = 0;
4987 TimeAxisView::Children c = tv->get_child_list ();
4988 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
4989 if (entered_track == i->get()) {
4990 entered_track = 0;
4994 /* remove it from the list of track views */
4996 TrackViewList::iterator i;
4998 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
4999 i = track_views.erase (i);
5002 /* update whatever the current mixer strip is displaying, if revelant */
5004 boost::shared_ptr<Route> route;
5006 if (rtav) {
5007 route = rtav->route ();
5010 if (current_mixer_strip && current_mixer_strip->route() == route) {
5012 TimeAxisView* next_tv;
5014 if (track_views.empty()) {
5015 next_tv = 0;
5016 } else if (i == track_views.end()) {
5017 next_tv = track_views.front();
5018 } else {
5019 next_tv = (*i);
5023 if (next_tv) {
5024 set_selected_mixer_strip (*next_tv);
5025 } else {
5026 /* make the editor mixer strip go away setting the
5027 * button to inactive (which also unticks the menu option)
5030 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
5035 void
5036 Editor::hide_track_in_display (TimeAxisView* tv, bool apply_to_selection)
5038 if (apply_to_selection) {
5039 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ) {
5041 TrackSelection::iterator j = i;
5042 ++j;
5044 hide_track_in_display (*i, false);
5046 i = j;
5048 } else {
5049 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
5051 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
5052 // this will hide the mixer strip
5053 set_selected_mixer_strip (*tv);
5056 _routes->hide_track_in_display (*tv);
5060 bool
5061 Editor::sync_track_view_list_and_routes ()
5063 track_views = TrackViewList (_routes->views ());
5065 _summary->set_dirty ();
5066 _group_tabs->set_dirty ();
5068 return false; // do not call again (until needed)
5071 void
5072 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
5074 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5075 theslot (**i);
5079 /** Find a RouteTimeAxisView by the ID of its route */
5080 RouteTimeAxisView*
5081 Editor::get_route_view_by_route_id (PBD::ID& id) const
5083 RouteTimeAxisView* v;
5085 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5086 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5087 if(v->route()->id() == id) {
5088 return v;
5093 return 0;
5096 void
5097 Editor::fit_route_group (RouteGroup *g)
5099 TrackViewList ts = axis_views_from_routes (g->route_list ());
5100 fit_tracks (ts);
5103 void
5104 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5106 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5108 if (r == 0) {
5109 _session->cancel_audition ();
5110 return;
5113 if (_session->is_auditioning()) {
5114 _session->cancel_audition ();
5115 if (r == last_audition_region) {
5116 return;
5120 _session->audition_region (r);
5121 last_audition_region = r;
5125 void
5126 Editor::hide_a_region (boost::shared_ptr<Region> r)
5128 r->set_hidden (true);
5131 void
5132 Editor::show_a_region (boost::shared_ptr<Region> r)
5134 r->set_hidden (false);
5137 void
5138 Editor::audition_region_from_region_list ()
5140 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5143 void
5144 Editor::hide_region_from_region_list ()
5146 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5149 void
5150 Editor::show_region_in_region_list ()
5152 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5155 void
5156 Editor::step_edit_status_change (bool yn)
5158 if (yn) {
5159 start_step_editing ();
5160 } else {
5161 stop_step_editing ();
5165 void
5166 Editor::start_step_editing ()
5168 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5171 void
5172 Editor::stop_step_editing ()
5174 step_edit_connection.disconnect ();
5177 bool
5178 Editor::check_step_edit ()
5180 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5181 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5182 if (mtv) {
5183 mtv->check_step_edit ();
5187 return true; // do it again, till we stop
5190 bool
5191 Editor::scroll_press (Direction dir)
5193 ++_scroll_callbacks;
5195 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5196 /* delay the first auto-repeat */
5197 return true;
5200 switch (dir) {
5201 case LEFT:
5202 scroll_backward (1);
5203 break;
5205 case RIGHT:
5206 scroll_forward (1);
5207 break;
5209 case UP:
5210 scroll_tracks_up_line ();
5211 break;
5213 case DOWN:
5214 scroll_tracks_down_line ();
5215 break;
5218 /* do hacky auto-repeat */
5219 if (!_scroll_connection.connected ()) {
5221 _scroll_connection = Glib::signal_timeout().connect (
5222 sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), dir), 100
5225 _scroll_callbacks = 0;
5228 return true;
5231 void
5232 Editor::scroll_release ()
5234 _scroll_connection.disconnect ();
5237 /** Queue a change for the Editor viewport x origin to follow the playhead */
5238 void
5239 Editor::reset_x_origin_to_follow_playhead ()
5241 framepos_t const frame = playhead_cursor->current_frame;
5243 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5245 if (_session->transport_speed() < 0) {
5247 if (frame > (current_page_frames() / 2)) {
5248 center_screen (frame-(current_page_frames()/2));
5249 } else {
5250 center_screen (current_page_frames()/2);
5253 } else {
5255 if (frame < leftmost_frame) {
5256 /* moving left */
5257 framepos_t l = 0;
5258 if (_session->transport_rolling()) {
5259 /* rolling; end up with the playhead at the right of the page */
5260 l = frame - current_page_frames ();
5261 } else {
5262 /* not rolling: end up with the playhead 3/4 of the way along the page */
5263 l = frame - (3 * current_page_frames() / 4);
5266 if (l < 0) {
5267 l = 0;
5270 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5271 } else {
5272 /* moving right */
5273 if (_session->transport_rolling()) {
5274 /* rolling: end up with the playhead on the left of the page */
5275 center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5276 } else {
5277 /* not rolling: end up with the playhead 1/4 of the way along the page */
5278 center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5285 void
5286 Editor::super_rapid_screen_update ()
5288 if (!_session || !_session->engine().running()) {
5289 return;
5292 /* METERING / MIXER STRIPS */
5294 /* update track meters, if required */
5295 if (is_mapped() && meters_running) {
5296 RouteTimeAxisView* rtv;
5297 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5298 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5299 rtv->fast_update ();
5304 /* and any current mixer strip */
5305 if (current_mixer_strip) {
5306 current_mixer_strip->fast_update ();
5309 /* PLAYHEAD AND VIEWPORT */
5311 framepos_t const frame = _session->audible_frame();
5313 /* There are a few reasons why we might not update the playhead / viewport stuff:
5315 * 1. we don't update things when there's a pending locate request, otherwise
5316 * when the editor requests a locate there is a chance that this method
5317 * will move the playhead before the locate request is processed, causing
5318 * a visual glitch.
5319 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5320 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5323 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5325 last_update_frame = frame;
5327 if (!_dragging_playhead) {
5328 playhead_cursor->set_position (frame);
5331 if (!_stationary_playhead) {
5333 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5334 reset_x_origin_to_follow_playhead ();
5337 } else {
5339 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5340 editor canvas
5342 #if 0
5343 // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5344 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5345 if (target <= 0.0) {
5346 target = 0.0;
5348 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5349 target = (target * 0.15) + (current * 0.85);
5350 } else {
5351 /* relax */
5354 current = target;
5355 set_horizontal_position (current);
5356 #endif
5363 void
5364 Editor::session_going_away ()
5366 _have_idled = false;
5368 _session_connections.drop_connections ();
5370 super_rapid_screen_update_connection.disconnect ();
5372 selection->clear ();
5373 cut_buffer->clear ();
5375 clicked_regionview = 0;
5376 clicked_axisview = 0;
5377 clicked_routeview = 0;
5378 clicked_crossfadeview = 0;
5379 entered_regionview = 0;
5380 entered_track = 0;
5381 last_update_frame = 0;
5382 _drags->abort ();
5384 playhead_cursor->canvas_item.hide ();
5386 /* rip everything out of the list displays */
5388 _regions->clear ();
5389 _routes->clear ();
5390 _route_groups->clear ();
5392 /* do this first so that deleting a track doesn't reset cms to null
5393 and thus cause a leak.
5396 if (current_mixer_strip) {
5397 if (current_mixer_strip->get_parent() != 0) {
5398 global_hpacker.remove (*current_mixer_strip);
5400 delete current_mixer_strip;
5401 current_mixer_strip = 0;
5404 /* delete all trackviews */
5406 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5407 delete *i;
5409 track_views.clear ();
5411 zoom_range_clock.set_session (0);
5412 nudge_clock.set_session (0);
5414 editor_list_button.set_active(false);
5415 editor_list_button.set_sensitive(false);
5417 /* clear tempo/meter rulers */
5418 remove_metric_marks ();
5419 hide_measures ();
5420 clear_marker_display ();
5422 delete current_bbt_points;
5423 current_bbt_points = 0;
5425 /* get rid of any existing editor mixer strip */
5427 WindowTitle title(Glib::get_application_name());
5428 title += _("Editor");
5430 set_title (title.get_string());
5432 SessionHandlePtr::session_going_away ();
5436 void
5437 Editor::show_editor_list (bool yn)
5439 if (yn) {
5440 _the_notebook.show ();
5441 } else {
5442 _the_notebook.hide ();
5446 void
5447 Editor::change_region_layering_order ()
5449 framepos_t const position = get_preferred_edit_position ();
5451 if (!clicked_routeview) {
5452 if (layering_order_editor) {
5453 layering_order_editor->hide ();
5455 return;
5458 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
5460 if (!track) {
5461 return;
5464 boost::shared_ptr<Playlist> pl = track->playlist();
5466 if (!pl) {
5467 return;
5470 if (layering_order_editor == 0) {
5471 layering_order_editor = new RegionLayeringOrderEditor(*this);
5474 layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position);
5475 layering_order_editor->maybe_present ();
5478 void
5479 Editor::update_region_layering_order_editor ()
5481 if (layering_order_editor && layering_order_editor->is_visible ()) {
5482 change_region_layering_order ();
5486 void
5487 Editor::setup_fade_images ()
5489 _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear")));
5490 _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut")));
5491 _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut")));
5492 _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut")));
5493 _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut")));
5495 _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear")));
5496 _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut")));
5497 _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut")));
5498 _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut")));
5499 _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut")));
5503 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
5504 Gtk::MenuItem&
5505 Editor::action_menu_item (std::string const & name)
5507 Glib::RefPtr<Action> a = editor_actions->get_action (name);
5508 assert (a);
5510 return *manage (a->create_menu_item ());
5513 void
5514 Editor::resize_text_widgets ()
5516 set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_FUDGE+10, 15);
5517 set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_FUDGE+10, 15);
5518 set_size_request_to_display_given_text (snap_type_selector, snap_type_strings, COMBO_FUDGE+10, 15);
5519 set_size_request_to_display_given_text (snap_mode_selector, snap_mode_strings, COMBO_FUDGE+10, 15);
5520 set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_FUDGE+10, 15);
5523 void
5524 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
5526 EventBox* b = manage (new EventBox);
5527 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
5528 Label* l = manage (new Label (name));
5529 l->set_angle (-90);
5530 b->add (*l);
5531 b->show_all ();
5532 _the_notebook.append_page (widget, *b);
5535 bool
5536 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
5538 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
5539 _the_notebook.set_current_page (_the_notebook.page_num (*page));
5542 if (ev->type == GDK_2BUTTON_PRESS) {
5544 /* double-click on a notebook tab shrinks or expands the notebook */
5546 if (_notebook_shrunk) {
5547 edit_pane.set_position (pre_maximal_horizontal_pane_position);
5548 _notebook_shrunk = false;
5549 } else {
5550 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
5551 edit_pane.set_position (edit_pane.get_position() + page->get_width());
5552 _notebook_shrunk = true;
5556 return true;