resize all editor comboboxes when DPI is reset. involved a minor refactoring of some...
[ardour2.git] / gtk2_ardour / editor.cc
blobab6c171411931bb562205075353b3d5c466311b6
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/grouped_buttons.h>
53 #include <gtkmm2ext/gtk_ui.h>
54 #include <gtkmm2ext/tearoff.h>
55 #include <gtkmm2ext/utils.h>
56 #include <gtkmm2ext/window_title.h>
57 #include <gtkmm2ext/choice.h>
58 #include <gtkmm2ext/cell_renderer_pixbuf_toggle.h>
60 #include "ardour/audio_diskstream.h"
61 #include "ardour/audio_track.h"
62 #include "ardour/audioplaylist.h"
63 #include "ardour/audioregion.h"
64 #include "ardour/location.h"
65 #include "ardour/midi_region.h"
66 #include "ardour/plugin_manager.h"
67 #include "ardour/profile.h"
68 #include "ardour/route_group.h"
69 #include "ardour/session_directory.h"
70 #include "ardour/session_route.h"
71 #include "ardour/session_state_utils.h"
72 #include "ardour/tempo.h"
73 #include "ardour/utils.h"
74 #include "ardour/session_playlists.h"
75 #include "ardour/audioengine.h"
77 #include "control_protocol/control_protocol.h"
79 #include "editor.h"
80 #include "keyboard.h"
81 #include "marker.h"
82 #include "playlist_selector.h"
83 #include "audio_region_view.h"
84 #include "rgb_macros.h"
85 #include "selection.h"
86 #include "audio_streamview.h"
87 #include "time_axis_view.h"
88 #include "audio_time_axis.h"
89 #include "utils.h"
90 #include "crossfade_view.h"
91 #include "canvas-noevent-text.h"
92 #include "editing.h"
93 #include "public_editor.h"
94 #include "crossfade_edit.h"
95 #include "canvas_impl.h"
96 #include "actions.h"
97 #include "sfdb_ui.h"
98 #include "gui_thread.h"
99 #include "simpleline.h"
100 #include "rhythm_ferret.h"
101 #include "actions.h"
102 #include "tempo_lines.h"
103 #include "analysis_window.h"
104 #include "bundle_manager.h"
105 #include "global_port_matrix.h"
106 #include "editor_drag.h"
107 #include "editor_group_tabs.h"
108 #include "automation_time_axis.h"
109 #include "editor_routes.h"
110 #include "midi_time_axis.h"
111 #include "mixer_strip.h"
112 #include "editor_route_groups.h"
113 #include "editor_regions.h"
114 #include "editor_locations.h"
115 #include "editor_snapshots.h"
116 #include "editor_summary.h"
117 #include "region_layering_order_editor.h"
118 #include "mouse_cursors.h"
119 #include "editor_cursors.h"
121 #include "i18n.h"
123 #ifdef WITH_CMT
124 #include "imageframe_socket_handler.h"
125 #endif
127 using namespace std;
128 using namespace ARDOUR;
129 using namespace PBD;
130 using namespace Gtk;
131 using namespace Glib;
132 using namespace Gtkmm2ext;
133 using namespace Editing;
135 using PBD::internationalize;
136 using PBD::atoi;
137 using Gtkmm2ext::Keyboard;
139 const double Editor::timebar_height = 15.0;
141 static const gchar *_snap_type_strings[] = {
142 N_("CD Frames"),
143 N_("Timecode Frames"),
144 N_("Timecode Seconds"),
145 N_("Timecode Minutes"),
146 N_("Seconds"),
147 N_("Minutes"),
148 N_("Beats/32"),
149 N_("Beats/28"),
150 N_("Beats/24"),
151 N_("Beats/20"),
152 N_("Beats/16"),
153 N_("Beats/14"),
154 N_("Beats/12"),
155 N_("Beats/10"),
156 N_("Beats/8"),
157 N_("Beats/7"),
158 N_("Beats/6"),
159 N_("Beats/5"),
160 N_("Beats/4"),
161 N_("Beats/3"),
162 N_("Beats/2"),
163 N_("Beats"),
164 N_("Bars"),
165 N_("Marks"),
166 N_("Region starts"),
167 N_("Region ends"),
168 N_("Region syncs"),
169 N_("Region bounds"),
173 static const gchar *_snap_mode_strings[] = {
174 N_("No Grid"),
175 N_("Grid"),
176 N_("Magnetic"),
180 static const gchar *_edit_point_strings[] = {
181 N_("Playhead"),
182 N_("Marker"),
183 N_("Mouse"),
187 static const gchar *_zoom_focus_strings[] = {
188 N_("Left"),
189 N_("Right"),
190 N_("Center"),
191 N_("Playhead"),
192 N_("Mouse"),
193 N_("Edit point"),
197 #ifdef USE_RUBBERBAND
198 static const gchar *_rb_opt_strings[] = {
199 N_("Mushy"),
200 N_("Smooth"),
201 N_("Balanced multitimbral mixture"),
202 N_("Unpitched percussion with stable notes"),
203 N_("Crisp monophonic instrumental"),
204 N_("Unpitched solo percussion"),
205 N_("Resample without preserving pitch"),
208 #endif
210 void
211 show_me_the_size (Requisition* r, const char* what)
213 cerr << "size of " << what << " = " << r->width << " x " << r->height << endl;
216 #ifdef GTKOSX
217 static void
218 pane_size_watcher (Paned* pane)
220 /* if the handle of a pane vanishes into (at least) the tabs of a notebook,
221 it is no longer accessible. so stop that. this doesn't happen on X11,
222 just the quartz backend.
224 ugh.
227 int max_width_of_lhs = GTK_WIDGET(pane->gobj())->allocation.width - 25;
229 gint pos = pane->get_position ();
231 if (pos > max_width_of_lhs) {
232 pane->set_position (max_width_of_lhs);
235 #endif
237 Editor::Editor ()
238 : _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
240 /* time display buttons */
241 , minsec_label (_("Mins:Secs"))
242 , bbt_label (_("Bars:Beats"))
243 , timecode_label (_("Timecode"))
244 , frame_label (_("Samples"))
245 , tempo_label (_("Tempo"))
246 , meter_label (_("Meter"))
247 , mark_label (_("Location Markers"))
248 , range_mark_label (_("Range Markers"))
249 , transport_mark_label (_("Loop/Punch Ranges"))
250 , cd_mark_label (_("CD Markers"))
251 , edit_packer (4, 4, true)
253 /* the values here don't matter: layout widgets
254 reset them as needed.
257 , vertical_adjustment (0.0, 0.0, 10.0, 400.0)
259 /* tool bar related */
261 , zoom_range_clock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, false, true)
263 , toolbar_selection_clock_table (2,3)
265 , automation_mode_button (_("mode"))
266 , global_automation_button (_("automation"))
268 , midi_panic_button (_("Panic"))
270 #ifdef WITH_CMT
271 , image_socket_listener(0)
272 #endif
274 /* nudge */
276 , nudge_clock (X_("nudge"), false, X_("NudgeClock"), true, false, true)
277 , meters_running(false)
278 , _pending_locate_request (false)
279 , _pending_initial_locate (false)
280 , _last_cut_copy_source_track (0)
282 , _region_selection_change_updates_region_list (true)
284 constructed = false;
286 /* we are a singleton */
288 PublicEditor::_instance = this;
290 _have_idled = false;
292 selection = new Selection (this);
293 cut_buffer = new Selection (this);
295 clicked_regionview = 0;
296 clicked_axisview = 0;
297 clicked_routeview = 0;
298 clicked_crossfadeview = 0;
299 clicked_control_point = 0;
300 last_update_frame = 0;
301 pre_press_cursor = 0;
302 _drags = new DragManager (this);
303 current_mixer_strip = 0;
304 current_bbt_points = 0;
305 tempo_lines = 0;
307 snap_type_strings = I18N (_snap_type_strings);
308 snap_mode_strings = I18N (_snap_mode_strings);
309 zoom_focus_strings = I18N (_zoom_focus_strings);
310 edit_point_strings = I18N (_edit_point_strings);
311 #ifdef USE_RUBBERBAND
312 rb_opt_strings = I18N (_rb_opt_strings);
313 rb_current_opt = 4;
314 #endif
316 snap_threshold = 5.0;
317 bbt_beat_subdivision = 4;
318 _canvas_width = 0;
319 _canvas_height = 0;
320 last_autoscroll_x = 0;
321 last_autoscroll_y = 0;
322 autoscroll_active = false;
323 autoscroll_timeout_tag = -1;
324 logo_item = 0;
326 analysis_window = 0;
328 current_interthread_info = 0;
329 _show_measures = true;
330 show_gain_after_trim = false;
331 verbose_cursor_on = true;
332 last_item_entered = 0;
334 have_pending_keyboard_selection = false;
335 _follow_playhead = true;
336 _stationary_playhead = false;
337 _xfade_visibility = true;
338 editor_ruler_menu = 0;
339 no_ruler_shown_update = false;
340 marker_menu = 0;
341 session_range_marker_menu = 0;
342 range_marker_menu = 0;
343 marker_menu_item = 0;
344 tempo_or_meter_marker_menu = 0;
345 transport_marker_menu = 0;
346 new_transport_marker_menu = 0;
347 editor_mixer_strip_width = Wide;
348 show_editor_mixer_when_tracks_arrive = false;
349 region_edit_menu_split_multichannel_item = 0;
350 region_edit_menu_split_item = 0;
351 temp_location = 0;
352 leftmost_frame = 0;
353 current_stepping_trackview = 0;
354 entered_track = 0;
355 entered_regionview = 0;
356 entered_marker = 0;
357 clear_entered_track = false;
358 current_timefx = 0;
359 playhead_cursor = 0;
360 button_release_can_deselect = true;
361 _dragging_playhead = false;
362 _dragging_edit_point = false;
363 select_new_marker = false;
364 rhythm_ferret = 0;
365 layering_order_editor = 0;
366 _bundle_manager = 0;
367 no_save_visual = false;
368 resize_idle_id = -1;
370 scrubbing_direction = 0;
372 sfbrowser = 0;
374 location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get();
375 location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get();
376 location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get();
377 location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get();
378 location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get();
380 _edit_point = EditAtMouse;
381 _internal_editing = false;
382 current_canvas_cursor = 0;
384 frames_per_unit = 2048; /* too early to use reset_zoom () */
386 _scroll_callbacks = 0;
388 zoom_focus = ZoomFocusLeft;
389 set_zoom_focus (ZoomFocusLeft);
390 zoom_range_clock.ValueChanged.connect (sigc::mem_fun(*this, &Editor::zoom_adjustment_changed));
392 bbt_label.set_name ("EditorTimeButton");
393 bbt_label.set_size_request (-1, (int)timebar_height);
394 bbt_label.set_alignment (1.0, 0.5);
395 bbt_label.set_padding (5,0);
396 bbt_label.hide ();
397 bbt_label.set_no_show_all();
398 minsec_label.set_name ("EditorTimeButton");
399 minsec_label.set_size_request (-1, (int)timebar_height);
400 minsec_label.set_alignment (1.0, 0.5);
401 minsec_label.set_padding (5,0);
402 minsec_label.hide ();
403 minsec_label.set_no_show_all();
404 timecode_label.set_name ("EditorTimeButton");
405 timecode_label.set_size_request (-1, (int)timebar_height);
406 timecode_label.set_alignment (1.0, 0.5);
407 timecode_label.set_padding (5,0);
408 timecode_label.hide ();
409 timecode_label.set_no_show_all();
410 frame_label.set_name ("EditorTimeButton");
411 frame_label.set_size_request (-1, (int)timebar_height);
412 frame_label.set_alignment (1.0, 0.5);
413 frame_label.set_padding (5,0);
414 frame_label.hide ();
415 frame_label.set_no_show_all();
417 tempo_label.set_name ("EditorTimeButton");
418 tempo_label.set_size_request (-1, (int)timebar_height);
419 tempo_label.set_alignment (1.0, 0.5);
420 tempo_label.set_padding (5,0);
421 tempo_label.hide();
422 tempo_label.set_no_show_all();
423 meter_label.set_name ("EditorTimeButton");
424 meter_label.set_size_request (-1, (int)timebar_height);
425 meter_label.set_alignment (1.0, 0.5);
426 meter_label.set_padding (5,0);
427 meter_label.hide();
428 meter_label.set_no_show_all();
429 mark_label.set_name ("EditorTimeButton");
430 mark_label.set_size_request (-1, (int)timebar_height);
431 mark_label.set_alignment (1.0, 0.5);
432 mark_label.set_padding (5,0);
433 mark_label.hide();
434 mark_label.set_no_show_all();
435 cd_mark_label.set_name ("EditorTimeButton");
436 cd_mark_label.set_size_request (-1, (int)timebar_height);
437 cd_mark_label.set_alignment (1.0, 0.5);
438 cd_mark_label.set_padding (5,0);
439 cd_mark_label.hide();
440 cd_mark_label.set_no_show_all();
441 range_mark_label.set_name ("EditorTimeButton");
442 range_mark_label.set_size_request (-1, (int)timebar_height);
443 range_mark_label.set_alignment (1.0, 0.5);
444 range_mark_label.set_padding (5,0);
445 range_mark_label.hide();
446 range_mark_label.set_no_show_all();
447 transport_mark_label.set_name ("EditorTimeButton");
448 transport_mark_label.set_size_request (-1, (int)timebar_height);
449 transport_mark_label.set_alignment (1.0, 0.5);
450 transport_mark_label.set_padding (5,0);
451 transport_mark_label.hide();
452 transport_mark_label.set_no_show_all();
454 initialize_rulers ();
455 initialize_canvas ();
456 _summary = new EditorSummary (this);
458 selection->TimeChanged.connect (sigc::mem_fun(*this, &Editor::time_selection_changed));
459 selection->TracksChanged.connect (sigc::mem_fun(*this, &Editor::track_selection_changed));
460 editor_regions_selection_changed_connection = selection->RegionsChanged.connect (sigc::mem_fun(*this, &Editor::region_selection_changed));
461 selection->PointsChanged.connect (sigc::mem_fun(*this, &Editor::point_selection_changed));
462 selection->MarkersChanged.connect (sigc::mem_fun(*this, &Editor::marker_selection_changed));
464 edit_controls_vbox.set_spacing (0);
465 vertical_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &Editor::tie_vertical_scrolling), true);
466 track_canvas->signal_map_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_map_handler));
468 HBox* h = manage (new HBox);
469 _group_tabs = new EditorGroupTabs (this);
470 h->pack_start (*_group_tabs, PACK_SHRINK);
471 h->pack_start (edit_controls_vbox);
472 controls_layout.add (*h);
474 controls_layout.set_name ("EditControlsBase");
475 controls_layout.add_events (Gdk::SCROLL_MASK);
476 controls_layout.signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::control_layout_scroll), false);
478 controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
479 controls_layout.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_release));
480 controls_layout_size_request_connection = controls_layout.signal_size_request().connect (sigc::mem_fun (*this, &Editor::controls_layout_size_request));
482 _cursors = new MouseCursors;
484 ArdourCanvas::Canvas* time_pad = manage(new ArdourCanvas::Canvas());
485 ArdourCanvas::SimpleLine* pad_line_1 = manage(new ArdourCanvas::SimpleLine(*time_pad->root(),
486 0.0, 1.0, 100.0, 1.0));
487 pad_line_1->property_color_rgba() = 0xFF0000FF;
488 pad_line_1->show();
489 time_pad->show();
491 time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2);
492 time_canvas_vbox.set_size_request (-1, -1);
494 ruler_label_event_box.add (ruler_label_vbox);
495 ruler_label_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
496 ruler_label_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
498 time_button_event_box.add (time_button_vbox);
499 time_button_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
500 time_button_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
502 /* these enable us to have a dedicated window (for cursor setting, etc.)
503 for the canvas areas.
506 track_canvas_event_box.add (*track_canvas);
508 time_canvas_event_box.add (time_canvas_vbox);
509 time_canvas_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
511 edit_packer.set_col_spacings (0);
512 edit_packer.set_row_spacings (0);
513 edit_packer.set_homogeneous (false);
514 edit_packer.set_border_width (0);
515 edit_packer.set_name ("EditorWindow");
517 /* labels for the rulers */
518 edit_packer.attach (ruler_label_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0);
519 /* labels for the marker "tracks" */
520 edit_packer.attach (time_button_event_box, 1, 2, 1, 2, FILL, SHRINK, 0, 0);
521 /* the rulers */
522 edit_packer.attach (time_canvas_event_box, 2, 3, 0, 1, FILL|EXPAND, FILL, 0, 0);
523 /* track controls */
524 edit_packer.attach (controls_layout, 0, 2, 2, 3, FILL, FILL|EXPAND, 0, 0);
525 /* main canvas */
526 edit_packer.attach (track_canvas_event_box, 2, 3, 1, 3, FILL|EXPAND, FILL|EXPAND, 0, 0);
528 bottom_hbox.set_border_width (2);
529 bottom_hbox.set_spacing (3);
531 _route_groups = new EditorRouteGroups (this);
532 _routes = new EditorRoutes (this);
533 _regions = new EditorRegions (this);
534 _snapshots = new EditorSnapshots (this);
535 _locations = new EditorLocations (this);
537 Gtk::Label* nlabel;
539 nlabel = manage (new Label (_("Regions")));
540 nlabel->set_angle (-90);
541 the_notebook.append_page (_regions->widget (), *nlabel);
542 nlabel = manage (new Label (_("Tracks & Busses")));
543 nlabel->set_angle (-90);
544 the_notebook.append_page (_routes->widget (), *nlabel);
545 nlabel = manage (new Label (_("Snapshots")));
546 nlabel->set_angle (-90);
547 the_notebook.append_page (_snapshots->widget (), *nlabel);
548 nlabel = manage (new Label (_("Route Groups")));
549 nlabel->set_angle (-90);
550 the_notebook.append_page (_route_groups->widget (), *nlabel);
551 nlabel = manage (new Label (_("Ranges & Marks")));
552 nlabel->set_angle (-90);
553 the_notebook.append_page (_locations->widget (), *nlabel);
555 the_notebook.set_show_tabs (true);
556 the_notebook.set_scrollable (true);
557 the_notebook.popup_disable ();
558 the_notebook.set_tab_pos (Gtk::POS_RIGHT);
559 the_notebook.show_all ();
561 post_maximal_editor_width = 0;
562 post_maximal_horizontal_pane_position = 0;
563 post_maximal_editor_height = 0;
564 post_maximal_vertical_pane_position = 0;
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::mem_fun (*this, &Editor::horizontal_scroll_left_press)));
571 summary_arrows_left_left->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_release));
572 Button* summary_arrows_left_right = manage (new Button);
573 summary_arrows_left_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
574 summary_arrows_left_right->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press)));
575 summary_arrows_left_right->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_release));
576 VBox* summary_arrows_left = manage (new VBox);
577 summary_arrows_left->pack_start (*summary_arrows_left_left);
578 summary_arrows_left->pack_start (*summary_arrows_left_right);
580 Button* summary_arrows_right_left = manage (new Button);
581 summary_arrows_right_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
582 summary_arrows_right_left->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press)));
583 summary_arrows_right_left->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_release));
584 Button* summary_arrows_right_right = manage (new Button);
585 summary_arrows_right_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
586 summary_arrows_right_right->signal_pressed().connect (sigc::hide_return (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press)));
587 summary_arrows_right_right->signal_released().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_release));
588 VBox* summary_arrows_right = manage (new VBox);
589 summary_arrows_right->pack_start (*summary_arrows_right_left);
590 summary_arrows_right->pack_start (*summary_arrows_right_right);
592 Frame* summary_frame = manage (new Frame);
593 summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN);
594 summary_frame->add (*_summary);
595 summary_frame->show ();
597 _summary_hbox.pack_start (*summary_arrows_left, false, false);
598 _summary_hbox.pack_start (*summary_frame, true, true);
599 _summary_hbox.pack_start (*summary_arrows_right, false, false);
601 editor_summary_pane.pack2 (_summary_hbox);
603 edit_pane.pack1 (editor_summary_pane, true, true);
604 edit_pane.pack2 (the_notebook, false, true);
606 editor_summary_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun (*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&editor_summary_pane)));
608 /* XXX: editor_summary_pane might need similar special OS X treatment to the edit_pane */
610 edit_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&edit_pane)));
611 #ifdef GTKOSX
612 Glib::PropertyProxy<int> proxy = edit_pane.property_position();
613 proxy.signal_changed().connect (bind (sigc::ptr_fun (pane_size_watcher), static_cast<Paned*> (&edit_pane)));
614 #endif
615 top_hbox.pack_start (toolbar_frame, false, true);
617 HBox *hbox = manage (new HBox);
618 hbox->pack_start (edit_pane, true, true);
620 global_vpacker.pack_start (top_hbox, false, false);
621 global_vpacker.pack_start (*hbox, true, true);
623 global_hpacker.pack_start (global_vpacker, true, true);
625 set_name ("EditorWindow");
626 add_accel_group (ActionManager::ui_manager->get_accel_group());
628 status_bar_hpacker.show ();
630 vpacker.pack_end (status_bar_hpacker, false, false);
631 vpacker.pack_end (global_hpacker, true, true);
633 /* register actions now so that set_state() can find them and set toggles/checks etc */
635 register_actions ();
637 setup_toolbar ();
638 setup_midi_toolbar ();
640 _snap_type = SnapToBeat;
641 set_snap_to (_snap_type);
642 _snap_mode = SnapOff;
643 set_snap_mode (_snap_mode);
644 set_mouse_mode (MouseObject, true);
645 set_edit_point_preference (EditAtMouse, true);
647 _playlist_selector = new PlaylistSelector();
648 _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
650 RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), ui_bind (&Editor::catch_vanishing_regionview, this, _1), gui_context());
652 /* nudge stuff */
654 nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
655 nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
657 nudge_forward_button.set_name ("TransportButton");
658 nudge_backward_button.set_name ("TransportButton");
660 fade_context_menu.set_name ("ArdourContextMenu");
662 /* icons, titles, WM stuff */
664 list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
665 Glib::RefPtr<Gdk::Pixbuf> icon;
667 if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
668 window_icons.push_back (icon);
670 if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
671 window_icons.push_back (icon);
673 if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
674 window_icons.push_back (icon);
676 if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
677 window_icons.push_back (icon);
679 if (!window_icons.empty()) {
680 set_icon_list (window_icons);
681 set_default_icon_list (window_icons);
684 WindowTitle title(Glib::get_application_name());
685 title += _("Editor");
686 set_title (title.get_string());
687 set_wmclass (X_("ardour_editor"), PROGRAM_NAME);
689 add (vpacker);
690 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
692 signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
693 signal_delete_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
695 /* allow external control surfaces/protocols to do various things */
697 ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context());
698 ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context());
699 ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context());
700 ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), ui_bind (&Editor::control_scroll, this, _1), gui_context());
701 BasicUI::AccessAction.connect (*this, invalidator (*this), ui_bind (&Editor::access_action, this, _1, _2), gui_context());
703 /* problematic: has to return a value and thus cannot be x-thread */
705 Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
707 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
709 TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
711 _ignore_region_action = false;
712 _last_region_menu_was_main = false;
713 _popup_region_menu_item = 0;
715 _show_marker_lines = false;
716 _over_region_trim_target = false;
718 constructed = true;
719 instant_save ();
721 setup_fade_images ();
724 Editor::~Editor()
726 #ifdef WITH_CMT
727 if(image_socket_listener) {
728 if(image_socket_listener->is_connected())
730 image_socket_listener->close_connection() ;
733 delete image_socket_listener ;
734 image_socket_listener = 0 ;
736 #endif
738 delete _routes;
739 delete _route_groups;
740 delete track_canvas;
741 delete _drags;
744 void
745 Editor::add_toplevel_controls (Container& cont)
747 vpacker.pack_start (cont, false, false);
748 cont.show_all ();
751 void
752 Editor::catch_vanishing_regionview (RegionView *rv)
754 /* note: the selection will take care of the vanishing
755 audioregionview by itself.
758 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
759 _drags->abort ();
762 if (clicked_regionview == rv) {
763 clicked_regionview = 0;
766 if (entered_regionview == rv) {
767 set_entered_regionview (0);
770 if (!_all_region_actions_sensitized) {
771 sensitize_all_region_actions (true);
775 void
776 Editor::set_entered_regionview (RegionView* rv)
778 if (rv == entered_regionview) {
779 return;
782 if (entered_regionview) {
783 entered_regionview->exited ();
786 if ((entered_regionview = rv) != 0) {
787 entered_regionview->entered (internal_editing ());
790 if (!_all_region_actions_sensitized && _last_region_menu_was_main) {
791 /* This RegionView entry might have changed what region actions
792 are allowed, so sensitize them all in case a key is pressed.
794 sensitize_all_region_actions (true);
798 void
799 Editor::set_entered_track (TimeAxisView* tav)
801 if (entered_track) {
802 entered_track->exited ();
805 if ((entered_track = tav) != 0) {
806 entered_track->entered ();
810 void
811 Editor::show_window ()
813 if (! is_visible ()) {
814 show_all ();
816 /* re-hide editor list if necessary */
817 editor_list_button_toggled ();
819 /* re-hide summary widget if necessary */
820 parameter_changed ("show-summary");
822 parameter_changed ("show-edit-group-tabs");
824 /* now reset all audio_time_axis heights, because widgets might need
825 to be re-hidden
828 TimeAxisView *tv;
830 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
831 tv = (static_cast<TimeAxisView*>(*i));
832 tv->reset_height ();
836 present ();
839 void
840 Editor::instant_save ()
842 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
843 return;
846 if (_session) {
847 _session->add_instant_xml(get_state());
848 } else {
849 Config->add_instant_xml(get_state());
853 void
854 Editor::zoom_adjustment_changed ()
856 if (_session == 0) {
857 return;
860 double fpu = zoom_range_clock.current_duration() / _canvas_width;
862 if (fpu < 1.0) {
863 fpu = 1.0;
864 zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width));
865 } else if (fpu > _session->current_end_frame() / _canvas_width) {
866 fpu = _session->current_end_frame() / _canvas_width;
867 zoom_range_clock.set ((framepos_t) floor (fpu * _canvas_width));
870 temporal_zoom (fpu);
873 void
874 Editor::control_scroll (float fraction)
876 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
878 if (!_session) {
879 return;
882 double step = fraction * current_page_frames();
885 _control_scroll_target is an optional<T>
887 it acts like a pointer to an framepos_t, with
888 a operator conversion to boolean to check
889 that it has a value could possibly use
890 playhead_cursor->current_frame to store the
891 value and a boolean in the class to know
892 when it's out of date
895 if (!_control_scroll_target) {
896 _control_scroll_target = _session->transport_frame();
897 _dragging_playhead = true;
900 if ((fraction < 0.0f) && (*_control_scroll_target < (framepos_t) fabs(step))) {
901 *_control_scroll_target = 0;
902 } else if ((fraction > 0.0f) && (max_framepos - *_control_scroll_target < step)) {
903 *_control_scroll_target = max_framepos - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
904 } else {
905 *_control_scroll_target += (framepos_t) floor (step);
908 /* move visuals, we'll catch up with it later */
910 playhead_cursor->set_position (*_control_scroll_target);
911 UpdateAllTransportClocks (*_control_scroll_target);
913 if (*_control_scroll_target > (current_page_frames() / 2)) {
914 /* try to center PH in window */
915 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
916 } else {
917 reset_x_origin (0);
921 Now we do a timeout to actually bring the session to the right place
922 according to the playhead. This is to avoid reading disk buffers on every
923 call to control_scroll, which is driven by ScrollTimeline and therefore
924 probably by a control surface wheel which can generate lots of events.
926 /* cancel the existing timeout */
928 control_scroll_connection.disconnect ();
930 /* add the next timeout */
932 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
935 bool
936 Editor::deferred_control_scroll (framepos_t /*target*/)
938 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
939 // reset for next stream
940 _control_scroll_target = boost::none;
941 _dragging_playhead = false;
942 return false;
945 void
946 Editor::access_action (std::string action_group, std::string action_item)
948 if (!_session) {
949 return;
952 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
954 RefPtr<Action> act;
955 act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
957 if (act) {
958 act->activate();
962 void
963 Editor::on_realize ()
965 Window::on_realize ();
966 Realized ();
969 void
970 Editor::map_position_change (framepos_t frame)
972 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, frame)
974 if (_session == 0) {
975 return;
978 if (_follow_playhead) {
979 center_screen (frame);
982 playhead_cursor->set_position (frame);
985 void
986 Editor::center_screen (framepos_t frame)
988 double page = _canvas_width * frames_per_unit;
990 /* if we're off the page, then scroll.
993 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
994 center_screen_internal (frame, page);
998 void
999 Editor::center_screen_internal (framepos_t frame, float page)
1001 page /= 2;
1003 if (frame > page) {
1004 frame -= (framepos_t) page;
1005 } else {
1006 frame = 0;
1009 reset_x_origin (frame);
1013 void
1014 Editor::update_title ()
1016 ENSURE_GUI_THREAD (*this, &Editor::update_title)
1018 if (_session) {
1019 bool dirty = _session->dirty();
1021 string session_name;
1023 if (_session->snap_name() != _session->name()) {
1024 session_name = _session->snap_name();
1025 } else {
1026 session_name = _session->name();
1029 if (dirty) {
1030 session_name = "*" + session_name;
1033 WindowTitle title(session_name);
1034 title += Glib::get_application_name();
1035 set_title (title.get_string());
1039 void
1040 Editor::set_session (Session *t)
1042 SessionHandlePtr::set_session (t);
1044 if (!_session) {
1045 return;
1048 zoom_range_clock.set_session (_session);
1049 _playlist_selector->set_session (_session);
1050 nudge_clock.set_session (_session);
1051 _summary->set_session (_session);
1052 _group_tabs->set_session (_session);
1053 _route_groups->set_session (_session);
1054 _regions->set_session (_session);
1055 _snapshots->set_session (_session);
1056 _routes->set_session (_session);
1057 _locations->set_session (_session);
1059 if (rhythm_ferret) {
1060 rhythm_ferret->set_session (_session);
1063 if (analysis_window) {
1064 analysis_window->set_session (_session);
1067 if (sfbrowser) {
1068 sfbrowser->set_session (_session);
1071 compute_fixed_ruler_scale ();
1073 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1074 set_state (*node, Stateful::loading_state_version);
1076 /* catch up with the playhead */
1078 _session->request_locate (playhead_cursor->current_frame);
1079 _pending_initial_locate = true;
1081 update_title ();
1083 /* These signals can all be emitted by a non-GUI thread. Therefore the
1084 handlers for them must not attempt to directly interact with the GUI,
1085 but use Gtkmm2ext::UI::instance()->call_slot();
1088 _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), ui_bind(&Editor::step_edit_status_change, this, _1), gui_context());
1089 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1090 _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context());
1091 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context());
1092 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1093 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context());
1094 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1095 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
1096 _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Editor::session_state_saved, this, _1), gui_context());
1097 _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context());
1098 _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&Editor::location_gone, this, _1), gui_context());
1099 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1100 _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display_s, this, _1), gui_context());
1101 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1103 if (Profile->get_sae()) {
1104 Timecode::BBT_Time bbt;
1105 bbt.bars = 0;
1106 bbt.beats = 0;
1107 bbt.ticks = 120;
1108 framepos_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1);
1109 nudge_clock.set_mode(AudioClock::BBT);
1110 nudge_clock.set (pos, true, 0, AudioClock::BBT);
1112 } else {
1113 nudge_clock.set (_session->frame_rate() * 5, true, 0, AudioClock::Timecode); // default of 5 seconds
1116 playhead_cursor->canvas_item.show ();
1118 Location* loc = _session->locations()->auto_loop_location();
1119 if (loc == 0) {
1120 loc = new Location (*_session, 0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1121 if (loc->start() == loc->end()) {
1122 loc->set_end (loc->start() + 1);
1124 _session->locations()->add (loc, false);
1125 _session->set_auto_loop_location (loc);
1126 } else {
1127 // force name
1128 loc->set_name (_("Loop"));
1131 loc = _session->locations()->auto_punch_location();
1132 if (loc == 0) {
1133 loc = new Location (*_session, 0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1134 if (loc->start() == loc->end()) {
1135 loc->set_end (loc->start() + 1);
1137 _session->locations()->add (loc, false);
1138 _session->set_auto_punch_location (loc);
1139 } else {
1140 // force name
1141 loc->set_name (_("Punch"));
1144 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1145 Config->map_parameters (pc);
1146 _session->config.map_parameters (pc);
1148 refresh_location_display ();
1150 restore_ruler_visibility ();
1151 //tempo_map_changed (PropertyChange (0));
1152 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1154 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1155 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1158 super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
1159 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1162 switch (_snap_type) {
1163 case SnapToRegionStart:
1164 case SnapToRegionEnd:
1165 case SnapToRegionSync:
1166 case SnapToRegionBoundary:
1167 build_region_boundary_cache ();
1168 break;
1170 default:
1171 break;
1174 /* register for undo history */
1175 _session->register_with_memento_command_factory(_id, this);
1177 ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated));
1179 start_updating_meters ();
1182 void
1183 Editor::action_pre_activated (Glib::RefPtr<Action> const & a)
1185 if (a->get_name() == "RegionMenu") {
1186 /* When the main menu's region menu is opened, we setup the actions so that they look right
1187 in the menu. I can't find a way of getting a signal when this menu is subsequently closed,
1188 so we resensitize all region actions when the entered regionview or the region selection
1189 changes. HOWEVER we can't always resensitize on entered_regionview change because that
1190 happens after the region context menu is opened. So we set a flag here, too.
1192 What a carry on :(
1194 sensitize_the_right_region_actions ();
1195 _last_region_menu_was_main = true;
1199 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1200 void
1201 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1203 using namespace Menu_Helpers;
1204 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1206 if (arv == 0) {
1207 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1208 /*NOTREACHED*/
1211 MenuList& items (fade_context_menu.items());
1213 items.clear ();
1215 switch (item_type) {
1216 case FadeInItem:
1217 case FadeInHandleItem:
1218 if (arv->audio_region()->fade_in_active()) {
1219 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1220 } else {
1221 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1224 items.push_back (SeparatorElem());
1226 if (Profile->get_sae()) {
1228 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)));
1229 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)));
1231 } else {
1233 items.push_back (
1234 ImageMenuElem (
1235 _("Linear"),
1236 *_fade_in_images[FadeLinear],
1237 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)
1241 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1243 items.push_back (
1244 ImageMenuElem (
1245 _("Slowest"),
1246 *_fade_in_images[FadeFast],
1247 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)
1250 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1252 items.push_back (
1253 ImageMenuElem (
1254 _("Slow"),
1255 *_fade_in_images[FadeLogB],
1256 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogB)
1259 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1261 items.push_back (
1262 ImageMenuElem (
1263 _("Fast"),
1264 *_fade_in_images[FadeLogA],
1265 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogA)
1268 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1270 items.push_back (
1271 ImageMenuElem (
1272 _("Fastest"),
1273 *_fade_in_images[FadeSlow],
1274 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow)
1277 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1280 break;
1282 case FadeOutItem:
1283 case FadeOutHandleItem:
1284 if (arv->audio_region()->fade_out_active()) {
1285 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1286 } else {
1287 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1290 items.push_back (SeparatorElem());
1292 if (Profile->get_sae()) {
1293 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)));
1294 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)));
1295 } else {
1297 items.push_back (
1298 ImageMenuElem (
1299 _("Linear"),
1300 *_fade_out_images[FadeLinear],
1301 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)
1305 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1307 items.push_back (
1308 ImageMenuElem (
1309 _("Slowest"),
1310 *_fade_out_images[FadeFast],
1311 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)
1314 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1316 items.push_back (
1317 ImageMenuElem (
1318 _("Slow"),
1319 *_fade_out_images[FadeLogB],
1320 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogA)
1323 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1325 items.push_back (
1326 ImageMenuElem (
1327 _("Fast"),
1328 *_fade_out_images[FadeLogA],
1329 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogB)
1332 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1334 items.push_back (
1335 ImageMenuElem (
1336 _("Fastest"),
1337 *_fade_out_images[FadeSlow],
1338 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast)
1341 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1344 break;
1346 default:
1347 fatal << _("programming error: ")
1348 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1349 << endmsg;
1350 /*NOTREACHED*/
1353 fade_context_menu.popup (button, time);
1356 void
1357 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1359 using namespace Menu_Helpers;
1360 Menu* (Editor::*build_menu_function)();
1361 Menu *menu;
1363 switch (item_type) {
1364 case RegionItem:
1365 case RegionViewName:
1366 case RegionViewNameHighlight:
1367 case LeftFrameHandle:
1368 case RightFrameHandle:
1369 if (with_selection) {
1370 build_menu_function = &Editor::build_track_selection_context_menu;
1371 } else {
1372 build_menu_function = &Editor::build_track_region_context_menu;
1374 break;
1376 case SelectionItem:
1377 if (with_selection) {
1378 build_menu_function = &Editor::build_track_selection_context_menu;
1379 } else {
1380 build_menu_function = &Editor::build_track_context_menu;
1382 break;
1384 case CrossfadeViewItem:
1385 build_menu_function = &Editor::build_track_crossfade_context_menu;
1386 break;
1388 case StreamItem:
1389 if (clicked_routeview->track()) {
1390 build_menu_function = &Editor::build_track_context_menu;
1391 } else {
1392 build_menu_function = &Editor::build_track_bus_context_menu;
1394 break;
1396 default:
1397 /* probably shouldn't happen but if it does, we don't care */
1398 return;
1401 menu = (this->*build_menu_function)();
1402 menu->set_name ("ArdourContextMenu");
1404 /* now handle specific situations */
1406 switch (item_type) {
1407 case RegionItem:
1408 case RegionViewName:
1409 case RegionViewNameHighlight:
1410 case LeftFrameHandle:
1411 case RightFrameHandle:
1412 if (!with_selection) {
1413 if (region_edit_menu_split_item) {
1414 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1415 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1416 } else {
1417 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1420 if (region_edit_menu_split_multichannel_item) {
1421 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1422 region_edit_menu_split_multichannel_item->set_sensitive (true);
1423 } else {
1424 region_edit_menu_split_multichannel_item->set_sensitive (false);
1428 break;
1430 case SelectionItem:
1431 break;
1433 case CrossfadeViewItem:
1434 break;
1436 case StreamItem:
1437 break;
1439 default:
1440 /* probably shouldn't happen but if it does, we don't care */
1441 return;
1444 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1446 /* Bounce to disk */
1448 using namespace Menu_Helpers;
1449 MenuList& edit_items = menu->items();
1451 edit_items.push_back (SeparatorElem());
1453 switch (clicked_routeview->audio_track()->freeze_state()) {
1454 case AudioTrack::NoFreeze:
1455 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1456 break;
1458 case AudioTrack::Frozen:
1459 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1460 break;
1462 case AudioTrack::UnFrozen:
1463 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1464 break;
1469 if (item_type == StreamItem && clicked_routeview) {
1470 clicked_routeview->build_underlay_menu(menu);
1473 /* When the region menu is opened, we setup the actions so that they look right
1474 in the menu.
1476 sensitize_the_right_region_actions ();
1477 _last_region_menu_was_main = false;
1479 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1480 menu->popup (button, time);
1483 Menu*
1484 Editor::build_track_context_menu ()
1486 using namespace Menu_Helpers;
1488 MenuList& edit_items = track_context_menu.items();
1489 edit_items.clear();
1491 add_dstream_context_items (edit_items);
1492 return &track_context_menu;
1495 Menu*
1496 Editor::build_track_bus_context_menu ()
1498 using namespace Menu_Helpers;
1500 MenuList& edit_items = track_context_menu.items();
1501 edit_items.clear();
1503 add_bus_context_items (edit_items);
1504 return &track_context_menu;
1507 Menu*
1508 Editor::build_track_region_context_menu ()
1510 using namespace Menu_Helpers;
1511 MenuList& edit_items = track_region_context_menu.items();
1512 edit_items.clear();
1514 /* we've just cleared the track region context menu, so the menu that these
1515 two items were on will have disappeared; stop them dangling.
1517 region_edit_menu_split_item = 0;
1518 region_edit_menu_split_multichannel_item = 0;
1520 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1522 if (rtv) {
1523 boost::shared_ptr<Track> tr;
1524 boost::shared_ptr<Playlist> pl;
1526 /* Don't offer a region submenu if we are in internal edit mode, as we don't select regions in this
1527 mode and so offering region context is somewhat confusing.
1529 if ((tr = rtv->track()) && ((pl = tr->playlist())) && !internal_editing()) {
1530 framepos_t const framepos = (framepos_t) floor ((double) get_preferred_edit_position() * tr->speed());
1531 uint32_t regions_at = pl->count_regions_at (framepos);
1532 add_region_context_items (edit_items, regions_at > 1);
1536 add_dstream_context_items (edit_items);
1538 return &track_region_context_menu;
1541 Menu*
1542 Editor::build_track_crossfade_context_menu ()
1544 using namespace Menu_Helpers;
1545 MenuList& edit_items = track_crossfade_context_menu.items();
1546 edit_items.clear ();
1548 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1550 if (atv) {
1551 boost::shared_ptr<Track> tr;
1552 boost::shared_ptr<Playlist> pl;
1553 boost::shared_ptr<AudioPlaylist> apl;
1555 if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1557 AudioPlaylist::Crossfades xfades;
1559 apl->crossfades_at (get_preferred_edit_position (), xfades);
1561 bool many = xfades.size() > 1;
1563 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1564 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1567 framepos_t framepos = (framepos_t) floor ((double) get_preferred_edit_position() * tr->speed());
1568 uint32_t regions_at = pl->count_regions_at (framepos);
1569 add_region_context_items (edit_items, regions_at > 1);
1573 add_dstream_context_items (edit_items);
1575 return &track_crossfade_context_menu;
1578 void
1579 Editor::analyze_region_selection ()
1581 if (analysis_window == 0) {
1582 analysis_window = new AnalysisWindow();
1584 if (_session != 0)
1585 analysis_window->set_session(_session);
1587 analysis_window->show_all();
1590 analysis_window->set_regionmode();
1591 analysis_window->analyze();
1593 analysis_window->present();
1596 void
1597 Editor::analyze_range_selection()
1599 if (analysis_window == 0) {
1600 analysis_window = new AnalysisWindow();
1602 if (_session != 0)
1603 analysis_window->set_session(_session);
1605 analysis_window->show_all();
1608 analysis_window->set_rangemode();
1609 analysis_window->analyze();
1611 analysis_window->present();
1614 Menu*
1615 Editor::build_track_selection_context_menu ()
1617 using namespace Menu_Helpers;
1618 MenuList& edit_items = track_selection_context_menu.items();
1619 edit_items.clear ();
1621 add_selection_context_items (edit_items);
1622 // edit_items.push_back (SeparatorElem());
1623 // add_dstream_context_items (edit_items);
1625 return &track_selection_context_menu;
1628 /** Add context menu items relevant to crossfades.
1629 * @param edit_items List to add the items to.
1631 void
1632 Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1634 using namespace Menu_Helpers;
1635 Menu *xfade_menu = manage (new Menu);
1636 MenuList& items = xfade_menu->items();
1637 xfade_menu->set_name ("ArdourContextMenu");
1638 string str;
1640 if (xfade->active()) {
1641 str = _("Mute");
1642 } else {
1643 str = _("Unmute");
1646 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1647 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1649 if (xfade->can_follow_overlap()) {
1651 if (xfade->following_overlap()) {
1652 str = _("Convert to Short");
1653 } else {
1654 str = _("Convert to Full");
1657 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1660 if (many) {
1661 str = xfade->out()->name();
1662 str += "->";
1663 str += xfade->in()->name();
1664 } else {
1665 str = _("Crossfade");
1668 edit_items.push_back (MenuElem (str, *xfade_menu));
1669 edit_items.push_back (SeparatorElem());
1672 void
1673 Editor::xfade_edit_left_region ()
1675 if (clicked_crossfadeview) {
1676 clicked_crossfadeview->left_view.show_region_editor ();
1680 void
1681 Editor::xfade_edit_right_region ()
1683 if (clicked_crossfadeview) {
1684 clicked_crossfadeview->right_view.show_region_editor ();
1688 void
1689 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, bool multiple_regions_at_position)
1691 using namespace Menu_Helpers;
1693 /* OK, stick the region submenu at the top of the list, and then add
1694 the standard items.
1697 /* we have to hack up the region name because "_" has a special
1698 meaning for menu titles.
1701 RegionSelection rs = get_regions_from_selection_and_entered ();
1703 string::size_type pos = 0;
1704 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1706 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1707 menu_item_name.replace (pos, 1, "__");
1708 pos += 2;
1711 if (_popup_region_menu_item == 0) {
1712 _popup_region_menu_item = new MenuItem (menu_item_name);
1713 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1714 _popup_region_menu_item->show ();
1715 } else {
1716 _popup_region_menu_item->set_label (menu_item_name);
1719 edit_items.push_back (*_popup_region_menu_item);
1720 if (multiple_regions_at_position && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1721 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region")->create_menu_item ()));
1723 edit_items.push_back (SeparatorElem());
1726 /** Add context menu items relevant to selection ranges.
1727 * @param edit_items List to add the items to.
1729 void
1730 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1732 using namespace Menu_Helpers;
1734 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1735 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1737 edit_items.push_back (SeparatorElem());
1738 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1740 if (!selection->regions.empty()) {
1741 edit_items.push_back (SeparatorElem());
1742 edit_items.push_back (MenuElem (_("Extend Range to End of Region"), sigc::bind (sigc::mem_fun(*this, &Editor::extend_selection_to_end_of_region), false)));
1743 edit_items.push_back (MenuElem (_("Extend Range to Start of Region"), sigc::bind (sigc::mem_fun(*this, &Editor::extend_selection_to_start_of_region), false)));
1746 edit_items.push_back (SeparatorElem());
1747 edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection)));
1748 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1750 edit_items.push_back (SeparatorElem());
1751 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1753 edit_items.push_back (SeparatorElem());
1754 edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1755 edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1757 edit_items.push_back (SeparatorElem());
1758 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1760 edit_items.push_back (SeparatorElem());
1761 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1762 edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1763 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1765 edit_items.push_back (SeparatorElem());
1766 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1767 edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1768 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1769 edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1770 edit_items.push_back (MenuElem (_("Export Range"), sigc::mem_fun(*this, &Editor::export_selection)));
1774 void
1775 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1777 using namespace Menu_Helpers;
1779 /* Playback */
1781 Menu *play_menu = manage (new Menu);
1782 MenuList& play_items = play_menu->items();
1783 play_menu->set_name ("ArdourContextMenu");
1785 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1786 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1787 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1788 play_items.push_back (SeparatorElem());
1789 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
1791 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1793 /* Selection */
1795 Menu *select_menu = manage (new Menu);
1796 MenuList& select_items = select_menu->items();
1797 select_menu->set_name ("ArdourContextMenu");
1799 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1800 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1801 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1802 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1803 select_items.push_back (SeparatorElem());
1804 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
1805 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
1806 select_items.push_back (SeparatorElem());
1807 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1808 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1809 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1810 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1811 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
1812 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
1813 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
1815 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1817 /* Cut-n-Paste */
1819 Menu *cutnpaste_menu = manage (new Menu);
1820 MenuList& cutnpaste_items = cutnpaste_menu->items();
1821 cutnpaste_menu->set_name ("ArdourContextMenu");
1823 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1824 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1825 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1827 cutnpaste_items.push_back (SeparatorElem());
1829 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
1830 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
1832 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
1834 /* Adding new material */
1836 edit_items.push_back (SeparatorElem());
1837 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
1838 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
1840 /* Nudge track */
1842 Menu *nudge_menu = manage (new Menu());
1843 MenuList& nudge_items = nudge_menu->items();
1844 nudge_menu->set_name ("ArdourContextMenu");
1846 edit_items.push_back (SeparatorElem());
1847 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1848 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1849 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1850 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1852 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1855 void
1856 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
1858 using namespace Menu_Helpers;
1860 /* Playback */
1862 Menu *play_menu = manage (new Menu);
1863 MenuList& play_items = play_menu->items();
1864 play_menu->set_name ("ArdourContextMenu");
1866 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1867 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1868 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1870 /* Selection */
1872 Menu *select_menu = manage (new Menu);
1873 MenuList& select_items = select_menu->items();
1874 select_menu->set_name ("ArdourContextMenu");
1876 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1877 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1878 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1879 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1880 select_items.push_back (SeparatorElem());
1881 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1882 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1883 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1884 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1886 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1888 /* Cut-n-Paste */
1890 Menu *cutnpaste_menu = manage (new Menu);
1891 MenuList& cutnpaste_items = cutnpaste_menu->items();
1892 cutnpaste_menu->set_name ("ArdourContextMenu");
1894 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1895 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1896 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1898 Menu *nudge_menu = manage (new Menu());
1899 MenuList& nudge_items = nudge_menu->items();
1900 nudge_menu->set_name ("ArdourContextMenu");
1902 edit_items.push_back (SeparatorElem());
1903 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1904 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1905 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1906 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1908 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1911 SnapType
1912 Editor::snap_type() const
1914 return _snap_type;
1917 SnapMode
1918 Editor::snap_mode() const
1920 return _snap_mode;
1923 void
1924 Editor::set_snap_to (SnapType st)
1926 unsigned int snap_ind = (unsigned int)st;
1928 _snap_type = st;
1930 if (snap_ind > snap_type_strings.size() - 1) {
1931 snap_ind = 0;
1932 _snap_type = (SnapType)snap_ind;
1935 string str = snap_type_strings[snap_ind];
1937 if (str != snap_type_selector.get_active_text()) {
1938 snap_type_selector.set_active_text (str);
1941 instant_save ();
1943 switch (_snap_type) {
1944 case SnapToBeatDiv32:
1945 case SnapToBeatDiv28:
1946 case SnapToBeatDiv24:
1947 case SnapToBeatDiv20:
1948 case SnapToBeatDiv16:
1949 case SnapToBeatDiv14:
1950 case SnapToBeatDiv12:
1951 case SnapToBeatDiv10:
1952 case SnapToBeatDiv8:
1953 case SnapToBeatDiv7:
1954 case SnapToBeatDiv6:
1955 case SnapToBeatDiv5:
1956 case SnapToBeatDiv4:
1957 case SnapToBeatDiv3:
1958 case SnapToBeatDiv2:
1959 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
1960 update_tempo_based_rulers ();
1961 break;
1963 case SnapToRegionStart:
1964 case SnapToRegionEnd:
1965 case SnapToRegionSync:
1966 case SnapToRegionBoundary:
1967 build_region_boundary_cache ();
1968 break;
1970 default:
1971 /* relax */
1972 break;
1975 SnapChanged (); /* EMIT SIGNAL */
1978 void
1979 Editor::set_snap_mode (SnapMode mode)
1981 _snap_mode = mode;
1982 string str = snap_mode_strings[(int)mode];
1984 if (str != snap_mode_selector.get_active_text ()) {
1985 snap_mode_selector.set_active_text (str);
1988 instant_save ();
1990 void
1991 Editor::set_edit_point_preference (EditPoint ep, bool force)
1993 bool changed = (_edit_point != ep);
1995 _edit_point = ep;
1996 string str = edit_point_strings[(int)ep];
1998 if (str != edit_point_selector.get_active_text ()) {
1999 edit_point_selector.set_active_text (str);
2002 set_canvas_cursor ();
2004 if (!force && !changed) {
2005 return;
2008 const char* action=NULL;
2010 switch (_edit_point) {
2011 case EditAtPlayhead:
2012 action = "edit-at-playhead";
2013 break;
2014 case EditAtSelectedMarker:
2015 action = "edit-at-marker";
2016 break;
2017 case EditAtMouse:
2018 action = "edit-at-mouse";
2019 break;
2022 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2023 if (act) {
2024 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2027 framepos_t foo;
2028 bool in_track_canvas;
2030 if (!mouse_frame (foo, in_track_canvas)) {
2031 in_track_canvas = false;
2034 reset_canvas_action_sensitivity (in_track_canvas);
2036 instant_save ();
2040 Editor::set_state (const XMLNode& node, int /*version*/)
2042 const XMLProperty* prop;
2043 XMLNode* geometry;
2044 int x, y, xoff, yoff;
2045 Gdk::Geometry g;
2047 if ((prop = node.property ("id")) != 0) {
2048 _id = prop->value ();
2051 g.base_width = default_width;
2052 g.base_height = default_height;
2053 x = 1;
2054 y = 1;
2055 xoff = 0;
2056 yoff = 21;
2058 if ((geometry = find_named_node (node, "geometry")) != 0) {
2060 XMLProperty* prop;
2062 if ((prop = geometry->property("x_size")) == 0) {
2063 prop = geometry->property ("x-size");
2065 if (prop) {
2066 g.base_width = atoi(prop->value());
2068 if ((prop = geometry->property("y_size")) == 0) {
2069 prop = geometry->property ("y-size");
2071 if (prop) {
2072 g.base_height = atoi(prop->value());
2075 if ((prop = geometry->property ("x_pos")) == 0) {
2076 prop = geometry->property ("x-pos");
2078 if (prop) {
2079 x = atoi (prop->value());
2082 if ((prop = geometry->property ("y_pos")) == 0) {
2083 prop = geometry->property ("y-pos");
2085 if (prop) {
2086 y = atoi (prop->value());
2089 if ((prop = geometry->property ("x_off")) == 0) {
2090 prop = geometry->property ("x-off");
2092 if (prop) {
2093 xoff = atoi (prop->value());
2095 if ((prop = geometry->property ("y_off")) == 0) {
2096 prop = geometry->property ("y-off");
2098 if (prop) {
2099 yoff = atoi (prop->value());
2103 set_default_size (g.base_width, g.base_height);
2104 move (x, y);
2106 if (_session && (prop = node.property ("playhead"))) {
2107 framepos_t pos;
2108 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2109 playhead_cursor->set_position (pos);
2110 } else {
2111 playhead_cursor->set_position (0);
2114 if ((prop = node.property ("mixer-width"))) {
2115 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2118 if ((prop = node.property ("zoom-focus"))) {
2119 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2122 if ((prop = node.property ("zoom"))) {
2123 reset_zoom (PBD::atof (prop->value()));
2124 } else {
2125 reset_zoom (frames_per_unit);
2128 if ((prop = node.property ("snap-to"))) {
2129 set_snap_to ((SnapType) atoi (prop->value()));
2132 if ((prop = node.property ("snap-mode"))) {
2133 set_snap_mode ((SnapMode) atoi (prop->value()));
2136 if ((prop = node.property ("mouse-mode"))) {
2137 MouseMode m = str2mousemode(prop->value());
2138 set_mouse_mode (m, true);
2139 } else {
2140 set_mouse_mode (MouseObject, true);
2143 if ((prop = node.property ("left-frame")) != 0){
2144 framepos_t pos;
2145 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2146 reset_x_origin (pos);
2150 if ((prop = node.property ("y-origin")) != 0) {
2151 reset_y_origin (atof (prop->value ()));
2154 if ((prop = node.property ("internal-edit"))) {
2155 bool yn = string_is_affirmative (prop->value());
2156 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2157 if (act) {
2158 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2159 tact->set_active (!yn);
2160 tact->set_active (yn);
2164 if ((prop = node.property ("join-object-range"))) {
2165 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2168 if ((prop = node.property ("edit-point"))) {
2169 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2172 if ((prop = node.property ("show-measures"))) {
2173 bool yn = string_is_affirmative (prop->value());
2174 _show_measures = yn;
2175 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2176 if (act) {
2177 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2178 /* do it twice to force the change */
2179 tact->set_active (!yn);
2180 tact->set_active (yn);
2184 if ((prop = node.property ("follow-playhead"))) {
2185 bool yn = string_is_affirmative (prop->value());
2186 set_follow_playhead (yn);
2187 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2188 if (act) {
2189 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2190 if (tact->get_active() != yn) {
2191 tact->set_active (yn);
2196 if ((prop = node.property ("stationary-playhead"))) {
2197 bool yn = (prop->value() == "yes");
2198 set_stationary_playhead (yn);
2199 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2200 if (act) {
2201 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2202 if (tact->get_active() != yn) {
2203 tact->set_active (yn);
2208 if ((prop = node.property ("region-list-sort-type"))) {
2209 RegionListSortType st;
2210 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2213 if ((prop = node.property ("xfades-visible"))) {
2214 bool yn = string_is_affirmative (prop->value());
2215 _xfade_visibility = !yn;
2216 // set_xfade_visibility (yn);
2219 if ((prop = node.property ("show-editor-mixer"))) {
2221 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2222 assert (act);
2224 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2225 bool yn = string_is_affirmative (prop->value());
2227 /* do it twice to force the change */
2229 tact->set_active (!yn);
2230 tact->set_active (yn);
2233 if ((prop = node.property ("show-editor-list"))) {
2235 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2236 assert (act);
2238 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2239 bool yn = string_is_affirmative (prop->value());
2241 /* do it twice to force the change */
2243 tact->set_active (!yn);
2244 tact->set_active (yn);
2247 if ((prop = node.property (X_("editor-list-page")))) {
2248 the_notebook.set_current_page (atoi (prop->value ()));
2251 if ((prop = node.property (X_("show-marker-lines")))) {
2252 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2253 assert (act);
2254 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2255 bool yn = string_is_affirmative (prop->value ());
2257 tact->set_active (!yn);
2258 tact->set_active (yn);
2261 XMLNodeList children = node.children ();
2262 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2263 selection->set_state (**i, Stateful::current_state_version);
2264 _regions->set_state (**i);
2267 return 0;
2270 XMLNode&
2271 Editor::get_state ()
2273 XMLNode* node = new XMLNode ("Editor");
2274 char buf[32];
2276 _id.print (buf, sizeof (buf));
2277 node->add_property ("id", buf);
2279 if (is_realized()) {
2280 Glib::RefPtr<Gdk::Window> win = get_window();
2282 int x, y, xoff, yoff, width, height;
2283 win->get_root_origin(x, y);
2284 win->get_position(xoff, yoff);
2285 win->get_size(width, height);
2287 XMLNode* geometry = new XMLNode ("geometry");
2289 snprintf(buf, sizeof(buf), "%d", width);
2290 geometry->add_property("x-size", string(buf));
2291 snprintf(buf, sizeof(buf), "%d", height);
2292 geometry->add_property("y-size", string(buf));
2293 snprintf(buf, sizeof(buf), "%d", x);
2294 geometry->add_property("x-pos", string(buf));
2295 snprintf(buf, sizeof(buf), "%d", y);
2296 geometry->add_property("y-pos", string(buf));
2297 snprintf(buf, sizeof(buf), "%d", xoff);
2298 geometry->add_property("x-off", string(buf));
2299 snprintf(buf, sizeof(buf), "%d", yoff);
2300 geometry->add_property("y-off", string(buf));
2301 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2302 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2303 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2304 geometry->add_property("edit-vertical-pane-pos", string(buf));
2306 node->add_child_nocopy (*geometry);
2309 maybe_add_mixer_strip_width (*node);
2311 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2312 node->add_property ("zoom-focus", buf);
2313 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2314 node->add_property ("zoom", buf);
2315 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2316 node->add_property ("snap-to", buf);
2317 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2318 node->add_property ("snap-mode", buf);
2320 node->add_property ("edit-point", enum_2_string (_edit_point));
2322 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2323 node->add_property ("playhead", buf);
2324 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2325 node->add_property ("left-frame", buf);
2326 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2327 node->add_property ("y-origin", buf);
2329 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2330 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2331 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2332 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2333 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2334 node->add_property ("mouse-mode", enum2str(mouse_mode));
2335 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2336 node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2338 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2339 if (act) {
2340 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2341 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2344 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2345 if (act) {
2346 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2347 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2350 snprintf (buf, sizeof (buf), "%d", the_notebook.get_current_page ());
2351 node->add_property (X_("editor-list-page"), buf);
2353 node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no");
2355 node->add_child_nocopy (selection->get_state ());
2356 node->add_child_nocopy (_regions->get_state ());
2358 return *node;
2363 /** @param y y offset from the top of all trackviews.
2364 * @return pair: TimeAxisView that y is over, layer index.
2365 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2366 * in stacked region display mode, otherwise 0.
2368 std::pair<TimeAxisView *, layer_t>
2369 Editor::trackview_by_y_position (double y)
2371 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2373 std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
2374 if (r.first) {
2375 return r;
2379 return std::make_pair ( (TimeAxisView *) 0, 0);
2382 /** Snap a position to the grid, if appropriate, taking into account current
2383 * grid settings and also the state of any snap modifier keys that may be pressed.
2384 * @param start Position to snap.
2385 * @param event Event to get current key modifier information from, or 0.
2387 void
2388 Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2390 if (!_session || !event) {
2391 return;
2394 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2395 if (_snap_mode == SnapOff) {
2396 snap_to_internal (start, direction, for_mark);
2398 } else {
2399 if (_snap_mode != SnapOff) {
2400 snap_to_internal (start, direction, for_mark);
2405 void
2406 Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark)
2408 if (!_session || _snap_mode == SnapOff) {
2409 return;
2412 snap_to_internal (start, direction, for_mark);
2415 void
2416 Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /*for_mark*/)
2418 const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2419 framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2421 switch (_snap_type) {
2422 case SnapToTimecodeFrame:
2423 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2424 start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2425 } else {
2426 start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2428 break;
2430 case SnapToTimecodeSeconds:
2431 if (_session->config.get_timecode_offset_negative()) {
2432 start += _session->config.get_timecode_offset ();
2433 } else {
2434 start -= _session->config.get_timecode_offset ();
2436 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2437 start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2438 } else {
2439 start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2442 if (_session->config.get_timecode_offset_negative()) {
2443 start -= _session->config.get_timecode_offset ();
2444 } else {
2445 start += _session->config.get_timecode_offset ();
2447 break;
2449 case SnapToTimecodeMinutes:
2450 if (_session->config.get_timecode_offset_negative()) {
2451 start += _session->config.get_timecode_offset ();
2452 } else {
2453 start -= _session->config.get_timecode_offset ();
2455 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2456 start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2457 } else {
2458 start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2460 if (_session->config.get_timecode_offset_negative()) {
2461 start -= _session->config.get_timecode_offset ();
2462 } else {
2463 start += _session->config.get_timecode_offset ();
2465 break;
2466 default:
2467 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2468 /*NOTREACHED*/
2472 void
2473 Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark)
2475 const framepos_t one_second = _session->frame_rate();
2476 const framepos_t one_minute = _session->frame_rate() * 60;
2477 framepos_t presnap = start;
2478 framepos_t before;
2479 framepos_t after;
2481 switch (_snap_type) {
2482 case SnapToTimecodeFrame:
2483 case SnapToTimecodeSeconds:
2484 case SnapToTimecodeMinutes:
2485 return timecode_snap_to_internal (start, direction, for_mark);
2487 case SnapToCDFrame:
2488 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2489 start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2490 } else {
2491 start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2493 break;
2495 case SnapToSeconds:
2496 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2497 start = (framepos_t) ceil ((double) start / one_second) * one_second;
2498 } else {
2499 start = (framepos_t) floor ((double) start / one_second) * one_second;
2501 break;
2503 case SnapToMinutes:
2504 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2505 start = (framepos_t) ceil ((double) start / one_minute) * one_minute;
2506 } else {
2507 start = (framepos_t) floor ((double) start / one_minute) * one_minute;
2509 break;
2511 case SnapToBar:
2512 start = _session->tempo_map().round_to_bar (start, direction);
2513 break;
2515 case SnapToBeat:
2516 start = _session->tempo_map().round_to_beat (start, direction);
2517 break;
2519 case SnapToBeatDiv32:
2520 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2521 break;
2522 case SnapToBeatDiv28:
2523 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2524 break;
2525 case SnapToBeatDiv24:
2526 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2527 break;
2528 case SnapToBeatDiv20:
2529 start = _session->tempo_map().round_to_beat_subdivision (start, 20, direction);
2530 break;
2531 case SnapToBeatDiv16:
2532 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2533 break;
2534 case SnapToBeatDiv14:
2535 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2536 break;
2537 case SnapToBeatDiv12:
2538 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2539 break;
2540 case SnapToBeatDiv10:
2541 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2542 break;
2543 case SnapToBeatDiv8:
2544 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2545 break;
2546 case SnapToBeatDiv7:
2547 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2548 break;
2549 case SnapToBeatDiv6:
2550 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2551 break;
2552 case SnapToBeatDiv5:
2553 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2554 break;
2555 case SnapToBeatDiv4:
2556 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2557 break;
2558 case SnapToBeatDiv3:
2559 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2560 break;
2561 case SnapToBeatDiv2:
2562 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2563 break;
2565 case SnapToMark:
2566 if (for_mark) {
2567 return;
2570 _session->locations()->marks_either_side (start, before, after);
2572 if (before == max_framepos) {
2573 start = after;
2574 } else if (after == max_framepos) {
2575 start = before;
2576 } else if (before != max_framepos && after != max_framepos) {
2577 /* have before and after */
2578 if ((start - before) < (after - start)) {
2579 start = before;
2580 } else {
2581 start = after;
2585 break;
2587 case SnapToRegionStart:
2588 case SnapToRegionEnd:
2589 case SnapToRegionSync:
2590 case SnapToRegionBoundary:
2591 if (!region_boundary_cache.empty()) {
2593 vector<framepos_t>::iterator prev = region_boundary_cache.end ();
2594 vector<framepos_t>::iterator next = region_boundary_cache.end ();
2596 if (direction > 0) {
2597 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2598 } else {
2599 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2602 if (next != region_boundary_cache.begin ()) {
2603 prev = next;
2604 prev--;
2607 framepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2608 framepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2610 if (start > (p + n) / 2) {
2611 start = n;
2612 } else {
2613 start = p;
2616 break;
2619 switch (_snap_mode) {
2620 case SnapNormal:
2621 return;
2623 case SnapMagnetic:
2625 if (presnap > start) {
2626 if (presnap > (start + unit_to_frame(snap_threshold))) {
2627 start = presnap;
2630 } else if (presnap < start) {
2631 if (presnap < (start - unit_to_frame(snap_threshold))) {
2632 start = presnap;
2636 default:
2637 /* handled at entry */
2638 return;
2644 void
2645 Editor::setup_toolbar ()
2647 string pixmap_path;
2649 /* Mode Buttons (tool selection) */
2651 mouse_move_button.set_relief(Gtk::RELIEF_NONE);
2652 mouse_select_button.set_relief(Gtk::RELIEF_NONE);
2653 mouse_gain_button.set_relief(Gtk::RELIEF_NONE);
2654 mouse_zoom_button.set_relief(Gtk::RELIEF_NONE);
2655 mouse_timefx_button.set_relief(Gtk::RELIEF_NONE);
2656 mouse_audition_button.set_relief(Gtk::RELIEF_NONE);
2657 // internal_edit_button.set_relief(Gtk::RELIEF_NONE);
2658 join_object_range_button.set_relief(Gtk::RELIEF_NONE);
2660 HBox* mode_box = manage(new HBox);
2661 mode_box->set_border_width (2);
2662 mode_box->set_spacing(4);
2664 /* table containing mode buttons */
2666 HBox* mouse_mode_button_box = manage (new HBox ());
2668 if (Profile->get_sae()) {
2669 mouse_mode_button_box->pack_start (mouse_move_button);
2670 } else {
2671 mouse_mode_button_box->pack_start (mouse_move_button);
2672 mouse_mode_button_box->pack_start (join_object_range_button);
2673 mouse_mode_button_box->pack_start (mouse_select_button);
2676 mouse_mode_button_box->pack_start (mouse_zoom_button);
2678 if (!Profile->get_sae()) {
2679 mouse_mode_button_box->pack_start (mouse_gain_button);
2682 mouse_mode_button_box->pack_start (mouse_timefx_button);
2683 mouse_mode_button_box->pack_start (mouse_audition_button);
2684 mouse_mode_button_box->pack_start (internal_edit_button);
2686 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2687 if (!Profile->get_sae()) {
2688 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2690 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2692 edit_mode_selector.set_name ("EditModeSelector");
2693 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2694 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2696 mode_box->pack_start (edit_mode_selector);
2697 mode_box->pack_start (*mouse_mode_button_box);
2699 _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2700 _mouse_mode_tearoff->set_name ("MouseModeBase");
2701 _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2703 if (Profile->get_sae()) {
2704 _mouse_mode_tearoff->set_can_be_torn_off (false);
2707 _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2708 &_mouse_mode_tearoff->tearoff_window()));
2709 _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2710 &_mouse_mode_tearoff->tearoff_window(), 1));
2711 _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2712 &_mouse_mode_tearoff->tearoff_window()));
2713 _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2714 &_mouse_mode_tearoff->tearoff_window(), 1));
2716 mouse_move_button.set_mode (false);
2717 mouse_select_button.set_mode (false);
2718 mouse_gain_button.set_mode (false);
2719 mouse_zoom_button.set_mode (false);
2720 mouse_timefx_button.set_mode (false);
2721 mouse_audition_button.set_mode (false);
2722 join_object_range_button.set_mode (false);
2724 mouse_move_button.set_name ("MouseModeButton");
2725 mouse_select_button.set_name ("MouseModeButton");
2726 mouse_gain_button.set_name ("MouseModeButton");
2727 mouse_zoom_button.set_name ("MouseModeButton");
2728 mouse_timefx_button.set_name ("MouseModeButton");
2729 mouse_audition_button.set_name ("MouseModeButton");
2730 internal_edit_button.set_name ("MouseModeButton");
2731 join_object_range_button.set_name ("MouseModeButton");
2733 mouse_move_button.unset_flags (CAN_FOCUS);
2734 mouse_select_button.unset_flags (CAN_FOCUS);
2735 mouse_gain_button.unset_flags (CAN_FOCUS);
2736 mouse_zoom_button.unset_flags (CAN_FOCUS);
2737 mouse_timefx_button.unset_flags (CAN_FOCUS);
2738 mouse_audition_button.unset_flags (CAN_FOCUS);
2739 internal_edit_button.unset_flags (CAN_FOCUS);
2740 join_object_range_button.unset_flags (CAN_FOCUS);
2742 /* Zoom */
2744 _zoom_box.set_spacing (1);
2745 _zoom_box.set_border_width (0);
2747 zoom_in_button.set_name ("EditorTimeButton");
2748 zoom_in_button.set_image (*(manage (new Image (Stock::ZOOM_IN, Gtk::ICON_SIZE_MENU))));
2749 zoom_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), false));
2751 zoom_out_button.set_name ("EditorTimeButton");
2752 zoom_out_button.set_image (*(manage (new Image (Stock::ZOOM_OUT, Gtk::ICON_SIZE_MENU))));
2753 zoom_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), true));
2755 zoom_out_full_button.set_name ("EditorTimeButton");
2756 zoom_out_full_button.set_image (*(manage (new Image (Stock::ZOOM_100, Gtk::ICON_SIZE_MENU))));
2757 zoom_out_full_button.signal_clicked().connect (sigc::mem_fun(*this, &Editor::temporal_zoom_session));
2759 zoom_focus_selector.set_name ("ZoomFocusSelector");
2760 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
2761 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2763 _zoom_box.pack_start (zoom_out_button, false, false);
2764 _zoom_box.pack_start (zoom_in_button, false, false);
2765 _zoom_box.pack_start (zoom_out_full_button, false, false);
2767 _zoom_box.pack_start (zoom_focus_selector);
2769 /* Track zoom buttons */
2770 tav_expand_button.set_name ("TrackHeightButton");
2771 tav_expand_button.set_size_request(-1,20);
2772 tav_expand_button.add (*(manage (new Image (::get_icon("tav_exp")))));
2773 tav_expand_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), true));
2775 tav_shrink_button.set_name ("TrackHeightButton");
2776 tav_shrink_button.set_size_request(-1,20);
2777 tav_shrink_button.add (*(manage (new Image (::get_icon("tav_shrink")))));
2778 tav_shrink_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::tav_zoom_step), false));
2780 _zoom_box.pack_start (tav_shrink_button);
2781 _zoom_box.pack_start (tav_expand_button);
2783 _zoom_tearoff = manage (new TearOff (_zoom_box));
2785 _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2786 &_zoom_tearoff->tearoff_window()));
2787 _zoom_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2788 &_zoom_tearoff->tearoff_window(), 0));
2789 _zoom_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2790 &_zoom_tearoff->tearoff_window()));
2791 _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2792 &_zoom_tearoff->tearoff_window(), 0));
2794 snap_box.set_spacing (1);
2795 snap_box.set_border_width (2);
2797 snap_type_selector.set_name ("SnapTypeSelector");
2798 set_popdown_strings (snap_type_selector, snap_type_strings, true);
2799 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2801 snap_mode_selector.set_name ("SnapModeSelector");
2802 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
2803 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2805 edit_point_selector.set_name ("EditPointSelector");
2806 set_popdown_strings (edit_point_selector, edit_point_strings, true);
2807 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2809 snap_box.pack_start (snap_mode_selector, false, false);
2810 snap_box.pack_start (snap_type_selector, false, false);
2811 snap_box.pack_start (edit_point_selector, false, false);
2813 /* Nudge */
2815 HBox *nudge_box = manage (new HBox);
2816 nudge_box->set_spacing(1);
2817 nudge_box->set_border_width (2);
2819 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2820 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2822 nudge_box->pack_start (nudge_backward_button, false, false);
2823 nudge_box->pack_start (nudge_forward_button, false, false);
2824 nudge_box->pack_start (nudge_clock, false, false);
2827 /* Pack everything in... */
2829 HBox* hbox = manage (new HBox);
2830 hbox->set_spacing(10);
2832 _tools_tearoff = manage (new TearOff (*hbox));
2833 _tools_tearoff->set_name ("MouseModeBase");
2834 _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
2836 if (Profile->get_sae()) {
2837 _tools_tearoff->set_can_be_torn_off (false);
2840 _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2841 &_tools_tearoff->tearoff_window()));
2842 _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2843 &_tools_tearoff->tearoff_window(), 0));
2844 _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2845 &_tools_tearoff->tearoff_window()));
2846 _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2847 &_tools_tearoff->tearoff_window(), 0));
2849 toolbar_hbox.set_spacing (10);
2850 toolbar_hbox.set_border_width (1);
2852 toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
2853 toolbar_hbox.pack_start (*_zoom_tearoff, false, false);
2854 toolbar_hbox.pack_start (*_tools_tearoff, false, false);
2856 hbox->pack_start (snap_box, false, false);
2857 hbox->pack_start (*nudge_box, false, false);
2858 hbox->pack_start (panic_box, false, false);
2860 hbox->show_all ();
2862 toolbar_base.set_name ("ToolBarBase");
2863 toolbar_base.add (toolbar_hbox);
2865 toolbar_frame.set_shadow_type (SHADOW_OUT);
2866 toolbar_frame.set_name ("BaseFrame");
2867 toolbar_frame.add (toolbar_base);
2869 DPIReset.connect (sigc::mem_fun (*this, &Editor::resize_text_widgets));
2872 void
2873 Editor::setup_tooltips ()
2875 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
2876 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain"));
2877 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
2878 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
2879 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2880 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
2881 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
2882 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
2883 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
2884 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
2885 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
2886 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
2887 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
2888 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
2889 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
2890 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
2891 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
2892 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
2893 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
2894 ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
2895 ARDOUR_UI::instance()->set_tip (midi_panic_button, _("Send note off and reset controller messages on all MIDI channels"));
2896 ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode"));
2899 void
2900 Editor::midi_panic ()
2902 cerr << "MIDI panic\n";
2904 if (_session) {
2905 _session->midi_panic();
2909 void
2910 Editor::setup_midi_toolbar ()
2912 RefPtr<Action> act;
2914 /* Midi sound notes */
2915 midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
2916 midi_sound_notes.set_relief(Gtk::RELIEF_NONE);
2917 midi_sound_notes.unset_flags (CAN_FOCUS);
2919 /* Panic */
2921 act = ActionManager::get_action (X_("MIDI"), X_("panic"));
2922 midi_panic_button.set_name("MidiPanicButton");
2923 act->connect_proxy (midi_panic_button);
2925 panic_box.pack_start (midi_sound_notes , true, true);
2926 panic_box.pack_start (midi_panic_button, true, true);
2930 Editor::convert_drop_to_paths (
2931 vector<string>& paths,
2932 const RefPtr<Gdk::DragContext>& /*context*/,
2933 gint /*x*/,
2934 gint /*y*/,
2935 const SelectionData& data,
2936 guint /*info*/,
2937 guint /*time*/)
2939 if (_session == 0) {
2940 return -1;
2943 vector<string> uris = data.get_uris();
2945 if (uris.empty()) {
2947 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
2948 are actually URI lists. So do it by hand.
2951 if (data.get_target() != "text/plain") {
2952 return -1;
2955 /* Parse the "uri-list" format that Nautilus provides,
2956 where each pathname is delimited by \r\n.
2958 THERE MAY BE NO NULL TERMINATING CHAR!!!
2961 string txt = data.get_text();
2962 const char* p;
2963 const char* q;
2965 p = (const char *) malloc (txt.length() + 1);
2966 txt.copy ((char *) p, txt.length(), 0);
2967 ((char*)p)[txt.length()] = '\0';
2969 while (p)
2971 if (*p != '#')
2973 while (g_ascii_isspace (*p))
2974 p++;
2976 q = p;
2977 while (*q && (*q != '\n') && (*q != '\r')) {
2978 q++;
2981 if (q > p)
2983 q--;
2984 while (q > p && g_ascii_isspace (*q))
2985 q--;
2987 if (q > p)
2989 uris.push_back (string (p, q - p + 1));
2993 p = strchr (p, '\n');
2994 if (p)
2995 p++;
2998 free ((void*)p);
3000 if (uris.empty()) {
3001 return -1;
3005 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3007 if ((*i).substr (0,7) == "file://") {
3009 string p = *i;
3010 PBD::url_decode (p);
3012 // scan forward past three slashes
3014 string::size_type slashcnt = 0;
3015 string::size_type n = 0;
3016 string::iterator x = p.begin();
3018 while (slashcnt < 3 && x != p.end()) {
3019 if ((*x) == '/') {
3020 slashcnt++;
3021 } else if (slashcnt == 3) {
3022 break;
3024 ++n;
3025 ++x;
3028 if (slashcnt != 3 || x == p.end()) {
3029 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3030 continue;
3033 paths.push_back (p.substr (n - 1));
3037 return 0;
3040 void
3041 Editor::new_tempo_section ()
3046 void
3047 Editor::map_transport_state ()
3049 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3051 if (_session && _session->transport_stopped()) {
3052 have_pending_keyboard_selection = false;
3055 update_loop_range_view (true);
3058 /* UNDO/REDO */
3060 Editor::State::State (PublicEditor const * e)
3062 selection = new Selection (e);
3065 Editor::State::~State ()
3067 delete selection;
3070 void
3071 Editor::begin_reversible_command (string name)
3073 if (_session) {
3074 _session->begin_reversible_command (name);
3078 void
3079 Editor::commit_reversible_command ()
3081 if (_session) {
3082 _session->commit_reversible_command ();
3086 void
3087 Editor::set_route_group_solo (Route& route, bool yn)
3089 RouteGroup *route_group;
3091 if ((route_group = route.route_group()) != 0) {
3092 route_group->apply (&Route::set_solo, yn, this);
3093 } else {
3094 route.set_solo (yn, this);
3098 void
3099 Editor::set_route_group_mute (Route& route, bool yn)
3101 RouteGroup *route_group = 0;
3103 if ((route_group = route.route_group()) != 0) {
3104 route_group->apply (&Route::set_mute, yn, this);
3105 } else {
3106 route.set_mute (yn, this);
3110 void
3111 Editor::history_changed ()
3113 string label;
3115 if (undo_action && _session) {
3116 if (_session->undo_depth() == 0) {
3117 label = _("Undo");
3118 } else {
3119 label = string_compose(_("Undo (%1)"), _session->next_undo());
3121 undo_action->property_label() = label;
3124 if (redo_action && _session) {
3125 if (_session->redo_depth() == 0) {
3126 label = _("Redo");
3127 } else {
3128 label = string_compose(_("Redo (%1)"), _session->next_redo());
3130 redo_action->property_label() = label;
3134 void
3135 Editor::duplicate_dialog (bool with_dialog)
3137 float times = 1.0f;
3139 if (mouse_mode == MouseRange) {
3140 if (selection->time.length() == 0) {
3141 return;
3145 RegionSelection rs = get_regions_from_selection_and_entered ();
3147 if (mouse_mode != MouseRange && rs.empty()) {
3148 return;
3151 if (with_dialog) {
3153 ArdourDialog win (_("Duplicate"));
3154 Label label (_("Number of duplications:"));
3155 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3156 SpinButton spinner (adjustment, 0.0, 1);
3157 HBox hbox;
3159 win.get_vbox()->set_spacing (12);
3160 win.get_vbox()->pack_start (hbox);
3161 hbox.set_border_width (6);
3162 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3164 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3165 place, visually. so do this by hand.
3168 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3169 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3170 spinner.grab_focus();
3172 hbox.show ();
3173 label.show ();
3174 spinner.show ();
3176 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3177 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3178 win.set_default_response (RESPONSE_ACCEPT);
3180 win.set_position (WIN_POS_MOUSE);
3182 spinner.grab_focus ();
3184 switch (win.run ()) {
3185 case RESPONSE_ACCEPT:
3186 break;
3187 default:
3188 return;
3191 times = adjustment.get_value();
3194 if (mouse_mode == MouseRange) {
3195 duplicate_selection (times);
3196 } else {
3197 duplicate_some_regions (rs, times);
3201 void
3202 Editor::show_verbose_canvas_cursor ()
3204 verbose_canvas_cursor->raise_to_top();
3205 verbose_canvas_cursor->show();
3206 verbose_cursor_visible = true;
3209 void
3210 Editor::hide_verbose_canvas_cursor ()
3212 verbose_canvas_cursor->hide();
3213 verbose_cursor_visible = false;
3216 double
3217 Editor::clamp_verbose_cursor_x (double x)
3219 if (x < 0) {
3220 x = 0;
3221 } else {
3222 x = min (_canvas_width - 200.0, x);
3224 return x;
3227 double
3228 Editor::clamp_verbose_cursor_y (double y)
3230 if (y < canvas_timebars_vsize) {
3231 y = canvas_timebars_vsize;
3232 } else {
3233 y = min (_canvas_height - 50, y);
3235 return y;
3238 void
3239 Editor::show_verbose_canvas_cursor_with (const string & txt, int32_t xoffset, int32_t yoffset)
3241 verbose_canvas_cursor->property_text() = txt.c_str();
3243 int x, y;
3244 double wx, wy;
3246 track_canvas->get_pointer (x, y);
3247 track_canvas->window_to_world (x, y, wx, wy);
3249 wx += xoffset;
3250 wy += yoffset;
3252 /* don't get too close to the edge */
3253 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (wx);
3254 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (wy);
3256 show_verbose_canvas_cursor ();
3259 void
3260 Editor::set_verbose_canvas_cursor (const string & txt, double x, double y)
3262 verbose_canvas_cursor->property_text() = txt.c_str();
3263 /* don't get too close to the edge */
3264 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (x);
3265 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (y);
3268 void
3269 Editor::set_verbose_canvas_cursor_text (const string & txt)
3271 verbose_canvas_cursor->property_text() = txt.c_str();
3274 void
3275 Editor::set_edit_mode (EditMode m)
3277 Config->set_edit_mode (m);
3280 void
3281 Editor::cycle_edit_mode ()
3283 switch (Config->get_edit_mode()) {
3284 case Slide:
3285 if (Profile->get_sae()) {
3286 Config->set_edit_mode (Lock);
3287 } else {
3288 Config->set_edit_mode (Splice);
3290 break;
3291 case Splice:
3292 Config->set_edit_mode (Lock);
3293 break;
3294 case Lock:
3295 Config->set_edit_mode (Slide);
3296 break;
3300 void
3301 Editor::edit_mode_selection_done ()
3303 string s = edit_mode_selector.get_active_text ();
3305 if (!s.empty()) {
3306 Config->set_edit_mode (string_to_edit_mode (s));
3310 void
3311 Editor::snap_type_selection_done ()
3313 string choice = snap_type_selector.get_active_text();
3314 SnapType snaptype = SnapToBeat;
3316 if (choice == _("Beats/2")) {
3317 snaptype = SnapToBeatDiv2;
3318 } else if (choice == _("Beats/3")) {
3319 snaptype = SnapToBeatDiv3;
3320 } else if (choice == _("Beats/4")) {
3321 snaptype = SnapToBeatDiv4;
3322 } else if (choice == _("Beats/5")) {
3323 snaptype = SnapToBeatDiv5;
3324 } else if (choice == _("Beats/6")) {
3325 snaptype = SnapToBeatDiv6;
3326 } else if (choice == _("Beats/7")) {
3327 snaptype = SnapToBeatDiv7;
3328 } else if (choice == _("Beats/8")) {
3329 snaptype = SnapToBeatDiv8;
3330 } else if (choice == _("Beats/10")) {
3331 snaptype = SnapToBeatDiv10;
3332 } else if (choice == _("Beats/12")) {
3333 snaptype = SnapToBeatDiv12;
3334 } else if (choice == _("Beats/14")) {
3335 snaptype = SnapToBeatDiv14;
3336 } else if (choice == _("Beats/16")) {
3337 snaptype = SnapToBeatDiv16;
3338 } else if (choice == _("Beats/20")) {
3339 snaptype = SnapToBeatDiv20;
3340 } else if (choice == _("Beats/24")) {
3341 snaptype = SnapToBeatDiv24;
3342 } else if (choice == _("Beats/28")) {
3343 snaptype = SnapToBeatDiv28;
3344 } else if (choice == _("Beats/32")) {
3345 snaptype = SnapToBeatDiv32;
3346 } else if (choice == _("Beats")) {
3347 snaptype = SnapToBeat;
3348 } else if (choice == _("Bars")) {
3349 snaptype = SnapToBar;
3350 } else if (choice == _("Marks")) {
3351 snaptype = SnapToMark;
3352 } else if (choice == _("Region starts")) {
3353 snaptype = SnapToRegionStart;
3354 } else if (choice == _("Region ends")) {
3355 snaptype = SnapToRegionEnd;
3356 } else if (choice == _("Region bounds")) {
3357 snaptype = SnapToRegionBoundary;
3358 } else if (choice == _("Region syncs")) {
3359 snaptype = SnapToRegionSync;
3360 } else if (choice == _("CD Frames")) {
3361 snaptype = SnapToCDFrame;
3362 } else if (choice == _("Timecode Frames")) {
3363 snaptype = SnapToTimecodeFrame;
3364 } else if (choice == _("Timecode Seconds")) {
3365 snaptype = SnapToTimecodeSeconds;
3366 } else if (choice == _("Timecode Minutes")) {
3367 snaptype = SnapToTimecodeMinutes;
3368 } else if (choice == _("Seconds")) {
3369 snaptype = SnapToSeconds;
3370 } else if (choice == _("Minutes")) {
3371 snaptype = SnapToMinutes;
3374 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3375 if (ract) {
3376 ract->set_active ();
3380 void
3381 Editor::snap_mode_selection_done ()
3383 string choice = snap_mode_selector.get_active_text();
3384 SnapMode mode = SnapNormal;
3386 if (choice == _("No Grid")) {
3387 mode = SnapOff;
3388 } else if (choice == _("Grid")) {
3389 mode = SnapNormal;
3390 } else if (choice == _("Magnetic")) {
3391 mode = SnapMagnetic;
3394 RefPtr<RadioAction> ract = snap_mode_action (mode);
3396 if (ract) {
3397 ract->set_active (true);
3401 void
3402 Editor::cycle_edit_point (bool with_marker)
3404 switch (_edit_point) {
3405 case EditAtMouse:
3406 set_edit_point_preference (EditAtPlayhead);
3407 break;
3408 case EditAtPlayhead:
3409 if (with_marker) {
3410 set_edit_point_preference (EditAtSelectedMarker);
3411 } else {
3412 set_edit_point_preference (EditAtMouse);
3414 break;
3415 case EditAtSelectedMarker:
3416 set_edit_point_preference (EditAtMouse);
3417 break;
3421 void
3422 Editor::edit_point_selection_done ()
3424 string choice = edit_point_selector.get_active_text();
3425 EditPoint ep = EditAtSelectedMarker;
3427 if (choice == _("Marker")) {
3428 set_edit_point_preference (EditAtSelectedMarker);
3429 } else if (choice == _("Playhead")) {
3430 set_edit_point_preference (EditAtPlayhead);
3431 } else {
3432 set_edit_point_preference (EditAtMouse);
3435 RefPtr<RadioAction> ract = edit_point_action (ep);
3437 if (ract) {
3438 ract->set_active (true);
3442 void
3443 Editor::zoom_focus_selection_done ()
3445 string choice = zoom_focus_selector.get_active_text();
3446 ZoomFocus focus_type = ZoomFocusLeft;
3448 if (choice == _("Left")) {
3449 focus_type = ZoomFocusLeft;
3450 } else if (choice == _("Right")) {
3451 focus_type = ZoomFocusRight;
3452 } else if (choice == _("Center")) {
3453 focus_type = ZoomFocusCenter;
3454 } else if (choice == _("Playhead")) {
3455 focus_type = ZoomFocusPlayhead;
3456 } else if (choice == _("Mouse")) {
3457 focus_type = ZoomFocusMouse;
3458 } else if (choice == _("Edit point")) {
3459 focus_type = ZoomFocusEdit;
3462 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3464 if (ract) {
3465 ract->set_active ();
3469 gint
3470 Editor::edit_controls_button_release (GdkEventButton* ev)
3472 if (Keyboard::is_context_menu_event (ev)) {
3473 ARDOUR_UI::instance()->add_route (this);
3475 return TRUE;
3478 gint
3479 Editor::mouse_select_button_release (GdkEventButton* ev)
3481 /* this handles just right-clicks */
3483 if (ev->button != 3) {
3484 return false;
3487 return true;
3490 void
3491 Editor::set_zoom_focus (ZoomFocus f)
3493 string str = zoom_focus_strings[(int)f];
3495 if (str != zoom_focus_selector.get_active_text()) {
3496 zoom_focus_selector.set_active_text (str);
3499 if (zoom_focus != f) {
3500 zoom_focus = f;
3502 ZoomFocusChanged (); /* EMIT_SIGNAL */
3504 instant_save ();
3508 void
3509 Editor::ensure_float (Window& win)
3511 win.set_transient_for (*this);
3514 void
3515 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3517 /* recover or initialize pane positions. do this here rather than earlier because
3518 we don't want the positions to change the child allocations, which they seem to do.
3521 int pos;
3522 XMLProperty* prop;
3523 char buf[32];
3524 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3525 int width, height;
3527 enum Pane {
3528 Horizontal = 0x1,
3529 Vertical = 0x2
3532 static Pane done;
3534 XMLNode* geometry;
3536 width = default_width;
3537 height = default_height;
3539 if ((geometry = find_named_node (*node, "geometry")) != 0) {
3541 prop = geometry->property ("x-size");
3542 if (prop) {
3543 width = atoi (prop->value());
3545 prop = geometry->property ("y-size");
3546 if (prop) {
3547 height = atoi (prop->value());
3551 if (which == static_cast<Paned*> (&edit_pane)) {
3553 if (done & Horizontal) {
3554 return;
3557 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3558 /* initial allocation is 90% to canvas, 10% to notebook */
3559 pos = (int) floor (alloc.get_width() * 0.90f);
3560 snprintf (buf, sizeof(buf), "%d", pos);
3561 } else {
3562 pos = atoi (prop->value());
3565 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3566 edit_pane.set_position (pos);
3567 pre_maximal_horizontal_pane_position = pos;
3570 done = (Pane) (done | Horizontal);
3572 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3574 if (done & Vertical) {
3575 return;
3578 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3579 /* initial allocation is 90% to canvas, 10% to summary */
3580 pos = (int) floor (alloc.get_height() * 0.90f);
3581 snprintf (buf, sizeof(buf), "%d", pos);
3582 } else {
3583 pos = atoi (prop->value());
3586 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3587 editor_summary_pane.set_position (pos);
3588 pre_maximal_vertical_pane_position = pos;
3591 done = (Pane) (done | Vertical);
3595 void
3596 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3598 if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) {
3599 top_hbox.remove (toolbar_frame);
3603 void
3604 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3606 if (toolbar_frame.get_parent() == 0) {
3607 top_hbox.pack_end (toolbar_frame);
3611 void
3612 Editor::set_show_measures (bool yn)
3614 if (_show_measures != yn) {
3615 hide_measures ();
3617 if ((_show_measures = yn) == true) {
3618 if (tempo_lines)
3619 tempo_lines->show();
3620 draw_measures ();
3622 instant_save ();
3626 void
3627 Editor::toggle_follow_playhead ()
3629 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3630 if (act) {
3631 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3632 set_follow_playhead (tact->get_active());
3636 void
3637 Editor::set_follow_playhead (bool yn)
3639 if (_follow_playhead != yn) {
3640 if ((_follow_playhead = yn) == true) {
3641 /* catch up */
3642 reset_x_origin_to_follow_playhead ();
3644 instant_save ();
3648 void
3649 Editor::toggle_stationary_playhead ()
3651 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3652 if (act) {
3653 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3654 set_stationary_playhead (tact->get_active());
3658 void
3659 Editor::set_stationary_playhead (bool yn)
3661 if (_stationary_playhead != yn) {
3662 if ((_stationary_playhead = yn) == true) {
3663 /* catch up */
3664 // FIXME need a 3.0 equivalent of this 2.X call
3665 // update_current_screen ();
3667 instant_save ();
3671 void
3672 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3674 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3675 if (xfade) {
3676 xfade->set_active (!xfade->active());
3680 void
3681 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3683 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3684 if (xfade) {
3685 xfade->set_follow_overlap (!xfade->following_overlap());
3689 void
3690 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3692 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3694 if (!xfade) {
3695 return;
3698 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3700 ensure_float (cew);
3702 switch (cew.run ()) {
3703 case RESPONSE_ACCEPT:
3704 break;
3705 default:
3706 return;
3709 cew.apply ();
3710 PropertyChange all_crossfade_properties;
3711 all_crossfade_properties.add (ARDOUR::Properties::active);
3712 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3713 xfade->PropertyChanged (all_crossfade_properties);
3716 PlaylistSelector&
3717 Editor::playlist_selector () const
3719 return *_playlist_selector;
3722 Evoral::MusicalTime
3723 Editor::get_grid_type_as_beats (bool& success, framepos_t position)
3725 success = true;
3727 switch (_snap_type) {
3728 case SnapToBeat:
3729 return 1.0;
3730 break;
3732 case SnapToBeatDiv32:
3733 return 1.0/32.0;
3734 break;
3735 case SnapToBeatDiv28:
3736 return 1.0/28.0;
3737 break;
3738 case SnapToBeatDiv24:
3739 return 1.0/24.0;
3740 break;
3741 case SnapToBeatDiv20:
3742 return 1.0/20.0;
3743 break;
3744 case SnapToBeatDiv16:
3745 return 1.0/16.0;
3746 break;
3747 case SnapToBeatDiv14:
3748 return 1.0/14.0;
3749 break;
3750 case SnapToBeatDiv12:
3751 return 1.0/12.0;
3752 break;
3753 case SnapToBeatDiv10:
3754 return 1.0/10.0;
3755 break;
3756 case SnapToBeatDiv8:
3757 return 1.0/8.0;
3758 break;
3759 case SnapToBeatDiv7:
3760 return 1.0/7.0;
3761 break;
3762 case SnapToBeatDiv6:
3763 return 1.0/6.0;
3764 break;
3765 case SnapToBeatDiv5:
3766 return 1.0/5.0;
3767 break;
3768 case SnapToBeatDiv4:
3769 return 1.0/4.0;
3770 break;
3771 case SnapToBeatDiv3:
3772 return 1.0/3.0;
3773 break;
3774 case SnapToBeatDiv2:
3775 return 1.0/2.0;
3776 break;
3778 case SnapToBar:
3779 if (_session) {
3780 return _session->tempo_map().meter_at (position).beats_per_bar();
3782 break;
3784 case SnapToCDFrame:
3785 case SnapToTimecodeFrame:
3786 case SnapToTimecodeSeconds:
3787 case SnapToTimecodeMinutes:
3788 case SnapToSeconds:
3789 case SnapToMinutes:
3790 case SnapToRegionStart:
3791 case SnapToRegionEnd:
3792 case SnapToRegionSync:
3793 case SnapToRegionBoundary:
3794 default:
3795 success = false;
3796 break;
3799 return 0.0;
3802 framecnt_t
3803 Editor::get_nudge_distance (framepos_t pos, framecnt_t& next)
3805 framecnt_t ret;
3807 ret = nudge_clock.current_duration (pos);
3808 next = ret + 1; /* XXXX fix me */
3810 return ret;
3814 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3816 ArdourDialog dialog (_("Playlist Deletion"));
3817 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3818 "If left alone, no audio files used by it will be cleaned.\n"
3819 "If deleted, audio files used by it alone by will cleaned."),
3820 pl->name()));
3822 dialog.set_position (WIN_POS_CENTER);
3823 dialog.get_vbox()->pack_start (label);
3825 label.show ();
3827 dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT);
3828 dialog.add_button (_("Keep playlist"), RESPONSE_REJECT);
3829 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3831 switch (dialog.run ()) {
3832 case RESPONSE_ACCEPT:
3833 /* delete the playlist */
3834 return 0;
3835 break;
3837 case RESPONSE_REJECT:
3838 /* keep the playlist */
3839 return 1;
3840 break;
3842 default:
3843 break;
3846 return -1;
3849 bool
3850 Editor::audio_region_selection_covers (framepos_t where)
3852 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3853 if ((*a)->region()->covers (where)) {
3854 return true;
3858 return false;
3861 void
3862 Editor::prepare_for_cleanup ()
3864 cut_buffer->clear_regions ();
3865 cut_buffer->clear_playlists ();
3867 selection->clear_regions ();
3868 selection->clear_playlists ();
3870 _regions->suspend_redisplay ();
3873 void
3874 Editor::finish_cleanup ()
3876 _regions->resume_redisplay ();
3879 Location*
3880 Editor::transport_loop_location()
3882 if (_session) {
3883 return _session->locations()->auto_loop_location();
3884 } else {
3885 return 0;
3889 Location*
3890 Editor::transport_punch_location()
3892 if (_session) {
3893 return _session->locations()->auto_punch_location();
3894 } else {
3895 return 0;
3899 bool
3900 Editor::control_layout_scroll (GdkEventScroll* ev)
3902 if (Keyboard::some_magic_widget_has_focus()) {
3903 return false;
3906 switch (ev->direction) {
3907 case GDK_SCROLL_UP:
3908 scroll_tracks_up_line ();
3909 return true;
3910 break;
3912 case GDK_SCROLL_DOWN:
3913 scroll_tracks_down_line ();
3914 return true;
3916 default:
3917 /* no left/right handling yet */
3918 break;
3921 return false;
3924 void
3925 Editor::session_state_saved (string)
3927 update_title ();
3928 _snapshots->redisplay ();
3931 void
3932 Editor::maximise_editing_space ()
3934 _mouse_mode_tearoff->set_visible (false);
3935 _tools_tearoff->set_visible (false);
3936 _zoom_tearoff->set_visible (false);
3938 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
3939 pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
3940 pre_maximal_editor_width = this->get_width ();
3941 pre_maximal_editor_height = this->get_height ();
3943 if (post_maximal_horizontal_pane_position == 0) {
3944 post_maximal_horizontal_pane_position = edit_pane.get_width();
3947 if (post_maximal_vertical_pane_position == 0) {
3948 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
3951 fullscreen ();
3953 if (post_maximal_editor_width) {
3954 edit_pane.set_position (post_maximal_horizontal_pane_position -
3955 abs(post_maximal_editor_width - pre_maximal_editor_width));
3956 } else {
3957 edit_pane.set_position (post_maximal_horizontal_pane_position);
3960 if (post_maximal_editor_height) {
3961 editor_summary_pane.set_position (post_maximal_vertical_pane_position -
3962 abs(post_maximal_editor_height - pre_maximal_editor_height));
3963 } else {
3964 editor_summary_pane.set_position (post_maximal_vertical_pane_position);
3967 if (Config->get_keep_tearoffs()) {
3968 _mouse_mode_tearoff->set_visible (true);
3969 _tools_tearoff->set_visible (true);
3970 _zoom_tearoff->set_visible (true);
3975 void
3976 Editor::restore_editing_space ()
3978 // user changed width/height of panes during fullscreen
3980 if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
3981 post_maximal_horizontal_pane_position = edit_pane.get_position();
3984 if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
3985 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
3988 unfullscreen();
3990 _mouse_mode_tearoff->set_visible (true);
3991 _tools_tearoff->set_visible (true);
3992 _zoom_tearoff->set_visible (true);
3993 post_maximal_editor_width = this->get_width();
3994 post_maximal_editor_height = this->get_height();
3996 edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
3997 editor_summary_pane.set_position (pre_maximal_vertical_pane_position + abs(this->get_height() - pre_maximal_editor_height));
4001 * Make new playlists for a given track and also any others that belong
4002 * to the same active route group with the `edit' property.
4003 * @param v Track.
4006 void
4007 Editor::new_playlists (TimeAxisView* v)
4009 begin_reversible_command (_("new playlists"));
4010 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4011 _session->playlists->get (playlists);
4012 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4013 commit_reversible_command ();
4017 * Use a copy of the current playlist for a given track and also any others that belong
4018 * to the same active route group with the `edit' property.
4019 * @param v Track.
4022 void
4023 Editor::copy_playlists (TimeAxisView* v)
4025 begin_reversible_command (_("copy playlists"));
4026 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4027 _session->playlists->get (playlists);
4028 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4029 commit_reversible_command ();
4032 /** Clear the current playlist for a given track and also any others that belong
4033 * to the same active route group with the `edit' property.
4034 * @param v Track.
4037 void
4038 Editor::clear_playlists (TimeAxisView* v)
4040 begin_reversible_command (_("clear playlists"));
4041 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4042 _session->playlists->get (playlists);
4043 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4044 commit_reversible_command ();
4047 void
4048 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4050 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4053 void
4054 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4056 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4059 void
4060 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4062 atv.clear_playlist ();
4065 bool
4066 Editor::on_key_press_event (GdkEventKey* ev)
4068 return key_press_focus_accelerator_handler (*this, ev);
4071 bool
4072 Editor::on_key_release_event (GdkEventKey* ev)
4074 return Gtk::Window::on_key_release_event (ev);
4075 // return key_press_focus_accelerator_handler (*this, ev);
4078 /** Queue up a change to the viewport x origin.
4079 * @param frame New x origin.
4081 void
4082 Editor::reset_x_origin (framepos_t frame)
4084 queue_visual_change (frame);
4087 void
4088 Editor::reset_y_origin (double y)
4090 queue_visual_change_y (y);
4093 void
4094 Editor::reset_zoom (double fpu)
4096 queue_visual_change (fpu);
4099 void
4100 Editor::reposition_and_zoom (framepos_t frame, double fpu)
4102 reset_x_origin (frame);
4103 reset_zoom (fpu);
4105 if (!no_save_visual) {
4106 undo_visual_stack.push_back (current_visual_state(false));
4110 Editor::VisualState*
4111 Editor::current_visual_state (bool with_tracks)
4113 VisualState* vs = new VisualState;
4114 vs->y_position = vertical_adjustment.get_value();
4115 vs->frames_per_unit = frames_per_unit;
4116 vs->leftmost_frame = leftmost_frame;
4117 vs->zoom_focus = zoom_focus;
4119 if (with_tracks) {
4120 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4121 vs->track_states.push_back (TAVState ((*i), &(*i)->get_state()));
4125 return vs;
4128 void
4129 Editor::undo_visual_state ()
4131 if (undo_visual_stack.empty()) {
4132 return;
4135 redo_visual_stack.push_back (current_visual_state());
4137 VisualState* vs = undo_visual_stack.back();
4138 undo_visual_stack.pop_back();
4139 use_visual_state (*vs);
4142 void
4143 Editor::redo_visual_state ()
4145 if (redo_visual_stack.empty()) {
4146 return;
4149 undo_visual_stack.push_back (current_visual_state());
4151 VisualState* vs = redo_visual_stack.back();
4152 redo_visual_stack.pop_back();
4153 use_visual_state (*vs);
4156 void
4157 Editor::swap_visual_state ()
4159 if (undo_visual_stack.empty()) {
4160 redo_visual_state ();
4161 } else {
4162 undo_visual_state ();
4166 void
4167 Editor::use_visual_state (VisualState& vs)
4169 no_save_visual = true;
4171 _routes->suspend_redisplay ();
4173 vertical_adjustment.set_value (vs.y_position);
4175 set_zoom_focus (vs.zoom_focus);
4176 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4178 for (list<TAVState>::iterator i = vs.track_states.begin(); i != vs.track_states.end(); ++i) {
4179 TrackViewList::iterator t;
4181 /* check if the track still exists - it could have been deleted */
4183 if ((t = find (track_views.begin(), track_views.end(), i->first)) != track_views.end()) {
4184 (*t)->set_state (*(i->second), Stateful::loading_state_version);
4189 if (!vs.track_states.empty()) {
4190 _routes->update_visibility ();
4193 _routes->resume_redisplay ();
4195 no_save_visual = false;
4198 void
4199 Editor::set_frames_per_unit (double fpu)
4201 /* this is the core function that controls the zoom level of the canvas. it is called
4202 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4205 if (fpu == frames_per_unit) {
4206 return;
4209 if (fpu < 2.0) {
4210 fpu = 2.0;
4214 /* don't allow zooms that fit more than the maximum number
4215 of frames into an 800 pixel wide space.
4218 if (max_framepos / fpu < 800.0) {
4219 return;
4222 if (tempo_lines)
4223 tempo_lines->tempo_map_changed();
4225 frames_per_unit = fpu;
4226 post_zoom ();
4229 void
4230 Editor::post_zoom ()
4232 // convert fpu to frame count
4234 framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width);
4236 if (frames_per_unit != zoom_range_clock.current_duration()) {
4237 zoom_range_clock.set (frames);
4240 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4241 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4242 (*i)->reshow_selection (selection->time);
4246 ZoomChanged (); /* EMIT_SIGNAL */
4248 //reset_scrolling_region ();
4250 if (playhead_cursor) {
4251 playhead_cursor->set_position (playhead_cursor->current_frame);
4254 refresh_location_display();
4255 _summary->set_overlays_dirty ();
4257 update_marker_labels ();
4259 instant_save ();
4262 void
4263 Editor::queue_visual_change (framepos_t where)
4265 pending_visual_change.add (VisualChange::TimeOrigin);
4266 pending_visual_change.time_origin = where;
4267 ensure_visual_change_idle_handler ();
4270 void
4271 Editor::queue_visual_change (double fpu)
4273 pending_visual_change.add (VisualChange::ZoomLevel);
4274 pending_visual_change.frames_per_unit = fpu;
4276 ensure_visual_change_idle_handler ();
4279 void
4280 Editor::queue_visual_change_y (double y)
4282 pending_visual_change.add (VisualChange::YOrigin);
4283 pending_visual_change.y_origin = y;
4285 ensure_visual_change_idle_handler ();
4288 void
4289 Editor::ensure_visual_change_idle_handler ()
4291 if (pending_visual_change.idle_handler_id < 0) {
4292 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4297 Editor::_idle_visual_changer (void* arg)
4299 return static_cast<Editor*>(arg)->idle_visual_changer ();
4303 Editor::idle_visual_changer ()
4305 VisualChange::Type p = pending_visual_change.pending;
4306 pending_visual_change.pending = (VisualChange::Type) 0;
4308 double const last_time_origin = horizontal_position ();
4310 if (p & VisualChange::TimeOrigin) {
4311 /* This is a bit of a hack, but set_frames_per_unit
4312 below will (if called) end up with the
4313 CrossfadeViews looking at Editor::leftmost_frame,
4314 and if we're changing origin and zoom in the same
4315 operation it will be the wrong value unless we
4316 update it here.
4319 leftmost_frame = pending_visual_change.time_origin;
4322 if (p & VisualChange::ZoomLevel) {
4323 set_frames_per_unit (pending_visual_change.frames_per_unit);
4325 compute_fixed_ruler_scale ();
4326 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4327 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4328 update_tempo_based_rulers ();
4330 if (p & VisualChange::TimeOrigin) {
4331 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4333 if (p & VisualChange::YOrigin) {
4334 vertical_adjustment.set_value (pending_visual_change.y_origin);
4337 if (last_time_origin == horizontal_position ()) {
4338 /* changed signal not emitted */
4339 update_fixed_rulers ();
4340 redisplay_tempo (true);
4343 _summary->set_overlays_dirty ();
4345 pending_visual_change.idle_handler_id = -1;
4346 return 0; /* this is always a one-shot call */
4349 struct EditorOrderTimeAxisSorter {
4350 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4351 return a->order () < b->order ();
4355 void
4356 Editor::sort_track_selection (TrackViewList* sel)
4358 EditorOrderTimeAxisSorter cmp;
4360 if (sel) {
4361 sel->sort (cmp);
4362 } else {
4363 selection->tracks.sort (cmp);
4367 framepos_t
4368 Editor::get_preferred_edit_position (bool ignore_playhead)
4370 bool ignored;
4371 framepos_t where = 0;
4372 EditPoint ep = _edit_point;
4374 if (entered_marker) {
4375 return entered_marker->position();
4378 if (ignore_playhead && ep == EditAtPlayhead) {
4379 ep = EditAtSelectedMarker;
4382 switch (ep) {
4383 case EditAtPlayhead:
4384 where = _session->audible_frame();
4385 break;
4387 case EditAtSelectedMarker:
4388 if (!selection->markers.empty()) {
4389 bool is_start;
4390 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4391 if (loc) {
4392 if (is_start) {
4393 where = loc->start();
4394 } else {
4395 where = loc->end();
4397 break;
4400 /* fallthru */
4402 default:
4403 case EditAtMouse:
4404 if (!mouse_frame (where, ignored)) {
4405 /* XXX not right but what can we do ? */
4406 return 0;
4408 snap_to (where);
4409 break;
4412 return where;
4415 void
4416 Editor::set_loop_range (framepos_t start, framepos_t end, string cmd)
4418 if (!_session) return;
4420 begin_reversible_command (cmd);
4422 Location* tll;
4424 if ((tll = transport_loop_location()) == 0) {
4425 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop);
4426 XMLNode &before = _session->locations()->get_state();
4427 _session->locations()->add (loc, true);
4428 _session->set_auto_loop_location (loc);
4429 XMLNode &after = _session->locations()->get_state();
4430 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4431 } else {
4432 XMLNode &before = tll->get_state();
4433 tll->set_hidden (false, this);
4434 tll->set (start, end);
4435 XMLNode &after = tll->get_state();
4436 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4439 commit_reversible_command ();
4442 void
4443 Editor::set_punch_range (framepos_t start, framepos_t end, string cmd)
4445 if (!_session) return;
4447 begin_reversible_command (cmd);
4449 Location* tpl;
4451 if ((tpl = transport_punch_location()) == 0) {
4452 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoPunch);
4453 XMLNode &before = _session->locations()->get_state();
4454 _session->locations()->add (loc, true);
4455 _session->set_auto_loop_location (loc);
4456 XMLNode &after = _session->locations()->get_state();
4457 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4459 else {
4460 XMLNode &before = tpl->get_state();
4461 tpl->set_hidden (false, this);
4462 tpl->set (start, end);
4463 XMLNode &after = tpl->get_state();
4464 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4467 commit_reversible_command ();
4470 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4471 * @param rs List to which found regions are added.
4472 * @param where Time to look at.
4473 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4475 void
4476 Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4478 const TrackViewList* tracks;
4480 if (ts.empty()) {
4481 tracks = &track_views;
4482 } else {
4483 tracks = &ts;
4486 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4487 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4488 if (rtv) {
4489 boost::shared_ptr<Track> tr;
4490 boost::shared_ptr<Playlist> pl;
4492 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4494 Playlist::RegionList* regions = pl->regions_at (
4495 (framepos_t) floor ( (double)where * tr->speed()));
4497 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4498 RegionView* rv = rtv->view()->find_view (*i);
4499 if (rv) {
4500 rs.add (rv);
4504 delete regions;
4510 void
4511 Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4513 const TrackViewList* tracks;
4515 if (ts.empty()) {
4516 tracks = &track_views;
4517 } else {
4518 tracks = &ts;
4521 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4522 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4523 if (rtv) {
4524 boost::shared_ptr<Track> tr;
4525 boost::shared_ptr<Playlist> pl;
4527 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4529 Playlist::RegionList* regions = pl->regions_touched (
4530 (framepos_t) floor ( (double)where * tr->speed()), max_framepos);
4532 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4534 RegionView* rv = rtv->view()->find_view (*i);
4536 if (rv) {
4537 rs.push_back (rv);
4541 delete regions;
4547 /** Get regions using the following method:
4549 * Make an initial region list using the selected regions, unless
4550 * the edit point is `mouse' and the mouse is over an unselected
4551 * region. In this case, start with just that region.
4553 * Then, make an initial track list of the tracks that these
4554 * regions are on, and if the edit point is not `mouse', add the
4555 * selected tracks.
4557 * Look at this track list and add any other tracks that are on the
4558 * same active edit-enabled route group as one of the initial tracks.
4560 * Finally take the initial region list and add any regions that are
4561 * under the edit point on one of the tracks on the track list to get
4562 * the returned region list.
4564 * The rationale here is that the mouse edit point is special in that
4565 * its position describes both a time and a track; the other edit
4566 * modes only describe a time. Hence if the edit point is `mouse' we
4567 * ignore selected tracks, as we assume the user means something by
4568 * pointing at a particular track. Also in this case we take note of
4569 * the region directly under the edit point, as there is always just one
4570 * (rather than possibly several with non-mouse edit points).
4573 RegionSelection
4574 Editor::get_regions_from_selection_and_edit_point ()
4576 RegionSelection regions;
4578 if (_edit_point == EditAtMouse && entered_regionview && !selection->regions.contains (entered_regionview)) {
4579 regions.add (entered_regionview);
4580 } else {
4581 regions = selection->regions;
4584 TrackViewList tracks;
4586 if (_edit_point != EditAtMouse) {
4587 tracks = selection->tracks;
4590 /* Add any other tracks that have regions that are in the same
4591 edit-activated route group as one of our regions.
4593 for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) {
4595 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4596 if (g && g->is_active() && g->is_edit()) {
4597 tracks.add (axis_views_from_routes (g->route_list()));
4602 if (!tracks.empty()) {
4603 /* now find regions that are at the edit position on those tracks */
4604 framepos_t const where = get_preferred_edit_position ();
4605 get_regions_at (regions, where, tracks);
4608 return regions;
4612 RegionSelection
4613 Editor::get_regions_from_selection_and_entered ()
4615 RegionSelection rs = selection->regions;
4617 if (rs.empty() && entered_regionview) {
4618 rs.add (entered_regionview);
4621 return rs;
4624 void
4625 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4627 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4629 RouteTimeAxisView* tatv;
4631 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4633 boost::shared_ptr<Playlist> pl;
4634 vector<boost::shared_ptr<Region> > results;
4635 RegionView* marv;
4636 boost::shared_ptr<Track> tr;
4638 if ((tr = tatv->track()) == 0) {
4639 /* bus */
4640 continue;
4643 if ((pl = (tr->playlist())) != 0) {
4644 pl->get_region_list_equivalent_regions (region, results);
4647 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4648 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4649 regions.push_back (marv);
4657 void
4658 Editor::show_rhythm_ferret ()
4660 if (rhythm_ferret == 0) {
4661 rhythm_ferret = new RhythmFerret(*this);
4664 rhythm_ferret->set_session (_session);
4665 rhythm_ferret->show ();
4666 rhythm_ferret->present ();
4669 void
4670 Editor::first_idle ()
4672 MessageDialog* dialog = 0;
4674 if (track_views.size() > 1) {
4675 dialog = new MessageDialog (*this,
4676 string_compose (_("Please wait while %1 loads visual data"), PROGRAM_NAME),
4677 true,
4678 Gtk::MESSAGE_INFO,
4679 Gtk::BUTTONS_NONE);
4680 dialog->present ();
4681 ARDOUR_UI::instance()->flush_pending ();
4684 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4685 (*t)->first_idle();
4688 // first idle adds route children (automation tracks), so we need to redisplay here
4689 _routes->redisplay ();
4691 delete dialog;
4693 _have_idled = true;
4696 gboolean
4697 Editor::_idle_resize (gpointer arg)
4699 return ((Editor*)arg)->idle_resize ();
4702 void
4703 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4705 if (resize_idle_id < 0) {
4706 resize_idle_id = g_idle_add (_idle_resize, this);
4707 _pending_resize_amount = 0;
4710 /* make a note of the smallest resulting height, so that we can clamp the
4711 lower limit at TimeAxisView::hSmall */
4713 int32_t min_resulting = INT32_MAX;
4715 _pending_resize_amount += h;
4716 _pending_resize_view = view;
4718 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4720 if (selection->tracks.contains (_pending_resize_view)) {
4721 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4722 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4726 if (min_resulting < 0) {
4727 min_resulting = 0;
4730 /* clamp */
4731 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4732 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4736 /** Handle pending resizing of tracks */
4737 bool
4738 Editor::idle_resize ()
4740 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4742 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4743 selection->tracks.contains (_pending_resize_view)) {
4745 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4746 if (*i != _pending_resize_view) {
4747 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4752 _pending_resize_amount = 0;
4753 flush_canvas ();
4754 _group_tabs->set_dirty ();
4755 resize_idle_id = -1;
4757 return false;
4760 void
4761 Editor::located ()
4763 ENSURE_GUI_THREAD (*this, &Editor::located);
4765 playhead_cursor->set_position (_session->audible_frame ());
4766 if (_follow_playhead && !_pending_initial_locate) {
4767 reset_x_origin_to_follow_playhead ();
4770 _pending_locate_request = false;
4771 _pending_initial_locate = false;
4774 void
4775 Editor::region_view_added (RegionView *)
4777 _summary->set_dirty ();
4780 TimeAxisView*
4781 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4783 TrackViewList::const_iterator j = track_views.begin ();
4784 while (j != track_views.end()) {
4785 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4786 if (rtv && rtv->route() == r) {
4787 return rtv;
4789 ++j;
4792 return 0;
4796 TrackViewList
4797 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4799 TrackViewList t;
4801 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4802 TimeAxisView* tv = axis_view_from_route (*i);
4803 if (tv) {
4804 t.push_back (tv);
4808 return t;
4812 void
4813 Editor::handle_new_route (RouteList& routes)
4815 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4817 RouteTimeAxisView *rtv;
4818 list<RouteTimeAxisView*> new_views;
4820 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4821 boost::shared_ptr<Route> route = (*x);
4823 if (route->is_hidden() || route->is_monitor()) {
4824 continue;
4827 DataType dt = route->input()->default_type();
4829 if (dt == ARDOUR::DataType::AUDIO) {
4830 rtv = new AudioTimeAxisView (*this, _session, route, *track_canvas);
4831 } else if (dt == ARDOUR::DataType::MIDI) {
4832 rtv = new MidiTimeAxisView (*this, _session, route, *track_canvas);
4833 } else {
4834 throw unknown_type();
4837 new_views.push_back (rtv);
4838 track_views.push_back (rtv);
4840 rtv->effective_gain_display ();
4842 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4845 _routes->routes_added (new_views);
4847 if (show_editor_mixer_when_tracks_arrive) {
4848 show_editor_mixer (true);
4851 editor_list_button.set_sensitive (true);
4853 _summary->set_dirty ();
4856 void
4857 Editor::timeaxisview_deleted (TimeAxisView *tv)
4859 if (_session && _session->deletion_in_progress()) {
4860 /* the situation is under control */
4861 return;
4864 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
4866 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
4868 _routes->route_removed (tv);
4870 if (tv == entered_track) {
4871 entered_track = 0;
4874 TimeAxisView::Children c = tv->get_child_list ();
4875 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
4876 if (entered_track == i->get()) {
4877 entered_track = 0;
4881 /* remove it from the list of track views */
4883 TrackViewList::iterator i;
4885 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
4886 i = track_views.erase (i);
4889 /* update whatever the current mixer strip is displaying, if revelant */
4891 boost::shared_ptr<Route> route;
4893 if (rtav) {
4894 route = rtav->route ();
4897 if (current_mixer_strip && current_mixer_strip->route() == route) {
4899 TimeAxisView* next_tv;
4901 if (track_views.empty()) {
4902 next_tv = 0;
4903 } else if (i == track_views.end()) {
4904 next_tv = track_views.front();
4905 } else {
4906 next_tv = (*i);
4910 if (next_tv) {
4911 set_selected_mixer_strip (*next_tv);
4912 } else {
4913 /* make the editor mixer strip go away setting the
4914 * button to inactive (which also unticks the menu option)
4917 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
4922 void
4923 Editor::hide_track_in_display (TimeAxisView* tv, bool /*temponly*/)
4925 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
4927 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
4928 // this will hide the mixer strip
4929 set_selected_mixer_strip (*tv);
4932 _routes->hide_track_in_display (*tv);
4935 bool
4936 Editor::sync_track_view_list_and_routes ()
4938 track_views = TrackViewList (_routes->views ());
4940 _summary->set_dirty ();
4941 _group_tabs->set_dirty ();
4943 return false; // do not call again (until needed)
4946 void
4947 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
4949 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4950 theslot (**i);
4954 /** Find a RouteTimeAxisView by the ID of its route */
4955 RouteTimeAxisView*
4956 Editor::get_route_view_by_route_id (PBD::ID& id) const
4958 RouteTimeAxisView* v;
4960 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
4961 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
4962 if(v->route()->id() == id) {
4963 return v;
4968 return 0;
4971 void
4972 Editor::fit_route_group (RouteGroup *g)
4974 TrackViewList ts = axis_views_from_routes (g->route_list ());
4975 fit_tracks (ts);
4978 void
4979 Editor::consider_auditioning (boost::shared_ptr<Region> region)
4981 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
4983 if (r == 0) {
4984 _session->cancel_audition ();
4985 return;
4988 if (_session->is_auditioning()) {
4989 _session->cancel_audition ();
4990 if (r == last_audition_region) {
4991 return;
4995 _session->audition_region (r);
4996 last_audition_region = r;
5000 void
5001 Editor::hide_a_region (boost::shared_ptr<Region> r)
5003 r->set_hidden (true);
5006 void
5007 Editor::show_a_region (boost::shared_ptr<Region> r)
5009 r->set_hidden (false);
5012 void
5013 Editor::audition_region_from_region_list ()
5015 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5018 void
5019 Editor::hide_region_from_region_list ()
5021 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5024 void
5025 Editor::show_region_in_region_list ()
5027 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5030 void
5031 Editor::step_edit_status_change (bool yn)
5033 if (yn) {
5034 start_step_editing ();
5035 } else {
5036 stop_step_editing ();
5040 void
5041 Editor::start_step_editing ()
5043 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5046 void
5047 Editor::stop_step_editing ()
5049 step_edit_connection.disconnect ();
5052 bool
5053 Editor::check_step_edit ()
5055 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5056 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5057 if (mtv) {
5058 mtv->check_step_edit ();
5062 return true; // do it again, till we stop
5065 bool
5066 Editor::horizontal_scroll_left_press ()
5068 ++_scroll_callbacks;
5070 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5071 /* delay the first auto-repeat */
5072 return true;
5075 double x = leftmost_position() - current_page_frames() / 5;
5076 if (x < 0) {
5077 x = 0;
5080 reset_x_origin (x);
5082 /* do hacky auto-repeat */
5083 if (!_scroll_connection.connected ()) {
5084 _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_left_press), 100);
5085 _scroll_callbacks = 0;
5088 return true;
5091 void
5092 Editor::horizontal_scroll_left_release ()
5094 _scroll_connection.disconnect ();
5097 bool
5098 Editor::horizontal_scroll_right_press ()
5100 ++_scroll_callbacks;
5102 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5103 /* delay the first auto-repeat */
5104 return true;
5107 reset_x_origin (leftmost_position() + current_page_frames() / 5);
5109 /* do hacky auto-repeat */
5110 if (!_scroll_connection.connected ()) {
5111 _scroll_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::horizontal_scroll_right_press), 100);
5112 _scroll_callbacks = 0;
5115 return true;
5118 void
5119 Editor::horizontal_scroll_right_release ()
5121 _scroll_connection.disconnect ();
5124 /** Queue a change for the Editor viewport x origin to follow the playhead */
5125 void
5126 Editor::reset_x_origin_to_follow_playhead ()
5128 framepos_t const frame = playhead_cursor->current_frame;
5130 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5132 if (_session->transport_speed() < 0) {
5134 if (frame > (current_page_frames() / 2)) {
5135 center_screen (frame-(current_page_frames()/2));
5136 } else {
5137 center_screen (current_page_frames()/2);
5140 } else {
5142 if (frame < leftmost_frame) {
5143 /* moving left */
5144 framepos_t l = 0;
5145 if (_session->transport_rolling()) {
5146 /* rolling; end up with the playhead at the right of the page */
5147 l = frame - current_page_frames ();
5148 } else {
5149 /* not rolling: end up with the playhead 3/4 of the way along the page */
5150 l = frame - (3 * current_page_frames() / 4);
5153 if (l < 0) {
5154 l = 0;
5157 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5158 } else {
5159 /* moving right */
5160 if (_session->transport_rolling()) {
5161 /* rolling: end up with the playhead on the left of the page */
5162 center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5163 } else {
5164 /* not rolling: end up with the playhead 1/4 of the way along the page */
5165 center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5172 void
5173 Editor::super_rapid_screen_update ()
5175 if (!_session || !_session->engine().running()) {
5176 return;
5179 /* METERING / MIXER STRIPS */
5181 /* update track meters, if required */
5182 if (is_mapped() && meters_running) {
5183 RouteTimeAxisView* rtv;
5184 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5185 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5186 rtv->fast_update ();
5191 /* and any current mixer strip */
5192 if (current_mixer_strip) {
5193 current_mixer_strip->fast_update ();
5196 /* PLAYHEAD AND VIEWPORT */
5198 framepos_t const frame = _session->audible_frame();
5200 /* There are a few reasons why we might not update the playhead / viewport stuff:
5202 * 1. we don't update things when there's a pending locate request, otherwise
5203 * when the editor requests a locate there is a chance that this method
5204 * will move the playhead before the locate request is processed, causing
5205 * a visual glitch.
5206 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5207 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5210 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5212 last_update_frame = frame;
5214 if (!_dragging_playhead) {
5215 playhead_cursor->set_position (frame);
5218 if (!_stationary_playhead) {
5220 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5221 reset_x_origin_to_follow_playhead ();
5224 } else {
5226 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5227 editor canvas
5229 #if 0
5230 // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5231 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5232 if (target <= 0.0) {
5233 target = 0.0;
5235 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5236 target = (target * 0.15) + (current * 0.85);
5237 } else {
5238 /* relax */
5241 current = target;
5242 set_horizontal_position (current);
5243 #endif
5250 void
5251 Editor::session_going_away ()
5253 _have_idled = false;
5255 _session_connections.drop_connections ();
5257 super_rapid_screen_update_connection.disconnect ();
5259 selection->clear ();
5260 cut_buffer->clear ();
5262 clicked_regionview = 0;
5263 clicked_axisview = 0;
5264 clicked_routeview = 0;
5265 clicked_crossfadeview = 0;
5266 entered_regionview = 0;
5267 entered_track = 0;
5268 last_update_frame = 0;
5269 _drags->abort ();
5271 playhead_cursor->canvas_item.hide ();
5273 /* rip everything out of the list displays */
5275 _regions->clear ();
5276 _routes->clear ();
5277 _route_groups->clear ();
5279 /* do this first so that deleting a track doesn't reset cms to null
5280 and thus cause a leak.
5283 if (current_mixer_strip) {
5284 if (current_mixer_strip->get_parent() != 0) {
5285 global_hpacker.remove (*current_mixer_strip);
5287 delete current_mixer_strip;
5288 current_mixer_strip = 0;
5291 /* delete all trackviews */
5293 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5294 delete *i;
5296 track_views.clear ();
5298 zoom_range_clock.set_session (0);
5299 nudge_clock.set_session (0);
5301 editor_list_button.set_active(false);
5302 editor_list_button.set_sensitive(false);
5304 /* clear tempo/meter rulers */
5305 remove_metric_marks ();
5306 hide_measures ();
5307 clear_marker_display ();
5309 delete current_bbt_points;
5310 current_bbt_points = 0;
5312 /* get rid of any existing editor mixer strip */
5314 WindowTitle title(Glib::get_application_name());
5315 title += _("Editor");
5317 set_title (title.get_string());
5319 SessionHandlePtr::session_going_away ();
5323 void
5324 Editor::show_editor_list (bool yn)
5326 if (yn) {
5327 the_notebook.show();
5328 } else {
5329 the_notebook.hide();
5333 void
5334 Editor::change_region_layering_order ()
5336 framepos_t const position = get_preferred_edit_position ();
5338 if (!clicked_routeview) {
5339 if (layering_order_editor) {
5340 layering_order_editor->hide ();
5342 return;
5345 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
5347 if (!track) {
5348 return;
5351 boost::shared_ptr<Playlist> pl = track->playlist();
5353 if (!pl) {
5354 return;
5357 if (layering_order_editor == 0) {
5358 layering_order_editor = new RegionLayeringOrderEditor(*this);
5361 layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position);
5362 layering_order_editor->maybe_present ();
5365 void
5366 Editor::update_region_layering_order_editor ()
5368 if (layering_order_editor && layering_order_editor->is_visible ()) {
5369 change_region_layering_order ();
5373 void
5374 Editor::setup_fade_images ()
5376 _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear")));
5377 _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut")));
5378 _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut")));
5379 _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut")));
5380 _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut")));
5382 _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear")));
5383 _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut")));
5384 _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut")));
5385 _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut")));
5386 _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut")));
5390 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
5391 Gtk::MenuItem&
5392 Editor::action_menu_item (std::string const & name)
5394 Glib::RefPtr<Action> a = editor_actions->get_action (name);
5395 assert (a);
5397 return *manage (a->create_menu_item ());
5400 void
5401 Editor::resize_text_widgets ()
5403 set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_FUDGE+10, 15);
5404 set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_FUDGE+10, 15);
5405 set_size_request_to_display_given_text (snap_type_selector, snap_type_strings, COMBO_FUDGE+10, 15);
5406 set_size_request_to_display_given_text (snap_mode_selector, snap_mode_strings, COMBO_FUDGE+10, 15);
5407 set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_FUDGE+10, 15);