fix keyboard event handling for host-provided plugin GUIs
[ardour2.git] / gtk2_ardour / editor.cc
blobd6dcfc949b673ff411e70adcb1f4ced40e06aff5
1 /*
2 Copyright (C) 2000-2007 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 #include <sys/time.h>
21 #include <unistd.h>
22 #include <cstdlib>
23 #include <cmath>
24 #include <string>
25 #include <algorithm>
27 #include <boost/none.hpp>
29 #include <sigc++/bind.h>
31 #include <pbd/convert.h>
32 #include <pbd/error.h>
33 #include <pbd/enumwriter.h>
34 #include <pbd/memento_command.h>
36 #include <glibmm/miscutils.h>
37 #include <gtkmm/image.h>
38 #include <gdkmm/color.h>
39 #include <gdkmm/bitmap.h>
41 #include <gtkmm2ext/grouped_buttons.h>
42 #include <gtkmm2ext/gtk_ui.h>
43 #include <gtkmm2ext/tearoff.h>
44 #include <gtkmm2ext/utils.h>
45 #include <gtkmm2ext/window_title.h>
46 #include <gtkmm2ext/choice.h>
48 #include <ardour/audio_track.h>
49 #include <ardour/audio_diskstream.h>
50 #include <ardour/plugin_manager.h>
51 #include <ardour/location.h>
52 #include <ardour/audioplaylist.h>
53 #include <ardour/audioregion.h>
54 #include <ardour/region.h>
55 #include <ardour/session_route.h>
56 #include <ardour/tempo.h>
57 #include <ardour/utils.h>
58 #include <ardour/profile.h>
60 #include <control_protocol/control_protocol.h>
62 #include "ardour_ui.h"
63 #include "editor.h"
64 #include "keyboard.h"
65 #include "marker.h"
66 #include "playlist_selector.h"
67 #include "audio_region_view.h"
68 #include "rgb_macros.h"
69 #include "selection.h"
70 #include "audio_streamview.h"
71 #include "time_axis_view.h"
72 #include "audio_time_axis.h"
73 #include "utils.h"
74 #include "crossfade_view.h"
75 #include "editing.h"
76 #include "public_editor.h"
77 #include "crossfade_edit.h"
78 #include "canvas_impl.h"
79 #include "actions.h"
80 #include "tempo_lines.h"
81 #include "gui_thread.h"
82 #include "sfdb_ui.h"
83 #include "rhythm_ferret.h"
84 #include "actions.h"
85 #include "region_layering_order_editor.h"
87 #ifdef FFT_ANALYSIS
88 #include "analysis_window.h"
89 #endif
91 #include "i18n.h"
93 /* <CMT Additions> */
94 #include "imageframe_socket_handler.h"
95 /* </CMT Additions> */
97 using namespace std;
98 using namespace sigc;
99 using namespace ARDOUR;
100 using namespace PBD;
101 using namespace Gtk;
102 using namespace Glib;
103 using namespace Gtkmm2ext;
104 using namespace Editing;
106 using PBD::atoi;
108 const double Editor::timebar_height = 15.0;
110 #include "editor_xpms"
112 static const gchar *_snap_type_strings[] = {
113 N_("CD Frames"),
114 N_("SMPTE Frames"),
115 N_("SMPTE Seconds"),
116 N_("SMPTE Minutes"),
117 N_("Seconds"),
118 N_("Minutes"),
119 N_("Beats/32"),
120 N_("Beats/16"),
121 N_("Beats/8"),
122 N_("Beats/4"),
123 N_("Beats/3"),
124 N_("Beats"),
125 N_("Bars"),
126 N_("Marks"),
127 N_("Region starts"),
128 N_("Region ends"),
129 N_("Region syncs"),
130 N_("Region bounds"),
134 static const gchar *_snap_mode_strings[] = {
135 N_("No Grid"),
136 N_("Grid"),
137 N_("Magnetic"),
141 static const gchar *_edit_point_strings[] = {
142 N_("Playhead"),
143 N_("Marker"),
144 N_("Mouse"),
148 static const gchar *_zoom_focus_strings[] = {
149 N_("Left"),
150 N_("Right"),
151 N_("Center"),
152 N_("Playhead"),
153 N_("Mouse"),
154 N_("Active Mark"),
158 #ifdef USE_RUBBERBAND
159 static const gchar *_rb_opt_strings[] = {
160 N_("Mushy"),
161 N_("Smooth"),
162 N_("Balanced multitimbral mixture"),
163 N_("Unpitched percussion with stable notes"),
164 N_("Crisp monophonic instrumental"),
165 N_("Unpitched solo percussion"),
168 #endif
170 /* Soundfile drag-n-drop */
172 Gdk::Cursor* Editor::cross_hair_cursor = 0;
173 Gdk::Cursor* Editor::selector_cursor = 0;
174 Gdk::Cursor* Editor::trimmer_cursor = 0;
175 Gdk::Cursor* Editor::grabber_cursor = 0;
176 Gdk::Cursor* Editor::grabber_edit_point_cursor = 0;
177 Gdk::Cursor* Editor::zoom_cursor = 0;
178 Gdk::Cursor* Editor::time_fx_cursor = 0;
179 Gdk::Cursor* Editor::fader_cursor = 0;
180 Gdk::Cursor* Editor::speaker_cursor = 0;
181 Gdk::Cursor* Editor::wait_cursor = 0;
182 Gdk::Cursor* Editor::timebar_cursor = 0;
183 Gdk::Cursor* Editor::transparent_cursor = 0;
185 void
186 show_me_the_size (Requisition* r, const char* what)
188 cerr << "size of " << what << " = " << r->width << " x " << r->height << endl;
191 void
192 DragInfo::clear_copied_locations ()
194 for (list<Location*>::iterator i = copied_locations.begin(); i != copied_locations.end(); ++i) {
195 delete *i;
197 copied_locations.clear ();
200 #ifdef GTKOSX
201 static void
202 pane_size_watcher (Paned* pane)
204 /* if the handle of a pane vanishes into (at least) the tabs of a notebook,
205 it is no longer accessible. so stop that. this doesn't happen on X11,
206 just the quartz backend.
208 ugh.
211 int max_width_of_lhs = GTK_WIDGET(pane->gobj())->allocation.width - 25;
213 gint pos = pane->get_position ();
215 if (pos > max_width_of_lhs) {
216 pane->set_position (max_width_of_lhs);
219 #endif
221 Editor::Editor ()
223 /* time display buttons */
224 minsec_label (_("Mins:Secs")),
225 bbt_label (_("Bars:Beats")),
226 smpte_label (_("Timecode")),
227 frame_label (_("Samples")),
228 tempo_label (_("Tempo")),
229 meter_label (_("Meter")),
230 mark_label (_("Location Markers")),
231 range_mark_label (_("Range Markers")),
232 transport_mark_label (_("Loop/Punch Ranges")),
233 cd_mark_label (_("CD Markers")),
234 edit_packer (3, 4, true),
236 /* the values here don't matter: layout widgets
237 reset them as needed.
240 vertical_adjustment (0.0, 0.0, 10.0, 400.0),
241 horizontal_adjustment (0.0, 0.0, 20.0, 1200.0),
243 /* tool bar related */
245 edit_point_clock (X_("editpoint"), false, X_("EditPointClock"), true),
246 zoom_range_clock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, true),
248 toolbar_selection_clock_table (2,3),
250 automation_mode_button (_("mode")),
251 global_automation_button (_("automation")),
253 /* <CMT Additions> */
254 image_socket_listener(0),
255 /* </CMT Additions> */
257 /* nudge */
259 nudge_clock (X_("nudge"), false, X_("NudgeClock"), true, true),
260 meters_running(false)
263 constructed = false;
265 /* we are a singleton */
267 PublicEditor::_instance = this;
269 session = 0;
270 _have_idled = false;
272 selection = new Selection;
273 cut_buffer = new Selection;
275 all_group_is_active = false;
277 selection->TimeChanged.connect (mem_fun(*this, &Editor::time_selection_changed));
278 selection->TracksChanged.connect (mem_fun(*this, &Editor::track_selection_changed));
279 selection->RegionsChanged.connect (mem_fun(*this, &Editor::region_selection_changed));
280 selection->PointsChanged.connect (mem_fun(*this, &Editor::point_selection_changed));
281 selection->MarkersChanged.connect (mem_fun(*this, &Editor::marker_selection_changed));
283 clicked_regionview = 0;
284 clicked_trackview = 0;
285 clicked_audio_trackview = 0;
286 clicked_crossfadeview = 0;
287 clicked_control_point = 0;
288 last_update_frame = 0;
289 drag_info.item = 0;
290 current_mixer_strip = 0;
291 current_bbt_points = 0;
292 tempo_lines = 0;
294 snap_type_strings = I18N (_snap_type_strings);
295 snap_mode_strings = I18N (_snap_mode_strings);
296 zoom_focus_strings = I18N (_zoom_focus_strings);
297 edit_point_strings = I18N (_edit_point_strings);
298 #ifdef USE_RUBBERBAND
299 rb_opt_strings = I18N (_rb_opt_strings);
300 #endif
302 snap_threshold = 5.0;
303 bbt_beat_subdivision = 4;
304 canvas_width = 0;
305 canvas_height = 0;
306 last_autoscroll_x = 0;
307 last_autoscroll_y = 0;
308 autoscroll_active = false;
309 autoscroll_timeout_tag = -1;
310 interthread_progress_window = 0;
311 logo_item = 0;
313 #ifdef FFT_ANALYSIS
314 analysis_window = 0;
315 #endif
317 current_interthread_info = 0;
318 _show_measures = true;
319 _show_waveforms = true;
320 _show_waveforms_rectified = false;
321 _show_waveforms_recording = true;
322 export_dialog = 0;
323 export_range_markers_dialog = 0;
324 show_gain_after_trim = false;
325 route_redisplay_does_not_sync_order_keys = false;
326 route_redisplay_does_not_reset_order_keys = false;
327 no_route_list_redisplay = false;
328 verbose_cursor_on = true;
329 route_removal = false;
330 show_automatic_regions_in_region_list = true;
331 last_item_entered = 0;
332 last_item_entered_n = 0;
334 region_list_sort_type = (Editing::RegionListSortType) 0;
335 have_pending_keyboard_selection = false;
336 _follow_playhead = true;
337 _stationary_playhead = false;
338 _xfade_visibility = true;
339 editor_ruler_menu = 0;
340 no_ruler_shown_update = false;
341 edit_group_list_menu = 0;
342 route_list_menu = 0;
343 region_list_menu = 0;
344 marker_menu = 0;
345 start_end_marker_menu = 0;
346 range_marker_menu = 0;
347 marker_menu_item = 0;
348 tm_marker_menu = 0;
349 transport_marker_menu = 0;
350 new_transport_marker_menu = 0;
351 editor_mixer_strip_width = Wide;
352 show_editor_mixer_when_tracks_arrive = false;
353 region_edit_menu_split_item = 0;
354 temp_location = 0;
355 region_edit_menu_split_multichannel_item = 0;
356 leftmost_frame = 0;
357 ignore_mouse_mode_toggle = false;
358 current_stepping_trackview = 0;
359 entered_track = 0;
360 entered_regionview = 0;
361 entered_marker = 0;
362 clear_entered_track = false;
363 _new_regionviews_show_envelope = false;
364 current_timefx = 0;
365 in_edit_group_row_change = false;
366 playhead_cursor = 0;
367 button_release_can_deselect = true;
368 _dragging_playhead = false;
369 _dragging_edit_point = false;
370 _dragging_hscrollbar = false;
371 select_new_marker = false;
372 rhythm_ferret = 0;
373 layering_order_editor = 0;
374 allow_vertical_scroll = false;
375 no_save_visual = false;
376 need_resize_line = false;
377 resize_line_y = 0;
378 old_resize_line_y = -1;
379 no_region_list_redisplay = false;
380 resize_idle_id = -1;
382 _scrubbing = false;
383 scrubbing_direction = 0;
385 sfbrowser = 0;
387 location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get();
388 location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get();
389 location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get();
390 location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get();
391 location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get();
393 range_marker_drag_rect = 0;
394 marker_drag_line = 0;
396 _edit_point = EditAtMouse;
397 set_mouse_mode (MouseObject, true);
399 frames_per_unit = 2048; /* too early to use reset_zoom () */
400 reset_hscrollbar_stepping ();
402 zoom_focus = ZoomFocusLeft;
403 set_zoom_focus (ZoomFocusLeft);
404 zoom_range_clock.ValueChanged.connect (mem_fun(*this, &Editor::zoom_adjustment_changed));
406 bbt_label.set_name ("EditorTimeButton");
407 bbt_label.set_size_request (-1, (int)timebar_height);
408 bbt_label.set_alignment (1.0, 0.5);
409 bbt_label.set_padding (5,0);
410 bbt_label.hide ();
411 bbt_label.set_no_show_all();
412 minsec_label.set_name ("EditorTimeButton");
413 minsec_label.set_size_request (-1, (int)timebar_height);
414 minsec_label.set_alignment (1.0, 0.5);
415 minsec_label.set_padding (5,0);
416 minsec_label.hide ();
417 minsec_label.set_no_show_all();
418 smpte_label.set_name ("EditorTimeButton");
419 smpte_label.set_size_request (-1, (int)timebar_height);
420 smpte_label.set_alignment (1.0, 0.5);
421 smpte_label.set_padding (5,0);
422 smpte_label.hide ();
423 smpte_label.set_no_show_all();
424 frame_label.set_name ("EditorTimeButton");
425 frame_label.set_size_request (-1, (int)timebar_height);
426 frame_label.set_alignment (1.0, 0.5);
427 frame_label.set_padding (5,0);
428 frame_label.hide ();
429 frame_label.set_no_show_all();
431 tempo_label.set_name ("EditorTimeButton");
432 tempo_label.set_size_request (-1, (int)timebar_height);
433 tempo_label.set_alignment (1.0, 0.5);
434 tempo_label.set_padding (5,0);
435 tempo_label.hide();
436 tempo_label.set_no_show_all();
437 meter_label.set_name ("EditorTimeButton");
438 meter_label.set_size_request (-1, (int)timebar_height);
439 meter_label.set_alignment (1.0, 0.5);
440 meter_label.set_padding (5,0);
441 meter_label.hide();
442 meter_label.set_no_show_all();
443 mark_label.set_name ("EditorTimeButton");
444 mark_label.set_size_request (-1, (int)timebar_height);
445 mark_label.set_alignment (1.0, 0.5);
446 mark_label.set_padding (5,0);
447 mark_label.hide();
448 mark_label.set_no_show_all();
449 cd_mark_label.set_name ("EditorTimeButton");
450 cd_mark_label.set_size_request (-1, (int)timebar_height);
451 cd_mark_label.set_alignment (1.0, 0.5);
452 cd_mark_label.set_padding (5,0);
453 cd_mark_label.hide();
454 cd_mark_label.set_no_show_all();
455 range_mark_label.set_name ("EditorTimeButton");
456 range_mark_label.set_size_request (-1, (int)timebar_height);
457 range_mark_label.set_alignment (1.0, 0.5);
458 range_mark_label.set_padding (5,0);
459 range_mark_label.hide();
460 range_mark_label.set_no_show_all();
461 transport_mark_label.set_name ("EditorTimeButton");
462 transport_mark_label.set_size_request (-1, (int)timebar_height);
463 transport_mark_label.set_alignment (1.0, 0.5);
464 transport_mark_label.set_padding (5,0);
465 transport_mark_label.hide();
466 transport_mark_label.set_no_show_all();
468 initialize_rulers ();
469 initialize_canvas ();
471 edit_controls_vbox.set_spacing (0);
472 horizontal_adjustment.signal_value_changed().connect (mem_fun(*this, &Editor::scroll_canvas_horizontally), false);
473 vertical_adjustment.signal_value_changed().connect (mem_fun(*this, &Editor::tie_vertical_scrolling), true);
474 track_canvas->signal_map_event().connect (mem_fun (*this, &Editor::track_canvas_map_handler));
476 controls_layout.add (edit_controls_vbox);
477 controls_layout.set_name ("EditControlsBase");
478 controls_layout.add_events (Gdk::SCROLL_MASK);
479 controls_layout.signal_scroll_event().connect (mem_fun(*this, &Editor::control_layout_scroll), false);
481 controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
482 controls_layout.signal_button_release_event().connect (mem_fun(*this, &Editor::edit_controls_button_release));
483 controls_layout_size_request_connection = controls_layout.signal_size_request().connect (mem_fun (*this, &Editor::controls_layout_size_request));
485 edit_vscrollbar.set_adjustment (vertical_adjustment);
486 edit_hscrollbar.set_adjustment (horizontal_adjustment);
488 edit_hscrollbar.signal_button_press_event().connect (mem_fun(*this, &Editor::hscrollbar_button_press), false);
489 edit_hscrollbar.signal_button_release_event().connect (mem_fun(*this, &Editor::hscrollbar_button_release), false);
490 edit_hscrollbar.signal_size_allocate().connect (mem_fun(*this, &Editor::hscrollbar_allocate));
492 edit_hscrollbar.set_name ("EditorHScrollbar");
494 build_cursors ();
495 setup_toolbar ();
497 edit_point_clock.ValueChanged.connect (mem_fun(*this, &Editor::edit_point_clock_changed));
499 //time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2);
500 time_canvas_vbox.set_size_request (-1, -1);
502 ruler_label_event_box.add (ruler_label_vbox);
503 ruler_label_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
504 ruler_label_event_box.set_name ("TimebarLabelBase");
505 ruler_label_event_box.signal_button_release_event().connect (mem_fun(*this, &Editor::ruler_label_button_release));
507 time_button_event_box.add (time_button_vbox);
509 time_button_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
510 time_button_event_box.set_name ("TimebarLabelBase");
511 time_button_event_box.signal_button_release_event().connect (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 edit_packer.attach (edit_vscrollbar, 0, 1, 0, 4, FILL, FILL|EXPAND, 0, 0);
530 edit_packer.attach (ruler_label_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0);
531 edit_packer.attach (time_button_event_box, 1, 2, 1, 2, FILL, SHRINK, 0, 0);
532 edit_packer.attach (time_canvas_event_box, 2, 3, 0, 1, FILL|EXPAND, FILL, 0, 0);
534 edit_packer.attach (controls_layout, 1, 2, 2, 3, FILL, FILL|EXPAND, 0, 0);
535 edit_packer.attach (track_canvas_event_box, 2, 3, 1, 3, FILL|EXPAND, FILL|EXPAND, 0, 0);
537 edit_packer.attach (zoom_box, 1, 2, 3, 4, FILL, FILL, 0, 0);
538 edit_packer.attach (edit_hscrollbar, 2, 3, 3, 4, FILL|EXPAND, FILL, 0, 0);
540 bottom_hbox.set_border_width (2);
541 bottom_hbox.set_spacing (3);
543 route_display_model = ListStore::create(route_display_columns);
544 route_list_display.set_model (route_display_model);
545 route_list_display.append_column (_("Show"), route_display_columns.visible);
546 route_list_display.append_column (_("Name"), route_display_columns.text);
547 route_list_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
548 route_list_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
549 route_list_display.set_headers_visible (true);
550 route_list_display.set_name ("TrackListDisplay");
551 route_list_display.get_selection()->set_mode (SELECTION_NONE);
552 route_list_display.set_reorderable (true);
553 route_list_display.set_size_request (100,-1);
555 CellRendererToggle* route_list_visible_cell = dynamic_cast<CellRendererToggle*>(route_list_display.get_column_cell_renderer (0));
556 route_list_visible_cell->property_activatable() = true;
557 route_list_visible_cell->property_radio() = false;
559 route_display_model->signal_row_deleted().connect (mem_fun (*this, &Editor::route_list_delete));
560 route_display_model->signal_row_changed().connect (mem_fun (*this, &Editor::route_list_change));
561 route_display_model->signal_rows_reordered().connect (mem_fun (*this, &Editor::track_list_reorder));
563 route_list_display.signal_button_press_event().connect (mem_fun (*this, &Editor::route_list_display_button_press), false);
565 route_list_scroller.add (route_list_display);
566 route_list_scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
568 group_model = ListStore::create(group_columns);
569 edit_group_display.set_model (group_model);
570 edit_group_display.append_column (_("Name"), group_columns.text);
571 edit_group_display.append_column (_("Active"), group_columns.is_active);
572 edit_group_display.append_column (_("Show"), group_columns.is_visible);
573 edit_group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
574 edit_group_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
575 edit_group_display.get_column (2)->set_data (X_("colnum"), GUINT_TO_POINTER(2));
576 edit_group_display.get_column (0)->set_expand (true);
577 edit_group_display.get_column (1)->set_expand (false);
578 edit_group_display.get_column (2)->set_expand (false);
579 edit_group_display.set_headers_visible (true);
581 /* name is directly editable */
583 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(edit_group_display.get_column_cell_renderer (0));
584 name_cell->property_editable() = true;
585 name_cell->signal_edited().connect (mem_fun (*this, &Editor::edit_group_name_edit));
587 /* use checkbox for the active + visible columns */
589 CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*>(edit_group_display.get_column_cell_renderer (1));
590 active_cell->property_activatable() = true;
591 active_cell->property_radio() = false;
593 active_cell = dynamic_cast<CellRendererToggle*>(edit_group_display.get_column_cell_renderer (1));
594 active_cell->property_activatable() = true;
595 active_cell->property_radio() = false;
597 group_model->signal_row_changed().connect (mem_fun (*this, &Editor::edit_group_row_change));
599 edit_group_display.set_name ("EditGroupList");
600 edit_group_display.get_selection()->set_mode (SELECTION_SINGLE);
601 edit_group_display.set_headers_visible (true);
602 edit_group_display.set_reorderable (false);
603 edit_group_display.set_rules_hint (true);
604 edit_group_display.set_size_request (75, -1);
606 edit_group_display_scroller.add (edit_group_display);
607 edit_group_display_scroller.set_policy (POLICY_AUTOMATIC, POLICY_AUTOMATIC);
609 edit_group_display.signal_button_press_event().connect (mem_fun(*this, &Editor::edit_group_list_button_press_event), false);
611 VBox* edit_group_display_packer = manage (new VBox());
612 HBox* edit_group_display_button_box = manage (new HBox());
613 edit_group_display_button_box->set_homogeneous (true);
615 Button* edit_group_add_button = manage (new Button ());
616 Button* edit_group_remove_button = manage (new Button ());
618 Widget* w;
620 w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
621 w->show();
622 edit_group_add_button->add (*w);
624 w = manage (new Image (Stock::REMOVE, ICON_SIZE_BUTTON));
625 w->show();
626 edit_group_remove_button->add (*w);
628 edit_group_add_button->signal_clicked().connect (mem_fun (*this, &Editor::new_edit_group));
629 edit_group_remove_button->signal_clicked().connect (mem_fun (*this, &Editor::remove_selected_edit_group));
631 edit_group_display_button_box->pack_start (*edit_group_add_button);
632 edit_group_display_button_box->pack_start (*edit_group_remove_button);
634 edit_group_display_packer->pack_start (edit_group_display_scroller, true, true);
635 edit_group_display_packer->pack_start (*edit_group_display_button_box, false, false);
637 region_list_display.set_size_request (100, -1);
638 region_list_display.set_name ("RegionListDisplay");
639 /* Try to prevent single mouse presses from initiating edits.
640 This relies on a hack in gtktreeview.c:gtk_treeview_button_press()
642 region_list_display.set_data ("mouse-edits-require-mod1", (gpointer) 0x1);
644 region_list_model = TreeStore::create (region_list_columns);
645 region_list_model->set_sort_func (0, mem_fun (*this, &Editor::region_list_sorter));
646 region_list_model->set_sort_column (0, SORT_ASCENDING);
648 region_list_display.set_model (region_list_model);
649 region_list_display.append_column (_("Regions"), region_list_columns.name);
650 region_list_display.set_headers_visible (false);
652 CellRendererText* region_name_cell = dynamic_cast<CellRendererText*>(region_list_display.get_column_cell_renderer (0));
653 region_name_cell->property_editable() = true;
654 region_name_cell->signal_edited().connect (mem_fun (*this, &Editor::region_name_edit));
656 region_list_display.get_selection()->set_select_function (mem_fun (*this, &Editor::region_list_selection_filter));
658 TreeViewColumn* tv_col = region_list_display.get_column(0);
659 CellRendererText* renderer = dynamic_cast<CellRendererText*>(region_list_display.get_column_cell_renderer (0));
660 tv_col->add_attribute(renderer->property_text(), region_list_columns.name);
661 tv_col->add_attribute(renderer->property_foreground_gdk(), region_list_columns.color_);
663 region_list_display.get_selection()->set_mode (SELECTION_MULTIPLE);
664 region_list_display.add_object_drag (region_list_columns.region.index(), "regions");
666 /* setup DnD handling */
668 list<TargetEntry> region_list_target_table;
670 region_list_target_table.push_back (TargetEntry ("text/plain"));
671 region_list_target_table.push_back (TargetEntry ("text/uri-list"));
672 region_list_target_table.push_back (TargetEntry ("application/x-rootwin-drop"));
674 region_list_display.add_drop_targets (region_list_target_table);
675 region_list_display.signal_drag_data_received().connect (mem_fun(*this, &Editor::region_list_display_drag_data_received));
677 region_list_scroller.add (region_list_display);
678 region_list_scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
680 region_list_display.signal_key_press_event().connect (mem_fun(*this, &Editor::region_list_display_key_press));
681 region_list_display.signal_key_release_event().connect (mem_fun(*this, &Editor::region_list_display_key_release));
682 region_list_display.signal_button_press_event().connect (mem_fun(*this, &Editor::region_list_display_button_press), false);
683 region_list_display.signal_button_release_event().connect (mem_fun(*this, &Editor::region_list_display_button_release));
684 region_list_display.get_selection()->signal_changed().connect (mem_fun(*this, &Editor::region_list_selection_changed));
685 // region_list_display.signal_popup_menu().connect (bind (mem_fun (*this, &Editor::show_region_list_display_context_menu), 1, 0));
687 named_selection_scroller.add (named_selection_display);
688 named_selection_scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
690 named_selection_model = TreeStore::create (named_selection_columns);
691 named_selection_display.set_model (named_selection_model);
692 named_selection_display.append_column (_("Chunks"), named_selection_columns.text);
693 named_selection_display.set_headers_visible (false);
694 named_selection_display.set_size_request (100, -1);
695 named_selection_display.set_name ("NamedSelectionDisplay");
697 named_selection_display.get_selection()->set_mode (SELECTION_SINGLE);
698 named_selection_display.set_size_request (100, -1);
699 named_selection_display.signal_button_release_event().connect (mem_fun(*this, &Editor::named_selection_display_button_release), false);
700 named_selection_display.signal_key_release_event().connect (mem_fun(*this, &Editor::named_selection_display_key_release), false);
701 named_selection_display.get_selection()->signal_changed().connect (mem_fun (*this, &Editor::named_selection_display_selection_changed));
703 /* SNAPSHOTS */
705 snapshot_display_model = ListStore::create (snapshot_display_columns);
706 snapshot_display.set_model (snapshot_display_model);
707 snapshot_display.append_column (X_("snapshot"), snapshot_display_columns.visible_name);
708 snapshot_display.set_name ("SnapshotDisplay");
709 snapshot_display.set_size_request (75, -1);
710 snapshot_display.set_headers_visible (false);
711 snapshot_display.set_reorderable (false);
712 snapshot_display_scroller.add (snapshot_display);
713 snapshot_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
715 snapshot_display.get_selection()->signal_changed().connect (mem_fun(*this, &Editor::snapshot_display_selection_changed));
716 snapshot_display.signal_button_press_event().connect (mem_fun (*this, &Editor::snapshot_display_button_press), false);
718 Gtk::Label* nlabel;
720 nlabel = manage (new Label (_("Regions")));
721 nlabel->set_angle (-90);
722 the_notebook.append_page (region_list_scroller, *nlabel);
723 nlabel = manage (new Label (_("Tracks/Busses")));
724 nlabel->set_angle (-90);
725 the_notebook.append_page (route_list_scroller, *nlabel);
726 nlabel = manage (new Label (_("Snapshots")));
727 nlabel->set_angle (-90);
728 the_notebook.append_page (snapshot_display_scroller, *nlabel);
729 nlabel = manage (new Label (_("Edit Groups")));
730 nlabel->set_angle (-90);
731 the_notebook.append_page (*edit_group_display_packer, *nlabel);
733 if (!Profile->get_sae()) {
734 nlabel = manage (new Label (_("Chunks")));
735 nlabel->set_angle (-90);
736 the_notebook.append_page (named_selection_scroller, *nlabel);
739 the_notebook.set_show_tabs (true);
740 the_notebook.set_scrollable (true);
741 the_notebook.popup_enable ();
742 the_notebook.set_tab_pos (Gtk::POS_RIGHT);
744 post_maximal_editor_width = 0;
745 post_maximal_pane_position = 0;
746 edit_pane.pack1 (edit_packer, true, true);
747 edit_pane.pack2 (the_notebook, false, true);
749 edit_pane.signal_size_allocate().connect (bind (mem_fun(*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&edit_pane)));
750 #ifdef GTKOSX
751 Glib::PropertyProxy<int> proxy = edit_pane.property_position();
752 proxy.signal_changed().connect (bind (sigc::ptr_fun (pane_size_watcher), static_cast<Paned*> (&edit_pane)));
753 #endif
755 top_hbox.pack_start (toolbar_frame, true, true);
757 HBox *hbox = manage (new HBox);
758 hbox->pack_start (edit_pane, true, true);
760 global_vpacker.pack_start (top_hbox, false, false);
761 global_vpacker.pack_start (*hbox, true, true);
763 global_hpacker.pack_start (global_vpacker, true, true);
765 set_name ("EditorWindow");
766 add_accel_group (ActionManager::ui_manager->get_accel_group());
768 status_bar_hpacker.show ();
770 vpacker.pack_end (status_bar_hpacker, false, false);
771 vpacker.pack_end (global_hpacker, true, true);
773 /* register actions now so that set_state() can find them and set toggles/checks etc */
775 register_actions ();
777 snap_type = SnapToBeat;
778 set_snap_to (snap_type);
779 snap_mode = SnapOff;
780 set_snap_mode (snap_mode);
781 set_edit_point_preference (EditAtMouse, true);
783 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
784 set_state (*node);
786 _playlist_selector = new PlaylistSelector();
787 _playlist_selector->signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
789 RegionView::RegionViewGoingAway.connect (mem_fun(*this, &Editor::catch_vanishing_regionview));
791 /* nudge stuff */
793 nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
794 nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
796 ARDOUR_UI::instance()->tooltips().set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
797 ARDOUR_UI::instance()->tooltips().set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
799 nudge_forward_button.set_name ("TransportButton");
800 nudge_backward_button.set_name ("TransportButton");
802 fade_context_menu.set_name ("ArdourContextMenu");
804 /* icons, titles, WM stuff */
806 list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
807 Glib::RefPtr<Gdk::Pixbuf> icon;
809 if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
810 window_icons.push_back (icon);
812 if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
813 window_icons.push_back (icon);
815 if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
816 window_icons.push_back (icon);
818 if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
819 window_icons.push_back (icon);
821 if (!window_icons.empty()) {
822 set_icon_list (window_icons);
823 set_default_icon_list (window_icons);
826 WindowTitle title(Glib::get_application_name());
827 title += _("Editor");
828 set_title (title.get_string());
829 set_wmclass (X_("ardour_editor"), PROGRAM_NAME);
831 add (vpacker);
832 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
834 signal_configure_event().connect (mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
835 signal_delete_event().connect (mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
837 /* allow external control surfaces/protocols to do various things */
839 ControlProtocol::ZoomToSession.connect (mem_fun (*this, &Editor::temporal_zoom_session));
840 ControlProtocol::ZoomIn.connect (bind (mem_fun (*this, &Editor::temporal_zoom_step), false));
841 ControlProtocol::ZoomOut.connect (bind (mem_fun (*this, &Editor::temporal_zoom_step), true));
842 ControlProtocol::ScrollTimeline.connect (mem_fun (*this, &Editor::control_scroll));
843 BasicUI::AccessAction.connect (mem_fun (*this, &Editor::access_action));
845 Config->ParameterChanged.connect (mem_fun (*this, &Editor::parameter_changed));
846 Route::SyncOrderKeys.connect (mem_fun (*this, &Editor::sync_order_keys));
848 constructed = true;
849 instant_save ();
852 Editor::~Editor()
854 /* <CMT Additions> */
855 if(image_socket_listener)
857 if(image_socket_listener->is_connected())
859 image_socket_listener->close_connection() ;
862 delete image_socket_listener ;
863 image_socket_listener = 0 ;
865 /* </CMT Additions> */
867 if (track_canvas) {
868 delete track_canvas;
869 track_canvas = 0;
873 void
874 Editor::add_toplevel_controls (Container& cont)
876 vpacker.pack_start (cont, false, false);
877 cont.show_all ();
880 void
881 Editor::catch_vanishing_regionview (RegionView *rv)
883 /* note: the selection will take care of the vanishing
884 audioregionview by itself.
887 if (rv->get_canvas_group() == drag_info.item) {
888 end_grab (drag_info.item, 0);
891 if (clicked_regionview == rv) {
892 clicked_regionview = 0;
895 if (entered_regionview == rv) {
896 set_entered_regionview (0);
900 void
901 Editor::set_entered_regionview (RegionView* rv)
903 if (rv == entered_regionview) {
904 return;
907 if (entered_regionview) {
908 entered_regionview->exited ();
911 if ((entered_regionview = rv) != 0) {
912 entered_regionview->entered ();
916 void
917 Editor::set_entered_track (TimeAxisView* tav)
919 if (entered_track) {
920 entered_track->exited ();
923 if ((entered_track = tav) != 0) {
924 entered_track->entered ();
928 void
929 Editor::show_window ()
931 if (! is_visible ()) {
932 show_all ();
934 /* now reset all audio_time_axis heights, because widgets might need
935 to be re-hidden
938 TimeAxisView *tv;
940 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
941 tv = (static_cast<TimeAxisView*>(*i));
942 tv->reset_height ();
945 present ();
948 void
949 Editor::instant_save ()
951 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
952 return;
955 if (session) {
956 session->add_instant_xml(get_state(), session->path());
957 } else {
958 Config->add_instant_xml(get_state(), get_user_ardour_path());
962 void
963 Editor::edit_point_clock_changed()
965 if (_dragging_edit_point) {
966 return;
969 if (selection->markers.empty()) {
970 return;
973 bool ignored;
974 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
976 if (!loc) {
977 return;
980 loc->move_to (edit_point_clock.current_time());
983 void
984 Editor::zoom_adjustment_changed ()
986 if (session == 0) {
987 return;
990 double fpu = zoom_range_clock.current_duration() / canvas_width;
992 if (fpu < 1.0) {
993 fpu = 1.0;
994 zoom_range_clock.set ((nframes64_t) floor (fpu * canvas_width));
995 } else if (fpu > session->current_end_frame() / canvas_width) {
996 fpu = session->current_end_frame() / canvas_width;
997 zoom_range_clock.set ((nframes64_t) floor (fpu * canvas_width));
1000 temporal_zoom (fpu);
1003 void
1004 Editor::control_scroll (float fraction)
1006 ENSURE_GUI_THREAD(bind (mem_fun (*this, &Editor::control_scroll), fraction));
1008 if (!session) {
1009 return;
1012 double step = fraction * current_page_frames();
1015 _control_scroll_target is an optional<T>
1017 it acts like a pointer to an nframes64_t, with
1018 a operator conversion to boolean to check
1019 that it has a value could possibly use
1020 playhead_cursor->current_frame to store the
1021 value and a boolean in the class to know
1022 when it's out of date
1025 if (!_control_scroll_target) {
1026 _control_scroll_target = session->transport_frame();
1027 _dragging_playhead = true;
1030 if ((fraction < 0.0f) && (*_control_scroll_target < (nframes64_t) fabs(step))) {
1031 *_control_scroll_target = 0;
1032 } else if ((fraction > 0.0f) && (max_frames - *_control_scroll_target < step)) {
1033 *_control_scroll_target = max_frames - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
1034 } else {
1035 *_control_scroll_target += (nframes64_t) floor (step);
1038 /* move visuals, we'll catch up with it later */
1040 playhead_cursor->set_position (*_control_scroll_target);
1041 UpdateAllTransportClocks (*_control_scroll_target);
1043 if (*_control_scroll_target > (current_page_frames() / 2)) {
1044 /* try to center PH in window */
1045 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
1046 } else {
1047 reset_x_origin (0);
1051 Now we do a timeout to actually bring the session to the right place
1052 according to the playhead. This is to avoid reading disk buffers on every
1053 call to control_scroll, which is driven by ScrollTimeline and therefore
1054 probably by a control surface wheel which can generate lots of events.
1056 /* cancel the existing timeout */
1058 control_scroll_connection.disconnect ();
1060 /* add the next timeout */
1062 control_scroll_connection = Glib::signal_timeout().connect (bind (mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
1065 bool
1066 Editor::deferred_control_scroll (nframes64_t target)
1068 session->request_locate (*_control_scroll_target, session->transport_rolling());
1069 // reset for next stream
1070 _control_scroll_target = boost::none;
1071 _dragging_playhead = false;
1072 return false;
1075 void
1076 Editor::access_action (std::string action_group, std::string action_item)
1078 if (!session) {
1079 return;
1082 ENSURE_GUI_THREAD (bind (mem_fun (*this, &Editor::access_action), action_group, action_item));
1084 RefPtr<Action> act;
1085 act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
1087 if (act) {
1088 act->activate();
1094 void
1095 Editor::on_realize ()
1097 Window::on_realize ();
1098 Realized ();
1101 void
1102 Editor::start_scrolling ()
1104 scroll_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect
1105 (mem_fun(*this, &Editor::update_current_screen));
1109 void
1110 Editor::stop_scrolling ()
1112 scroll_connection.disconnect ();
1115 void
1116 Editor::map_position_change (nframes64_t frame)
1118 ENSURE_GUI_THREAD (bind (mem_fun(*this, &Editor::map_position_change), frame));
1120 if (session == 0 || !_follow_playhead) {
1121 return;
1124 center_screen (frame);
1125 playhead_cursor->set_position (frame);
1128 void
1129 Editor::center_screen (nframes64_t frame)
1131 double page = canvas_width * frames_per_unit;
1133 /* if we're off the page, then scroll.
1136 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
1137 center_screen_internal (frame, page);
1141 void
1142 Editor::center_screen_internal (nframes64_t frame, float page)
1144 page /= 2;
1146 if (frame > page) {
1147 frame -= (nframes64_t) page;
1148 } else {
1149 frame = 0;
1152 reset_x_origin (frame);
1155 void
1156 Editor::handle_new_duration ()
1158 if (!session) {
1159 return;
1162 ENSURE_GUI_THREAD (mem_fun (*this, &Editor::handle_new_duration));
1164 nframes64_t new_end = session->current_end_frame() + (nframes64_t) floorf (current_page_frames() * 0.10f);
1166 horizontal_adjustment.set_upper (new_end / frames_per_unit);
1167 horizontal_adjustment.set_page_size (current_page_frames()/frames_per_unit);
1169 if (horizontal_adjustment.get_value() + canvas_width > horizontal_adjustment.get_upper()) {
1170 horizontal_adjustment.set_value (horizontal_adjustment.get_upper() - canvas_width);
1172 //cerr << "Editor::handle_new_duration () called ha v:l:u:ps:lcf = " << horizontal_adjustment.get_value() << ":" << horizontal_adjustment.get_lower() << ":" << horizontal_adjustment.get_upper() << ":" << horizontal_adjustment.get_page_size() << ":" << endl;//DEBUG
1175 void
1176 Editor::update_title_s (const string & snap_name)
1178 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::update_title_s), snap_name));
1180 update_title ();
1183 void
1184 Editor::update_title ()
1186 ENSURE_GUI_THREAD (mem_fun(*this, &Editor::update_title));
1188 if (session) {
1189 bool dirty = session->dirty();
1191 string session_name;
1193 if (session->snap_name() != session->name()) {
1194 session_name = session->snap_name();
1195 } else {
1196 session_name = session->name();
1199 if (dirty) {
1200 session_name = "*" + session_name;
1203 WindowTitle title(session_name);
1204 title += Glib::get_application_name();
1205 set_title (title.get_string());
1209 void
1210 Editor::connect_to_session (Session *t)
1212 session = t;
1214 /* there are never any selected regions at startup */
1216 sensitize_the_right_region_actions (false);
1218 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1219 set_state (*node);
1221 /* catch up with the playhead */
1223 session->request_locate (playhead_cursor->current_frame);
1225 update_title ();
1227 session->GoingAway.connect (mem_fun(*this, &Editor::session_going_away));
1228 session->history().Changed.connect (mem_fun (*this, &Editor::history_changed));
1230 /* These signals can all be emitted by a non-GUI thread. Therefore the
1231 handlers for them must not attempt to directly interact with the GUI,
1232 but use Gtkmm2ext::UI::instance()->call_slot();
1235 session_connections.push_back (session->TransportStateChange.connect (mem_fun(*this, &Editor::map_transport_state)));
1236 session_connections.push_back (session->PositionChanged.connect (mem_fun(*this, &Editor::map_position_change)));
1237 session_connections.push_back (session->RouteAdded.connect (mem_fun(*this, &Editor::handle_new_route)));
1238 session_connections.push_back (session->AudioRegionsAdded.connect (mem_fun(*this, &Editor::handle_new_audio_regions)));
1239 session_connections.push_back (session->AudioRegionRemoved.connect (mem_fun(*this, &Editor::handle_audio_region_removed)));
1240 session_connections.push_back (session->DurationChanged.connect (mem_fun(*this, &Editor::handle_new_duration)));
1241 session_connections.push_back (session->edit_group_added.connect (mem_fun(*this, &Editor::add_edit_group)));
1242 session_connections.push_back (session->edit_group_removed.connect (mem_fun(*this, &Editor::edit_groups_changed)));
1243 session_connections.push_back (session->NamedSelectionAdded.connect (mem_fun(*this, &Editor::handle_new_named_selection)));
1244 session_connections.push_back (session->NamedSelectionRemoved.connect (mem_fun(*this, &Editor::handle_new_named_selection)));
1245 session_connections.push_back (session->DirtyChanged.connect (mem_fun(*this, &Editor::update_title)));
1246 session_connections.push_back (session->StateSaved.connect (mem_fun(*this, &Editor::update_title_s)));
1247 session_connections.push_back (session->AskAboutPlaylistDeletion.connect (mem_fun(*this, &Editor::playlist_deletion_dialog)));
1248 session_connections.push_back (session->RegionHiddenChange.connect (mem_fun(*this, &Editor::region_hidden)));
1250 session_connections.push_back (session->SMPTEOffsetChanged.connect (mem_fun(*this, &Editor::update_just_smpte)));
1252 session_connections.push_back (session->tempo_map().StateChanged.connect (mem_fun(*this, &Editor::tempo_map_changed)));
1254 edit_groups_changed ();
1256 edit_point_clock.set_mode(AudioClock::BBT);
1257 edit_point_clock.set_session (session);
1258 zoom_range_clock.set_session (session);
1259 _playlist_selector->set_session (session);
1260 nudge_clock.set_session (session);
1261 if (Profile->get_sae()) {
1262 BBT_Time bbt;
1263 bbt.bars = 0;
1264 bbt.beats = 0;
1265 bbt.ticks = 120;
1266 nframes_t pos = session->tempo_map().bbt_duration_at (0, bbt, 1);
1267 nudge_clock.set_mode(AudioClock::BBT);
1268 nudge_clock.set (pos, true, 0, AudioClock::BBT);
1270 } else {
1271 nudge_clock.set (session->frame_rate() * 5, true, 0, AudioClock::SMPTE); // default of 5 seconds
1274 playhead_cursor->canvas_item.show ();
1276 if (rhythm_ferret) {
1277 rhythm_ferret->set_session (session);
1280 #ifdef FFT_ANALYSIS
1281 if (analysis_window != 0)
1282 analysis_window->set_session (session);
1283 #endif
1285 Location* loc = session->locations()->auto_loop_location();
1286 if (loc == 0) {
1287 loc = new Location (0, session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1288 if (loc->start() == loc->end()) {
1289 loc->set_end (loc->start() + 1);
1291 session->locations()->add (loc, false);
1292 session->set_auto_loop_location (loc);
1293 } else {
1294 // force name
1295 loc->set_name (_("Loop"));
1298 loc = session->locations()->auto_punch_location();
1299 if (loc == 0) {
1300 loc = new Location (0, session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1301 if (loc->start() == loc->end()) {
1302 loc->set_end (loc->start() + 1);
1304 session->locations()->add (loc, false);
1305 session->set_auto_punch_location (loc);
1306 } else {
1307 // force name
1308 loc->set_name (_("Punch"));
1311 Config->map_parameters (mem_fun (*this, &Editor::parameter_changed));
1313 session->StateSaved.connect (mem_fun(*this, &Editor::session_state_saved));
1315 refresh_location_display ();
1316 session->locations()->added.connect (mem_fun(*this, &Editor::add_new_location));
1317 session->locations()->removed.connect (mem_fun(*this, &Editor::location_gone));
1318 session->locations()->changed.connect (mem_fun(*this, &Editor::refresh_location_display));
1319 session->locations()->StateChanged.connect (mem_fun(*this, &Editor::refresh_location_display_s));
1320 session->locations()->end_location()->changed.connect (mem_fun(*this, &Editor::end_location_changed));
1322 if (sfbrowser) {
1323 sfbrowser->set_session (session);
1326 handle_new_duration ();
1328 redisplay_regions ();
1329 redisplay_named_selections ();
1330 redisplay_snapshots ();
1332 restore_ruler_visibility ();
1333 //tempo_map_changed (Change (0));
1334 session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1336 initial_route_list_display ();
1338 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1339 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1342 start_scrolling ();
1344 /* don't show master bus in a new session */
1346 if (ARDOUR_UI::instance()->session_is_new ()) {
1348 TreeModel::Children rows = route_display_model->children();
1349 TreeModel::Children::iterator i;
1351 no_route_list_redisplay = true;
1353 for (i = rows.begin(); i != rows.end(); ++i) {
1354 TimeAxisView *tv = (*i)[route_display_columns.tv];
1355 AudioTimeAxisView *atv;
1357 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
1358 if (atv->route()->master()) {
1359 route_list_display.get_selection()->unselect (i);
1364 no_route_list_redisplay = false;
1365 redisplay_route_list ();
1368 switch (snap_type) {
1369 case SnapToRegionStart:
1370 case SnapToRegionEnd:
1371 case SnapToRegionSync:
1372 case SnapToRegionBoundary:
1373 build_region_boundary_cache ();
1374 break;
1376 default:
1377 break;
1380 /* register for undo history */
1382 session->register_with_memento_command_factory(_id, this);
1384 start_updating ();
1387 void
1388 Editor::build_cursors ()
1390 using namespace Gdk;
1392 Gdk::Color mbg ("#000000" ); /* Black */
1393 Gdk::Color mfg ("#0000ff" ); /* Blue. */
1396 RefPtr<Bitmap> source, mask;
1397 source = Bitmap::create (mag_bits, mag_width, mag_height);
1398 mask = Bitmap::create (magmask_bits, mag_width, mag_height);
1399 zoom_cursor = new Gdk::Cursor (source, mask, mfg, mbg, mag_x_hot, mag_y_hot);
1402 Gdk::Color fbg ("#ffffff" );
1403 Gdk::Color ffg ("#000000" );
1406 RefPtr<Bitmap> source, mask;
1408 source = Bitmap::create (fader_cursor_bits, fader_cursor_width, fader_cursor_height);
1409 mask = Bitmap::create (fader_cursor_mask_bits, fader_cursor_width, fader_cursor_height);
1410 fader_cursor = new Gdk::Cursor (source, mask, ffg, fbg, fader_cursor_x_hot, fader_cursor_y_hot);
1414 RefPtr<Bitmap> source, mask;
1415 source = Bitmap::create (speaker_cursor_bits, speaker_cursor_width, speaker_cursor_height);
1416 mask = Bitmap::create (speaker_cursor_mask_bits, speaker_cursor_width, speaker_cursor_height);
1417 speaker_cursor = new Gdk::Cursor (source, mask, ffg, fbg, speaker_cursor_x_hot, speaker_cursor_y_hot);
1421 RefPtr<Bitmap> bits;
1422 char pix[4] = { 0, 0, 0, 0 };
1423 bits = Bitmap::create (pix, 2, 2);
1424 Gdk::Color c;
1425 transparent_cursor = new Gdk::Cursor (bits, bits, c, c, 0, 0);
1429 grabber_cursor = new Gdk::Cursor (HAND2);
1432 Glib::RefPtr<Gdk::Pixbuf> grabber_edit_point_pixbuf (::get_icon ("grabber_edit_point"));
1433 grabber_edit_point_cursor = new Gdk::Cursor (Gdk::Display::get_default(), grabber_edit_point_pixbuf, 5, 17);
1436 cross_hair_cursor = new Gdk::Cursor (CROSSHAIR);
1437 trimmer_cursor = new Gdk::Cursor (SB_H_DOUBLE_ARROW);
1438 selector_cursor = new Gdk::Cursor (XTERM);
1439 time_fx_cursor = new Gdk::Cursor (SIZING);
1440 wait_cursor = new Gdk::Cursor (WATCH);
1441 timebar_cursor = new Gdk::Cursor(LEFT_PTR);
1444 void
1445 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1447 using namespace Menu_Helpers;
1448 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1450 if (arv == 0) {
1451 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1452 /*NOTREACHED*/
1455 MenuList& items (fade_context_menu.items());
1457 items.clear ();
1459 switch (item_type) {
1460 case FadeInItem:
1461 case FadeInHandleItem:
1462 if (arv->audio_region()->fade_in_active()) {
1463 items.push_back (MenuElem (_("Deactivate"), bind (mem_fun (*this, &Editor::set_fade_in_active), false)));
1464 } else {
1465 items.push_back (MenuElem (_("Activate"), bind (mem_fun (*this, &Editor::set_fade_in_active), true)));
1468 items.push_back (SeparatorElem());
1470 if (Profile->get_sae()) {
1471 items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear)));
1472 items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast)));
1473 } else {
1474 items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear)));
1475 items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast)));
1476 items.push_back (MenuElem (_("Slow"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogB)));
1477 items.push_back (MenuElem (_("Fast"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogA)));
1478 items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Slow)));
1480 break;
1482 case FadeOutItem:
1483 case FadeOutHandleItem:
1484 if (arv->audio_region()->fade_out_active()) {
1485 items.push_back (MenuElem (_("Deactivate"), bind (mem_fun (*this, &Editor::set_fade_out_active), false)));
1486 } else {
1487 items.push_back (MenuElem (_("Activate"), bind (mem_fun (*this, &Editor::set_fade_out_active), true)));
1490 items.push_back (SeparatorElem());
1492 if (Profile->get_sae()) {
1493 items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear)));
1494 items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow)));
1495 } else {
1496 items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear)));
1497 items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow)));
1498 items.push_back (MenuElem (_("Slow"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogA)));
1499 items.push_back (MenuElem (_("Fast"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogB)));
1500 items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Fast)));
1502 break;
1504 default:
1505 fatal << _("programming error: ")
1506 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1507 << endmsg;
1508 /*NOTREACHED*/
1511 fade_context_menu.popup (button, time);
1514 void
1515 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection, nframes64_t frame)
1517 using namespace Menu_Helpers;
1518 Menu* (Editor::*build_menu_function)(nframes64_t);
1519 Menu *menu;
1521 switch (item_type) {
1522 case RegionItem:
1523 case RegionViewName:
1524 case RegionViewNameHighlight:
1525 if (with_selection) {
1526 build_menu_function = &Editor::build_track_selection_context_menu;
1527 } else {
1528 build_menu_function = &Editor::build_track_region_context_menu;
1530 break;
1532 case SelectionItem:
1533 if (with_selection) {
1534 build_menu_function = &Editor::build_track_selection_context_menu;
1535 } else {
1536 build_menu_function = &Editor::build_track_context_menu;
1538 break;
1540 case CrossfadeViewItem:
1541 build_menu_function = &Editor::build_track_crossfade_context_menu;
1542 break;
1544 case StreamItem:
1545 if (clicked_audio_trackview->get_diskstream()) {
1546 build_menu_function = &Editor::build_track_context_menu;
1547 } else {
1548 build_menu_function = &Editor::build_track_bus_context_menu;
1550 break;
1552 default:
1553 /* probably shouldn't happen but if it does, we don't care */
1554 return;
1557 menu = (this->*build_menu_function)(frame);
1558 menu->set_name ("ArdourContextMenu");
1560 /* now handle specific situations */
1562 switch (item_type) {
1563 case RegionItem:
1564 case RegionViewName:
1565 case RegionViewNameHighlight:
1566 if (!with_selection) {
1567 if (region_edit_menu_split_item) {
1568 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1569 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1570 } else {
1571 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1575 if (region_edit_menu_split_multichannel_item) {
1576 if (clicked_regionview && clicked_regionview->region().n_channels() > 1) {
1577 // GTK2FIX find the action, change its sensitivity
1578 // region_edit_menu_split_multichannel_item->set_sensitive (true);
1579 } else {
1580 // GTK2FIX see above
1581 // region_edit_menu_split_multichannel_item->set_sensitive (false);
1585 break;
1587 case SelectionItem:
1588 break;
1590 case CrossfadeViewItem:
1591 break;
1593 case StreamItem:
1594 break;
1596 default:
1597 /* probably shouldn't happen but if it does, we don't care */
1598 return;
1601 if (item_type != SelectionItem && clicked_audio_trackview && clicked_audio_trackview->audio_track()) {
1603 /* Bounce to disk */
1605 using namespace Menu_Helpers;
1606 MenuList& edit_items = menu->items();
1608 edit_items.push_back (SeparatorElem());
1610 switch (clicked_audio_trackview->audio_track()->freeze_state()) {
1611 case AudioTrack::NoFreeze:
1612 edit_items.push_back (MenuElem (_("Freeze"), mem_fun(*this, &Editor::freeze_route)));
1613 break;
1615 case AudioTrack::Frozen:
1616 edit_items.push_back (MenuElem (_("Unfreeze"), mem_fun(*this, &Editor::unfreeze_route)));
1617 break;
1619 case AudioTrack::UnFrozen:
1620 edit_items.push_back (MenuElem (_("Freeze"), mem_fun(*this, &Editor::freeze_route)));
1621 break;
1626 menu->popup (button, time);
1629 Menu*
1630 Editor::build_track_context_menu (nframes64_t ignored)
1632 using namespace Menu_Helpers;
1634 MenuList& edit_items = track_context_menu.items();
1635 edit_items.clear();
1637 add_dstream_context_items (edit_items);
1638 return &track_context_menu;
1641 Menu*
1642 Editor::build_track_bus_context_menu (nframes64_t ignored)
1644 using namespace Menu_Helpers;
1646 MenuList& edit_items = track_context_menu.items();
1647 edit_items.clear();
1649 add_bus_context_items (edit_items);
1650 return &track_context_menu;
1653 Menu*
1654 Editor::build_track_region_context_menu (nframes64_t frame)
1656 using namespace Menu_Helpers;
1657 MenuList& edit_items = track_region_context_menu.items();
1658 edit_items.clear();
1660 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_trackview);
1662 if (atv) {
1663 boost::shared_ptr<Diskstream> ds;
1664 boost::shared_ptr<Playlist> pl;
1666 if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()))) {
1668 nframes64_t frame_pos = (nframes64_t) floor ((double)frame * ds->speed());
1669 uint32_t regions_at = pl->count_regions_at (frame_pos);
1671 if (selection->regions.size() > 1) {
1672 // there's already a multiple selection: just add a
1673 // single region context menu that will act on all
1674 // selected regions
1675 boost::shared_ptr<Region> dummy_region; // = NULL
1676 add_region_context_items (atv->audio_view(), dummy_region, edit_items, frame_pos, regions_at > 1);
1677 } else {
1678 // Find the topmost region and make the context menu for it
1679 boost::shared_ptr<Region> top_region = pl->top_region_at (frame_pos);
1680 add_region_context_items (atv->audio_view(), top_region, edit_items, frame_pos, regions_at > 1);
1685 add_dstream_context_items (edit_items);
1687 return &track_region_context_menu;
1690 Menu*
1691 Editor::build_track_crossfade_context_menu (nframes64_t frame)
1693 using namespace Menu_Helpers;
1694 MenuList& edit_items = track_crossfade_context_menu.items();
1695 edit_items.clear ();
1697 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_trackview);
1699 if (atv) {
1700 boost::shared_ptr<Diskstream> ds;
1701 boost::shared_ptr<Playlist> pl;
1702 boost::shared_ptr<AudioPlaylist> apl;
1704 if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1706 AudioPlaylist::Crossfades xfades;
1708 apl->crossfades_at (frame, xfades);
1710 bool many = xfades.size() > 1;
1712 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1713 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1716 nframes64_t frame_pos = (nframes64_t) floor ((double)frame * ds->speed());
1717 uint32_t regions_at = pl->count_regions_at (frame_pos);
1719 if (selection->regions.size() > 1) {
1720 // there's already a multiple selection: just add a
1721 // single region context menu that will act on all
1722 // selected regions
1723 boost::shared_ptr<Region> dummy_region; // = NULL
1724 add_region_context_items (atv->audio_view(), dummy_region, edit_items, frame_pos, regions_at > 1); // OR frame ???
1725 } else {
1726 // Find the topmost region and make the context menu for it
1727 boost::shared_ptr<Region> top_region = pl->top_region_at (frame_pos);
1728 add_region_context_items (atv->audio_view(), top_region, edit_items, frame_pos, regions_at > 1); // OR frame ???
1733 add_dstream_context_items (edit_items);
1735 return &track_crossfade_context_menu;
1738 #ifdef FFT_ANALYSIS
1739 void
1740 Editor::analyze_region_selection()
1742 if (analysis_window == 0) {
1743 analysis_window = new AnalysisWindow();
1745 if (session != 0)
1746 analysis_window->set_session(session);
1748 analysis_window->show_all();
1751 analysis_window->set_regionmode();
1752 analysis_window->analyze();
1754 analysis_window->present();
1757 void
1758 Editor::analyze_range_selection()
1760 if (analysis_window == 0) {
1761 analysis_window = new AnalysisWindow();
1763 if (session != 0)
1764 analysis_window->set_session(session);
1766 analysis_window->show_all();
1769 analysis_window->set_rangemode();
1770 analysis_window->analyze();
1772 analysis_window->present();
1774 #endif /* FFT_ANALYSIS */
1778 Menu*
1779 Editor::build_track_selection_context_menu (nframes64_t ignored)
1781 using namespace Menu_Helpers;
1782 MenuList& edit_items = track_selection_context_menu.items();
1783 edit_items.clear ();
1785 add_selection_context_items (edit_items);
1786 // edit_items.push_back (SeparatorElem());
1787 // add_dstream_context_items (edit_items);
1789 return &track_selection_context_menu;
1792 void
1793 Editor::add_crossfade_context_items (AudioStreamView* view, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1795 using namespace Menu_Helpers;
1796 Menu *xfade_menu = manage (new Menu);
1797 MenuList& items = xfade_menu->items();
1798 xfade_menu->set_name ("ArdourContextMenu");
1799 string str;
1801 if (xfade->active()) {
1802 str = _("Mute");
1803 } else {
1804 str = _("Unmute");
1807 items.push_back (MenuElem (str, bind (mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1808 items.push_back (MenuElem (_("Edit"), bind (mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1810 if (xfade->can_follow_overlap()) {
1812 if (xfade->following_overlap()) {
1813 str = _("Convert to short");
1814 } else {
1815 str = _("Convert to full");
1818 items.push_back (MenuElem (str, bind (mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1821 if (many) {
1822 str = xfade->out()->name();
1823 str += "->";
1824 str += xfade->in()->name();
1825 } else {
1826 str = _("Crossfade");
1829 edit_items.push_back (MenuElem (str, *xfade_menu));
1830 edit_items.push_back (SeparatorElem());
1833 void
1834 Editor::xfade_edit_left_region ()
1836 if (clicked_crossfadeview) {
1837 clicked_crossfadeview->left_view.show_region_editor ();
1841 void
1842 Editor::xfade_edit_right_region ()
1844 if (clicked_crossfadeview) {
1845 clicked_crossfadeview->right_view.show_region_editor ();
1849 void
1850 Editor::add_region_context_items (AudioStreamView* sv, boost::shared_ptr<Region> region, Menu_Helpers::MenuList& edit_items,
1851 nframes64_t position, bool multiple_region_at_position)
1853 using namespace Menu_Helpers;
1854 Gtk::MenuItem* foo_item;
1855 Menu *region_menu = manage (new Menu);
1856 MenuList& items = region_menu->items();
1857 region_menu->set_name ("ArdourContextMenu");
1859 boost::shared_ptr<AudioRegion> ar;
1861 if (region) {
1862 ar = boost::dynamic_pointer_cast<AudioRegion> (region);
1864 /* when this particular menu pops up, make the relevant region
1865 become selected.
1868 region_menu->signal_map_event().connect (
1869 bind (
1870 mem_fun(*this, &Editor::set_selected_regionview_from_map_event),
1871 sv,
1872 boost::weak_ptr<Region>(region)
1876 items.push_back (MenuElem (_("Rename"), mem_fun(*this, &Editor::rename_region)));
1877 items.push_back (MenuElem (_("Region Editor"), mem_fun(*this, &Editor::edit_region)));
1880 items.push_back (MenuElem (_("Raise to top layer"), mem_fun(*this, &Editor::raise_region_to_top)));
1881 items.push_back (MenuElem (_("Lower to bottom layer"), mem_fun (*this, &Editor::lower_region_to_bottom)));
1882 items.push_back (SeparatorElem());
1883 items.push_back (MenuElem (_("Define sync point"), mem_fun(*this, &Editor::set_region_sync_from_edit_point)));
1884 if (_edit_point == EditAtMouse) {
1885 items.back ().set_sensitive (false);
1887 items.push_back (MenuElem (_("Remove sync point"), mem_fun(*this, &Editor::remove_region_sync)));
1888 items.push_back (SeparatorElem());
1890 items.push_back (MenuElem (_("Audition"), mem_fun(*this, &Editor::play_selected_region)));
1891 items.push_back (MenuElem (_("Export"), mem_fun(*this, &Editor::export_region)));
1892 items.push_back (MenuElem (_("Bounce"), mem_fun(*this, &Editor::bounce_region_selection)));
1894 #ifdef FFT_ANALYSIS
1895 items.push_back (MenuElem (_("Spectral Analysis"), mem_fun(*this, &Editor::analyze_region_selection)));
1896 #endif
1898 items.push_back (SeparatorElem());
1900 sigc::connection fooc;
1901 boost::shared_ptr<Region> region_to_check;
1903 if (region) {
1904 region_to_check = region;
1905 } else {
1906 region_to_check = selection->regions.front()->region();
1909 items.push_back (CheckMenuElem (_("Lock")));
1910 CheckMenuItem* region_lock_item = static_cast<CheckMenuItem*>(&items.back());
1911 if (region_to_check->locked()) {
1912 region_lock_item->set_active();
1914 region_lock_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_lock));
1916 items.push_back (CheckMenuElem (_("Glue to Bars&Beats")));
1917 CheckMenuItem* bbt_glue_item = static_cast<CheckMenuItem*>(&items.back());
1919 switch (region_to_check->positional_lock_style()) {
1920 case Region::MusicTime:
1921 bbt_glue_item->set_active (true);
1922 break;
1923 default:
1924 bbt_glue_item->set_active (false);
1925 break;
1928 bbt_glue_item->signal_activate().connect (bind (mem_fun (*this, &Editor::set_region_lock_style), Region::MusicTime));
1930 items.push_back (CheckMenuElem (_("Mute")));
1931 CheckMenuItem* region_mute_item = static_cast<CheckMenuItem*>(&items.back());
1932 fooc = region_mute_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_mute));
1933 if (region_to_check->muted()) {
1934 fooc.block (true);
1935 region_mute_item->set_active();
1936 fooc.block (false);
1939 items.push_back (MenuElem (_("Transpose"), mem_fun(*this, &Editor::pitch_shift_regions)));
1941 if (!Profile->get_sae()) {
1942 items.push_back (CheckMenuElem (_("Opaque")));
1943 CheckMenuItem* region_opaque_item = static_cast<CheckMenuItem*>(&items.back());
1944 fooc = region_opaque_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_opaque));
1945 if (region_to_check->opaque()) {
1946 fooc.block (true);
1947 region_opaque_item->set_active();
1948 fooc.block (false);
1952 items.push_back (CheckMenuElem (_("Original position"), mem_fun(*this, &Editor::naturalize)));
1953 if (region_to_check->at_natural_position()) {
1954 items.back().set_sensitive (false);
1957 items.push_back (SeparatorElem());
1959 if (ar) {
1961 RegionView* rv = sv->find_view (ar);
1962 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1964 if (!Profile->get_sae()) {
1965 items.push_back (MenuElem (_("Reset Envelope"), mem_fun(*this, &Editor::reset_region_gain_envelopes)));
1967 items.push_back (CheckMenuElem (_("Envelope Visible")));
1968 CheckMenuItem* region_envelope_visible_item = static_cast<CheckMenuItem*> (&items.back());
1969 fooc = region_envelope_visible_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_gain_envelope_visibility));
1970 if (arv->envelope_visible()) {
1971 fooc.block (true);
1972 region_envelope_visible_item->set_active (true);
1973 fooc.block (false);
1976 items.push_back (CheckMenuElem (_("Envelope Active")));
1977 CheckMenuItem* region_envelope_active_item = static_cast<CheckMenuItem*> (&items.back());
1978 fooc = region_envelope_active_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_gain_envelope_active));
1980 if (ar->envelope_active()) {
1981 fooc.block (true);
1982 region_envelope_active_item->set_active (true);
1983 fooc.block (false);
1986 items.push_back (SeparatorElem());
1989 if (ar->scale_amplitude() != 1.0f) {
1990 items.push_back (MenuElem (_("DeNormalize"), mem_fun(*this, &Editor::denormalize_region)));
1991 } else {
1992 items.push_back (MenuElem (_("Normalize"), mem_fun(*this, &Editor::normalize_region)));
1996 items.push_back (MenuElem (_("Reverse"), mem_fun(*this, &Editor::reverse_region)));
1997 items.push_back (SeparatorElem());
1999 /* range related stuff */
2001 items.push_back (MenuElem (_("Add Single Range"), mem_fun (*this, &Editor::add_location_from_audio_region)));
2002 items.push_back (MenuElem (_("Add Range Markers"), mem_fun (*this, &Editor::add_locations_from_audio_region)));
2003 if (selection->regions.size() < 2) {
2004 items.back().set_sensitive (false);
2007 items.push_back (MenuElem (_("Set Range Selection"), mem_fun (*this, &Editor::set_selection_from_audio_region)));
2008 items.push_back (SeparatorElem());
2010 /* Nudge region */
2012 Menu *nudge_menu = manage (new Menu());
2013 MenuList& nudge_items = nudge_menu->items();
2014 nudge_menu->set_name ("ArdourContextMenu");
2016 nudge_items.push_back (MenuElem (_("Nudge fwd"), (bind (mem_fun(*this, &Editor::nudge_forward), false, false))));
2017 nudge_items.push_back (MenuElem (_("Nudge bwd"), (bind (mem_fun(*this, &Editor::nudge_backward), false, false))));
2018 nudge_items.push_back (MenuElem (_("Nudge fwd by capture offset"), (mem_fun(*this, &Editor::nudge_forward_capture_offset))));
2019 nudge_items.push_back (MenuElem (_("Nudge bwd by capture offset"), (mem_fun(*this, &Editor::nudge_backward_capture_offset))));
2021 items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2022 items.push_back (SeparatorElem());
2024 Menu *trim_menu = manage (new Menu);
2025 MenuList& trim_items = trim_menu->items();
2026 trim_menu->set_name ("ArdourContextMenu");
2028 trim_items.push_back (MenuElem (_("Start to edit point"), mem_fun(*this, &Editor::trim_region_from_edit_point)));
2029 foo_item = &trim_items.back();
2030 if (_edit_point == EditAtMouse) {
2031 foo_item->set_sensitive (false);
2033 trim_items.push_back (MenuElem (_("Edit point to end"), mem_fun(*this, &Editor::trim_region_to_edit_point)));
2034 foo_item = &trim_items.back();
2035 if (_edit_point == EditAtMouse) {
2036 foo_item->set_sensitive (false);
2038 trim_items.push_back (MenuElem (_("Trim To Loop"), mem_fun(*this, &Editor::trim_region_to_loop)));
2039 trim_items.push_back (MenuElem (_("Trim To Punch"), mem_fun(*this, &Editor::trim_region_to_punch)));
2041 items.push_back (MenuElem (_("Trim"), *trim_menu));
2042 items.push_back (SeparatorElem());
2044 items.push_back (MenuElem (_("Split"), (mem_fun(*this, &Editor::split_region))));
2045 region_edit_menu_split_item = &items.back();
2047 if (_edit_point == EditAtMouse) {
2048 region_edit_menu_split_item->set_sensitive (false);
2051 items.push_back (MenuElem (_("Make mono regions"), (mem_fun(*this, &Editor::split_multichannel_region))));
2052 region_edit_menu_split_multichannel_item = &items.back();
2054 items.push_back (MenuElem (_("Duplicate"), (bind (mem_fun(*this, &Editor::duplicate_dialog), false))));
2055 items.push_back (MenuElem (_("Multi-Duplicate"), (bind (mem_fun(*this, &Editor::duplicate_dialog), true))));
2056 items.push_back (MenuElem (_("Fill Track"), (mem_fun(*this, &Editor::region_fill_track))));
2057 items.push_back (SeparatorElem());
2058 items.push_back (MenuElem (_("Remove"), mem_fun(*this, &Editor::remove_region)));
2060 /* OK, stick the region submenu at the top of the list, and then add
2061 the standard items.
2064 /* we have to hack up the region name because "_" has a special
2065 meaning for menu titles.
2068 string::size_type pos = 0;
2069 string menu_item_name = (region) ? region->name() : _("Selected regions");
2071 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
2072 menu_item_name.replace (pos, 1, "__");
2073 pos += 2;
2076 edit_items.push_back (MenuElem (menu_item_name, *region_menu));
2077 if (multiple_region_at_position && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
2078 edit_items.push_back (MenuElem (_("Choose top region"), (bind (mem_fun(*this, &Editor::change_region_layering_order), position))));
2080 edit_items.push_back (SeparatorElem());
2083 void
2084 Editor::add_selection_context_items (Menu_Helpers::MenuList& items)
2086 using namespace Menu_Helpers;
2088 items.push_back (MenuElem (_("Play range"), mem_fun(*this, &Editor::play_selection)));
2089 items.push_back (MenuElem (_("Loop range"), bind (mem_fun(*this, &Editor::set_loop_from_selection), true)));
2091 #ifdef FFT_ANALYSIS
2092 items.push_back (SeparatorElem());
2093 items.push_back (MenuElem (_("Spectral Analysis"), mem_fun(*this, &Editor::analyze_range_selection)));
2094 #endif
2096 items.push_back (SeparatorElem());
2097 items.push_back (MenuElem (_("Extend Range to End of Region"), bind (mem_fun(*this, &Editor::extend_selection_to_end_of_region), false)));
2098 items.push_back (MenuElem (_("Extend Range to Start of Region"), bind (mem_fun(*this, &Editor::extend_selection_to_start_of_region), false)));
2100 items.push_back (SeparatorElem());
2101 items.push_back (MenuElem (_("Convert to region in-place"), mem_fun(*this, &Editor::separate_region_from_selection)));
2102 items.push_back (MenuElem (_("Convert to region in region list"), mem_fun(*this, &Editor::new_region_from_selection)));
2104 items.push_back (SeparatorElem());
2105 items.push_back (MenuElem (_("Select all in range"), mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
2107 items.push_back (SeparatorElem());
2108 items.push_back (MenuElem (_("Set loop from selection"), bind (mem_fun(*this, &Editor::set_loop_from_selection), false)));
2109 items.push_back (MenuElem (_("Set punch from selection"), mem_fun(*this, &Editor::set_punch_from_selection)));
2111 items.push_back (SeparatorElem());
2112 items.push_back (MenuElem (_("Add Range Markers"), mem_fun (*this, &Editor::add_location_from_selection)));
2113 items.push_back (SeparatorElem());
2114 items.push_back (MenuElem (_("Crop region to range"), mem_fun(*this, &Editor::crop_region_to_selection)));
2115 items.push_back (MenuElem (_("Fill range with region"), mem_fun(*this, &Editor::region_fill_selection)));
2116 items.push_back (MenuElem (_("Duplicate range"), bind (mem_fun(*this, &Editor::duplicate_dialog), false)));
2117 items.push_back (MenuElem (_("Create chunk from range"), mem_fun(*this, &Editor::create_named_selection)));
2118 items.push_back (SeparatorElem());
2119 items.push_back (MenuElem (_("Consolidate range"), bind (mem_fun(*this, &Editor::bounce_range_selection), true, false)));
2120 items.push_back (MenuElem (_("Consolidate range with processing"), bind (mem_fun(*this, &Editor::bounce_range_selection), true, true)));
2121 items.push_back (MenuElem (_("Bounce range to region list"), bind (mem_fun(*this, &Editor::bounce_range_selection), false, false)));
2122 items.push_back (MenuElem (_("Bounce range to region list with processing"), bind (mem_fun(*this, &Editor::bounce_range_selection), false, true)));
2123 items.push_back (MenuElem (_("Export range"), mem_fun(*this, &Editor::export_selection)));
2126 void
2127 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
2129 using namespace Menu_Helpers;
2131 /* Playback */
2133 Menu *play_menu = manage (new Menu);
2134 MenuList& play_items = play_menu->items();
2135 play_menu->set_name ("ArdourContextMenu");
2137 play_items.push_back (MenuElem (_("Play from edit point"), mem_fun(*this, &Editor::play_from_edit_point)));
2138 play_items.push_back (MenuElem (_("Play from start"), mem_fun(*this, &Editor::play_from_start)));
2139 play_items.push_back (MenuElem (_("Play region"), mem_fun(*this, &Editor::play_selected_region)));
2140 play_items.push_back (SeparatorElem());
2141 play_items.push_back (MenuElem (_("Loop Region"), mem_fun(*this, &Editor::loop_selected_region)));
2143 edit_items.push_back (MenuElem (_("Play"), *play_menu));
2145 /* Selection */
2147 Menu *select_menu = manage (new Menu);
2148 MenuList& select_items = select_menu->items();
2149 select_menu->set_name ("ArdourContextMenu");
2151 select_items.push_back (MenuElem (_("Select All in track"), bind (mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2152 select_items.push_back (MenuElem (_("Select All"), bind (mem_fun(*this, &Editor::select_all), Selection::Set)));
2153 select_items.push_back (MenuElem (_("Invert selection in track"), mem_fun(*this, &Editor::invert_selection_in_track)));
2154 select_items.push_back (MenuElem (_("Invert selection"), mem_fun(*this, &Editor::invert_selection)));
2155 select_items.push_back (SeparatorElem());
2156 select_items.push_back (MenuElem (_("Set range to loop range"), mem_fun(*this, &Editor::set_selection_from_loop)));
2157 select_items.push_back (MenuElem (_("Set range to punch range"), mem_fun(*this, &Editor::set_selection_from_punch)));
2158 select_items.push_back (SeparatorElem());
2159 select_items.push_back (MenuElem (_("Select All After Edit Point"), bind (mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
2160 select_items.push_back (MenuElem (_("Select All Before Edit Point"), bind (mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
2161 select_items.push_back (MenuElem (_("Select All After Playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2162 select_items.push_back (MenuElem (_("Select All Before Playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2163 select_items.push_back (MenuElem (_("Select All Between Playhead & Edit Point"), bind (mem_fun(*this, &Editor::select_all_selectables_between), false)));
2164 select_items.push_back (MenuElem (_("Select All Within Playhead & Edit Point"), bind (mem_fun(*this, &Editor::select_all_selectables_between), true)));
2165 select_items.push_back (MenuElem (_("Select Range Between Playhead & Edit Point"), mem_fun(*this, &Editor::select_range_between)));
2167 select_items.push_back (SeparatorElem());
2169 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2171 /* Cut-n-Paste */
2173 Menu *cutnpaste_menu = manage (new Menu);
2174 MenuList& cutnpaste_items = cutnpaste_menu->items();
2175 cutnpaste_menu->set_name ("ArdourContextMenu");
2177 cutnpaste_items.push_back (MenuElem (_("Cut"), mem_fun(*this, &Editor::cut)));
2178 cutnpaste_items.push_back (MenuElem (_("Copy"), mem_fun(*this, &Editor::copy)));
2179 cutnpaste_items.push_back (MenuElem (_("Paste"), bind (mem_fun(*this, &Editor::paste), 1.0f)));
2181 cutnpaste_items.push_back (SeparatorElem());
2183 cutnpaste_items.push_back (MenuElem (_("Align"), bind (mem_fun(*this, &Editor::align), ARDOUR::SyncPoint)));
2184 cutnpaste_items.push_back (MenuElem (_("Align Relative"), bind (mem_fun(*this, &Editor::align_relative), ARDOUR::SyncPoint)));
2186 cutnpaste_items.push_back (SeparatorElem());
2188 cutnpaste_items.push_back (MenuElem (_("Insert chunk"), bind (mem_fun(*this, &Editor::paste_named_selection), 1.0f)));
2190 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
2192 /* Adding new material */
2194 edit_items.push_back (SeparatorElem());
2195 edit_items.push_back (MenuElem (_("Insert Selected Region"), bind (mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
2196 edit_items.push_back (MenuElem (_("Insert Existing Audio"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
2198 /* Nudge track */
2200 Menu *nudge_menu = manage (new Menu());
2201 MenuList& nudge_items = nudge_menu->items();
2202 nudge_menu->set_name ("ArdourContextMenu");
2204 edit_items.push_back (SeparatorElem());
2205 nudge_items.push_back (MenuElem (_("Nudge entire track fwd"), (bind (mem_fun(*this, &Editor::nudge_track), false, true))));
2206 nudge_items.push_back (MenuElem (_("Nudge track after edit point fwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, true))));
2207 nudge_items.push_back (MenuElem (_("Nudge entire track bwd"), (bind (mem_fun(*this, &Editor::nudge_track), false, false))));
2208 nudge_items.push_back (MenuElem (_("Nudge track after edit point bwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, false))));
2210 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2213 void
2214 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
2216 using namespace Menu_Helpers;
2218 /* Playback */
2220 Menu *play_menu = manage (new Menu);
2221 MenuList& play_items = play_menu->items();
2222 play_menu->set_name ("ArdourContextMenu");
2224 play_items.push_back (MenuElem (_("Play from edit point"), mem_fun(*this, &Editor::play_from_edit_point)));
2225 play_items.push_back (MenuElem (_("Play from start"), mem_fun(*this, &Editor::play_from_start)));
2226 edit_items.push_back (MenuElem (_("Play"), *play_menu));
2228 /* Selection */
2230 Menu *select_menu = manage (new Menu);
2231 MenuList& select_items = select_menu->items();
2232 select_menu->set_name ("ArdourContextMenu");
2234 select_items.push_back (MenuElem (_("Select All in track"), bind (mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2235 select_items.push_back (MenuElem (_("Select All"), bind (mem_fun(*this, &Editor::select_all), Selection::Set)));
2236 select_items.push_back (MenuElem (_("Invert selection in track"), mem_fun(*this, &Editor::invert_selection_in_track)));
2237 select_items.push_back (MenuElem (_("Invert selection"), mem_fun(*this, &Editor::invert_selection)));
2238 select_items.push_back (SeparatorElem());
2239 select_items.push_back (MenuElem (_("Select all after edit point"), bind (mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
2240 select_items.push_back (MenuElem (_("Select all before edit point"), bind (mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
2241 select_items.push_back (MenuElem (_("Select all after playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2242 select_items.push_back (MenuElem (_("Select all before playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2244 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2246 /* Cut-n-Paste */
2248 Menu *cutnpaste_menu = manage (new Menu);
2249 MenuList& cutnpaste_items = cutnpaste_menu->items();
2250 cutnpaste_menu->set_name ("ArdourContextMenu");
2252 cutnpaste_items.push_back (MenuElem (_("Cut"), mem_fun(*this, &Editor::cut)));
2253 cutnpaste_items.push_back (MenuElem (_("Copy"), mem_fun(*this, &Editor::copy)));
2254 cutnpaste_items.push_back (MenuElem (_("Paste"), bind (mem_fun(*this, &Editor::paste), 1.0f)));
2256 Menu *nudge_menu = manage (new Menu());
2257 MenuList& nudge_items = nudge_menu->items();
2258 nudge_menu->set_name ("ArdourContextMenu");
2260 edit_items.push_back (SeparatorElem());
2261 nudge_items.push_back (MenuElem (_("Nudge entire track fwd"), (bind (mem_fun(*this, &Editor::nudge_track), false, true))));
2262 nudge_items.push_back (MenuElem (_("Nudge track after edit point fwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, true))));
2263 nudge_items.push_back (MenuElem (_("Nudge entire track bwd"), (bind (mem_fun(*this, &Editor::nudge_track), false, false))));
2264 nudge_items.push_back (MenuElem (_("Nudge track after edit point bwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, false))));
2266 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2269 void
2270 Editor::set_snap_to (SnapType st)
2272 unsigned int snap_ind = (unsigned int)st;
2273 snap_type = st;
2275 if (snap_ind > snap_type_strings.size() - 1) {
2276 snap_ind = 0;
2277 snap_type = (SnapType)snap_ind;
2280 string str = snap_type_strings[snap_ind];
2282 if (str != snap_type_selector.get_active_text()) {
2283 snap_type_selector.set_active_text (str);
2286 instant_save ();
2288 switch (snap_type) {
2289 case SnapToAThirtysecondBeat:
2290 case SnapToASixteenthBeat:
2291 case SnapToAEighthBeat:
2292 case SnapToAQuarterBeat:
2293 case SnapToAThirdBeat:
2294 update_tempo_based_rulers ();
2295 break;
2297 case SnapToRegionStart:
2298 case SnapToRegionEnd:
2299 case SnapToRegionSync:
2300 case SnapToRegionBoundary:
2301 build_region_boundary_cache ();
2302 break;
2304 default:
2305 /* relax */
2306 break;
2310 void
2311 Editor::set_snap_mode (SnapMode mode)
2313 snap_mode = mode;
2314 string str = snap_mode_strings[(int)mode];
2316 if (str != snap_mode_selector.get_active_text ()) {
2317 snap_mode_selector.set_active_text (str);
2320 instant_save ();
2322 void
2323 Editor::set_edit_point_preference (EditPoint ep, bool force)
2325 bool changed = (_edit_point != ep);
2327 _edit_point = ep;
2328 string str = edit_point_strings[(int)ep];
2330 if (str != edit_point_selector.get_active_text ()) {
2331 edit_point_selector.set_active_text (str);
2334 set_canvas_cursor ();
2336 if (!force && !changed) {
2337 return;
2340 switch (zoom_focus) {
2341 case ZoomFocusMouse:
2342 case ZoomFocusPlayhead:
2343 case ZoomFocusEdit:
2344 switch (_edit_point) {
2345 case EditAtMouse:
2346 set_zoom_focus (ZoomFocusMouse);
2347 break;
2348 case EditAtPlayhead:
2349 set_zoom_focus (ZoomFocusPlayhead);
2350 break;
2351 case EditAtSelectedMarker:
2352 set_zoom_focus (ZoomFocusEdit);
2353 break;
2355 break;
2356 default:
2357 break;
2360 const char* action=NULL;
2362 switch (_edit_point) {
2363 case EditAtPlayhead:
2364 action = "edit-at-playhead";
2365 break;
2366 case EditAtSelectedMarker:
2367 action = "edit-at-marker";
2368 break;
2369 case EditAtMouse:
2370 action = "edit-at-mouse";
2371 break;
2374 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2375 if (act) {
2376 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2379 nframes64_t foo;
2380 bool in_track_canvas;
2382 if (!mouse_frame (foo, in_track_canvas)) {
2383 in_track_canvas = false;
2386 reset_canvas_action_sensitivity (in_track_canvas);
2388 instant_save ();
2392 Editor::set_state (const XMLNode& node)
2394 const XMLProperty* prop;
2395 XMLNode* geometry;
2396 int x, y, xoff, yoff;
2397 Gdk::Geometry g;
2399 if ((prop = node.property ("id")) != 0) {
2400 _id = prop->value ();
2403 g.base_width = default_width;
2404 g.base_height = default_height;
2405 x = 1;
2406 y = 1;
2407 xoff = 0;
2408 yoff = 21;
2410 if ((geometry = find_named_node (node, "geometry")) != 0) {
2412 XMLProperty* prop;
2414 if ((prop = geometry->property("x_size")) == 0) {
2415 prop = geometry->property ("x-size");
2417 if (prop) {
2418 g.base_width = atoi(prop->value());
2420 if ((prop = geometry->property("y_size")) == 0) {
2421 prop = geometry->property ("y-size");
2423 if (prop) {
2424 g.base_height = atoi(prop->value());
2427 if ((prop = geometry->property ("x_pos")) == 0) {
2428 prop = geometry->property ("x-pos");
2430 if (prop) {
2431 x = atoi (prop->value());
2434 if ((prop = geometry->property ("y_pos")) == 0) {
2435 prop = geometry->property ("y-pos");
2437 if (prop) {
2438 y = atoi (prop->value());
2441 if ((prop = geometry->property ("x_off")) == 0) {
2442 prop = geometry->property ("x-off");
2444 if (prop) {
2445 xoff = atoi (prop->value());
2447 if ((prop = geometry->property ("y_off")) == 0) {
2448 prop = geometry->property ("y-off");
2450 if (prop) {
2451 yoff = atoi (prop->value());
2456 set_default_size (g.base_width, g.base_height);
2457 move (x, y);
2459 if (session && (prop = node.property ("playhead"))) {
2460 nframes64_t pos;
2461 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2462 playhead_cursor->set_position (pos);
2463 } else {
2464 playhead_cursor->set_position (0);
2466 /* reset_x_origin() doesn't work right here, since the old
2467 position may be zero already, and it does nothing in such
2468 circumstances.
2471 leftmost_frame = 0;
2472 horizontal_adjustment.set_value (0);
2475 if ((prop = node.property ("mixer-width"))) {
2476 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2479 if ((prop = node.property ("zoom-focus"))) {
2480 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2483 if ((prop = node.property ("zoom"))) {
2484 reset_zoom (PBD::atof (prop->value()));
2487 if ((prop = node.property ("snap-to"))) {
2488 set_snap_to ((SnapType) atoi (prop->value()));
2491 if ((prop = node.property ("snap-mode"))) {
2492 set_snap_mode ((SnapMode) atoi (prop->value()));
2495 if ((prop = node.property ("edit-point"))) {
2496 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2499 if ((prop = node.property ("mouse-mode"))) {
2500 MouseMode m = str2mousemode(prop->value());
2501 mouse_mode = MouseMode ((int) m + 1); /* lie, force mode switch */
2502 set_mouse_mode (m, true);
2503 } else {
2504 mouse_mode = MouseGain; /* lie, to force the mode switch */
2505 set_mouse_mode (MouseObject, true);
2508 if ((prop = node.property ("show-waveforms"))) {
2509 bool yn = (string_is_affirmative (prop->value()));
2510 _show_waveforms = !yn;
2511 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-waveform-visible"));
2512 if (act) {
2513 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2514 /* do it twice to force the change */
2515 tact->set_active (!yn);
2516 tact->set_active (yn);
2520 if ((prop = node.property ("show-waveforms-rectified"))) {
2521 bool yn = (string_is_affirmative (prop->value()));
2522 _show_waveforms_rectified = !yn;
2523 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-waveform-rectified"));
2524 if (act) {
2525 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2526 /* do it twice to force the change */
2527 tact->set_active (!yn);
2528 tact->set_active (yn);
2532 if ((prop = node.property ("show-waveforms-recording"))) {
2533 bool yn = (string_is_affirmative (prop->value()));
2534 _show_waveforms_recording = !yn;
2535 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleWaveformsWhileRecording"));
2536 if (act) {
2537 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2538 /* do it twice to force the change */
2539 tact->set_active (!yn);
2540 tact->set_active (yn);
2544 if ((prop = node.property ("show-measures"))) {
2545 bool yn = (string_is_affirmative (prop->value()));
2546 _show_measures = !yn;
2547 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2548 if (act) {
2549 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2550 /* do it twice to force the change */
2551 tact->set_active (!yn);
2552 tact->set_active (yn);
2556 if ((prop = node.property ("follow-playhead"))) {
2557 bool yn = (string_is_affirmative (prop->value()));
2558 set_follow_playhead (yn);
2559 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2560 if (act) {
2561 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2562 if (tact->get_active() != yn) {
2563 tact->set_active (yn);
2568 if ((prop = node.property ("stationary-playhead"))) {
2569 bool yn = (string_is_affirmative (prop->value()));
2570 set_stationary_playhead (yn);
2571 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2572 if (act) {
2573 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2574 if (tact->get_active() != yn) {
2575 tact->set_active (yn);
2580 if ((prop = node.property ("region-list-sort-type"))) {
2581 region_list_sort_type = (Editing::RegionListSortType) -1; // force change
2582 reset_region_list_sort_type(str2regionlistsorttype(prop->value()));
2585 if ((prop = node.property ("xfades-visible"))) {
2586 bool yn = (string_is_affirmative (prop->value()));
2587 _xfade_visibility = !yn;
2588 // set_xfade_visibility (yn);
2591 if ((prop = node.property ("show-editor-mixer"))) {
2593 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2594 if (act) {
2596 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2597 bool yn = (prop->value() == X_("yes"));
2599 /* do it twice to force the change */
2601 tact->set_active (!yn);
2602 tact->set_active (yn);
2607 return 0;
2610 XMLNode&
2611 Editor::get_state ()
2613 XMLNode* node = new XMLNode ("Editor");
2614 char buf[32];
2616 _id.print (buf, sizeof (buf));
2617 node->add_property ("id", buf);
2619 if (is_realized()) {
2620 Glib::RefPtr<Gdk::Window> win = get_window();
2622 int x, y, xoff, yoff, width, height;
2623 win->get_root_origin(x, y);
2624 win->get_position(xoff, yoff);
2625 win->get_size(width, height);
2627 XMLNode* geometry = new XMLNode ("geometry");
2629 snprintf(buf, sizeof(buf), "%d", width);
2630 geometry->add_property("x_size", string(buf));
2631 snprintf(buf, sizeof(buf), "%d", height);
2632 geometry->add_property("y_size", string(buf));
2633 snprintf(buf, sizeof(buf), "%d", x);
2634 geometry->add_property("x_pos", string(buf));
2635 snprintf(buf, sizeof(buf), "%d", y);
2636 geometry->add_property("y_pos", string(buf));
2637 snprintf(buf, sizeof(buf), "%d", xoff);
2638 geometry->add_property("x_off", string(buf));
2639 snprintf(buf, sizeof(buf), "%d", yoff);
2640 geometry->add_property("y_off", string(buf));
2641 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2642 geometry->add_property("edit_pane_pos", string(buf));
2644 node->add_child_nocopy (*geometry);
2647 maybe_add_mixer_strip_width (*node);
2649 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2650 node->add_property ("zoom-focus", buf);
2651 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2652 node->add_property ("zoom", buf);
2653 snprintf (buf, sizeof(buf), "%d", (int) snap_type);
2654 node->add_property ("snap-to", buf);
2655 snprintf (buf, sizeof(buf), "%d", (int) snap_mode);
2656 node->add_property ("snap-mode", buf);
2658 node->add_property ("edit-point", enum_2_string (_edit_point));
2660 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2661 node->add_property ("playhead", buf);
2663 node->add_property ("show-waveforms", _show_waveforms ? "yes" : "no");
2664 node->add_property ("show-waveforms-rectified", _show_waveforms_rectified ? "yes" : "no");
2665 node->add_property ("show-waveforms-recording", _show_waveforms_recording ? "yes" : "no");
2666 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2667 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2668 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2669 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2670 node->add_property ("region-list-sort-type", enum2str(region_list_sort_type));
2671 node->add_property ("mouse-mode", enum2str(mouse_mode));
2673 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2674 if (act) {
2675 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2676 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2679 return *node;
2684 TimeAxisView *
2685 Editor::trackview_by_y_position (double y)
2687 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2689 TimeAxisView *tv;
2691 if ((tv = (*iter)->covers_y_position (y)) != 0) {
2692 return tv;
2696 return 0;
2699 void
2700 Editor::snap_to (nframes64_t& start, int32_t direction, bool for_mark)
2702 if (!session || snap_mode == SnapOff) {
2703 return;
2706 snap_to_internal (start, direction, for_mark);
2709 void
2710 Editor::snap_to_internal (nframes64_t& start, int32_t direction, bool for_mark)
2712 Location* before = 0;
2713 Location* after = 0;
2715 const nframes64_t one_second = session->frame_rate();
2716 const nframes64_t one_minute = session->frame_rate() * 60;
2717 const nframes64_t one_smpte_second = (nframes64_t)(rint(session->smpte_frames_per_second()) * session->frames_per_smpte_frame());
2718 nframes64_t one_smpte_minute = (nframes64_t)(rint(session->smpte_frames_per_second()) * session->frames_per_smpte_frame() * 60);
2719 nframes64_t presnap = start;
2721 switch (snap_type) {
2722 case SnapToCDFrame:
2723 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2724 start = (nframes64_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2725 } else {
2726 start = (nframes64_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2728 break;
2730 case SnapToSMPTEFrame:
2731 if (((direction == 0) && (fmod((double)start, (double)session->frames_per_smpte_frame()) > (session->frames_per_smpte_frame() / 2))) || (direction > 0)) {
2732 start = (nframes64_t) (ceil ((double) start / session->frames_per_smpte_frame()) * session->frames_per_smpte_frame());
2733 } else {
2734 start = (nframes64_t) (floor ((double) start / session->frames_per_smpte_frame()) * session->frames_per_smpte_frame());
2736 break;
2738 case SnapToSMPTESeconds:
2739 if (session->smpte_offset_negative())
2741 start += session->smpte_offset ();
2742 } else {
2743 start -= session->smpte_offset ();
2745 if (((direction == 0) && (start % one_smpte_second > one_smpte_second / 2)) || direction > 0) {
2746 start = (nframes64_t) ceil ((double) start / one_smpte_second) * one_smpte_second;
2747 } else {
2748 start = (nframes64_t) floor ((double) start / one_smpte_second) * one_smpte_second;
2751 if (session->smpte_offset_negative())
2753 start -= session->smpte_offset ();
2754 } else {
2755 start += session->smpte_offset ();
2757 break;
2759 case SnapToSMPTEMinutes:
2760 if (session->smpte_offset_negative())
2762 start += session->smpte_offset ();
2763 } else {
2764 start -= session->smpte_offset ();
2766 if (((direction == 0) && (start % one_smpte_minute > one_smpte_minute / 2)) || direction > 0) {
2767 start = (nframes64_t) ceil ((double) start / one_smpte_minute) * one_smpte_minute;
2768 } else {
2769 start = (nframes64_t) floor ((double) start / one_smpte_minute) * one_smpte_minute;
2771 if (session->smpte_offset_negative())
2773 start -= session->smpte_offset ();
2774 } else {
2775 start += session->smpte_offset ();
2777 break;
2779 case SnapToSeconds:
2780 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2781 start = (nframes64_t) ceil ((double) start / one_second) * one_second;
2782 } else {
2783 start = (nframes64_t) floor ((double) start / one_second) * one_second;
2785 break;
2787 case SnapToMinutes:
2788 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2789 start = (nframes64_t) ceil ((double) start / one_minute) * one_minute;
2790 } else {
2791 start = (nframes64_t) floor ((double) start / one_minute) * one_minute;
2793 break;
2795 case SnapToBar:
2796 start = session->tempo_map().round_to_bar (start, direction);
2797 break;
2799 case SnapToBeat:
2800 start = session->tempo_map().round_to_beat (start, direction);
2801 break;
2803 case SnapToAThirtysecondBeat:
2804 start = session->tempo_map().round_to_beat_subdivision (start, 32);
2805 break;
2807 case SnapToASixteenthBeat:
2808 start = session->tempo_map().round_to_beat_subdivision (start, 16);
2809 break;
2811 case SnapToAEighthBeat:
2812 start = session->tempo_map().round_to_beat_subdivision (start, 8);
2813 break;
2815 case SnapToAQuarterBeat:
2816 start = session->tempo_map().round_to_beat_subdivision (start, 4);
2817 break;
2819 case SnapToAThirdBeat:
2820 start = session->tempo_map().round_to_beat_subdivision (start, 3);
2821 break;
2823 case SnapToMark:
2824 if (for_mark) {
2825 return;
2828 before = session->locations()->first_location_before (start);
2829 after = session->locations()->first_location_after (start);
2831 if (direction < 0) {
2832 if (before) {
2833 start = before->start();
2834 } else {
2835 start = 0;
2837 } else if (direction > 0) {
2838 if (after) {
2839 start = after->start();
2840 } else {
2841 start = session->current_end_frame();
2843 } else {
2844 if (before) {
2845 if (after) {
2846 /* find nearest of the two */
2847 if ((start - before->start()) < (after->start() - start)) {
2848 start = before->start();
2849 } else {
2850 start = after->start();
2852 } else {
2853 start = before->start();
2855 } else if (after) {
2856 start = after->start();
2857 } else {
2858 /* relax */
2861 break;
2863 case SnapToRegionStart:
2864 case SnapToRegionEnd:
2865 case SnapToRegionSync:
2866 case SnapToRegionBoundary:
2867 if (!region_boundary_cache.empty()) {
2868 vector<nframes64_t>::iterator i;
2870 if (direction > 0) {
2871 i = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2872 } else {
2873 i = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2876 if (i != region_boundary_cache.end()) {
2878 /* lower bound doesn't quite to the right thing for our purposes */
2880 if (direction < 0 && i != region_boundary_cache.begin()) {
2881 --i;
2884 start = *i;
2886 } else {
2887 start = region_boundary_cache.back();
2890 break;
2893 switch (snap_mode) {
2894 case SnapNormal:
2895 return;
2897 case SnapMagnetic:
2899 if (presnap > start) {
2900 if (presnap > (start + unit_to_frame(snap_threshold))) {
2901 start = presnap;
2904 } else if (presnap < start) {
2905 if (presnap < (start - unit_to_frame(snap_threshold))) {
2906 start = presnap;
2910 default:
2911 /* handled at entry */
2912 return;
2917 void
2918 Editor::setup_toolbar ()
2920 string pixmap_path;
2922 /* Mode Buttons (tool selection) */
2924 vector<ToggleButton *> mouse_mode_buttons;
2926 mouse_move_button.add (*(manage (new Image (::get_icon("tool_object")))));
2927 mouse_move_button.set_relief(Gtk::RELIEF_NONE);
2928 mouse_mode_buttons.push_back (&mouse_move_button);
2930 if (!Profile->get_sae()) {
2931 mouse_select_button.add (*(manage (new Image (get_xpm("tool_range.xpm")))));
2932 mouse_select_button.set_relief(Gtk::RELIEF_NONE);
2933 mouse_mode_buttons.push_back (&mouse_select_button);
2935 mouse_gain_button.add (*(manage (new Image (::get_icon("tool_gain")))));
2936 mouse_gain_button.set_relief(Gtk::RELIEF_NONE);
2937 mouse_mode_buttons.push_back (&mouse_gain_button);
2940 mouse_zoom_button.add (*(manage (new Image (::get_icon("tool_zoom")))));
2941 mouse_zoom_button.set_relief(Gtk::RELIEF_NONE);
2942 mouse_mode_buttons.push_back (&mouse_zoom_button);
2943 mouse_timefx_button.add (*(manage (new Image (::get_icon("tool_stretch")))));
2944 mouse_timefx_button.set_relief(Gtk::RELIEF_NONE);
2945 mouse_mode_buttons.push_back (&mouse_timefx_button);
2946 mouse_audition_button.add (*(manage (new Image (::get_icon("tool_audition")))));
2947 mouse_audition_button.set_relief(Gtk::RELIEF_NONE);
2948 mouse_mode_buttons.push_back (&mouse_audition_button);
2950 mouse_mode_button_set = new GroupedButtons (mouse_mode_buttons);
2952 HBox* mode_box = manage(new HBox);
2953 mode_box->set_border_width (2);
2954 mode_box->set_spacing(4);
2955 mouse_mode_button_box.set_spacing(1);
2956 mouse_mode_button_box.pack_start(mouse_move_button, true, true);
2957 if (!Profile->get_sae()) {
2958 mouse_mode_button_box.pack_start(mouse_select_button, true, true);
2960 mouse_mode_button_box.pack_start(mouse_zoom_button, true, true);
2961 if (!Profile->get_sae()) {
2962 mouse_mode_button_box.pack_start(mouse_gain_button, true, true);
2964 mouse_mode_button_box.pack_start(mouse_timefx_button, true, true);
2965 mouse_mode_button_box.pack_start(mouse_audition_button, true, true);
2966 mouse_mode_button_box.set_homogeneous(true);
2968 vector<string> edit_mode_strings;
2969 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2970 if (!Profile->get_sae()) {
2971 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2973 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2975 edit_mode_selector.set_name ("EditModeSelector");
2976 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2977 edit_mode_selector.signal_changed().connect (mem_fun(*this, &Editor::edit_mode_selection_done));
2979 mode_box->pack_start(edit_mode_selector);
2980 mode_box->pack_start(mouse_mode_button_box);
2982 mouse_mode_tearoff = manage (new TearOff (*mode_box));
2983 mouse_mode_tearoff->set_name ("MouseModeBase");
2984 mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (bind (sigc::ptr_fun (relay_key_press), &mouse_mode_tearoff->tearoff_window()));
2986 if (Profile->get_sae()) {
2987 mouse_mode_tearoff->set_can_be_torn_off (false);
2990 mouse_mode_tearoff->Detach.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2991 &mouse_mode_tearoff->tearoff_window()));
2992 mouse_mode_tearoff->Attach.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2993 &mouse_mode_tearoff->tearoff_window(), 1));
2994 mouse_mode_tearoff->Hidden.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2995 &mouse_mode_tearoff->tearoff_window()));
2996 mouse_mode_tearoff->Visible.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2997 &mouse_mode_tearoff->tearoff_window(), 1));
2999 mouse_move_button.set_name ("MouseModeButton");
3000 mouse_select_button.set_name ("MouseModeButton");
3001 mouse_gain_button.set_name ("MouseModeButton");
3002 mouse_zoom_button.set_name ("MouseModeButton");
3003 mouse_timefx_button.set_name ("MouseModeButton");
3004 mouse_audition_button.set_name ("MouseModeButton");
3006 ARDOUR_UI::instance()->tooltips().set_tip (mouse_move_button, _("Select/Move Objects"));
3007 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Select/Move Ranges"));
3008 ARDOUR_UI::instance()->tooltips().set_tip (mouse_gain_button, _("Draw Gain Automation"));
3009 ARDOUR_UI::instance()->tooltips().set_tip (mouse_zoom_button, _("Select Zoom Range"));
3010 ARDOUR_UI::instance()->tooltips().set_tip (mouse_timefx_button, _("Stretch/Shrink Regions"));
3011 ARDOUR_UI::instance()->tooltips().set_tip (mouse_audition_button, _("Listen to Specific Regions"));
3013 mouse_move_button.unset_flags (CAN_FOCUS);
3014 mouse_select_button.unset_flags (CAN_FOCUS);
3015 mouse_gain_button.unset_flags (CAN_FOCUS);
3016 mouse_zoom_button.unset_flags (CAN_FOCUS);
3017 mouse_timefx_button.unset_flags (CAN_FOCUS);
3018 mouse_audition_button.unset_flags (CAN_FOCUS);
3020 mouse_select_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseRange));
3021 mouse_select_button.signal_button_release_event().connect (mem_fun(*this, &Editor::mouse_select_button_release));
3023 mouse_move_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseObject));
3024 mouse_gain_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseGain));
3025 mouse_zoom_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseZoom));
3026 mouse_timefx_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseTimeFX));
3027 mouse_audition_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseAudition));
3029 // mouse_move_button.set_active (true);
3032 /* Zoom */
3034 zoom_box.set_spacing (1);
3035 zoom_box.set_border_width (0);
3037 zoom_in_button.set_name ("EditorTimeButton");
3038 zoom_in_button.set_size_request(-1,16);
3039 zoom_in_button.add (*(manage (new Image (::get_icon("zoom_in")))));
3040 zoom_in_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::temporal_zoom_step), false));
3041 ARDOUR_UI::instance()->tooltips().set_tip (zoom_in_button, _("Zoom In"));
3043 zoom_out_button.set_name ("EditorTimeButton");
3044 zoom_out_button.set_size_request(-1,16);
3045 zoom_out_button.add (*(manage (new Image (::get_icon("zoom_out")))));
3046 zoom_out_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::temporal_zoom_step), true));
3047 ARDOUR_UI::instance()->tooltips().set_tip (zoom_out_button, _("Zoom Out"));
3049 zoom_out_full_button.set_name ("EditorTimeButton");
3050 zoom_out_full_button.set_size_request(-1,16);
3051 zoom_out_full_button.add (*(manage (new Image (::get_icon("zoom_full")))));
3052 zoom_out_full_button.signal_clicked().connect (mem_fun(*this, &Editor::temporal_zoom_session));
3053 ARDOUR_UI::instance()->tooltips().set_tip (zoom_out_full_button, _("Zoom to Session"));
3055 zoom_focus_selector.set_name ("ZoomFocusSelector");
3056 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
3057 zoom_focus_selector.signal_changed().connect (mem_fun(*this, &Editor::zoom_focus_selection_done));
3058 ARDOUR_UI::instance()->tooltips().set_tip (zoom_focus_selector, _("Zoom focus"));
3060 zoom_box.pack_start (zoom_focus_selector, true, true);
3061 zoom_box.pack_start (zoom_out_button, false, false);
3062 zoom_box.pack_start (zoom_in_button, false, false);
3063 zoom_box.pack_start (zoom_out_full_button, false, false);
3065 snap_box.set_spacing (1);
3066 snap_box.set_border_width (2);
3068 snap_type_selector.set_name ("SnapTypeSelector");
3069 set_popdown_strings (snap_type_selector, snap_type_strings, true);
3070 snap_type_selector.signal_changed().connect (mem_fun(*this, &Editor::snap_type_selection_done));
3071 ARDOUR_UI::instance()->tooltips().set_tip (snap_type_selector, _("Snap/Grid Units"));
3073 snap_mode_selector.set_name ("SnapModeSelector");
3074 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
3075 snap_mode_selector.signal_changed().connect (mem_fun(*this, &Editor::snap_mode_selection_done));
3076 ARDOUR_UI::instance()->tooltips().set_tip (snap_mode_selector, _("Snap/Grid Mode"));
3078 edit_point_selector.set_name ("EditPointSelector");
3079 set_popdown_strings (edit_point_selector, edit_point_strings, true);
3080 edit_point_selector.signal_changed().connect (mem_fun(*this, &Editor::edit_point_selection_done));
3081 ARDOUR_UI::instance()->tooltips().set_tip (edit_point_selector, _("Edit point"));
3083 if (Profile->get_sae()) {
3084 snap_box.pack_start (edit_point_clock, false, false);
3086 snap_box.pack_start (snap_mode_selector, false, false);
3087 snap_box.pack_start (snap_type_selector, false, false);
3088 snap_box.pack_start (edit_point_selector, false, false);
3090 /* Nudge */
3092 HBox *nudge_box = manage (new HBox);
3093 nudge_box->set_spacing(1);
3094 nudge_box->set_border_width (2);
3096 nudge_forward_button.signal_button_release_event().connect (mem_fun(*this, &Editor::nudge_forward_release), false);
3097 nudge_backward_button.signal_button_release_event().connect (mem_fun(*this, &Editor::nudge_backward_release), false);
3099 nudge_box->pack_start (nudge_backward_button, false, false);
3100 nudge_box->pack_start (nudge_forward_button, false, false);
3101 nudge_box->pack_start (nudge_clock, false, false);
3104 /* Pack everything in... */
3106 HBox* hbox = new HBox;
3107 hbox->set_spacing(10);
3109 tools_tearoff = new TearOff (*hbox);
3110 tools_tearoff->set_name ("MouseModeBase");
3111 tools_tearoff->tearoff_window().signal_key_press_event().connect (bind (sigc::ptr_fun (relay_key_press), &tools_tearoff->tearoff_window()));
3113 if (Profile->get_sae()) {
3114 tools_tearoff->set_can_be_torn_off (false);
3117 tools_tearoff->Detach.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
3118 &tools_tearoff->tearoff_window()));
3119 tools_tearoff->Attach.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
3120 &tools_tearoff->tearoff_window(), 0));
3121 tools_tearoff->Hidden.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
3122 &tools_tearoff->tearoff_window()));
3123 tools_tearoff->Visible.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
3124 &tools_tearoff->tearoff_window(), 0));
3126 toolbar_hbox.set_spacing (10);
3127 toolbar_hbox.set_border_width (1);
3129 toolbar_hbox.pack_start (*mouse_mode_tearoff, false, false);
3130 toolbar_hbox.pack_start (*tools_tearoff, false, false);
3133 hbox->pack_start (snap_box, false, false);
3134 // hbox->pack_start (zoom_box, false, false);
3135 hbox->pack_start (*nudge_box, false, false);
3137 hbox->show_all ();
3139 toolbar_base.set_name ("ToolBarBase");
3140 toolbar_base.add (toolbar_hbox);
3142 toolbar_frame.set_shadow_type (SHADOW_OUT);
3143 toolbar_frame.set_name ("BaseFrame");
3144 toolbar_frame.add (toolbar_base);
3148 Editor::convert_drop_to_paths (vector<ustring>& paths,
3149 const RefPtr<Gdk::DragContext>& context,
3150 gint x,
3151 gint y,
3152 const SelectionData& data,
3153 guint info,
3154 guint time)
3157 if (session == 0) {
3158 return -1;
3161 vector<ustring> uris = data.get_uris();
3163 if (uris.empty()) {
3165 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3166 are actually URI lists. So do it by hand.
3169 if (data.get_target() != "text/plain") {
3170 return -1;
3173 /* Parse the "uri-list" format that Nautilus provides,
3174 where each pathname is delimited by \r\n.
3176 THERE MAY BE NO NULL TERMINATING CHAR!!!
3179 ustring txt = data.get_text();
3180 const char* p;
3181 const char* q;
3183 p = (const char *) malloc (txt.length() + 1);
3184 txt.copy ((char *) p, txt.length(), 0);
3185 ((char*)p)[txt.length()] = '\0';
3187 while (p)
3189 if (*p != '#')
3191 while (g_ascii_isspace (*p))
3192 p++;
3194 q = p;
3195 while (*q && (*q != '\n') && (*q != '\r')) {
3196 q++;
3199 if (q > p)
3201 q--;
3202 while (q > p && g_ascii_isspace (*q))
3203 q--;
3205 if (q > p)
3207 uris.push_back (ustring (p, q - p + 1));
3211 p = strchr (p, '\n');
3212 if (p)
3213 p++;
3216 free ((void*)p);
3218 if (uris.empty()) {
3219 return -1;
3223 for (vector<ustring>::iterator i = uris.begin(); i != uris.end(); ++i) {
3225 if ((*i).substr (0,7) == "file://") {
3228 ustring p = *i;
3229 PBD::url_decode (p);
3231 // scan forward past three slashes
3233 ustring::size_type slashcnt = 0;
3234 ustring::size_type n = 0;
3235 ustring::iterator x = p.begin();
3237 while (slashcnt < 3 && x != p.end()) {
3238 if ((*x) == '/') {
3239 slashcnt++;
3240 } else if (slashcnt == 3) {
3241 break;
3243 ++n;
3244 ++x;
3247 if (slashcnt != 3 || x == p.end()) {
3248 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3249 continue;
3252 paths.push_back (p.substr (n - 1));
3256 return 0;
3259 void
3260 Editor::new_tempo_section ()
3265 void
3266 Editor::map_transport_state ()
3268 ENSURE_GUI_THREAD (mem_fun(*this, &Editor::map_transport_state));
3270 if (session && session->transport_stopped()) {
3271 have_pending_keyboard_selection = false;
3274 update_loop_range_view (true);
3277 /* UNDO/REDO */
3279 Editor::State::State ()
3281 selection = new Selection;
3284 Editor::State::~State ()
3286 delete selection;
3289 UndoAction
3290 Editor::get_memento () const
3292 State *state = new State;
3294 store_state (*state);
3295 return bind (mem_fun (*(const_cast<Editor*>(this)), &Editor::restore_state), state);
3298 void
3299 Editor::store_state (State& state) const
3301 *state.selection = *selection;
3304 void
3305 Editor::restore_state (State *state)
3307 if (*selection == *state->selection) {
3308 return;
3311 *selection = *state->selection;
3312 time_selection_changed ();
3313 region_selection_changed ();
3315 /* XXX other selection change handlers? */
3318 void
3319 Editor::begin_reversible_command (string name)
3321 if (session) {
3322 // before = &get_state();
3323 session->begin_reversible_command (name);
3327 void
3328 Editor::commit_reversible_command ()
3330 if (session) {
3331 // session->commit_reversible_command (new MementoCommand<Editor>(*this, before, &get_state()));
3332 session->commit_reversible_command ();
3336 void
3337 Editor::set_edit_group_solo (Route& route, bool yn)
3339 RouteGroup *edit_group;
3341 if ((edit_group = route.edit_group()) != 0) {
3342 edit_group->apply (&Route::set_solo, yn, this);
3343 } else {
3344 route.set_solo (yn, this);
3348 void
3349 Editor::set_edit_group_mute (Route& route, bool yn)
3351 RouteGroup *edit_group = 0;
3353 if ((edit_group == route.edit_group()) != 0) {
3354 edit_group->apply (&Route::set_mute, yn, this);
3355 } else {
3356 route.set_mute (yn, this);
3360 void
3361 Editor::history_changed ()
3363 string label;
3365 if (undo_action && session) {
3366 if (session->undo_depth() == 0) {
3367 label = _("Undo");
3368 } else {
3369 label = string_compose(_("Undo (%1)"), session->next_undo());
3371 undo_action->property_label() = label;
3374 if (redo_action && session) {
3375 if (session->redo_depth() == 0) {
3376 label = _("Redo");
3377 } else {
3378 label = string_compose(_("Redo (%1)"), session->next_redo());
3380 redo_action->property_label() = label;
3384 void
3385 Editor::duplicate_dialog (bool with_dialog)
3387 float times = 1.0f;
3389 if (mouse_mode == MouseRange) {
3390 if (selection->time.length() == 0) {
3391 return;
3395 RegionSelection rs;
3396 get_regions_for_action (rs);
3398 if (mouse_mode != MouseRange) {
3400 if (rs.empty()) {
3401 return;
3405 if (with_dialog) {
3407 ArdourDialog win ("Duplicate");
3408 Label label (_("Number of Duplications:"));
3409 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3410 SpinButton spinner (adjustment, 0.0, 1);
3411 HBox hbox;
3413 win.get_vbox()->set_spacing (12);
3414 win.get_vbox()->pack_start (hbox);
3415 hbox.set_border_width (6);
3416 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3418 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3419 place, visually. so do this by hand.
3422 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3423 spinner.signal_activate().connect (sigc::bind (mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3424 spinner.grab_focus();
3426 hbox.show ();
3427 label.show ();
3428 spinner.show ();
3430 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3431 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3432 win.set_default_response (RESPONSE_ACCEPT);
3434 win.set_position (WIN_POS_MOUSE);
3436 spinner.grab_focus ();
3438 switch (win.run ()) {
3439 case RESPONSE_ACCEPT:
3440 break;
3441 default:
3442 return;
3445 times = adjustment.get_value();
3448 if (mouse_mode == MouseRange) {
3449 duplicate_selection (times);
3450 } else {
3451 duplicate_some_regions (rs, times);
3455 void
3456 Editor::show_verbose_canvas_cursor ()
3458 verbose_canvas_cursor->raise_to_top();
3459 verbose_canvas_cursor->show();
3460 verbose_cursor_visible = true;
3463 void
3464 Editor::hide_verbose_canvas_cursor ()
3466 verbose_canvas_cursor->hide();
3467 verbose_cursor_visible = false;
3470 double
3471 Editor::clamp_verbose_cursor_x (double x)
3473 if (x < 0) {
3474 x = 0;
3475 } else {
3476 x = min (canvas_width - 200.0, x);
3478 return x;
3481 double
3482 Editor::clamp_verbose_cursor_y (double y)
3484 if (y < canvas_timebars_vsize) {
3485 y = canvas_timebars_vsize;
3486 } else {
3487 y = min (canvas_height - 50, y);
3489 return y;
3492 void
3493 Editor::set_verbose_canvas_cursor (const string & txt, double x, double y)
3495 verbose_canvas_cursor->property_text() = txt.c_str();
3496 /* don't get too close to the edge */
3497 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (x);
3498 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (y);
3501 void
3502 Editor::set_verbose_canvas_cursor_text (const string & txt)
3504 verbose_canvas_cursor->property_text() = txt.c_str();
3507 void
3508 Editor::set_edit_mode (EditMode m)
3510 Config->set_edit_mode (m);
3513 void
3514 Editor::cycle_edit_mode ()
3516 switch (Config->get_edit_mode()) {
3517 case Slide:
3518 if (Profile->get_sae()) {
3519 Config->set_edit_mode (Lock);
3520 } else {
3521 Config->set_edit_mode (Splice);
3523 break;
3524 case Splice:
3525 Config->set_edit_mode (Lock);
3526 break;
3527 case Lock:
3528 Config->set_edit_mode (Slide);
3529 break;
3533 void
3534 Editor::edit_mode_selection_done ()
3536 if (session == 0) {
3537 return;
3540 string choice = edit_mode_selector.get_active_text();
3541 EditMode mode = Slide;
3543 if (choice == _("Splice Edit")) {
3544 mode = Splice;
3545 } else if (choice == _("Slide Edit")) {
3546 mode = Slide;
3547 } else if (choice == _("Lock Edit")) {
3548 mode = Lock;
3551 Config->set_edit_mode (mode);
3554 void
3555 Editor::snap_type_selection_done ()
3557 string choice = snap_type_selector.get_active_text();
3558 SnapType snaptype = SnapToBeat;
3560 if (choice == _("Beats/3")) {
3561 snaptype = SnapToAThirdBeat;
3562 } else if (choice == _("Beats/4")) {
3563 snaptype = SnapToAQuarterBeat;
3564 } else if (choice == _("Beats/8")) {
3565 snaptype = SnapToAEighthBeat;
3566 } else if (choice == _("Beats/16")) {
3567 snaptype = SnapToASixteenthBeat;
3568 } else if (choice == _("Beats/32")) {
3569 snaptype = SnapToAThirtysecondBeat;
3570 } else if (choice == _("Beats")) {
3571 snaptype = SnapToBeat;
3572 } else if (choice == _("Bars")) {
3573 snaptype = SnapToBar;
3574 } else if (choice == _("Marks")) {
3575 snaptype = SnapToMark;
3576 } else if (choice == _("Region starts")) {
3577 snaptype = SnapToRegionStart;
3578 } else if (choice == _("Region ends")) {
3579 snaptype = SnapToRegionEnd;
3580 } else if (choice == _("Region bounds")) {
3581 snaptype = SnapToRegionBoundary;
3582 } else if (choice == _("Region syncs")) {
3583 snaptype = SnapToRegionSync;
3584 } else if (choice == _("CD Frames")) {
3585 snaptype = SnapToCDFrame;
3586 } else if (choice == _("SMPTE Frames")) {
3587 snaptype = SnapToSMPTEFrame;
3588 } else if (choice == _("SMPTE Seconds")) {
3589 snaptype = SnapToSMPTESeconds;
3590 } else if (choice == _("SMPTE Minutes")) {
3591 snaptype = SnapToSMPTEMinutes;
3592 } else if (choice == _("Seconds")) {
3593 snaptype = SnapToSeconds;
3594 } else if (choice == _("Minutes")) {
3595 snaptype = SnapToMinutes;
3598 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3599 if (ract) {
3600 ract->set_active ();
3604 void
3605 Editor::snap_mode_selection_done ()
3607 string choice = snap_mode_selector.get_active_text();
3608 SnapMode mode = SnapNormal;
3610 if (choice == _("No Grid")) {
3611 mode = SnapOff;
3612 } else if (choice == _("Grid")) {
3613 mode = SnapNormal;
3614 } else if (choice == _("Magnetic")) {
3615 mode = SnapMagnetic;
3618 RefPtr<RadioAction> ract = snap_mode_action (mode);
3620 if (ract) {
3621 ract->set_active (true);
3625 void
3626 Editor::cycle_edit_point (bool with_marker)
3628 switch (_edit_point) {
3629 case EditAtMouse:
3630 set_edit_point_preference (EditAtPlayhead);
3631 break;
3632 case EditAtPlayhead:
3633 if (with_marker) {
3634 set_edit_point_preference (EditAtSelectedMarker);
3635 } else {
3636 set_edit_point_preference (EditAtMouse);
3638 break;
3639 case EditAtSelectedMarker:
3640 set_edit_point_preference (EditAtMouse);
3641 break;
3645 void
3646 Editor::edit_point_selection_done ()
3648 string choice = edit_point_selector.get_active_text();
3649 EditPoint ep = EditAtSelectedMarker;
3651 if (choice == _("Marker")) {
3652 set_edit_point_preference (EditAtSelectedMarker);
3653 } else if (choice == _("Playhead")) {
3654 set_edit_point_preference (EditAtPlayhead);
3655 } else {
3656 set_edit_point_preference (EditAtMouse);
3659 RefPtr<RadioAction> ract = edit_point_action (ep);
3661 if (ract) {
3662 ract->set_active (true);
3666 void
3667 Editor::zoom_focus_selection_done ()
3669 string choice = zoom_focus_selector.get_active_text();
3670 ZoomFocus focus_type = ZoomFocusLeft;
3672 if (choice == _("Left")) {
3673 focus_type = ZoomFocusLeft;
3674 } else if (choice == _("Right")) {
3675 focus_type = ZoomFocusRight;
3676 } else if (choice == _("Center")) {
3677 focus_type = ZoomFocusCenter;
3678 } else if (choice == _("Playhead")) {
3679 focus_type = ZoomFocusPlayhead;
3680 } else if (choice == _("Mouse")) {
3681 focus_type = ZoomFocusMouse;
3682 } else if (choice == _("Active Mark")) {
3683 focus_type = ZoomFocusEdit;
3686 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3688 if (ract) {
3689 ract->set_active ();
3693 gint
3694 Editor::edit_controls_button_release (GdkEventButton* ev)
3696 if (Keyboard::is_context_menu_event (ev)) {
3697 ARDOUR_UI::instance()->add_route (this);
3699 return TRUE;
3702 gint
3703 Editor::mouse_select_button_release (GdkEventButton* ev)
3705 /* this handles just right-clicks */
3707 if (ev->button != 3) {
3708 return false;
3711 return true;
3714 Editor::TrackViewList *
3715 Editor::get_valid_views (TimeAxisView* track, RouteGroup* group)
3717 TrackViewList *v;
3718 TrackViewList::iterator i;
3720 v = new TrackViewList;
3722 if (all_group_is_active || (track == 0 && group == 0) ) {
3724 /* all views */
3726 for (i = track_views.begin(); i != track_views.end (); ++i) {
3727 v->push_back (*i);
3730 } else if ((track != 0 && group == 0) || (track != 0 && group != 0 && !group->is_active())) {
3732 /* just the view for this track
3735 v->push_back (track);
3737 } else {
3739 /* views for all tracks in the edit group */
3741 for (i = track_views.begin(); i != track_views.end (); ++i) {
3743 if (group == 0 || (*i)->edit_group() == group) {
3744 v->push_back (*i);
3749 return v;
3752 void
3753 Editor::set_zoom_focus (ZoomFocus f)
3755 string str = zoom_focus_strings[(int)f];
3757 if (str != zoom_focus_selector.get_active_text()) {
3758 zoom_focus_selector.set_active_text (str);
3761 if (zoom_focus != f) {
3762 zoom_focus = f;
3764 ZoomFocusChanged (); /* EMIT_SIGNAL */
3766 instant_save ();
3770 void
3771 Editor::ensure_float (Window& win)
3773 win.set_transient_for (*this);
3776 void
3777 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3779 /* recover or initialize pane positions. do this here rather than earlier because
3780 we don't want the positions to change the child allocations, which they seem to do.
3783 int pos;
3784 XMLProperty* prop;
3785 char buf[32];
3786 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3787 int width, height;
3788 static int32_t done;
3789 XMLNode* geometry;
3791 width = default_width;
3792 height = default_height;
3794 if ((geometry = find_named_node (*node, "geometry")) != 0) {
3796 if ((prop = geometry->property ("x_size")) == 0) {
3797 prop = geometry->property ("x-size");
3799 if (prop) {
3800 width = atoi (prop->value());
3802 if ((prop = geometry->property ("y_size")) == 0) {
3803 prop = geometry->property ("y-size");
3805 if (prop) {
3806 height = atoi (prop->value());
3810 if (which == static_cast<Paned*> (&edit_pane)) {
3812 if (done) {
3813 return;
3816 if (!geometry || (prop = geometry->property ("edit_pane_pos")) == 0) {
3817 /* initial allocation is 90% to canvas, 10% to notebook */
3818 pos = (int) floor (alloc.get_width() * 0.90f);
3819 snprintf (buf, sizeof(buf), "%d", pos);
3820 } else {
3821 pos = atoi (prop->value());
3824 if ((done = GTK_WIDGET(edit_pane.gobj())->allocation.width > pos)) {
3825 edit_pane.set_position (pos);
3826 pre_maximal_pane_position = pos;
3831 void
3832 Editor::detach_tearoff (Box* b, Window* w)
3834 if (tools_tearoff->torn_off() &&
3835 mouse_mode_tearoff->torn_off()) {
3836 top_hbox.remove (toolbar_frame);
3840 void
3841 Editor::reattach_tearoff (Box* b, Window* w, int32_t n)
3843 if (toolbar_frame.get_parent() == 0) {
3844 top_hbox.pack_end (toolbar_frame);
3848 void
3849 Editor::set_show_measures (bool yn)
3851 if (_show_measures != yn) {
3852 hide_measures ();
3854 if ((_show_measures = yn) == true) {
3855 if (tempo_lines) {
3856 tempo_lines->show();
3858 draw_measures ();
3860 instant_save ();
3864 void
3865 Editor::toggle_follow_playhead ()
3867 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3868 if (act) {
3869 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3870 set_follow_playhead (tact->get_active());
3874 void
3875 Editor::set_follow_playhead (bool yn)
3877 if (_follow_playhead != yn) {
3878 if ((_follow_playhead = yn) == true) {
3879 /* catch up */
3880 update_current_screen ();
3882 instant_save ();
3886 void
3887 Editor::toggle_stationary_playhead ()
3889 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3890 if (act) {
3891 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3892 set_stationary_playhead (tact->get_active());
3896 void
3897 Editor::set_stationary_playhead (bool yn)
3899 if (_stationary_playhead != yn) {
3900 if ((_stationary_playhead = yn) == true) {
3901 /* catch up */
3902 update_current_screen ();
3904 instant_save ();
3908 void
3909 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3911 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3912 if (xfade) {
3913 xfade->set_active (!xfade->active());
3917 void
3918 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3920 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3921 if (xfade) {
3922 xfade->set_follow_overlap (!xfade->following_overlap());
3926 void
3927 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3929 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3931 if (!xfade) {
3932 return;
3935 CrossfadeEditor cew (*session, xfade, xfade->fade_in().get_min_y(), 1.0);
3937 ensure_float (cew);
3939 switch (cew.run ()) {
3940 case RESPONSE_ACCEPT:
3941 break;
3942 default:
3943 return;
3946 cew.apply ();
3947 xfade->StateChanged (Change (~0));
3950 PlaylistSelector&
3951 Editor::playlist_selector () const
3953 return *_playlist_selector;
3956 nframes64_t
3957 Editor::get_nudge_distance (nframes64_t pos, nframes64_t& next)
3959 nframes64_t ret;
3961 ret = nudge_clock.current_duration (pos);
3962 next = ret + 1; /* XXXX fix me */
3964 return ret;
3967 void
3968 Editor::end_location_changed (Location* location)
3970 ENSURE_GUI_THREAD (bind (mem_fun(*this, &Editor::end_location_changed), location));
3971 //reset_scrolling_region ();
3972 nframes64_t session_span = location->start() + (nframes64_t) floorf (current_page_frames() * 0.10f);
3973 horizontal_adjustment.set_upper (session_span / frames_per_unit);
3977 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3979 ArdourDialog dialog ("playlist deletion dialog");
3980 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3981 "If left alone, no audio files used by it will be cleaned.\n"
3982 "If deleted, audio files used by it alone by will cleaned."),
3983 pl->name()));
3985 dialog.set_position (WIN_POS_CENTER);
3986 dialog.get_vbox()->pack_start (label);
3988 label.show ();
3990 dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT);
3991 dialog.add_button (_("Keep playlist"), RESPONSE_REJECT);
3992 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3994 switch (dialog.run ()) {
3995 case RESPONSE_ACCEPT:
3996 /* delete the playlist */
3997 return 0;
3998 break;
4000 case RESPONSE_REJECT:
4001 /* keep the playlist */
4002 return 1;
4003 break;
4005 default:
4006 break;
4009 return -1;
4012 bool
4013 Editor::audio_region_selection_covers (nframes64_t where)
4015 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
4016 if ((*a)->region()->covers (where)) {
4017 return true;
4021 return false;
4024 void
4025 Editor::prepare_for_cleanup ()
4027 cut_buffer->clear_regions ();
4028 cut_buffer->clear_playlists ();
4030 selection->clear_regions ();
4031 selection->clear_playlists ();
4033 no_region_list_redisplay = true;
4036 void
4037 Editor::finish_cleanup ()
4039 no_region_list_redisplay = false;
4040 redisplay_regions ();
4043 Location*
4044 Editor::transport_loop_location()
4046 if (session) {
4047 return session->locations()->auto_loop_location();
4048 } else {
4049 return 0;
4053 Location*
4054 Editor::transport_punch_location()
4056 if (session) {
4057 return session->locations()->auto_punch_location();
4058 } else {
4059 return 0;
4063 bool
4064 Editor::control_layout_scroll (GdkEventScroll* ev)
4066 switch (ev->direction) {
4067 case GDK_SCROLL_UP:
4068 scroll_tracks_up_line ();
4069 return true;
4070 break;
4072 case GDK_SCROLL_DOWN:
4073 scroll_tracks_down_line ();
4074 return true;
4076 default:
4077 /* no left/right handling yet */
4078 break;
4081 return false;
4085 /** A new snapshot has been selected.
4087 void
4088 Editor::snapshot_display_selection_changed ()
4090 if (snapshot_display.get_selection()->count_selected_rows() > 0) {
4092 TreeModel::iterator i = snapshot_display.get_selection()->get_selected();
4094 Glib::ustring snap_name = (*i)[snapshot_display_columns.real_name];
4096 if (snap_name.length() == 0) {
4097 return;
4100 if (session->snap_name() == snap_name) {
4101 return;
4104 ARDOUR_UI::instance()->load_session(session->path(), string (snap_name));
4108 bool
4109 Editor::snapshot_display_button_press (GdkEventButton* ev)
4111 if (ev->button == 3) {
4112 /* Right-click on the snapshot list. Work out which snapshot it
4113 was over. */
4114 Gtk::TreeModel::Path path;
4115 Gtk::TreeViewColumn* col;
4116 int cx;
4117 int cy;
4118 snapshot_display.get_path_at_pos ((int) ev->x, (int) ev->y, path, col, cx, cy);
4119 Gtk::TreeModel::iterator iter = snapshot_display_model->get_iter (path);
4120 if (iter) {
4121 Gtk::TreeModel::Row row = *iter;
4122 popup_snapshot_context_menu (ev->button, ev->time, row[snapshot_display_columns.real_name]);
4124 return true;
4127 return false;
4131 /** Pop up the snapshot display context menu.
4132 * @param button Button used to open the menu.
4133 * @param time Menu open time.
4134 * @snapshot_name Name of the snapshot that the menu click was over.
4137 void
4138 Editor::popup_snapshot_context_menu (int button, int32_t time, Glib::ustring snapshot_name)
4140 using namespace Menu_Helpers;
4142 MenuList& items (snapshot_context_menu.items());
4143 items.clear ();
4145 const bool modification_allowed = (session->snap_name() != snapshot_name && session->name() != snapshot_name);
4147 items.push_back (MenuElem (_("Remove"), bind (mem_fun (*this, &Editor::remove_snapshot), snapshot_name)));
4148 if (!modification_allowed) {
4149 items.back().set_sensitive (false);
4152 items.push_back (MenuElem (_("Rename"), bind (mem_fun (*this, &Editor::rename_snapshot), snapshot_name)));
4153 if (!modification_allowed) {
4154 items.back().set_sensitive (false);
4157 snapshot_context_menu.popup (button, time);
4160 void
4161 Editor::rename_snapshot (Glib::ustring old_name)
4163 ArdourPrompter prompter(true);
4165 string new_name;
4167 prompter.set_name ("Prompter");
4168 prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
4169 prompter.set_prompt (_("New name of snapshot"));
4170 prompter.set_initial_text (old_name);
4172 if (prompter.run() == RESPONSE_ACCEPT) {
4173 prompter.get_result (new_name);
4174 if (new_name.length()) {
4175 session->rename_state (old_name, new_name);
4176 redisplay_snapshots ();
4182 void
4183 Editor::remove_snapshot (Glib::ustring name)
4185 vector<string> choices;
4187 std::string prompt = string_compose (_("Do you really want to remove snapshot \"%1\" ?\n(cannot be undone)"), name);
4189 choices.push_back (_("No, do nothing."));
4190 choices.push_back (_("Yes, remove it."));
4192 Gtkmm2ext::Choice prompter (prompt, choices);
4194 if (prompter.run () == 1) {
4195 session->remove_state (name);
4196 redisplay_snapshots ();
4200 void
4201 Editor::redisplay_snapshots ()
4203 if (session == 0) {
4204 return;
4207 vector<string*>* states;
4209 if ((states = session->possible_states()) == 0) {
4210 return;
4213 snapshot_display_model->clear ();
4215 for (vector<string*>::iterator i = states->begin(); i != states->end(); ++i) {
4216 string statename = *(*i);
4217 TreeModel::Row row = *(snapshot_display_model->append());
4219 /* this lingers on in case we ever want to change the visible
4220 name of the snapshot.
4223 string display_name;
4224 display_name = statename;
4226 if (statename == session->snap_name()) {
4227 snapshot_display.get_selection()->select(row);
4230 row[snapshot_display_columns.visible_name] = display_name;
4231 row[snapshot_display_columns.real_name] = statename;
4234 delete states;
4237 void
4238 Editor::session_state_saved (string snap_name)
4240 ENSURE_GUI_THREAD (bind (mem_fun(*this, &Editor::session_state_saved), snap_name));
4241 redisplay_snapshots ();
4244 void
4245 Editor::maximise_editing_space ()
4247 mouse_mode_tearoff->set_visible (false);
4248 tools_tearoff->set_visible (false);
4250 pre_maximal_pane_position = edit_pane.get_position();
4251 pre_maximal_editor_width = this->get_width();
4253 if(post_maximal_pane_position == 0) {
4254 post_maximal_pane_position = edit_pane.get_width();
4257 fullscreen();
4259 if(post_maximal_editor_width) {
4260 edit_pane.set_position (post_maximal_pane_position -
4261 abs(post_maximal_editor_width - pre_maximal_editor_width));
4262 } else {
4263 edit_pane.set_position (post_maximal_pane_position);
4267 void
4268 Editor::restore_editing_space ()
4270 // user changed width of pane during fullscreen
4271 if(post_maximal_pane_position != edit_pane.get_position()) {
4272 post_maximal_pane_position = edit_pane.get_position();
4275 unfullscreen();
4277 mouse_mode_tearoff->set_visible (true);
4278 tools_tearoff->set_visible (true);
4279 post_maximal_editor_width = this->get_width();
4282 edit_pane.set_position (
4283 pre_maximal_pane_position + abs(this->get_width() - pre_maximal_editor_width)
4288 * Make new playlists for a given track and also any others that belong
4289 * to the same active edit group.
4290 * @param v Track.
4293 void
4294 Editor::new_playlists (TimeAxisView* v)
4296 begin_reversible_command (_("new playlists"));
4297 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4298 session->get_playlists(playlists);
4299 mapover_audio_tracks ( bind(mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v );
4300 commit_reversible_command ();
4305 * Use a copy of the current playlist for a given track and also any others that belong
4306 * to the same active edit group.
4307 * @param v Track.
4310 void
4311 Editor::copy_playlists (TimeAxisView* v)
4313 begin_reversible_command (_("copy playlists"));
4314 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4315 session->get_playlists(playlists);
4316 mapover_audio_tracks ( bind(mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v );
4317 commit_reversible_command ();
4322 * Clear the current playlist for a given track and also any others that belong
4323 * to the same active edit group.
4324 * @param v Track.
4327 void
4328 Editor::clear_playlists (TimeAxisView* v)
4330 begin_reversible_command (_("clear playlists"));
4331 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4332 session->get_playlists(playlists);
4333 mapover_audio_tracks ( mem_fun (*this, &Editor::mapped_clear_playlist), v );
4334 commit_reversible_command ();
4337 void
4338 Editor::mapped_use_new_playlist (AudioTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4340 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4343 void
4344 Editor::mapped_use_copy_playlist (AudioTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4346 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4349 void
4350 Editor::mapped_clear_playlist (AudioTimeAxisView& atv, uint32_t sz)
4352 atv.clear_playlist ();
4355 bool
4356 Editor::on_key_press_event (GdkEventKey* ev)
4358 return key_press_focus_accelerator_handler (*this, ev);
4361 bool
4362 Editor::on_key_release_event (GdkEventKey* ev)
4364 return Gtk::Window::on_key_release_event (ev);
4365 // return key_press_focus_accelerator_handler (*this, ev);
4368 void
4369 Editor::reset_x_origin (nframes64_t frame)
4371 queue_visual_change (frame);
4374 void
4375 Editor::reset_zoom (double fpu)
4377 queue_visual_change (fpu);
4380 void
4381 Editor::reposition_and_zoom (nframes64_t frame, double fpu)
4383 //cerr << "Editor::reposition_and_zoom () called ha v:l:u:ps:fpu = " << horizontal_adjustment.get_value() << ":" << horizontal_adjustment.get_lower() << ":" << horizontal_adjustment.get_upper() << ":" << horizontal_adjustment.get_page_size() << ":" << frames_per_unit << endl;//DEBUG
4384 reset_x_origin (frame);
4385 reset_zoom (fpu);
4387 if (!no_save_visual) {
4388 undo_visual_stack.push_back (current_visual_state(false));
4392 Editor::VisualState*
4393 Editor::current_visual_state (bool with_tracks)
4395 VisualState* vs = new VisualState;
4396 vs->y_position = vertical_adjustment.get_value();
4397 vs->frames_per_unit = frames_per_unit;
4398 vs->leftmost_frame = leftmost_frame;
4399 vs->zoom_focus = zoom_focus;
4401 if (with_tracks) {
4402 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4403 vs->track_states.push_back (TAVState ((*i), &(*i)->get_state()));
4407 return vs;
4410 void
4411 Editor::undo_visual_state ()
4413 if (undo_visual_stack.empty()) {
4414 return;
4417 redo_visual_stack.push_back (current_visual_state());
4419 VisualState* vs = undo_visual_stack.back();
4420 undo_visual_stack.pop_back();
4421 use_visual_state (*vs);
4424 void
4425 Editor::redo_visual_state ()
4427 if (redo_visual_stack.empty()) {
4428 return;
4431 undo_visual_stack.push_back (current_visual_state());
4433 VisualState* vs = redo_visual_stack.back();
4434 redo_visual_stack.pop_back();
4435 use_visual_state (*vs);
4438 void
4439 Editor::swap_visual_state ()
4441 if (undo_visual_stack.empty()) {
4442 redo_visual_state ();
4443 } else {
4444 undo_visual_state ();
4445 undo_visual_stack.clear(); //swap_visual_state truncates the undo stack so we are just bouncing between 2 states
4449 void
4450 Editor::use_visual_state (VisualState& vs)
4452 no_save_visual = true;
4453 no_route_list_redisplay = true;
4455 vertical_adjustment.set_value (vs.y_position);
4457 set_zoom_focus (vs.zoom_focus);
4458 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4460 for (list<TAVState>::iterator i = vs.track_states.begin(); i != vs.track_states.end(); ++i) {
4461 TrackViewList::iterator t;
4463 /* check if the track still exists - it could have been deleted */
4465 if ((t = find (track_views.begin(), track_views.end(), i->first)) != track_views.end()) {
4466 (*t)->set_state (*(i->second));
4470 if (!vs.track_states.empty()) {
4471 update_route_visibility ();
4474 no_route_list_redisplay = false;
4475 redisplay_route_list ();
4477 no_save_visual = false;
4480 void
4481 Editor::set_frames_per_unit (double fpu)
4483 /* this is the core function that controls the zoom level of the canvas. it is called
4484 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4487 if (fpu == frames_per_unit) {
4488 return;
4491 if (fpu < 1.0) {
4492 fpu = 1.0;
4496 /* don't allow zooms that fit more than the maximum number
4497 of frames into an 800 pixel wide space.
4500 if (max_frames / fpu < 800.0) {
4501 return;
4504 if (tempo_lines) {
4505 tempo_lines->tempo_map_changed();
4508 frames_per_unit = fpu;
4509 post_zoom ();
4512 void
4513 Editor::post_zoom ()
4515 nframes64_t cef = 0;
4517 // convert fpu to frame count
4519 nframes64_t frames = (nframes64_t) floor (frames_per_unit * canvas_width);
4521 if (frames_per_unit != zoom_range_clock.current_duration()) {
4522 zoom_range_clock.set (frames);
4525 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4526 if (!selection->tracks.empty()) {
4527 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4528 (*i)->reshow_selection (selection->time);
4530 } else {
4531 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4532 (*i)->reshow_selection (selection->time);
4536 update_loop_range_view (false);
4537 update_punch_range_view (false);
4539 if (playhead_cursor) {
4540 playhead_cursor->set_position (playhead_cursor->current_frame);
4543 leftmost_frame = (nframes64_t) floor (horizontal_adjustment.get_value() * frames_per_unit);
4545 ZoomChanged (); /* EMIT_SIGNAL */
4547 reset_hscrollbar_stepping ();
4549 if (session) {
4550 cef = session->current_end_frame() + (current_page_frames() / 10);// Add a little extra so we can see the end marker
4552 horizontal_adjustment.set_upper (cef / frames_per_unit);
4554 //reset_scrolling_region ();
4556 instant_save ();
4559 void
4560 Editor::queue_visual_change (nframes64_t where)
4562 pending_visual_change.pending = VisualChange::Type (pending_visual_change.pending | VisualChange::TimeOrigin);
4564 /* if we're moving beyond the end, make sure the upper limit of the horizontal adjustment
4565 can reach.
4568 if (where > session->current_end_frame()) {
4569 horizontal_adjustment.set_upper ((where + current_page_frames()) / frames_per_unit);
4572 pending_visual_change.time_origin = where;
4574 if (pending_visual_change.idle_handler_id < 0) {
4575 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4579 void
4580 Editor::queue_visual_change (double fpu)
4582 pending_visual_change.pending = VisualChange::Type (pending_visual_change.pending | VisualChange::ZoomLevel);
4583 pending_visual_change.frames_per_unit = fpu;
4585 if (pending_visual_change.idle_handler_id < 0) {
4586 pending_visual_change.idle_handler_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE, _idle_visual_changer, this, 0);
4591 Editor::_idle_visual_changer (void* arg)
4593 return static_cast<Editor*>(arg)->idle_visual_changer ();
4597 Editor::idle_visual_changer ()
4599 VisualChange::Type p = pending_visual_change.pending;
4600 pending_visual_change.pending = (VisualChange::Type) 0;
4602 double last_time_origin = horizontal_adjustment.get_value();
4603 if (p & VisualChange::ZoomLevel) {
4604 set_frames_per_unit (pending_visual_change.frames_per_unit);
4607 if (p & VisualChange::TimeOrigin) {
4608 horizontal_adjustment.set_value (pending_visual_change.time_origin / frames_per_unit);
4611 if (last_time_origin == horizontal_adjustment.get_value() ) {
4612 /* changed signal not emitted */
4613 update_fixed_rulers ();
4614 redisplay_tempo (true);
4616 // cerr << "Editor::idle_visual_changer () called ha v:l:u:ps:fpu = " << horizontal_adjustment.get_value() << ":" << horizontal_adjustment.get_lower() << ":" << horizontal_adjustment.get_upper() << ":" << horizontal_adjustment.get_page_size() << ":" << frames_per_unit << endl;//DEBUG
4617 pending_visual_change.idle_handler_id = -1;
4618 return 0; /* this is always a one-shot call */
4621 struct EditorOrderTimeAxisSorter {
4622 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4623 return a->order < b->order;
4627 void
4628 Editor::sort_track_selection (TrackSelection* sel)
4630 EditorOrderTimeAxisSorter cmp;
4632 if (sel) {
4633 sel->sort (cmp);
4634 } else {
4635 selection->tracks.sort (cmp);
4639 nframes64_t
4640 Editor::get_preferred_edit_position (bool ignore_playhead)
4642 bool ignored;
4643 nframes64_t where = 0;
4644 EditPoint ep = _edit_point;
4646 if (entered_marker) {
4647 return entered_marker->position();
4650 if (ignore_playhead && ep == EditAtPlayhead) {
4651 ep = EditAtSelectedMarker;
4654 switch (ep) {
4655 case EditAtPlayhead:
4656 where = session->audible_frame();
4657 break;
4659 case EditAtSelectedMarker:
4660 if (!selection->markers.empty()) {
4661 bool is_start;
4662 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4663 if (loc) {
4664 if (is_start) {
4665 where = loc->start();
4666 } else {
4667 where = loc->end();
4669 break;
4672 /* fallthru */
4674 default:
4675 case EditAtMouse:
4676 if (!mouse_frame (where, ignored)) {
4677 /* XXX not right but what can we do ? */
4678 return 0;
4680 snap_to (where);
4681 break;
4684 return where;
4687 void
4688 Editor::set_loop_range (nframes64_t start, nframes64_t end, string cmd)
4690 if (!session) return;
4692 begin_reversible_command (cmd);
4694 Location* tll;
4696 if ((tll = transport_loop_location()) == 0) {
4697 Location* loc = new Location (start, end, _("Loop"), Location::IsAutoLoop);
4698 XMLNode &before = session->locations()->get_state();
4699 session->locations()->add (loc, true);
4700 session->set_auto_loop_location (loc);
4701 XMLNode &after = session->locations()->get_state();
4702 session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after));
4703 } else {
4704 XMLNode &before = tll->get_state();
4705 tll->set_hidden (false, this);
4706 tll->set (start, end);
4707 XMLNode &after = tll->get_state();
4708 session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4711 commit_reversible_command ();
4714 void
4715 Editor::set_punch_range (nframes64_t start, nframes64_t end, string cmd)
4717 if (!session) return;
4719 begin_reversible_command (cmd);
4721 Location* tpl;
4723 if ((tpl = transport_punch_location()) == 0) {
4724 Location* loc = new Location (start, end, _("Loop"), Location::IsAutoPunch);
4725 XMLNode &before = session->locations()->get_state();
4726 session->locations()->add (loc, true);
4727 session->set_auto_loop_location (loc);
4728 XMLNode &after = session->locations()->get_state();
4729 session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after));
4731 else {
4732 XMLNode &before = tpl->get_state();
4733 tpl->set_hidden (false, this);
4734 tpl->set (start, end);
4735 XMLNode &after = tpl->get_state();
4736 session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4739 commit_reversible_command ();
4742 void
4743 Editor::get_regions_at (RegionSelection& rs, nframes64_t where, const TrackSelection& ts) const
4745 const TrackSelection* tracks;
4747 if (ts.empty()) {
4748 tracks = &track_views;
4749 } else {
4750 tracks = &ts;
4753 for (TrackSelection::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4755 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*t);
4757 if (atv) {
4758 boost::shared_ptr<Diskstream> ds;
4759 boost::shared_ptr<Playlist> pl;
4761 if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()))) {
4763 Playlist::RegionList* regions = pl->regions_at ((nframes64_t) floor ( (double)where * ds->speed()));
4765 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4767 RegionView* rv = atv->audio_view()->find_view (*i);
4769 if (rv) {
4770 rs.add (rv);
4774 delete regions;
4780 void
4781 Editor::get_regions_after (RegionSelection& rs, nframes64_t where, const TrackSelection& ts) const
4783 const TrackSelection* tracks;
4785 if (ts.empty()) {
4786 tracks = &track_views;
4787 } else {
4788 tracks = &ts;
4791 for (TrackSelection::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4793 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*t);
4795 if (atv) {
4796 boost::shared_ptr<Diskstream> ds;
4797 boost::shared_ptr<Playlist> pl;
4799 if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()))) {
4801 Playlist::RegionList* regions = pl->regions_touched ((nframes64_t) floor ( (double)where * ds->speed()), max_frames);
4803 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4805 RegionView* rv = atv->audio_view()->find_view (*i);
4807 if (rv) {
4808 rs.push_back (rv);
4812 delete regions;
4818 void
4819 Editor::get_regions_for_action (RegionSelection& rs, bool allow_entered)
4821 if (selection->regions.empty()) {
4823 if (selection->tracks.empty()) {
4825 /* no regions or tracks selected
4828 if (entered_regionview && mouse_mode == Editing::MouseObject) {
4830 /* entered regionview is valid and we're in object mode -
4831 just use entered regionview
4834 rs.add (entered_regionview);
4837 return;
4839 } else {
4841 /* no regions selected, so get all regions at the edit point across
4842 all selected tracks.
4845 nframes64_t where = get_preferred_edit_position();
4846 get_regions_at (rs, where, selection->tracks);
4848 /* if the entered regionview wasn't selected and neither was its track
4849 then add it.
4852 if (entered_regionview != 0 &&
4853 !selection->selected (entered_regionview) &&
4854 !selection->selected (&entered_regionview->get_time_axis_view())) {
4855 rs.add (entered_regionview);
4859 } else {
4861 /* just use the selected regions */
4863 rs = selection->regions;
4865 /* if the entered regionview wasn't selected and we allow this sort of thing,
4866 then add it.
4869 if (allow_entered && entered_regionview && !selection->selected (entered_regionview)) {
4870 rs.add (entered_regionview);
4876 void
4877 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4880 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4882 RouteTimeAxisView* tatv;
4884 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4886 boost::shared_ptr<Playlist> pl;
4887 vector<boost::shared_ptr<Region> > results;
4888 RegionView* marv;
4889 boost::shared_ptr<Diskstream> ds;
4891 if ((ds = tatv->get_diskstream()) == 0) {
4892 /* bus */
4893 continue;
4896 if ((pl = (ds->playlist())) != 0) {
4897 pl->get_region_list_equivalent_regions (region, results);
4900 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4901 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4902 regions.push_back (marv);
4910 void
4911 Editor::show_rhythm_ferret ()
4913 if (rhythm_ferret == 0) {
4914 rhythm_ferret = new RhythmFerret(*this);
4917 rhythm_ferret->set_session (session);
4918 rhythm_ferret->show ();
4919 rhythm_ferret->present ();
4922 void
4923 Editor::first_idle ()
4925 MessageDialog* dialog = 0;
4927 if (track_views.size() > 1) {
4928 dialog = new MessageDialog (*this,
4929 string_compose (_("Please wait while %1 loads visual data"), PROGRAM_NAME),
4930 true,
4931 Gtk::MESSAGE_INFO,
4932 Gtk::BUTTONS_NONE);
4933 dialog->present ();
4934 ARDOUR_UI::instance()->flush_pending ();
4937 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4938 (*t)->first_idle();
4941 // first idle adds route children (automation tracks), so we need to redisplay here
4942 redisplay_route_list();
4944 if (dialog) {
4945 delete dialog;
4948 _have_idled = true;
4951 bool
4952 Editor::on_expose_event (GdkEventExpose* ev)
4954 /* cerr << "+++ editor expose "
4955 << ev->area.x << ',' << ev->area.y
4956 << ' '
4957 << ev->area.width << " x " << ev->area.height
4958 << " need reize ? " << need_resize_line
4959 << endl;
4961 bool ret = Window::on_expose_event (ev);
4963 #if 0
4964 if (need_resize_line) {
4966 int xroot, yroot, discard;
4967 int controls_width;
4969 /* Our root coordinates for drawing the line will be the left edge
4970 of the track controls, and the upper left edge of our own window.
4973 get_window()->get_origin (discard, yroot);
4974 controls_layout.get_window()->get_origin (xroot, discard);
4975 controls_width = controls_layout.get_width();
4977 GdkRectangle lr;
4978 GdkRectangle intersection;
4980 lr.x = 0;
4981 lr.y = resize_line_y;
4982 lr.width = controls_width + (int) canvas_width;
4983 lr.height = 3;
4985 if (gdk_rectangle_intersect (&lr, &ev->area, &intersection)) {
4987 Glib::RefPtr<Gtk::Style> style (get_style());
4988 Glib::RefPtr<Gdk::GC> black_gc (style->get_black_gc ());
4989 Glib::RefPtr<Gdk::GC> gc = wrap (black_gc->gobj_copy(), false);
4991 /* draw on root window */
4993 GdkWindow* win = gdk_get_default_root_window();
4995 gc->set_subwindow (Gdk::INCLUDE_INFERIORS);
4996 gc->set_line_attributes (3, Gdk::LINE_SOLID,
4997 Gdk::CAP_NOT_LAST,
4998 Gdk::JOIN_MITER);
5000 gdk_draw_line (win, gc->gobj(),
5002 resize_line_y,
5003 (int) canvas_width + controls_width,
5004 resize_line_y);
5005 #if 0
5006 cerr << "drew line @ " << xroot << ", " << yroot + resize_line_y
5007 << " to " << xroot + (int) canvas_width + controls_width
5008 << ", " << yroot + resize_line_y
5009 << endl;
5010 #endif
5011 old_resize_line_y = resize_line_y;
5012 cerr << "NEXT EXPOSE SHOULD BE AT " << old_resize_line_y << endl;
5013 } else {
5014 cerr << "no intersect with "
5015 << lr.x << ',' << lr.y
5016 << ' '
5017 << lr.width << " x " << lr.height
5018 << endl;
5022 //cerr << "--- editor expose\n";
5023 #endif
5025 return ret;
5028 static gboolean
5029 _idle_resizer (gpointer arg)
5031 return ((Editor*)arg)->idle_resize ();
5034 void
5035 Editor::add_to_idle_resize (TimeAxisView* view, uint32_t h)
5037 if (resize_idle_id < 0) {
5038 resize_idle_id = g_idle_add (_idle_resizer, this);
5041 resize_idle_target = h;
5043 pending_resizes.push_back (view);
5045 if (selection->selected (view) && !selection->tracks.empty()) {
5046 pending_resizes.insert (pending_resizes.end(), selection->tracks.begin(), selection->tracks.end());
5050 bool
5051 Editor::idle_resize ()
5053 for (vector<TimeAxisView*>::iterator i = pending_resizes.begin(); i != pending_resizes.end(); ++i) {
5054 (*i)->idle_resize (resize_idle_target);
5056 pending_resizes.clear();
5057 //flush_canvas ();
5058 resize_idle_id = -1;
5059 return false;
5062 void
5063 Editor::change_region_layering_order (nframes64_t position)
5065 if (clicked_regionview == 0) {
5066 if (layering_order_editor) {
5067 layering_order_editor->hide ();
5069 return;
5072 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_trackview);
5074 if (atv == 0) {
5075 return;
5078 boost::shared_ptr<Diskstream> ds;
5079 boost::shared_ptr<Playlist> pl;
5081 if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()))) {
5083 if (layering_order_editor == 0) {
5084 layering_order_editor = new RegionLayeringOrderEditor(*this);
5086 layering_order_editor->set_context (atv->name(), session, pl, position);
5087 layering_order_editor->maybe_present ();
5091 void
5092 Editor::update_region_layering_order_editor (nframes64_t frame)
5094 if (layering_order_editor && layering_order_editor->is_visible ()) {
5095 change_region_layering_order (frame);