Fix vertical pane resizing during fullscreen/unfullscreen
[ardour2.git] / gtk2_ardour / editor.cc
blobdaeac13fba2c2447b4ea349378137abe955c243d
1 /*
2 Copyright (C) 2000-2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /* Note: public Editor methods are documented in public_editor.h */
22 #include <stdint.h>
23 #include <unistd.h>
24 #include <cstdlib>
25 #include <cmath>
26 #include <string>
27 #include <algorithm>
28 #include <map>
30 #include "ardour_ui.h"
32 * ardour_ui.h include was moved to the top of the list
33 * due to a conflicting definition of 'Style' between
34 * Apple's MacTypes.h and BarController.
37 #include <boost/none.hpp>
39 #include <sigc++/bind.h>
41 #include "pbd/convert.h"
42 #include "pbd/error.h"
43 #include "pbd/enumwriter.h"
44 #include "pbd/memento_command.h"
45 #include "pbd/unknown_type.h"
47 #include <glibmm/miscutils.h>
48 #include <gtkmm/image.h>
49 #include <gdkmm/color.h>
50 #include <gdkmm/bitmap.h>
52 #include "gtkmm2ext/bindings.h"
53 #include "gtkmm2ext/grouped_buttons.h"
54 #include "gtkmm2ext/gtk_ui.h"
55 #include "gtkmm2ext/tearoff.h"
56 #include "gtkmm2ext/utils.h"
57 #include "gtkmm2ext/window_title.h"
58 #include "gtkmm2ext/choice.h"
59 #include "gtkmm2ext/cell_renderer_pixbuf_toggle.h"
61 #include "ardour/audio_diskstream.h"
62 #include "ardour/audio_track.h"
63 #include "ardour/audioplaylist.h"
64 #include "ardour/audioregion.h"
65 #include "ardour/location.h"
66 #include "ardour/midi_region.h"
67 #include "ardour/plugin_manager.h"
68 #include "ardour/profile.h"
69 #include "ardour/route_group.h"
70 #include "ardour/session_directory.h"
71 #include "ardour/session_route.h"
72 #include "ardour/session_state_utils.h"
73 #include "ardour/tempo.h"
74 #include "ardour/utils.h"
75 #include "ardour/session_playlists.h"
76 #include "ardour/audioengine.h"
78 #include "control_protocol/control_protocol.h"
80 #include "actions.h"
81 #include "actions.h"
82 #include "analysis_window.h"
83 #include "audio_clock.h"
84 #include "audio_region_view.h"
85 #include "audio_streamview.h"
86 #include "audio_time_axis.h"
87 #include "automation_time_axis.h"
88 #include "bundle_manager.h"
89 #include "canvas-noevent-text.h"
90 #include "canvas_impl.h"
91 #include "crossfade_edit.h"
92 #include "crossfade_view.h"
93 #include "debug.h"
94 #include "editing.h"
95 #include "editor.h"
96 #include "editor_cursors.h"
97 #include "editor_drag.h"
98 #include "editor_group_tabs.h"
99 #include "editor_locations.h"
100 #include "editor_regions.h"
101 #include "editor_route_groups.h"
102 #include "editor_routes.h"
103 #include "editor_snapshots.h"
104 #include "editor_summary.h"
105 #include "global_port_matrix.h"
106 #include "gui_object.h"
107 #include "gui_thread.h"
108 #include "keyboard.h"
109 #include "marker.h"
110 #include "midi_time_axis.h"
111 #include "mixer_strip.h"
112 #include "mouse_cursors.h"
113 #include "playlist_selector.h"
114 #include "public_editor.h"
115 #include "region_layering_order_editor.h"
116 #include "rgb_macros.h"
117 #include "rhythm_ferret.h"
118 #include "selection.h"
119 #include "sfdb_ui.h"
120 #include "simpleline.h"
121 #include "tempo_lines.h"
122 #include "time_axis_view.h"
123 #include "utils.h"
125 #include "i18n.h"
127 #ifdef WITH_CMT
128 #include "imageframe_socket_handler.h"
129 #endif
131 using namespace std;
132 using namespace ARDOUR;
133 using namespace PBD;
134 using namespace Gtk;
135 using namespace Glib;
136 using namespace Gtkmm2ext;
137 using namespace Editing;
139 using PBD::internationalize;
140 using PBD::atoi;
141 using Gtkmm2ext::Keyboard;
143 const double Editor::timebar_height = 15.0;
145 static const gchar *_snap_type_strings[] = {
146 N_("CD Frames"),
147 N_("Timecode Frames"),
148 N_("Timecode Seconds"),
149 N_("Timecode Minutes"),
150 N_("Seconds"),
151 N_("Minutes"),
152 N_("Beats/32"),
153 N_("Beats/28"),
154 N_("Beats/24"),
155 N_("Beats/20"),
156 N_("Beats/16"),
157 N_("Beats/14"),
158 N_("Beats/12"),
159 N_("Beats/10"),
160 N_("Beats/8"),
161 N_("Beats/7"),
162 N_("Beats/6"),
163 N_("Beats/5"),
164 N_("Beats/4"),
165 N_("Beats/3"),
166 N_("Beats/2"),
167 N_("Beats"),
168 N_("Bars"),
169 N_("Marks"),
170 N_("Region starts"),
171 N_("Region ends"),
172 N_("Region syncs"),
173 N_("Region bounds"),
177 static const gchar *_snap_mode_strings[] = {
178 N_("No Grid"),
179 N_("Grid"),
180 N_("Magnetic"),
184 static const gchar *_edit_point_strings[] = {
185 N_("Playhead"),
186 N_("Marker"),
187 N_("Mouse"),
191 static const gchar *_zoom_focus_strings[] = {
192 N_("Left"),
193 N_("Right"),
194 N_("Center"),
195 N_("Playhead"),
196 N_("Mouse"),
197 N_("Edit point"),
201 #ifdef USE_RUBBERBAND
202 static const gchar *_rb_opt_strings[] = {
203 N_("Mushy"),
204 N_("Smooth"),
205 N_("Balanced multitimbral mixture"),
206 N_("Unpitched percussion with stable notes"),
207 N_("Crisp monophonic instrumental"),
208 N_("Unpitched solo percussion"),
209 N_("Resample without preserving pitch"),
212 #endif
214 void
215 show_me_the_size (Requisition* r, const char* what)
217 cerr << "size of " << what << " = " << r->width << " x " << r->height << endl;
220 #ifdef GTKOSX
221 static void
222 pane_size_watcher (Paned* pane)
224 /* if the handle of a pane vanishes into (at least) the tabs of a notebook,
225 it is no longer accessible. so stop that. this doesn't happen on X11,
226 just the quartz backend.
228 ugh.
231 int max_width_of_lhs = GTK_WIDGET(pane->gobj())->allocation.width - 25;
233 gint pos = pane->get_position ();
235 if (pos > max_width_of_lhs) {
236 pane->set_position (max_width_of_lhs);
239 #endif
241 Editor::Editor ()
242 : _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
244 /* time display buttons */
245 , minsec_label (_("Mins:Secs"))
246 , bbt_label (_("Bars:Beats"))
247 , timecode_label (_("Timecode"))
248 , samples_label (_("Samples"))
249 , tempo_label (_("Tempo"))
250 , meter_label (_("Meter"))
251 , mark_label (_("Location Markers"))
252 , range_mark_label (_("Range Markers"))
253 , transport_mark_label (_("Loop/Punch Ranges"))
254 , cd_mark_label (_("CD Markers"))
255 , edit_packer (4, 4, true)
257 /* the values here don't matter: layout widgets
258 reset them as needed.
261 , vertical_adjustment (0.0, 0.0, 10.0, 400.0)
263 /* tool bar related */
265 , zoom_range_clock (new AudioClock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, false, true))
267 , toolbar_selection_clock_table (2,3)
269 , automation_mode_button (_("mode"))
270 , global_automation_button (_("automation"))
272 , _toolbar_viewport (*manage (new Gtk::Adjustment (0, 0, 1e10)), *manage (new Gtk::Adjustment (0, 0, 1e10)))
273 , midi_panic_button (_("Panic"))
275 #ifdef WITH_CMT
276 , image_socket_listener(0)
277 #endif
279 /* nudge */
281 , nudge_clock (new AudioClock (X_("nudge"), false, X_("NudgeClock"), true, false, true))
282 , meters_running(false)
283 , _pending_locate_request (false)
284 , _pending_initial_locate (false)
285 , _last_cut_copy_source_track (0)
287 , _region_selection_change_updates_region_list (true)
289 constructed = false;
291 /* we are a singleton */
293 PublicEditor::_instance = this;
295 _have_idled = false;
297 selection = new Selection (this);
298 cut_buffer = new Selection (this);
300 clicked_regionview = 0;
301 clicked_axisview = 0;
302 clicked_routeview = 0;
303 clicked_crossfadeview = 0;
304 clicked_control_point = 0;
305 last_update_frame = 0;
306 pre_press_cursor = 0;
307 _drags = new DragManager (this);
308 current_mixer_strip = 0;
309 current_bbt_points = 0;
310 tempo_lines = 0;
312 snap_type_strings = I18N (_snap_type_strings);
313 snap_mode_strings = I18N (_snap_mode_strings);
314 zoom_focus_strings = I18N (_zoom_focus_strings);
315 edit_point_strings = I18N (_edit_point_strings);
316 #ifdef USE_RUBBERBAND
317 rb_opt_strings = I18N (_rb_opt_strings);
318 rb_current_opt = 4;
319 #endif
321 snap_threshold = 5.0;
322 bbt_beat_subdivision = 4;
323 _canvas_width = 0;
324 _canvas_height = 0;
325 last_autoscroll_x = 0;
326 last_autoscroll_y = 0;
327 autoscroll_active = false;
328 autoscroll_timeout_tag = -1;
329 logo_item = 0;
331 analysis_window = 0;
333 current_interthread_info = 0;
334 _show_measures = true;
335 show_gain_after_trim = false;
336 last_item_entered = 0;
338 have_pending_keyboard_selection = false;
339 _follow_playhead = true;
340 _stationary_playhead = false;
341 _xfade_visibility = true;
342 editor_ruler_menu = 0;
343 no_ruler_shown_update = false;
344 marker_menu = 0;
345 range_marker_menu = 0;
346 marker_menu_item = 0;
347 tempo_or_meter_marker_menu = 0;
348 transport_marker_menu = 0;
349 new_transport_marker_menu = 0;
350 editor_mixer_strip_width = Wide;
351 show_editor_mixer_when_tracks_arrive = false;
352 region_edit_menu_split_multichannel_item = 0;
353 region_edit_menu_split_item = 0;
354 temp_location = 0;
355 leftmost_frame = 0;
356 current_stepping_trackview = 0;
357 entered_track = 0;
358 entered_regionview = 0;
359 entered_marker = 0;
360 clear_entered_track = false;
361 current_timefx = 0;
362 playhead_cursor = 0;
363 button_release_can_deselect = true;
364 _dragging_playhead = false;
365 _dragging_edit_point = false;
366 select_new_marker = false;
367 rhythm_ferret = 0;
368 layering_order_editor = 0;
369 no_save_visual = false;
370 resize_idle_id = -1;
372 scrubbing_direction = 0;
374 sfbrowser = 0;
376 location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get();
377 location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get();
378 location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get();
379 location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get();
380 location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get();
382 _edit_point = EditAtMouse;
383 _internal_editing = false;
384 current_canvas_cursor = 0;
386 frames_per_unit = 2048; /* too early to use reset_zoom () */
388 _scroll_callbacks = 0;
390 zoom_focus = ZoomFocusLeft;
391 set_zoom_focus (ZoomFocusLeft);
392 zoom_range_clock->ValueChanged.connect (sigc::mem_fun(*this, &Editor::zoom_adjustment_changed));
394 bbt_label.set_name ("EditorTimeButton");
395 bbt_label.set_size_request (-1, (int)timebar_height);
396 bbt_label.set_alignment (1.0, 0.5);
397 bbt_label.set_padding (5,0);
398 bbt_label.hide ();
399 bbt_label.set_no_show_all();
400 minsec_label.set_name ("EditorTimeButton");
401 minsec_label.set_size_request (-1, (int)timebar_height);
402 minsec_label.set_alignment (1.0, 0.5);
403 minsec_label.set_padding (5,0);
404 minsec_label.hide ();
405 minsec_label.set_no_show_all();
406 timecode_label.set_name ("EditorTimeButton");
407 timecode_label.set_size_request (-1, (int)timebar_height);
408 timecode_label.set_alignment (1.0, 0.5);
409 timecode_label.set_padding (5,0);
410 timecode_label.hide ();
411 timecode_label.set_no_show_all();
412 samples_label.set_name ("EditorTimeButton");
413 samples_label.set_size_request (-1, (int)timebar_height);
414 samples_label.set_alignment (1.0, 0.5);
415 samples_label.set_padding (5,0);
416 samples_label.hide ();
417 samples_label.set_no_show_all();
419 tempo_label.set_name ("EditorTimeButton");
420 tempo_label.set_size_request (-1, (int)timebar_height);
421 tempo_label.set_alignment (1.0, 0.5);
422 tempo_label.set_padding (5,0);
423 tempo_label.hide();
424 tempo_label.set_no_show_all();
426 meter_label.set_name ("EditorTimeButton");
427 meter_label.set_size_request (-1, (int)timebar_height);
428 meter_label.set_alignment (1.0, 0.5);
429 meter_label.set_padding (5,0);
430 meter_label.hide();
431 meter_label.set_no_show_all();
433 mark_label.set_name ("EditorTimeButton");
434 mark_label.set_size_request (-1, (int)timebar_height);
435 mark_label.set_alignment (1.0, 0.5);
436 mark_label.set_padding (5,0);
437 mark_label.hide();
438 mark_label.set_no_show_all();
440 cd_mark_label.set_name ("EditorTimeButton");
441 cd_mark_label.set_size_request (-1, (int)timebar_height);
442 cd_mark_label.set_alignment (1.0, 0.5);
443 cd_mark_label.set_padding (5,0);
444 cd_mark_label.hide();
445 cd_mark_label.set_no_show_all();
447 range_mark_label.set_name ("EditorTimeButton");
448 range_mark_label.set_size_request (-1, (int)timebar_height);
449 range_mark_label.set_alignment (1.0, 0.5);
450 range_mark_label.set_padding (5,0);
451 range_mark_label.hide();
452 range_mark_label.set_no_show_all();
454 transport_mark_label.set_name ("EditorTimeButton");
455 transport_mark_label.set_size_request (-1, (int)timebar_height);
456 transport_mark_label.set_alignment (1.0, 0.5);
457 transport_mark_label.set_padding (5,0);
458 transport_mark_label.hide();
459 transport_mark_label.set_no_show_all();
461 initialize_rulers ();
462 initialize_canvas ();
464 _summary = new EditorSummary (this);
466 selection->TimeChanged.connect (sigc::mem_fun(*this, &Editor::time_selection_changed));
467 selection->TracksChanged.connect (sigc::mem_fun(*this, &Editor::track_selection_changed));
469 editor_regions_selection_changed_connection = selection->RegionsChanged.connect (sigc::mem_fun(*this, &Editor::region_selection_changed));
471 selection->PointsChanged.connect (sigc::mem_fun(*this, &Editor::point_selection_changed));
472 selection->MarkersChanged.connect (sigc::mem_fun(*this, &Editor::marker_selection_changed));
474 edit_controls_vbox.set_spacing (0);
475 vertical_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &Editor::tie_vertical_scrolling), true);
476 track_canvas->signal_map_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_map_handler));
478 HBox* h = manage (new HBox);
479 _group_tabs = new EditorGroupTabs (this);
480 h->pack_start (*_group_tabs, PACK_SHRINK);
481 h->pack_start (edit_controls_vbox);
482 controls_layout.add (*h);
484 controls_layout.set_name ("EditControlsBase");
485 controls_layout.add_events (Gdk::SCROLL_MASK);
486 controls_layout.signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::control_layout_scroll), false);
488 controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
489 controls_layout.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_release));
491 _cursors = new MouseCursors;
493 ArdourCanvas::Canvas* time_pad = manage(new ArdourCanvas::Canvas());
494 ArdourCanvas::SimpleLine* pad_line_1 = manage(new ArdourCanvas::SimpleLine(*time_pad->root(),
495 0.0, 1.0, 100.0, 1.0));
497 pad_line_1->property_color_rgba() = 0xFF0000FF;
498 pad_line_1->show();
500 time_pad->show();
502 time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2);
503 time_canvas_vbox.set_size_request (-1, -1);
505 ruler_label_event_box.add (ruler_label_vbox);
506 ruler_label_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
507 ruler_label_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
509 time_button_event_box.add (time_button_vbox);
510 time_button_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
511 time_button_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
513 /* these enable us to have a dedicated window (for cursor setting, etc.)
514 for the canvas areas.
517 track_canvas_event_box.add (*track_canvas);
519 time_canvas_event_box.add (time_canvas_vbox);
520 time_canvas_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
522 edit_packer.set_col_spacings (0);
523 edit_packer.set_row_spacings (0);
524 edit_packer.set_homogeneous (false);
525 edit_packer.set_border_width (0);
526 edit_packer.set_name ("EditorWindow");
528 /* labels for the rulers */
529 edit_packer.attach (ruler_label_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0);
530 /* labels for the marker "tracks" */
531 edit_packer.attach (time_button_event_box, 1, 2, 1, 2, FILL, SHRINK, 0, 0);
532 /* the rulers */
533 edit_packer.attach (time_canvas_event_box, 2, 3, 0, 1, FILL|EXPAND, FILL, 0, 0);
534 /* track controls */
535 edit_packer.attach (controls_layout, 0, 2, 2, 3, FILL, FILL|EXPAND, 0, 0);
536 /* main canvas */
537 edit_packer.attach (track_canvas_event_box, 2, 3, 1, 3, FILL|EXPAND, FILL|EXPAND, 0, 0);
539 bottom_hbox.set_border_width (2);
540 bottom_hbox.set_spacing (3);
542 _route_groups = new EditorRouteGroups (this);
543 _routes = new EditorRoutes (this);
544 _regions = new EditorRegions (this);
545 _snapshots = new EditorSnapshots (this);
546 _locations = new EditorLocations (this);
548 add_notebook_page (_("Regions"), _regions->widget ());
549 add_notebook_page (_("Tracks & Busses"), _routes->widget ());
550 add_notebook_page (_("Snapshots"), _snapshots->widget ());
551 add_notebook_page (_("Route Groups"), _route_groups->widget ());
552 add_notebook_page (_("Ranges & Marks"), _locations->widget ());
554 _the_notebook.set_show_tabs (true);
555 _the_notebook.set_scrollable (true);
556 _the_notebook.popup_disable ();
557 _the_notebook.set_tab_pos (Gtk::POS_RIGHT);
558 _the_notebook.show_all ();
560 post_maximal_editor_width = 0;
561 post_maximal_horizontal_pane_position = 0;
562 post_maximal_editor_height = 0;
563 post_maximal_vertical_pane_position = 0;
564 _notebook_shrunk = false;
566 editor_summary_pane.pack1(edit_packer);
568 Button* summary_arrows_left_left = manage (new Button);
569 summary_arrows_left_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
570 summary_arrows_left_left->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), LEFT)));
571 summary_arrows_left_left->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
573 Button* summary_arrows_left_right = manage (new Button);
574 summary_arrows_left_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
575 summary_arrows_left_right->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), RIGHT)));
576 summary_arrows_left_right->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
578 VBox* summary_arrows_left = manage (new VBox);
579 summary_arrows_left->pack_start (*summary_arrows_left_left);
580 summary_arrows_left->pack_start (*summary_arrows_left_right);
582 Button* summary_arrows_right_up = manage (new Button);
583 summary_arrows_right_up->add (*manage (new Arrow (ARROW_UP, SHADOW_NONE)));
584 summary_arrows_right_up->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), UP)));
585 summary_arrows_right_up->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
587 Button* summary_arrows_right_down = manage (new Button);
588 summary_arrows_right_down->add (*manage (new Arrow (ARROW_DOWN, SHADOW_NONE)));
589 summary_arrows_right_down->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), DOWN)));
590 summary_arrows_right_down->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
592 VBox* summary_arrows_right = manage (new VBox);
593 summary_arrows_right->pack_start (*summary_arrows_right_up);
594 summary_arrows_right->pack_start (*summary_arrows_right_down);
596 Frame* summary_frame = manage (new Frame);
597 summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN);
599 summary_frame->add (*_summary);
600 summary_frame->show ();
602 _summary_hbox.pack_start (*summary_arrows_left, false, false);
603 _summary_hbox.pack_start (*summary_frame, true, true);
604 _summary_hbox.pack_start (*summary_arrows_right, false, false);
606 editor_summary_pane.pack2 (_summary_hbox);
608 edit_pane.pack1 (editor_summary_pane, true, true);
609 edit_pane.pack2 (_the_notebook, false, true);
611 editor_summary_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun (*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&editor_summary_pane)));
613 /* XXX: editor_summary_pane might need similar special OS X treatment to the edit_pane */
615 edit_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&edit_pane)));
616 #ifdef GTKOSX
617 Glib::PropertyProxy<int> proxy = edit_pane.property_position();
618 proxy.signal_changed().connect (bind (sigc::ptr_fun (pane_size_watcher), static_cast<Paned*> (&edit_pane)));
619 #endif
620 top_hbox.pack_start (toolbar_frame);
622 HBox *hbox = manage (new HBox);
623 hbox->pack_start (edit_pane, true, true);
625 global_vpacker.pack_start (top_hbox, false, false);
626 global_vpacker.pack_start (*hbox, true, true);
628 global_hpacker.pack_start (global_vpacker, true, true);
630 set_name ("EditorWindow");
631 add_accel_group (ActionManager::ui_manager->get_accel_group());
633 status_bar_hpacker.show ();
635 vpacker.pack_end (status_bar_hpacker, false, false);
636 vpacker.pack_end (global_hpacker, true, true);
638 /* register actions now so that set_state() can find them and set toggles/checks etc */
640 register_actions ();
642 setup_toolbar ();
643 setup_midi_toolbar ();
645 _snap_type = SnapToBeat;
646 set_snap_to (_snap_type);
647 _snap_mode = SnapOff;
648 set_snap_mode (_snap_mode);
649 set_mouse_mode (MouseObject, true);
650 pre_internal_mouse_mode = MouseObject;
651 set_edit_point_preference (EditAtMouse, true);
653 _playlist_selector = new PlaylistSelector();
654 _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
656 RegionView::RegionViewGoingAway.connect (*this, invalidator (*this), ui_bind (&Editor::catch_vanishing_regionview, this, _1), gui_context());
658 /* nudge stuff */
660 nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
661 nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
663 nudge_forward_button.set_name ("TransportButton");
664 nudge_backward_button.set_name ("TransportButton");
666 fade_context_menu.set_name ("ArdourContextMenu");
668 /* icons, titles, WM stuff */
670 list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
671 Glib::RefPtr<Gdk::Pixbuf> icon;
673 if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
674 window_icons.push_back (icon);
676 if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
677 window_icons.push_back (icon);
679 if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
680 window_icons.push_back (icon);
682 if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
683 window_icons.push_back (icon);
685 if (!window_icons.empty()) {
686 // set_icon_list (window_icons);
687 set_default_icon_list (window_icons);
690 WindowTitle title(Glib::get_application_name());
691 title += _("Editor");
692 set_title (title.get_string());
693 set_wmclass (X_("ardour_editor"), PROGRAM_NAME);
695 add (vpacker);
696 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
698 signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
699 signal_delete_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
701 /* allow external control surfaces/protocols to do various things */
703 ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context());
704 ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context());
705 ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context());
706 ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), ui_bind (&Editor::control_scroll, this, _1), gui_context());
707 BasicUI::AccessAction.connect (*this, invalidator (*this), ui_bind (&Editor::access_action, this, _1, _2), gui_context());
709 /* problematic: has to return a value and thus cannot be x-thread */
711 Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
713 Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
715 TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
717 _ignore_region_action = false;
718 _last_region_menu_was_main = false;
719 _popup_region_menu_item = 0;
721 _show_marker_lines = false;
722 _over_region_trim_target = false;
724 /* Button bindings */
726 button_bindings = new Bindings;
728 XMLNode* node = button_settings();
729 if (node) {
730 for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) {
731 button_bindings->load (**i);
735 constructed = true;
736 instant_save ();
738 setup_fade_images ();
741 Editor::~Editor()
743 #ifdef WITH_CMT
744 if(image_socket_listener) {
745 if(image_socket_listener->is_connected())
747 image_socket_listener->close_connection() ;
750 delete image_socket_listener ;
751 image_socket_listener = 0 ;
753 #endif
755 delete button_bindings;
756 delete _routes;
757 delete _route_groups;
758 delete track_canvas;
759 delete _drags;
762 XMLNode*
763 Editor::button_settings () const
765 XMLNode* settings = ARDOUR_UI::instance()->editor_settings();
766 XMLNode* node = find_named_node (*settings, X_("Buttons"));
768 if (!node) {
769 cerr << "new empty Button node\n";
770 node = new XMLNode (X_("Buttons"));
773 return node;
776 void
777 Editor::add_toplevel_controls (Container& cont)
779 vpacker.pack_start (cont, false, false);
780 cont.show_all ();
783 void
784 Editor::catch_vanishing_regionview (RegionView *rv)
786 /* note: the selection will take care of the vanishing
787 audioregionview by itself.
790 if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
791 _drags->abort ();
794 if (clicked_regionview == rv) {
795 clicked_regionview = 0;
798 if (entered_regionview == rv) {
799 set_entered_regionview (0);
802 if (!_all_region_actions_sensitized) {
803 sensitize_all_region_actions (true);
806 _over_region_trim_target = false;
809 void
810 Editor::set_entered_regionview (RegionView* rv)
812 if (rv == entered_regionview) {
813 return;
816 if (entered_regionview) {
817 entered_regionview->exited ();
820 if ((entered_regionview = rv) != 0) {
821 entered_regionview->entered (internal_editing ());
824 if (!_all_region_actions_sensitized && _last_region_menu_was_main) {
825 /* This RegionView entry might have changed what region actions
826 are allowed, so sensitize them all in case a key is pressed.
828 sensitize_all_region_actions (true);
832 void
833 Editor::set_entered_track (TimeAxisView* tav)
835 if (entered_track) {
836 entered_track->exited ();
839 if ((entered_track = tav) != 0) {
840 entered_track->entered ();
844 void
845 Editor::show_window ()
847 if (!is_visible ()) {
848 show_all ();
850 /* XXX: this is a bit unfortunate; it would probably
851 be nicer if we could just call show () above rather
852 than needing the show_all ()
855 /* re-hide stuff if necessary */
856 editor_list_button_toggled ();
857 parameter_changed ("show-summary");
858 parameter_changed ("show-group-tabs");
859 parameter_changed ("show-zoom-tools");
861 /* now reset all audio_time_axis heights, because widgets might need
862 to be re-hidden
865 TimeAxisView *tv;
867 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
868 tv = (static_cast<TimeAxisView*>(*i));
869 tv->reset_height ();
872 if (current_mixer_strip) {
873 current_mixer_strip->hide_things ();
877 present ();
880 void
881 Editor::instant_save ()
883 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
884 return;
887 if (_session) {
888 _session->add_instant_xml(get_state());
889 } else {
890 Config->add_instant_xml(get_state());
894 void
895 Editor::zoom_adjustment_changed ()
897 if (_session == 0) {
898 return;
901 double fpu = zoom_range_clock->current_duration() / _canvas_width;
903 if (fpu < 1.0) {
904 fpu = 1.0;
905 zoom_range_clock->set ((framepos_t) floor (fpu * _canvas_width));
906 } else if (fpu > _session->current_end_frame() / _canvas_width) {
907 fpu = _session->current_end_frame() / _canvas_width;
908 zoom_range_clock->set ((framepos_t) floor (fpu * _canvas_width));
911 temporal_zoom (fpu);
914 void
915 Editor::control_scroll (float fraction)
917 ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
919 if (!_session) {
920 return;
923 double step = fraction * current_page_frames();
926 _control_scroll_target is an optional<T>
928 it acts like a pointer to an framepos_t, with
929 a operator conversion to boolean to check
930 that it has a value could possibly use
931 playhead_cursor->current_frame to store the
932 value and a boolean in the class to know
933 when it's out of date
936 if (!_control_scroll_target) {
937 _control_scroll_target = _session->transport_frame();
938 _dragging_playhead = true;
941 if ((fraction < 0.0f) && (*_control_scroll_target < (framepos_t) fabs(step))) {
942 *_control_scroll_target = 0;
943 } else if ((fraction > 0.0f) && (max_framepos - *_control_scroll_target < step)) {
944 *_control_scroll_target = max_framepos - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
945 } else {
946 *_control_scroll_target += (framepos_t) floor (step);
949 /* move visuals, we'll catch up with it later */
951 playhead_cursor->set_position (*_control_scroll_target);
952 UpdateAllTransportClocks (*_control_scroll_target);
954 if (*_control_scroll_target > (current_page_frames() / 2)) {
955 /* try to center PH in window */
956 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
957 } else {
958 reset_x_origin (0);
962 Now we do a timeout to actually bring the session to the right place
963 according to the playhead. This is to avoid reading disk buffers on every
964 call to control_scroll, which is driven by ScrollTimeline and therefore
965 probably by a control surface wheel which can generate lots of events.
967 /* cancel the existing timeout */
969 control_scroll_connection.disconnect ();
971 /* add the next timeout */
973 control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
976 bool
977 Editor::deferred_control_scroll (framepos_t /*target*/)
979 _session->request_locate (*_control_scroll_target, _session->transport_rolling());
980 // reset for next stream
981 _control_scroll_target = boost::none;
982 _dragging_playhead = false;
983 return false;
986 void
987 Editor::access_action (std::string action_group, std::string action_item)
989 if (!_session) {
990 return;
993 ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
995 RefPtr<Action> act;
996 act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
998 if (act) {
999 act->activate();
1003 void
1004 Editor::on_realize ()
1006 Window::on_realize ();
1007 Realized ();
1010 void
1011 Editor::map_position_change (framepos_t frame)
1013 ENSURE_GUI_THREAD (*this, &Editor::map_position_change, frame)
1015 if (_session == 0) {
1016 return;
1019 if (_follow_playhead) {
1020 center_screen (frame);
1023 playhead_cursor->set_position (frame);
1026 void
1027 Editor::center_screen (framepos_t frame)
1029 double page = _canvas_width * frames_per_unit;
1031 /* if we're off the page, then scroll.
1034 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
1035 center_screen_internal (frame, page);
1039 void
1040 Editor::center_screen_internal (framepos_t frame, float page)
1042 page /= 2;
1044 if (frame > page) {
1045 frame -= (framepos_t) page;
1046 } else {
1047 frame = 0;
1050 reset_x_origin (frame);
1054 void
1055 Editor::update_title ()
1057 ENSURE_GUI_THREAD (*this, &Editor::update_title)
1059 if (_session) {
1060 bool dirty = _session->dirty();
1062 string session_name;
1064 if (_session->snap_name() != _session->name()) {
1065 session_name = _session->snap_name();
1066 } else {
1067 session_name = _session->name();
1070 if (dirty) {
1071 session_name = "*" + session_name;
1074 WindowTitle title(session_name);
1075 title += Glib::get_application_name();
1076 set_title (title.get_string());
1080 void
1081 Editor::set_session (Session *t)
1083 SessionHandlePtr::set_session (t);
1085 if (!_session) {
1086 return;
1089 zoom_range_clock->set_session (_session);
1090 _playlist_selector->set_session (_session);
1091 nudge_clock->set_session (_session);
1092 _summary->set_session (_session);
1093 _group_tabs->set_session (_session);
1094 _route_groups->set_session (_session);
1095 _regions->set_session (_session);
1096 _snapshots->set_session (_session);
1097 _routes->set_session (_session);
1098 _locations->set_session (_session);
1100 if (rhythm_ferret) {
1101 rhythm_ferret->set_session (_session);
1104 if (analysis_window) {
1105 analysis_window->set_session (_session);
1108 if (sfbrowser) {
1109 sfbrowser->set_session (_session);
1112 compute_fixed_ruler_scale ();
1114 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1115 set_state (*node, Stateful::loading_state_version);
1117 /* catch up with the playhead */
1119 _session->request_locate (playhead_cursor->current_frame);
1120 _pending_initial_locate = true;
1122 update_title ();
1124 /* These signals can all be emitted by a non-GUI thread. Therefore the
1125 handlers for them must not attempt to directly interact with the GUI,
1126 but use Gtkmm2ext::UI::instance()->call_slot();
1129 _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), ui_bind(&Editor::step_edit_status_change, this, _1), gui_context());
1130 _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1131 _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context());
1132 _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context());
1133 _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1134 _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context());
1135 _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1136 _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
1137 _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Editor::session_state_saved, this, _1), gui_context());
1138 _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context());
1139 _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&Editor::location_gone, this, _1), gui_context());
1140 _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1141 _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display, this), gui_context());
1142 _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1144 if (Profile->get_sae()) {
1145 Timecode::BBT_Time bbt;
1146 bbt.bars = 0;
1147 bbt.beats = 0;
1148 bbt.ticks = 120;
1149 framepos_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1);
1150 nudge_clock->set_mode(AudioClock::BBT);
1151 nudge_clock->set (pos, true, 0, AudioClock::BBT);
1153 } else {
1154 nudge_clock->set (_session->frame_rate() * 5, true, 0, AudioClock::Timecode); // default of 5 seconds
1157 playhead_cursor->canvas_item.show ();
1159 Location* loc = _session->locations()->auto_loop_location();
1160 if (loc == 0) {
1161 loc = new Location (*_session, 0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1163 if (loc->start() == loc->end()) {
1164 loc->set_end (loc->start() + 1);
1167 _session->locations()->add (loc, false);
1168 _session->set_auto_loop_location (loc);
1169 } else {
1170 // force name
1171 loc->set_name (_("Loop"));
1174 loc = _session->locations()->auto_punch_location();
1176 if (loc == 0) {
1177 loc = new Location (*_session, 0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1179 if (loc->start() == loc->end()) {
1180 loc->set_end (loc->start() + 1);
1183 _session->locations()->add (loc, false);
1184 _session->set_auto_punch_location (loc);
1185 } else {
1186 // force name
1187 loc->set_name (_("Punch"));
1190 boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1191 Config->map_parameters (pc);
1192 _session->config.map_parameters (pc);
1194 refresh_location_display ();
1196 restore_ruler_visibility ();
1197 //tempo_map_changed (PropertyChange (0));
1198 _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1200 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1201 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1204 super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
1205 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1208 switch (_snap_type) {
1209 case SnapToRegionStart:
1210 case SnapToRegionEnd:
1211 case SnapToRegionSync:
1212 case SnapToRegionBoundary:
1213 build_region_boundary_cache ();
1214 break;
1216 default:
1217 break;
1220 /* register for undo history */
1221 _session->register_with_memento_command_factory(_id, this);
1223 ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated));
1225 start_updating_meters ();
1228 void
1229 Editor::action_pre_activated (Glib::RefPtr<Action> const & a)
1231 if (a->get_name() == "RegionMenu") {
1232 /* When the main menu's region menu is opened, we setup the actions so that they look right
1233 in the menu. I can't find a way of getting a signal when this menu is subsequently closed,
1234 so we resensitize all region actions when the entered regionview or the region selection
1235 changes. HOWEVER we can't always resensitize on entered_regionview change because that
1236 happens after the region context menu is opened. So we set a flag here, too.
1238 What a carry on :(
1240 sensitize_the_right_region_actions ();
1241 _last_region_menu_was_main = true;
1245 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1246 void
1247 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1249 using namespace Menu_Helpers;
1250 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1252 if (arv == 0) {
1253 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1254 /*NOTREACHED*/
1257 MenuList& items (fade_context_menu.items());
1259 items.clear ();
1261 switch (item_type) {
1262 case FadeInItem:
1263 case FadeInHandleItem:
1264 if (arv->audio_region()->fade_in_active()) {
1265 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1266 } else {
1267 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1270 items.push_back (SeparatorElem());
1272 if (Profile->get_sae()) {
1274 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)));
1275 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)));
1277 } else {
1279 items.push_back (
1280 ImageMenuElem (
1281 _("Linear"),
1282 *_fade_in_images[FadeLinear],
1283 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)
1287 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1289 items.push_back (
1290 ImageMenuElem (
1291 _("Slowest"),
1292 *_fade_in_images[FadeFast],
1293 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)
1296 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1298 items.push_back (
1299 ImageMenuElem (
1300 _("Slow"),
1301 *_fade_in_images[FadeLogB],
1302 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogB)
1305 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1307 items.push_back (
1308 ImageMenuElem (
1309 _("Fast"),
1310 *_fade_in_images[FadeLogA],
1311 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogA)
1314 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1316 items.push_back (
1317 ImageMenuElem (
1318 _("Fastest"),
1319 *_fade_in_images[FadeSlow],
1320 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow)
1323 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1326 break;
1328 case FadeOutItem:
1329 case FadeOutHandleItem:
1330 if (arv->audio_region()->fade_out_active()) {
1331 items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1332 } else {
1333 items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1336 items.push_back (SeparatorElem());
1338 if (Profile->get_sae()) {
1339 items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)));
1340 items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)));
1341 } else {
1343 items.push_back (
1344 ImageMenuElem (
1345 _("Linear"),
1346 *_fade_out_images[FadeLinear],
1347 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)
1351 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1353 items.push_back (
1354 ImageMenuElem (
1355 _("Slowest"),
1356 *_fade_out_images[FadeFast],
1357 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)
1360 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1362 items.push_back (
1363 ImageMenuElem (
1364 _("Slow"),
1365 *_fade_out_images[FadeLogB],
1366 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogA)
1369 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1371 items.push_back (
1372 ImageMenuElem (
1373 _("Fast"),
1374 *_fade_out_images[FadeLogA],
1375 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogB)
1378 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1380 items.push_back (
1381 ImageMenuElem (
1382 _("Fastest"),
1383 *_fade_out_images[FadeSlow],
1384 sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast)
1387 dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1390 break;
1392 default:
1393 fatal << _("programming error: ")
1394 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1395 << endmsg;
1396 /*NOTREACHED*/
1399 fade_context_menu.popup (button, time);
1402 void
1403 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1405 using namespace Menu_Helpers;
1406 Menu* (Editor::*build_menu_function)();
1407 Menu *menu;
1409 switch (item_type) {
1410 case RegionItem:
1411 case RegionViewName:
1412 case RegionViewNameHighlight:
1413 case LeftFrameHandle:
1414 case RightFrameHandle:
1415 if (with_selection) {
1416 build_menu_function = &Editor::build_track_selection_context_menu;
1417 } else {
1418 build_menu_function = &Editor::build_track_region_context_menu;
1420 break;
1422 case SelectionItem:
1423 if (with_selection) {
1424 build_menu_function = &Editor::build_track_selection_context_menu;
1425 } else {
1426 build_menu_function = &Editor::build_track_context_menu;
1428 break;
1430 case CrossfadeViewItem:
1431 build_menu_function = &Editor::build_track_crossfade_context_menu;
1432 break;
1434 case StreamItem:
1435 if (clicked_routeview->track()) {
1436 build_menu_function = &Editor::build_track_context_menu;
1437 } else {
1438 build_menu_function = &Editor::build_track_bus_context_menu;
1440 break;
1442 default:
1443 /* probably shouldn't happen but if it does, we don't care */
1444 return;
1447 menu = (this->*build_menu_function)();
1448 menu->set_name ("ArdourContextMenu");
1450 /* now handle specific situations */
1452 switch (item_type) {
1453 case RegionItem:
1454 case RegionViewName:
1455 case RegionViewNameHighlight:
1456 case LeftFrameHandle:
1457 case RightFrameHandle:
1458 if (!with_selection) {
1459 if (region_edit_menu_split_item) {
1460 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1461 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1462 } else {
1463 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1466 if (region_edit_menu_split_multichannel_item) {
1467 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1468 region_edit_menu_split_multichannel_item->set_sensitive (true);
1469 } else {
1470 region_edit_menu_split_multichannel_item->set_sensitive (false);
1474 break;
1476 case SelectionItem:
1477 break;
1479 case CrossfadeViewItem:
1480 break;
1482 case StreamItem:
1483 break;
1485 default:
1486 /* probably shouldn't happen but if it does, we don't care */
1487 return;
1490 if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1492 /* Bounce to disk */
1494 using namespace Menu_Helpers;
1495 MenuList& edit_items = menu->items();
1497 edit_items.push_back (SeparatorElem());
1499 switch (clicked_routeview->audio_track()->freeze_state()) {
1500 case AudioTrack::NoFreeze:
1501 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1502 break;
1504 case AudioTrack::Frozen:
1505 edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1506 break;
1508 case AudioTrack::UnFrozen:
1509 edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1510 break;
1515 if (item_type == StreamItem && clicked_routeview) {
1516 clicked_routeview->build_underlay_menu(menu);
1519 /* When the region menu is opened, we setup the actions so that they look right
1520 in the menu.
1522 sensitize_the_right_region_actions ();
1523 _last_region_menu_was_main = false;
1525 menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1526 menu->popup (button, time);
1529 Menu*
1530 Editor::build_track_context_menu ()
1532 using namespace Menu_Helpers;
1534 MenuList& edit_items = track_context_menu.items();
1535 edit_items.clear();
1537 add_dstream_context_items (edit_items);
1538 return &track_context_menu;
1541 Menu*
1542 Editor::build_track_bus_context_menu ()
1544 using namespace Menu_Helpers;
1546 MenuList& edit_items = track_context_menu.items();
1547 edit_items.clear();
1549 add_bus_context_items (edit_items);
1550 return &track_context_menu;
1553 Menu*
1554 Editor::build_track_region_context_menu ()
1556 using namespace Menu_Helpers;
1557 MenuList& edit_items = track_region_context_menu.items();
1558 edit_items.clear();
1560 /* we've just cleared the track region context menu, so the menu that these
1561 two items were on will have disappeared; stop them dangling.
1563 region_edit_menu_split_item = 0;
1564 region_edit_menu_split_multichannel_item = 0;
1566 /* we might try to use items that are currently attached to a crossfade menu,
1567 so clear that, too.
1569 track_crossfade_context_menu.items().clear ();
1571 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1573 if (rtv) {
1574 boost::shared_ptr<Track> tr;
1575 boost::shared_ptr<Playlist> pl;
1577 if ((tr = rtv->track())) {
1578 add_region_context_items (edit_items, tr);
1582 add_dstream_context_items (edit_items);
1584 return &track_region_context_menu;
1587 Menu*
1588 Editor::build_track_crossfade_context_menu ()
1590 using namespace Menu_Helpers;
1591 MenuList& edit_items = track_crossfade_context_menu.items();
1592 edit_items.clear ();
1594 /* we might try to use items that are currently attached to a crossfade menu,
1595 so clear that, too.
1597 track_region_context_menu.items().clear ();
1599 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1601 if (atv) {
1602 boost::shared_ptr<Track> tr;
1603 boost::shared_ptr<Playlist> pl;
1604 boost::shared_ptr<AudioPlaylist> apl;
1606 if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1608 AudioPlaylist::Crossfades xfades;
1609 framepos_t where;
1610 bool ignored;
1612 /* The xfade menu is a bit of a special case, as we always use the mouse position
1613 to decide whether or not to display it (rather than the edit point). No particularly
1614 strong reasons for this, other than it is a bit surprising to right-click on a xfade
1615 and not get a menu.
1617 mouse_frame (where, ignored);
1618 apl->crossfades_at (where, xfades);
1620 bool const many = xfades.size() > 1;
1622 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1623 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1626 add_region_context_items (edit_items, tr);
1630 add_dstream_context_items (edit_items);
1632 return &track_crossfade_context_menu;
1635 void
1636 Editor::analyze_region_selection ()
1638 if (analysis_window == 0) {
1639 analysis_window = new AnalysisWindow();
1641 if (_session != 0)
1642 analysis_window->set_session(_session);
1644 analysis_window->show_all();
1647 analysis_window->set_regionmode();
1648 analysis_window->analyze();
1650 analysis_window->present();
1653 void
1654 Editor::analyze_range_selection()
1656 if (analysis_window == 0) {
1657 analysis_window = new AnalysisWindow();
1659 if (_session != 0)
1660 analysis_window->set_session(_session);
1662 analysis_window->show_all();
1665 analysis_window->set_rangemode();
1666 analysis_window->analyze();
1668 analysis_window->present();
1671 Menu*
1672 Editor::build_track_selection_context_menu ()
1674 using namespace Menu_Helpers;
1675 MenuList& edit_items = track_selection_context_menu.items();
1676 edit_items.clear ();
1678 add_selection_context_items (edit_items);
1679 // edit_items.push_back (SeparatorElem());
1680 // add_dstream_context_items (edit_items);
1682 return &track_selection_context_menu;
1685 /** Add context menu items relevant to crossfades.
1686 * @param edit_items List to add the items to.
1688 void
1689 Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1691 using namespace Menu_Helpers;
1692 Menu *xfade_menu = manage (new Menu);
1693 MenuList& items = xfade_menu->items();
1694 xfade_menu->set_name ("ArdourContextMenu");
1695 string str;
1697 if (xfade->active()) {
1698 str = _("Mute");
1699 } else {
1700 str = _("Unmute");
1703 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1704 items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1706 if (xfade->can_follow_overlap()) {
1708 if (xfade->following_overlap()) {
1709 str = _("Convert to Short");
1710 } else {
1711 str = _("Convert to Full");
1714 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1717 if (many) {
1718 str = xfade->out()->name();
1719 str += "->";
1720 str += xfade->in()->name();
1721 } else {
1722 str = _("Crossfade");
1725 edit_items.push_back (MenuElem (str, *xfade_menu));
1726 edit_items.push_back (SeparatorElem());
1729 void
1730 Editor::xfade_edit_left_region ()
1732 if (clicked_crossfadeview) {
1733 clicked_crossfadeview->left_view.show_region_editor ();
1737 void
1738 Editor::xfade_edit_right_region ()
1740 if (clicked_crossfadeview) {
1741 clicked_crossfadeview->right_view.show_region_editor ();
1745 void
1746 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::shared_ptr<Track> track)
1748 using namespace Menu_Helpers;
1750 /* OK, stick the region submenu at the top of the list, and then add
1751 the standard items.
1754 RegionSelection rs = get_regions_from_selection_and_entered ();
1756 string::size_type pos = 0;
1757 string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1759 /* we have to hack up the region name because "_" has a special
1760 meaning for menu titles.
1763 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1764 menu_item_name.replace (pos, 1, "__");
1765 pos += 2;
1768 if (_popup_region_menu_item == 0) {
1769 _popup_region_menu_item = new MenuItem (menu_item_name);
1770 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1771 _popup_region_menu_item->show ();
1772 } else {
1773 _popup_region_menu_item->set_label (menu_item_name);
1776 /* Use the mouse position rather than the edit point to decide whether to show the `choose top region'
1777 dialogue. If we use the edit point it gets a bit messy because the user still has to click over
1778 *some* region in order to get the region context menu stuff to be displayed at all.
1781 framepos_t mouse;
1782 bool ignored;
1783 mouse_frame (mouse, ignored);
1785 edit_items.push_back (*_popup_region_menu_item);
1786 if (track->playlist()->count_regions_at (mouse) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1787 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region")->create_menu_item ()));
1789 edit_items.push_back (SeparatorElem());
1792 /** Add context menu items relevant to selection ranges.
1793 * @param edit_items List to add the items to.
1795 void
1796 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1798 using namespace Menu_Helpers;
1800 edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1801 edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1803 edit_items.push_back (SeparatorElem());
1804 edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1806 edit_items.push_back (SeparatorElem());
1808 edit_items.push_back (
1809 MenuElem (
1810 _("Move Range Start to Previous Region Boundary"),
1811 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, false)
1815 edit_items.push_back (
1816 MenuElem (
1817 _("Move Range Start to Next Region Boundary"),
1818 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, true)
1822 edit_items.push_back (
1823 MenuElem (
1824 _("Move Range End to Previous Region Boundary"),
1825 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, false)
1829 edit_items.push_back (
1830 MenuElem (
1831 _("Move Range End to Next Region Boundary"),
1832 sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, true)
1836 edit_items.push_back (SeparatorElem());
1837 edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection)));
1838 edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1840 edit_items.push_back (SeparatorElem());
1841 edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1843 edit_items.push_back (SeparatorElem());
1844 edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1845 edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1847 edit_items.push_back (SeparatorElem());
1848 edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1850 edit_items.push_back (SeparatorElem());
1851 edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1852 edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1853 edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1855 edit_items.push_back (SeparatorElem());
1856 edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1857 edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1858 edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1859 edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1860 edit_items.push_back (MenuElem (_("Export Range"), sigc::mem_fun(*this, &Editor::export_selection)));
1864 void
1865 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1867 using namespace Menu_Helpers;
1869 /* Playback */
1871 Menu *play_menu = manage (new Menu);
1872 MenuList& play_items = play_menu->items();
1873 play_menu->set_name ("ArdourContextMenu");
1875 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1876 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1877 play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1878 play_items.push_back (SeparatorElem());
1879 play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
1881 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1883 /* Selection */
1885 Menu *select_menu = manage (new Menu);
1886 MenuList& select_items = select_menu->items();
1887 select_menu->set_name ("ArdourContextMenu");
1889 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1890 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1891 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1892 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1893 select_items.push_back (SeparatorElem());
1894 select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
1895 select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
1896 select_items.push_back (SeparatorElem());
1897 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1898 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1899 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1900 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1901 select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
1902 select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
1903 select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
1905 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1907 /* Cut-n-Paste */
1909 Menu *cutnpaste_menu = manage (new Menu);
1910 MenuList& cutnpaste_items = cutnpaste_menu->items();
1911 cutnpaste_menu->set_name ("ArdourContextMenu");
1913 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1914 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1915 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1917 cutnpaste_items.push_back (SeparatorElem());
1919 cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
1920 cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
1922 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
1924 /* Adding new material */
1926 edit_items.push_back (SeparatorElem());
1927 edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
1928 edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
1930 /* Nudge track */
1932 Menu *nudge_menu = manage (new Menu());
1933 MenuList& nudge_items = nudge_menu->items();
1934 nudge_menu->set_name ("ArdourContextMenu");
1936 edit_items.push_back (SeparatorElem());
1937 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1938 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1939 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1940 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1942 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1945 void
1946 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
1948 using namespace Menu_Helpers;
1950 /* Playback */
1952 Menu *play_menu = manage (new Menu);
1953 MenuList& play_items = play_menu->items();
1954 play_menu->set_name ("ArdourContextMenu");
1956 play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1957 play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1958 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1960 /* Selection */
1962 Menu *select_menu = manage (new Menu);
1963 MenuList& select_items = select_menu->items();
1964 select_menu->set_name ("ArdourContextMenu");
1966 select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1967 select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1968 select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1969 select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1970 select_items.push_back (SeparatorElem());
1971 select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1972 select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1973 select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1974 select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1976 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1978 /* Cut-n-Paste */
1980 Menu *cutnpaste_menu = manage (new Menu);
1981 MenuList& cutnpaste_items = cutnpaste_menu->items();
1982 cutnpaste_menu->set_name ("ArdourContextMenu");
1984 cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1985 cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1986 cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1988 Menu *nudge_menu = manage (new Menu());
1989 MenuList& nudge_items = nudge_menu->items();
1990 nudge_menu->set_name ("ArdourContextMenu");
1992 edit_items.push_back (SeparatorElem());
1993 nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1994 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1995 nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1996 nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1998 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2001 SnapType
2002 Editor::snap_type() const
2004 return _snap_type;
2007 SnapMode
2008 Editor::snap_mode() const
2010 return _snap_mode;
2013 void
2014 Editor::set_snap_to (SnapType st)
2016 unsigned int snap_ind = (unsigned int)st;
2018 _snap_type = st;
2020 if (snap_ind > snap_type_strings.size() - 1) {
2021 snap_ind = 0;
2022 _snap_type = (SnapType)snap_ind;
2025 string str = snap_type_strings[snap_ind];
2027 if (str != snap_type_selector.get_active_text()) {
2028 snap_type_selector.set_active_text (str);
2031 instant_save ();
2033 switch (_snap_type) {
2034 case SnapToBeatDiv32:
2035 case SnapToBeatDiv28:
2036 case SnapToBeatDiv24:
2037 case SnapToBeatDiv20:
2038 case SnapToBeatDiv16:
2039 case SnapToBeatDiv14:
2040 case SnapToBeatDiv12:
2041 case SnapToBeatDiv10:
2042 case SnapToBeatDiv8:
2043 case SnapToBeatDiv7:
2044 case SnapToBeatDiv6:
2045 case SnapToBeatDiv5:
2046 case SnapToBeatDiv4:
2047 case SnapToBeatDiv3:
2048 case SnapToBeatDiv2:
2049 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
2050 update_tempo_based_rulers ();
2051 break;
2053 case SnapToRegionStart:
2054 case SnapToRegionEnd:
2055 case SnapToRegionSync:
2056 case SnapToRegionBoundary:
2057 build_region_boundary_cache ();
2058 break;
2060 default:
2061 /* relax */
2062 break;
2065 SnapChanged (); /* EMIT SIGNAL */
2068 void
2069 Editor::set_snap_mode (SnapMode mode)
2071 _snap_mode = mode;
2072 string str = snap_mode_strings[(int)mode];
2074 if (str != snap_mode_selector.get_active_text ()) {
2075 snap_mode_selector.set_active_text (str);
2078 instant_save ();
2080 void
2081 Editor::set_edit_point_preference (EditPoint ep, bool force)
2083 bool changed = (_edit_point != ep);
2085 _edit_point = ep;
2086 string str = edit_point_strings[(int)ep];
2088 if (str != edit_point_selector.get_active_text ()) {
2089 edit_point_selector.set_active_text (str);
2092 set_canvas_cursor ();
2094 if (!force && !changed) {
2095 return;
2098 const char* action=NULL;
2100 switch (_edit_point) {
2101 case EditAtPlayhead:
2102 action = "edit-at-playhead";
2103 break;
2104 case EditAtSelectedMarker:
2105 action = "edit-at-marker";
2106 break;
2107 case EditAtMouse:
2108 action = "edit-at-mouse";
2109 break;
2112 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2113 if (act) {
2114 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2117 framepos_t foo;
2118 bool in_track_canvas;
2120 if (!mouse_frame (foo, in_track_canvas)) {
2121 in_track_canvas = false;
2124 reset_canvas_action_sensitivity (in_track_canvas);
2126 instant_save ();
2130 Editor::set_state (const XMLNode& node, int /*version*/)
2132 const XMLProperty* prop;
2133 XMLNode* geometry;
2134 int x, y;
2135 Gdk::Geometry g;
2137 if ((prop = node.property ("id")) != 0) {
2138 _id = prop->value ();
2141 g.base_width = default_width;
2142 g.base_height = default_height;
2143 x = 1;
2144 y = 1;
2146 if ((geometry = find_named_node (node, "geometry")) != 0) {
2148 XMLProperty* prop;
2150 if ((prop = geometry->property("x_size")) == 0) {
2151 prop = geometry->property ("x-size");
2153 if (prop) {
2154 g.base_width = atoi(prop->value());
2156 if ((prop = geometry->property("y_size")) == 0) {
2157 prop = geometry->property ("y-size");
2159 if (prop) {
2160 g.base_height = atoi(prop->value());
2163 if ((prop = geometry->property ("x_pos")) == 0) {
2164 prop = geometry->property ("x-pos");
2166 if (prop) {
2167 x = atoi (prop->value());
2170 if ((prop = geometry->property ("y_pos")) == 0) {
2171 prop = geometry->property ("y-pos");
2173 if (prop) {
2174 y = atoi (prop->value());
2178 set_default_size (g.base_width, g.base_height);
2179 move (x, y);
2181 if (_session && (prop = node.property ("playhead"))) {
2182 framepos_t pos;
2183 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2184 playhead_cursor->set_position (pos);
2185 } else {
2186 playhead_cursor->set_position (0);
2189 if ((prop = node.property ("mixer-width"))) {
2190 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2193 if ((prop = node.property ("zoom-focus"))) {
2194 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2197 if ((prop = node.property ("zoom"))) {
2198 reset_zoom (PBD::atof (prop->value()));
2199 } else {
2200 reset_zoom (frames_per_unit);
2203 if ((prop = node.property ("snap-to"))) {
2204 set_snap_to ((SnapType) atoi (prop->value()));
2207 if ((prop = node.property ("snap-mode"))) {
2208 set_snap_mode ((SnapMode) atoi (prop->value()));
2211 if ((prop = node.property ("mouse-mode"))) {
2212 MouseMode m = str2mousemode(prop->value());
2213 set_mouse_mode (m, true);
2214 } else {
2215 set_mouse_mode (MouseObject, true);
2218 if ((prop = node.property ("left-frame")) != 0) {
2219 framepos_t pos;
2220 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2221 reset_x_origin (pos);
2225 if ((prop = node.property ("y-origin")) != 0) {
2226 reset_y_origin (atof (prop->value ()));
2229 if ((prop = node.property ("internal-edit"))) {
2230 bool yn = string_is_affirmative (prop->value());
2231 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2232 if (act) {
2233 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2234 tact->set_active (!yn);
2235 tact->set_active (yn);
2239 if ((prop = node.property ("join-object-range"))) {
2240 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2243 if ((prop = node.property ("edit-point"))) {
2244 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2247 if ((prop = node.property ("show-measures"))) {
2248 bool yn = string_is_affirmative (prop->value());
2249 _show_measures = yn;
2250 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2251 if (act) {
2252 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2253 /* do it twice to force the change */
2254 tact->set_active (!yn);
2255 tact->set_active (yn);
2259 if ((prop = node.property ("follow-playhead"))) {
2260 bool yn = string_is_affirmative (prop->value());
2261 set_follow_playhead (yn);
2262 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2263 if (act) {
2264 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2265 if (tact->get_active() != yn) {
2266 tact->set_active (yn);
2271 if ((prop = node.property ("stationary-playhead"))) {
2272 bool yn = string_is_affirmative (prop->value());
2273 set_stationary_playhead (yn);
2274 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2275 if (act) {
2276 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2277 if (tact->get_active() != yn) {
2278 tact->set_active (yn);
2283 if ((prop = node.property ("region-list-sort-type"))) {
2284 RegionListSortType st;
2285 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2288 if ((prop = node.property ("xfades-visible"))) {
2289 bool yn = string_is_affirmative (prop->value());
2290 _xfade_visibility = !yn;
2291 // set_xfade_visibility (yn);
2294 if ((prop = node.property ("show-editor-mixer"))) {
2296 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2297 assert (act);
2299 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2300 bool yn = string_is_affirmative (prop->value());
2302 /* do it twice to force the change */
2304 tact->set_active (!yn);
2305 tact->set_active (yn);
2308 if ((prop = node.property ("show-editor-list"))) {
2310 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2311 assert (act);
2313 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2314 bool yn = string_is_affirmative (prop->value());
2316 /* do it twice to force the change */
2318 tact->set_active (!yn);
2319 tact->set_active (yn);
2322 if ((prop = node.property (X_("editor-list-page")))) {
2323 _the_notebook.set_current_page (atoi (prop->value ()));
2326 if ((prop = node.property (X_("show-marker-lines")))) {
2327 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2328 assert (act);
2329 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2330 bool yn = string_is_affirmative (prop->value ());
2332 tact->set_active (!yn);
2333 tact->set_active (yn);
2336 XMLNodeList children = node.children ();
2337 for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2338 selection->set_state (**i, Stateful::current_state_version);
2339 _regions->set_state (**i);
2342 return 0;
2345 XMLNode&
2346 Editor::get_state ()
2348 XMLNode* node = new XMLNode ("Editor");
2349 char buf[32];
2351 _id.print (buf, sizeof (buf));
2352 node->add_property ("id", buf);
2354 if (is_realized()) {
2355 Glib::RefPtr<Gdk::Window> win = get_window();
2357 int x, y, width, height;
2358 win->get_root_origin(x, y);
2359 win->get_size(width, height);
2361 XMLNode* geometry = new XMLNode ("geometry");
2363 snprintf(buf, sizeof(buf), "%d", width);
2364 geometry->add_property("x-size", string(buf));
2365 snprintf(buf, sizeof(buf), "%d", height);
2366 geometry->add_property("y-size", string(buf));
2367 snprintf(buf, sizeof(buf), "%d", x);
2368 geometry->add_property("x-pos", string(buf));
2369 snprintf(buf, sizeof(buf), "%d", y);
2370 geometry->add_property("y-pos", string(buf));
2371 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2372 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2373 geometry->add_property("notebook-shrunk", _notebook_shrunk ? "1" : "0");
2374 snprintf(buf,sizeof(buf), "%d",pre_maximal_horizontal_pane_position);
2375 geometry->add_property("pre-maximal-horizontal-pane-position", string(buf));
2376 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2377 geometry->add_property("edit-vertical-pane-pos", string(buf));
2379 node->add_child_nocopy (*geometry);
2382 maybe_add_mixer_strip_width (*node);
2384 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2385 node->add_property ("zoom-focus", buf);
2386 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2387 node->add_property ("zoom", buf);
2388 snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2389 node->add_property ("snap-to", buf);
2390 snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2391 node->add_property ("snap-mode", buf);
2393 node->add_property ("edit-point", enum_2_string (_edit_point));
2395 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2396 node->add_property ("playhead", buf);
2397 snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2398 node->add_property ("left-frame", buf);
2399 snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2400 node->add_property ("y-origin", buf);
2402 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2403 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2404 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2405 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2406 node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2407 node->add_property ("mouse-mode", enum2str(mouse_mode));
2408 node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2409 node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2411 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2412 if (act) {
2413 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2414 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2417 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2418 if (act) {
2419 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2420 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2423 snprintf (buf, sizeof (buf), "%d", _the_notebook.get_current_page ());
2424 node->add_property (X_("editor-list-page"), buf);
2426 if (button_bindings) {
2427 XMLNode* bb = new XMLNode (X_("Buttons"));
2428 button_bindings->save (*bb);
2429 node->add_child_nocopy (*bb);
2432 node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no");
2434 node->add_child_nocopy (selection->get_state ());
2435 node->add_child_nocopy (_regions->get_state ());
2437 return *node;
2442 /** @param y y offset from the top of all trackviews.
2443 * @return pair: TimeAxisView that y is over, layer index.
2444 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
2445 * in stacked region display mode, otherwise 0.
2447 std::pair<TimeAxisView *, layer_t>
2448 Editor::trackview_by_y_position (double y)
2450 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2452 std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
2453 if (r.first) {
2454 return r;
2458 return std::make_pair ( (TimeAxisView *) 0, 0);
2461 /** Snap a position to the grid, if appropriate, taking into account current
2462 * grid settings and also the state of any snap modifier keys that may be pressed.
2463 * @param start Position to snap.
2464 * @param event Event to get current key modifier information from, or 0.
2466 void
2467 Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2469 if (!_session || !event) {
2470 return;
2473 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2474 if (_snap_mode == SnapOff) {
2475 snap_to_internal (start, direction, for_mark);
2477 } else {
2478 if (_snap_mode != SnapOff) {
2479 snap_to_internal (start, direction, for_mark);
2484 void
2485 Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark)
2487 if (!_session || _snap_mode == SnapOff) {
2488 return;
2491 snap_to_internal (start, direction, for_mark);
2494 void
2495 Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /*for_mark*/)
2497 const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2498 framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2500 switch (_snap_type) {
2501 case SnapToTimecodeFrame:
2502 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2503 start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2504 } else {
2505 start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2507 break;
2509 case SnapToTimecodeSeconds:
2510 if (_session->config.get_timecode_offset_negative()) {
2511 start += _session->config.get_timecode_offset ();
2512 } else {
2513 start -= _session->config.get_timecode_offset ();
2515 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2516 start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2517 } else {
2518 start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2521 if (_session->config.get_timecode_offset_negative()) {
2522 start -= _session->config.get_timecode_offset ();
2523 } else {
2524 start += _session->config.get_timecode_offset ();
2526 break;
2528 case SnapToTimecodeMinutes:
2529 if (_session->config.get_timecode_offset_negative()) {
2530 start += _session->config.get_timecode_offset ();
2531 } else {
2532 start -= _session->config.get_timecode_offset ();
2534 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2535 start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2536 } else {
2537 start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2539 if (_session->config.get_timecode_offset_negative()) {
2540 start -= _session->config.get_timecode_offset ();
2541 } else {
2542 start += _session->config.get_timecode_offset ();
2544 break;
2545 default:
2546 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2547 /*NOTREACHED*/
2551 void
2552 Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark)
2554 const framepos_t one_second = _session->frame_rate();
2555 const framepos_t one_minute = _session->frame_rate() * 60;
2556 framepos_t presnap = start;
2557 framepos_t before;
2558 framepos_t after;
2560 switch (_snap_type) {
2561 case SnapToTimecodeFrame:
2562 case SnapToTimecodeSeconds:
2563 case SnapToTimecodeMinutes:
2564 return timecode_snap_to_internal (start, direction, for_mark);
2566 case SnapToCDFrame:
2567 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2568 start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2569 } else {
2570 start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2572 break;
2574 case SnapToSeconds:
2575 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2576 start = (framepos_t) ceil ((double) start / one_second) * one_second;
2577 } else {
2578 start = (framepos_t) floor ((double) start / one_second) * one_second;
2580 break;
2582 case SnapToMinutes:
2583 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2584 start = (framepos_t) ceil ((double) start / one_minute) * one_minute;
2585 } else {
2586 start = (framepos_t) floor ((double) start / one_minute) * one_minute;
2588 break;
2590 case SnapToBar:
2591 start = _session->tempo_map().round_to_bar (start, direction);
2592 break;
2594 case SnapToBeat:
2595 start = _session->tempo_map().round_to_beat (start, direction);
2596 break;
2598 case SnapToBeatDiv32:
2599 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2600 break;
2601 case SnapToBeatDiv28:
2602 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2603 break;
2604 case SnapToBeatDiv24:
2605 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2606 break;
2607 case SnapToBeatDiv20:
2608 start = _session->tempo_map().round_to_beat_subdivision (start, 20, direction);
2609 break;
2610 case SnapToBeatDiv16:
2611 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2612 break;
2613 case SnapToBeatDiv14:
2614 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2615 break;
2616 case SnapToBeatDiv12:
2617 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2618 break;
2619 case SnapToBeatDiv10:
2620 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2621 break;
2622 case SnapToBeatDiv8:
2623 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2624 break;
2625 case SnapToBeatDiv7:
2626 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2627 break;
2628 case SnapToBeatDiv6:
2629 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2630 break;
2631 case SnapToBeatDiv5:
2632 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2633 break;
2634 case SnapToBeatDiv4:
2635 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2636 break;
2637 case SnapToBeatDiv3:
2638 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2639 break;
2640 case SnapToBeatDiv2:
2641 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2642 break;
2644 case SnapToMark:
2645 if (for_mark) {
2646 return;
2649 _session->locations()->marks_either_side (start, before, after);
2651 if (before == max_framepos) {
2652 start = after;
2653 } else if (after == max_framepos) {
2654 start = before;
2655 } else if (before != max_framepos && after != max_framepos) {
2656 /* have before and after */
2657 if ((start - before) < (after - start)) {
2658 start = before;
2659 } else {
2660 start = after;
2664 break;
2666 case SnapToRegionStart:
2667 case SnapToRegionEnd:
2668 case SnapToRegionSync:
2669 case SnapToRegionBoundary:
2670 if (!region_boundary_cache.empty()) {
2672 vector<framepos_t>::iterator prev = region_boundary_cache.end ();
2673 vector<framepos_t>::iterator next = region_boundary_cache.end ();
2675 if (direction > 0) {
2676 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2677 } else {
2678 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2681 if (next != region_boundary_cache.begin ()) {
2682 prev = next;
2683 prev--;
2686 framepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2687 framepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2689 if (start > (p + n) / 2) {
2690 start = n;
2691 } else {
2692 start = p;
2695 break;
2698 switch (_snap_mode) {
2699 case SnapNormal:
2700 return;
2702 case SnapMagnetic:
2704 if (presnap > start) {
2705 if (presnap > (start + unit_to_frame(snap_threshold))) {
2706 start = presnap;
2709 } else if (presnap < start) {
2710 if (presnap < (start - unit_to_frame(snap_threshold))) {
2711 start = presnap;
2715 default:
2716 /* handled at entry */
2717 return;
2723 void
2724 Editor::setup_toolbar ()
2726 HBox* mode_box = manage(new HBox);
2727 mode_box->set_border_width (2);
2728 mode_box->set_spacing(4);
2730 /* table containing mode buttons */
2732 HBox* mouse_mode_button_box = manage (new HBox ());
2734 if (Profile->get_sae()) {
2735 mouse_mode_button_box->pack_start (mouse_move_button);
2736 } else {
2737 mouse_mode_button_box->pack_start (mouse_move_button);
2738 mouse_mode_button_box->pack_start (join_object_range_button);
2739 mouse_mode_button_box->pack_start (mouse_select_button);
2742 mouse_mode_button_box->pack_start (mouse_zoom_button);
2744 if (!Profile->get_sae()) {
2745 mouse_mode_button_box->pack_start (mouse_gain_button);
2748 mouse_mode_button_box->pack_start (mouse_timefx_button);
2749 mouse_mode_button_box->pack_start (mouse_audition_button);
2750 mouse_mode_button_box->pack_start (internal_edit_button);
2752 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2753 if (!Profile->get_sae()) {
2754 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2756 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2758 edit_mode_selector.set_name ("EditModeSelector");
2759 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2760 edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2762 mode_box->pack_start (edit_mode_selector);
2763 mode_box->pack_start (*mouse_mode_button_box);
2765 _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2766 _mouse_mode_tearoff->set_name ("MouseModeBase");
2767 _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2769 if (Profile->get_sae()) {
2770 _mouse_mode_tearoff->set_can_be_torn_off (false);
2773 _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2774 &_mouse_mode_tearoff->tearoff_window()));
2775 _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2776 &_mouse_mode_tearoff->tearoff_window(), 1));
2777 _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2778 &_mouse_mode_tearoff->tearoff_window()));
2779 _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2780 &_mouse_mode_tearoff->tearoff_window(), 1));
2782 mouse_move_button.set_mode (false);
2783 mouse_select_button.set_mode (false);
2784 mouse_gain_button.set_mode (false);
2785 mouse_zoom_button.set_mode (false);
2786 mouse_timefx_button.set_mode (false);
2787 mouse_audition_button.set_mode (false);
2788 join_object_range_button.set_mode (false);
2790 mouse_move_button.set_name ("MouseModeButton");
2791 mouse_select_button.set_name ("MouseModeButton");
2792 mouse_gain_button.set_name ("MouseModeButton");
2793 mouse_zoom_button.set_name ("MouseModeButton");
2794 mouse_timefx_button.set_name ("MouseModeButton");
2795 mouse_audition_button.set_name ("MouseModeButton");
2796 internal_edit_button.set_name ("MouseModeButton");
2797 join_object_range_button.set_name ("MouseModeButton");
2799 mouse_move_button.unset_flags (CAN_FOCUS);
2800 mouse_select_button.unset_flags (CAN_FOCUS);
2801 mouse_gain_button.unset_flags (CAN_FOCUS);
2802 mouse_zoom_button.unset_flags (CAN_FOCUS);
2803 mouse_timefx_button.unset_flags (CAN_FOCUS);
2804 mouse_audition_button.unset_flags (CAN_FOCUS);
2805 internal_edit_button.unset_flags (CAN_FOCUS);
2806 join_object_range_button.unset_flags (CAN_FOCUS);
2808 /* Zoom */
2810 _zoom_box.set_spacing (1);
2811 _zoom_box.set_border_width (0);
2813 zoom_in_button.set_name ("EditorTimeButton");
2814 zoom_in_button.set_image (*(manage (new Image (::get_icon ("zoom_in")))));
2815 zoom_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), false));
2817 zoom_out_button.set_name ("EditorTimeButton");
2818 zoom_out_button.set_image (*(manage (new Image (::get_icon ("zoom_out")))));
2819 zoom_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), true));
2821 zoom_out_full_button.set_name ("EditorTimeButton");
2822 zoom_out_full_button.set_image (*(manage (new Image (::get_icon ("zoom_full")))));
2823 zoom_out_full_button.signal_clicked().connect (sigc::mem_fun(*this, &Editor::temporal_zoom_session));
2825 zoom_focus_selector.set_name ("ZoomFocusSelector");
2826 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
2827 zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2829 _zoom_box.pack_start (zoom_out_button, false, false);
2830 _zoom_box.pack_start (zoom_in_button, false, false);
2831 _zoom_box.pack_start (zoom_out_full_button, false, false);
2833 _zoom_box.pack_start (zoom_focus_selector);
2835 /* Track zoom buttons */
2836 tav_expand_button.set_name ("TrackHeightButton");
2837 tav_expand_button.set_size_request (-1, 20);
2838 tav_expand_button.add (*(manage (new Image (::get_icon ("tav_exp")))));
2839 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("expand-tracks"));
2840 act->connect_proxy (tav_expand_button);
2842 tav_shrink_button.set_name ("TrackHeightButton");
2843 tav_shrink_button.set_size_request (-1, 20);
2844 tav_shrink_button.add (*(manage (new Image (::get_icon ("tav_shrink")))));
2845 act = ActionManager::get_action (X_("Editor"), X_("shrink-tracks"));
2846 act->connect_proxy (tav_shrink_button);
2848 _zoom_box.pack_start (tav_shrink_button);
2849 _zoom_box.pack_start (tav_expand_button);
2851 _zoom_tearoff = manage (new TearOff (_zoom_box));
2853 _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2854 &_zoom_tearoff->tearoff_window()));
2855 _zoom_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2856 &_zoom_tearoff->tearoff_window(), 0));
2857 _zoom_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2858 &_zoom_tearoff->tearoff_window()));
2859 _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2860 &_zoom_tearoff->tearoff_window(), 0));
2862 snap_box.set_spacing (1);
2863 snap_box.set_border_width (2);
2865 snap_type_selector.set_name ("SnapTypeSelector");
2866 set_popdown_strings (snap_type_selector, snap_type_strings, true);
2867 snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2869 snap_mode_selector.set_name ("SnapModeSelector");
2870 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
2871 snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2873 edit_point_selector.set_name ("EditPointSelector");
2874 set_popdown_strings (edit_point_selector, edit_point_strings, true);
2875 edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2877 snap_box.pack_start (snap_mode_selector, false, false);
2878 snap_box.pack_start (snap_type_selector, false, false);
2879 snap_box.pack_start (edit_point_selector, false, false);
2881 /* Nudge */
2883 HBox *nudge_box = manage (new HBox);
2884 nudge_box->set_spacing(1);
2885 nudge_box->set_border_width (2);
2887 nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2888 nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2890 nudge_box->pack_start (nudge_backward_button, false, false);
2891 nudge_box->pack_start (nudge_forward_button, false, false);
2892 nudge_box->pack_start (*nudge_clock, false, false);
2895 /* Pack everything in... */
2897 HBox* hbox = manage (new HBox);
2898 hbox->set_spacing(10);
2900 _tools_tearoff = manage (new TearOff (*hbox));
2901 _tools_tearoff->set_name ("MouseModeBase");
2902 _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
2904 if (Profile->get_sae()) {
2905 _tools_tearoff->set_can_be_torn_off (false);
2908 _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2909 &_tools_tearoff->tearoff_window()));
2910 _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2911 &_tools_tearoff->tearoff_window(), 0));
2912 _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2913 &_tools_tearoff->tearoff_window()));
2914 _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2915 &_tools_tearoff->tearoff_window(), 0));
2917 toolbar_hbox.set_spacing (10);
2918 toolbar_hbox.set_border_width (1);
2920 toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
2921 toolbar_hbox.pack_start (*_zoom_tearoff, false, false);
2922 toolbar_hbox.pack_start (*_tools_tearoff, false, false);
2924 hbox->pack_start (snap_box, false, false);
2925 if (!Profile->get_small_screen()) {
2926 hbox->pack_start (*nudge_box, false, false);
2927 } else {
2928 ARDOUR_UI::instance()->editor_transport_box().pack_start (*nudge_box, false, false);
2930 hbox->pack_start (panic_box, false, false);
2932 hbox->show_all ();
2934 toolbar_base.set_name ("ToolBarBase");
2935 toolbar_base.add (toolbar_hbox);
2937 _toolbar_viewport.add (toolbar_base);
2938 /* stick to the required height but allow width to vary if there's not enough room */
2939 _toolbar_viewport.set_size_request (1, -1);
2941 toolbar_frame.set_shadow_type (SHADOW_OUT);
2942 toolbar_frame.set_name ("BaseFrame");
2943 toolbar_frame.add (_toolbar_viewport);
2945 DPIReset.connect (sigc::mem_fun (*this, &Editor::resize_text_widgets));
2948 void
2949 Editor::setup_tooltips ()
2951 ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
2952 ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain"));
2953 ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
2954 ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
2955 ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2956 ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
2957 ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
2958 ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
2959 ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
2960 ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
2961 ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
2962 ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
2963 ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
2964 ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
2965 ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
2966 ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
2967 ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
2968 ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
2969 ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
2970 ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
2971 ARDOUR_UI::instance()->set_tip (midi_panic_button, _("Send note off and reset controller messages on all MIDI channels"));
2972 ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode"));
2975 void
2976 Editor::midi_panic ()
2978 cerr << "MIDI panic\n";
2980 if (_session) {
2981 _session->midi_panic();
2985 void
2986 Editor::setup_midi_toolbar ()
2988 RefPtr<Action> act;
2990 /* Midi sound notes */
2991 midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
2992 midi_sound_notes.unset_flags (CAN_FOCUS);
2994 /* Panic */
2996 act = ActionManager::get_action (X_("MIDI"), X_("panic"));
2997 midi_panic_button.set_name("MidiPanicButton");
2998 act->connect_proxy (midi_panic_button);
3000 panic_box.pack_start (midi_sound_notes , true, true);
3001 panic_box.pack_start (midi_panic_button, true, true);
3005 Editor::convert_drop_to_paths (
3006 vector<string>& paths,
3007 const RefPtr<Gdk::DragContext>& /*context*/,
3008 gint /*x*/,
3009 gint /*y*/,
3010 const SelectionData& data,
3011 guint /*info*/,
3012 guint /*time*/)
3014 if (_session == 0) {
3015 return -1;
3018 vector<string> uris = data.get_uris();
3020 if (uris.empty()) {
3022 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3023 are actually URI lists. So do it by hand.
3026 if (data.get_target() != "text/plain") {
3027 return -1;
3030 /* Parse the "uri-list" format that Nautilus provides,
3031 where each pathname is delimited by \r\n.
3033 THERE MAY BE NO NULL TERMINATING CHAR!!!
3036 string txt = data.get_text();
3037 const char* p;
3038 const char* q;
3040 p = (const char *) malloc (txt.length() + 1);
3041 txt.copy ((char *) p, txt.length(), 0);
3042 ((char*)p)[txt.length()] = '\0';
3044 while (p)
3046 if (*p != '#')
3048 while (g_ascii_isspace (*p))
3049 p++;
3051 q = p;
3052 while (*q && (*q != '\n') && (*q != '\r')) {
3053 q++;
3056 if (q > p)
3058 q--;
3059 while (q > p && g_ascii_isspace (*q))
3060 q--;
3062 if (q > p)
3064 uris.push_back (string (p, q - p + 1));
3068 p = strchr (p, '\n');
3069 if (p)
3070 p++;
3073 free ((void*)p);
3075 if (uris.empty()) {
3076 return -1;
3080 for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3082 if ((*i).substr (0,7) == "file://") {
3084 string p = *i;
3085 PBD::url_decode (p);
3087 // scan forward past three slashes
3089 string::size_type slashcnt = 0;
3090 string::size_type n = 0;
3091 string::iterator x = p.begin();
3093 while (slashcnt < 3 && x != p.end()) {
3094 if ((*x) == '/') {
3095 slashcnt++;
3096 } else if (slashcnt == 3) {
3097 break;
3099 ++n;
3100 ++x;
3103 if (slashcnt != 3 || x == p.end()) {
3104 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3105 continue;
3108 paths.push_back (p.substr (n - 1));
3112 return 0;
3115 void
3116 Editor::new_tempo_section ()
3121 void
3122 Editor::map_transport_state ()
3124 ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3126 if (_session && _session->transport_stopped()) {
3127 have_pending_keyboard_selection = false;
3130 update_loop_range_view (true);
3133 /* UNDO/REDO */
3135 Editor::State::State (PublicEditor const * e)
3137 selection = new Selection (e);
3140 Editor::State::~State ()
3142 delete selection;
3145 void
3146 Editor::begin_reversible_command (string name)
3148 if (_session) {
3149 _session->begin_reversible_command (name);
3153 void
3154 Editor::begin_reversible_command (GQuark q)
3156 if (_session) {
3157 _session->begin_reversible_command (q);
3161 void
3162 Editor::commit_reversible_command ()
3164 if (_session) {
3165 _session->commit_reversible_command ();
3169 void
3170 Editor::history_changed ()
3172 string label;
3174 if (undo_action && _session) {
3175 if (_session->undo_depth() == 0) {
3176 label = _("Undo");
3177 } else {
3178 label = string_compose(_("Undo (%1)"), _session->next_undo());
3180 undo_action->property_label() = label;
3183 if (redo_action && _session) {
3184 if (_session->redo_depth() == 0) {
3185 label = _("Redo");
3186 } else {
3187 label = string_compose(_("Redo (%1)"), _session->next_redo());
3189 redo_action->property_label() = label;
3193 void
3194 Editor::duplicate_dialog (bool with_dialog)
3196 float times = 1.0f;
3198 if (mouse_mode == MouseRange) {
3199 if (selection->time.length() == 0) {
3200 return;
3204 RegionSelection rs = get_regions_from_selection_and_entered ();
3206 if (mouse_mode != MouseRange && rs.empty()) {
3207 return;
3210 if (with_dialog) {
3212 ArdourDialog win (_("Duplicate"));
3213 Label label (_("Number of duplications:"));
3214 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3215 SpinButton spinner (adjustment, 0.0, 1);
3216 HBox hbox;
3218 win.get_vbox()->set_spacing (12);
3219 win.get_vbox()->pack_start (hbox);
3220 hbox.set_border_width (6);
3221 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3223 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3224 place, visually. so do this by hand.
3227 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3228 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3229 spinner.grab_focus();
3231 hbox.show ();
3232 label.show ();
3233 spinner.show ();
3235 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3236 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3237 win.set_default_response (RESPONSE_ACCEPT);
3239 win.set_position (WIN_POS_MOUSE);
3241 spinner.grab_focus ();
3243 switch (win.run ()) {
3244 case RESPONSE_ACCEPT:
3245 break;
3246 default:
3247 return;
3250 times = adjustment.get_value();
3253 if (mouse_mode == MouseRange) {
3254 duplicate_selection (times);
3255 } else {
3256 duplicate_some_regions (rs, times);
3260 void
3261 Editor::set_edit_mode (EditMode m)
3263 Config->set_edit_mode (m);
3266 void
3267 Editor::cycle_edit_mode ()
3269 switch (Config->get_edit_mode()) {
3270 case Slide:
3271 if (Profile->get_sae()) {
3272 Config->set_edit_mode (Lock);
3273 } else {
3274 Config->set_edit_mode (Splice);
3276 break;
3277 case Splice:
3278 Config->set_edit_mode (Lock);
3279 break;
3280 case Lock:
3281 Config->set_edit_mode (Slide);
3282 break;
3286 void
3287 Editor::edit_mode_selection_done ()
3289 string s = edit_mode_selector.get_active_text ();
3291 if (!s.empty()) {
3292 Config->set_edit_mode (string_to_edit_mode (s));
3296 void
3297 Editor::snap_type_selection_done ()
3299 string choice = snap_type_selector.get_active_text();
3300 SnapType snaptype = SnapToBeat;
3302 if (choice == _("Beats/2")) {
3303 snaptype = SnapToBeatDiv2;
3304 } else if (choice == _("Beats/3")) {
3305 snaptype = SnapToBeatDiv3;
3306 } else if (choice == _("Beats/4")) {
3307 snaptype = SnapToBeatDiv4;
3308 } else if (choice == _("Beats/5")) {
3309 snaptype = SnapToBeatDiv5;
3310 } else if (choice == _("Beats/6")) {
3311 snaptype = SnapToBeatDiv6;
3312 } else if (choice == _("Beats/7")) {
3313 snaptype = SnapToBeatDiv7;
3314 } else if (choice == _("Beats/8")) {
3315 snaptype = SnapToBeatDiv8;
3316 } else if (choice == _("Beats/10")) {
3317 snaptype = SnapToBeatDiv10;
3318 } else if (choice == _("Beats/12")) {
3319 snaptype = SnapToBeatDiv12;
3320 } else if (choice == _("Beats/14")) {
3321 snaptype = SnapToBeatDiv14;
3322 } else if (choice == _("Beats/16")) {
3323 snaptype = SnapToBeatDiv16;
3324 } else if (choice == _("Beats/20")) {
3325 snaptype = SnapToBeatDiv20;
3326 } else if (choice == _("Beats/24")) {
3327 snaptype = SnapToBeatDiv24;
3328 } else if (choice == _("Beats/28")) {
3329 snaptype = SnapToBeatDiv28;
3330 } else if (choice == _("Beats/32")) {
3331 snaptype = SnapToBeatDiv32;
3332 } else if (choice == _("Beats")) {
3333 snaptype = SnapToBeat;
3334 } else if (choice == _("Bars")) {
3335 snaptype = SnapToBar;
3336 } else if (choice == _("Marks")) {
3337 snaptype = SnapToMark;
3338 } else if (choice == _("Region starts")) {
3339 snaptype = SnapToRegionStart;
3340 } else if (choice == _("Region ends")) {
3341 snaptype = SnapToRegionEnd;
3342 } else if (choice == _("Region bounds")) {
3343 snaptype = SnapToRegionBoundary;
3344 } else if (choice == _("Region syncs")) {
3345 snaptype = SnapToRegionSync;
3346 } else if (choice == _("CD Frames")) {
3347 snaptype = SnapToCDFrame;
3348 } else if (choice == _("Timecode Frames")) {
3349 snaptype = SnapToTimecodeFrame;
3350 } else if (choice == _("Timecode Seconds")) {
3351 snaptype = SnapToTimecodeSeconds;
3352 } else if (choice == _("Timecode Minutes")) {
3353 snaptype = SnapToTimecodeMinutes;
3354 } else if (choice == _("Seconds")) {
3355 snaptype = SnapToSeconds;
3356 } else if (choice == _("Minutes")) {
3357 snaptype = SnapToMinutes;
3360 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3361 if (ract) {
3362 ract->set_active ();
3366 void
3367 Editor::snap_mode_selection_done ()
3369 string choice = snap_mode_selector.get_active_text();
3370 SnapMode mode = SnapNormal;
3372 if (choice == _("No Grid")) {
3373 mode = SnapOff;
3374 } else if (choice == _("Grid")) {
3375 mode = SnapNormal;
3376 } else if (choice == _("Magnetic")) {
3377 mode = SnapMagnetic;
3380 RefPtr<RadioAction> ract = snap_mode_action (mode);
3382 if (ract) {
3383 ract->set_active (true);
3387 void
3388 Editor::cycle_edit_point (bool with_marker)
3390 switch (_edit_point) {
3391 case EditAtMouse:
3392 set_edit_point_preference (EditAtPlayhead);
3393 break;
3394 case EditAtPlayhead:
3395 if (with_marker) {
3396 set_edit_point_preference (EditAtSelectedMarker);
3397 } else {
3398 set_edit_point_preference (EditAtMouse);
3400 break;
3401 case EditAtSelectedMarker:
3402 set_edit_point_preference (EditAtMouse);
3403 break;
3407 void
3408 Editor::edit_point_selection_done ()
3410 string choice = edit_point_selector.get_active_text();
3411 EditPoint ep = EditAtSelectedMarker;
3413 if (choice == _("Marker")) {
3414 set_edit_point_preference (EditAtSelectedMarker);
3415 } else if (choice == _("Playhead")) {
3416 set_edit_point_preference (EditAtPlayhead);
3417 } else {
3418 set_edit_point_preference (EditAtMouse);
3421 RefPtr<RadioAction> ract = edit_point_action (ep);
3423 if (ract) {
3424 ract->set_active (true);
3428 void
3429 Editor::zoom_focus_selection_done ()
3431 string choice = zoom_focus_selector.get_active_text();
3432 ZoomFocus focus_type = ZoomFocusLeft;
3434 if (choice == _("Left")) {
3435 focus_type = ZoomFocusLeft;
3436 } else if (choice == _("Right")) {
3437 focus_type = ZoomFocusRight;
3438 } else if (choice == _("Center")) {
3439 focus_type = ZoomFocusCenter;
3440 } else if (choice == _("Playhead")) {
3441 focus_type = ZoomFocusPlayhead;
3442 } else if (choice == _("Mouse")) {
3443 focus_type = ZoomFocusMouse;
3444 } else if (choice == _("Edit point")) {
3445 focus_type = ZoomFocusEdit;
3448 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3450 if (ract) {
3451 ract->set_active ();
3455 bool
3456 Editor::edit_controls_button_release (GdkEventButton* ev)
3458 if (Keyboard::is_context_menu_event (ev)) {
3459 ARDOUR_UI::instance()->add_route (this);
3460 } else if (ev->button == 1) {
3461 selection->clear_tracks ();
3464 return true;
3467 bool
3468 Editor::mouse_select_button_release (GdkEventButton* ev)
3470 /* this handles just right-clicks */
3472 if (ev->button != 3) {
3473 return false;
3476 return true;
3479 void
3480 Editor::set_zoom_focus (ZoomFocus f)
3482 string str = zoom_focus_strings[(int)f];
3484 if (str != zoom_focus_selector.get_active_text()) {
3485 zoom_focus_selector.set_active_text (str);
3488 if (zoom_focus != f) {
3489 zoom_focus = f;
3490 instant_save ();
3494 void
3495 Editor::ensure_float (Window& win)
3497 win.set_transient_for (*this);
3500 void
3501 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3503 /* recover or initialize pane positions. do this here rather than earlier because
3504 we don't want the positions to change the child allocations, which they seem to do.
3507 int pos;
3508 XMLProperty* prop;
3509 char buf[32];
3510 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3512 enum Pane {
3513 Horizontal = 0x1,
3514 Vertical = 0x2
3517 static Pane done;
3519 XMLNode* geometry = find_named_node (*node, "geometry");
3521 if (which == static_cast<Paned*> (&edit_pane)) {
3523 if (done & Horizontal) {
3524 return;
3527 if (geometry && (prop = geometry->property ("notebook-shrunk"))) {
3528 _notebook_shrunk = string_is_affirmative (prop->value ());
3531 if (geometry && (prop = geometry->property ("pre-maximal-horizontal-pane-position"))) {
3532 pre_maximal_horizontal_pane_position = atoi (prop->value ());
3535 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3536 /* initial allocation is 90% to canvas, 10% to notebook */
3537 pos = (int) floor (alloc.get_width() * 0.90f);
3538 snprintf (buf, sizeof(buf), "%d", pos);
3539 } else {
3540 pos = atoi (prop->value());
3543 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3544 edit_pane.set_position (pos);
3545 if (pre_maximal_horizontal_pane_position == 0) {
3546 pre_maximal_horizontal_pane_position = pos;
3550 done = (Pane) (done | Horizontal);
3552 } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3554 if (done & Vertical) {
3555 return;
3558 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3559 /* initial allocation is 90% to canvas, 10% to summary */
3560 pos = (int) floor (alloc.get_height() * 0.90f);
3561 snprintf (buf, sizeof(buf), "%d", pos);
3562 } else {
3563 pos = atoi (prop->value());
3566 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3567 editor_summary_pane.set_position (pos);
3568 pre_maximal_vertical_pane_position = pos;
3571 done = (Pane) (done | Vertical);
3575 void
3576 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3578 if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) {
3579 top_hbox.remove (toolbar_frame);
3583 void
3584 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3586 if (toolbar_frame.get_parent() == 0) {
3587 top_hbox.pack_end (toolbar_frame);
3591 void
3592 Editor::set_show_measures (bool yn)
3594 if (_show_measures != yn) {
3595 hide_measures ();
3597 if ((_show_measures = yn) == true) {
3598 if (tempo_lines)
3599 tempo_lines->show();
3600 draw_measures ();
3602 instant_save ();
3606 void
3607 Editor::toggle_follow_playhead ()
3609 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3610 if (act) {
3611 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3612 set_follow_playhead (tact->get_active());
3616 /** @param yn true to follow playhead, otherwise false.
3617 * @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
3619 void
3620 Editor::set_follow_playhead (bool yn, bool catch_up)
3622 if (_follow_playhead != yn) {
3623 if ((_follow_playhead = yn) == true && catch_up) {
3624 /* catch up */
3625 reset_x_origin_to_follow_playhead ();
3627 instant_save ();
3631 void
3632 Editor::toggle_stationary_playhead ()
3634 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3635 if (act) {
3636 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3637 set_stationary_playhead (tact->get_active());
3641 void
3642 Editor::set_stationary_playhead (bool yn)
3644 if (_stationary_playhead != yn) {
3645 if ((_stationary_playhead = yn) == true) {
3646 /* catch up */
3647 // FIXME need a 3.0 equivalent of this 2.X call
3648 // update_current_screen ();
3650 instant_save ();
3654 void
3655 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3657 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3658 if (xfade) {
3659 xfade->set_active (!xfade->active());
3663 void
3664 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3666 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3667 if (xfade) {
3668 xfade->set_follow_overlap (!xfade->following_overlap());
3672 void
3673 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3675 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3677 if (!xfade) {
3678 return;
3681 CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3683 ensure_float (cew);
3685 switch (cew.run ()) {
3686 case RESPONSE_ACCEPT:
3687 break;
3688 default:
3689 return;
3692 cew.apply ();
3693 PropertyChange all_crossfade_properties;
3694 all_crossfade_properties.add (ARDOUR::Properties::active);
3695 all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3696 xfade->PropertyChanged (all_crossfade_properties);
3699 PlaylistSelector&
3700 Editor::playlist_selector () const
3702 return *_playlist_selector;
3705 Evoral::MusicalTime
3706 Editor::get_grid_type_as_beats (bool& success, framepos_t position)
3708 success = true;
3710 switch (_snap_type) {
3711 case SnapToBeat:
3712 return 1.0;
3713 break;
3715 case SnapToBeatDiv32:
3716 return 1.0/32.0;
3717 break;
3718 case SnapToBeatDiv28:
3719 return 1.0/28.0;
3720 break;
3721 case SnapToBeatDiv24:
3722 return 1.0/24.0;
3723 break;
3724 case SnapToBeatDiv20:
3725 return 1.0/20.0;
3726 break;
3727 case SnapToBeatDiv16:
3728 return 1.0/16.0;
3729 break;
3730 case SnapToBeatDiv14:
3731 return 1.0/14.0;
3732 break;
3733 case SnapToBeatDiv12:
3734 return 1.0/12.0;
3735 break;
3736 case SnapToBeatDiv10:
3737 return 1.0/10.0;
3738 break;
3739 case SnapToBeatDiv8:
3740 return 1.0/8.0;
3741 break;
3742 case SnapToBeatDiv7:
3743 return 1.0/7.0;
3744 break;
3745 case SnapToBeatDiv6:
3746 return 1.0/6.0;
3747 break;
3748 case SnapToBeatDiv5:
3749 return 1.0/5.0;
3750 break;
3751 case SnapToBeatDiv4:
3752 return 1.0/4.0;
3753 break;
3754 case SnapToBeatDiv3:
3755 return 1.0/3.0;
3756 break;
3757 case SnapToBeatDiv2:
3758 return 1.0/2.0;
3759 break;
3761 case SnapToBar:
3762 if (_session) {
3763 return _session->tempo_map().meter_at (position).beats_per_bar();
3765 break;
3767 case SnapToCDFrame:
3768 case SnapToTimecodeFrame:
3769 case SnapToTimecodeSeconds:
3770 case SnapToTimecodeMinutes:
3771 case SnapToSeconds:
3772 case SnapToMinutes:
3773 case SnapToRegionStart:
3774 case SnapToRegionEnd:
3775 case SnapToRegionSync:
3776 case SnapToRegionBoundary:
3777 default:
3778 success = false;
3779 break;
3782 return 0.0;
3785 framecnt_t
3786 Editor::get_nudge_distance (framepos_t pos, framecnt_t& next)
3788 framecnt_t ret;
3790 ret = nudge_clock->current_duration (pos);
3791 next = ret + 1; /* XXXX fix me */
3793 return ret;
3797 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3799 ArdourDialog dialog (_("Playlist Deletion"));
3800 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3801 "If it is kept, its audio files will not be cleaned.\n"
3802 "If it is deleted, audio files used by it alone will be cleaned."),
3803 pl->name()));
3805 dialog.set_position (WIN_POS_CENTER);
3806 dialog.get_vbox()->pack_start (label);
3808 label.show ();
3810 dialog.add_button (_("Delete Playlist"), RESPONSE_ACCEPT);
3811 dialog.add_button (_("Keep Playlist"), RESPONSE_REJECT);
3812 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3814 switch (dialog.run ()) {
3815 case RESPONSE_ACCEPT:
3816 /* delete the playlist */
3817 return 0;
3818 break;
3820 case RESPONSE_REJECT:
3821 /* keep the playlist */
3822 return 1;
3823 break;
3825 default:
3826 break;
3829 return -1;
3832 bool
3833 Editor::audio_region_selection_covers (framepos_t where)
3835 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3836 if ((*a)->region()->covers (where)) {
3837 return true;
3841 return false;
3844 void
3845 Editor::prepare_for_cleanup ()
3847 cut_buffer->clear_regions ();
3848 cut_buffer->clear_playlists ();
3850 selection->clear_regions ();
3851 selection->clear_playlists ();
3853 _regions->suspend_redisplay ();
3856 void
3857 Editor::finish_cleanup ()
3859 _regions->resume_redisplay ();
3862 Location*
3863 Editor::transport_loop_location()
3865 if (_session) {
3866 return _session->locations()->auto_loop_location();
3867 } else {
3868 return 0;
3872 Location*
3873 Editor::transport_punch_location()
3875 if (_session) {
3876 return _session->locations()->auto_punch_location();
3877 } else {
3878 return 0;
3882 bool
3883 Editor::control_layout_scroll (GdkEventScroll* ev)
3885 if (Keyboard::some_magic_widget_has_focus()) {
3886 return false;
3889 switch (ev->direction) {
3890 case GDK_SCROLL_UP:
3891 scroll_tracks_up_line ();
3892 return true;
3893 break;
3895 case GDK_SCROLL_DOWN:
3896 scroll_tracks_down_line ();
3897 return true;
3899 default:
3900 /* no left/right handling yet */
3901 break;
3904 return false;
3907 void
3908 Editor::session_state_saved (string)
3910 update_title ();
3911 _snapshots->redisplay ();
3914 void
3915 Editor::maximise_editing_space ()
3917 _mouse_mode_tearoff->set_visible (false);
3918 _tools_tearoff->set_visible (false);
3919 _zoom_tearoff->set_visible (false);
3921 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
3922 pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
3923 pre_maximal_editor_width = this->get_width ();
3924 pre_maximal_editor_height = this->get_height ();
3926 if (post_maximal_horizontal_pane_position == 0) {
3927 post_maximal_horizontal_pane_position = edit_pane.get_width();
3930 if (post_maximal_vertical_pane_position == 0) {
3931 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
3934 fullscreen ();
3936 if (post_maximal_editor_width) {
3937 edit_pane.set_position (post_maximal_horizontal_pane_position -
3938 abs(post_maximal_editor_width - pre_maximal_editor_width));
3939 } else {
3940 edit_pane.set_position (post_maximal_horizontal_pane_position);
3943 /* Hack: we must do this in an idle handler for it to work; see comment in
3944 restore_editing_space()
3947 Glib::signal_idle().connect (
3948 sigc::bind (
3949 sigc::mem_fun (*this, &Editor::idle_reset_vertical_pane_position),
3950 post_maximal_vertical_pane_position
3954 if (Config->get_keep_tearoffs()) {
3955 _mouse_mode_tearoff->set_visible (true);
3956 _tools_tearoff->set_visible (true);
3957 if (Config->get_show_zoom_tools ()) {
3958 _zoom_tearoff->set_visible (true);
3964 bool
3965 Editor::idle_reset_vertical_pane_position (int p)
3967 editor_summary_pane.set_position (p);
3968 return false;
3971 void
3972 Editor::restore_editing_space ()
3974 // user changed width/height of panes during fullscreen
3976 if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
3977 post_maximal_horizontal_pane_position = edit_pane.get_position();
3980 if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
3981 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
3984 unfullscreen();
3986 _mouse_mode_tearoff->set_visible (true);
3987 _tools_tearoff->set_visible (true);
3988 if (Config->get_show_zoom_tools ()) {
3989 _zoom_tearoff->set_visible (true);
3991 post_maximal_editor_width = this->get_width();
3992 post_maximal_editor_height = this->get_height();
3994 edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
3996 /* This is a bit of a hack, but it seems that if you set the vertical pane position
3997 here it gets reset to some wrong value after this method has finished. Doing
3998 the setup in an idle callback seems to work.
4000 Glib::signal_idle().connect (
4001 sigc::bind (
4002 sigc::mem_fun (*this, &Editor::idle_reset_vertical_pane_position),
4003 pre_maximal_vertical_pane_position
4009 * Make new playlists for a given track and also any others that belong
4010 * to the same active route group with the `edit' property.
4011 * @param v Track.
4014 void
4015 Editor::new_playlists (TimeAxisView* v)
4017 begin_reversible_command (_("new playlists"));
4018 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4019 _session->playlists->get (playlists);
4020 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4021 commit_reversible_command ();
4025 * Use a copy of the current playlist for a given track and also any others that belong
4026 * to the same active route group with the `edit' property.
4027 * @param v Track.
4030 void
4031 Editor::copy_playlists (TimeAxisView* v)
4033 begin_reversible_command (_("copy playlists"));
4034 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4035 _session->playlists->get (playlists);
4036 mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4037 commit_reversible_command ();
4040 /** Clear the current playlist for a given track and also any others that belong
4041 * to the same active route group with the `edit' property.
4042 * @param v Track.
4045 void
4046 Editor::clear_playlists (TimeAxisView* v)
4048 begin_reversible_command (_("clear playlists"));
4049 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4050 _session->playlists->get (playlists);
4051 mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4052 commit_reversible_command ();
4055 void
4056 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4058 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4061 void
4062 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4064 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4067 void
4068 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4070 atv.clear_playlist ();
4073 bool
4074 Editor::on_key_press_event (GdkEventKey* ev)
4076 return key_press_focus_accelerator_handler (*this, ev);
4079 bool
4080 Editor::on_key_release_event (GdkEventKey* ev)
4082 return Gtk::Window::on_key_release_event (ev);
4083 // return key_press_focus_accelerator_handler (*this, ev);
4086 /** Queue up a change to the viewport x origin.
4087 * @param frame New x origin.
4089 void
4090 Editor::reset_x_origin (framepos_t frame)
4092 queue_visual_change (frame);
4095 void
4096 Editor::reset_y_origin (double y)
4098 queue_visual_change_y (y);
4101 void
4102 Editor::reset_zoom (double fpu)
4104 queue_visual_change (fpu);
4107 void
4108 Editor::reposition_and_zoom (framepos_t frame, double fpu)
4110 reset_x_origin (frame);
4111 reset_zoom (fpu);
4113 if (!no_save_visual) {
4114 undo_visual_stack.push_back (current_visual_state(false));
4118 Editor::VisualState::VisualState ()
4119 : gui_state (new GUIObjectState)
4123 Editor::VisualState::~VisualState ()
4125 delete gui_state;
4128 Editor::VisualState*
4129 Editor::current_visual_state (bool with_tracks)
4131 VisualState* vs = new VisualState;
4132 vs->y_position = vertical_adjustment.get_value();
4133 vs->frames_per_unit = frames_per_unit;
4134 vs->leftmost_frame = leftmost_frame;
4135 vs->zoom_focus = zoom_focus;
4137 if (with_tracks) {
4138 *(vs->gui_state) = *ARDOUR_UI::instance()->gui_object_state;
4141 return vs;
4144 void
4145 Editor::undo_visual_state ()
4147 if (undo_visual_stack.empty()) {
4148 return;
4151 redo_visual_stack.push_back (current_visual_state());
4153 VisualState* vs = undo_visual_stack.back();
4154 undo_visual_stack.pop_back();
4155 use_visual_state (*vs);
4158 void
4159 Editor::redo_visual_state ()
4161 if (redo_visual_stack.empty()) {
4162 return;
4165 undo_visual_stack.push_back (current_visual_state());
4167 VisualState* vs = redo_visual_stack.back();
4168 redo_visual_stack.pop_back();
4169 use_visual_state (*vs);
4172 void
4173 Editor::swap_visual_state ()
4175 if (undo_visual_stack.empty()) {
4176 redo_visual_state ();
4177 } else {
4178 undo_visual_state ();
4182 void
4183 Editor::use_visual_state (VisualState& vs)
4185 no_save_visual = true;
4187 _routes->suspend_redisplay ();
4189 vertical_adjustment.set_value (vs.y_position);
4191 set_zoom_focus (vs.zoom_focus);
4192 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4194 *ARDOUR_UI::instance()->gui_object_state = *vs.gui_state;
4196 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4197 (*i)->reset_visual_state ();
4200 _routes->update_visibility ();
4201 _routes->resume_redisplay ();
4203 no_save_visual = false;
4206 void
4207 Editor::set_frames_per_unit (double fpu)
4209 /* this is the core function that controls the zoom level of the canvas. it is called
4210 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4213 if (fpu == frames_per_unit) {
4214 return;
4217 if (fpu < 2.0) {
4218 fpu = 2.0;
4222 /* don't allow zooms that fit more than the maximum number
4223 of frames into an 800 pixel wide space.
4226 if (max_framepos / fpu < 800.0) {
4227 return;
4230 if (tempo_lines)
4231 tempo_lines->tempo_map_changed();
4233 frames_per_unit = fpu;
4234 post_zoom ();
4237 void
4238 Editor::post_zoom ()
4240 // convert fpu to frame count
4242 framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width);
4244 if (frames_per_unit != zoom_range_clock->current_duration()) {
4245 zoom_range_clock->set (frames);
4248 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4249 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4250 (*i)->reshow_selection (selection->time);
4254 ZoomChanged (); /* EMIT_SIGNAL */
4256 //reset_scrolling_region ();
4258 if (playhead_cursor) {
4259 playhead_cursor->set_position (playhead_cursor->current_frame);
4262 refresh_location_display();
4263 _summary->set_overlays_dirty ();
4265 update_marker_labels ();
4267 instant_save ();
4270 void
4271 Editor::queue_visual_change (framepos_t where)
4273 pending_visual_change.add (VisualChange::TimeOrigin);
4274 pending_visual_change.time_origin = where;
4275 ensure_visual_change_idle_handler ();
4278 void
4279 Editor::queue_visual_change (double fpu)
4281 pending_visual_change.add (VisualChange::ZoomLevel);
4282 pending_visual_change.frames_per_unit = fpu;
4284 ensure_visual_change_idle_handler ();
4287 void
4288 Editor::queue_visual_change_y (double y)
4290 pending_visual_change.add (VisualChange::YOrigin);
4291 pending_visual_change.y_origin = y;
4293 ensure_visual_change_idle_handler ();
4296 void
4297 Editor::ensure_visual_change_idle_handler ()
4299 if (pending_visual_change.idle_handler_id < 0) {
4300 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4305 Editor::_idle_visual_changer (void* arg)
4307 return static_cast<Editor*>(arg)->idle_visual_changer ();
4311 Editor::idle_visual_changer ()
4313 VisualChange::Type p = pending_visual_change.pending;
4314 pending_visual_change.pending = (VisualChange::Type) 0;
4316 double const last_time_origin = horizontal_position ();
4318 if (p & VisualChange::TimeOrigin) {
4319 /* This is a bit of a hack, but set_frames_per_unit
4320 below will (if called) end up with the
4321 CrossfadeViews looking at Editor::leftmost_frame,
4322 and if we're changing origin and zoom in the same
4323 operation it will be the wrong value unless we
4324 update it here.
4327 leftmost_frame = pending_visual_change.time_origin;
4330 if (p & VisualChange::ZoomLevel) {
4331 set_frames_per_unit (pending_visual_change.frames_per_unit);
4333 compute_fixed_ruler_scale ();
4334 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4335 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4336 update_tempo_based_rulers ();
4338 if (p & VisualChange::TimeOrigin) {
4339 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4341 if (p & VisualChange::YOrigin) {
4342 vertical_adjustment.set_value (pending_visual_change.y_origin);
4345 if (last_time_origin == horizontal_position ()) {
4346 /* changed signal not emitted */
4347 update_fixed_rulers ();
4348 redisplay_tempo (true);
4351 _summary->set_overlays_dirty ();
4353 pending_visual_change.idle_handler_id = -1;
4354 return 0; /* this is always a one-shot call */
4357 struct EditorOrderTimeAxisSorter {
4358 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4359 return a->order () < b->order ();
4363 void
4364 Editor::sort_track_selection (TrackViewList* sel)
4366 EditorOrderTimeAxisSorter cmp;
4368 if (sel) {
4369 sel->sort (cmp);
4370 } else {
4371 selection->tracks.sort (cmp);
4375 framepos_t
4376 Editor::get_preferred_edit_position (bool ignore_playhead)
4378 bool ignored;
4379 framepos_t where = 0;
4380 EditPoint ep = _edit_point;
4382 if (entered_marker) {
4383 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position()));
4384 return entered_marker->position();
4387 if (ignore_playhead && ep == EditAtPlayhead) {
4388 ep = EditAtSelectedMarker;
4391 switch (ep) {
4392 case EditAtPlayhead:
4393 where = _session->audible_frame();
4394 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where));
4395 break;
4397 case EditAtSelectedMarker:
4398 if (!selection->markers.empty()) {
4399 bool is_start;
4400 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4401 if (loc) {
4402 if (is_start) {
4403 where = loc->start();
4404 } else {
4405 where = loc->end();
4407 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where));
4408 break;
4411 /* fallthru */
4413 default:
4414 case EditAtMouse:
4415 if (!mouse_frame (where, ignored)) {
4416 /* XXX not right but what can we do ? */
4417 return 0;
4419 snap_to (where);
4420 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where));
4421 break;
4424 return where;
4427 void
4428 Editor::set_loop_range (framepos_t start, framepos_t end, string cmd)
4430 if (!_session) return;
4432 begin_reversible_command (cmd);
4434 Location* tll;
4436 if ((tll = transport_loop_location()) == 0) {
4437 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoLoop);
4438 XMLNode &before = _session->locations()->get_state();
4439 _session->locations()->add (loc, true);
4440 _session->set_auto_loop_location (loc);
4441 XMLNode &after = _session->locations()->get_state();
4442 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4443 } else {
4444 XMLNode &before = tll->get_state();
4445 tll->set_hidden (false, this);
4446 tll->set (start, end);
4447 XMLNode &after = tll->get_state();
4448 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4451 commit_reversible_command ();
4454 void
4455 Editor::set_punch_range (framepos_t start, framepos_t end, string cmd)
4457 if (!_session) return;
4459 begin_reversible_command (cmd);
4461 Location* tpl;
4463 if ((tpl = transport_punch_location()) == 0) {
4464 Location* loc = new Location (*_session, start, end, _("Loop"), Location::IsAutoPunch);
4465 XMLNode &before = _session->locations()->get_state();
4466 _session->locations()->add (loc, true);
4467 _session->set_auto_loop_location (loc);
4468 XMLNode &after = _session->locations()->get_state();
4469 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4471 else {
4472 XMLNode &before = tpl->get_state();
4473 tpl->set_hidden (false, this);
4474 tpl->set (start, end);
4475 XMLNode &after = tpl->get_state();
4476 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4479 commit_reversible_command ();
4482 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4483 * @param rs List to which found regions are added.
4484 * @param where Time to look at.
4485 * @param ts Tracks to look on; if this is empty, all tracks are examined.
4487 void
4488 Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4490 const TrackViewList* tracks;
4492 if (ts.empty()) {
4493 tracks = &track_views;
4494 } else {
4495 tracks = &ts;
4498 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4500 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4502 if (rtv) {
4503 boost::shared_ptr<Track> tr;
4504 boost::shared_ptr<Playlist> pl;
4506 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4508 Playlist::RegionList* regions = pl->regions_at (
4509 (framepos_t) floor ( (double) where * tr->speed()));
4511 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4512 RegionView* rv = rtv->view()->find_view (*i);
4513 if (rv) {
4514 rs.add (rv);
4518 delete regions;
4524 void
4525 Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4527 const TrackViewList* tracks;
4529 if (ts.empty()) {
4530 tracks = &track_views;
4531 } else {
4532 tracks = &ts;
4535 for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4536 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4537 if (rtv) {
4538 boost::shared_ptr<Track> tr;
4539 boost::shared_ptr<Playlist> pl;
4541 if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4543 Playlist::RegionList* regions = pl->regions_touched (
4544 (framepos_t) floor ( (double)where * tr->speed()), max_framepos);
4546 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4548 RegionView* rv = rtv->view()->find_view (*i);
4550 if (rv) {
4551 rs.push_back (rv);
4555 delete regions;
4561 /** Start with regions that are selected. Then add equivalent regions
4562 * on tracks in the same active edit-enabled route group as any of
4563 * the regions that we started with.
4566 RegionSelection
4567 Editor::get_regions_from_selection ()
4569 return get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
4572 /** Get regions using the following method:
4574 * Make an initial region list using the selected regions, unless
4575 * the edit point is `mouse' and the mouse is over an unselected
4576 * region. In this case, start with just that region.
4578 * Then, make an initial track list of the tracks that these
4579 * regions are on, and if the edit point is not `mouse', add the
4580 * selected tracks.
4582 * Look at this track list and add any other tracks that are on the
4583 * same active edit-enabled route group as one of the initial tracks.
4585 * Finally take the initial region list and add any regions that are
4586 * under the edit point on one of the tracks on the track list to get
4587 * the returned region list.
4589 * The rationale here is that the mouse edit point is special in that
4590 * its position describes both a time and a track; the other edit
4591 * modes only describe a time. Hence if the edit point is `mouse' we
4592 * ignore selected tracks, as we assume the user means something by
4593 * pointing at a particular track. Also in this case we take note of
4594 * the region directly under the edit point, as there is always just one
4595 * (rather than possibly several with non-mouse edit points).
4598 RegionSelection
4599 Editor::get_regions_from_selection_and_edit_point ()
4601 RegionSelection regions;
4603 if (_edit_point == EditAtMouse && entered_regionview && !selection->regions.contains (entered_regionview)) {
4604 regions.add (entered_regionview);
4605 } else {
4606 regions = selection->regions;
4609 TrackViewList tracks;
4611 if (_edit_point != EditAtMouse) {
4612 tracks = selection->tracks;
4615 /* Add any other tracks that have regions that are in the same
4616 edit-activated route group as one of our regions.
4618 for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) {
4620 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4622 if (g && g->is_active() && g->is_edit()) {
4623 tracks.add (axis_views_from_routes (g->route_list()));
4627 if (!tracks.empty()) {
4628 /* now find regions that are at the edit position on those tracks */
4629 framepos_t const where = get_preferred_edit_position ();
4630 get_regions_at (regions, where, tracks);
4633 return regions;
4636 /** Start with regions that are selected, or the entered regionview if none are selected.
4637 * Then add equivalent regions on tracks in the same active edit-enabled route group as any
4638 * of the regions that we started with.
4641 RegionSelection
4642 Editor::get_regions_from_selection_and_entered ()
4644 RegionSelection regions = selection->regions;
4646 if (regions.empty() && entered_regionview) {
4647 regions.add (entered_regionview);
4650 return get_equivalent_regions (regions, ARDOUR::Properties::edit.property_id);
4653 void
4654 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4656 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4658 RouteTimeAxisView* tatv;
4660 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4662 boost::shared_ptr<Playlist> pl;
4663 vector<boost::shared_ptr<Region> > results;
4664 RegionView* marv;
4665 boost::shared_ptr<Track> tr;
4667 if ((tr = tatv->track()) == 0) {
4668 /* bus */
4669 continue;
4672 if ((pl = (tr->playlist())) != 0) {
4673 pl->get_region_list_equivalent_regions (region, results);
4676 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4677 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4678 regions.push_back (marv);
4686 void
4687 Editor::show_rhythm_ferret ()
4689 if (rhythm_ferret == 0) {
4690 rhythm_ferret = new RhythmFerret(*this);
4693 rhythm_ferret->set_session (_session);
4694 rhythm_ferret->show ();
4695 rhythm_ferret->present ();
4698 void
4699 Editor::first_idle ()
4701 MessageDialog* dialog = 0;
4703 if (track_views.size() > 1) {
4704 dialog = new MessageDialog (*this,
4705 string_compose (_("Please wait while %1 loads visual data"), PROGRAM_NAME),
4706 true,
4707 Gtk::MESSAGE_INFO,
4708 Gtk::BUTTONS_NONE);
4709 dialog->present ();
4710 ARDOUR_UI::instance()->flush_pending ();
4713 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4714 (*t)->first_idle();
4717 // first idle adds route children (automation tracks), so we need to redisplay here
4718 _routes->redisplay ();
4720 delete dialog;
4722 _have_idled = true;
4725 gboolean
4726 Editor::_idle_resize (gpointer arg)
4728 return ((Editor*)arg)->idle_resize ();
4731 void
4732 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4734 if (resize_idle_id < 0) {
4735 resize_idle_id = g_idle_add (_idle_resize, this);
4736 _pending_resize_amount = 0;
4739 /* make a note of the smallest resulting height, so that we can clamp the
4740 lower limit at TimeAxisView::hSmall */
4742 int32_t min_resulting = INT32_MAX;
4744 _pending_resize_amount += h;
4745 _pending_resize_view = view;
4747 min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4749 if (selection->tracks.contains (_pending_resize_view)) {
4750 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4751 min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4755 if (min_resulting < 0) {
4756 min_resulting = 0;
4759 /* clamp */
4760 if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4761 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4765 /** Handle pending resizing of tracks */
4766 bool
4767 Editor::idle_resize ()
4769 _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4771 if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4772 selection->tracks.contains (_pending_resize_view)) {
4774 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4775 if (*i != _pending_resize_view) {
4776 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4781 _pending_resize_amount = 0;
4782 flush_canvas ();
4783 _group_tabs->set_dirty ();
4784 resize_idle_id = -1;
4786 return false;
4789 void
4790 Editor::located ()
4792 ENSURE_GUI_THREAD (*this, &Editor::located);
4794 playhead_cursor->set_position (_session->audible_frame ());
4795 if (_follow_playhead && !_pending_initial_locate) {
4796 reset_x_origin_to_follow_playhead ();
4799 _pending_locate_request = false;
4800 _pending_initial_locate = false;
4803 void
4804 Editor::region_view_added (RegionView *)
4806 _summary->set_dirty ();
4809 void
4810 Editor::region_view_removed ()
4812 _summary->set_dirty ();
4815 TimeAxisView*
4816 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4818 TrackViewList::const_iterator j = track_views.begin ();
4819 while (j != track_views.end()) {
4820 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4821 if (rtv && rtv->route() == r) {
4822 return rtv;
4824 ++j;
4827 return 0;
4831 TrackViewList
4832 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4834 TrackViewList t;
4836 for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4837 TimeAxisView* tv = axis_view_from_route (*i);
4838 if (tv) {
4839 t.push_back (tv);
4843 return t;
4847 void
4848 Editor::handle_new_route (RouteList& routes)
4850 ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4852 RouteTimeAxisView *rtv;
4853 list<RouteTimeAxisView*> new_views;
4855 for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4856 boost::shared_ptr<Route> route = (*x);
4858 if (route->is_hidden() || route->is_monitor()) {
4859 continue;
4862 DataType dt = route->input()->default_type();
4864 if (dt == ARDOUR::DataType::AUDIO) {
4865 rtv = new AudioTimeAxisView (*this, _session, *track_canvas);
4866 rtv->set_route (route);
4867 } else if (dt == ARDOUR::DataType::MIDI) {
4868 rtv = new MidiTimeAxisView (*this, _session, *track_canvas);
4869 rtv->set_route (route);
4870 } else {
4871 throw unknown_type();
4874 new_views.push_back (rtv);
4875 track_views.push_back (rtv);
4877 rtv->effective_gain_display ();
4879 if (internal_editing()) {
4880 rtv->enter_internal_edit_mode ();
4881 } else {
4882 rtv->leave_internal_edit_mode ();
4885 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4886 rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed));
4889 _routes->routes_added (new_views);
4890 _summary->routes_added (new_views);
4892 if (show_editor_mixer_when_tracks_arrive) {
4893 show_editor_mixer (true);
4896 editor_list_button.set_sensitive (true);
4899 void
4900 Editor::timeaxisview_deleted (TimeAxisView *tv)
4902 if (_session && _session->deletion_in_progress()) {
4903 /* the situation is under control */
4904 return;
4907 ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
4909 RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
4911 _routes->route_removed (tv);
4913 if (tv == entered_track) {
4914 entered_track = 0;
4917 TimeAxisView::Children c = tv->get_child_list ();
4918 for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
4919 if (entered_track == i->get()) {
4920 entered_track = 0;
4924 /* remove it from the list of track views */
4926 TrackViewList::iterator i;
4928 if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
4929 i = track_views.erase (i);
4932 /* update whatever the current mixer strip is displaying, if revelant */
4934 boost::shared_ptr<Route> route;
4936 if (rtav) {
4937 route = rtav->route ();
4940 if (current_mixer_strip && current_mixer_strip->route() == route) {
4942 TimeAxisView* next_tv;
4944 if (track_views.empty()) {
4945 next_tv = 0;
4946 } else if (i == track_views.end()) {
4947 next_tv = track_views.front();
4948 } else {
4949 next_tv = (*i);
4953 if (next_tv) {
4954 set_selected_mixer_strip (*next_tv);
4955 } else {
4956 /* make the editor mixer strip go away setting the
4957 * button to inactive (which also unticks the menu option)
4960 ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
4965 void
4966 Editor::hide_track_in_display (TimeAxisView* tv, bool apply_to_selection)
4968 if (apply_to_selection) {
4969 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ) {
4971 TrackSelection::iterator j = i;
4972 ++j;
4974 hide_track_in_display (*i, false);
4976 i = j;
4978 } else {
4979 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
4981 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
4982 // this will hide the mixer strip
4983 set_selected_mixer_strip (*tv);
4986 _routes->hide_track_in_display (*tv);
4990 bool
4991 Editor::sync_track_view_list_and_routes ()
4993 track_views = TrackViewList (_routes->views ());
4995 _summary->set_dirty ();
4996 _group_tabs->set_dirty ();
4998 return false; // do not call again (until needed)
5001 void
5002 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
5004 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5005 theslot (**i);
5009 /** Find a RouteTimeAxisView by the ID of its route */
5010 RouteTimeAxisView*
5011 Editor::get_route_view_by_route_id (PBD::ID& id) const
5013 RouteTimeAxisView* v;
5015 for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5016 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5017 if(v->route()->id() == id) {
5018 return v;
5023 return 0;
5026 void
5027 Editor::fit_route_group (RouteGroup *g)
5029 TrackViewList ts = axis_views_from_routes (g->route_list ());
5030 fit_tracks (ts);
5033 void
5034 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5036 boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5038 if (r == 0) {
5039 _session->cancel_audition ();
5040 return;
5043 if (_session->is_auditioning()) {
5044 _session->cancel_audition ();
5045 if (r == last_audition_region) {
5046 return;
5050 _session->audition_region (r);
5051 last_audition_region = r;
5055 void
5056 Editor::hide_a_region (boost::shared_ptr<Region> r)
5058 r->set_hidden (true);
5061 void
5062 Editor::show_a_region (boost::shared_ptr<Region> r)
5064 r->set_hidden (false);
5067 void
5068 Editor::audition_region_from_region_list ()
5070 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5073 void
5074 Editor::hide_region_from_region_list ()
5076 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5079 void
5080 Editor::show_region_in_region_list ()
5082 _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5085 void
5086 Editor::step_edit_status_change (bool yn)
5088 if (yn) {
5089 start_step_editing ();
5090 } else {
5091 stop_step_editing ();
5095 void
5096 Editor::start_step_editing ()
5098 step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5101 void
5102 Editor::stop_step_editing ()
5104 step_edit_connection.disconnect ();
5107 bool
5108 Editor::check_step_edit ()
5110 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5111 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5112 if (mtv) {
5113 mtv->check_step_edit ();
5117 return true; // do it again, till we stop
5120 bool
5121 Editor::scroll_press (Direction dir)
5123 ++_scroll_callbacks;
5125 if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5126 /* delay the first auto-repeat */
5127 return true;
5130 switch (dir) {
5131 case LEFT:
5132 scroll_backward (1);
5133 break;
5135 case RIGHT:
5136 scroll_forward (1);
5137 break;
5139 case UP:
5140 scroll_tracks_up_line ();
5141 break;
5143 case DOWN:
5144 scroll_tracks_down_line ();
5145 break;
5148 /* do hacky auto-repeat */
5149 if (!_scroll_connection.connected ()) {
5151 _scroll_connection = Glib::signal_timeout().connect (
5152 sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), dir), 100
5155 _scroll_callbacks = 0;
5158 return true;
5161 void
5162 Editor::scroll_release ()
5164 _scroll_connection.disconnect ();
5167 /** Queue a change for the Editor viewport x origin to follow the playhead */
5168 void
5169 Editor::reset_x_origin_to_follow_playhead ()
5171 framepos_t const frame = playhead_cursor->current_frame;
5173 if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5175 if (_session->transport_speed() < 0) {
5177 if (frame > (current_page_frames() / 2)) {
5178 center_screen (frame-(current_page_frames()/2));
5179 } else {
5180 center_screen (current_page_frames()/2);
5183 } else {
5185 if (frame < leftmost_frame) {
5186 /* moving left */
5187 framepos_t l = 0;
5188 if (_session->transport_rolling()) {
5189 /* rolling; end up with the playhead at the right of the page */
5190 l = frame - current_page_frames ();
5191 } else {
5192 /* not rolling: end up with the playhead 3/4 of the way along the page */
5193 l = frame - (3 * current_page_frames() / 4);
5196 if (l < 0) {
5197 l = 0;
5200 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5201 } else {
5202 /* moving right */
5203 if (_session->transport_rolling()) {
5204 /* rolling: end up with the playhead on the left of the page */
5205 center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5206 } else {
5207 /* not rolling: end up with the playhead 1/4 of the way along the page */
5208 center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5215 void
5216 Editor::super_rapid_screen_update ()
5218 if (!_session || !_session->engine().running()) {
5219 return;
5222 /* METERING / MIXER STRIPS */
5224 /* update track meters, if required */
5225 if (is_mapped() && meters_running) {
5226 RouteTimeAxisView* rtv;
5227 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5228 if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5229 rtv->fast_update ();
5234 /* and any current mixer strip */
5235 if (current_mixer_strip) {
5236 current_mixer_strip->fast_update ();
5239 /* PLAYHEAD AND VIEWPORT */
5241 framepos_t const frame = _session->audible_frame();
5243 /* There are a few reasons why we might not update the playhead / viewport stuff:
5245 * 1. we don't update things when there's a pending locate request, otherwise
5246 * when the editor requests a locate there is a chance that this method
5247 * will move the playhead before the locate request is processed, causing
5248 * a visual glitch.
5249 * 2. if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5250 * 3. if we're still at the same frame that we were last time, there's nothing to do.
5253 if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5255 last_update_frame = frame;
5257 if (!_dragging_playhead) {
5258 playhead_cursor->set_position (frame);
5261 if (!_stationary_playhead) {
5263 if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5264 reset_x_origin_to_follow_playhead ();
5267 } else {
5269 /* don't do continuous scroll till the new position is in the rightmost quarter of the
5270 editor canvas
5272 #if 0
5273 // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5274 double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5275 if (target <= 0.0) {
5276 target = 0.0;
5278 if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5279 target = (target * 0.15) + (current * 0.85);
5280 } else {
5281 /* relax */
5284 current = target;
5285 set_horizontal_position (current);
5286 #endif
5293 void
5294 Editor::session_going_away ()
5296 _have_idled = false;
5298 _session_connections.drop_connections ();
5300 super_rapid_screen_update_connection.disconnect ();
5302 selection->clear ();
5303 cut_buffer->clear ();
5305 clicked_regionview = 0;
5306 clicked_axisview = 0;
5307 clicked_routeview = 0;
5308 clicked_crossfadeview = 0;
5309 entered_regionview = 0;
5310 entered_track = 0;
5311 last_update_frame = 0;
5312 _drags->abort ();
5314 playhead_cursor->canvas_item.hide ();
5316 /* rip everything out of the list displays */
5318 _regions->clear ();
5319 _routes->clear ();
5320 _route_groups->clear ();
5322 /* do this first so that deleting a track doesn't reset cms to null
5323 and thus cause a leak.
5326 if (current_mixer_strip) {
5327 if (current_mixer_strip->get_parent() != 0) {
5328 global_hpacker.remove (*current_mixer_strip);
5330 delete current_mixer_strip;
5331 current_mixer_strip = 0;
5334 /* delete all trackviews */
5336 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5337 delete *i;
5339 track_views.clear ();
5341 zoom_range_clock->set_session (0);
5342 nudge_clock->set_session (0);
5344 editor_list_button.set_active(false);
5345 editor_list_button.set_sensitive(false);
5347 /* clear tempo/meter rulers */
5348 remove_metric_marks ();
5349 hide_measures ();
5350 clear_marker_display ();
5352 delete current_bbt_points;
5353 current_bbt_points = 0;
5355 /* get rid of any existing editor mixer strip */
5357 WindowTitle title(Glib::get_application_name());
5358 title += _("Editor");
5360 set_title (title.get_string());
5362 SessionHandlePtr::session_going_away ();
5366 void
5367 Editor::show_editor_list (bool yn)
5369 if (yn) {
5370 _the_notebook.show ();
5371 } else {
5372 _the_notebook.hide ();
5376 void
5377 Editor::change_region_layering_order ()
5379 framepos_t const position = get_preferred_edit_position ();
5381 if (!clicked_routeview) {
5382 if (layering_order_editor) {
5383 layering_order_editor->hide ();
5385 return;
5388 boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
5390 if (!track) {
5391 return;
5394 boost::shared_ptr<Playlist> pl = track->playlist();
5396 if (!pl) {
5397 return;
5400 if (layering_order_editor == 0) {
5401 layering_order_editor = new RegionLayeringOrderEditor(*this);
5404 layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position);
5405 layering_order_editor->maybe_present ();
5408 void
5409 Editor::update_region_layering_order_editor ()
5411 if (layering_order_editor && layering_order_editor->is_visible ()) {
5412 change_region_layering_order ();
5416 void
5417 Editor::setup_fade_images ()
5419 _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear")));
5420 _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut")));
5421 _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut")));
5422 _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut")));
5423 _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut")));
5425 _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear")));
5426 _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut")));
5427 _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut")));
5428 _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut")));
5429 _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut")));
5433 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
5434 Gtk::MenuItem&
5435 Editor::action_menu_item (std::string const & name)
5437 Glib::RefPtr<Action> a = editor_actions->get_action (name);
5438 assert (a);
5440 return *manage (a->create_menu_item ());
5443 void
5444 Editor::resize_text_widgets ()
5446 set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_FUDGE+10, 15);
5447 set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_FUDGE+10, 15);
5448 set_size_request_to_display_given_text (snap_type_selector, snap_type_strings, COMBO_FUDGE+10, 15);
5449 set_size_request_to_display_given_text (snap_mode_selector, snap_mode_strings, COMBO_FUDGE+10, 15);
5450 set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_FUDGE+10, 15);
5453 void
5454 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
5456 EventBox* b = manage (new EventBox);
5457 b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
5458 Label* l = manage (new Label (name));
5459 l->set_angle (-90);
5460 b->add (*l);
5461 b->show_all ();
5462 _the_notebook.append_page (widget, *b);
5465 bool
5466 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
5468 if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
5469 _the_notebook.set_current_page (_the_notebook.page_num (*page));
5472 if (ev->type == GDK_2BUTTON_PRESS) {
5474 /* double-click on a notebook tab shrinks or expands the notebook */
5476 if (_notebook_shrunk) {
5477 edit_pane.set_position (pre_maximal_horizontal_pane_position);
5478 _notebook_shrunk = false;
5479 } else {
5480 pre_maximal_horizontal_pane_position = edit_pane.get_position ();
5481 edit_pane.set_position (edit_pane.get_position() + page->get_width());
5482 _notebook_shrunk = true;
5486 return true;