various fixes to MidiRegionView selection handling, key handling, drawing of ghost...
[ardour2.git] / gtk2_ardour / editor.cc
blobcf1619156f1252fdfce5db507461b19a6b480d51
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 "actions.h"
81 #include "actions.h"
82 #include "analysis_window.h"
83 #include "audio_clock.h"
84 #include "audio_region_view.h"
85 #include "audio_streamview.h"
86 #include "audio_time_axis.h"
87 #include "automation_time_axis.h"
88 #include "bundle_manager.h"
89 #include "canvas-noevent-text.h"
90 #include "canvas_impl.h"
91 #include "crossfade_edit.h"
92 #include "crossfade_view.h"
93 #include "debug.h"
94 #include "editing.h"
95 #include "editor.h"
96 #include "editor_cursors.h"
97 #include "editor_drag.h"
98 #include "editor_group_tabs.h"
99 #include "editor_locations.h"
100 #include "editor_regions.h"
101 #include "editor_route_groups.h"
102 #include "editor_routes.h"
103 #include "editor_snapshots.h"
104 #include "editor_summary.h"
105 #include "global_port_matrix.h"
106 #include "gui_object.h"
107 #include "gui_thread.h"
108 #include "keyboard.h"
109 #include "marker.h"
110 #include "midi_time_axis.h"
111 #include "mixer_strip.h"
112 #include "mouse_cursors.h"
113 #include "playlist_selector.h"
114 #include "public_editor.h"
115 #include "region_layering_order_editor.h"
116 #include "rgb_macros.h"
117 #include "rhythm_ferret.h"
118 #include "selection.h"
119 #include "sfdb_ui.h"
120 #include "simpleline.h"
121 #include "tempo_lines.h"
122 #include "time_axis_view.h"
123 #include "utils.h"
125 #include "i18n.h"
127 #ifdef WITH_CMT
128 #include "imageframe_socket_handler.h"
129 #endif
131 using namespace std;
132 using namespace ARDOUR;
133 using namespace PBD;
134 using namespace Gtk;
135 using namespace Glib;
136 using namespace Gtkmm2ext;
137 using namespace Editing;
139 using PBD::internationalize;
140 using PBD::atoi;
141 using Gtkmm2ext::Keyboard;
143 const double Editor::timebar_height = 15.0;
145 static const gchar *_snap_type_strings[] = {
146 N_("CD Frames"),
147 N_("Timecode Frames"),
148 N_("Timecode Seconds"),
149 N_("Timecode Minutes"),
150 N_("Seconds"),
151 N_("Minutes"),
152 N_("Beats/32"),
153 N_("Beats/28"),
154 N_("Beats/24"),
155 N_("Beats/20"),
156 N_("Beats/16"),
157 N_("Beats/14"),
158 N_("Beats/12"),
159 N_("Beats/10"),
160 N_("Beats/8"),
161 N_("Beats/7"),
162 N_("Beats/6"),
163 N_("Beats/5"),
164 N_("Beats/4"),
165 N_("Beats/3"),
166 N_("Beats/2"),
167 N_("Beats"),
168 N_("Bars"),
169 N_("Marks"),
170 N_("Region starts"),
171 N_("Region ends"),
172 N_("Region syncs"),
173 N_("Region bounds"),
177 static const gchar *_snap_mode_strings[] = {
178 N_("No Grid"),
179 N_("Grid"),
180 N_("Magnetic"),
184 static const gchar *_edit_point_strings[] = {
185 N_("Playhead"),
186 N_("Marker"),
187 N_("Mouse"),
191 static const gchar *_zoom_focus_strings[] = {
192 N_("Left"),
193 N_("Right"),
194 N_("Center"),
195 N_("Playhead"),
196 N_("Mouse"),
197 N_("Edit point"),
201 #ifdef USE_RUBBERBAND
202 static const gchar *_rb_opt_strings[] = {
203 N_("Mushy"),
204 N_("Smooth"),
205 N_("Balanced multitimbral mixture"),
206 N_("Unpitched percussion with stable notes"),
207 N_("Crisp monophonic instrumental"),
208 N_("Unpitched solo percussion"),
209 N_("Resample without preserving pitch"),
212 #endif
214 void
215 show_me_the_size (Requisition* r, const char* what)
217 cerr << "size of " << what << " = " << r->width << " x " << r->height << endl;
220 #ifdef GTKOSX
221 static void
222 pane_size_watcher (Paned* pane)
224 /* if the handle of a pane vanishes into (at least) the tabs of a notebook,
225 it is no longer accessible. so stop that. this doesn't happen on X11,
226 just the quartz backend.
228 ugh.
231 int max_width_of_lhs = GTK_WIDGET(pane->gobj())->allocation.width - 25;
233 gint pos = pane->get_position ();
235 if (pos > max_width_of_lhs) {
236 pane->set_position (max_width_of_lhs);
239 #endif
241 Editor::Editor ()
242 : _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
244 /* time display buttons */
245 , minsec_label (_("Mins:Secs"))
246 , bbt_label (_("Bars:Beats"))
247 , timecode_label (_("Timecode"))
248 , samples_label (_("Samples"))
249 , tempo_label (_("Tempo"))
250 , meter_label (_("Meter"))
251 , mark_label (_("Location Markers"))
252 , range_mark_label (_("Range Markers"))
253 , transport_mark_label (_("Loop/Punch Ranges"))
254 , cd_mark_label (_("CD Markers"))
255 , edit_packer (4, 4, true)
257 /* the values here don't matter: layout widgets
258 reset them as needed.
261 , vertical_adjustment (0.0, 0.0, 10.0, 400.0)
263 /* tool bar related */
265 , zoom_range_clock (new AudioClock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, false, true))
267 , toolbar_selection_clock_table (2,3)
269 , automation_mode_button (_("mode"))
270 , global_automation_button (_("automation"))
272 , _toolbar_viewport (*manage (new Gtk::Adjustment (0, 0, 1e10)), *manage (new Gtk::Adjustment (0, 0, 1e10)))
273 , midi_panic_button (_("Panic"))
275 #ifdef WITH_CMT
276 , image_socket_listener(0)
277 #endif
279 /* nudge */
281 , nudge_clock (new AudioClock (X_("nudge"), false, X_("NudgeClock"), true, false, true))
282 , meters_running(false)
283 , _pending_locate_request (false)
284 , _pending_initial_locate (false)
285 , _last_cut_copy_source_track (0)
287 , _region_selection_change_updates_region_list (true)
289 constructed = false;
291 /* we are a singleton */
293 PublicEditor::_instance = this;
295 _have_idled = false;
297 selection = new Selection (this);
298 cut_buffer = new Selection (this);
300 clicked_regionview = 0;
301 clicked_axisview = 0;
302 clicked_routeview = 0;
303 clicked_crossfadeview = 0;
304 clicked_control_point = 0;
305 last_update_frame = 0;
306 pre_press_cursor = 0;
307 _drags = new DragManager (this);
308 current_mixer_strip = 0;
309 current_bbt_points = 0;
310 tempo_lines = 0;
312 snap_type_strings = I18N (_snap_type_strings);
313 snap_mode_strings = I18N (_snap_mode_strings);
314 zoom_focus_strings = I18N (_zoom_focus_strings);
315 edit_point_strings = I18N (_edit_point_strings);
316 #ifdef USE_RUBBERBAND
317 rb_opt_strings = I18N (_rb_opt_strings);
318 rb_current_opt = 4;
319 #endif
321 snap_threshold = 5.0;
322 bbt_beat_subdivision = 4;
323 _canvas_width = 0;
324 _canvas_height = 0;
325 last_autoscroll_x = 0;
326 last_autoscroll_y = 0;
327 autoscroll_active = false;
328 autoscroll_timeout_tag = -1;
329 logo_item = 0;
331 analysis_window = 0;
333 current_interthread_info = 0;
334 _show_measures = true;
335 show_gain_after_trim = false;
336 last_item_entered = 0;
338 have_pending_keyboard_selection = false;
339 _follow_playhead = true;
340 _stationary_playhead = false;
341 _xfade_visibility = true;
342 editor_ruler_menu = 0;
343 no_ruler_shown_update = false;
344 marker_menu = 0;
345 range_marker_menu = 0;
346 marker_menu_item = 0;
347 tempo_or_meter_marker_menu = 0;
348 transport_marker_menu = 0;
349 new_transport_marker_menu = 0;
350 editor_mixer_strip_width = Wide;
351 show_editor_mixer_when_tracks_arrive = false;
352 region_edit_menu_split_multichannel_item = 0;
353 region_edit_menu_split_item = 0;
354 temp_location = 0;
355 leftmost_frame = 0;
356 current_stepping_trackview = 0;
357 entered_track = 0;
358 entered_regionview = 0;
359 entered_marker = 0;
360 clear_entered_track = false;
361 current_timefx = 0;
362 playhead_cursor = 0;
363 button_release_can_deselect = true;
364 _dragging_playhead = false;
365 _dragging_edit_point = false;
366 select_new_marker = false;
367 rhythm_ferret = 0;
368 layering_order_editor = 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 ControlProtocol::SelectByRID.connect (*this, invalidator (*this), ui_bind (&Editor::control_select, this, _1), gui_context());
708 BasicUI::AccessAction.connect (*this, invalidator (*this), ui_bind (&Editor::access_action, this, _1, _2), gui_context());
710 /* problematic: has to return a value and thus cannot be x-thread */
712 Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
714 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
716 TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
718 _ignore_region_action = false;
719 _last_region_menu_was_main = false;
720 _popup_region_menu_item = 0;
722 _show_marker_lines = false;
723 _over_region_trim_target = false;
725 /* Button bindings */
727 button_bindings = new Bindings;
729 XMLNode* node = button_settings();
730 if (node) {
731 for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) {
732 button_bindings->load (**i);
736 constructed = true;
737 instant_save ();
739 setup_fade_images ();
742 Editor::~Editor()
744 #ifdef WITH_CMT
745 if(image_socket_listener) {
746 if(image_socket_listener->is_connected())
748 image_socket_listener->close_connection() ;
751 delete image_socket_listener ;
752 image_socket_listener = 0 ;
754 #endif
756 delete button_bindings;
757 delete _routes;
758 delete _route_groups;
759 delete track_canvas;
760 delete _drags;
763 XMLNode*
764 Editor::button_settings () const
766 XMLNode* settings = ARDOUR_UI::instance()->editor_settings();
767 XMLNode* node = find_named_node (*settings, X_("Buttons"));
769 if (!node) {
770 cerr << "new empty Button node\n";
771 node = new XMLNode (X_("Buttons"));
774 return node;
777 void
778 Editor::add_toplevel_controls (Container& cont)
780 vpacker.pack_start (cont, false, false);
781 cont.show_all ();
784 void
785 Editor::catch_vanishing_regionview (RegionView *rv)
787 /* note: the selection will take care of the vanishing
788 audioregionview by itself.
791 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
792 _drags->abort ();
795 if (clicked_regionview == rv) {
796 clicked_regionview = 0;
799 if (entered_regionview == rv) {
800 set_entered_regionview (0);
803 if (!_all_region_actions_sensitized) {
804 sensitize_all_region_actions (true);
807 _over_region_trim_target = false;
810 void
811 Editor::set_entered_regionview (RegionView* rv)
813 if (rv == entered_regionview) {
814 return;
817 if (entered_regionview) {
818 entered_regionview->exited ();
821 if ((entered_regionview = rv) != 0) {
822 entered_regionview->entered (internal_editing ());
825 if (!_all_region_actions_sensitized && _last_region_menu_was_main) {
826 /* This RegionView entry might have changed what region actions
827 are allowed, so sensitize them all in case a key is pressed.
829 sensitize_all_region_actions (true);
833 void
834 Editor::set_entered_track (TimeAxisView* tav)
836 if (entered_track) {
837 entered_track->exited ();
840 if ((entered_track = tav) != 0) {
841 entered_track->entered ();
845 void
846 Editor::show_window ()
848 if (!is_visible ()) {
849 show_all ();
851 /* XXX: this is a bit unfortunate; it would probably
852 be nicer if we could just call show () above rather
853 than needing the show_all ()
856 /* re-hide stuff if necessary */
857 editor_list_button_toggled ();
858 parameter_changed ("show-summary");
859 parameter_changed ("show-group-tabs");
860 parameter_changed ("show-zoom-tools");
862 /* now reset all audio_time_axis heights, because widgets might need
863 to be re-hidden
866 TimeAxisView *tv;
868 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
869 tv = (static_cast<TimeAxisView*>(*i));
870 tv->reset_height ();
873 if (current_mixer_strip) {
874 current_mixer_strip->hide_things ();
878 present ();
881 void
882 Editor::instant_save ()
884 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
885 return;
888 if (_session) {
889 _session->add_instant_xml(get_state());
890 } else {
891 Config->add_instant_xml(get_state());
895 void
896 Editor::zoom_adjustment_changed ()
898 if (_session == 0) {
899 return;
902 double fpu = zoom_range_clock->current_duration() / _canvas_width;
904 if (fpu < 1.0) {
905 fpu = 1.0;
906 zoom_range_clock->set ((framepos_t) floor (fpu * _canvas_width));
907 } else if (fpu > _session->current_end_frame() / _canvas_width) {
908 fpu = _session->current_end_frame() / _canvas_width;
909 zoom_range_clock->set ((framepos_t) floor (fpu * _canvas_width));
912 temporal_zoom (fpu);
915 void
916 Editor::control_select (uint32_t rid)
918 /* handles the (static) signal from the ControlProtocol class that
919 * requests setting the selected track to a given RID
922 if (!_session) {
923 return;
926 boost::shared_ptr<Route> r = _session->route_by_remote_id (rid);
928 if (!r) {
929 return;
932 TimeAxisView* tav = axis_view_from_route (r);
934 if (tav) {
935 selection->set (tav);
936 } else {
937 selection->clear_tracks ();
941 void
942 Editor::control_scroll (float fraction)
944 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
946 if (!_session) {
947 return;
950 double step = fraction * current_page_frames();
953 _control_scroll_target is an optional<T>
955 it acts like a pointer to an framepos_t, with
956 a operator conversion to boolean to check
957 that it has a value could possibly use
958 playhead_cursor->current_frame to store the
959 value and a boolean in the class to know
960 when it's out of date
963 if (!_control_scroll_target) {
964 _control_scroll_target = _session->transport_frame();
965 _dragging_playhead = true;
968 if ((fraction < 0.0f) && (*_control_scroll_target < (framepos_t) fabs(step))) {
969 *_control_scroll_target = 0;
970 } else if ((fraction > 0.0f) && (max_framepos - *_control_scroll_target < step)) {
971 *_control_scroll_target = max_framepos - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
972 } else {
973 *_control_scroll_target += (framepos_t) floor (step);
976 /* move visuals, we'll catch up with it later */
978 playhead_cursor->set_position (*_control_scroll_target);
979 UpdateAllTransportClocks (*_control_scroll_target);
981 if (*_control_scroll_target > (current_page_frames() / 2)) {
982 /* try to center PH in window */
983 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
984 } else {
985 reset_x_origin (0);
989 Now we do a timeout to actually bring the session to the right place
990 according to the playhead. This is to avoid reading disk buffers on every
991 call to control_scroll, which is driven by ScrollTimeline and therefore
992 probably by a control surface wheel which can generate lots of events.
994 /* cancel the existing timeout */
996 control_scroll_connection.disconnect ();
998 /* add the next timeout */
1000 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
1003 bool
1004 Editor::deferred_control_scroll (framepos_t /*target*/)
1006 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
1007 // reset for next stream
1008 _control_scroll_target = boost::none;
1009 _dragging_playhead = false;
1010 return false;
1013 void
1014 Editor::access_action (std::string action_group, std::string action_item)
1016 if (!_session) {
1017 return;
1020 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
1022 RefPtr<Action> act;
1023 act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
1025 if (act) {
1026 act->activate();
1030 void
1031 Editor::on_realize ()
1033 Window::on_realize ();
1034 Realized ();
1037 void
1038 Editor::map_position_change (framepos_t frame)
1040 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, frame)
1042 if (_session == 0) {
1043 return;
1046 if (_follow_playhead) {
1047 center_screen (frame);
1050 playhead_cursor->set_position (frame);
1053 void
1054 Editor::center_screen (framepos_t frame)
1056 double page = _canvas_width * frames_per_unit;
1058 /* if we're off the page, then scroll.
1061 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
1062 center_screen_internal (frame, page);
1066 void
1067 Editor::center_screen_internal (framepos_t frame, float page)
1069 page /= 2;
1071 if (frame > page) {
1072 frame -= (framepos_t) page;
1073 } else {
1074 frame = 0;
1077 reset_x_origin (frame);
1081 void
1082 Editor::update_title ()
1084 ENSURE_GUI_THREAD (*this, &Editor::update_title)
1086 if (_session) {
1087 bool dirty = _session->dirty();
1089 string session_name;
1091 if (_session->snap_name() != _session->name()) {
1092 session_name = _session->snap_name();
1093 } else {
1094 session_name = _session->name();
1097 if (dirty) {
1098 session_name = "*" + session_name;
1101 WindowTitle title(session_name);
1102 title += Glib::get_application_name();
1103 set_title (title.get_string());
1107 void
1108 Editor::set_session (Session *t)
1110 SessionHandlePtr::set_session (t);
1112 if (!_session) {
1113 return;
1116 zoom_range_clock->set_session (_session);
1117 _playlist_selector->set_session (_session);
1118 nudge_clock->set_session (_session);
1119 _summary->set_session (_session);
1120 _group_tabs->set_session (_session);
1121 _route_groups->set_session (_session);
1122 _regions->set_session (_session);
1123 _snapshots->set_session (_session);
1124 _routes->set_session (_session);
1125 _locations->set_session (_session);
1127 if (rhythm_ferret) {
1128 rhythm_ferret->set_session (_session);
1131 if (analysis_window) {
1132 analysis_window->set_session (_session);
1135 if (sfbrowser) {
1136 sfbrowser->set_session (_session);
1139 compute_fixed_ruler_scale ();
1141 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1142 set_state (*node, Stateful::loading_state_version);
1144 /* catch up with the playhead */
1146 _session->request_locate (playhead_cursor->current_frame);
1147 _pending_initial_locate = true;
1149 update_title ();
1151 /* These signals can all be emitted by a non-GUI thread. Therefore the
1152 handlers for them must not attempt to directly interact with the GUI,
1153 but use Gtkmm2ext::UI::instance()->call_slot();
1156 _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), ui_bind(&Editor::step_edit_status_change, this, _1), gui_context());
1157 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1158 _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context());
1159 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context());
1160 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1161 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context());
1162 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1163 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
1164 _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Editor::session_state_saved, this, _1), gui_context());
1165 _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context());
1166 _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&Editor::location_gone, this, _1), gui_context());
1167 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1168 _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display, this), gui_context());
1169 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1171 if (Profile->get_sae()) {
1172 Timecode::BBT_Time bbt;
1173 bbt.bars = 0;
1174 bbt.beats = 0;
1175 bbt.ticks = 120;
1176 framepos_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1);
1177 nudge_clock->set_mode(AudioClock::BBT);
1178 nudge_clock->set (pos, true, 0, AudioClock::BBT);
1180 } else {
1181 nudge_clock->set (_session->frame_rate() * 5, true, 0, AudioClock::Timecode); // default of 5 seconds
1184 playhead_cursor->canvas_item.show ();
1186 Location* loc = _session->locations()->auto_loop_location();
1187 if (loc == 0) {
1188 loc = new Location (*_session, 0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1190 if (loc->start() == loc->end()) {
1191 loc->set_end (loc->start() + 1);
1194 _session->locations()->add (loc, false);
1195 _session->set_auto_loop_location (loc);
1196 } else {
1197 // force name
1198 loc->set_name (_("Loop"));
1201 loc = _session->locations()->auto_punch_location();
1203 if (loc == 0) {
1204 loc = new Location (*_session, 0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1206 if (loc->start() == loc->end()) {
1207 loc->set_end (loc->start() + 1);
1210 _session->locations()->add (loc, false);
1211 _session->set_auto_punch_location (loc);
1212 } else {
1213 // force name
1214 loc->set_name (_("Punch"));
1217 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1218 Config->map_parameters (pc);
1219 _session->config.map_parameters (pc);
1221 refresh_location_display ();
1223 restore_ruler_visibility ();
1224 //tempo_map_changed (PropertyChange (0));
1225 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1227 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1228 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1231 super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
1232 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1235 switch (_snap_type) {
1236 case SnapToRegionStart:
1237 case SnapToRegionEnd:
1238 case SnapToRegionSync:
1239 case SnapToRegionBoundary:
1240 build_region_boundary_cache ();
1241 break;
1243 default:
1244 break;
1247 /* register for undo history */
1248 _session->register_with_memento_command_factory(_id, this);
1250 ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated));
1252 start_updating_meters ();
1255 void
1256 Editor::action_pre_activated (Glib::RefPtr<Action> const & a)
1258 if (a->get_name() == "RegionMenu") {
1259 /* When the main menu's region menu is opened, we setup the actions so that they look right
1260 in the menu. I can't find a way of getting a signal when this menu is subsequently closed,
1261 so we resensitize all region actions when the entered regionview or the region selection
1262 changes. HOWEVER we can't always resensitize on entered_regionview change because that
1263 happens after the region context menu is opened. So we set a flag here, too.
1265 What a carry on :(
1267 sensitize_the_right_region_actions ();
1268 _last_region_menu_was_main = true;
1272 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1273 void
1274 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1276 using namespace Menu_Helpers;
1277 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1279 if (arv == 0) {
1280 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1281 /*NOTREACHED*/
1284 MenuList& items (fade_context_menu.items());
1286 items.clear ();
1288 switch (item_type) {
1289 case FadeInItem:
1290 case FadeInHandleItem:
1291 if (arv->audio_region()->fade_in_active()) {
1292 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1293 } else {
1294 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1297 items.push_back (SeparatorElem());
1299 if (Profile->get_sae()) {
1301 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)));
1302 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)));
1304 } else {
1306 items.push_back (
1307 ImageMenuElem (
1308 _("Linear"),
1309 *_fade_in_images[FadeLinear],
1310 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)
1314 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1316 items.push_back (
1317 ImageMenuElem (
1318 _("Slowest"),
1319 *_fade_in_images[FadeFast],
1320 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)
1323 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1325 items.push_back (
1326 ImageMenuElem (
1327 _("Slow"),
1328 *_fade_in_images[FadeLogB],
1329 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogB)
1332 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1334 items.push_back (
1335 ImageMenuElem (
1336 _("Fast"),
1337 *_fade_in_images[FadeLogA],
1338 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogA)
1341 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1343 items.push_back (
1344 ImageMenuElem (
1345 _("Fastest"),
1346 *_fade_in_images[FadeSlow],
1347 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow)
1350 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1353 break;
1355 case FadeOutItem:
1356 case FadeOutHandleItem:
1357 if (arv->audio_region()->fade_out_active()) {
1358 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1359 } else {
1360 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1363 items.push_back (SeparatorElem());
1365 if (Profile->get_sae()) {
1366 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)));
1367 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)));
1368 } else {
1370 items.push_back (
1371 ImageMenuElem (
1372 _("Linear"),
1373 *_fade_out_images[FadeLinear],
1374 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)
1378 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1380 items.push_back (
1381 ImageMenuElem (
1382 _("Slowest"),
1383 *_fade_out_images[FadeFast],
1384 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)
1387 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1389 items.push_back (
1390 ImageMenuElem (
1391 _("Slow"),
1392 *_fade_out_images[FadeLogB],
1393 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogA)
1396 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1398 items.push_back (
1399 ImageMenuElem (
1400 _("Fast"),
1401 *_fade_out_images[FadeLogA],
1402 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogB)
1405 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1407 items.push_back (
1408 ImageMenuElem (
1409 _("Fastest"),
1410 *_fade_out_images[FadeSlow],
1411 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast)
1414 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1417 break;
1419 default:
1420 fatal << _("programming error: ")
1421 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1422 << endmsg;
1423 /*NOTREACHED*/
1426 fade_context_menu.popup (button, time);
1429 void
1430 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1432 using namespace Menu_Helpers;
1433 Menu* (Editor::*build_menu_function)();
1434 Menu *menu;
1436 switch (item_type) {
1437 case RegionItem:
1438 case RegionViewName:
1439 case RegionViewNameHighlight:
1440 case LeftFrameHandle:
1441 case RightFrameHandle:
1442 if (with_selection) {
1443 build_menu_function = &Editor::build_track_selection_context_menu;
1444 } else {
1445 build_menu_function = &Editor::build_track_region_context_menu;
1447 break;
1449 case SelectionItem:
1450 if (with_selection) {
1451 build_menu_function = &Editor::build_track_selection_context_menu;
1452 } else {
1453 build_menu_function = &Editor::build_track_context_menu;
1455 break;
1457 case CrossfadeViewItem:
1458 build_menu_function = &Editor::build_track_crossfade_context_menu;
1459 break;
1461 case StreamItem:
1462 if (clicked_routeview->track()) {
1463 build_menu_function = &Editor::build_track_context_menu;
1464 } else {
1465 build_menu_function = &Editor::build_track_bus_context_menu;
1467 break;
1469 default:
1470 /* probably shouldn't happen but if it does, we don't care */
1471 return;
1474 menu = (this->*build_menu_function)();
1475 menu->set_name ("ArdourContextMenu");
1477 /* now handle specific situations */
1479 switch (item_type) {
1480 case RegionItem:
1481 case RegionViewName:
1482 case RegionViewNameHighlight:
1483 case LeftFrameHandle:
1484 case RightFrameHandle:
1485 if (!with_selection) {
1486 if (region_edit_menu_split_item) {
1487 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1488 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1489 } else {
1490 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1493 if (region_edit_menu_split_multichannel_item) {
1494 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1495 region_edit_menu_split_multichannel_item->set_sensitive (true);
1496 } else {
1497 region_edit_menu_split_multichannel_item->set_sensitive (false);
1501 break;
1503 case SelectionItem:
1504 break;
1506 case CrossfadeViewItem:
1507 break;
1509 case StreamItem:
1510 break;
1512 default:
1513 /* probably shouldn't happen but if it does, we don't care */
1514 return;
1517 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1519 /* Bounce to disk */
1521 using namespace Menu_Helpers;
1522 MenuList& edit_items = menu->items();
1524 edit_items.push_back (SeparatorElem());
1526 switch (clicked_routeview->audio_track()->freeze_state()) {
1527 case AudioTrack::NoFreeze:
1528 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1529 break;
1531 case AudioTrack::Frozen:
1532 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1533 break;
1535 case AudioTrack::UnFrozen:
1536 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1537 break;
1542 if (item_type == StreamItem && clicked_routeview) {
1543 clicked_routeview->build_underlay_menu(menu);
1546 /* When the region menu is opened, we setup the actions so that they look right
1547 in the menu.
1549 sensitize_the_right_region_actions ();
1550 _last_region_menu_was_main = false;
1552 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1553 menu->popup (button, time);
1556 Menu*
1557 Editor::build_track_context_menu ()
1559 using namespace Menu_Helpers;
1561 MenuList& edit_items = track_context_menu.items();
1562 edit_items.clear();
1564 add_dstream_context_items (edit_items);
1565 return &track_context_menu;
1568 Menu*
1569 Editor::build_track_bus_context_menu ()
1571 using namespace Menu_Helpers;
1573 MenuList& edit_items = track_context_menu.items();
1574 edit_items.clear();
1576 add_bus_context_items (edit_items);
1577 return &track_context_menu;
1580 Menu*
1581 Editor::build_track_region_context_menu ()
1583 using namespace Menu_Helpers;
1584 MenuList& edit_items = track_region_context_menu.items();
1585 edit_items.clear();
1587 /* we've just cleared the track region context menu, so the menu that these
1588 two items were on will have disappeared; stop them dangling.
1590 region_edit_menu_split_item = 0;
1591 region_edit_menu_split_multichannel_item = 0;
1593 /* we might try to use items that are currently attached to a crossfade menu,
1594 so clear that, too.
1596 track_crossfade_context_menu.items().clear ();
1598 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1600 if (rtv) {
1601 boost::shared_ptr<Track> tr;
1602 boost::shared_ptr<Playlist> pl;
1604 if ((tr = rtv->track())) {
1605 add_region_context_items (edit_items, tr);
1609 add_dstream_context_items (edit_items);
1611 return &track_region_context_menu;
1614 Menu*
1615 Editor::build_track_crossfade_context_menu ()
1617 using namespace Menu_Helpers;
1618 MenuList& edit_items = track_crossfade_context_menu.items();
1619 edit_items.clear ();
1621 /* we might try to use items that are currently attached to a crossfade menu,
1622 so clear that, too.
1624 track_region_context_menu.items().clear ();
1626 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1628 if (atv) {
1629 boost::shared_ptr<Track> tr;
1630 boost::shared_ptr<Playlist> pl;
1631 boost::shared_ptr<AudioPlaylist> apl;
1633 if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1635 AudioPlaylist::Crossfades xfades;
1636 framepos_t where;
1637 bool ignored;
1639 /* The xfade menu is a bit of a special case, as we always use the mouse position
1640 to decide whether or not to display it (rather than the edit point). No particularly
1641 strong reasons for this, other than it is a bit surprising to right-click on a xfade
1642 and not get a menu.
1644 mouse_frame (where, ignored);
1645 apl->crossfades_at (where, xfades);
1647 bool const many = xfades.size() > 1;
1649 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1650 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1653 add_region_context_items (edit_items, tr);
1657 add_dstream_context_items (edit_items);
1659 return &track_crossfade_context_menu;
1662 void
1663 Editor::analyze_region_selection ()
1665 if (analysis_window == 0) {
1666 analysis_window = new AnalysisWindow();
1668 if (_session != 0)
1669 analysis_window->set_session(_session);
1671 analysis_window->show_all();
1674 analysis_window->set_regionmode();
1675 analysis_window->analyze();
1677 analysis_window->present();
1680 void
1681 Editor::analyze_range_selection()
1683 if (analysis_window == 0) {
1684 analysis_window = new AnalysisWindow();
1686 if (_session != 0)
1687 analysis_window->set_session(_session);
1689 analysis_window->show_all();
1692 analysis_window->set_rangemode();
1693 analysis_window->analyze();
1695 analysis_window->present();
1698 Menu*
1699 Editor::build_track_selection_context_menu ()
1701 using namespace Menu_Helpers;
1702 MenuList& edit_items = track_selection_context_menu.items();
1703 edit_items.clear ();
1705 add_selection_context_items (edit_items);
1706 // edit_items.push_back (SeparatorElem());
1707 // add_dstream_context_items (edit_items);
1709 return &track_selection_context_menu;
1712 /** Add context menu items relevant to crossfades.
1713 * @param edit_items List to add the items to.
1715 void
1716 Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1718 using namespace Menu_Helpers;
1719 Menu *xfade_menu = manage (new Menu);
1720 MenuList& items = xfade_menu->items();
1721 xfade_menu->set_name ("ArdourContextMenu");
1722 string str;
1724 if (xfade->active()) {
1725 str = _("Mute");
1726 } else {
1727 str = _("Unmute");
1730 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1731 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1733 if (xfade->can_follow_overlap()) {
1735 if (xfade->following_overlap()) {
1736 str = _("Convert to Short");
1737 } else {
1738 str = _("Convert to Full");
1741 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1744 if (many) {
1745 str = xfade->out()->name();
1746 str += "->";
1747 str += xfade->in()->name();
1748 } else {
1749 str = _("Crossfade");
1752 edit_items.push_back (MenuElem (str, *xfade_menu));
1753 edit_items.push_back (SeparatorElem());
1756 void
1757 Editor::xfade_edit_left_region ()
1759 if (clicked_crossfadeview) {
1760 clicked_crossfadeview->left_view.show_region_editor ();
1764 void
1765 Editor::xfade_edit_right_region ()
1767 if (clicked_crossfadeview) {
1768 clicked_crossfadeview->right_view.show_region_editor ();
1772 void
1773 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::shared_ptr<Track> track)
1775 using namespace Menu_Helpers;
1777 /* OK, stick the region submenu at the top of the list, and then add
1778 the standard items.
1781 RegionSelection rs = get_regions_from_selection_and_entered ();
1783 string::size_type pos = 0;
1784 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1786 /* we have to hack up the region name because "_" has a special
1787 meaning for menu titles.
1790 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1791 menu_item_name.replace (pos, 1, "__");
1792 pos += 2;
1795 if (_popup_region_menu_item == 0) {
1796 _popup_region_menu_item = new MenuItem (menu_item_name);
1797 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1798 _popup_region_menu_item->show ();
1799 } else {
1800 _popup_region_menu_item->set_label (menu_item_name);
1803 /* Use the mouse position rather than the edit point to decide whether to show the `choose top region'
1804 dialogue. If we use the edit point it gets a bit messy because the user still has to click over
1805 *some* region in order to get the region context menu stuff to be displayed at all.
1808 framepos_t mouse;
1809 bool ignored;
1810 mouse_frame (mouse, ignored);
1812 edit_items.push_back (*_popup_region_menu_item);
1813 if (track->playlist()->count_regions_at (mouse) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1814 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region")->create_menu_item ()));
1816 edit_items.push_back (SeparatorElem());
1819 /** Add context menu items relevant to selection ranges.
1820 * @param edit_items List to add the items to.
1822 void
1823 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1825 using namespace Menu_Helpers;
1827 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1828 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1830 edit_items.push_back (SeparatorElem());
1831 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1833 edit_items.push_back (SeparatorElem());
1835 edit_items.push_back (
1836 MenuElem (
1837 _("Move Range Start to Previous Region Boundary"),
1838 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, false)
1842 edit_items.push_back (
1843 MenuElem (
1844 _("Move Range Start to Next Region Boundary"),
1845 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, true)
1849 edit_items.push_back (
1850 MenuElem (
1851 _("Move Range End to Previous Region Boundary"),
1852 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, false)
1856 edit_items.push_back (
1857 MenuElem (
1858 _("Move Range End to Next Region Boundary"),
1859 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, true)
1863 edit_items.push_back (SeparatorElem());
1864 edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection)));
1865 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1867 edit_items.push_back (SeparatorElem());
1868 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1870 edit_items.push_back (SeparatorElem());
1871 edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1872 edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1874 edit_items.push_back (SeparatorElem());
1875 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1877 edit_items.push_back (SeparatorElem());
1878 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1879 edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1880 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1882 edit_items.push_back (SeparatorElem());
1883 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1884 edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1885 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1886 edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1887 edit_items.push_back (MenuElem (_("Export Range"), sigc::mem_fun(*this, &Editor::export_selection)));
1891 void
1892 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1894 using namespace Menu_Helpers;
1896 /* Playback */
1898 Menu *play_menu = manage (new Menu);
1899 MenuList& play_items = play_menu->items();
1900 play_menu->set_name ("ArdourContextMenu");
1902 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1903 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1904 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1905 play_items.push_back (SeparatorElem());
1906 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
1908 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1910 /* Selection */
1912 Menu *select_menu = manage (new Menu);
1913 MenuList& select_items = select_menu->items();
1914 select_menu->set_name ("ArdourContextMenu");
1916 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1917 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1918 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1919 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1920 select_items.push_back (SeparatorElem());
1921 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
1922 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
1923 select_items.push_back (SeparatorElem());
1924 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1925 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1926 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1927 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1928 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
1929 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
1930 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
1932 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1934 /* Cut-n-Paste */
1936 Menu *cutnpaste_menu = manage (new Menu);
1937 MenuList& cutnpaste_items = cutnpaste_menu->items();
1938 cutnpaste_menu->set_name ("ArdourContextMenu");
1940 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1941 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1942 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1944 cutnpaste_items.push_back (SeparatorElem());
1946 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
1947 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
1949 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
1951 /* Adding new material */
1953 edit_items.push_back (SeparatorElem());
1954 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
1955 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
1957 /* Nudge track */
1959 Menu *nudge_menu = manage (new Menu());
1960 MenuList& nudge_items = nudge_menu->items();
1961 nudge_menu->set_name ("ArdourContextMenu");
1963 edit_items.push_back (SeparatorElem());
1964 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1965 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1966 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1967 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1969 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1972 void
1973 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
1975 using namespace Menu_Helpers;
1977 /* Playback */
1979 Menu *play_menu = manage (new Menu);
1980 MenuList& play_items = play_menu->items();
1981 play_menu->set_name ("ArdourContextMenu");
1983 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1984 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1985 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1987 /* Selection */
1989 Menu *select_menu = manage (new Menu);
1990 MenuList& select_items = select_menu->items();
1991 select_menu->set_name ("ArdourContextMenu");
1993 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1994 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1995 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1996 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1997 select_items.push_back (SeparatorElem());
1998 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1999 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
2000 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2001 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2003 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2005 /* Cut-n-Paste */
2007 Menu *cutnpaste_menu = manage (new Menu);
2008 MenuList& cutnpaste_items = cutnpaste_menu->items();
2009 cutnpaste_menu->set_name ("ArdourContextMenu");
2011 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
2012 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
2013 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
2015 Menu *nudge_menu = manage (new Menu());
2016 MenuList& nudge_items = nudge_menu->items();
2017 nudge_menu->set_name ("ArdourContextMenu");
2019 edit_items.push_back (SeparatorElem());
2020 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
2021 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
2022 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
2023 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
2025 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2028 SnapType
2029 Editor::snap_type() const
2031 return _snap_type;
2034 SnapMode
2035 Editor::snap_mode() const
2037 return _snap_mode;
2040 void
2041 Editor::set_snap_to (SnapType st)
2043 unsigned int snap_ind = (unsigned int)st;
2045 _snap_type = st;
2047 if (snap_ind > snap_type_strings.size() - 1) {
2048 snap_ind = 0;
2049 _snap_type = (SnapType)snap_ind;
2052 string str = snap_type_strings[snap_ind];
2054 if (str != snap_type_selector.get_active_text()) {
2055 snap_type_selector.set_active_text (str);
2058 instant_save ();
2060 switch (_snap_type) {
2061 case SnapToBeatDiv32:
2062 case SnapToBeatDiv28:
2063 case SnapToBeatDiv24:
2064 case SnapToBeatDiv20:
2065 case SnapToBeatDiv16:
2066 case SnapToBeatDiv14:
2067 case SnapToBeatDiv12:
2068 case SnapToBeatDiv10:
2069 case SnapToBeatDiv8:
2070 case SnapToBeatDiv7:
2071 case SnapToBeatDiv6:
2072 case SnapToBeatDiv5:
2073 case SnapToBeatDiv4:
2074 case SnapToBeatDiv3:
2075 case SnapToBeatDiv2:
2076 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
2077 update_tempo_based_rulers ();
2078 break;
2080 case SnapToRegionStart:
2081 case SnapToRegionEnd:
2082 case SnapToRegionSync:
2083 case SnapToRegionBoundary:
2084 build_region_boundary_cache ();
2085 break;
2087 default:
2088 /* relax */
2089 break;
2092 SnapChanged (); /* EMIT SIGNAL */
2095 void
2096 Editor::set_snap_mode (SnapMode mode)
2098 _snap_mode = mode;
2099 string str = snap_mode_strings[(int)mode];
2101 if (str != snap_mode_selector.get_active_text ()) {
2102 snap_mode_selector.set_active_text (str);
2105 instant_save ();
2107 void
2108 Editor::set_edit_point_preference (EditPoint ep, bool force)
2110 bool changed = (_edit_point != ep);
2112 _edit_point = ep;
2113 string str = edit_point_strings[(int)ep];
2115 if (str != edit_point_selector.get_active_text ()) {
2116 edit_point_selector.set_active_text (str);
2119 set_canvas_cursor ();
2121 if (!force && !changed) {
2122 return;
2125 const char* action=NULL;
2127 switch (_edit_point) {
2128 case EditAtPlayhead:
2129 action = "edit-at-playhead";
2130 break;
2131 case EditAtSelectedMarker:
2132 action = "edit-at-marker";
2133 break;
2134 case EditAtMouse:
2135 action = "edit-at-mouse";
2136 break;
2139 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2140 if (act) {
2141 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2144 framepos_t foo;
2145 bool in_track_canvas;
2147 if (!mouse_frame (foo, in_track_canvas)) {
2148 in_track_canvas = false;
2151 reset_canvas_action_sensitivity (in_track_canvas);
2153 instant_save ();
2157 Editor::set_state (const XMLNode& node, int /*version*/)
2159 const XMLProperty* prop;
2160 XMLNode* geometry;
2161 int x, y;
2162 Gdk::Geometry g;
2164 if ((prop = node.property ("id")) != 0) {
2165 _id = prop->value ();
2168 g.base_width = default_width;
2169 g.base_height = default_height;
2170 x = 1;
2171 y = 1;
2173 if ((geometry = find_named_node (node, "geometry")) != 0) {
2175 XMLProperty* prop;
2177 if ((prop = geometry->property("x_size")) == 0) {
2178 prop = geometry->property ("x-size");
2180 if (prop) {
2181 g.base_width = atoi(prop->value());
2183 if ((prop = geometry->property("y_size")) == 0) {
2184 prop = geometry->property ("y-size");
2186 if (prop) {
2187 g.base_height = atoi(prop->value());
2190 if ((prop = geometry->property ("x_pos")) == 0) {
2191 prop = geometry->property ("x-pos");
2193 if (prop) {
2194 x = atoi (prop->value());
2197 if ((prop = geometry->property ("y_pos")) == 0) {
2198 prop = geometry->property ("y-pos");
2200 if (prop) {
2201 y = atoi (prop->value());
2205 set_default_size (g.base_width, g.base_height);
2206 move (x, y);
2208 if (_session && (prop = node.property ("playhead"))) {
2209 framepos_t pos;
2210 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2211 playhead_cursor->set_position (pos);
2212 } else {
2213 playhead_cursor->set_position (0);
2216 if ((prop = node.property ("mixer-width"))) {
2217 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2220 if ((prop = node.property ("zoom-focus"))) {
2221 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2224 if ((prop = node.property ("zoom"))) {
2225 reset_zoom (PBD::atof (prop->value()));
2226 } else {
2227 reset_zoom (frames_per_unit);
2230 if ((prop = node.property ("snap-to"))) {
2231 set_snap_to ((SnapType) atoi (prop->value()));
2234 if ((prop = node.property ("snap-mode"))) {
2235 set_snap_mode ((SnapMode) atoi (prop->value()));
2238 if ((prop = node.property ("mouse-mode"))) {
2239 MouseMode m = str2mousemode(prop->value());
2240 set_mouse_mode (m, true);
2241 } else {
2242 set_mouse_mode (MouseObject, true);
2245 if ((prop = node.property ("left-frame")) != 0) {
2246 framepos_t pos;
2247 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2248 reset_x_origin (pos);
2252 if ((prop = node.property ("y-origin")) != 0) {
2253 reset_y_origin (atof (prop->value ()));
2256 if ((prop = node.property ("internal-edit"))) {
2257 bool yn = string_is_affirmative (prop->value());
2258 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2259 if (act) {
2260 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2261 tact->set_active (!yn);
2262 tact->set_active (yn);
2266 if ((prop = node.property ("join-object-range"))) {
2267 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2270 if ((prop = node.property ("edit-point"))) {
2271 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2274 if ((prop = node.property ("show-measures"))) {
2275 bool yn = string_is_affirmative (prop->value());
2276 _show_measures = yn;
2277 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2278 if (act) {
2279 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2280 /* do it twice to force the change */
2281 tact->set_active (!yn);
2282 tact->set_active (yn);
2286 if ((prop = node.property ("follow-playhead"))) {
2287 bool yn = string_is_affirmative (prop->value());
2288 set_follow_playhead (yn);
2289 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2290 if (act) {
2291 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2292 if (tact->get_active() != yn) {
2293 tact->set_active (yn);
2298 if ((prop = node.property ("stationary-playhead"))) {
2299 bool yn = string_is_affirmative (prop->value());
2300 set_stationary_playhead (yn);
2301 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2302 if (act) {
2303 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2304 if (tact->get_active() != yn) {
2305 tact->set_active (yn);
2310 if ((prop = node.property ("region-list-sort-type"))) {
2311 RegionListSortType st;
2312 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2315 if ((prop = node.property ("xfades-visible"))) {
2316 bool yn = string_is_affirmative (prop->value());
2317 _xfade_visibility = !yn;
2318 // set_xfade_visibility (yn);
2321 if ((prop = node.property ("show-editor-mixer"))) {
2323 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2324 assert (act);
2326 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2327 bool yn = string_is_affirmative (prop->value());
2329 /* do it twice to force the change */
2331 tact->set_active (!yn);
2332 tact->set_active (yn);
2335 if ((prop = node.property ("show-editor-list"))) {
2337 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2338 assert (act);
2340 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2341 bool yn = string_is_affirmative (prop->value());
2343 /* do it twice to force the change */
2345 tact->set_active (!yn);
2346 tact->set_active (yn);
2349 if ((prop = node.property (X_("editor-list-page")))) {
2350 _the_notebook.set_current_page (atoi (prop->value ()));
2353 if ((prop = node.property (X_("show-marker-lines")))) {
2354 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2355 assert (act);
2356 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2357 bool yn = string_is_affirmative (prop->value ());
2359 tact->set_active (!yn);
2360 tact->set_active (yn);
2363 XMLNodeList children = node.children ();
2364 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2365 selection->set_state (**i, Stateful::current_state_version);
2366 _regions->set_state (**i);
2369 return 0;
2372 XMLNode&
2373 Editor::get_state ()
2375 XMLNode* node = new XMLNode ("Editor");
2376 char buf[32];
2378 _id.print (buf, sizeof (buf));
2379 node->add_property ("id", buf);
2381 if (is_realized()) {
2382 Glib::RefPtr<Gdk::Window> win = get_window();
2384 int x, y, width, height;
2385 win->get_root_origin(x, y);
2386 win->get_size(width, height);
2388 XMLNode* geometry = new XMLNode ("geometry");
2390 snprintf(buf, sizeof(buf), "%d", width);
2391 geometry->add_property("x-size", string(buf));
2392 snprintf(buf, sizeof(buf), "%d", height);
2393 geometry->add_property("y-size", string(buf));
2394 snprintf(buf, sizeof(buf), "%d", x);
2395 geometry->add_property("x-pos", string(buf));
2396 snprintf(buf, sizeof(buf), "%d", y);
2397 geometry->add_property("y-pos", string(buf));
2398 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2399 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2400 geometry->add_property("notebook-shrunk", _notebook_shrunk ? "1" : "0");
2401 snprintf(buf,sizeof(buf), "%d",pre_maximal_horizontal_pane_position);
2402 geometry->add_property("pre-maximal-horizontal-pane-position", string(buf));
2403 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2404 geometry->add_property("edit-vertical-pane-pos", string(buf));
2406 node->add_child_nocopy (*geometry);
2409 maybe_add_mixer_strip_width (*node);
2411 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2412 node->add_property ("zoom-focus", buf);
2413 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2414 node->add_property ("zoom", buf);
2415 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2416 node->add_property ("snap-to", buf);
2417 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2418 node->add_property ("snap-mode", buf);
2420 node->add_property ("edit-point", enum_2_string (_edit_point));
2422 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2423 node->add_property ("playhead", buf);
2424 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2425 node->add_property ("left-frame", buf);
2426 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2427 node->add_property ("y-origin", buf);
2429 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2430 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2431 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2432 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2433 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2434 node->add_property ("mouse-mode", enum2str(mouse_mode));
2435 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2436 node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2438 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2439 if (act) {
2440 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2441 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2444 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2445 if (act) {
2446 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2447 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2450 snprintf (buf, sizeof (buf), "%d", _the_notebook.get_current_page ());
2451 node->add_property (X_("editor-list-page"), buf);
2453 if (button_bindings) {
2454 XMLNode* bb = new XMLNode (X_("Buttons"));
2455 button_bindings->save (*bb);
2456 node->add_child_nocopy (*bb);
2459 node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no");
2461 node->add_child_nocopy (selection->get_state ());
2462 node->add_child_nocopy (_regions->get_state ());
2464 return *node;
2469 /** @param y y offset from the top of all trackviews.
2470 * @return pair: TimeAxisView that y is over, layer index.
2471 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2472 * in stacked region display mode, otherwise 0.
2474 std::pair<TimeAxisView *, layer_t>
2475 Editor::trackview_by_y_position (double y)
2477 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2479 std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
2480 if (r.first) {
2481 return r;
2485 return std::make_pair ( (TimeAxisView *) 0, 0);
2488 /** Snap a position to the grid, if appropriate, taking into account current
2489 * grid settings and also the state of any snap modifier keys that may be pressed.
2490 * @param start Position to snap.
2491 * @param event Event to get current key modifier information from, or 0.
2493 void
2494 Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2496 if (!_session || !event) {
2497 return;
2500 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2501 if (_snap_mode == SnapOff) {
2502 snap_to_internal (start, direction, for_mark);
2504 } else {
2505 if (_snap_mode != SnapOff) {
2506 snap_to_internal (start, direction, for_mark);
2511 void
2512 Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark)
2514 if (!_session || _snap_mode == SnapOff) {
2515 return;
2518 snap_to_internal (start, direction, for_mark);
2521 void
2522 Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /*for_mark*/)
2524 const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2525 framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2527 switch (_snap_type) {
2528 case SnapToTimecodeFrame:
2529 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2530 start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2531 } else {
2532 start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2534 break;
2536 case SnapToTimecodeSeconds:
2537 if (_session->config.get_timecode_offset_negative()) {
2538 start += _session->config.get_timecode_offset ();
2539 } else {
2540 start -= _session->config.get_timecode_offset ();
2542 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2543 start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2544 } else {
2545 start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2548 if (_session->config.get_timecode_offset_negative()) {
2549 start -= _session->config.get_timecode_offset ();
2550 } else {
2551 start += _session->config.get_timecode_offset ();
2553 break;
2555 case SnapToTimecodeMinutes:
2556 if (_session->config.get_timecode_offset_negative()) {
2557 start += _session->config.get_timecode_offset ();
2558 } else {
2559 start -= _session->config.get_timecode_offset ();
2561 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2562 start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2563 } else {
2564 start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2566 if (_session->config.get_timecode_offset_negative()) {
2567 start -= _session->config.get_timecode_offset ();
2568 } else {
2569 start += _session->config.get_timecode_offset ();
2571 break;
2572 default:
2573 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2574 /*NOTREACHED*/
2578 void
2579 Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark)
2581 const framepos_t one_second = _session->frame_rate();
2582 const framepos_t one_minute = _session->frame_rate() * 60;
2583 framepos_t presnap = start;
2584 framepos_t before;
2585 framepos_t after;
2587 switch (_snap_type) {
2588 case SnapToTimecodeFrame:
2589 case SnapToTimecodeSeconds:
2590 case SnapToTimecodeMinutes:
2591 return timecode_snap_to_internal (start, direction, for_mark);
2593 case SnapToCDFrame:
2594 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2595 start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2596 } else {
2597 start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2599 break;
2601 case SnapToSeconds:
2602 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2603 start = (framepos_t) ceil ((double) start / one_second) * one_second;
2604 } else {
2605 start = (framepos_t) floor ((double) start / one_second) * one_second;
2607 break;
2609 case SnapToMinutes:
2610 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2611 start = (framepos_t) ceil ((double) start / one_minute) * one_minute;
2612 } else {
2613 start = (framepos_t) floor ((double) start / one_minute) * one_minute;
2615 break;
2617 case SnapToBar:
2618 start = _session->tempo_map().round_to_bar (start, direction);
2619 break;
2621 case SnapToBeat:
2622 start = _session->tempo_map().round_to_beat (start, direction);
2623 break;
2625 case SnapToBeatDiv32:
2626 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2627 break;
2628 case SnapToBeatDiv28:
2629 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2630 break;
2631 case SnapToBeatDiv24:
2632 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2633 break;
2634 case SnapToBeatDiv20:
2635 start = _session->tempo_map().round_to_beat_subdivision (start, 20, direction);
2636 break;
2637 case SnapToBeatDiv16:
2638 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2639 break;
2640 case SnapToBeatDiv14:
2641 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2642 break;
2643 case SnapToBeatDiv12:
2644 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2645 break;
2646 case SnapToBeatDiv10:
2647 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2648 break;
2649 case SnapToBeatDiv8:
2650 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2651 break;
2652 case SnapToBeatDiv7:
2653 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2654 break;
2655 case SnapToBeatDiv6:
2656 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2657 break;
2658 case SnapToBeatDiv5:
2659 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2660 break;
2661 case SnapToBeatDiv4:
2662 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2663 break;
2664 case SnapToBeatDiv3:
2665 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2666 break;
2667 case SnapToBeatDiv2:
2668 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2669 break;
2671 case SnapToMark:
2672 if (for_mark) {
2673 return;
2676 _session->locations()->marks_either_side (start, before, after);
2678 if (before == max_framepos) {
2679 start = after;
2680 } else if (after == max_framepos) {
2681 start = before;
2682 } else if (before != max_framepos && after != max_framepos) {
2683 /* have before and after */
2684 if ((start - before) < (after - start)) {
2685 start = before;
2686 } else {
2687 start = after;
2691 break;
2693 case SnapToRegionStart:
2694 case SnapToRegionEnd:
2695 case SnapToRegionSync:
2696 case SnapToRegionBoundary:
2697 if (!region_boundary_cache.empty()) {
2699 vector<framepos_t>::iterator prev = region_boundary_cache.end ();
2700 vector<framepos_t>::iterator next = region_boundary_cache.end ();
2702 if (direction > 0) {
2703 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2704 } else {
2705 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2708 if (next != region_boundary_cache.begin ()) {
2709 prev = next;
2710 prev--;
2713 framepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2714 framepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2716 if (start > (p + n) / 2) {
2717 start = n;
2718 } else {
2719 start = p;
2722 break;
2725 switch (_snap_mode) {
2726 case SnapNormal:
2727 return;
2729 case SnapMagnetic:
2731 if (presnap > start) {
2732 if (presnap > (start + unit_to_frame(snap_threshold))) {
2733 start = presnap;
2736 } else if (presnap < start) {
2737 if (presnap < (start - unit_to_frame(snap_threshold))) {
2738 start = presnap;
2742 default:
2743 /* handled at entry */
2744 return;
2750 void
2751 Editor::setup_toolbar ()
2753 HBox* mode_box = manage(new HBox);
2754 mode_box->set_border_width (2);
2755 mode_box->set_spacing(4);
2757 /* table containing mode buttons */
2759 HBox* mouse_mode_button_box = manage (new HBox ());
2761 if (Profile->get_sae()) {
2762 mouse_mode_button_box->pack_start (mouse_move_button);
2763 } else {
2764 mouse_mode_button_box->pack_start (mouse_move_button);
2765 mouse_mode_button_box->pack_start (join_object_range_button);
2766 mouse_mode_button_box->pack_start (mouse_select_button);
2769 mouse_mode_button_box->pack_start (mouse_zoom_button);
2771 if (!Profile->get_sae()) {
2772 mouse_mode_button_box->pack_start (mouse_gain_button);
2775 mouse_mode_button_box->pack_start (mouse_timefx_button);
2776 mouse_mode_button_box->pack_start (mouse_audition_button);
2777 mouse_mode_button_box->pack_start (internal_edit_button);
2779 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2780 if (!Profile->get_sae()) {
2781 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2783 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2785 edit_mode_selector.set_name ("EditModeSelector");
2786 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2787 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2789 mode_box->pack_start (edit_mode_selector);
2790 mode_box->pack_start (*mouse_mode_button_box);
2792 _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2793 _mouse_mode_tearoff->set_name ("MouseModeBase");
2794 _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2796 if (Profile->get_sae()) {
2797 _mouse_mode_tearoff->set_can_be_torn_off (false);
2800 _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2801 &_mouse_mode_tearoff->tearoff_window()));
2802 _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2803 &_mouse_mode_tearoff->tearoff_window(), 1));
2804 _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2805 &_mouse_mode_tearoff->tearoff_window()));
2806 _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2807 &_mouse_mode_tearoff->tearoff_window(), 1));
2809 mouse_move_button.set_mode (false);
2810 mouse_select_button.set_mode (false);
2811 mouse_gain_button.set_mode (false);
2812 mouse_zoom_button.set_mode (false);
2813 mouse_timefx_button.set_mode (false);
2814 mouse_audition_button.set_mode (false);
2815 join_object_range_button.set_mode (false);
2817 mouse_move_button.set_name ("MouseModeButton");
2818 mouse_select_button.set_name ("MouseModeButton");
2819 mouse_gain_button.set_name ("MouseModeButton");
2820 mouse_zoom_button.set_name ("MouseModeButton");
2821 mouse_timefx_button.set_name ("MouseModeButton");
2822 mouse_audition_button.set_name ("MouseModeButton");
2823 internal_edit_button.set_name ("MouseModeButton");
2824 join_object_range_button.set_name ("MouseModeButton");
2826 mouse_move_button.unset_flags (CAN_FOCUS);
2827 mouse_select_button.unset_flags (CAN_FOCUS);
2828 mouse_gain_button.unset_flags (CAN_FOCUS);
2829 mouse_zoom_button.unset_flags (CAN_FOCUS);
2830 mouse_timefx_button.unset_flags (CAN_FOCUS);
2831 mouse_audition_button.unset_flags (CAN_FOCUS);
2832 internal_edit_button.unset_flags (CAN_FOCUS);
2833 join_object_range_button.unset_flags (CAN_FOCUS);
2835 /* Zoom */
2837 _zoom_box.set_spacing (1);
2838 _zoom_box.set_border_width (0);
2840 zoom_in_button.set_name ("EditorTimeButton");
2841 zoom_in_button.set_image (*(manage (new Image (::get_icon ("zoom_in")))));
2842 zoom_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), false));
2844 zoom_out_button.set_name ("EditorTimeButton");
2845 zoom_out_button.set_image (*(manage (new Image (::get_icon ("zoom_out")))));
2846 zoom_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), true));
2848 zoom_out_full_button.set_name ("EditorTimeButton");
2849 zoom_out_full_button.set_image (*(manage (new Image (::get_icon ("zoom_full")))));
2850 zoom_out_full_button.signal_clicked().connect (sigc::mem_fun(*this, &Editor::temporal_zoom_session));
2852 zoom_focus_selector.set_name ("ZoomFocusSelector");
2853 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
2854 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2856 _zoom_box.pack_start (zoom_out_button, false, false);
2857 _zoom_box.pack_start (zoom_in_button, false, false);
2858 _zoom_box.pack_start (zoom_out_full_button, false, false);
2860 _zoom_box.pack_start (zoom_focus_selector);
2862 /* Track zoom buttons */
2863 tav_expand_button.set_name ("TrackHeightButton");
2864 tav_expand_button.set_size_request (-1, 20);
2865 tav_expand_button.add (*(manage (new Image (::get_icon ("tav_exp")))));
2866 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("expand-tracks"));
2867 act->connect_proxy (tav_expand_button);
2869 tav_shrink_button.set_name ("TrackHeightButton");
2870 tav_shrink_button.set_size_request (-1, 20);
2871 tav_shrink_button.add (*(manage (new Image (::get_icon ("tav_shrink")))));
2872 act = ActionManager::get_action (X_("Editor"), X_("shrink-tracks"));
2873 act->connect_proxy (tav_shrink_button);
2875 _zoom_box.pack_start (tav_shrink_button);
2876 _zoom_box.pack_start (tav_expand_button);
2878 _zoom_tearoff = manage (new TearOff (_zoom_box));
2880 _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2881 &_zoom_tearoff->tearoff_window()));
2882 _zoom_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2883 &_zoom_tearoff->tearoff_window(), 0));
2884 _zoom_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2885 &_zoom_tearoff->tearoff_window()));
2886 _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2887 &_zoom_tearoff->tearoff_window(), 0));
2889 snap_box.set_spacing (1);
2890 snap_box.set_border_width (2);
2892 snap_type_selector.set_name ("SnapTypeSelector");
2893 set_popdown_strings (snap_type_selector, snap_type_strings, true);
2894 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2896 snap_mode_selector.set_name ("SnapModeSelector");
2897 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
2898 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2900 edit_point_selector.set_name ("EditPointSelector");
2901 set_popdown_strings (edit_point_selector, edit_point_strings, true);
2902 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2904 snap_box.pack_start (snap_mode_selector, false, false);
2905 snap_box.pack_start (snap_type_selector, false, false);
2906 snap_box.pack_start (edit_point_selector, false, false);
2908 /* Nudge */
2910 HBox *nudge_box = manage (new HBox);
2911 nudge_box->set_spacing(1);
2912 nudge_box->set_border_width (2);
2914 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2915 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2917 nudge_box->pack_start (nudge_backward_button, false, false);
2918 nudge_box->pack_start (nudge_forward_button, false, false);
2919 nudge_box->pack_start (*nudge_clock, false, false);
2922 /* Pack everything in... */
2924 HBox* hbox = manage (new HBox);
2925 hbox->set_spacing(10);
2927 _tools_tearoff = manage (new TearOff (*hbox));
2928 _tools_tearoff->set_name ("MouseModeBase");
2929 _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
2931 if (Profile->get_sae()) {
2932 _tools_tearoff->set_can_be_torn_off (false);
2935 _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2936 &_tools_tearoff->tearoff_window()));
2937 _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2938 &_tools_tearoff->tearoff_window(), 0));
2939 _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2940 &_tools_tearoff->tearoff_window()));
2941 _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2942 &_tools_tearoff->tearoff_window(), 0));
2944 toolbar_hbox.set_spacing (10);
2945 toolbar_hbox.set_border_width (1);
2947 toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
2948 toolbar_hbox.pack_start (*_zoom_tearoff, false, false);
2949 toolbar_hbox.pack_start (*_tools_tearoff, false, false);
2951 hbox->pack_start (snap_box, false, false);
2952 if (!Profile->get_small_screen()) {
2953 hbox->pack_start (*nudge_box, false, false);
2954 } else {
2955 ARDOUR_UI::instance()->editor_transport_box().pack_start (*nudge_box, false, false);
2957 hbox->pack_start (panic_box, false, false);
2959 hbox->show_all ();
2961 toolbar_base.set_name ("ToolBarBase");
2962 toolbar_base.add (toolbar_hbox);
2964 _toolbar_viewport.add (toolbar_base);
2965 /* stick to the required height but allow width to vary if there's not enough room */
2966 _toolbar_viewport.set_size_request (1, -1);
2968 toolbar_frame.set_shadow_type (SHADOW_OUT);
2969 toolbar_frame.set_name ("BaseFrame");
2970 toolbar_frame.add (_toolbar_viewport);
2972 DPIReset.connect (sigc::mem_fun (*this, &Editor::resize_text_widgets));
2975 void
2976 Editor::setup_tooltips ()
2978 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
2979 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain"));
2980 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
2981 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
2982 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2983 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
2984 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
2985 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
2986 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
2987 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
2988 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
2989 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
2990 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
2991 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
2992 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
2993 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
2994 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
2995 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
2996 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
2997 ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
2998 ARDOUR_UI::instance()->set_tip (midi_panic_button, _("Send note off and reset controller messages on all MIDI channels"));
2999 ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode"));
3002 void
3003 Editor::midi_panic ()
3005 cerr << "MIDI panic\n";
3007 if (_session) {
3008 _session->midi_panic();
3012 void
3013 Editor::setup_midi_toolbar ()
3015 RefPtr<Action> act;
3017 /* Midi sound notes */
3018 midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
3019 midi_sound_notes.unset_flags (CAN_FOCUS);
3021 /* Panic */
3023 act = ActionManager::get_action (X_("MIDI"), X_("panic"));
3024 midi_panic_button.set_name("MidiPanicButton");
3025 act->connect_proxy (midi_panic_button);
3027 panic_box.pack_start (midi_sound_notes , true, true);
3028 panic_box.pack_start (midi_panic_button, true, true);
3032 Editor::convert_drop_to_paths (
3033 vector<string>& paths,
3034 const RefPtr<Gdk::DragContext>& /*context*/,
3035 gint /*x*/,
3036 gint /*y*/,
3037 const SelectionData& data,
3038 guint /*info*/,
3039 guint /*time*/)
3041 if (_session == 0) {
3042 return -1;
3045 vector<string> uris = data.get_uris();
3047 if (uris.empty()) {
3049 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3050 are actually URI lists. So do it by hand.
3053 if (data.get_target() != "text/plain") {
3054 return -1;
3057 /* Parse the "uri-list" format that Nautilus provides,
3058 where each pathname is delimited by \r\n.
3060 THERE MAY BE NO NULL TERMINATING CHAR!!!
3063 string txt = data.get_text();
3064 const char* p;
3065 const char* q;
3067 p = (const char *) malloc (txt.length() + 1);
3068 txt.copy ((char *) p, txt.length(), 0);
3069 ((char*)p)[txt.length()] = '\0';
3071 while (p)
3073 if (*p != '#')
3075 while (g_ascii_isspace (*p))
3076 p++;
3078 q = p;
3079 while (*q && (*q != '\n') && (*q != '\r')) {
3080 q++;
3083 if (q > p)
3085 q--;
3086 while (q > p && g_ascii_isspace (*q))
3087 q--;
3089 if (q > p)
3091 uris.push_back (string (p, q - p + 1));
3095 p = strchr (p, '\n');
3096 if (p)
3097 p++;
3100 free ((void*)p);
3102 if (uris.empty()) {
3103 return -1;
3107 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3109 if ((*i).substr (0,7) == "file://") {
3111 string p = *i;
3112 PBD::url_decode (p);
3114 // scan forward past three slashes
3116 string::size_type slashcnt = 0;
3117 string::size_type n = 0;
3118 string::iterator x = p.begin();
3120 while (slashcnt < 3 && x != p.end()) {
3121 if ((*x) == '/') {
3122 slashcnt++;
3123 } else if (slashcnt == 3) {
3124 break;
3126 ++n;
3127 ++x;
3130 if (slashcnt != 3 || x == p.end()) {
3131 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3132 continue;
3135 paths.push_back (p.substr (n - 1));
3139 return 0;
3142 void
3143 Editor::new_tempo_section ()
3148 void
3149 Editor::map_transport_state ()
3151 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3153 if (_session && _session->transport_stopped()) {
3154 have_pending_keyboard_selection = false;
3157 update_loop_range_view (true);
3160 /* UNDO/REDO */
3162 Editor::State::State (PublicEditor const * e)
3164 selection = new Selection (e);
3167 Editor::State::~State ()
3169 delete selection;
3172 void
3173 Editor::begin_reversible_command (string name)
3175 if (_session) {
3176 _session->begin_reversible_command (name);
3180 void
3181 Editor::begin_reversible_command (GQuark q)
3183 if (_session) {
3184 _session->begin_reversible_command (q);
3188 void
3189 Editor::commit_reversible_command ()
3191 if (_session) {
3192 _session->commit_reversible_command ();
3196 void
3197 Editor::history_changed ()
3199 string label;
3201 if (undo_action && _session) {
3202 if (_session->undo_depth() == 0) {
3203 label = _("Undo");
3204 } else {
3205 label = string_compose(_("Undo (%1)"), _session->next_undo());
3207 undo_action->property_label() = label;
3210 if (redo_action && _session) {
3211 if (_session->redo_depth() == 0) {
3212 label = _("Redo");
3213 } else {
3214 label = string_compose(_("Redo (%1)"), _session->next_redo());
3216 redo_action->property_label() = label;
3220 void
3221 Editor::duplicate_dialog (bool with_dialog)
3223 float times = 1.0f;
3225 if (mouse_mode == MouseRange) {
3226 if (selection->time.length() == 0) {
3227 return;
3231 RegionSelection rs = get_regions_from_selection_and_entered ();
3233 if (mouse_mode != MouseRange && rs.empty()) {
3234 return;
3237 if (with_dialog) {
3239 ArdourDialog win (_("Duplicate"));
3240 Label label (_("Number of duplications:"));
3241 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3242 SpinButton spinner (adjustment, 0.0, 1);
3243 HBox hbox;
3245 win.get_vbox()->set_spacing (12);
3246 win.get_vbox()->pack_start (hbox);
3247 hbox.set_border_width (6);
3248 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3250 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3251 place, visually. so do this by hand.
3254 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3255 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3256 spinner.grab_focus();
3258 hbox.show ();
3259 label.show ();
3260 spinner.show ();
3262 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3263 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3264 win.set_default_response (RESPONSE_ACCEPT);
3266 win.set_position (WIN_POS_MOUSE);
3268 spinner.grab_focus ();
3270 switch (win.run ()) {
3271 case RESPONSE_ACCEPT:
3272 break;
3273 default:
3274 return;
3277 times = adjustment.get_value();
3280 if (mouse_mode == MouseRange) {
3281 duplicate_selection (times);
3282 } else {
3283 duplicate_some_regions (rs, times);
3287 void
3288 Editor::set_edit_mode (EditMode m)
3290 Config->set_edit_mode (m);
3293 void
3294 Editor::cycle_edit_mode ()
3296 switch (Config->get_edit_mode()) {
3297 case Slide:
3298 if (Profile->get_sae()) {
3299 Config->set_edit_mode (Lock);
3300 } else {
3301 Config->set_edit_mode (Splice);
3303 break;
3304 case Splice:
3305 Config->set_edit_mode (Lock);
3306 break;
3307 case Lock:
3308 Config->set_edit_mode (Slide);
3309 break;
3313 void
3314 Editor::edit_mode_selection_done ()
3316 string s = edit_mode_selector.get_active_text ();
3318 if (!s.empty()) {
3319 Config->set_edit_mode (string_to_edit_mode (s));
3323 void
3324 Editor::snap_type_selection_done ()
3326 string choice = snap_type_selector.get_active_text();
3327 SnapType snaptype = SnapToBeat;
3329 if (choice == _("Beats/2")) {
3330 snaptype = SnapToBeatDiv2;
3331 } else if (choice == _("Beats/3")) {
3332 snaptype = SnapToBeatDiv3;
3333 } else if (choice == _("Beats/4")) {
3334 snaptype = SnapToBeatDiv4;
3335 } else if (choice == _("Beats/5")) {
3336 snaptype = SnapToBeatDiv5;
3337 } else if (choice == _("Beats/6")) {
3338 snaptype = SnapToBeatDiv6;
3339 } else if (choice == _("Beats/7")) {
3340 snaptype = SnapToBeatDiv7;
3341 } else if (choice == _("Beats/8")) {
3342 snaptype = SnapToBeatDiv8;
3343 } else if (choice == _("Beats/10")) {
3344 snaptype = SnapToBeatDiv10;
3345 } else if (choice == _("Beats/12")) {
3346 snaptype = SnapToBeatDiv12;
3347 } else if (choice == _("Beats/14")) {
3348 snaptype = SnapToBeatDiv14;
3349 } else if (choice == _("Beats/16")) {
3350 snaptype = SnapToBeatDiv16;
3351 } else if (choice == _("Beats/20")) {
3352 snaptype = SnapToBeatDiv20;
3353 } else if (choice == _("Beats/24")) {
3354 snaptype = SnapToBeatDiv24;
3355 } else if (choice == _("Beats/28")) {
3356 snaptype = SnapToBeatDiv28;
3357 } else if (choice == _("Beats/32")) {
3358 snaptype = SnapToBeatDiv32;
3359 } else if (choice == _("Beats")) {
3360 snaptype = SnapToBeat;
3361 } else if (choice == _("Bars")) {
3362 snaptype = SnapToBar;
3363 } else if (choice == _("Marks")) {
3364 snaptype = SnapToMark;
3365 } else if (choice == _("Region starts")) {
3366 snaptype = SnapToRegionStart;
3367 } else if (choice == _("Region ends")) {
3368 snaptype = SnapToRegionEnd;
3369 } else if (choice == _("Region bounds")) {
3370 snaptype = SnapToRegionBoundary;
3371 } else if (choice == _("Region syncs")) {
3372 snaptype = SnapToRegionSync;
3373 } else if (choice == _("CD Frames")) {
3374 snaptype = SnapToCDFrame;
3375 } else if (choice == _("Timecode Frames")) {
3376 snaptype = SnapToTimecodeFrame;
3377 } else if (choice == _("Timecode Seconds")) {
3378 snaptype = SnapToTimecodeSeconds;
3379 } else if (choice == _("Timecode Minutes")) {
3380 snaptype = SnapToTimecodeMinutes;
3381 } else if (choice == _("Seconds")) {
3382 snaptype = SnapToSeconds;
3383 } else if (choice == _("Minutes")) {
3384 snaptype = SnapToMinutes;
3387 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3388 if (ract) {
3389 ract->set_active ();
3393 void
3394 Editor::snap_mode_selection_done ()
3396 string choice = snap_mode_selector.get_active_text();
3397 SnapMode mode = SnapNormal;
3399 if (choice == _("No Grid")) {
3400 mode = SnapOff;
3401 } else if (choice == _("Grid")) {
3402 mode = SnapNormal;
3403 } else if (choice == _("Magnetic")) {
3404 mode = SnapMagnetic;
3407 RefPtr<RadioAction> ract = snap_mode_action (mode);
3409 if (ract) {
3410 ract->set_active (true);
3414 void
3415 Editor::cycle_edit_point (bool with_marker)
3417 switch (_edit_point) {
3418 case EditAtMouse:
3419 set_edit_point_preference (EditAtPlayhead);
3420 break;
3421 case EditAtPlayhead:
3422 if (with_marker) {
3423 set_edit_point_preference (EditAtSelectedMarker);
3424 } else {
3425 set_edit_point_preference (EditAtMouse);
3427 break;
3428 case EditAtSelectedMarker:
3429 set_edit_point_preference (EditAtMouse);
3430 break;
3434 void
3435 Editor::edit_point_selection_done ()
3437 string choice = edit_point_selector.get_active_text();
3438 EditPoint ep = EditAtSelectedMarker;
3440 if (choice == _("Marker")) {
3441 set_edit_point_preference (EditAtSelectedMarker);
3442 } else if (choice == _("Playhead")) {
3443 set_edit_point_preference (EditAtPlayhead);
3444 } else {
3445 set_edit_point_preference (EditAtMouse);
3448 RefPtr<RadioAction> ract = edit_point_action (ep);
3450 if (ract) {
3451 ract->set_active (true);
3455 void
3456 Editor::zoom_focus_selection_done ()
3458 string choice = zoom_focus_selector.get_active_text();
3459 ZoomFocus focus_type = ZoomFocusLeft;
3461 if (choice == _("Left")) {
3462 focus_type = ZoomFocusLeft;
3463 } else if (choice == _("Right")) {
3464 focus_type = ZoomFocusRight;
3465 } else if (choice == _("Center")) {
3466 focus_type = ZoomFocusCenter;
3467 } else if (choice == _("Playhead")) {
3468 focus_type = ZoomFocusPlayhead;
3469 } else if (choice == _("Mouse")) {
3470 focus_type = ZoomFocusMouse;
3471 } else if (choice == _("Edit point")) {
3472 focus_type = ZoomFocusEdit;
3475 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3477 if (ract) {
3478 ract->set_active ();
3482 bool
3483 Editor::edit_controls_button_release (GdkEventButton* ev)
3485 if (Keyboard::is_context_menu_event (ev)) {
3486 ARDOUR_UI::instance()->add_route (this);
3487 } else if (ev->button == 1) {
3488 selection->clear_tracks ();
3491 return true;
3494 bool
3495 Editor::mouse_select_button_release (GdkEventButton* ev)
3497 /* this handles just right-clicks */
3499 if (ev->button != 3) {
3500 return false;
3503 return true;
3506 void
3507 Editor::set_zoom_focus (ZoomFocus f)
3509 string str = zoom_focus_strings[(int)f];
3511 if (str != zoom_focus_selector.get_active_text()) {
3512 zoom_focus_selector.set_active_text (str);
3515 if (zoom_focus != f) {
3516 zoom_focus = f;
3517 instant_save ();
3521 void
3522 Editor::ensure_float (Window& win)
3524 win.set_transient_for (*this);
3527 void
3528 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3530 /* recover or initialize pane positions. do this here rather than earlier because
3531 we don't want the positions to change the child allocations, which they seem to do.
3534 int pos;
3535 XMLProperty* prop;
3536 char buf[32];
3537 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3539 enum Pane {
3540 Horizontal = 0x1,
3541 Vertical = 0x2
3544 static Pane done;
3546 XMLNode* geometry = find_named_node (*node, "geometry");
3548 if (which == static_cast<Paned*> (&edit_pane)) {
3550 if (done & Horizontal) {
3551 return;
3554 if (geometry && (prop = geometry->property ("notebook-shrunk"))) {
3555 _notebook_shrunk = string_is_affirmative (prop->value ());
3558 if (geometry && (prop = geometry->property ("pre-maximal-horizontal-pane-position"))) {
3559 pre_maximal_horizontal_pane_position = atoi (prop->value ());
3562 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3563 /* initial allocation is 90% to canvas, 10% to notebook */
3564 pos = (int) floor (alloc.get_width() * 0.90f);
3565 snprintf (buf, sizeof(buf), "%d", pos);
3566 } else {
3567 pos = atoi (prop->value());
3570 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3571 edit_pane.set_position (pos);
3572 if (pre_maximal_horizontal_pane_position == 0) {
3573 pre_maximal_horizontal_pane_position = pos;
3577 done = (Pane) (done | Horizontal);
3579 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3581 if (done & Vertical) {
3582 return;
3585 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3586 /* initial allocation is 90% to canvas, 10% to summary */
3587 pos = (int) floor (alloc.get_height() * 0.90f);
3588 snprintf (buf, sizeof(buf), "%d", pos);
3589 } else {
3590 pos = atoi (prop->value());
3593 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3594 editor_summary_pane.set_position (pos);
3595 pre_maximal_vertical_pane_position = pos;
3598 done = (Pane) (done | Vertical);
3602 void
3603 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3605 if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) {
3606 top_hbox.remove (toolbar_frame);
3610 void
3611 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3613 if (toolbar_frame.get_parent() == 0) {
3614 top_hbox.pack_end (toolbar_frame);
3618 void
3619 Editor::set_show_measures (bool yn)
3621 if (_show_measures != yn) {
3622 hide_measures ();
3624 if ((_show_measures = yn) == true) {
3625 if (tempo_lines)
3626 tempo_lines->show();
3627 draw_measures ();
3629 instant_save ();
3633 void
3634 Editor::toggle_follow_playhead ()
3636 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3637 if (act) {
3638 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3639 set_follow_playhead (tact->get_active());
3643 /** @param yn true to follow playhead, otherwise false.
3644 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
3646 void
3647 Editor::set_follow_playhead (bool yn, bool catch_up)
3649 if (_follow_playhead != yn) {
3650 if ((_follow_playhead = yn) == true && catch_up) {
3651 /* catch up */
3652 reset_x_origin_to_follow_playhead ();
3654 instant_save ();
3658 void
3659 Editor::toggle_stationary_playhead ()
3661 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3662 if (act) {
3663 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3664 set_stationary_playhead (tact->get_active());
3668 void
3669 Editor::set_stationary_playhead (bool yn)
3671 if (_stationary_playhead != yn) {
3672 if ((_stationary_playhead = yn) == true) {
3673 /* catch up */
3674 // FIXME need a 3.0 equivalent of this 2.X call
3675 // update_current_screen ();
3677 instant_save ();
3681 void
3682 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3684 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3685 if (xfade) {
3686 xfade->set_active (!xfade->active());
3690 void
3691 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3693 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3694 if (xfade) {
3695 xfade->set_follow_overlap (!xfade->following_overlap());
3699 void
3700 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3702 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3704 if (!xfade) {
3705 return;
3708 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3710 ensure_float (cew);
3712 switch (cew.run ()) {
3713 case RESPONSE_ACCEPT:
3714 break;
3715 default:
3716 return;
3719 cew.apply ();
3720 PropertyChange all_crossfade_properties;
3721 all_crossfade_properties.add (ARDOUR::Properties::active);
3722 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3723 xfade->PropertyChanged (all_crossfade_properties);
3726 PlaylistSelector&
3727 Editor::playlist_selector () const
3729 return *_playlist_selector;
3732 Evoral::MusicalTime
3733 Editor::get_grid_type_as_beats (bool& success, framepos_t position)
3735 success = true;
3737 switch (_snap_type) {
3738 case SnapToBeat:
3739 return 1.0;
3740 break;
3742 case SnapToBeatDiv32:
3743 return 1.0/32.0;
3744 break;
3745 case SnapToBeatDiv28:
3746 return 1.0/28.0;
3747 break;
3748 case SnapToBeatDiv24:
3749 return 1.0/24.0;
3750 break;
3751 case SnapToBeatDiv20:
3752 return 1.0/20.0;
3753 break;
3754 case SnapToBeatDiv16:
3755 return 1.0/16.0;
3756 break;
3757 case SnapToBeatDiv14:
3758 return 1.0/14.0;
3759 break;
3760 case SnapToBeatDiv12:
3761 return 1.0/12.0;
3762 break;
3763 case SnapToBeatDiv10:
3764 return 1.0/10.0;
3765 break;
3766 case SnapToBeatDiv8:
3767 return 1.0/8.0;
3768 break;
3769 case SnapToBeatDiv7:
3770 return 1.0/7.0;
3771 break;
3772 case SnapToBeatDiv6:
3773 return 1.0/6.0;
3774 break;
3775 case SnapToBeatDiv5:
3776 return 1.0/5.0;
3777 break;
3778 case SnapToBeatDiv4:
3779 return 1.0/4.0;
3780 break;
3781 case SnapToBeatDiv3:
3782 return 1.0/3.0;
3783 break;
3784 case SnapToBeatDiv2:
3785 return 1.0/2.0;
3786 break;
3788 case SnapToBar:
3789 if (_session) {
3790 return _session->tempo_map().meter_at (position).beats_per_bar();
3792 break;
3794 case SnapToCDFrame:
3795 case SnapToTimecodeFrame:
3796 case SnapToTimecodeSeconds:
3797 case SnapToTimecodeMinutes:
3798 case SnapToSeconds:
3799 case SnapToMinutes:
3800 case SnapToRegionStart:
3801 case SnapToRegionEnd:
3802 case SnapToRegionSync:
3803 case SnapToRegionBoundary:
3804 default:
3805 success = false;
3806 break;
3809 return 0.0;
3812 framecnt_t
3813 Editor::get_nudge_distance (framepos_t pos, framecnt_t& next)
3815 framecnt_t ret;
3817 ret = nudge_clock->current_duration (pos);
3818 next = ret + 1; /* XXXX fix me */
3820 return ret;
3824 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3826 ArdourDialog dialog (_("Playlist Deletion"));
3827 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3828 "If it is kept, its audio files will not be cleaned.\n"
3829 "If it is deleted, audio files used by it alone will be cleaned."),
3830 pl->name()));
3832 dialog.set_position (WIN_POS_CENTER);
3833 dialog.get_vbox()->pack_start (label);
3835 label.show ();
3837 dialog.add_button (_("Delete Playlist"), RESPONSE_ACCEPT);
3838 dialog.add_button (_("Keep Playlist"), RESPONSE_REJECT);
3839 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3841 switch (dialog.run ()) {
3842 case RESPONSE_ACCEPT:
3843 /* delete the playlist */
3844 return 0;
3845 break;
3847 case RESPONSE_REJECT:
3848 /* keep the playlist */
3849 return 1;
3850 break;
3852 default:
3853 break;
3856 return -1;
3859 bool
3860 Editor::audio_region_selection_covers (framepos_t where)
3862 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3863 if ((*a)->region()->covers (where)) {
3864 return true;
3868 return false;
3871 void
3872 Editor::prepare_for_cleanup ()
3874 cut_buffer->clear_regions ();
3875 cut_buffer->clear_playlists ();
3877 selection->clear_regions ();
3878 selection->clear_playlists ();
3880 _regions->suspend_redisplay ();
3883 void
3884 Editor::finish_cleanup ()
3886 _regions->resume_redisplay ();
3889 Location*
3890 Editor::transport_loop_location()
3892 if (_session) {
3893 return _session->locations()->auto_loop_location();
3894 } else {
3895 return 0;
3899 Location*
3900 Editor::transport_punch_location()
3902 if (_session) {
3903 return _session->locations()->auto_punch_location();
3904 } else {
3905 return 0;
3909 bool
3910 Editor::control_layout_scroll (GdkEventScroll* ev)
3912 if (Keyboard::some_magic_widget_has_focus()) {
3913 return false;
3916 switch (ev->direction) {
3917 case GDK_SCROLL_UP:
3918 scroll_tracks_up_line ();
3919 return true;
3920 break;
3922 case GDK_SCROLL_DOWN:
3923 scroll_tracks_down_line ();
3924 return true;
3926 default:
3927 /* no left/right handling yet */
3928 break;
3931 return false;
3934 void
3935 Editor::session_state_saved (string)
3937 update_title ();
3938 _snapshots->redisplay ();
3941 void
3942 Editor::maximise_editing_space ()
3944 _mouse_mode_tearoff->set_visible (false);
3945 _tools_tearoff->set_visible (false);
3946 _zoom_tearoff->set_visible (false);
3948 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
3949 pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
3950 pre_maximal_editor_width = this->get_width ();
3951 pre_maximal_editor_height = this->get_height ();
3953 if (post_maximal_horizontal_pane_position == 0) {
3954 post_maximal_horizontal_pane_position = edit_pane.get_width();
3957 if (post_maximal_vertical_pane_position == 0) {
3958 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
3961 fullscreen ();
3963 if (post_maximal_editor_width) {
3964 edit_pane.set_position (post_maximal_horizontal_pane_position -
3965 abs(post_maximal_editor_width - pre_maximal_editor_width));
3966 } else {
3967 edit_pane.set_position (post_maximal_horizontal_pane_position);
3970 /* Hack: we must do this in an idle handler for it to work; see comment in
3971 restore_editing_space()
3974 Glib::signal_idle().connect (
3975 sigc::bind (
3976 sigc::mem_fun (*this, &Editor::idle_reset_vertical_pane_position),
3977 post_maximal_vertical_pane_position
3981 if (Config->get_keep_tearoffs()) {
3982 _mouse_mode_tearoff->set_visible (true);
3983 _tools_tearoff->set_visible (true);
3984 if (Config->get_show_zoom_tools ()) {
3985 _zoom_tearoff->set_visible (true);
3991 bool
3992 Editor::idle_reset_vertical_pane_position (int p)
3994 editor_summary_pane.set_position (p);
3995 return false;
3998 void
3999 Editor::restore_editing_space ()
4001 // user changed width/height of panes during fullscreen
4003 if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
4004 post_maximal_horizontal_pane_position = edit_pane.get_position();
4007 if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
4008 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
4011 unfullscreen();
4013 _mouse_mode_tearoff->set_visible (true);
4014 _tools_tearoff->set_visible (true);
4015 if (Config->get_show_zoom_tools ()) {
4016 _zoom_tearoff->set_visible (true);
4018 post_maximal_editor_width = this->get_width();
4019 post_maximal_editor_height = this->get_height();
4021 edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
4023 /* This is a bit of a hack, but it seems that if you set the vertical pane position
4024 here it gets reset to some wrong value after this method has finished. Doing
4025 the setup in an idle callback seems to work.
4027 Glib::signal_idle().connect (
4028 sigc::bind (
4029 sigc::mem_fun (*this, &Editor::idle_reset_vertical_pane_position),
4030 pre_maximal_vertical_pane_position
4036 * Make new playlists for a given track and also any others that belong
4037 * to the same active route group with the `edit' property.
4038 * @param v Track.
4041 void
4042 Editor::new_playlists (TimeAxisView* v)
4044 begin_reversible_command (_("new playlists"));
4045 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4046 _session->playlists->get (playlists);
4047 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4048 commit_reversible_command ();
4052 * Use a copy of the current playlist for a given track and also any others that belong
4053 * to the same active route group with the `edit' property.
4054 * @param v Track.
4057 void
4058 Editor::copy_playlists (TimeAxisView* v)
4060 begin_reversible_command (_("copy playlists"));
4061 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4062 _session->playlists->get (playlists);
4063 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4064 commit_reversible_command ();
4067 /** Clear the current playlist for a given track and also any others that belong
4068 * to the same active route group with the `edit' property.
4069 * @param v Track.
4072 void
4073 Editor::clear_playlists (TimeAxisView* v)
4075 begin_reversible_command (_("clear playlists"));
4076 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4077 _session->playlists->get (playlists);
4078 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4079 commit_reversible_command ();
4082 void
4083 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4085 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4088 void
4089 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4091 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4094 void
4095 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4097 atv.clear_playlist ();
4100 bool
4101 Editor::on_key_press_event (GdkEventKey* ev)
4103 return key_press_focus_accelerator_handler (*this, ev);
4106 bool
4107 Editor::on_key_release_event (GdkEventKey* ev)
4109 return Gtk::Window::on_key_release_event (ev);
4110 // return key_press_focus_accelerator_handler (*this, ev);
4113 /** Queue up a change to the viewport x origin.
4114 * @param frame New x origin.
4116 void
4117 Editor::reset_x_origin (framepos_t frame)
4119 queue_visual_change (frame);
4122 void
4123 Editor::reset_y_origin (double y)
4125 queue_visual_change_y (y);
4128 void
4129 Editor::reset_zoom (double fpu)
4131 queue_visual_change (fpu);
4134 void
4135 Editor::reposition_and_zoom (framepos_t frame, double fpu)
4137 reset_x_origin (frame);
4138 reset_zoom (fpu);
4140 if (!no_save_visual) {
4141 undo_visual_stack.push_back (current_visual_state(false));
4145 Editor::VisualState::VisualState ()
4146 : gui_state (new GUIObjectState)
4150 Editor::VisualState::~VisualState ()
4152 delete gui_state;
4155 Editor::VisualState*
4156 Editor::current_visual_state (bool with_tracks)
4158 VisualState* vs = new VisualState;
4159 vs->y_position = vertical_adjustment.get_value();
4160 vs->frames_per_unit = frames_per_unit;
4161 vs->leftmost_frame = leftmost_frame;
4162 vs->zoom_focus = zoom_focus;
4164 if (with_tracks) {
4165 *(vs->gui_state) = *ARDOUR_UI::instance()->gui_object_state;
4168 return vs;
4171 void
4172 Editor::undo_visual_state ()
4174 if (undo_visual_stack.empty()) {
4175 return;
4178 redo_visual_stack.push_back (current_visual_state());
4180 VisualState* vs = undo_visual_stack.back();
4181 undo_visual_stack.pop_back();
4182 use_visual_state (*vs);
4185 void
4186 Editor::redo_visual_state ()
4188 if (redo_visual_stack.empty()) {
4189 return;
4192 undo_visual_stack.push_back (current_visual_state());
4194 VisualState* vs = redo_visual_stack.back();
4195 redo_visual_stack.pop_back();
4196 use_visual_state (*vs);
4199 void
4200 Editor::swap_visual_state ()
4202 if (undo_visual_stack.empty()) {
4203 redo_visual_state ();
4204 } else {
4205 undo_visual_state ();
4209 void
4210 Editor::use_visual_state (VisualState& vs)
4212 no_save_visual = true;
4214 _routes->suspend_redisplay ();
4216 vertical_adjustment.set_value (vs.y_position);
4218 set_zoom_focus (vs.zoom_focus);
4219 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4221 *ARDOUR_UI::instance()->gui_object_state = *vs.gui_state;
4223 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4224 (*i)->reset_visual_state ();
4227 _routes->update_visibility ();
4228 _routes->resume_redisplay ();
4230 no_save_visual = false;
4233 void
4234 Editor::set_frames_per_unit (double fpu)
4236 /* this is the core function that controls the zoom level of the canvas. it is called
4237 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4240 if (fpu == frames_per_unit) {
4241 return;
4244 if (fpu < 2.0) {
4245 fpu = 2.0;
4249 /* don't allow zooms that fit more than the maximum number
4250 of frames into an 800 pixel wide space.
4253 if (max_framepos / fpu < 800.0) {
4254 return;
4257 if (tempo_lines)
4258 tempo_lines->tempo_map_changed();
4260 frames_per_unit = fpu;
4261 post_zoom ();
4264 void
4265 Editor::post_zoom ()
4267 // convert fpu to frame count
4269 framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width);
4271 if (frames_per_unit != zoom_range_clock->current_duration()) {
4272 zoom_range_clock->set (frames);
4275 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4276 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4277 (*i)->reshow_selection (selection->time);
4281 ZoomChanged (); /* EMIT_SIGNAL */
4283 //reset_scrolling_region ();
4285 if (playhead_cursor) {
4286 playhead_cursor->set_position (playhead_cursor->current_frame);
4289 refresh_location_display();
4290 _summary->set_overlays_dirty ();
4292 update_marker_labels ();
4294 instant_save ();
4297 void
4298 Editor::queue_visual_change (framepos_t where)
4300 pending_visual_change.add (VisualChange::TimeOrigin);
4301 pending_visual_change.time_origin = where;
4302 ensure_visual_change_idle_handler ();
4305 void
4306 Editor::queue_visual_change (double fpu)
4308 pending_visual_change.add (VisualChange::ZoomLevel);
4309 pending_visual_change.frames_per_unit = fpu;
4311 ensure_visual_change_idle_handler ();
4314 void
4315 Editor::queue_visual_change_y (double y)
4317 pending_visual_change.add (VisualChange::YOrigin);
4318 pending_visual_change.y_origin = y;
4320 ensure_visual_change_idle_handler ();
4323 void
4324 Editor::ensure_visual_change_idle_handler ()
4326 if (pending_visual_change.idle_handler_id < 0) {
4327 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4332 Editor::_idle_visual_changer (void* arg)
4334 return static_cast<Editor*>(arg)->idle_visual_changer ();
4338 Editor::idle_visual_changer ()
4340 VisualChange::Type p = pending_visual_change.pending;
4341 pending_visual_change.pending = (VisualChange::Type) 0;
4343 double const last_time_origin = horizontal_position ();
4345 if (p & VisualChange::TimeOrigin) {
4346 /* This is a bit of a hack, but set_frames_per_unit
4347 below will (if called) end up with the
4348 CrossfadeViews looking at Editor::leftmost_frame,
4349 and if we're changing origin and zoom in the same
4350 operation it will be the wrong value unless we
4351 update it here.
4354 leftmost_frame = pending_visual_change.time_origin;
4357 if (p & VisualChange::ZoomLevel) {
4358 set_frames_per_unit (pending_visual_change.frames_per_unit);
4360 compute_fixed_ruler_scale ();
4361 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4362 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4363 update_tempo_based_rulers ();
4365 if (p & VisualChange::TimeOrigin) {
4366 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4368 if (p & VisualChange::YOrigin) {
4369 vertical_adjustment.set_value (pending_visual_change.y_origin);
4372 if (last_time_origin == horizontal_position ()) {
4373 /* changed signal not emitted */
4374 update_fixed_rulers ();
4375 redisplay_tempo (true);
4378 _summary->set_overlays_dirty ();
4380 pending_visual_change.idle_handler_id = -1;
4381 return 0; /* this is always a one-shot call */
4384 struct EditorOrderTimeAxisSorter {
4385 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4386 return a->order () < b->order ();
4390 void
4391 Editor::sort_track_selection (TrackViewList* sel)
4393 EditorOrderTimeAxisSorter cmp;
4395 if (sel) {
4396 sel->sort (cmp);
4397 } else {
4398 selection->tracks.sort (cmp);
4402 framepos_t
4403 Editor::get_preferred_edit_position (bool ignore_playhead)
4405 bool ignored;
4406 framepos_t where = 0;
4407 EditPoint ep = _edit_point;
4409 if (entered_marker) {
4410 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position()));
4411 return entered_marker->position();
4414 if (ignore_playhead && ep == EditAtPlayhead) {
4415 ep = EditAtSelectedMarker;
4418 switch (ep) {
4419 case EditAtPlayhead:
4420 where = _session->audible_frame();
4421 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where));
4422 break;
4424 case EditAtSelectedMarker:
4425 if (!selection->markers.empty()) {
4426 bool is_start;
4427 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4428 if (loc) {
4429 if (is_start) {
4430 where = loc->start();
4431 } else {
4432 where = loc->end();
4434 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where));
4435 break;
4438 /* fallthru */
4440 default:
4441 case EditAtMouse:
4442 if (!mouse_frame (where, ignored)) {
4443 /* XXX not right but what can we do ? */
4444 return 0;
4446 snap_to (where);
4447 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where));
4448 break;
4451 return where;
4454 void
4455 Editor::set_loop_range (framepos_t start, framepos_t end, string cmd)
4457 if (!_session) return;
4459 begin_reversible_command (cmd);
4461 Location* tll;
4463 if ((tll = transport_loop_location()) == 0) {
4464 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop);
4465 XMLNode &before = _session->locations()->get_state();
4466 _session->locations()->add (loc, true);
4467 _session->set_auto_loop_location (loc);
4468 XMLNode &after = _session->locations()->get_state();
4469 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4470 } else {
4471 XMLNode &before = tll->get_state();
4472 tll->set_hidden (false, this);
4473 tll->set (start, end);
4474 XMLNode &after = tll->get_state();
4475 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4478 commit_reversible_command ();
4481 void
4482 Editor::set_punch_range (framepos_t start, framepos_t end, string cmd)
4484 if (!_session) return;
4486 begin_reversible_command (cmd);
4488 Location* tpl;
4490 if ((tpl = transport_punch_location()) == 0) {
4491 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoPunch);
4492 XMLNode &before = _session->locations()->get_state();
4493 _session->locations()->add (loc, true);
4494 _session->set_auto_loop_location (loc);
4495 XMLNode &after = _session->locations()->get_state();
4496 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4498 else {
4499 XMLNode &before = tpl->get_state();
4500 tpl->set_hidden (false, this);
4501 tpl->set (start, end);
4502 XMLNode &after = tpl->get_state();
4503 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4506 commit_reversible_command ();
4509 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4510 * @param rs List to which found regions are added.
4511 * @param where Time to look at.
4512 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4514 void
4515 Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4517 const TrackViewList* tracks;
4519 if (ts.empty()) {
4520 tracks = &track_views;
4521 } else {
4522 tracks = &ts;
4525 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4527 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4529 if (rtv) {
4530 boost::shared_ptr<Track> tr;
4531 boost::shared_ptr<Playlist> pl;
4533 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4535 Playlist::RegionList* regions = pl->regions_at (
4536 (framepos_t) floor ( (double) where * tr->speed()));
4538 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4539 RegionView* rv = rtv->view()->find_view (*i);
4540 if (rv) {
4541 rs.add (rv);
4545 delete regions;
4551 void
4552 Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4554 const TrackViewList* tracks;
4556 if (ts.empty()) {
4557 tracks = &track_views;
4558 } else {
4559 tracks = &ts;
4562 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4563 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4564 if (rtv) {
4565 boost::shared_ptr<Track> tr;
4566 boost::shared_ptr<Playlist> pl;
4568 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4570 Playlist::RegionList* regions = pl->regions_touched (
4571 (framepos_t) floor ( (double)where * tr->speed()), max_framepos);
4573 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4575 RegionView* rv = rtv->view()->find_view (*i);
4577 if (rv) {
4578 rs.push_back (rv);
4582 delete regions;
4588 /** Start with regions that are selected. Then add equivalent regions
4589 * on tracks in the same active edit-enabled route group as any of
4590 * the regions that we started with.
4593 RegionSelection
4594 Editor::get_regions_from_selection ()
4596 return get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
4599 /** Get regions using the following method:
4601 * Make an initial region list using the selected regions, unless
4602 * the edit point is `mouse' and the mouse is over an unselected
4603 * region. In this case, start with just that region.
4605 * Then, make an initial track list of the tracks that these
4606 * regions are on, and if the edit point is not `mouse', add the
4607 * selected tracks.
4609 * Look at this track list and add any other tracks that are on the
4610 * same active edit-enabled route group as one of the initial tracks.
4612 * Finally take the initial region list and add any regions that are
4613 * under the edit point on one of the tracks on the track list to get
4614 * the returned region list.
4616 * The rationale here is that the mouse edit point is special in that
4617 * its position describes both a time and a track; the other edit
4618 * modes only describe a time. Hence if the edit point is `mouse' we
4619 * ignore selected tracks, as we assume the user means something by
4620 * pointing at a particular track. Also in this case we take note of
4621 * the region directly under the edit point, as there is always just one
4622 * (rather than possibly several with non-mouse edit points).
4625 RegionSelection
4626 Editor::get_regions_from_selection_and_edit_point ()
4628 RegionSelection regions;
4630 if (_edit_point == EditAtMouse && entered_regionview && !selection->regions.contains (entered_regionview)) {
4631 regions.add (entered_regionview);
4632 } else {
4633 regions = selection->regions;
4636 TrackViewList tracks;
4638 if (_edit_point != EditAtMouse) {
4639 tracks = selection->tracks;
4642 /* Add any other tracks that have regions that are in the same
4643 edit-activated route group as one of our regions.
4645 for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) {
4647 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4649 if (g && g->is_active() && g->is_edit()) {
4650 tracks.add (axis_views_from_routes (g->route_list()));
4654 if (!tracks.empty()) {
4655 /* now find regions that are at the edit position on those tracks */
4656 framepos_t const where = get_preferred_edit_position ();
4657 get_regions_at (regions, where, tracks);
4660 return regions;
4663 /** Start with regions that are selected, or the entered regionview if none are selected.
4664 * Then add equivalent regions on tracks in the same active edit-enabled route group as any
4665 * of the regions that we started with.
4668 RegionSelection
4669 Editor::get_regions_from_selection_and_entered ()
4671 RegionSelection regions = selection->regions;
4673 if (regions.empty() && entered_regionview) {
4674 regions.add (entered_regionview);
4677 return get_equivalent_regions (regions, ARDOUR::Properties::edit.property_id);
4680 void
4681 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4683 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4685 RouteTimeAxisView* tatv;
4687 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4689 boost::shared_ptr<Playlist> pl;
4690 vector<boost::shared_ptr<Region> > results;
4691 RegionView* marv;
4692 boost::shared_ptr<Track> tr;
4694 if ((tr = tatv->track()) == 0) {
4695 /* bus */
4696 continue;
4699 if ((pl = (tr->playlist())) != 0) {
4700 pl->get_region_list_equivalent_regions (region, results);
4703 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4704 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4705 regions.push_back (marv);
4713 void
4714 Editor::show_rhythm_ferret ()
4716 if (rhythm_ferret == 0) {
4717 rhythm_ferret = new RhythmFerret(*this);
4720 rhythm_ferret->set_session (_session);
4721 rhythm_ferret->show ();
4722 rhythm_ferret->present ();
4725 void
4726 Editor::first_idle ()
4728 MessageDialog* dialog = 0;
4730 if (track_views.size() > 1) {
4731 dialog = new MessageDialog (*this,
4732 string_compose (_("Please wait while %1 loads visual data"), PROGRAM_NAME),
4733 true,
4734 Gtk::MESSAGE_INFO,
4735 Gtk::BUTTONS_NONE);
4736 dialog->present ();
4737 ARDOUR_UI::instance()->flush_pending ();
4740 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4741 (*t)->first_idle();
4744 // first idle adds route children (automation tracks), so we need to redisplay here
4745 _routes->redisplay ();
4747 delete dialog;
4749 _have_idled = true;
4752 gboolean
4753 Editor::_idle_resize (gpointer arg)
4755 return ((Editor*)arg)->idle_resize ();
4758 void
4759 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4761 if (resize_idle_id < 0) {
4762 resize_idle_id = g_idle_add (_idle_resize, this);
4763 _pending_resize_amount = 0;
4766 /* make a note of the smallest resulting height, so that we can clamp the
4767 lower limit at TimeAxisView::hSmall */
4769 int32_t min_resulting = INT32_MAX;
4771 _pending_resize_amount += h;
4772 _pending_resize_view = view;
4774 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4776 if (selection->tracks.contains (_pending_resize_view)) {
4777 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4778 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4782 if (min_resulting < 0) {
4783 min_resulting = 0;
4786 /* clamp */
4787 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4788 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4792 /** Handle pending resizing of tracks */
4793 bool
4794 Editor::idle_resize ()
4796 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4798 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4799 selection->tracks.contains (_pending_resize_view)) {
4801 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4802 if (*i != _pending_resize_view) {
4803 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4808 _pending_resize_amount = 0;
4809 flush_canvas ();
4810 _group_tabs->set_dirty ();
4811 resize_idle_id = -1;
4813 return false;
4816 void
4817 Editor::located ()
4819 ENSURE_GUI_THREAD (*this, &Editor::located);
4821 playhead_cursor->set_position (_session->audible_frame ());
4822 if (_follow_playhead && !_pending_initial_locate) {
4823 reset_x_origin_to_follow_playhead ();
4826 _pending_locate_request = false;
4827 _pending_initial_locate = false;
4830 void
4831 Editor::region_view_added (RegionView *)
4833 _summary->set_dirty ();
4836 void
4837 Editor::region_view_removed ()
4839 _summary->set_dirty ();
4842 TimeAxisView*
4843 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4845 TrackViewList::const_iterator j = track_views.begin ();
4846 while (j != track_views.end()) {
4847 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4848 if (rtv && rtv->route() == r) {
4849 return rtv;
4851 ++j;
4854 return 0;
4858 TrackViewList
4859 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4861 TrackViewList t;
4863 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4864 TimeAxisView* tv = axis_view_from_route (*i);
4865 if (tv) {
4866 t.push_back (tv);
4870 return t;
4874 void
4875 Editor::handle_new_route (RouteList& routes)
4877 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4879 RouteTimeAxisView *rtv;
4880 list<RouteTimeAxisView*> new_views;
4882 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4883 boost::shared_ptr<Route> route = (*x);
4885 if (route->is_hidden() || route->is_monitor()) {
4886 continue;
4889 DataType dt = route->input()->default_type();
4891 if (dt == ARDOUR::DataType::AUDIO) {
4892 rtv = new AudioTimeAxisView (*this, _session, *track_canvas);
4893 rtv->set_route (route);
4894 } else if (dt == ARDOUR::DataType::MIDI) {
4895 rtv = new MidiTimeAxisView (*this, _session, *track_canvas);
4896 rtv->set_route (route);
4897 } else {
4898 throw unknown_type();
4901 new_views.push_back (rtv);
4902 track_views.push_back (rtv);
4904 rtv->effective_gain_display ();
4906 if (internal_editing()) {
4907 rtv->enter_internal_edit_mode ();
4908 } else {
4909 rtv->leave_internal_edit_mode ();
4912 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4913 rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed));
4916 _routes->routes_added (new_views);
4917 _summary->routes_added (new_views);
4919 if (show_editor_mixer_when_tracks_arrive) {
4920 show_editor_mixer (true);
4923 editor_list_button.set_sensitive (true);
4926 void
4927 Editor::timeaxisview_deleted (TimeAxisView *tv)
4929 if (_session && _session->deletion_in_progress()) {
4930 /* the situation is under control */
4931 return;
4934 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
4936 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
4938 _routes->route_removed (tv);
4940 if (tv == entered_track) {
4941 entered_track = 0;
4944 TimeAxisView::Children c = tv->get_child_list ();
4945 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
4946 if (entered_track == i->get()) {
4947 entered_track = 0;
4951 /* remove it from the list of track views */
4953 TrackViewList::iterator i;
4955 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
4956 i = track_views.erase (i);
4959 /* update whatever the current mixer strip is displaying, if revelant */
4961 boost::shared_ptr<Route> route;
4963 if (rtav) {
4964 route = rtav->route ();
4967 if (current_mixer_strip && current_mixer_strip->route() == route) {
4969 TimeAxisView* next_tv;
4971 if (track_views.empty()) {
4972 next_tv = 0;
4973 } else if (i == track_views.end()) {
4974 next_tv = track_views.front();
4975 } else {
4976 next_tv = (*i);
4980 if (next_tv) {
4981 set_selected_mixer_strip (*next_tv);
4982 } else {
4983 /* make the editor mixer strip go away setting the
4984 * button to inactive (which also unticks the menu option)
4987 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
4992 void
4993 Editor::hide_track_in_display (TimeAxisView* tv, bool apply_to_selection)
4995 if (apply_to_selection) {
4996 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ) {
4998 TrackSelection::iterator j = i;
4999 ++j;
5001 hide_track_in_display (*i, false);
5003 i = j;
5005 } else {
5006 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
5008 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
5009 // this will hide the mixer strip
5010 set_selected_mixer_strip (*tv);
5013 _routes->hide_track_in_display (*tv);
5017 bool
5018 Editor::sync_track_view_list_and_routes ()
5020 track_views = TrackViewList (_routes->views ());
5022 _summary->set_dirty ();
5023 _group_tabs->set_dirty ();
5025 return false; // do not call again (until needed)
5028 void
5029 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
5031 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5032 theslot (**i);
5036 /** Find a RouteTimeAxisView by the ID of its route */
5037 RouteTimeAxisView*
5038 Editor::get_route_view_by_route_id (PBD::ID& id) const
5040 RouteTimeAxisView* v;
5042 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5043 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5044 if(v->route()->id() == id) {
5045 return v;
5050 return 0;
5053 void
5054 Editor::fit_route_group (RouteGroup *g)
5056 TrackViewList ts = axis_views_from_routes (g->route_list ());
5057 fit_tracks (ts);
5060 void
5061 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5063 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5065 if (r == 0) {
5066 _session->cancel_audition ();
5067 return;
5070 if (_session->is_auditioning()) {
5071 _session->cancel_audition ();
5072 if (r == last_audition_region) {
5073 return;
5077 _session->audition_region (r);
5078 last_audition_region = r;
5082 void
5083 Editor::hide_a_region (boost::shared_ptr<Region> r)
5085 r->set_hidden (true);
5088 void
5089 Editor::show_a_region (boost::shared_ptr<Region> r)
5091 r->set_hidden (false);
5094 void
5095 Editor::audition_region_from_region_list ()
5097 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5100 void
5101 Editor::hide_region_from_region_list ()
5103 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5106 void
5107 Editor::show_region_in_region_list ()
5109 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5112 void
5113 Editor::step_edit_status_change (bool yn)
5115 if (yn) {
5116 start_step_editing ();
5117 } else {
5118 stop_step_editing ();
5122 void
5123 Editor::start_step_editing ()
5125 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5128 void
5129 Editor::stop_step_editing ()
5131 step_edit_connection.disconnect ();
5134 bool
5135 Editor::check_step_edit ()
5137 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5138 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5139 if (mtv) {
5140 mtv->check_step_edit ();
5144 return true; // do it again, till we stop
5147 bool
5148 Editor::scroll_press (Direction dir)
5150 ++_scroll_callbacks;
5152 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5153 /* delay the first auto-repeat */
5154 return true;
5157 switch (dir) {
5158 case LEFT:
5159 scroll_backward (1);
5160 break;
5162 case RIGHT:
5163 scroll_forward (1);
5164 break;
5166 case UP:
5167 scroll_tracks_up_line ();
5168 break;
5170 case DOWN:
5171 scroll_tracks_down_line ();
5172 break;
5175 /* do hacky auto-repeat */
5176 if (!_scroll_connection.connected ()) {
5178 _scroll_connection = Glib::signal_timeout().connect (
5179 sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), dir), 100
5182 _scroll_callbacks = 0;
5185 return true;
5188 void
5189 Editor::scroll_release ()
5191 _scroll_connection.disconnect ();
5194 /** Queue a change for the Editor viewport x origin to follow the playhead */
5195 void
5196 Editor::reset_x_origin_to_follow_playhead ()
5198 framepos_t const frame = playhead_cursor->current_frame;
5200 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5202 if (_session->transport_speed() < 0) {
5204 if (frame > (current_page_frames() / 2)) {
5205 center_screen (frame-(current_page_frames()/2));
5206 } else {
5207 center_screen (current_page_frames()/2);
5210 } else {
5212 if (frame < leftmost_frame) {
5213 /* moving left */
5214 framepos_t l = 0;
5215 if (_session->transport_rolling()) {
5216 /* rolling; end up with the playhead at the right of the page */
5217 l = frame - current_page_frames ();
5218 } else {
5219 /* not rolling: end up with the playhead 3/4 of the way along the page */
5220 l = frame - (3 * current_page_frames() / 4);
5223 if (l < 0) {
5224 l = 0;
5227 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5228 } else {
5229 /* moving right */
5230 if (_session->transport_rolling()) {
5231 /* rolling: end up with the playhead on the left of the page */
5232 center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5233 } else {
5234 /* not rolling: end up with the playhead 1/4 of the way along the page */
5235 center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5242 void
5243 Editor::super_rapid_screen_update ()
5245 if (!_session || !_session->engine().running()) {
5246 return;
5249 /* METERING / MIXER STRIPS */
5251 /* update track meters, if required */
5252 if (is_mapped() && meters_running) {
5253 RouteTimeAxisView* rtv;
5254 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5255 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5256 rtv->fast_update ();
5261 /* and any current mixer strip */
5262 if (current_mixer_strip) {
5263 current_mixer_strip->fast_update ();
5266 /* PLAYHEAD AND VIEWPORT */
5268 framepos_t const frame = _session->audible_frame();
5270 /* There are a few reasons why we might not update the playhead / viewport stuff:
5272 * 1. we don't update things when there's a pending locate request, otherwise
5273 * when the editor requests a locate there is a chance that this method
5274 * will move the playhead before the locate request is processed, causing
5275 * a visual glitch.
5276 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5277 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5280 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5282 last_update_frame = frame;
5284 if (!_dragging_playhead) {
5285 playhead_cursor->set_position (frame);
5288 if (!_stationary_playhead) {
5290 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5291 reset_x_origin_to_follow_playhead ();
5294 } else {
5296 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5297 editor canvas
5299 #if 0
5300 // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5301 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5302 if (target <= 0.0) {
5303 target = 0.0;
5305 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5306 target = (target * 0.15) + (current * 0.85);
5307 } else {
5308 /* relax */
5311 current = target;
5312 set_horizontal_position (current);
5313 #endif
5320 void
5321 Editor::session_going_away ()
5323 _have_idled = false;
5325 _session_connections.drop_connections ();
5327 super_rapid_screen_update_connection.disconnect ();
5329 selection->clear ();
5330 cut_buffer->clear ();
5332 clicked_regionview = 0;
5333 clicked_axisview = 0;
5334 clicked_routeview = 0;
5335 clicked_crossfadeview = 0;
5336 entered_regionview = 0;
5337 entered_track = 0;
5338 last_update_frame = 0;
5339 _drags->abort ();
5341 playhead_cursor->canvas_item.hide ();
5343 /* rip everything out of the list displays */
5345 _regions->clear ();
5346 _routes->clear ();
5347 _route_groups->clear ();
5349 /* do this first so that deleting a track doesn't reset cms to null
5350 and thus cause a leak.
5353 if (current_mixer_strip) {
5354 if (current_mixer_strip->get_parent() != 0) {
5355 global_hpacker.remove (*current_mixer_strip);
5357 delete current_mixer_strip;
5358 current_mixer_strip = 0;
5361 /* delete all trackviews */
5363 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5364 delete *i;
5366 track_views.clear ();
5368 zoom_range_clock->set_session (0);
5369 nudge_clock->set_session (0);
5371 editor_list_button.set_active(false);
5372 editor_list_button.set_sensitive(false);
5374 /* clear tempo/meter rulers */
5375 remove_metric_marks ();
5376 hide_measures ();
5377 clear_marker_display ();
5379 delete current_bbt_points;
5380 current_bbt_points = 0;
5382 /* get rid of any existing editor mixer strip */
5384 WindowTitle title(Glib::get_application_name());
5385 title += _("Editor");
5387 set_title (title.get_string());
5389 SessionHandlePtr::session_going_away ();
5393 void
5394 Editor::show_editor_list (bool yn)
5396 if (yn) {
5397 _the_notebook.show ();
5398 } else {
5399 _the_notebook.hide ();
5403 void
5404 Editor::change_region_layering_order ()
5406 framepos_t const position = get_preferred_edit_position ();
5408 if (!clicked_routeview) {
5409 if (layering_order_editor) {
5410 layering_order_editor->hide ();
5412 return;
5415 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
5417 if (!track) {
5418 return;
5421 boost::shared_ptr<Playlist> pl = track->playlist();
5423 if (!pl) {
5424 return;
5427 if (layering_order_editor == 0) {
5428 layering_order_editor = new RegionLayeringOrderEditor(*this);
5431 layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position);
5432 layering_order_editor->maybe_present ();
5435 void
5436 Editor::update_region_layering_order_editor ()
5438 if (layering_order_editor && layering_order_editor->is_visible ()) {
5439 change_region_layering_order ();
5443 void
5444 Editor::setup_fade_images ()
5446 _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear")));
5447 _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut")));
5448 _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut")));
5449 _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut")));
5450 _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut")));
5452 _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear")));
5453 _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut")));
5454 _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut")));
5455 _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut")));
5456 _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut")));
5460 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
5461 Gtk::MenuItem&
5462 Editor::action_menu_item (std::string const & name)
5464 Glib::RefPtr<Action> a = editor_actions->get_action (name);
5465 assert (a);
5467 return *manage (a->create_menu_item ());
5470 void
5471 Editor::resize_text_widgets ()
5473 set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_FUDGE+10, 15);
5474 set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_FUDGE+10, 15);
5475 set_size_request_to_display_given_text (snap_type_selector, snap_type_strings, COMBO_FUDGE+10, 15);
5476 set_size_request_to_display_given_text (snap_mode_selector, snap_mode_strings, COMBO_FUDGE+10, 15);
5477 set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_FUDGE+10, 15);
5480 void
5481 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
5483 EventBox* b = manage (new EventBox);
5484 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
5485 Label* l = manage (new Label (name));
5486 l->set_angle (-90);
5487 b->add (*l);
5488 b->show_all ();
5489 _the_notebook.append_page (widget, *b);
5492 bool
5493 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
5495 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
5496 _the_notebook.set_current_page (_the_notebook.page_num (*page));
5499 if (ev->type == GDK_2BUTTON_PRESS) {
5501 /* double-click on a notebook tab shrinks or expands the notebook */
5503 if (_notebook_shrunk) {
5504 edit_pane.set_position (pre_maximal_horizontal_pane_position);
5505 _notebook_shrunk = false;
5506 } else {
5507 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
5508 edit_pane.set_position (edit_pane.get_position() + page->get_width());
5509 _notebook_shrunk = true;
5513 return true;
5516 void
5517 Editor::popup_control_point_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
5519 using namespace Menu_Helpers;
5521 MenuList& items = _control_point_context_menu.items ();
5522 items.clear ();
5524 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun (*this, &Editor::edit_control_point), item)));
5525 items.push_back (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun (*this, &Editor::remove_control_point), item)));
5526 if (!can_remove_control_point (item)) {
5527 items.back().set_sensitive (false);
5530 _control_point_context_menu.popup (event->button.button, event->button.time);