force AIFF format exported files to big-endian
[ardour2.git] / gtk2_ardour / editor.cc
blob525ab9c7e655700e51eefdeca03f6f1a2c75fdf0
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 selection->TimeChanged.connect (mem_fun(*this, &Editor::time_selection_changed));
276 selection->TracksChanged.connect (mem_fun(*this, &Editor::track_selection_changed));
277 selection->RegionsChanged.connect (mem_fun(*this, &Editor::region_selection_changed));
278 selection->PointsChanged.connect (mem_fun(*this, &Editor::point_selection_changed));
279 selection->MarkersChanged.connect (mem_fun(*this, &Editor::marker_selection_changed));
281 clicked_regionview = 0;
282 clicked_trackview = 0;
283 clicked_audio_trackview = 0;
284 clicked_crossfadeview = 0;
285 clicked_control_point = 0;
286 last_update_frame = 0;
287 drag_info.item = 0;
288 current_mixer_strip = 0;
289 current_bbt_points = 0;
290 tempo_lines = 0;
292 snap_type_strings = I18N (_snap_type_strings);
293 snap_mode_strings = I18N (_snap_mode_strings);
294 zoom_focus_strings = I18N (_zoom_focus_strings);
295 edit_point_strings = I18N (_edit_point_strings);
296 #ifdef USE_RUBBERBAND
297 rb_opt_strings = I18N (_rb_opt_strings);
298 #endif
300 snap_threshold = 5.0;
301 bbt_beat_subdivision = 4;
302 canvas_width = 0;
303 canvas_height = 0;
304 last_autoscroll_x = 0;
305 last_autoscroll_y = 0;
306 autoscroll_active = false;
307 autoscroll_timeout_tag = -1;
308 interthread_progress_window = 0;
309 logo_item = 0;
311 #ifdef FFT_ANALYSIS
312 analysis_window = 0;
313 #endif
315 current_interthread_info = 0;
316 _show_measures = true;
317 _show_waveforms = true;
318 _show_waveforms_rectified = false;
319 _show_waveforms_recording = true;
320 export_dialog = 0;
321 export_range_markers_dialog = 0;
322 show_gain_after_trim = false;
323 route_redisplay_does_not_sync_order_keys = false;
324 route_redisplay_does_not_reset_order_keys = false;
325 no_route_list_redisplay = false;
326 verbose_cursor_on = true;
327 route_removal = false;
328 show_automatic_regions_in_region_list = true;
329 last_item_entered = 0;
330 last_item_entered_n = 0;
332 region_list_sort_type = (Editing::RegionListSortType) 0;
333 have_pending_keyboard_selection = false;
334 _follow_playhead = true;
335 _stationary_playhead = false;
336 _xfade_visibility = true;
337 editor_ruler_menu = 0;
338 no_ruler_shown_update = false;
339 edit_group_list_menu = 0;
340 route_list_menu = 0;
341 region_list_menu = 0;
342 marker_menu = 0;
343 start_end_marker_menu = 0;
344 range_marker_menu = 0;
345 marker_menu_item = 0;
346 tm_marker_menu = 0;
347 transport_marker_menu = 0;
348 new_transport_marker_menu = 0;
349 editor_mixer_strip_width = Wide;
350 show_editor_mixer_when_tracks_arrive = false;
351 region_edit_menu_split_item = 0;
352 temp_location = 0;
353 region_edit_menu_split_multichannel_item = 0;
354 leftmost_frame = 0;
355 ignore_mouse_mode_toggle = false;
356 current_stepping_trackview = 0;
357 entered_track = 0;
358 entered_regionview = 0;
359 entered_marker = 0;
360 clear_entered_track = false;
361 _new_regionviews_show_envelope = false;
362 current_timefx = 0;
363 in_edit_group_row_change = false;
364 playhead_cursor = 0;
365 button_release_can_deselect = true;
366 _dragging_playhead = false;
367 _dragging_edit_point = false;
368 _dragging_hscrollbar = false;
369 select_new_marker = false;
370 rhythm_ferret = 0;
371 layering_order_editor = 0;
372 allow_vertical_scroll = false;
373 no_save_visual = false;
374 need_resize_line = false;
375 resize_line_y = 0;
376 old_resize_line_y = -1;
377 no_region_list_redisplay = false;
378 resize_idle_id = -1;
380 _scrubbing = false;
381 scrubbing_direction = 0;
383 sfbrowser = 0;
385 location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get();
386 location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get();
387 location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get();
388 location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get();
389 location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get();
391 range_marker_drag_rect = 0;
392 marker_drag_line = 0;
394 _edit_point = EditAtMouse;
395 set_mouse_mode (MouseObject, true);
397 frames_per_unit = 2048; /* too early to use reset_zoom () */
398 reset_hscrollbar_stepping ();
400 zoom_focus = ZoomFocusLeft;
401 set_zoom_focus (ZoomFocusLeft);
402 zoom_range_clock.ValueChanged.connect (mem_fun(*this, &Editor::zoom_adjustment_changed));
404 bbt_label.set_name ("EditorTimeButton");
405 bbt_label.set_size_request (-1, (int)timebar_height);
406 bbt_label.set_alignment (1.0, 0.5);
407 bbt_label.set_padding (5,0);
408 bbt_label.hide ();
409 bbt_label.set_no_show_all();
410 minsec_label.set_name ("EditorTimeButton");
411 minsec_label.set_size_request (-1, (int)timebar_height);
412 minsec_label.set_alignment (1.0, 0.5);
413 minsec_label.set_padding (5,0);
414 minsec_label.hide ();
415 minsec_label.set_no_show_all();
416 smpte_label.set_name ("EditorTimeButton");
417 smpte_label.set_size_request (-1, (int)timebar_height);
418 smpte_label.set_alignment (1.0, 0.5);
419 smpte_label.set_padding (5,0);
420 smpte_label.hide ();
421 smpte_label.set_no_show_all();
422 frame_label.set_name ("EditorTimeButton");
423 frame_label.set_size_request (-1, (int)timebar_height);
424 frame_label.set_alignment (1.0, 0.5);
425 frame_label.set_padding (5,0);
426 frame_label.hide ();
427 frame_label.set_no_show_all();
429 tempo_label.set_name ("EditorTimeButton");
430 tempo_label.set_size_request (-1, (int)timebar_height);
431 tempo_label.set_alignment (1.0, 0.5);
432 tempo_label.set_padding (5,0);
433 tempo_label.hide();
434 tempo_label.set_no_show_all();
435 meter_label.set_name ("EditorTimeButton");
436 meter_label.set_size_request (-1, (int)timebar_height);
437 meter_label.set_alignment (1.0, 0.5);
438 meter_label.set_padding (5,0);
439 meter_label.hide();
440 meter_label.set_no_show_all();
441 mark_label.set_name ("EditorTimeButton");
442 mark_label.set_size_request (-1, (int)timebar_height);
443 mark_label.set_alignment (1.0, 0.5);
444 mark_label.set_padding (5,0);
445 mark_label.hide();
446 mark_label.set_no_show_all();
447 cd_mark_label.set_name ("EditorTimeButton");
448 cd_mark_label.set_size_request (-1, (int)timebar_height);
449 cd_mark_label.set_alignment (1.0, 0.5);
450 cd_mark_label.set_padding (5,0);
451 cd_mark_label.hide();
452 cd_mark_label.set_no_show_all();
453 range_mark_label.set_name ("EditorTimeButton");
454 range_mark_label.set_size_request (-1, (int)timebar_height);
455 range_mark_label.set_alignment (1.0, 0.5);
456 range_mark_label.set_padding (5,0);
457 range_mark_label.hide();
458 range_mark_label.set_no_show_all();
459 transport_mark_label.set_name ("EditorTimeButton");
460 transport_mark_label.set_size_request (-1, (int)timebar_height);
461 transport_mark_label.set_alignment (1.0, 0.5);
462 transport_mark_label.set_padding (5,0);
463 transport_mark_label.hide();
464 transport_mark_label.set_no_show_all();
466 initialize_rulers ();
467 initialize_canvas ();
469 edit_controls_vbox.set_spacing (0);
470 horizontal_adjustment.signal_value_changed().connect (mem_fun(*this, &Editor::scroll_canvas_horizontally), false);
471 vertical_adjustment.signal_value_changed().connect (mem_fun(*this, &Editor::tie_vertical_scrolling), true);
472 track_canvas->signal_map_event().connect (mem_fun (*this, &Editor::track_canvas_map_handler));
474 controls_layout.add (edit_controls_vbox);
475 controls_layout.set_name ("EditControlsBase");
476 controls_layout.add_events (Gdk::SCROLL_MASK);
477 controls_layout.signal_scroll_event().connect (mem_fun(*this, &Editor::control_layout_scroll), false);
479 controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
480 controls_layout.signal_button_release_event().connect (mem_fun(*this, &Editor::edit_controls_button_release));
481 controls_layout_size_request_connection = controls_layout.signal_size_request().connect (mem_fun (*this, &Editor::controls_layout_size_request));
483 edit_vscrollbar.set_adjustment (vertical_adjustment);
484 edit_hscrollbar.set_adjustment (horizontal_adjustment);
486 edit_hscrollbar.signal_button_press_event().connect (mem_fun(*this, &Editor::hscrollbar_button_press), false);
487 edit_hscrollbar.signal_button_release_event().connect (mem_fun(*this, &Editor::hscrollbar_button_release), false);
488 edit_hscrollbar.signal_size_allocate().connect (mem_fun(*this, &Editor::hscrollbar_allocate));
490 edit_hscrollbar.set_name ("EditorHScrollbar");
492 build_cursors ();
493 setup_toolbar ();
495 edit_point_clock.ValueChanged.connect (mem_fun(*this, &Editor::edit_point_clock_changed));
497 //time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2);
498 time_canvas_vbox.set_size_request (-1, -1);
500 ruler_label_event_box.add (ruler_label_vbox);
501 ruler_label_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
502 ruler_label_event_box.set_name ("TimebarLabelBase");
503 ruler_label_event_box.signal_button_release_event().connect (mem_fun(*this, &Editor::ruler_label_button_release));
505 time_button_event_box.add (time_button_vbox);
507 time_button_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
508 time_button_event_box.set_name ("TimebarLabelBase");
509 time_button_event_box.signal_button_release_event().connect (mem_fun(*this, &Editor::ruler_label_button_release));
511 /* these enable us to have a dedicated window (for cursor setting, etc.)
512 for the canvas areas.
515 track_canvas_event_box.add (*track_canvas);
517 time_canvas_event_box.add (time_canvas_vbox);
518 time_canvas_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
520 edit_packer.set_col_spacings (0);
521 edit_packer.set_row_spacings (0);
522 edit_packer.set_homogeneous (false);
523 edit_packer.set_border_width (0);
524 edit_packer.set_name ("EditorWindow");
526 edit_packer.attach (edit_vscrollbar, 0, 1, 0, 4, FILL, FILL|EXPAND, 0, 0);
528 edit_packer.attach (ruler_label_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0);
529 edit_packer.attach (time_button_event_box, 1, 2, 1, 2, FILL, SHRINK, 0, 0);
530 edit_packer.attach (time_canvas_event_box, 2, 3, 0, 1, FILL|EXPAND, FILL, 0, 0);
532 edit_packer.attach (controls_layout, 1, 2, 2, 3, FILL, FILL|EXPAND, 0, 0);
533 edit_packer.attach (track_canvas_event_box, 2, 3, 1, 3, FILL|EXPAND, FILL|EXPAND, 0, 0);
535 edit_packer.attach (zoom_box, 1, 2, 3, 4, FILL, FILL, 0, 0);
536 edit_packer.attach (edit_hscrollbar, 2, 3, 3, 4, FILL|EXPAND, FILL, 0, 0);
538 bottom_hbox.set_border_width (2);
539 bottom_hbox.set_spacing (3);
541 route_display_model = ListStore::create(route_display_columns);
542 route_list_display.set_model (route_display_model);
543 route_list_display.append_column (_("Show"), route_display_columns.visible);
544 route_list_display.append_column (_("Name"), route_display_columns.text);
545 route_list_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
546 route_list_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
547 route_list_display.set_headers_visible (true);
548 route_list_display.set_name ("TrackListDisplay");
549 route_list_display.get_selection()->set_mode (SELECTION_NONE);
550 route_list_display.set_reorderable (true);
551 route_list_display.set_size_request (100,-1);
553 CellRendererToggle* route_list_visible_cell = dynamic_cast<CellRendererToggle*>(route_list_display.get_column_cell_renderer (0));
554 route_list_visible_cell->property_activatable() = true;
555 route_list_visible_cell->property_radio() = false;
557 route_display_model->signal_row_deleted().connect (mem_fun (*this, &Editor::route_list_delete));
558 route_display_model->signal_row_changed().connect (mem_fun (*this, &Editor::route_list_change));
559 route_display_model->signal_rows_reordered().connect (mem_fun (*this, &Editor::track_list_reorder));
561 route_list_display.signal_button_press_event().connect (mem_fun (*this, &Editor::route_list_display_button_press), false);
563 route_list_scroller.add (route_list_display);
564 route_list_scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
566 group_model = ListStore::create(group_columns);
567 edit_group_display.set_model (group_model);
568 edit_group_display.append_column (_("Name"), group_columns.text);
569 edit_group_display.append_column (_("Active"), group_columns.is_active);
570 edit_group_display.append_column (_("Show"), group_columns.is_visible);
571 edit_group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
572 edit_group_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
573 edit_group_display.get_column (2)->set_data (X_("colnum"), GUINT_TO_POINTER(2));
574 edit_group_display.get_column (0)->set_expand (true);
575 edit_group_display.get_column (1)->set_expand (false);
576 edit_group_display.get_column (2)->set_expand (false);
577 edit_group_display.set_headers_visible (true);
579 /* name is directly editable */
581 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(edit_group_display.get_column_cell_renderer (0));
582 name_cell->property_editable() = true;
583 name_cell->signal_edited().connect (mem_fun (*this, &Editor::edit_group_name_edit));
585 /* use checkbox for the active + visible columns */
587 CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*>(edit_group_display.get_column_cell_renderer (1));
588 active_cell->property_activatable() = true;
589 active_cell->property_radio() = false;
591 active_cell = dynamic_cast<CellRendererToggle*>(edit_group_display.get_column_cell_renderer (1));
592 active_cell->property_activatable() = true;
593 active_cell->property_radio() = false;
595 group_model->signal_row_changed().connect (mem_fun (*this, &Editor::edit_group_row_change));
597 edit_group_display.set_name ("EditGroupList");
598 edit_group_display.get_selection()->set_mode (SELECTION_SINGLE);
599 edit_group_display.set_headers_visible (true);
600 edit_group_display.set_reorderable (false);
601 edit_group_display.set_rules_hint (true);
602 edit_group_display.set_size_request (75, -1);
604 edit_group_display_scroller.add (edit_group_display);
605 edit_group_display_scroller.set_policy (POLICY_AUTOMATIC, POLICY_AUTOMATIC);
607 edit_group_display.signal_button_press_event().connect (mem_fun(*this, &Editor::edit_group_list_button_press_event), false);
609 VBox* edit_group_display_packer = manage (new VBox());
610 HBox* edit_group_display_button_box = manage (new HBox());
611 edit_group_display_button_box->set_homogeneous (true);
613 Button* edit_group_add_button = manage (new Button ());
614 Button* edit_group_remove_button = manage (new Button ());
616 Widget* w;
618 w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
619 w->show();
620 edit_group_add_button->add (*w);
622 w = manage (new Image (Stock::REMOVE, ICON_SIZE_BUTTON));
623 w->show();
624 edit_group_remove_button->add (*w);
626 edit_group_add_button->signal_clicked().connect (mem_fun (*this, &Editor::new_edit_group));
627 edit_group_remove_button->signal_clicked().connect (mem_fun (*this, &Editor::remove_selected_edit_group));
629 edit_group_display_button_box->pack_start (*edit_group_add_button);
630 edit_group_display_button_box->pack_start (*edit_group_remove_button);
632 edit_group_display_packer->pack_start (edit_group_display_scroller, true, true);
633 edit_group_display_packer->pack_start (*edit_group_display_button_box, false, false);
635 region_list_display.set_size_request (100, -1);
636 region_list_display.set_name ("RegionListDisplay");
637 /* Try to prevent single mouse presses from initiating edits.
638 This relies on a hack in gtktreeview.c:gtk_treeview_button_press()
640 region_list_display.set_data ("mouse-edits-require-mod1", (gpointer) 0x1);
642 region_list_model = TreeStore::create (region_list_columns);
643 region_list_model->set_sort_func (0, mem_fun (*this, &Editor::region_list_sorter));
644 region_list_model->set_sort_column (0, SORT_ASCENDING);
646 region_list_display.set_model (region_list_model);
647 region_list_display.append_column (_("Regions"), region_list_columns.name);
648 region_list_display.set_headers_visible (false);
650 CellRendererText* region_name_cell = dynamic_cast<CellRendererText*>(region_list_display.get_column_cell_renderer (0));
651 region_name_cell->property_editable() = true;
652 region_name_cell->signal_edited().connect (mem_fun (*this, &Editor::region_name_edit));
654 region_list_display.get_selection()->set_select_function (mem_fun (*this, &Editor::region_list_selection_filter));
656 TreeViewColumn* tv_col = region_list_display.get_column(0);
657 CellRendererText* renderer = dynamic_cast<CellRendererText*>(region_list_display.get_column_cell_renderer (0));
658 tv_col->add_attribute(renderer->property_text(), region_list_columns.name);
659 tv_col->add_attribute(renderer->property_foreground_gdk(), region_list_columns.color_);
661 region_list_display.get_selection()->set_mode (SELECTION_MULTIPLE);
662 region_list_display.add_object_drag (region_list_columns.region.index(), "regions");
664 /* setup DnD handling */
666 list<TargetEntry> region_list_target_table;
668 region_list_target_table.push_back (TargetEntry ("text/plain"));
669 region_list_target_table.push_back (TargetEntry ("text/uri-list"));
670 region_list_target_table.push_back (TargetEntry ("application/x-rootwin-drop"));
672 region_list_display.add_drop_targets (region_list_target_table);
673 region_list_display.signal_drag_data_received().connect (mem_fun(*this, &Editor::region_list_display_drag_data_received));
675 region_list_scroller.add (region_list_display);
676 region_list_scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
678 region_list_display.signal_key_press_event().connect (mem_fun(*this, &Editor::region_list_display_key_press));
679 region_list_display.signal_key_release_event().connect (mem_fun(*this, &Editor::region_list_display_key_release));
680 region_list_display.signal_button_press_event().connect (mem_fun(*this, &Editor::region_list_display_button_press), false);
681 region_list_display.signal_button_release_event().connect (mem_fun(*this, &Editor::region_list_display_button_release));
682 region_list_display.get_selection()->signal_changed().connect (mem_fun(*this, &Editor::region_list_selection_changed));
683 // region_list_display.signal_popup_menu().connect (bind (mem_fun (*this, &Editor::show_region_list_display_context_menu), 1, 0));
685 named_selection_scroller.add (named_selection_display);
686 named_selection_scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
688 named_selection_model = TreeStore::create (named_selection_columns);
689 named_selection_display.set_model (named_selection_model);
690 named_selection_display.append_column (_("Chunks"), named_selection_columns.text);
691 named_selection_display.set_headers_visible (false);
692 named_selection_display.set_size_request (100, -1);
693 named_selection_display.set_name ("NamedSelectionDisplay");
695 named_selection_display.get_selection()->set_mode (SELECTION_SINGLE);
696 named_selection_display.set_size_request (100, -1);
697 named_selection_display.signal_button_release_event().connect (mem_fun(*this, &Editor::named_selection_display_button_release), false);
698 named_selection_display.signal_key_release_event().connect (mem_fun(*this, &Editor::named_selection_display_key_release), false);
699 named_selection_display.get_selection()->signal_changed().connect (mem_fun (*this, &Editor::named_selection_display_selection_changed));
701 /* SNAPSHOTS */
703 snapshot_display_model = ListStore::create (snapshot_display_columns);
704 snapshot_display.set_model (snapshot_display_model);
705 snapshot_display.append_column (X_("snapshot"), snapshot_display_columns.visible_name);
706 snapshot_display.set_name ("SnapshotDisplay");
707 snapshot_display.set_size_request (75, -1);
708 snapshot_display.set_headers_visible (false);
709 snapshot_display.set_reorderable (false);
710 snapshot_display_scroller.add (snapshot_display);
711 snapshot_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
713 snapshot_display.get_selection()->signal_changed().connect (mem_fun(*this, &Editor::snapshot_display_selection_changed));
714 snapshot_display.signal_button_press_event().connect (mem_fun (*this, &Editor::snapshot_display_button_press), false);
716 Gtk::Label* nlabel;
718 nlabel = manage (new Label (_("Regions")));
719 nlabel->set_angle (-90);
720 the_notebook.append_page (region_list_scroller, *nlabel);
721 nlabel = manage (new Label (_("Tracks/Busses")));
722 nlabel->set_angle (-90);
723 the_notebook.append_page (route_list_scroller, *nlabel);
724 nlabel = manage (new Label (_("Snapshots")));
725 nlabel->set_angle (-90);
726 the_notebook.append_page (snapshot_display_scroller, *nlabel);
727 nlabel = manage (new Label (_("Edit Groups")));
728 nlabel->set_angle (-90);
729 the_notebook.append_page (*edit_group_display_packer, *nlabel);
731 if (!Profile->get_sae()) {
732 nlabel = manage (new Label (_("Chunks")));
733 nlabel->set_angle (-90);
734 the_notebook.append_page (named_selection_scroller, *nlabel);
737 the_notebook.set_show_tabs (true);
738 the_notebook.set_scrollable (true);
739 the_notebook.popup_enable ();
740 the_notebook.set_tab_pos (Gtk::POS_RIGHT);
742 post_maximal_editor_width = 0;
743 post_maximal_pane_position = 0;
744 edit_pane.pack1 (edit_packer, true, true);
745 edit_pane.pack2 (the_notebook, false, true);
747 edit_pane.signal_size_allocate().connect (bind (mem_fun(*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&edit_pane)));
748 #ifdef GTKOSX
749 Glib::PropertyProxy<int> proxy = edit_pane.property_position();
750 proxy.signal_changed().connect (bind (sigc::ptr_fun (pane_size_watcher), static_cast<Paned*> (&edit_pane)));
751 #endif
753 top_hbox.pack_start (toolbar_frame, true, true);
755 HBox *hbox = manage (new HBox);
756 hbox->pack_start (edit_pane, true, true);
758 global_vpacker.pack_start (top_hbox, false, false);
759 global_vpacker.pack_start (*hbox, true, true);
761 global_hpacker.pack_start (global_vpacker, true, true);
763 set_name ("EditorWindow");
764 add_accel_group (ActionManager::ui_manager->get_accel_group());
766 status_bar_hpacker.show ();
768 vpacker.pack_end (status_bar_hpacker, false, false);
769 vpacker.pack_end (global_hpacker, true, true);
771 /* register actions now so that set_state() can find them and set toggles/checks etc */
773 register_actions ();
775 snap_type = SnapToBeat;
776 set_snap_to (snap_type);
777 snap_mode = SnapOff;
778 set_snap_mode (snap_mode);
779 set_edit_point_preference (EditAtMouse, true);
781 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
782 set_state (*node);
784 _playlist_selector = new PlaylistSelector();
785 _playlist_selector->signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
787 RegionView::RegionViewGoingAway.connect (mem_fun(*this, &Editor::catch_vanishing_regionview));
789 /* nudge stuff */
791 nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
792 nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
794 ARDOUR_UI::instance()->tooltips().set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
795 ARDOUR_UI::instance()->tooltips().set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
797 nudge_forward_button.set_name ("TransportButton");
798 nudge_backward_button.set_name ("TransportButton");
800 fade_context_menu.set_name ("ArdourContextMenu");
802 /* icons, titles, WM stuff */
804 list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
805 Glib::RefPtr<Gdk::Pixbuf> icon;
807 if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
808 window_icons.push_back (icon);
810 if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
811 window_icons.push_back (icon);
813 if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
814 window_icons.push_back (icon);
816 if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
817 window_icons.push_back (icon);
819 if (!window_icons.empty()) {
820 set_icon_list (window_icons);
821 set_default_icon_list (window_icons);
824 WindowTitle title(Glib::get_application_name());
825 title += _("Editor");
826 set_title (title.get_string());
827 set_wmclass (X_("ardour_editor"), PROGRAM_NAME);
829 add (vpacker);
830 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
832 signal_configure_event().connect (mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
833 signal_delete_event().connect (mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
835 /* allow external control surfaces/protocols to do various things */
837 ControlProtocol::ZoomToSession.connect (mem_fun (*this, &Editor::temporal_zoom_session));
838 ControlProtocol::ZoomIn.connect (bind (mem_fun (*this, &Editor::temporal_zoom_step), false));
839 ControlProtocol::ZoomOut.connect (bind (mem_fun (*this, &Editor::temporal_zoom_step), true));
840 ControlProtocol::ScrollTimeline.connect (mem_fun (*this, &Editor::control_scroll));
841 BasicUI::AccessAction.connect (mem_fun (*this, &Editor::access_action));
843 Config->ParameterChanged.connect (mem_fun (*this, &Editor::parameter_changed));
844 Route::SyncOrderKeys.connect (mem_fun (*this, &Editor::sync_order_keys));
846 constructed = true;
847 instant_save ();
850 Editor::~Editor()
852 /* <CMT Additions> */
853 if(image_socket_listener)
855 if(image_socket_listener->is_connected())
857 image_socket_listener->close_connection() ;
860 delete image_socket_listener ;
861 image_socket_listener = 0 ;
863 /* </CMT Additions> */
865 if (track_canvas) {
866 delete track_canvas;
867 track_canvas = 0;
871 void
872 Editor::add_toplevel_controls (Container& cont)
874 vpacker.pack_start (cont, false, false);
875 cont.show_all ();
878 void
879 Editor::catch_vanishing_regionview (RegionView *rv)
881 /* note: the selection will take care of the vanishing
882 audioregionview by itself.
885 if (rv->get_canvas_group() == drag_info.item) {
886 end_grab (drag_info.item, 0);
889 if (clicked_regionview == rv) {
890 clicked_regionview = 0;
893 if (entered_regionview == rv) {
894 set_entered_regionview (0);
898 void
899 Editor::set_entered_regionview (RegionView* rv)
901 if (rv == entered_regionview) {
902 return;
905 if (entered_regionview) {
906 entered_regionview->exited ();
909 if ((entered_regionview = rv) != 0) {
910 entered_regionview->entered ();
914 void
915 Editor::set_entered_track (TimeAxisView* tav)
917 if (entered_track) {
918 entered_track->exited ();
921 if ((entered_track = tav) != 0) {
922 entered_track->entered ();
926 void
927 Editor::show_window ()
929 if (! is_visible ()) {
930 show_all ();
932 /* now reset all audio_time_axis heights, because widgets might need
933 to be re-hidden
936 TimeAxisView *tv;
938 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
939 tv = (static_cast<TimeAxisView*>(*i));
940 tv->reset_height ();
943 present ();
946 void
947 Editor::instant_save ()
949 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
950 return;
953 if (session) {
954 session->add_instant_xml(get_state(), session->path());
955 } else {
956 Config->add_instant_xml(get_state(), get_user_ardour_path());
960 void
961 Editor::edit_point_clock_changed()
963 if (_dragging_edit_point) {
964 return;
967 if (selection->markers.empty()) {
968 return;
971 bool ignored;
972 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
974 if (!loc) {
975 return;
978 loc->move_to (edit_point_clock.current_time());
981 void
982 Editor::zoom_adjustment_changed ()
984 if (session == 0) {
985 return;
988 double fpu = zoom_range_clock.current_duration() / canvas_width;
990 if (fpu < 1.0) {
991 fpu = 1.0;
992 zoom_range_clock.set ((nframes64_t) floor (fpu * canvas_width));
993 } else if (fpu > session->current_end_frame() / canvas_width) {
994 fpu = session->current_end_frame() / canvas_width;
995 zoom_range_clock.set ((nframes64_t) floor (fpu * canvas_width));
998 temporal_zoom (fpu);
1001 void
1002 Editor::control_scroll (float fraction)
1004 ENSURE_GUI_THREAD(bind (mem_fun (*this, &Editor::control_scroll), fraction));
1006 if (!session) {
1007 return;
1010 double step = fraction * current_page_frames();
1013 _control_scroll_target is an optional<T>
1015 it acts like a pointer to an nframes64_t, with
1016 a operator conversion to boolean to check
1017 that it has a value could possibly use
1018 playhead_cursor->current_frame to store the
1019 value and a boolean in the class to know
1020 when it's out of date
1023 if (!_control_scroll_target) {
1024 _control_scroll_target = session->transport_frame();
1025 _dragging_playhead = true;
1028 if ((fraction < 0.0f) && (*_control_scroll_target < (nframes64_t) fabs(step))) {
1029 *_control_scroll_target = 0;
1030 } else if ((fraction > 0.0f) && (max_frames - *_control_scroll_target < step)) {
1031 *_control_scroll_target = max_frames - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
1032 } else {
1033 *_control_scroll_target += (nframes64_t) floor (step);
1036 /* move visuals, we'll catch up with it later */
1038 playhead_cursor->set_position (*_control_scroll_target);
1039 UpdateAllTransportClocks (*_control_scroll_target);
1041 if (*_control_scroll_target > (current_page_frames() / 2)) {
1042 /* try to center PH in window */
1043 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
1044 } else {
1045 reset_x_origin (0);
1049 Now we do a timeout to actually bring the session to the right place
1050 according to the playhead. This is to avoid reading disk buffers on every
1051 call to control_scroll, which is driven by ScrollTimeline and therefore
1052 probably by a control surface wheel which can generate lots of events.
1054 /* cancel the existing timeout */
1056 control_scroll_connection.disconnect ();
1058 /* add the next timeout */
1060 control_scroll_connection = Glib::signal_timeout().connect (bind (mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
1063 bool
1064 Editor::deferred_control_scroll (nframes64_t target)
1066 session->request_locate (*_control_scroll_target, session->transport_rolling());
1067 // reset for next stream
1068 _control_scroll_target = boost::none;
1069 _dragging_playhead = false;
1070 return false;
1073 void
1074 Editor::access_action (std::string action_group, std::string action_item)
1076 if (!session) {
1077 return;
1080 ENSURE_GUI_THREAD (bind (mem_fun (*this, &Editor::access_action), action_group, action_item));
1082 RefPtr<Action> act;
1083 act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
1085 if (act) {
1086 act->activate();
1092 void
1093 Editor::on_realize ()
1095 Window::on_realize ();
1096 Realized ();
1099 void
1100 Editor::start_scrolling ()
1102 scroll_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect
1103 (mem_fun(*this, &Editor::update_current_screen));
1107 void
1108 Editor::stop_scrolling ()
1110 scroll_connection.disconnect ();
1113 void
1114 Editor::map_position_change (nframes64_t frame)
1116 ENSURE_GUI_THREAD (bind (mem_fun(*this, &Editor::map_position_change), frame));
1118 if (session == 0 || !_follow_playhead) {
1119 return;
1122 center_screen (frame);
1123 playhead_cursor->set_position (frame);
1126 void
1127 Editor::center_screen (nframes64_t frame)
1129 double page = canvas_width * frames_per_unit;
1131 /* if we're off the page, then scroll.
1134 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
1135 center_screen_internal (frame, page);
1139 void
1140 Editor::center_screen_internal (nframes64_t frame, float page)
1142 page /= 2;
1144 if (frame > page) {
1145 frame -= (nframes64_t) page;
1146 } else {
1147 frame = 0;
1150 reset_x_origin (frame);
1153 void
1154 Editor::handle_new_duration ()
1156 if (!session) {
1157 return;
1160 ENSURE_GUI_THREAD (mem_fun (*this, &Editor::handle_new_duration));
1162 nframes64_t new_end = session->current_end_frame() + (nframes64_t) floorf (current_page_frames() * 0.10f);
1164 horizontal_adjustment.set_upper (new_end / frames_per_unit);
1165 horizontal_adjustment.set_page_size (current_page_frames()/frames_per_unit);
1167 if (horizontal_adjustment.get_value() + canvas_width > horizontal_adjustment.get_upper()) {
1168 horizontal_adjustment.set_value (horizontal_adjustment.get_upper() - canvas_width);
1170 //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
1173 void
1174 Editor::update_title_s (const string & snap_name)
1176 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::update_title_s), snap_name));
1178 update_title ();
1181 void
1182 Editor::update_title ()
1184 ENSURE_GUI_THREAD (mem_fun(*this, &Editor::update_title));
1186 if (session) {
1187 bool dirty = session->dirty();
1189 string session_name;
1191 if (session->snap_name() != session->name()) {
1192 session_name = session->snap_name();
1193 } else {
1194 session_name = session->name();
1197 if (dirty) {
1198 session_name = "*" + session_name;
1201 WindowTitle title(session_name);
1202 title += Glib::get_application_name();
1203 set_title (title.get_string());
1207 void
1208 Editor::connect_to_session (Session *t)
1210 session = t;
1212 /* there are never any selected regions at startup */
1214 sensitize_the_right_region_actions (false);
1216 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1217 set_state (*node);
1219 /* catch up with the playhead */
1221 session->request_locate (playhead_cursor->current_frame);
1223 update_title ();
1225 session->GoingAway.connect (mem_fun(*this, &Editor::session_going_away));
1226 session->history().Changed.connect (mem_fun (*this, &Editor::history_changed));
1228 /* These signals can all be emitted by a non-GUI thread. Therefore the
1229 handlers for them must not attempt to directly interact with the GUI,
1230 but use Gtkmm2ext::UI::instance()->call_slot();
1233 session_connections.push_back (session->TransportStateChange.connect (mem_fun(*this, &Editor::map_transport_state)));
1234 session_connections.push_back (session->PositionChanged.connect (mem_fun(*this, &Editor::map_position_change)));
1235 session_connections.push_back (session->RouteAdded.connect (mem_fun(*this, &Editor::handle_new_route)));
1236 session_connections.push_back (session->AudioRegionsAdded.connect (mem_fun(*this, &Editor::handle_new_audio_regions)));
1237 session_connections.push_back (session->AudioRegionRemoved.connect (mem_fun(*this, &Editor::handle_audio_region_removed)));
1238 session_connections.push_back (session->DurationChanged.connect (mem_fun(*this, &Editor::handle_new_duration)));
1239 session_connections.push_back (session->edit_group_added.connect (mem_fun(*this, &Editor::add_edit_group)));
1240 session_connections.push_back (session->edit_group_removed.connect (mem_fun(*this, &Editor::edit_groups_changed)));
1241 session_connections.push_back (session->NamedSelectionAdded.connect (mem_fun(*this, &Editor::handle_new_named_selection)));
1242 session_connections.push_back (session->NamedSelectionRemoved.connect (mem_fun(*this, &Editor::handle_new_named_selection)));
1243 session_connections.push_back (session->DirtyChanged.connect (mem_fun(*this, &Editor::update_title)));
1244 session_connections.push_back (session->StateSaved.connect (mem_fun(*this, &Editor::update_title_s)));
1245 session_connections.push_back (session->AskAboutPlaylistDeletion.connect (mem_fun(*this, &Editor::playlist_deletion_dialog)));
1246 session_connections.push_back (session->RegionHiddenChange.connect (mem_fun(*this, &Editor::region_hidden)));
1248 session_connections.push_back (session->SMPTEOffsetChanged.connect (mem_fun(*this, &Editor::update_just_smpte)));
1250 session_connections.push_back (session->tempo_map().StateChanged.connect (mem_fun(*this, &Editor::tempo_map_changed)));
1252 edit_groups_changed ();
1254 edit_point_clock.set_mode(AudioClock::BBT);
1255 edit_point_clock.set_session (session);
1256 zoom_range_clock.set_session (session);
1257 _playlist_selector->set_session (session);
1258 nudge_clock.set_session (session);
1259 if (Profile->get_sae()) {
1260 BBT_Time bbt;
1261 bbt.bars = 0;
1262 bbt.beats = 0;
1263 bbt.ticks = 120;
1264 nframes_t pos = session->tempo_map().bbt_duration_at (0, bbt, 1);
1265 nudge_clock.set_mode(AudioClock::BBT);
1266 nudge_clock.set (pos, true, 0, AudioClock::BBT);
1268 } else {
1269 nudge_clock.set (session->frame_rate() * 5, true, 0, AudioClock::SMPTE); // default of 5 seconds
1272 playhead_cursor->canvas_item.show ();
1274 if (rhythm_ferret) {
1275 rhythm_ferret->set_session (session);
1278 #ifdef FFT_ANALYSIS
1279 if (analysis_window != 0)
1280 analysis_window->set_session (session);
1281 #endif
1283 Location* loc = session->locations()->auto_loop_location();
1284 if (loc == 0) {
1285 loc = new Location (0, session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1286 if (loc->start() == loc->end()) {
1287 loc->set_end (loc->start() + 1);
1289 session->locations()->add (loc, false);
1290 session->set_auto_loop_location (loc);
1291 } else {
1292 // force name
1293 loc->set_name (_("Loop"));
1296 loc = session->locations()->auto_punch_location();
1297 if (loc == 0) {
1298 loc = new Location (0, session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1299 if (loc->start() == loc->end()) {
1300 loc->set_end (loc->start() + 1);
1302 session->locations()->add (loc, false);
1303 session->set_auto_punch_location (loc);
1304 } else {
1305 // force name
1306 loc->set_name (_("Punch"));
1309 Config->map_parameters (mem_fun (*this, &Editor::parameter_changed));
1311 session->StateSaved.connect (mem_fun(*this, &Editor::session_state_saved));
1313 refresh_location_display ();
1314 session->locations()->added.connect (mem_fun(*this, &Editor::add_new_location));
1315 session->locations()->removed.connect (mem_fun(*this, &Editor::location_gone));
1316 session->locations()->changed.connect (mem_fun(*this, &Editor::refresh_location_display));
1317 session->locations()->StateChanged.connect (mem_fun(*this, &Editor::refresh_location_display_s));
1318 session->locations()->end_location()->changed.connect (mem_fun(*this, &Editor::end_location_changed));
1320 if (sfbrowser) {
1321 sfbrowser->set_session (session);
1324 handle_new_duration ();
1326 redisplay_regions ();
1327 redisplay_named_selections ();
1328 redisplay_snapshots ();
1330 restore_ruler_visibility ();
1331 //tempo_map_changed (Change (0));
1332 session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1334 initial_route_list_display ();
1336 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1337 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1340 start_scrolling ();
1342 /* don't show master bus in a new session */
1344 if (ARDOUR_UI::instance()->session_is_new ()) {
1346 TreeModel::Children rows = route_display_model->children();
1347 TreeModel::Children::iterator i;
1349 no_route_list_redisplay = true;
1351 for (i = rows.begin(); i != rows.end(); ++i) {
1352 TimeAxisView *tv = (*i)[route_display_columns.tv];
1353 AudioTimeAxisView *atv;
1355 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
1356 if (atv->route()->master()) {
1357 route_list_display.get_selection()->unselect (i);
1362 no_route_list_redisplay = false;
1363 redisplay_route_list ();
1366 switch (snap_type) {
1367 case SnapToRegionStart:
1368 case SnapToRegionEnd:
1369 case SnapToRegionSync:
1370 case SnapToRegionBoundary:
1371 build_region_boundary_cache ();
1372 break;
1374 default:
1375 break;
1378 /* register for undo history */
1380 session->register_with_memento_command_factory(_id, this);
1382 start_updating ();
1385 void
1386 Editor::build_cursors ()
1388 using namespace Gdk;
1390 Gdk::Color mbg ("#000000" ); /* Black */
1391 Gdk::Color mfg ("#0000ff" ); /* Blue. */
1394 RefPtr<Bitmap> source, mask;
1395 source = Bitmap::create (mag_bits, mag_width, mag_height);
1396 mask = Bitmap::create (magmask_bits, mag_width, mag_height);
1397 zoom_cursor = new Gdk::Cursor (source, mask, mfg, mbg, mag_x_hot, mag_y_hot);
1400 Gdk::Color fbg ("#ffffff" );
1401 Gdk::Color ffg ("#000000" );
1404 RefPtr<Bitmap> source, mask;
1406 source = Bitmap::create (fader_cursor_bits, fader_cursor_width, fader_cursor_height);
1407 mask = Bitmap::create (fader_cursor_mask_bits, fader_cursor_width, fader_cursor_height);
1408 fader_cursor = new Gdk::Cursor (source, mask, ffg, fbg, fader_cursor_x_hot, fader_cursor_y_hot);
1412 RefPtr<Bitmap> source, mask;
1413 source = Bitmap::create (speaker_cursor_bits, speaker_cursor_width, speaker_cursor_height);
1414 mask = Bitmap::create (speaker_cursor_mask_bits, speaker_cursor_width, speaker_cursor_height);
1415 speaker_cursor = new Gdk::Cursor (source, mask, ffg, fbg, speaker_cursor_x_hot, speaker_cursor_y_hot);
1419 RefPtr<Bitmap> bits;
1420 char pix[4] = { 0, 0, 0, 0 };
1421 bits = Bitmap::create (pix, 2, 2);
1422 Gdk::Color c;
1423 transparent_cursor = new Gdk::Cursor (bits, bits, c, c, 0, 0);
1427 grabber_cursor = new Gdk::Cursor (HAND2);
1430 Glib::RefPtr<Gdk::Pixbuf> grabber_edit_point_pixbuf (::get_icon ("grabber_edit_point"));
1431 grabber_edit_point_cursor = new Gdk::Cursor (Gdk::Display::get_default(), grabber_edit_point_pixbuf, 5, 17);
1434 cross_hair_cursor = new Gdk::Cursor (CROSSHAIR);
1435 trimmer_cursor = new Gdk::Cursor (SB_H_DOUBLE_ARROW);
1436 selector_cursor = new Gdk::Cursor (XTERM);
1437 time_fx_cursor = new Gdk::Cursor (SIZING);
1438 wait_cursor = new Gdk::Cursor (WATCH);
1439 timebar_cursor = new Gdk::Cursor(LEFT_PTR);
1442 void
1443 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1445 using namespace Menu_Helpers;
1446 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1448 if (arv == 0) {
1449 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1450 /*NOTREACHED*/
1453 MenuList& items (fade_context_menu.items());
1455 items.clear ();
1457 switch (item_type) {
1458 case FadeInItem:
1459 case FadeInHandleItem:
1460 if (arv->audio_region()->fade_in_active()) {
1461 items.push_back (MenuElem (_("Deactivate"), bind (mem_fun (*this, &Editor::set_fade_in_active), false)));
1462 } else {
1463 items.push_back (MenuElem (_("Activate"), bind (mem_fun (*this, &Editor::set_fade_in_active), true)));
1466 items.push_back (SeparatorElem());
1468 if (Profile->get_sae()) {
1469 items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear)));
1470 items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast)));
1471 } else {
1472 items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear)));
1473 items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast)));
1474 items.push_back (MenuElem (_("Slow"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogB)));
1475 items.push_back (MenuElem (_("Fast"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogA)));
1476 items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Slow)));
1478 break;
1480 case FadeOutItem:
1481 case FadeOutHandleItem:
1482 if (arv->audio_region()->fade_out_active()) {
1483 items.push_back (MenuElem (_("Deactivate"), bind (mem_fun (*this, &Editor::set_fade_out_active), false)));
1484 } else {
1485 items.push_back (MenuElem (_("Activate"), bind (mem_fun (*this, &Editor::set_fade_out_active), true)));
1488 items.push_back (SeparatorElem());
1490 if (Profile->get_sae()) {
1491 items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear)));
1492 items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow)));
1493 } else {
1494 items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear)));
1495 items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow)));
1496 items.push_back (MenuElem (_("Slow"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogA)));
1497 items.push_back (MenuElem (_("Fast"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogB)));
1498 items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Fast)));
1500 break;
1502 default:
1503 fatal << _("programming error: ")
1504 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1505 << endmsg;
1506 /*NOTREACHED*/
1509 fade_context_menu.popup (button, time);
1512 void
1513 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection, nframes64_t frame)
1515 using namespace Menu_Helpers;
1516 Menu* (Editor::*build_menu_function)(nframes64_t);
1517 Menu *menu;
1519 switch (item_type) {
1520 case RegionItem:
1521 case RegionViewName:
1522 case RegionViewNameHighlight:
1523 if (with_selection) {
1524 build_menu_function = &Editor::build_track_selection_context_menu;
1525 } else {
1526 build_menu_function = &Editor::build_track_region_context_menu;
1528 break;
1530 case SelectionItem:
1531 if (with_selection) {
1532 build_menu_function = &Editor::build_track_selection_context_menu;
1533 } else {
1534 build_menu_function = &Editor::build_track_context_menu;
1536 break;
1538 case CrossfadeViewItem:
1539 build_menu_function = &Editor::build_track_crossfade_context_menu;
1540 break;
1542 case StreamItem:
1543 if (clicked_audio_trackview->get_diskstream()) {
1544 build_menu_function = &Editor::build_track_context_menu;
1545 } else {
1546 build_menu_function = &Editor::build_track_bus_context_menu;
1548 break;
1550 default:
1551 /* probably shouldn't happen but if it does, we don't care */
1552 return;
1555 menu = (this->*build_menu_function)(frame);
1556 menu->set_name ("ArdourContextMenu");
1558 /* now handle specific situations */
1560 switch (item_type) {
1561 case RegionItem:
1562 case RegionViewName:
1563 case RegionViewNameHighlight:
1564 if (!with_selection) {
1565 if (region_edit_menu_split_item) {
1566 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1567 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1568 } else {
1569 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1573 if (region_edit_menu_split_multichannel_item) {
1574 if (clicked_regionview && clicked_regionview->region().n_channels() > 1) {
1575 // GTK2FIX find the action, change its sensitivity
1576 // region_edit_menu_split_multichannel_item->set_sensitive (true);
1577 } else {
1578 // GTK2FIX see above
1579 // region_edit_menu_split_multichannel_item->set_sensitive (false);
1583 break;
1585 case SelectionItem:
1586 break;
1588 case CrossfadeViewItem:
1589 break;
1591 case StreamItem:
1592 break;
1594 default:
1595 /* probably shouldn't happen but if it does, we don't care */
1596 return;
1599 if (item_type != SelectionItem && clicked_audio_trackview && clicked_audio_trackview->audio_track()) {
1601 /* Bounce to disk */
1603 using namespace Menu_Helpers;
1604 MenuList& edit_items = menu->items();
1606 edit_items.push_back (SeparatorElem());
1608 switch (clicked_audio_trackview->audio_track()->freeze_state()) {
1609 case AudioTrack::NoFreeze:
1610 edit_items.push_back (MenuElem (_("Freeze"), mem_fun(*this, &Editor::freeze_route)));
1611 break;
1613 case AudioTrack::Frozen:
1614 edit_items.push_back (MenuElem (_("Unfreeze"), mem_fun(*this, &Editor::unfreeze_route)));
1615 break;
1617 case AudioTrack::UnFrozen:
1618 edit_items.push_back (MenuElem (_("Freeze"), mem_fun(*this, &Editor::freeze_route)));
1619 break;
1624 menu->popup (button, time);
1627 Menu*
1628 Editor::build_track_context_menu (nframes64_t ignored)
1630 using namespace Menu_Helpers;
1632 MenuList& edit_items = track_context_menu.items();
1633 edit_items.clear();
1635 add_dstream_context_items (edit_items);
1636 return &track_context_menu;
1639 Menu*
1640 Editor::build_track_bus_context_menu (nframes64_t ignored)
1642 using namespace Menu_Helpers;
1644 MenuList& edit_items = track_context_menu.items();
1645 edit_items.clear();
1647 add_bus_context_items (edit_items);
1648 return &track_context_menu;
1651 Menu*
1652 Editor::build_track_region_context_menu (nframes64_t frame)
1654 using namespace Menu_Helpers;
1655 MenuList& edit_items = track_region_context_menu.items();
1656 edit_items.clear();
1658 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_trackview);
1660 if (atv) {
1661 boost::shared_ptr<Diskstream> ds;
1662 boost::shared_ptr<Playlist> pl;
1664 if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()))) {
1666 nframes64_t frame_pos = (nframes64_t) floor ((double)frame * ds->speed());
1667 uint32_t regions_at = pl->count_regions_at (frame_pos);
1669 if (selection->regions.size() > 1) {
1670 // there's already a multiple selection: just add a
1671 // single region context menu that will act on all
1672 // selected regions
1673 boost::shared_ptr<Region> dummy_region; // = NULL
1674 add_region_context_items (atv->audio_view(), dummy_region, edit_items, frame_pos, regions_at > 1);
1675 } else {
1676 // Find the topmost region and make the context menu for it
1677 boost::shared_ptr<Region> top_region = pl->top_region_at (frame_pos);
1678 add_region_context_items (atv->audio_view(), top_region, edit_items, frame_pos, regions_at > 1);
1683 add_dstream_context_items (edit_items);
1685 return &track_region_context_menu;
1688 Menu*
1689 Editor::build_track_crossfade_context_menu (nframes64_t frame)
1691 using namespace Menu_Helpers;
1692 MenuList& edit_items = track_crossfade_context_menu.items();
1693 edit_items.clear ();
1695 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_trackview);
1697 if (atv) {
1698 boost::shared_ptr<Diskstream> ds;
1699 boost::shared_ptr<Playlist> pl;
1700 boost::shared_ptr<AudioPlaylist> apl;
1702 if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1704 AudioPlaylist::Crossfades xfades;
1706 apl->crossfades_at (frame, xfades);
1708 bool many = xfades.size() > 1;
1710 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1711 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1714 nframes64_t frame_pos = (nframes64_t) floor ((double)frame * ds->speed());
1715 uint32_t regions_at = pl->count_regions_at (frame_pos);
1717 if (selection->regions.size() > 1) {
1718 // there's already a multiple selection: just add a
1719 // single region context menu that will act on all
1720 // selected regions
1721 boost::shared_ptr<Region> dummy_region; // = NULL
1722 add_region_context_items (atv->audio_view(), dummy_region, edit_items, frame_pos, regions_at > 1); // OR frame ???
1723 } else {
1724 // Find the topmost region and make the context menu for it
1725 boost::shared_ptr<Region> top_region = pl->top_region_at (frame_pos);
1726 add_region_context_items (atv->audio_view(), top_region, edit_items, frame_pos, regions_at > 1); // OR frame ???
1731 add_dstream_context_items (edit_items);
1733 return &track_crossfade_context_menu;
1736 #ifdef FFT_ANALYSIS
1737 void
1738 Editor::analyze_region_selection()
1740 if (analysis_window == 0) {
1741 analysis_window = new AnalysisWindow();
1743 if (session != 0)
1744 analysis_window->set_session(session);
1746 analysis_window->show_all();
1749 analysis_window->set_regionmode();
1750 analysis_window->analyze();
1752 analysis_window->present();
1755 void
1756 Editor::analyze_range_selection()
1758 if (analysis_window == 0) {
1759 analysis_window = new AnalysisWindow();
1761 if (session != 0)
1762 analysis_window->set_session(session);
1764 analysis_window->show_all();
1767 analysis_window->set_rangemode();
1768 analysis_window->analyze();
1770 analysis_window->present();
1772 #endif /* FFT_ANALYSIS */
1776 Menu*
1777 Editor::build_track_selection_context_menu (nframes64_t ignored)
1779 using namespace Menu_Helpers;
1780 MenuList& edit_items = track_selection_context_menu.items();
1781 edit_items.clear ();
1783 add_selection_context_items (edit_items);
1784 // edit_items.push_back (SeparatorElem());
1785 // add_dstream_context_items (edit_items);
1787 return &track_selection_context_menu;
1790 void
1791 Editor::add_crossfade_context_items (AudioStreamView* view, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1793 using namespace Menu_Helpers;
1794 Menu *xfade_menu = manage (new Menu);
1795 MenuList& items = xfade_menu->items();
1796 xfade_menu->set_name ("ArdourContextMenu");
1797 string str;
1799 if (xfade->active()) {
1800 str = _("Mute");
1801 } else {
1802 str = _("Unmute");
1805 items.push_back (MenuElem (str, bind (mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1806 items.push_back (MenuElem (_("Edit"), bind (mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1808 if (xfade->can_follow_overlap()) {
1810 if (xfade->following_overlap()) {
1811 str = _("Convert to short");
1812 } else {
1813 str = _("Convert to full");
1816 items.push_back (MenuElem (str, bind (mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1819 if (many) {
1820 str = xfade->out()->name();
1821 str += "->";
1822 str += xfade->in()->name();
1823 } else {
1824 str = _("Crossfade");
1827 edit_items.push_back (MenuElem (str, *xfade_menu));
1828 edit_items.push_back (SeparatorElem());
1831 void
1832 Editor::xfade_edit_left_region ()
1834 if (clicked_crossfadeview) {
1835 clicked_crossfadeview->left_view.show_region_editor ();
1839 void
1840 Editor::xfade_edit_right_region ()
1842 if (clicked_crossfadeview) {
1843 clicked_crossfadeview->right_view.show_region_editor ();
1847 void
1848 Editor::add_region_context_items (AudioStreamView* sv, boost::shared_ptr<Region> region, Menu_Helpers::MenuList& edit_items,
1849 nframes64_t position, bool multiple_region_at_position)
1851 using namespace Menu_Helpers;
1852 Gtk::MenuItem* foo_item;
1853 Menu *region_menu = manage (new Menu);
1854 MenuList& items = region_menu->items();
1855 region_menu->set_name ("ArdourContextMenu");
1857 boost::shared_ptr<AudioRegion> ar;
1859 if (region) {
1860 ar = boost::dynamic_pointer_cast<AudioRegion> (region);
1862 /* when this particular menu pops up, make the relevant region
1863 become selected.
1866 region_menu->signal_map_event().connect (
1867 bind (
1868 mem_fun(*this, &Editor::set_selected_regionview_from_map_event),
1869 sv,
1870 boost::weak_ptr<Region>(region)
1874 items.push_back (MenuElem (_("Rename"), mem_fun(*this, &Editor::rename_region)));
1875 items.push_back (MenuElem (_("Region Editor"), mem_fun(*this, &Editor::edit_region)));
1878 items.push_back (MenuElem (_("Raise to top layer"), mem_fun(*this, &Editor::raise_region_to_top)));
1879 items.push_back (MenuElem (_("Lower to bottom layer"), mem_fun (*this, &Editor::lower_region_to_bottom)));
1880 items.push_back (SeparatorElem());
1881 items.push_back (MenuElem (_("Define sync point"), mem_fun(*this, &Editor::set_region_sync_from_edit_point)));
1882 if (_edit_point == EditAtMouse) {
1883 items.back ().set_sensitive (false);
1885 items.push_back (MenuElem (_("Remove sync point"), mem_fun(*this, &Editor::remove_region_sync)));
1886 items.push_back (SeparatorElem());
1888 items.push_back (MenuElem (_("Audition"), mem_fun(*this, &Editor::play_selected_region)));
1889 items.push_back (MenuElem (_("Export"), mem_fun(*this, &Editor::export_region)));
1890 items.push_back (MenuElem (_("Bounce"), mem_fun(*this, &Editor::bounce_region_selection)));
1892 #ifdef FFT_ANALYSIS
1893 items.push_back (MenuElem (_("Spectral Analysis"), mem_fun(*this, &Editor::analyze_region_selection)));
1894 #endif
1896 items.push_back (SeparatorElem());
1898 sigc::connection fooc;
1899 boost::shared_ptr<Region> region_to_check;
1901 if (region) {
1902 region_to_check = region;
1903 } else {
1904 region_to_check = selection->regions.front()->region();
1907 items.push_back (CheckMenuElem (_("Lock")));
1908 CheckMenuItem* region_lock_item = static_cast<CheckMenuItem*>(&items.back());
1909 if (region_to_check->locked()) {
1910 region_lock_item->set_active();
1912 region_lock_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_lock));
1914 items.push_back (CheckMenuElem (_("Glue to Bars&Beats")));
1915 CheckMenuItem* bbt_glue_item = static_cast<CheckMenuItem*>(&items.back());
1917 switch (region_to_check->positional_lock_style()) {
1918 case Region::MusicTime:
1919 bbt_glue_item->set_active (true);
1920 break;
1921 default:
1922 bbt_glue_item->set_active (false);
1923 break;
1926 bbt_glue_item->signal_activate().connect (bind (mem_fun (*this, &Editor::set_region_lock_style), Region::MusicTime));
1928 items.push_back (CheckMenuElem (_("Mute")));
1929 CheckMenuItem* region_mute_item = static_cast<CheckMenuItem*>(&items.back());
1930 fooc = region_mute_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_mute));
1931 if (region_to_check->muted()) {
1932 fooc.block (true);
1933 region_mute_item->set_active();
1934 fooc.block (false);
1937 items.push_back (MenuElem (_("Transpose"), mem_fun(*this, &Editor::pitch_shift_regions)));
1939 if (!Profile->get_sae()) {
1940 items.push_back (CheckMenuElem (_("Opaque")));
1941 CheckMenuItem* region_opaque_item = static_cast<CheckMenuItem*>(&items.back());
1942 fooc = region_opaque_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_opaque));
1943 if (region_to_check->opaque()) {
1944 fooc.block (true);
1945 region_opaque_item->set_active();
1946 fooc.block (false);
1950 items.push_back (CheckMenuElem (_("Original position"), mem_fun(*this, &Editor::naturalize)));
1951 if (region_to_check->at_natural_position()) {
1952 items.back().set_sensitive (false);
1955 items.push_back (SeparatorElem());
1957 if (ar) {
1959 RegionView* rv = sv->find_view (ar);
1960 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1962 if (!Profile->get_sae()) {
1963 items.push_back (MenuElem (_("Reset Envelope"), mem_fun(*this, &Editor::reset_region_gain_envelopes)));
1965 items.push_back (CheckMenuElem (_("Envelope Visible")));
1966 CheckMenuItem* region_envelope_visible_item = static_cast<CheckMenuItem*> (&items.back());
1967 fooc = region_envelope_visible_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_gain_envelope_visibility));
1968 if (arv->envelope_visible()) {
1969 fooc.block (true);
1970 region_envelope_visible_item->set_active (true);
1971 fooc.block (false);
1974 items.push_back (CheckMenuElem (_("Envelope Active")));
1975 CheckMenuItem* region_envelope_active_item = static_cast<CheckMenuItem*> (&items.back());
1976 fooc = region_envelope_active_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_gain_envelope_active));
1978 if (ar->envelope_active()) {
1979 fooc.block (true);
1980 region_envelope_active_item->set_active (true);
1981 fooc.block (false);
1984 items.push_back (SeparatorElem());
1987 if (ar->scale_amplitude() != 1.0f) {
1988 items.push_back (MenuElem (_("DeNormalize"), mem_fun(*this, &Editor::denormalize_region)));
1989 } else {
1990 items.push_back (MenuElem (_("Normalize"), mem_fun(*this, &Editor::normalize_region)));
1994 items.push_back (MenuElem (_("Reverse"), mem_fun(*this, &Editor::reverse_region)));
1995 items.push_back (SeparatorElem());
1997 /* range related stuff */
1999 items.push_back (MenuElem (_("Add Single Range"), mem_fun (*this, &Editor::add_location_from_audio_region)));
2000 items.push_back (MenuElem (_("Add Range Markers"), mem_fun (*this, &Editor::add_locations_from_audio_region)));
2001 if (selection->regions.size() < 2) {
2002 items.back().set_sensitive (false);
2005 items.push_back (MenuElem (_("Set Range Selection"), mem_fun (*this, &Editor::set_selection_from_audio_region)));
2006 items.push_back (SeparatorElem());
2008 /* Nudge region */
2010 Menu *nudge_menu = manage (new Menu());
2011 MenuList& nudge_items = nudge_menu->items();
2012 nudge_menu->set_name ("ArdourContextMenu");
2014 nudge_items.push_back (MenuElem (_("Nudge fwd"), (bind (mem_fun(*this, &Editor::nudge_forward), false, false))));
2015 nudge_items.push_back (MenuElem (_("Nudge bwd"), (bind (mem_fun(*this, &Editor::nudge_backward), false, false))));
2016 nudge_items.push_back (MenuElem (_("Nudge fwd by capture offset"), (mem_fun(*this, &Editor::nudge_forward_capture_offset))));
2017 nudge_items.push_back (MenuElem (_("Nudge bwd by capture offset"), (mem_fun(*this, &Editor::nudge_backward_capture_offset))));
2019 items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2020 items.push_back (SeparatorElem());
2022 Menu *trim_menu = manage (new Menu);
2023 MenuList& trim_items = trim_menu->items();
2024 trim_menu->set_name ("ArdourContextMenu");
2026 trim_items.push_back (MenuElem (_("Start to edit point"), mem_fun(*this, &Editor::trim_region_from_edit_point)));
2027 foo_item = &trim_items.back();
2028 if (_edit_point == EditAtMouse) {
2029 foo_item->set_sensitive (false);
2031 trim_items.push_back (MenuElem (_("Edit point to end"), mem_fun(*this, &Editor::trim_region_to_edit_point)));
2032 foo_item = &trim_items.back();
2033 if (_edit_point == EditAtMouse) {
2034 foo_item->set_sensitive (false);
2036 trim_items.push_back (MenuElem (_("Trim To Loop"), mem_fun(*this, &Editor::trim_region_to_loop)));
2037 trim_items.push_back (MenuElem (_("Trim To Punch"), mem_fun(*this, &Editor::trim_region_to_punch)));
2039 items.push_back (MenuElem (_("Trim"), *trim_menu));
2040 items.push_back (SeparatorElem());
2042 items.push_back (MenuElem (_("Split"), (mem_fun(*this, &Editor::split_region))));
2043 region_edit_menu_split_item = &items.back();
2045 if (_edit_point == EditAtMouse) {
2046 region_edit_menu_split_item->set_sensitive (false);
2049 items.push_back (MenuElem (_("Make mono regions"), (mem_fun(*this, &Editor::split_multichannel_region))));
2050 region_edit_menu_split_multichannel_item = &items.back();
2052 items.push_back (MenuElem (_("Duplicate"), (bind (mem_fun(*this, &Editor::duplicate_dialog), false))));
2053 items.push_back (MenuElem (_("Multi-Duplicate"), (bind (mem_fun(*this, &Editor::duplicate_dialog), true))));
2054 items.push_back (MenuElem (_("Fill Track"), (mem_fun(*this, &Editor::region_fill_track))));
2055 items.push_back (SeparatorElem());
2056 items.push_back (MenuElem (_("Remove"), mem_fun(*this, &Editor::remove_region)));
2058 /* OK, stick the region submenu at the top of the list, and then add
2059 the standard items.
2062 /* we have to hack up the region name because "_" has a special
2063 meaning for menu titles.
2066 string::size_type pos = 0;
2067 string menu_item_name = (region) ? region->name() : _("Selected regions");
2069 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
2070 menu_item_name.replace (pos, 1, "__");
2071 pos += 2;
2074 edit_items.push_back (MenuElem (menu_item_name, *region_menu));
2075 if (multiple_region_at_position && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
2076 edit_items.push_back (MenuElem (_("Choose top region"), (bind (mem_fun(*this, &Editor::change_region_layering_order), position))));
2078 edit_items.push_back (SeparatorElem());
2081 void
2082 Editor::add_selection_context_items (Menu_Helpers::MenuList& items)
2084 using namespace Menu_Helpers;
2086 items.push_back (MenuElem (_("Play range"), mem_fun(*this, &Editor::play_selection)));
2087 items.push_back (MenuElem (_("Loop range"), bind (mem_fun(*this, &Editor::set_loop_from_selection), true)));
2089 #ifdef FFT_ANALYSIS
2090 items.push_back (SeparatorElem());
2091 items.push_back (MenuElem (_("Spectral Analysis"), mem_fun(*this, &Editor::analyze_range_selection)));
2092 #endif
2094 items.push_back (SeparatorElem());
2095 items.push_back (MenuElem (_("Extend Range to End of Region"), bind (mem_fun(*this, &Editor::extend_selection_to_end_of_region), false)));
2096 items.push_back (MenuElem (_("Extend Range to Start of Region"), bind (mem_fun(*this, &Editor::extend_selection_to_start_of_region), false)));
2098 items.push_back (SeparatorElem());
2099 items.push_back (MenuElem (_("Convert to region in-place"), mem_fun(*this, &Editor::separate_region_from_selection)));
2100 items.push_back (MenuElem (_("Convert to region in region list"), mem_fun(*this, &Editor::new_region_from_selection)));
2102 items.push_back (SeparatorElem());
2103 items.push_back (MenuElem (_("Select all in range"), mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
2105 items.push_back (SeparatorElem());
2106 items.push_back (MenuElem (_("Set loop from selection"), bind (mem_fun(*this, &Editor::set_loop_from_selection), false)));
2107 items.push_back (MenuElem (_("Set punch from selection"), mem_fun(*this, &Editor::set_punch_from_selection)));
2109 items.push_back (SeparatorElem());
2110 items.push_back (MenuElem (_("Add Range Markers"), mem_fun (*this, &Editor::add_location_from_selection)));
2111 items.push_back (SeparatorElem());
2112 items.push_back (MenuElem (_("Crop region to range"), mem_fun(*this, &Editor::crop_region_to_selection)));
2113 items.push_back (MenuElem (_("Fill range with region"), mem_fun(*this, &Editor::region_fill_selection)));
2114 items.push_back (MenuElem (_("Duplicate range"), bind (mem_fun(*this, &Editor::duplicate_dialog), false)));
2115 items.push_back (MenuElem (_("Create chunk from range"), mem_fun(*this, &Editor::create_named_selection)));
2116 items.push_back (SeparatorElem());
2117 items.push_back (MenuElem (_("Consolidate range"), bind (mem_fun(*this, &Editor::bounce_range_selection), true, false)));
2118 items.push_back (MenuElem (_("Consolidate range with processing"), bind (mem_fun(*this, &Editor::bounce_range_selection), true, true)));
2119 items.push_back (MenuElem (_("Bounce range to region list"), bind (mem_fun(*this, &Editor::bounce_range_selection), false, false)));
2120 items.push_back (MenuElem (_("Bounce range to region list with processing"), bind (mem_fun(*this, &Editor::bounce_range_selection), false, true)));
2121 items.push_back (MenuElem (_("Export range"), mem_fun(*this, &Editor::export_selection)));
2124 void
2125 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
2127 using namespace Menu_Helpers;
2129 /* Playback */
2131 Menu *play_menu = manage (new Menu);
2132 MenuList& play_items = play_menu->items();
2133 play_menu->set_name ("ArdourContextMenu");
2135 play_items.push_back (MenuElem (_("Play from edit point"), mem_fun(*this, &Editor::play_from_edit_point)));
2136 play_items.push_back (MenuElem (_("Play from start"), mem_fun(*this, &Editor::play_from_start)));
2137 play_items.push_back (MenuElem (_("Play region"), mem_fun(*this, &Editor::play_selected_region)));
2138 play_items.push_back (SeparatorElem());
2139 play_items.push_back (MenuElem (_("Loop Region"), mem_fun(*this, &Editor::loop_selected_region)));
2141 edit_items.push_back (MenuElem (_("Play"), *play_menu));
2143 /* Selection */
2145 Menu *select_menu = manage (new Menu);
2146 MenuList& select_items = select_menu->items();
2147 select_menu->set_name ("ArdourContextMenu");
2149 select_items.push_back (MenuElem (_("Select All in track"), bind (mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2150 select_items.push_back (MenuElem (_("Select All"), bind (mem_fun(*this, &Editor::select_all), Selection::Set)));
2151 select_items.push_back (MenuElem (_("Invert selection in track"), mem_fun(*this, &Editor::invert_selection_in_track)));
2152 select_items.push_back (MenuElem (_("Invert selection"), mem_fun(*this, &Editor::invert_selection)));
2153 select_items.push_back (SeparatorElem());
2154 select_items.push_back (MenuElem (_("Set range to loop range"), mem_fun(*this, &Editor::set_selection_from_loop)));
2155 select_items.push_back (MenuElem (_("Set range to punch range"), mem_fun(*this, &Editor::set_selection_from_punch)));
2156 select_items.push_back (SeparatorElem());
2157 select_items.push_back (MenuElem (_("Select All After Edit Point"), bind (mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
2158 select_items.push_back (MenuElem (_("Select All Before Edit Point"), bind (mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
2159 select_items.push_back (MenuElem (_("Select All After Playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2160 select_items.push_back (MenuElem (_("Select All Before Playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2161 select_items.push_back (MenuElem (_("Select All Between Playhead & Edit Point"), bind (mem_fun(*this, &Editor::select_all_selectables_between), false)));
2162 select_items.push_back (MenuElem (_("Select All Within Playhead & Edit Point"), bind (mem_fun(*this, &Editor::select_all_selectables_between), true)));
2163 select_items.push_back (MenuElem (_("Select Range Between Playhead & Edit Point"), mem_fun(*this, &Editor::select_range_between)));
2165 select_items.push_back (SeparatorElem());
2167 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2169 /* Cut-n-Paste */
2171 Menu *cutnpaste_menu = manage (new Menu);
2172 MenuList& cutnpaste_items = cutnpaste_menu->items();
2173 cutnpaste_menu->set_name ("ArdourContextMenu");
2175 cutnpaste_items.push_back (MenuElem (_("Cut"), mem_fun(*this, &Editor::cut)));
2176 cutnpaste_items.push_back (MenuElem (_("Copy"), mem_fun(*this, &Editor::copy)));
2177 cutnpaste_items.push_back (MenuElem (_("Paste"), bind (mem_fun(*this, &Editor::paste), 1.0f)));
2179 cutnpaste_items.push_back (SeparatorElem());
2181 cutnpaste_items.push_back (MenuElem (_("Align"), bind (mem_fun(*this, &Editor::align), ARDOUR::SyncPoint)));
2182 cutnpaste_items.push_back (MenuElem (_("Align Relative"), bind (mem_fun(*this, &Editor::align_relative), ARDOUR::SyncPoint)));
2184 cutnpaste_items.push_back (SeparatorElem());
2186 cutnpaste_items.push_back (MenuElem (_("Insert chunk"), bind (mem_fun(*this, &Editor::paste_named_selection), 1.0f)));
2188 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
2190 /* Adding new material */
2192 edit_items.push_back (SeparatorElem());
2193 edit_items.push_back (MenuElem (_("Insert Selected Region"), bind (mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
2194 edit_items.push_back (MenuElem (_("Insert Existing Audio"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
2196 /* Nudge track */
2198 Menu *nudge_menu = manage (new Menu());
2199 MenuList& nudge_items = nudge_menu->items();
2200 nudge_menu->set_name ("ArdourContextMenu");
2202 edit_items.push_back (SeparatorElem());
2203 nudge_items.push_back (MenuElem (_("Nudge entire track fwd"), (bind (mem_fun(*this, &Editor::nudge_track), false, true))));
2204 nudge_items.push_back (MenuElem (_("Nudge track after edit point fwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, true))));
2205 nudge_items.push_back (MenuElem (_("Nudge entire track bwd"), (bind (mem_fun(*this, &Editor::nudge_track), false, false))));
2206 nudge_items.push_back (MenuElem (_("Nudge track after edit point bwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, false))));
2208 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2211 void
2212 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
2214 using namespace Menu_Helpers;
2216 /* Playback */
2218 Menu *play_menu = manage (new Menu);
2219 MenuList& play_items = play_menu->items();
2220 play_menu->set_name ("ArdourContextMenu");
2222 play_items.push_back (MenuElem (_("Play from edit point"), mem_fun(*this, &Editor::play_from_edit_point)));
2223 play_items.push_back (MenuElem (_("Play from start"), mem_fun(*this, &Editor::play_from_start)));
2224 edit_items.push_back (MenuElem (_("Play"), *play_menu));
2226 /* Selection */
2228 Menu *select_menu = manage (new Menu);
2229 MenuList& select_items = select_menu->items();
2230 select_menu->set_name ("ArdourContextMenu");
2232 select_items.push_back (MenuElem (_("Select All in track"), bind (mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2233 select_items.push_back (MenuElem (_("Select All"), bind (mem_fun(*this, &Editor::select_all), Selection::Set)));
2234 select_items.push_back (MenuElem (_("Invert selection in track"), mem_fun(*this, &Editor::invert_selection_in_track)));
2235 select_items.push_back (MenuElem (_("Invert selection"), mem_fun(*this, &Editor::invert_selection)));
2236 select_items.push_back (SeparatorElem());
2237 select_items.push_back (MenuElem (_("Select all after edit point"), bind (mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
2238 select_items.push_back (MenuElem (_("Select all before edit point"), bind (mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
2239 select_items.push_back (MenuElem (_("Select all after playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2240 select_items.push_back (MenuElem (_("Select all before playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2242 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2244 /* Cut-n-Paste */
2246 Menu *cutnpaste_menu = manage (new Menu);
2247 MenuList& cutnpaste_items = cutnpaste_menu->items();
2248 cutnpaste_menu->set_name ("ArdourContextMenu");
2250 cutnpaste_items.push_back (MenuElem (_("Cut"), mem_fun(*this, &Editor::cut)));
2251 cutnpaste_items.push_back (MenuElem (_("Copy"), mem_fun(*this, &Editor::copy)));
2252 cutnpaste_items.push_back (MenuElem (_("Paste"), bind (mem_fun(*this, &Editor::paste), 1.0f)));
2254 Menu *nudge_menu = manage (new Menu());
2255 MenuList& nudge_items = nudge_menu->items();
2256 nudge_menu->set_name ("ArdourContextMenu");
2258 edit_items.push_back (SeparatorElem());
2259 nudge_items.push_back (MenuElem (_("Nudge entire track fwd"), (bind (mem_fun(*this, &Editor::nudge_track), false, true))));
2260 nudge_items.push_back (MenuElem (_("Nudge track after edit point fwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, true))));
2261 nudge_items.push_back (MenuElem (_("Nudge entire track bwd"), (bind (mem_fun(*this, &Editor::nudge_track), false, false))));
2262 nudge_items.push_back (MenuElem (_("Nudge track after edit point bwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, false))));
2264 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2267 void
2268 Editor::set_snap_to (SnapType st)
2270 unsigned int snap_ind = (unsigned int)st;
2271 snap_type = st;
2273 if (snap_ind > snap_type_strings.size() - 1) {
2274 snap_ind = 0;
2275 snap_type = (SnapType)snap_ind;
2278 string str = snap_type_strings[snap_ind];
2280 if (str != snap_type_selector.get_active_text()) {
2281 snap_type_selector.set_active_text (str);
2284 instant_save ();
2286 switch (snap_type) {
2287 case SnapToAThirtysecondBeat:
2288 case SnapToASixteenthBeat:
2289 case SnapToAEighthBeat:
2290 case SnapToAQuarterBeat:
2291 case SnapToAThirdBeat:
2292 update_tempo_based_rulers ();
2293 break;
2295 case SnapToRegionStart:
2296 case SnapToRegionEnd:
2297 case SnapToRegionSync:
2298 case SnapToRegionBoundary:
2299 build_region_boundary_cache ();
2300 break;
2302 default:
2303 /* relax */
2304 break;
2308 void
2309 Editor::set_snap_mode (SnapMode mode)
2311 snap_mode = mode;
2312 string str = snap_mode_strings[(int)mode];
2314 if (str != snap_mode_selector.get_active_text ()) {
2315 snap_mode_selector.set_active_text (str);
2318 instant_save ();
2320 void
2321 Editor::set_edit_point_preference (EditPoint ep, bool force)
2323 bool changed = (_edit_point != ep);
2325 _edit_point = ep;
2326 string str = edit_point_strings[(int)ep];
2328 if (str != edit_point_selector.get_active_text ()) {
2329 edit_point_selector.set_active_text (str);
2332 set_canvas_cursor ();
2334 if (!force && !changed) {
2335 return;
2338 switch (zoom_focus) {
2339 case ZoomFocusMouse:
2340 case ZoomFocusPlayhead:
2341 case ZoomFocusEdit:
2342 switch (_edit_point) {
2343 case EditAtMouse:
2344 set_zoom_focus (ZoomFocusMouse);
2345 break;
2346 case EditAtPlayhead:
2347 set_zoom_focus (ZoomFocusPlayhead);
2348 break;
2349 case EditAtSelectedMarker:
2350 set_zoom_focus (ZoomFocusEdit);
2351 break;
2353 break;
2354 default:
2355 break;
2358 const char* action=NULL;
2360 switch (_edit_point) {
2361 case EditAtPlayhead:
2362 action = "edit-at-playhead";
2363 break;
2364 case EditAtSelectedMarker:
2365 action = "edit-at-marker";
2366 break;
2367 case EditAtMouse:
2368 action = "edit-at-mouse";
2369 break;
2372 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2373 if (act) {
2374 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2377 nframes64_t foo;
2378 bool in_track_canvas;
2380 if (!mouse_frame (foo, in_track_canvas)) {
2381 in_track_canvas = false;
2384 reset_canvas_action_sensitivity (in_track_canvas);
2386 instant_save ();
2390 Editor::set_state (const XMLNode& node)
2392 const XMLProperty* prop;
2393 XMLNode* geometry;
2394 int x, y, xoff, yoff;
2395 Gdk::Geometry g;
2397 if ((prop = node.property ("id")) != 0) {
2398 _id = prop->value ();
2401 g.base_width = default_width;
2402 g.base_height = default_height;
2403 x = 1;
2404 y = 1;
2405 xoff = 0;
2406 yoff = 21;
2408 if ((geometry = find_named_node (node, "geometry")) != 0) {
2410 XMLProperty* prop;
2412 if ((prop = geometry->property("x_size")) == 0) {
2413 prop = geometry->property ("x-size");
2415 if (prop) {
2416 g.base_width = atoi(prop->value());
2418 if ((prop = geometry->property("y_size")) == 0) {
2419 prop = geometry->property ("y-size");
2421 if (prop) {
2422 g.base_height = atoi(prop->value());
2425 if ((prop = geometry->property ("x_pos")) == 0) {
2426 prop = geometry->property ("x-pos");
2428 if (prop) {
2429 x = atoi (prop->value());
2432 if ((prop = geometry->property ("y_pos")) == 0) {
2433 prop = geometry->property ("y-pos");
2435 if (prop) {
2436 y = atoi (prop->value());
2439 if ((prop = geometry->property ("x_off")) == 0) {
2440 prop = geometry->property ("x-off");
2442 if (prop) {
2443 xoff = atoi (prop->value());
2445 if ((prop = geometry->property ("y_off")) == 0) {
2446 prop = geometry->property ("y-off");
2448 if (prop) {
2449 yoff = atoi (prop->value());
2454 set_default_size (g.base_width, g.base_height);
2455 move (x, y);
2457 if (session && (prop = node.property ("playhead"))) {
2458 nframes64_t pos;
2459 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2460 playhead_cursor->set_position (pos);
2461 } else {
2462 playhead_cursor->set_position (0);
2464 /* reset_x_origin() doesn't work right here, since the old
2465 position may be zero already, and it does nothing in such
2466 circumstances.
2469 leftmost_frame = 0;
2470 horizontal_adjustment.set_value (0);
2473 if ((prop = node.property ("mixer-width"))) {
2474 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2477 if ((prop = node.property ("zoom-focus"))) {
2478 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2481 if ((prop = node.property ("zoom"))) {
2482 reset_zoom (PBD::atof (prop->value()));
2485 if ((prop = node.property ("snap-to"))) {
2486 set_snap_to ((SnapType) atoi (prop->value()));
2489 if ((prop = node.property ("snap-mode"))) {
2490 set_snap_mode ((SnapMode) atoi (prop->value()));
2493 if ((prop = node.property ("edit-point"))) {
2494 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2497 if ((prop = node.property ("mouse-mode"))) {
2498 MouseMode m = str2mousemode(prop->value());
2499 mouse_mode = MouseMode ((int) m + 1); /* lie, force mode switch */
2500 set_mouse_mode (m, true);
2501 } else {
2502 mouse_mode = MouseGain; /* lie, to force the mode switch */
2503 set_mouse_mode (MouseObject, true);
2506 if ((prop = node.property ("show-waveforms"))) {
2507 bool yn = (string_is_affirmative (prop->value()));
2508 _show_waveforms = !yn;
2509 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-waveform-visible"));
2510 if (act) {
2511 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2512 /* do it twice to force the change */
2513 tact->set_active (!yn);
2514 tact->set_active (yn);
2518 if ((prop = node.property ("show-waveforms-rectified"))) {
2519 bool yn = (string_is_affirmative (prop->value()));
2520 _show_waveforms_rectified = !yn;
2521 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-waveform-rectified"));
2522 if (act) {
2523 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2524 /* do it twice to force the change */
2525 tact->set_active (!yn);
2526 tact->set_active (yn);
2530 if ((prop = node.property ("show-waveforms-recording"))) {
2531 bool yn = (string_is_affirmative (prop->value()));
2532 _show_waveforms_recording = !yn;
2533 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleWaveformsWhileRecording"));
2534 if (act) {
2535 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2536 /* do it twice to force the change */
2537 tact->set_active (!yn);
2538 tact->set_active (yn);
2542 if ((prop = node.property ("show-measures"))) {
2543 bool yn = (string_is_affirmative (prop->value()));
2544 _show_measures = !yn;
2545 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2546 if (act) {
2547 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2548 /* do it twice to force the change */
2549 tact->set_active (!yn);
2550 tact->set_active (yn);
2554 if ((prop = node.property ("follow-playhead"))) {
2555 bool yn = (string_is_affirmative (prop->value()));
2556 set_follow_playhead (yn);
2557 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2558 if (act) {
2559 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2560 if (tact->get_active() != yn) {
2561 tact->set_active (yn);
2566 if ((prop = node.property ("stationary-playhead"))) {
2567 bool yn = (string_is_affirmative (prop->value()));
2568 set_stationary_playhead (yn);
2569 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2570 if (act) {
2571 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2572 if (tact->get_active() != yn) {
2573 tact->set_active (yn);
2578 if ((prop = node.property ("region-list-sort-type"))) {
2579 region_list_sort_type = (Editing::RegionListSortType) -1; // force change
2580 reset_region_list_sort_type(str2regionlistsorttype(prop->value()));
2583 if ((prop = node.property ("xfades-visible"))) {
2584 bool yn = (string_is_affirmative (prop->value()));
2585 _xfade_visibility = !yn;
2586 // set_xfade_visibility (yn);
2589 if ((prop = node.property ("show-editor-mixer"))) {
2591 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2592 if (act) {
2594 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2595 bool yn = (prop->value() == X_("yes"));
2597 /* do it twice to force the change */
2599 tact->set_active (!yn);
2600 tact->set_active (yn);
2605 return 0;
2608 XMLNode&
2609 Editor::get_state ()
2611 XMLNode* node = new XMLNode ("Editor");
2612 char buf[32];
2614 _id.print (buf, sizeof (buf));
2615 node->add_property ("id", buf);
2617 if (is_realized()) {
2618 Glib::RefPtr<Gdk::Window> win = get_window();
2620 int x, y, xoff, yoff, width, height;
2621 win->get_root_origin(x, y);
2622 win->get_position(xoff, yoff);
2623 win->get_size(width, height);
2625 XMLNode* geometry = new XMLNode ("geometry");
2627 snprintf(buf, sizeof(buf), "%d", width);
2628 geometry->add_property("x_size", string(buf));
2629 snprintf(buf, sizeof(buf), "%d", height);
2630 geometry->add_property("y_size", string(buf));
2631 snprintf(buf, sizeof(buf), "%d", x);
2632 geometry->add_property("x_pos", string(buf));
2633 snprintf(buf, sizeof(buf), "%d", y);
2634 geometry->add_property("y_pos", string(buf));
2635 snprintf(buf, sizeof(buf), "%d", xoff);
2636 geometry->add_property("x_off", string(buf));
2637 snprintf(buf, sizeof(buf), "%d", yoff);
2638 geometry->add_property("y_off", string(buf));
2639 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2640 geometry->add_property("edit_pane_pos", string(buf));
2642 node->add_child_nocopy (*geometry);
2645 maybe_add_mixer_strip_width (*node);
2647 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2648 node->add_property ("zoom-focus", buf);
2649 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2650 node->add_property ("zoom", buf);
2651 snprintf (buf, sizeof(buf), "%d", (int) snap_type);
2652 node->add_property ("snap-to", buf);
2653 snprintf (buf, sizeof(buf), "%d", (int) snap_mode);
2654 node->add_property ("snap-mode", buf);
2656 node->add_property ("edit-point", enum_2_string (_edit_point));
2658 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2659 node->add_property ("playhead", buf);
2661 node->add_property ("show-waveforms", _show_waveforms ? "yes" : "no");
2662 node->add_property ("show-waveforms-rectified", _show_waveforms_rectified ? "yes" : "no");
2663 node->add_property ("show-waveforms-recording", _show_waveforms_recording ? "yes" : "no");
2664 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2665 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2666 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2667 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2668 node->add_property ("region-list-sort-type", enum2str(region_list_sort_type));
2669 node->add_property ("mouse-mode", enum2str(mouse_mode));
2671 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2672 if (act) {
2673 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2674 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2677 return *node;
2682 TimeAxisView *
2683 Editor::trackview_by_y_position (double y)
2685 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2687 TimeAxisView *tv;
2689 if ((tv = (*iter)->covers_y_position (y)) != 0) {
2690 return tv;
2694 return 0;
2697 void
2698 Editor::snap_to (nframes64_t& start, int32_t direction, bool for_mark)
2700 if (!session || snap_mode == SnapOff) {
2701 return;
2704 snap_to_internal (start, direction, for_mark);
2707 void
2708 Editor::snap_to_internal (nframes64_t& start, int32_t direction, bool for_mark)
2710 Location* before = 0;
2711 Location* after = 0;
2713 const nframes64_t one_second = session->frame_rate();
2714 const nframes64_t one_minute = session->frame_rate() * 60;
2715 const nframes64_t one_smpte_second = (nframes64_t)(rint(session->smpte_frames_per_second()) * session->frames_per_smpte_frame());
2716 nframes64_t one_smpte_minute = (nframes64_t)(rint(session->smpte_frames_per_second()) * session->frames_per_smpte_frame() * 60);
2717 nframes64_t presnap = start;
2719 switch (snap_type) {
2720 case SnapToCDFrame:
2721 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2722 start = (nframes64_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2723 } else {
2724 start = (nframes64_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2726 break;
2728 case SnapToSMPTEFrame:
2729 if (((direction == 0) && (fmod((double)start, (double)session->frames_per_smpte_frame()) > (session->frames_per_smpte_frame() / 2))) || (direction > 0)) {
2730 start = (nframes64_t) (ceil ((double) start / session->frames_per_smpte_frame()) * session->frames_per_smpte_frame());
2731 } else {
2732 start = (nframes64_t) (floor ((double) start / session->frames_per_smpte_frame()) * session->frames_per_smpte_frame());
2734 break;
2736 case SnapToSMPTESeconds:
2737 if (session->smpte_offset_negative())
2739 start += session->smpte_offset ();
2740 } else {
2741 start -= session->smpte_offset ();
2743 if (((direction == 0) && (start % one_smpte_second > one_smpte_second / 2)) || direction > 0) {
2744 start = (nframes64_t) ceil ((double) start / one_smpte_second) * one_smpte_second;
2745 } else {
2746 start = (nframes64_t) floor ((double) start / one_smpte_second) * one_smpte_second;
2749 if (session->smpte_offset_negative())
2751 start -= session->smpte_offset ();
2752 } else {
2753 start += session->smpte_offset ();
2755 break;
2757 case SnapToSMPTEMinutes:
2758 if (session->smpte_offset_negative())
2760 start += session->smpte_offset ();
2761 } else {
2762 start -= session->smpte_offset ();
2764 if (((direction == 0) && (start % one_smpte_minute > one_smpte_minute / 2)) || direction > 0) {
2765 start = (nframes64_t) ceil ((double) start / one_smpte_minute) * one_smpte_minute;
2766 } else {
2767 start = (nframes64_t) floor ((double) start / one_smpte_minute) * one_smpte_minute;
2769 if (session->smpte_offset_negative())
2771 start -= session->smpte_offset ();
2772 } else {
2773 start += session->smpte_offset ();
2775 break;
2777 case SnapToSeconds:
2778 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2779 start = (nframes64_t) ceil ((double) start / one_second) * one_second;
2780 } else {
2781 start = (nframes64_t) floor ((double) start / one_second) * one_second;
2783 break;
2785 case SnapToMinutes:
2786 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2787 start = (nframes64_t) ceil ((double) start / one_minute) * one_minute;
2788 } else {
2789 start = (nframes64_t) floor ((double) start / one_minute) * one_minute;
2791 break;
2793 case SnapToBar:
2794 start = session->tempo_map().round_to_bar (start, direction);
2795 break;
2797 case SnapToBeat:
2798 start = session->tempo_map().round_to_beat (start, direction);
2799 break;
2801 case SnapToAThirtysecondBeat:
2802 start = session->tempo_map().round_to_beat_subdivision (start, 32);
2803 break;
2805 case SnapToASixteenthBeat:
2806 start = session->tempo_map().round_to_beat_subdivision (start, 16);
2807 break;
2809 case SnapToAEighthBeat:
2810 start = session->tempo_map().round_to_beat_subdivision (start, 8);
2811 break;
2813 case SnapToAQuarterBeat:
2814 start = session->tempo_map().round_to_beat_subdivision (start, 4);
2815 break;
2817 case SnapToAThirdBeat:
2818 start = session->tempo_map().round_to_beat_subdivision (start, 3);
2819 break;
2821 case SnapToMark:
2822 if (for_mark) {
2823 return;
2826 before = session->locations()->first_location_before (start);
2827 after = session->locations()->first_location_after (start);
2829 if (direction < 0) {
2830 if (before) {
2831 start = before->start();
2832 } else {
2833 start = 0;
2835 } else if (direction > 0) {
2836 if (after) {
2837 start = after->start();
2838 } else {
2839 start = session->current_end_frame();
2841 } else {
2842 if (before) {
2843 if (after) {
2844 /* find nearest of the two */
2845 if ((start - before->start()) < (after->start() - start)) {
2846 start = before->start();
2847 } else {
2848 start = after->start();
2850 } else {
2851 start = before->start();
2853 } else if (after) {
2854 start = after->start();
2855 } else {
2856 /* relax */
2859 break;
2861 case SnapToRegionStart:
2862 case SnapToRegionEnd:
2863 case SnapToRegionSync:
2864 case SnapToRegionBoundary:
2865 if (!region_boundary_cache.empty()) {
2866 vector<nframes64_t>::iterator i;
2868 if (direction > 0) {
2869 i = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2870 } else {
2871 i = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2874 if (i != region_boundary_cache.end()) {
2876 /* lower bound doesn't quite to the right thing for our purposes */
2878 if (direction < 0 && i != region_boundary_cache.begin()) {
2879 --i;
2882 start = *i;
2884 } else {
2885 start = region_boundary_cache.back();
2888 break;
2891 switch (snap_mode) {
2892 case SnapNormal:
2893 return;
2895 case SnapMagnetic:
2897 if (presnap > start) {
2898 if (presnap > (start + unit_to_frame(snap_threshold))) {
2899 start = presnap;
2902 } else if (presnap < start) {
2903 if (presnap < (start - unit_to_frame(snap_threshold))) {
2904 start = presnap;
2908 default:
2909 /* handled at entry */
2910 return;
2915 void
2916 Editor::setup_toolbar ()
2918 string pixmap_path;
2920 /* Mode Buttons (tool selection) */
2922 vector<ToggleButton *> mouse_mode_buttons;
2924 mouse_move_button.add (*(manage (new Image (::get_icon("tool_object")))));
2925 mouse_move_button.set_relief(Gtk::RELIEF_NONE);
2926 mouse_mode_buttons.push_back (&mouse_move_button);
2928 if (!Profile->get_sae()) {
2929 mouse_select_button.add (*(manage (new Image (get_xpm("tool_range.xpm")))));
2930 mouse_select_button.set_relief(Gtk::RELIEF_NONE);
2931 mouse_mode_buttons.push_back (&mouse_select_button);
2933 mouse_gain_button.add (*(manage (new Image (::get_icon("tool_gain")))));
2934 mouse_gain_button.set_relief(Gtk::RELIEF_NONE);
2935 mouse_mode_buttons.push_back (&mouse_gain_button);
2938 mouse_zoom_button.add (*(manage (new Image (::get_icon("tool_zoom")))));
2939 mouse_zoom_button.set_relief(Gtk::RELIEF_NONE);
2940 mouse_mode_buttons.push_back (&mouse_zoom_button);
2941 mouse_timefx_button.add (*(manage (new Image (::get_icon("tool_stretch")))));
2942 mouse_timefx_button.set_relief(Gtk::RELIEF_NONE);
2943 mouse_mode_buttons.push_back (&mouse_timefx_button);
2944 mouse_audition_button.add (*(manage (new Image (::get_icon("tool_audition")))));
2945 mouse_audition_button.set_relief(Gtk::RELIEF_NONE);
2946 mouse_mode_buttons.push_back (&mouse_audition_button);
2948 mouse_mode_button_set = new GroupedButtons (mouse_mode_buttons);
2950 HBox* mode_box = manage(new HBox);
2951 mode_box->set_border_width (2);
2952 mode_box->set_spacing(4);
2953 mouse_mode_button_box.set_spacing(1);
2954 mouse_mode_button_box.pack_start(mouse_move_button, true, true);
2955 if (!Profile->get_sae()) {
2956 mouse_mode_button_box.pack_start(mouse_select_button, true, true);
2958 mouse_mode_button_box.pack_start(mouse_zoom_button, true, true);
2959 if (!Profile->get_sae()) {
2960 mouse_mode_button_box.pack_start(mouse_gain_button, true, true);
2962 mouse_mode_button_box.pack_start(mouse_timefx_button, true, true);
2963 mouse_mode_button_box.pack_start(mouse_audition_button, true, true);
2964 mouse_mode_button_box.set_homogeneous(true);
2966 vector<string> edit_mode_strings;
2967 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2968 if (!Profile->get_sae()) {
2969 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2971 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2973 edit_mode_selector.set_name ("EditModeSelector");
2974 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2975 edit_mode_selector.signal_changed().connect (mem_fun(*this, &Editor::edit_mode_selection_done));
2977 mode_box->pack_start(edit_mode_selector);
2978 mode_box->pack_start(mouse_mode_button_box);
2980 mouse_mode_tearoff = manage (new TearOff (*mode_box));
2981 mouse_mode_tearoff->set_name ("MouseModeBase");
2982 mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (bind (sigc::ptr_fun (relay_key_press), &mouse_mode_tearoff->tearoff_window()));
2984 if (Profile->get_sae()) {
2985 mouse_mode_tearoff->set_can_be_torn_off (false);
2988 mouse_mode_tearoff->Detach.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2989 &mouse_mode_tearoff->tearoff_window()));
2990 mouse_mode_tearoff->Attach.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2991 &mouse_mode_tearoff->tearoff_window(), 1));
2992 mouse_mode_tearoff->Hidden.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2993 &mouse_mode_tearoff->tearoff_window()));
2994 mouse_mode_tearoff->Visible.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2995 &mouse_mode_tearoff->tearoff_window(), 1));
2997 mouse_move_button.set_name ("MouseModeButton");
2998 mouse_select_button.set_name ("MouseModeButton");
2999 mouse_gain_button.set_name ("MouseModeButton");
3000 mouse_zoom_button.set_name ("MouseModeButton");
3001 mouse_timefx_button.set_name ("MouseModeButton");
3002 mouse_audition_button.set_name ("MouseModeButton");
3004 ARDOUR_UI::instance()->tooltips().set_tip (mouse_move_button, _("Select/Move Objects"));
3005 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Select/Move Ranges"));
3006 ARDOUR_UI::instance()->tooltips().set_tip (mouse_gain_button, _("Draw Gain Automation"));
3007 ARDOUR_UI::instance()->tooltips().set_tip (mouse_zoom_button, _("Select Zoom Range"));
3008 ARDOUR_UI::instance()->tooltips().set_tip (mouse_timefx_button, _("Stretch/Shrink Regions"));
3009 ARDOUR_UI::instance()->tooltips().set_tip (mouse_audition_button, _("Listen to Specific Regions"));
3011 mouse_move_button.unset_flags (CAN_FOCUS);
3012 mouse_select_button.unset_flags (CAN_FOCUS);
3013 mouse_gain_button.unset_flags (CAN_FOCUS);
3014 mouse_zoom_button.unset_flags (CAN_FOCUS);
3015 mouse_timefx_button.unset_flags (CAN_FOCUS);
3016 mouse_audition_button.unset_flags (CAN_FOCUS);
3018 mouse_select_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseRange));
3019 mouse_select_button.signal_button_release_event().connect (mem_fun(*this, &Editor::mouse_select_button_release));
3021 mouse_move_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseObject));
3022 mouse_gain_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseGain));
3023 mouse_zoom_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseZoom));
3024 mouse_timefx_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseTimeFX));
3025 mouse_audition_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseAudition));
3027 // mouse_move_button.set_active (true);
3030 /* Zoom */
3032 zoom_box.set_spacing (1);
3033 zoom_box.set_border_width (0);
3035 zoom_in_button.set_name ("EditorTimeButton");
3036 zoom_in_button.set_size_request(-1,16);
3037 zoom_in_button.add (*(manage (new Image (::get_icon("zoom_in")))));
3038 zoom_in_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::temporal_zoom_step), false));
3039 ARDOUR_UI::instance()->tooltips().set_tip (zoom_in_button, _("Zoom In"));
3041 zoom_out_button.set_name ("EditorTimeButton");
3042 zoom_out_button.set_size_request(-1,16);
3043 zoom_out_button.add (*(manage (new Image (::get_icon("zoom_out")))));
3044 zoom_out_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::temporal_zoom_step), true));
3045 ARDOUR_UI::instance()->tooltips().set_tip (zoom_out_button, _("Zoom Out"));
3047 zoom_out_full_button.set_name ("EditorTimeButton");
3048 zoom_out_full_button.set_size_request(-1,16);
3049 zoom_out_full_button.add (*(manage (new Image (::get_icon("zoom_full")))));
3050 zoom_out_full_button.signal_clicked().connect (mem_fun(*this, &Editor::temporal_zoom_session));
3051 ARDOUR_UI::instance()->tooltips().set_tip (zoom_out_full_button, _("Zoom to Session"));
3053 zoom_focus_selector.set_name ("ZoomFocusSelector");
3054 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
3055 zoom_focus_selector.signal_changed().connect (mem_fun(*this, &Editor::zoom_focus_selection_done));
3056 ARDOUR_UI::instance()->tooltips().set_tip (zoom_focus_selector, _("Zoom focus"));
3058 zoom_box.pack_start (zoom_focus_selector, true, true);
3059 zoom_box.pack_start (zoom_out_button, false, false);
3060 zoom_box.pack_start (zoom_in_button, false, false);
3061 zoom_box.pack_start (zoom_out_full_button, false, false);
3063 snap_box.set_spacing (1);
3064 snap_box.set_border_width (2);
3066 snap_type_selector.set_name ("SnapTypeSelector");
3067 set_popdown_strings (snap_type_selector, snap_type_strings, true);
3068 snap_type_selector.signal_changed().connect (mem_fun(*this, &Editor::snap_type_selection_done));
3069 ARDOUR_UI::instance()->tooltips().set_tip (snap_type_selector, _("Snap/Grid Units"));
3071 snap_mode_selector.set_name ("SnapModeSelector");
3072 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
3073 snap_mode_selector.signal_changed().connect (mem_fun(*this, &Editor::snap_mode_selection_done));
3074 ARDOUR_UI::instance()->tooltips().set_tip (snap_mode_selector, _("Snap/Grid Mode"));
3076 edit_point_selector.set_name ("EditPointSelector");
3077 set_popdown_strings (edit_point_selector, edit_point_strings, true);
3078 edit_point_selector.signal_changed().connect (mem_fun(*this, &Editor::edit_point_selection_done));
3079 ARDOUR_UI::instance()->tooltips().set_tip (edit_point_selector, _("Edit point"));
3081 if (Profile->get_sae()) {
3082 snap_box.pack_start (edit_point_clock, false, false);
3084 snap_box.pack_start (snap_mode_selector, false, false);
3085 snap_box.pack_start (snap_type_selector, false, false);
3086 snap_box.pack_start (edit_point_selector, false, false);
3088 /* Nudge */
3090 HBox *nudge_box = manage (new HBox);
3091 nudge_box->set_spacing(1);
3092 nudge_box->set_border_width (2);
3094 nudge_forward_button.signal_button_release_event().connect (mem_fun(*this, &Editor::nudge_forward_release), false);
3095 nudge_backward_button.signal_button_release_event().connect (mem_fun(*this, &Editor::nudge_backward_release), false);
3097 nudge_box->pack_start (nudge_backward_button, false, false);
3098 nudge_box->pack_start (nudge_forward_button, false, false);
3099 nudge_box->pack_start (nudge_clock, false, false);
3102 /* Pack everything in... */
3104 HBox* hbox = new HBox;
3105 hbox->set_spacing(10);
3107 tools_tearoff = new TearOff (*hbox);
3108 tools_tearoff->set_name ("MouseModeBase");
3109 tools_tearoff->tearoff_window().signal_key_press_event().connect (bind (sigc::ptr_fun (relay_key_press), &tools_tearoff->tearoff_window()));
3111 if (Profile->get_sae()) {
3112 tools_tearoff->set_can_be_torn_off (false);
3115 tools_tearoff->Detach.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
3116 &tools_tearoff->tearoff_window()));
3117 tools_tearoff->Attach.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
3118 &tools_tearoff->tearoff_window(), 0));
3119 tools_tearoff->Hidden.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
3120 &tools_tearoff->tearoff_window()));
3121 tools_tearoff->Visible.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
3122 &tools_tearoff->tearoff_window(), 0));
3124 toolbar_hbox.set_spacing (10);
3125 toolbar_hbox.set_border_width (1);
3127 toolbar_hbox.pack_start (*mouse_mode_tearoff, false, false);
3128 toolbar_hbox.pack_start (*tools_tearoff, false, false);
3131 hbox->pack_start (snap_box, false, false);
3132 // hbox->pack_start (zoom_box, false, false);
3133 hbox->pack_start (*nudge_box, false, false);
3135 hbox->show_all ();
3137 toolbar_base.set_name ("ToolBarBase");
3138 toolbar_base.add (toolbar_hbox);
3140 toolbar_frame.set_shadow_type (SHADOW_OUT);
3141 toolbar_frame.set_name ("BaseFrame");
3142 toolbar_frame.add (toolbar_base);
3146 Editor::convert_drop_to_paths (vector<ustring>& paths,
3147 const RefPtr<Gdk::DragContext>& context,
3148 gint x,
3149 gint y,
3150 const SelectionData& data,
3151 guint info,
3152 guint time)
3155 if (session == 0) {
3156 return -1;
3159 vector<ustring> uris = data.get_uris();
3161 if (uris.empty()) {
3163 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3164 are actually URI lists. So do it by hand.
3167 if (data.get_target() != "text/plain") {
3168 return -1;
3171 /* Parse the "uri-list" format that Nautilus provides,
3172 where each pathname is delimited by \r\n.
3174 THERE MAY BE NO NULL TERMINATING CHAR!!!
3177 ustring txt = data.get_text();
3178 const char* p;
3179 const char* q;
3181 p = (const char *) malloc (txt.length() + 1);
3182 txt.copy ((char *) p, txt.length(), 0);
3183 ((char*)p)[txt.length()] = '\0';
3185 while (p)
3187 if (*p != '#')
3189 while (g_ascii_isspace (*p))
3190 p++;
3192 q = p;
3193 while (*q && (*q != '\n') && (*q != '\r')) {
3194 q++;
3197 if (q > p)
3199 q--;
3200 while (q > p && g_ascii_isspace (*q))
3201 q--;
3203 if (q > p)
3205 uris.push_back (ustring (p, q - p + 1));
3209 p = strchr (p, '\n');
3210 if (p)
3211 p++;
3214 free ((void*)p);
3216 if (uris.empty()) {
3217 return -1;
3221 for (vector<ustring>::iterator i = uris.begin(); i != uris.end(); ++i) {
3223 if ((*i).substr (0,7) == "file://") {
3226 ustring p = *i;
3227 PBD::url_decode (p);
3229 // scan forward past three slashes
3231 ustring::size_type slashcnt = 0;
3232 ustring::size_type n = 0;
3233 ustring::iterator x = p.begin();
3235 while (slashcnt < 3 && x != p.end()) {
3236 if ((*x) == '/') {
3237 slashcnt++;
3238 } else if (slashcnt == 3) {
3239 break;
3241 ++n;
3242 ++x;
3245 if (slashcnt != 3 || x == p.end()) {
3246 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3247 continue;
3250 paths.push_back (p.substr (n - 1));
3254 return 0;
3257 void
3258 Editor::new_tempo_section ()
3263 void
3264 Editor::map_transport_state ()
3266 ENSURE_GUI_THREAD (mem_fun(*this, &Editor::map_transport_state));
3268 if (session->transport_stopped()) {
3269 have_pending_keyboard_selection = false;
3272 update_loop_range_view (true);
3275 /* UNDO/REDO */
3277 Editor::State::State ()
3279 selection = new Selection;
3282 Editor::State::~State ()
3284 delete selection;
3287 UndoAction
3288 Editor::get_memento () const
3290 State *state = new State;
3292 store_state (*state);
3293 return bind (mem_fun (*(const_cast<Editor*>(this)), &Editor::restore_state), state);
3296 void
3297 Editor::store_state (State& state) const
3299 *state.selection = *selection;
3302 void
3303 Editor::restore_state (State *state)
3305 if (*selection == *state->selection) {
3306 return;
3309 *selection = *state->selection;
3310 time_selection_changed ();
3311 region_selection_changed ();
3313 /* XXX other selection change handlers? */
3316 void
3317 Editor::begin_reversible_command (string name)
3319 if (session) {
3320 // before = &get_state();
3321 session->begin_reversible_command (name);
3325 void
3326 Editor::commit_reversible_command ()
3328 if (session) {
3329 // session->commit_reversible_command (new MementoCommand<Editor>(*this, before, &get_state()));
3330 session->commit_reversible_command ();
3334 void
3335 Editor::set_edit_group_solo (Route& route, bool yn)
3337 RouteGroup *edit_group;
3339 if ((edit_group = route.edit_group()) != 0) {
3340 edit_group->apply (&Route::set_solo, yn, this);
3341 } else {
3342 route.set_solo (yn, this);
3346 void
3347 Editor::set_edit_group_mute (Route& route, bool yn)
3349 RouteGroup *edit_group = 0;
3351 if ((edit_group == route.edit_group()) != 0) {
3352 edit_group->apply (&Route::set_mute, yn, this);
3353 } else {
3354 route.set_mute (yn, this);
3358 void
3359 Editor::history_changed ()
3361 string label;
3363 if (undo_action && session) {
3364 if (session->undo_depth() == 0) {
3365 label = _("Undo");
3366 } else {
3367 label = string_compose(_("Undo (%1)"), session->next_undo());
3369 undo_action->property_label() = label;
3372 if (redo_action && session) {
3373 if (session->redo_depth() == 0) {
3374 label = _("Redo");
3375 } else {
3376 label = string_compose(_("Redo (%1)"), session->next_redo());
3378 redo_action->property_label() = label;
3382 void
3383 Editor::duplicate_dialog (bool with_dialog)
3385 float times = 1.0f;
3387 if (mouse_mode == MouseRange) {
3388 if (selection->time.length() == 0) {
3389 return;
3393 RegionSelection rs;
3394 get_regions_for_action (rs);
3396 if (mouse_mode != MouseRange) {
3398 if (rs.empty()) {
3399 return;
3403 if (with_dialog) {
3405 ArdourDialog win ("Duplicate");
3406 Label label (_("Number of Duplications:"));
3407 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3408 SpinButton spinner (adjustment, 0.0, 1);
3409 HBox hbox;
3411 win.get_vbox()->set_spacing (12);
3412 win.get_vbox()->pack_start (hbox);
3413 hbox.set_border_width (6);
3414 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3416 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3417 place, visually. so do this by hand.
3420 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3421 spinner.signal_activate().connect (sigc::bind (mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3422 spinner.grab_focus();
3424 hbox.show ();
3425 label.show ();
3426 spinner.show ();
3428 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3429 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3430 win.set_default_response (RESPONSE_ACCEPT);
3432 win.set_position (WIN_POS_MOUSE);
3434 spinner.grab_focus ();
3436 switch (win.run ()) {
3437 case RESPONSE_ACCEPT:
3438 break;
3439 default:
3440 return;
3443 times = adjustment.get_value();
3446 if (mouse_mode == MouseRange) {
3447 duplicate_selection (times);
3448 } else {
3449 duplicate_some_regions (rs, times);
3453 void
3454 Editor::show_verbose_canvas_cursor ()
3456 verbose_canvas_cursor->raise_to_top();
3457 verbose_canvas_cursor->show();
3458 verbose_cursor_visible = true;
3461 void
3462 Editor::hide_verbose_canvas_cursor ()
3464 verbose_canvas_cursor->hide();
3465 verbose_cursor_visible = false;
3468 double
3469 Editor::clamp_verbose_cursor_x (double x)
3471 if (x < 0) {
3472 x = 0;
3473 } else {
3474 x = min (canvas_width - 200.0, x);
3476 return x;
3479 double
3480 Editor::clamp_verbose_cursor_y (double y)
3482 if (y < canvas_timebars_vsize) {
3483 y = canvas_timebars_vsize;
3484 } else {
3485 y = min (canvas_height - 50, y);
3487 return y;
3490 void
3491 Editor::set_verbose_canvas_cursor (const string & txt, double x, double y)
3493 verbose_canvas_cursor->property_text() = txt.c_str();
3494 /* don't get too close to the edge */
3495 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (x);
3496 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (y);
3499 void
3500 Editor::set_verbose_canvas_cursor_text (const string & txt)
3502 verbose_canvas_cursor->property_text() = txt.c_str();
3505 void
3506 Editor::set_edit_mode (EditMode m)
3508 Config->set_edit_mode (m);
3511 void
3512 Editor::cycle_edit_mode ()
3514 switch (Config->get_edit_mode()) {
3515 case Slide:
3516 if (Profile->get_sae()) {
3517 Config->set_edit_mode (Lock);
3518 } else {
3519 Config->set_edit_mode (Splice);
3521 break;
3522 case Splice:
3523 Config->set_edit_mode (Lock);
3524 break;
3525 case Lock:
3526 Config->set_edit_mode (Slide);
3527 break;
3531 void
3532 Editor::edit_mode_selection_done ()
3534 if (session == 0) {
3535 return;
3538 string choice = edit_mode_selector.get_active_text();
3539 EditMode mode = Slide;
3541 if (choice == _("Splice Edit")) {
3542 mode = Splice;
3543 } else if (choice == _("Slide Edit")) {
3544 mode = Slide;
3545 } else if (choice == _("Lock Edit")) {
3546 mode = Lock;
3549 Config->set_edit_mode (mode);
3552 void
3553 Editor::snap_type_selection_done ()
3555 string choice = snap_type_selector.get_active_text();
3556 SnapType snaptype = SnapToBeat;
3558 if (choice == _("Beats/3")) {
3559 snaptype = SnapToAThirdBeat;
3560 } else if (choice == _("Beats/4")) {
3561 snaptype = SnapToAQuarterBeat;
3562 } else if (choice == _("Beats/8")) {
3563 snaptype = SnapToAEighthBeat;
3564 } else if (choice == _("Beats/16")) {
3565 snaptype = SnapToASixteenthBeat;
3566 } else if (choice == _("Beats/32")) {
3567 snaptype = SnapToAThirtysecondBeat;
3568 } else if (choice == _("Beats")) {
3569 snaptype = SnapToBeat;
3570 } else if (choice == _("Bars")) {
3571 snaptype = SnapToBar;
3572 } else if (choice == _("Marks")) {
3573 snaptype = SnapToMark;
3574 } else if (choice == _("Region starts")) {
3575 snaptype = SnapToRegionStart;
3576 } else if (choice == _("Region ends")) {
3577 snaptype = SnapToRegionEnd;
3578 } else if (choice == _("Region bounds")) {
3579 snaptype = SnapToRegionBoundary;
3580 } else if (choice == _("Region syncs")) {
3581 snaptype = SnapToRegionSync;
3582 } else if (choice == _("CD Frames")) {
3583 snaptype = SnapToCDFrame;
3584 } else if (choice == _("SMPTE Frames")) {
3585 snaptype = SnapToSMPTEFrame;
3586 } else if (choice == _("SMPTE Seconds")) {
3587 snaptype = SnapToSMPTESeconds;
3588 } else if (choice == _("SMPTE Minutes")) {
3589 snaptype = SnapToSMPTEMinutes;
3590 } else if (choice == _("Seconds")) {
3591 snaptype = SnapToSeconds;
3592 } else if (choice == _("Minutes")) {
3593 snaptype = SnapToMinutes;
3596 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3597 if (ract) {
3598 ract->set_active ();
3602 void
3603 Editor::snap_mode_selection_done ()
3605 string choice = snap_mode_selector.get_active_text();
3606 SnapMode mode = SnapNormal;
3608 if (choice == _("No Grid")) {
3609 mode = SnapOff;
3610 } else if (choice == _("Grid")) {
3611 mode = SnapNormal;
3612 } else if (choice == _("Magnetic")) {
3613 mode = SnapMagnetic;
3616 RefPtr<RadioAction> ract = snap_mode_action (mode);
3618 if (ract) {
3619 ract->set_active (true);
3623 void
3624 Editor::cycle_edit_point (bool with_marker)
3626 switch (_edit_point) {
3627 case EditAtMouse:
3628 set_edit_point_preference (EditAtPlayhead);
3629 break;
3630 case EditAtPlayhead:
3631 if (with_marker) {
3632 set_edit_point_preference (EditAtSelectedMarker);
3633 } else {
3634 set_edit_point_preference (EditAtMouse);
3636 break;
3637 case EditAtSelectedMarker:
3638 set_edit_point_preference (EditAtMouse);
3639 break;
3643 void
3644 Editor::edit_point_selection_done ()
3646 string choice = edit_point_selector.get_active_text();
3647 EditPoint ep = EditAtSelectedMarker;
3649 if (choice == _("Marker")) {
3650 set_edit_point_preference (EditAtSelectedMarker);
3651 } else if (choice == _("Playhead")) {
3652 set_edit_point_preference (EditAtPlayhead);
3653 } else {
3654 set_edit_point_preference (EditAtMouse);
3657 RefPtr<RadioAction> ract = edit_point_action (ep);
3659 if (ract) {
3660 ract->set_active (true);
3664 void
3665 Editor::zoom_focus_selection_done ()
3667 string choice = zoom_focus_selector.get_active_text();
3668 ZoomFocus focus_type = ZoomFocusLeft;
3670 if (choice == _("Left")) {
3671 focus_type = ZoomFocusLeft;
3672 } else if (choice == _("Right")) {
3673 focus_type = ZoomFocusRight;
3674 } else if (choice == _("Center")) {
3675 focus_type = ZoomFocusCenter;
3676 } else if (choice == _("Playhead")) {
3677 focus_type = ZoomFocusPlayhead;
3678 } else if (choice == _("Mouse")) {
3679 focus_type = ZoomFocusMouse;
3680 } else if (choice == _("Active Mark")) {
3681 focus_type = ZoomFocusEdit;
3684 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3686 if (ract) {
3687 ract->set_active ();
3691 gint
3692 Editor::edit_controls_button_release (GdkEventButton* ev)
3694 if (Keyboard::is_context_menu_event (ev)) {
3695 ARDOUR_UI::instance()->add_route (this);
3697 return TRUE;
3700 gint
3701 Editor::mouse_select_button_release (GdkEventButton* ev)
3703 /* this handles just right-clicks */
3705 if (ev->button != 3) {
3706 return false;
3709 return true;
3712 Editor::TrackViewList *
3713 Editor::get_valid_views (TimeAxisView* track, RouteGroup* group)
3715 TrackViewList *v;
3716 TrackViewList::iterator i;
3718 v = new TrackViewList;
3720 if (track == 0 && group == 0) {
3722 /* all views */
3724 for (i = track_views.begin(); i != track_views.end (); ++i) {
3725 v->push_back (*i);
3728 } else if ((track != 0 && group == 0) || (track != 0 && group != 0 && !group->is_active())) {
3730 /* just the view for this track
3733 v->push_back (track);
3735 } else {
3737 /* views for all tracks in the edit group */
3739 for (i = track_views.begin(); i != track_views.end (); ++i) {
3741 if (group == 0 || (*i)->edit_group() == group) {
3742 v->push_back (*i);
3747 return v;
3750 void
3751 Editor::set_zoom_focus (ZoomFocus f)
3753 string str = zoom_focus_strings[(int)f];
3755 if (str != zoom_focus_selector.get_active_text()) {
3756 zoom_focus_selector.set_active_text (str);
3759 if (zoom_focus != f) {
3760 zoom_focus = f;
3762 ZoomFocusChanged (); /* EMIT_SIGNAL */
3764 instant_save ();
3768 void
3769 Editor::ensure_float (Window& win)
3771 win.set_transient_for (*this);
3774 void
3775 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3777 /* recover or initialize pane positions. do this here rather than earlier because
3778 we don't want the positions to change the child allocations, which they seem to do.
3781 int pos;
3782 XMLProperty* prop;
3783 char buf[32];
3784 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3785 int width, height;
3786 static int32_t done;
3787 XMLNode* geometry;
3789 width = default_width;
3790 height = default_height;
3792 if ((geometry = find_named_node (*node, "geometry")) != 0) {
3794 if ((prop = geometry->property ("x_size")) == 0) {
3795 prop = geometry->property ("x-size");
3797 if (prop) {
3798 width = atoi (prop->value());
3800 if ((prop = geometry->property ("y_size")) == 0) {
3801 prop = geometry->property ("y-size");
3803 if (prop) {
3804 height = atoi (prop->value());
3808 if (which == static_cast<Paned*> (&edit_pane)) {
3810 if (done) {
3811 return;
3814 if (!geometry || (prop = geometry->property ("edit_pane_pos")) == 0) {
3815 /* initial allocation is 90% to canvas, 10% to notebook */
3816 pos = (int) floor (alloc.get_width() * 0.90f);
3817 snprintf (buf, sizeof(buf), "%d", pos);
3818 } else {
3819 pos = atoi (prop->value());
3822 if ((done = GTK_WIDGET(edit_pane.gobj())->allocation.width > pos)) {
3823 edit_pane.set_position (pos);
3824 pre_maximal_pane_position = pos;
3829 void
3830 Editor::detach_tearoff (Box* b, Window* w)
3832 if (tools_tearoff->torn_off() &&
3833 mouse_mode_tearoff->torn_off()) {
3834 top_hbox.remove (toolbar_frame);
3838 void
3839 Editor::reattach_tearoff (Box* b, Window* w, int32_t n)
3841 if (toolbar_frame.get_parent() == 0) {
3842 top_hbox.pack_end (toolbar_frame);
3846 void
3847 Editor::set_show_measures (bool yn)
3849 if (_show_measures != yn) {
3850 hide_measures ();
3852 if ((_show_measures = yn) == true) {
3853 if (tempo_lines) {
3854 tempo_lines->show();
3856 draw_measures ();
3858 instant_save ();
3862 void
3863 Editor::toggle_follow_playhead ()
3865 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3866 if (act) {
3867 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3868 set_follow_playhead (tact->get_active());
3872 void
3873 Editor::set_follow_playhead (bool yn)
3875 if (_follow_playhead != yn) {
3876 if ((_follow_playhead = yn) == true) {
3877 /* catch up */
3878 update_current_screen ();
3880 instant_save ();
3884 void
3885 Editor::toggle_stationary_playhead ()
3887 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3888 if (act) {
3889 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3890 set_stationary_playhead (tact->get_active());
3894 void
3895 Editor::set_stationary_playhead (bool yn)
3897 if (_stationary_playhead != yn) {
3898 if ((_stationary_playhead = yn) == true) {
3899 /* catch up */
3900 update_current_screen ();
3902 instant_save ();
3906 void
3907 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3909 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3910 if (xfade) {
3911 xfade->set_active (!xfade->active());
3915 void
3916 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3918 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3919 if (xfade) {
3920 xfade->set_follow_overlap (!xfade->following_overlap());
3924 void
3925 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3927 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3929 if (!xfade) {
3930 return;
3933 CrossfadeEditor cew (*session, xfade, xfade->fade_in().get_min_y(), 1.0);
3935 ensure_float (cew);
3937 switch (cew.run ()) {
3938 case RESPONSE_ACCEPT:
3939 break;
3940 default:
3941 return;
3944 cew.apply ();
3945 xfade->StateChanged (Change (~0));
3948 PlaylistSelector&
3949 Editor::playlist_selector () const
3951 return *_playlist_selector;
3954 nframes64_t
3955 Editor::get_nudge_distance (nframes64_t pos, nframes64_t& next)
3957 nframes64_t ret;
3959 ret = nudge_clock.current_duration (pos);
3960 next = ret + 1; /* XXXX fix me */
3962 return ret;
3965 void
3966 Editor::end_location_changed (Location* location)
3968 ENSURE_GUI_THREAD (bind (mem_fun(*this, &Editor::end_location_changed), location));
3969 //reset_scrolling_region ();
3970 nframes64_t session_span = location->start() + (nframes64_t) floorf (current_page_frames() * 0.10f);
3971 horizontal_adjustment.set_upper (session_span / frames_per_unit);
3975 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3977 ArdourDialog dialog ("playlist deletion dialog");
3978 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3979 "If left alone, no audio files used by it will be cleaned.\n"
3980 "If deleted, audio files used by it alone by will cleaned."),
3981 pl->name()));
3983 dialog.set_position (WIN_POS_CENTER);
3984 dialog.get_vbox()->pack_start (label);
3986 label.show ();
3988 dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT);
3989 dialog.add_button (_("Keep playlist"), RESPONSE_REJECT);
3990 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3992 switch (dialog.run ()) {
3993 case RESPONSE_ACCEPT:
3994 /* delete the playlist */
3995 return 0;
3996 break;
3998 case RESPONSE_REJECT:
3999 /* keep the playlist */
4000 return 1;
4001 break;
4003 default:
4004 break;
4007 return -1;
4010 bool
4011 Editor::audio_region_selection_covers (nframes64_t where)
4013 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
4014 if ((*a)->region()->covers (where)) {
4015 return true;
4019 return false;
4022 void
4023 Editor::prepare_for_cleanup ()
4025 cut_buffer->clear_regions ();
4026 cut_buffer->clear_playlists ();
4028 selection->clear_regions ();
4029 selection->clear_playlists ();
4031 no_region_list_redisplay = true;
4034 void
4035 Editor::finish_cleanup ()
4037 no_region_list_redisplay = false;
4038 redisplay_regions ();
4041 Location*
4042 Editor::transport_loop_location()
4044 if (session) {
4045 return session->locations()->auto_loop_location();
4046 } else {
4047 return 0;
4051 Location*
4052 Editor::transport_punch_location()
4054 if (session) {
4055 return session->locations()->auto_punch_location();
4056 } else {
4057 return 0;
4061 bool
4062 Editor::control_layout_scroll (GdkEventScroll* ev)
4064 switch (ev->direction) {
4065 case GDK_SCROLL_UP:
4066 scroll_tracks_up_line ();
4067 return true;
4068 break;
4070 case GDK_SCROLL_DOWN:
4071 scroll_tracks_down_line ();
4072 return true;
4074 default:
4075 /* no left/right handling yet */
4076 break;
4079 return false;
4083 /** A new snapshot has been selected.
4085 void
4086 Editor::snapshot_display_selection_changed ()
4088 if (snapshot_display.get_selection()->count_selected_rows() > 0) {
4090 TreeModel::iterator i = snapshot_display.get_selection()->get_selected();
4092 Glib::ustring snap_name = (*i)[snapshot_display_columns.real_name];
4094 if (snap_name.length() == 0) {
4095 return;
4098 if (session->snap_name() == snap_name) {
4099 return;
4102 ARDOUR_UI::instance()->load_session(session->path(), string (snap_name));
4106 bool
4107 Editor::snapshot_display_button_press (GdkEventButton* ev)
4109 if (ev->button == 3) {
4110 /* Right-click on the snapshot list. Work out which snapshot it
4111 was over. */
4112 Gtk::TreeModel::Path path;
4113 Gtk::TreeViewColumn* col;
4114 int cx;
4115 int cy;
4116 snapshot_display.get_path_at_pos ((int) ev->x, (int) ev->y, path, col, cx, cy);
4117 Gtk::TreeModel::iterator iter = snapshot_display_model->get_iter (path);
4118 if (iter) {
4119 Gtk::TreeModel::Row row = *iter;
4120 popup_snapshot_context_menu (ev->button, ev->time, row[snapshot_display_columns.real_name]);
4122 return true;
4125 return false;
4129 /** Pop up the snapshot display context menu.
4130 * @param button Button used to open the menu.
4131 * @param time Menu open time.
4132 * @snapshot_name Name of the snapshot that the menu click was over.
4135 void
4136 Editor::popup_snapshot_context_menu (int button, int32_t time, Glib::ustring snapshot_name)
4138 using namespace Menu_Helpers;
4140 MenuList& items (snapshot_context_menu.items());
4141 items.clear ();
4143 const bool modification_allowed = (session->snap_name() != snapshot_name && session->name() != snapshot_name);
4145 items.push_back (MenuElem (_("Remove"), bind (mem_fun (*this, &Editor::remove_snapshot), snapshot_name)));
4146 if (!modification_allowed) {
4147 items.back().set_sensitive (false);
4150 items.push_back (MenuElem (_("Rename"), bind (mem_fun (*this, &Editor::rename_snapshot), snapshot_name)));
4151 if (!modification_allowed) {
4152 items.back().set_sensitive (false);
4155 snapshot_context_menu.popup (button, time);
4158 void
4159 Editor::rename_snapshot (Glib::ustring old_name)
4161 ArdourPrompter prompter(true);
4163 string new_name;
4165 prompter.set_name ("Prompter");
4166 prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
4167 prompter.set_prompt (_("New name of snapshot"));
4168 prompter.set_initial_text (old_name);
4170 if (prompter.run() == RESPONSE_ACCEPT) {
4171 prompter.get_result (new_name);
4172 if (new_name.length()) {
4173 session->rename_state (old_name, new_name);
4174 redisplay_snapshots ();
4180 void
4181 Editor::remove_snapshot (Glib::ustring name)
4183 vector<string> choices;
4185 std::string prompt = string_compose (_("Do you really want to remove snapshot \"%1\" ?\n(cannot be undone)"), name);
4187 choices.push_back (_("No, do nothing."));
4188 choices.push_back (_("Yes, remove it."));
4190 Gtkmm2ext::Choice prompter (prompt, choices);
4192 if (prompter.run () == 1) {
4193 session->remove_state (name);
4194 redisplay_snapshots ();
4198 void
4199 Editor::redisplay_snapshots ()
4201 if (session == 0) {
4202 return;
4205 vector<string*>* states;
4207 if ((states = session->possible_states()) == 0) {
4208 return;
4211 snapshot_display_model->clear ();
4213 for (vector<string*>::iterator i = states->begin(); i != states->end(); ++i) {
4214 string statename = *(*i);
4215 TreeModel::Row row = *(snapshot_display_model->append());
4217 /* this lingers on in case we ever want to change the visible
4218 name of the snapshot.
4221 string display_name;
4222 display_name = statename;
4224 if (statename == session->snap_name()) {
4225 snapshot_display.get_selection()->select(row);
4228 row[snapshot_display_columns.visible_name] = display_name;
4229 row[snapshot_display_columns.real_name] = statename;
4232 delete states;
4235 void
4236 Editor::session_state_saved (string snap_name)
4238 ENSURE_GUI_THREAD (bind (mem_fun(*this, &Editor::session_state_saved), snap_name));
4239 redisplay_snapshots ();
4242 void
4243 Editor::maximise_editing_space ()
4245 mouse_mode_tearoff->set_visible (false);
4246 tools_tearoff->set_visible (false);
4248 pre_maximal_pane_position = edit_pane.get_position();
4249 pre_maximal_editor_width = this->get_width();
4251 if(post_maximal_pane_position == 0) {
4252 post_maximal_pane_position = edit_pane.get_width();
4255 fullscreen();
4257 if(post_maximal_editor_width) {
4258 edit_pane.set_position (post_maximal_pane_position -
4259 abs(post_maximal_editor_width - pre_maximal_editor_width));
4260 } else {
4261 edit_pane.set_position (post_maximal_pane_position);
4265 void
4266 Editor::restore_editing_space ()
4268 // user changed width of pane during fullscreen
4269 if(post_maximal_pane_position != edit_pane.get_position()) {
4270 post_maximal_pane_position = edit_pane.get_position();
4273 unfullscreen();
4275 mouse_mode_tearoff->set_visible (true);
4276 tools_tearoff->set_visible (true);
4277 post_maximal_editor_width = this->get_width();
4280 edit_pane.set_position (
4281 pre_maximal_pane_position + abs(this->get_width() - pre_maximal_editor_width)
4286 * Make new playlists for a given track and also any others that belong
4287 * to the same active edit group.
4288 * @param v Track.
4291 void
4292 Editor::new_playlists (TimeAxisView* v)
4294 begin_reversible_command (_("new playlists"));
4295 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4296 session->get_playlists(playlists);
4297 mapover_audio_tracks ( bind(mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v );
4298 commit_reversible_command ();
4303 * Use a copy of the current playlist for a given track and also any others that belong
4304 * to the same active edit group.
4305 * @param v Track.
4308 void
4309 Editor::copy_playlists (TimeAxisView* v)
4311 begin_reversible_command (_("copy playlists"));
4312 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4313 session->get_playlists(playlists);
4314 mapover_audio_tracks ( bind(mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v );
4315 commit_reversible_command ();
4320 * Clear the current playlist for a given track and also any others that belong
4321 * to the same active edit group.
4322 * @param v Track.
4325 void
4326 Editor::clear_playlists (TimeAxisView* v)
4328 begin_reversible_command (_("clear playlists"));
4329 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4330 session->get_playlists(playlists);
4331 mapover_audio_tracks ( mem_fun (*this, &Editor::mapped_clear_playlist), v );
4332 commit_reversible_command ();
4335 void
4336 Editor::mapped_use_new_playlist (AudioTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4338 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4341 void
4342 Editor::mapped_use_copy_playlist (AudioTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4344 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4347 void
4348 Editor::mapped_clear_playlist (AudioTimeAxisView& atv, uint32_t sz)
4350 atv.clear_playlist ();
4353 bool
4354 Editor::on_key_press_event (GdkEventKey* ev)
4356 return key_press_focus_accelerator_handler (*this, ev);
4359 bool
4360 Editor::on_key_release_event (GdkEventKey* ev)
4362 return Gtk::Window::on_key_release_event (ev);
4363 // return key_press_focus_accelerator_handler (*this, ev);
4366 void
4367 Editor::reset_x_origin (nframes64_t frame)
4369 queue_visual_change (frame);
4372 void
4373 Editor::reset_zoom (double fpu)
4375 queue_visual_change (fpu);
4378 void
4379 Editor::reposition_and_zoom (nframes64_t frame, double fpu)
4381 //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
4382 reset_x_origin (frame);
4383 reset_zoom (fpu);
4385 if (!no_save_visual) {
4386 undo_visual_stack.push_back (current_visual_state(false));
4390 Editor::VisualState*
4391 Editor::current_visual_state (bool with_tracks)
4393 VisualState* vs = new VisualState;
4394 vs->y_position = vertical_adjustment.get_value();
4395 vs->frames_per_unit = frames_per_unit;
4396 vs->leftmost_frame = leftmost_frame;
4397 vs->zoom_focus = zoom_focus;
4399 if (with_tracks) {
4400 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4401 vs->track_states.push_back (TAVState ((*i), &(*i)->get_state()));
4405 return vs;
4408 void
4409 Editor::undo_visual_state ()
4411 if (undo_visual_stack.empty()) {
4412 return;
4415 redo_visual_stack.push_back (current_visual_state());
4417 VisualState* vs = undo_visual_stack.back();
4418 undo_visual_stack.pop_back();
4419 use_visual_state (*vs);
4422 void
4423 Editor::redo_visual_state ()
4425 if (redo_visual_stack.empty()) {
4426 return;
4429 undo_visual_stack.push_back (current_visual_state());
4431 VisualState* vs = redo_visual_stack.back();
4432 redo_visual_stack.pop_back();
4433 use_visual_state (*vs);
4436 void
4437 Editor::swap_visual_state ()
4439 if (undo_visual_stack.empty()) {
4440 redo_visual_state ();
4441 } else {
4442 undo_visual_state ();
4443 undo_visual_stack.clear(); //swap_visual_state truncates the undo stack so we are just bouncing between 2 states
4447 void
4448 Editor::use_visual_state (VisualState& vs)
4450 no_save_visual = true;
4451 no_route_list_redisplay = true;
4453 vertical_adjustment.set_value (vs.y_position);
4455 set_zoom_focus (vs.zoom_focus);
4456 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4458 for (list<TAVState>::iterator i = vs.track_states.begin(); i != vs.track_states.end(); ++i) {
4459 TrackViewList::iterator t;
4461 /* check if the track still exists - it could have been deleted */
4463 if ((t = find (track_views.begin(), track_views.end(), i->first)) != track_views.end()) {
4464 (*t)->set_state (*(i->second));
4468 if (!vs.track_states.empty()) {
4469 update_route_visibility ();
4472 no_route_list_redisplay = false;
4473 redisplay_route_list ();
4475 no_save_visual = false;
4478 void
4479 Editor::set_frames_per_unit (double fpu)
4481 /* this is the core function that controls the zoom level of the canvas. it is called
4482 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4485 if (fpu == frames_per_unit) {
4486 return;
4489 if (fpu < 1.0) {
4490 fpu = 1.0;
4494 /* don't allow zooms that fit more than the maximum number
4495 of frames into an 800 pixel wide space.
4498 if (max_frames / fpu < 800.0) {
4499 return;
4502 if (tempo_lines) {
4503 tempo_lines->tempo_map_changed();
4506 frames_per_unit = fpu;
4507 post_zoom ();
4510 void
4511 Editor::post_zoom ()
4513 nframes64_t cef = 0;
4515 // convert fpu to frame count
4517 nframes64_t frames = (nframes64_t) floor (frames_per_unit * canvas_width);
4519 if (frames_per_unit != zoom_range_clock.current_duration()) {
4520 zoom_range_clock.set (frames);
4523 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4524 if (!selection->tracks.empty()) {
4525 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4526 (*i)->reshow_selection (selection->time);
4528 } else {
4529 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4530 (*i)->reshow_selection (selection->time);
4534 update_loop_range_view (false);
4535 update_punch_range_view (false);
4537 if (playhead_cursor) {
4538 playhead_cursor->set_position (playhead_cursor->current_frame);
4541 leftmost_frame = (nframes64_t) floor (horizontal_adjustment.get_value() * frames_per_unit);
4543 ZoomChanged (); /* EMIT_SIGNAL */
4545 reset_hscrollbar_stepping ();
4547 if (session) {
4548 cef = session->current_end_frame() + (current_page_frames() / 10);// Add a little extra so we can see the end marker
4550 horizontal_adjustment.set_upper (cef / frames_per_unit);
4552 //reset_scrolling_region ();
4554 instant_save ();
4557 void
4558 Editor::queue_visual_change (nframes64_t where)
4560 pending_visual_change.pending = VisualChange::Type (pending_visual_change.pending | VisualChange::TimeOrigin);
4562 /* if we're moving beyond the end, make sure the upper limit of the horizontal adjustment
4563 can reach.
4566 if (where > session->current_end_frame()) {
4567 horizontal_adjustment.set_upper ((where + current_page_frames()) / frames_per_unit);
4570 pending_visual_change.time_origin = where;
4572 if (pending_visual_change.idle_handler_id < 0) {
4573 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4577 void
4578 Editor::queue_visual_change (double fpu)
4580 pending_visual_change.pending = VisualChange::Type (pending_visual_change.pending | VisualChange::ZoomLevel);
4581 pending_visual_change.frames_per_unit = fpu;
4583 if (pending_visual_change.idle_handler_id < 0) {
4584 pending_visual_change.idle_handler_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE, _idle_visual_changer, this, 0);
4589 Editor::_idle_visual_changer (void* arg)
4591 return static_cast<Editor*>(arg)->idle_visual_changer ();
4595 Editor::idle_visual_changer ()
4597 VisualChange::Type p = pending_visual_change.pending;
4598 pending_visual_change.pending = (VisualChange::Type) 0;
4600 double last_time_origin = horizontal_adjustment.get_value();
4601 if (p & VisualChange::ZoomLevel) {
4602 set_frames_per_unit (pending_visual_change.frames_per_unit);
4605 if (p & VisualChange::TimeOrigin) {
4606 horizontal_adjustment.set_value (pending_visual_change.time_origin / frames_per_unit);
4609 if (last_time_origin == horizontal_adjustment.get_value() ) {
4610 /* changed signal not emitted */
4611 update_fixed_rulers ();
4612 redisplay_tempo (true);
4614 // 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
4615 pending_visual_change.idle_handler_id = -1;
4616 return 0; /* this is always a one-shot call */
4619 struct EditorOrderTimeAxisSorter {
4620 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4621 return a->order < b->order;
4625 void
4626 Editor::sort_track_selection (TrackSelection* sel)
4628 EditorOrderTimeAxisSorter cmp;
4630 if (sel) {
4631 sel->sort (cmp);
4632 } else {
4633 selection->tracks.sort (cmp);
4637 nframes64_t
4638 Editor::get_preferred_edit_position (bool ignore_playhead)
4640 bool ignored;
4641 nframes64_t where = 0;
4642 EditPoint ep = _edit_point;
4644 if (entered_marker) {
4645 return entered_marker->position();
4648 if (ignore_playhead && ep == EditAtPlayhead) {
4649 ep = EditAtSelectedMarker;
4652 switch (ep) {
4653 case EditAtPlayhead:
4654 where = session->audible_frame();
4655 break;
4657 case EditAtSelectedMarker:
4658 if (!selection->markers.empty()) {
4659 bool is_start;
4660 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4661 if (loc) {
4662 if (is_start) {
4663 where = loc->start();
4664 } else {
4665 where = loc->end();
4667 break;
4670 /* fallthru */
4672 default:
4673 case EditAtMouse:
4674 if (!mouse_frame (where, ignored)) {
4675 /* XXX not right but what can we do ? */
4676 return 0;
4678 snap_to (where);
4679 break;
4682 return where;
4685 void
4686 Editor::set_loop_range (nframes64_t start, nframes64_t end, string cmd)
4688 if (!session) return;
4690 begin_reversible_command (cmd);
4692 Location* tll;
4694 if ((tll = transport_loop_location()) == 0) {
4695 Location* loc = new Location (start, end, _("Loop"), Location::IsAutoLoop);
4696 XMLNode &before = session->locations()->get_state();
4697 session->locations()->add (loc, true);
4698 session->set_auto_loop_location (loc);
4699 XMLNode &after = session->locations()->get_state();
4700 session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after));
4701 } else {
4702 XMLNode &before = tll->get_state();
4703 tll->set_hidden (false, this);
4704 tll->set (start, end);
4705 XMLNode &after = tll->get_state();
4706 session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4709 commit_reversible_command ();
4712 void
4713 Editor::set_punch_range (nframes64_t start, nframes64_t end, string cmd)
4715 if (!session) return;
4717 begin_reversible_command (cmd);
4719 Location* tpl;
4721 if ((tpl = transport_punch_location()) == 0) {
4722 Location* loc = new Location (start, end, _("Loop"), Location::IsAutoPunch);
4723 XMLNode &before = session->locations()->get_state();
4724 session->locations()->add (loc, true);
4725 session->set_auto_loop_location (loc);
4726 XMLNode &after = session->locations()->get_state();
4727 session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after));
4729 else {
4730 XMLNode &before = tpl->get_state();
4731 tpl->set_hidden (false, this);
4732 tpl->set (start, end);
4733 XMLNode &after = tpl->get_state();
4734 session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4737 commit_reversible_command ();
4740 void
4741 Editor::get_regions_at (RegionSelection& rs, nframes64_t where, const TrackSelection& ts) const
4743 const TrackSelection* tracks;
4745 if (ts.empty()) {
4746 tracks = &track_views;
4747 } else {
4748 tracks = &ts;
4751 for (TrackSelection::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4753 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*t);
4755 if (atv) {
4756 boost::shared_ptr<Diskstream> ds;
4757 boost::shared_ptr<Playlist> pl;
4759 if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()))) {
4761 Playlist::RegionList* regions = pl->regions_at ((nframes64_t) floor ( (double)where * ds->speed()));
4763 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4765 RegionView* rv = atv->audio_view()->find_view (*i);
4767 if (rv) {
4768 rs.add (rv);
4772 delete regions;
4778 void
4779 Editor::get_regions_after (RegionSelection& rs, nframes64_t where, const TrackSelection& ts) const
4781 const TrackSelection* tracks;
4783 if (ts.empty()) {
4784 tracks = &track_views;
4785 } else {
4786 tracks = &ts;
4789 for (TrackSelection::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4791 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*t);
4793 if (atv) {
4794 boost::shared_ptr<Diskstream> ds;
4795 boost::shared_ptr<Playlist> pl;
4797 if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()))) {
4799 Playlist::RegionList* regions = pl->regions_touched ((nframes64_t) floor ( (double)where * ds->speed()), max_frames);
4801 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4803 RegionView* rv = atv->audio_view()->find_view (*i);
4805 if (rv) {
4806 rs.push_back (rv);
4810 delete regions;
4816 void
4817 Editor::get_regions_for_action (RegionSelection& rs, bool allow_entered)
4819 if (selection->regions.empty()) {
4821 if (selection->tracks.empty()) {
4823 /* no regions or tracks selected
4826 if (entered_regionview && mouse_mode == Editing::MouseObject) {
4828 /* entered regionview is valid and we're in object mode -
4829 just use entered regionview
4832 rs.add (entered_regionview);
4835 return;
4837 } else {
4839 /* no regions selected, so get all regions at the edit point across
4840 all selected tracks.
4843 nframes64_t where = get_preferred_edit_position();
4844 get_regions_at (rs, where, selection->tracks);
4846 /* if the entered regionview wasn't selected and neither was its track
4847 then add it.
4850 if (entered_regionview != 0 &&
4851 !selection->selected (entered_regionview) &&
4852 !selection->selected (&entered_regionview->get_time_axis_view())) {
4853 rs.add (entered_regionview);
4857 } else {
4859 /* just use the selected regions */
4861 rs = selection->regions;
4863 /* if the entered regionview wasn't selected and we allow this sort of thing,
4864 then add it.
4867 if (allow_entered && entered_regionview && !selection->selected (entered_regionview)) {
4868 rs.add (entered_regionview);
4874 void
4875 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4878 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4880 RouteTimeAxisView* tatv;
4882 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4884 boost::shared_ptr<Playlist> pl;
4885 vector<boost::shared_ptr<Region> > results;
4886 RegionView* marv;
4887 boost::shared_ptr<Diskstream> ds;
4889 if ((ds = tatv->get_diskstream()) == 0) {
4890 /* bus */
4891 continue;
4894 if ((pl = (ds->playlist())) != 0) {
4895 pl->get_region_list_equivalent_regions (region, results);
4898 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4899 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4900 regions.push_back (marv);
4908 void
4909 Editor::show_rhythm_ferret ()
4911 if (rhythm_ferret == 0) {
4912 rhythm_ferret = new RhythmFerret(*this);
4915 rhythm_ferret->set_session (session);
4916 rhythm_ferret->show ();
4917 rhythm_ferret->present ();
4920 void
4921 Editor::first_idle ()
4923 MessageDialog* dialog = 0;
4925 if (track_views.size() > 1) {
4926 dialog = new MessageDialog (*this,
4927 string_compose (_("Please wait while %1 loads visual data"), PROGRAM_NAME),
4928 true,
4929 Gtk::MESSAGE_INFO,
4930 Gtk::BUTTONS_NONE);
4931 dialog->present ();
4932 ARDOUR_UI::instance()->flush_pending ();
4935 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4936 (*t)->first_idle();
4939 // first idle adds route children (automation tracks), so we need to redisplay here
4940 redisplay_route_list();
4942 if (dialog) {
4943 delete dialog;
4946 _have_idled = true;
4949 void
4950 Editor::start_resize_line_ops ()
4952 #if 0
4953 old_resize_line_y = -1;
4954 resize_line_y = -1;
4955 need_resize_line = true;
4956 #endif
4959 void
4960 Editor::end_resize_line_ops ()
4962 #if 0
4963 need_resize_line = false;
4965 if (old_resize_line_y >= 0) {
4966 Gdk::Rectangle r (0, old_resize_line_y, (int) canvas_width, 3);
4967 Glib::RefPtr<Gdk::Window> win = get_window();
4968 cerr << "Final invalidation at " << old_resize_line_y << endl;
4969 win->invalidate_rect (r, false);
4971 #endif
4974 void
4975 Editor::queue_draw_resize_line (int at)
4977 #if 0
4978 Glib::RefPtr<Gdk::Window> win = get_window();
4980 resize_line_y = at;
4982 if (win && canvas_width) {
4984 int controls_width = controls_layout.get_width();
4985 int xroot, discard;
4987 controls_layout.get_window()->get_origin (xroot, discard);
4989 if (old_resize_line_y >= 0) {
4991 /* redraw where it used to be */
4994 Gdk::Rectangle r (0, old_resize_line_y - 1, controls_width + (int) canvas_width, 3);
4995 win->invalidate_rect (r, true);
4996 cerr << "invalidate " << xroot << "," << old_resize_line_y - 1 << ' '
4997 << controls_width + canvas_width << " x 3\n";
5000 /* draw where it is */
5002 Gdk::Rectangle r (0, at - 1, controls_width + (int) canvas_width, 3);
5003 win->invalidate_rect (r, true);
5005 #endif
5008 bool
5009 Editor::on_expose_event (GdkEventExpose* ev)
5011 /* cerr << "+++ editor expose "
5012 << ev->area.x << ',' << ev->area.y
5013 << ' '
5014 << ev->area.width << " x " << ev->area.height
5015 << " need reize ? " << need_resize_line
5016 << endl;
5018 bool ret = Window::on_expose_event (ev);
5020 #if 0
5021 if (need_resize_line) {
5023 int xroot, yroot, discard;
5024 int controls_width;
5026 /* Our root coordinates for drawing the line will be the left edge
5027 of the track controls, and the upper left edge of our own window.
5030 get_window()->get_origin (discard, yroot);
5031 controls_layout.get_window()->get_origin (xroot, discard);
5032 controls_width = controls_layout.get_width();
5034 GdkRectangle lr;
5035 GdkRectangle intersection;
5037 lr.x = 0;
5038 lr.y = resize_line_y;
5039 lr.width = controls_width + (int) canvas_width;
5040 lr.height = 3;
5042 if (gdk_rectangle_intersect (&lr, &ev->area, &intersection)) {
5044 Glib::RefPtr<Gtk::Style> style (get_style());
5045 Glib::RefPtr<Gdk::GC> black_gc (style->get_black_gc ());
5046 Glib::RefPtr<Gdk::GC> gc = wrap (black_gc->gobj_copy(), false);
5048 /* draw on root window */
5050 GdkWindow* win = gdk_get_default_root_window();
5052 gc->set_subwindow (Gdk::INCLUDE_INFERIORS);
5053 gc->set_line_attributes (3, Gdk::LINE_SOLID,
5054 Gdk::CAP_NOT_LAST,
5055 Gdk::JOIN_MITER);
5057 gdk_draw_line (win, gc->gobj(),
5059 resize_line_y,
5060 (int) canvas_width + controls_width,
5061 resize_line_y);
5062 #if 0
5063 cerr << "drew line @ " << xroot << ", " << yroot + resize_line_y
5064 << " to " << xroot + (int) canvas_width + controls_width
5065 << ", " << yroot + resize_line_y
5066 << endl;
5067 #endif
5068 old_resize_line_y = resize_line_y;
5069 cerr << "NEXT EXPOSE SHOULD BE AT " << old_resize_line_y << endl;
5070 } else {
5071 cerr << "no intersect with "
5072 << lr.x << ',' << lr.y
5073 << ' '
5074 << lr.width << " x " << lr.height
5075 << endl;
5079 //cerr << "--- editor expose\n";
5080 #endif
5082 return ret;
5085 static gboolean
5086 _idle_resizer (gpointer arg)
5088 return ((Editor*)arg)->idle_resize ();
5091 void
5092 Editor::add_to_idle_resize (TimeAxisView* view, uint32_t h)
5094 if (resize_idle_id < 0) {
5095 resize_idle_id = g_idle_add (_idle_resizer, this);
5098 resize_idle_target = h;
5100 pending_resizes.push_back (view);
5102 if (selection->selected (view) && !selection->tracks.empty()) {
5103 pending_resizes.insert (pending_resizes.end(), selection->tracks.begin(), selection->tracks.end());
5107 bool
5108 Editor::idle_resize ()
5110 for (vector<TimeAxisView*>::iterator i = pending_resizes.begin(); i != pending_resizes.end(); ++i) {
5111 (*i)->idle_resize (resize_idle_target);
5113 pending_resizes.clear();
5114 //flush_canvas ();
5115 resize_idle_id = -1;
5116 return false;
5119 void
5120 Editor::change_region_layering_order (nframes64_t position)
5122 if (clicked_regionview == 0) {
5123 if (layering_order_editor) {
5124 layering_order_editor->hide ();
5126 return;
5129 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_trackview);
5131 if (atv == 0) {
5132 return;
5135 boost::shared_ptr<Diskstream> ds;
5136 boost::shared_ptr<Playlist> pl;
5138 if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()))) {
5140 if (layering_order_editor == 0) {
5141 layering_order_editor = new RegionLayeringOrderEditor(*this);
5143 layering_order_editor->set_context (atv->name(), session, pl, position);
5144 layering_order_editor->maybe_present ();
5148 void
5149 Editor::update_region_layering_order_editor (nframes64_t frame)
5151 if (layering_order_editor && layering_order_editor->is_visible ()) {
5152 change_region_layering_order (frame);