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 /* Note: public Editor methods are documented in public_editor.h */
28 #include <sigc++/bind.h>
30 #include <pbd/convert.h>
31 #include <pbd/error.h>
32 #include <pbd/enumwriter.h>
33 #include <pbd/memento_command.h>
35 #include <glibmm/miscutils.h>
36 #include <gtkmm/image.h>
37 #include <gdkmm/color.h>
38 #include <gdkmm/bitmap.h>
40 #include <gtkmm2ext/grouped_buttons.h>
41 #include <gtkmm2ext/gtk_ui.h>
42 #include <gtkmm2ext/tearoff.h>
43 #include <gtkmm2ext/utils.h>
44 #include <gtkmm2ext/window_title.h>
45 #include <gtkmm2ext/choice.h>
47 #include <ardour/audio_track.h>
48 #include <ardour/audio_diskstream.h>
49 #include <ardour/plugin_manager.h>
50 #include <ardour/location.h>
51 #include <ardour/audioplaylist.h>
52 #include <ardour/audioregion.h>
53 #include <ardour/region.h>
54 #include <ardour/session_route.h>
55 #include <ardour/tempo.h>
56 #include <ardour/utils.h>
58 #include <control_protocol/control_protocol.h>
60 #include "ardour_ui.h"
64 #include "playlist_selector.h"
65 #include "audio_region_view.h"
66 #include "rgb_macros.h"
67 #include "selection.h"
68 #include "audio_streamview.h"
69 #include "time_axis_view.h"
70 #include "audio_time_axis.h"
72 #include "crossfade_view.h"
74 #include "public_editor.h"
75 #include "crossfade_edit.h"
76 #include "canvas_impl.h"
78 #include "gui_thread.h"
81 #include "analysis_window.h"
87 #include "imageframe_socket_handler.h"
92 using namespace ARDOUR;
96 using namespace Gtkmm2ext;
97 using namespace Editing;
99 using PBD::internationalize;
102 const double Editor::timebar_height = 15.0;
104 #include "editor_xpms"
106 static const gchar *_snap_type_strings[] = {
130 static const gchar *_snap_mode_strings[] = {
136 static const gchar *_zoom_focus_strings[] = {
145 /* Soundfile drag-n-drop */
147 Gdk::Cursor* Editor::cross_hair_cursor = 0;
148 Gdk::Cursor* Editor::selector_cursor = 0;
149 Gdk::Cursor* Editor::trimmer_cursor = 0;
150 Gdk::Cursor* Editor::grabber_cursor = 0;
151 Gdk::Cursor* Editor::zoom_cursor = 0;
152 Gdk::Cursor* Editor::time_fx_cursor = 0;
153 Gdk::Cursor* Editor::fader_cursor = 0;
154 Gdk::Cursor* Editor::speaker_cursor = 0;
155 Gdk::Cursor* Editor::note_cursor = 0;
156 Gdk::Cursor* Editor::wait_cursor = 0;
157 Gdk::Cursor* Editor::timebar_cursor = 0;
160 show_me_the_size (Requisition* r, const char* what)
162 cerr << "size of " << what << " = " << r->width << " x " << r->height << endl;
166 check_adjustment (Gtk::Adjustment* adj)
168 cerr << "CHANGE adj = "
169 << adj->get_lower () << ' '
170 << adj->get_upper () << ' '
171 << adj->get_value () << ' '
172 << adj->get_step_increment () << ' '
173 << adj->get_page_increment () << ' '
174 << adj->get_page_size () << ' '
181 /* time display buttons */
183 minsec_label (_("Mins:Secs")),
184 bbt_label (_("Bars:Beats")),
185 smpte_label (_("Timecode")),
186 frame_label (_("Frames")),
187 tempo_label (_("Tempo")),
188 meter_label (_("Meter")),
189 mark_label (_("Location Markers")),
190 range_mark_label (_("Range Markers")),
191 transport_mark_label (_("Loop/Punch Ranges")),
193 edit_packer (3, 3, false),
195 /* the values here don't matter: layout widgets
196 reset them as needed.
199 vertical_adjustment (0.0, 0.0, 10.0, 400.0),
200 horizontal_adjustment (0.0, 0.0, 20.0, 1200.0),
202 /* tool bar related */
204 edit_cursor_clock (X_("editcursor"), false, X_("EditCursorClock"), true),
205 zoom_range_clock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, true),
207 toolbar_selection_clock_table (2,3),
209 automation_mode_button (_("mode")),
210 global_automation_button (_("automation")),
213 image_socket_listener(0),
218 nudge_clock (X_("nudge"), false, X_("NudgeClock"), true, true)
223 /* we are a singleton */
225 PublicEditor::_instance = this;
229 selection = new Selection;
230 cut_buffer = new Selection;
232 selection->TimeChanged.connect (mem_fun(*this, &Editor::time_selection_changed));
233 selection->TracksChanged.connect (mem_fun(*this, &Editor::track_selection_changed));
234 selection->RegionsChanged.connect (mem_fun(*this, &Editor::region_selection_changed));
235 selection->PointsChanged.connect (mem_fun(*this, &Editor::point_selection_changed));
237 clicked_regionview = 0;
238 clicked_axisview = 0;
239 clicked_routeview = 0;
240 clicked_crossfadeview = 0;
241 clicked_control_point = 0;
242 latest_regionview = 0;
243 last_update_frame = 0;
245 current_mixer_strip = 0;
246 current_bbt_points = 0;
248 snap_type_strings = I18N (_snap_type_strings);
249 snap_mode_strings = I18N (_snap_mode_strings);
250 zoom_focus_strings = I18N(_zoom_focus_strings);
252 snap_type = SnapToFrame;
253 set_snap_to (snap_type);
254 snap_mode = SnapNormal;
255 set_snap_mode (snap_mode);
256 snap_threshold = 5.0;
257 bbt_beat_subdivision = 4;
260 autoscroll_active = false;
261 autoscroll_timeout_tag = -1;
262 interthread_progress_window = 0;
268 current_interthread_info = 0;
269 _show_measures = true;
270 _show_waveforms = true;
271 _show_waveforms_recording = true;
272 first_action_message = 0;
274 show_gain_after_trim = false;
275 ignore_route_list_reorder = false;
276 no_route_list_redisplay = false;
277 verbose_cursor_on = true;
278 route_removal = false;
279 show_automatic_regions_in_region_list = true;
280 region_list_sort_type = (Editing::RegionListSortType) 0;
281 have_pending_keyboard_selection = false;
282 _follow_playhead = true;
283 _xfade_visibility = true;
284 editor_ruler_menu = 0;
285 no_ruler_shown_update = false;
286 edit_group_list_menu = 0;
288 region_list_menu = 0;
290 start_end_marker_menu = 0;
291 range_marker_menu = 0;
292 marker_menu_item = 0;
294 transport_marker_menu = 0;
295 new_transport_marker_menu = 0;
296 editor_mixer_strip_width = Wide;
297 show_editor_mixer_when_tracks_arrive = false;
300 ignore_mouse_mode_toggle = false;
301 current_stepping_trackview = 0;
303 entered_regionview = 0;
304 clear_entered_track = false;
305 _new_regionviews_show_envelope = false;
306 current_timestretch = 0;
307 in_edit_group_row_change = false;
308 last_canvas_frame = 0;
311 button_release_can_deselect = true;
312 canvas_idle_queued = false;
313 _dragging_playhead = false;
314 _dragging_hscrollbar = false;
316 location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get();
317 location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get();
318 location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get();
319 location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get();
320 location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get();
322 range_marker_drag_rect = 0;
323 marker_drag_line = 0;
325 set_mouse_mode (MouseObject, true);
327 frames_per_unit = 2048; /* too early to use reset_zoom () */
328 reset_hscrollbar_stepping ();
330 zoom_focus = ZoomFocusLeft;
331 set_zoom_focus (ZoomFocusLeft);
332 zoom_range_clock.ValueChanged.connect (mem_fun(*this, &Editor::zoom_adjustment_changed));
334 initialize_rulers ();
335 initialize_canvas ();
337 edit_controls_vbox.set_spacing (0);
338 horizontal_adjustment.signal_value_changed().connect (mem_fun(*this, &Editor::canvas_horizontally_scrolled));
339 vertical_adjustment.signal_value_changed().connect (mem_fun(*this, &Editor::tie_vertical_scrolling));
341 track_canvas.set_hadjustment (horizontal_adjustment);
342 track_canvas.set_vadjustment (vertical_adjustment);
343 time_canvas.set_hadjustment (horizontal_adjustment);
345 track_canvas.signal_map_event().connect (mem_fun (*this, &Editor::track_canvas_map_handler));
346 time_canvas.signal_map_event().connect (mem_fun (*this, &Editor::time_canvas_map_handler));
348 controls_layout.add (edit_controls_vbox);
349 controls_layout.set_name ("EditControlsBase");
350 controls_layout.add_events (Gdk::SCROLL_MASK);
351 controls_layout.signal_scroll_event().connect (mem_fun(*this, &Editor::control_layout_scroll), false);
353 controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
354 controls_layout.signal_button_release_event().connect (mem_fun(*this, &Editor::edit_controls_button_release));
355 controls_layout.signal_size_request().connect (mem_fun (*this, &Editor::controls_layout_size_request));
357 edit_vscrollbar.set_adjustment (vertical_adjustment);
358 edit_hscrollbar.set_adjustment (horizontal_adjustment);
360 edit_hscrollbar.signal_button_press_event().connect (mem_fun(*this, &Editor::hscrollbar_button_press), false);
361 edit_hscrollbar.signal_button_release_event().connect (mem_fun(*this, &Editor::hscrollbar_button_release), false);
362 edit_hscrollbar.signal_size_allocate().connect (mem_fun(*this, &Editor::hscrollbar_allocate));
364 edit_hscrollbar.set_name ("EditorHScrollbar");
369 edit_cursor_clock.ValueChanged.connect (mem_fun(*this, &Editor::edit_cursor_clock_changed));
371 time_canvas_vbox.pack_start (*_ruler_separator, false, false);
372 time_canvas_vbox.pack_start (*minsec_ruler, false, false);
373 time_canvas_vbox.pack_start (*smpte_ruler, false, false);
374 time_canvas_vbox.pack_start (*frames_ruler, false, false);
375 time_canvas_vbox.pack_start (*bbt_ruler, false, false);
376 time_canvas_vbox.pack_start (time_canvas, true, true);
377 time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2);
379 bbt_label.set_name ("EditorTimeButton");
380 bbt_label.set_size_request (-1, (int)timebar_height);
381 bbt_label.set_alignment (1.0, 0.5);
382 bbt_label.set_padding (5,0);
383 minsec_label.set_name ("EditorTimeButton");
384 minsec_label.set_size_request (-1, (int)timebar_height);
385 minsec_label.set_alignment (1.0, 0.5);
386 minsec_label.set_padding (5,0);
387 smpte_label.set_name ("EditorTimeButton");
388 smpte_label.set_size_request (-1, (int)timebar_height);
389 smpte_label.set_alignment (1.0, 0.5);
390 smpte_label.set_padding (5,0);
391 frame_label.set_name ("EditorTimeButton");
392 frame_label.set_size_request (-1, (int)timebar_height);
393 frame_label.set_alignment (1.0, 0.5);
394 frame_label.set_padding (5,0);
395 tempo_label.set_name ("EditorTimeButton");
396 tempo_label.set_size_request (-1, (int)timebar_height);
397 tempo_label.set_alignment (1.0, 0.5);
398 tempo_label.set_padding (5,0);
399 meter_label.set_name ("EditorTimeButton");
400 meter_label.set_size_request (-1, (int)timebar_height);
401 meter_label.set_alignment (1.0, 0.5);
402 meter_label.set_padding (5,0);
403 mark_label.set_name ("EditorTimeButton");
404 mark_label.set_size_request (-1, (int)timebar_height);
405 mark_label.set_alignment (1.0, 0.5);
406 mark_label.set_padding (5,0);
407 range_mark_label.set_name ("EditorTimeButton");
408 range_mark_label.set_size_request (-1, (int)timebar_height);
409 range_mark_label.set_alignment (1.0, 0.5);
410 range_mark_label.set_padding (5,0);
411 transport_mark_label.set_name ("EditorTimeButton");
412 transport_mark_label.set_size_request (-1, (int)timebar_height);
413 transport_mark_label.set_alignment (1.0, 0.5);
414 transport_mark_label.set_padding (5,0);
416 time_button_vbox.pack_start (minsec_label, false, false);
417 time_button_vbox.pack_start (smpte_label, false, false);
418 time_button_vbox.pack_start (frame_label, false, false);
419 time_button_vbox.pack_start (bbt_label, false, false);
420 time_button_vbox.pack_start (meter_label, false, false);
421 time_button_vbox.pack_start (tempo_label, false, false);
422 time_button_vbox.pack_start (mark_label, false, false);
424 time_button_event_box.add (time_button_vbox);
426 time_button_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
427 time_button_event_box.set_name ("TimebarLabelBase");
428 time_button_event_box.signal_button_release_event().connect (mem_fun(*this, &Editor::ruler_label_button_release));
430 time_button_frame.add(time_button_event_box);
431 time_button_frame.property_shadow_type() = Gtk::SHADOW_OUT;
433 /* these enable us to have a dedicated window (for cursor setting, etc.)
434 for the canvas areas.
437 track_canvas_event_box.add (track_canvas);
439 time_canvas_event_box.add (time_canvas_vbox);
440 time_canvas_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
442 edit_packer.set_col_spacings (0);
443 edit_packer.set_row_spacings (0);
444 edit_packer.set_homogeneous (false);
445 edit_packer.set_border_width (0);
446 edit_packer.set_name ("EditorWindow");
448 edit_packer.attach (edit_vscrollbar, 3, 4, 1, 2, FILL, FILL|EXPAND, 0, 0);
450 edit_packer.attach (time_button_frame, 0, 2, 0, 1, FILL, FILL, 0, 0);
451 edit_packer.attach (time_canvas_event_box, 2, 4, 0, 1, FILL|EXPAND, FILL, 0, 0);
453 edit_packer.attach (controls_layout, 1, 2, 1, 2, FILL, FILL|EXPAND, 0, 0);
454 edit_packer.attach (track_canvas_event_box, 2, 3, 1, 2, FILL|EXPAND, FILL|EXPAND, 0, 0);
456 edit_packer.attach (zoom_box, 1, 2, 2, 3, FILL, FILL, 0, 0);
457 edit_packer.attach (edit_hscrollbar, 2, 3, 2, 3, FILL|EXPAND, FILL, 0, 0);
459 bottom_hbox.set_border_width (2);
460 bottom_hbox.set_spacing (3);
462 route_display_model = ListStore::create(route_display_columns);
463 route_list_display.set_model (route_display_model);
464 route_list_display.append_column (_("Show"), route_display_columns.visible);
465 route_list_display.append_column (_("Name"), route_display_columns.text);
466 route_list_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
467 route_list_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
468 route_list_display.set_headers_visible (true);
469 route_list_display.set_name ("TrackListDisplay");
470 route_list_display.get_selection()->set_mode (SELECTION_NONE);
471 route_list_display.set_reorderable (true);
472 route_list_display.set_size_request (100,-1);
474 CellRendererToggle* route_list_visible_cell = dynamic_cast<CellRendererToggle*>(route_list_display.get_column_cell_renderer (0));
475 route_list_visible_cell->property_activatable() = true;
476 route_list_visible_cell->property_radio() = false;
478 route_display_model->signal_row_deleted().connect (mem_fun (*this, &Editor::route_list_delete));
479 route_display_model->signal_row_changed().connect (mem_fun (*this, &Editor::route_list_change));
481 route_list_display.signal_button_press_event().connect (mem_fun (*this, &Editor::route_list_display_button_press), false);
483 route_list_scroller.add (route_list_display);
484 route_list_scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
486 group_model = ListStore::create(group_columns);
487 edit_group_display.set_model (group_model);
488 edit_group_display.append_column (_("Name"), group_columns.text);
489 edit_group_display.append_column (_("Active"), group_columns.is_active);
490 edit_group_display.append_column (_("Show"), group_columns.is_visible);
491 edit_group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
492 edit_group_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
493 edit_group_display.get_column (2)->set_data (X_("colnum"), GUINT_TO_POINTER(2));
494 edit_group_display.get_column (0)->set_expand (true);
495 edit_group_display.get_column (1)->set_expand (false);
496 edit_group_display.get_column (2)->set_expand (false);
497 edit_group_display.set_headers_visible (true);
499 /* name is directly editable */
501 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(edit_group_display.get_column_cell_renderer (0));
502 name_cell->property_editable() = true;
503 name_cell->signal_edited().connect (mem_fun (*this, &Editor::edit_group_name_edit));
505 /* use checkbox for the active + visible columns */
507 CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*>(edit_group_display.get_column_cell_renderer (1));
508 active_cell->property_activatable() = true;
509 active_cell->property_radio() = false;
511 active_cell = dynamic_cast<CellRendererToggle*>(edit_group_display.get_column_cell_renderer (1));
512 active_cell->property_activatable() = true;
513 active_cell->property_radio() = false;
515 group_model->signal_row_changed().connect (mem_fun (*this, &Editor::edit_group_row_change));
517 edit_group_display.set_name ("EditGroupList");
518 edit_group_display.get_selection()->set_mode (SELECTION_SINGLE);
519 edit_group_display.set_headers_visible (true);
520 edit_group_display.set_reorderable (false);
521 edit_group_display.set_rules_hint (true);
522 edit_group_display.set_size_request (75, -1);
524 edit_group_display_scroller.add (edit_group_display);
525 edit_group_display_scroller.set_policy (POLICY_AUTOMATIC, POLICY_AUTOMATIC);
527 edit_group_display.signal_button_press_event().connect (mem_fun(*this, &Editor::edit_group_list_button_press_event), false);
529 VBox* edit_group_display_packer = manage (new VBox());
530 HBox* edit_group_display_button_box = manage (new HBox());
531 edit_group_display_button_box->set_homogeneous (true);
533 Button* edit_group_add_button = manage (new Button ());
534 Button* edit_group_remove_button = manage (new Button ());
538 w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
540 edit_group_add_button->add (*w);
542 w = manage (new Image (Stock::REMOVE, ICON_SIZE_BUTTON));
544 edit_group_remove_button->add (*w);
546 edit_group_add_button->signal_clicked().connect (mem_fun (*this, &Editor::new_edit_group));
547 edit_group_remove_button->signal_clicked().connect (mem_fun (*this, &Editor::remove_selected_edit_group));
549 edit_group_display_button_box->pack_start (*edit_group_add_button);
550 edit_group_display_button_box->pack_start (*edit_group_remove_button);
552 edit_group_display_packer->pack_start (edit_group_display_scroller, true, true);
553 edit_group_display_packer->pack_start (*edit_group_display_button_box, false, false);
555 region_list_display.set_size_request (100, -1);
556 region_list_display.set_name ("RegionListDisplay");
558 region_list_model = TreeStore::create (region_list_columns);
559 region_list_model->set_sort_func (0, mem_fun (*this, &Editor::region_list_sorter));
560 region_list_model->set_sort_column (0, SORT_ASCENDING);
562 region_list_display.set_model (region_list_model);
563 region_list_display.append_column (_("Regions"), region_list_columns.name);
564 region_list_display.set_headers_visible (false);
566 region_list_display.get_selection()->set_select_function (mem_fun (*this, &Editor::region_list_selection_filter));
568 TreeViewColumn* tv_col = region_list_display.get_column(0);
569 CellRendererText* renderer = dynamic_cast<CellRendererText*>(region_list_display.get_column_cell_renderer (0));
570 tv_col->add_attribute(renderer->property_text(), region_list_columns.name);
571 tv_col->add_attribute(renderer->property_foreground_gdk(), region_list_columns.color_);
573 region_list_display.get_selection()->set_mode (SELECTION_MULTIPLE);
574 region_list_display.add_object_drag (region_list_columns.region.index(), "regions");
576 /* setup DnD handling */
578 list<TargetEntry> region_list_target_table;
580 region_list_target_table.push_back (TargetEntry ("text/plain"));
581 region_list_target_table.push_back (TargetEntry ("text/uri-list"));
582 region_list_target_table.push_back (TargetEntry ("application/x-rootwin-drop"));
584 region_list_display.add_drop_targets (region_list_target_table);
585 region_list_display.signal_drag_data_received().connect (mem_fun(*this, &Editor::region_list_display_drag_data_received));
587 region_list_scroller.add (region_list_display);
588 region_list_scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
590 region_list_display.signal_key_press_event().connect (mem_fun(*this, &Editor::region_list_display_key_press));
591 region_list_display.signal_key_release_event().connect (mem_fun(*this, &Editor::region_list_display_key_release));
592 region_list_display.signal_button_press_event().connect (mem_fun(*this, &Editor::region_list_display_button_press), false);
593 region_list_display.signal_button_release_event().connect (mem_fun(*this, &Editor::region_list_display_button_release));
594 region_list_display.get_selection()->signal_changed().connect (mem_fun(*this, &Editor::region_list_selection_changed));
595 // region_list_display.signal_popup_menu().connect (bind (mem_fun (*this, &Editor::show_region_list_display_context_menu), 1, 0));
597 named_selection_scroller.add (named_selection_display);
598 named_selection_scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
600 named_selection_model = TreeStore::create (named_selection_columns);
601 named_selection_display.set_model (named_selection_model);
602 named_selection_display.append_column (_("Chunks"), named_selection_columns.text);
603 named_selection_display.set_headers_visible (false);
604 named_selection_display.set_size_request (100, -1);
605 named_selection_display.set_name ("NamedSelectionDisplay");
607 named_selection_display.get_selection()->set_mode (SELECTION_SINGLE);
608 named_selection_display.set_size_request (100, -1);
609 named_selection_display.signal_button_release_event().connect (mem_fun(*this, &Editor::named_selection_display_button_release), false);
610 named_selection_display.signal_key_release_event().connect (mem_fun(*this, &Editor::named_selection_display_key_release), false);
611 named_selection_display.get_selection()->signal_changed().connect (mem_fun (*this, &Editor::named_selection_display_selection_changed));
615 snapshot_display_model = ListStore::create (snapshot_display_columns);
616 snapshot_display.set_model (snapshot_display_model);
617 snapshot_display.append_column (X_("snapshot"), snapshot_display_columns.visible_name);
618 snapshot_display.set_name ("SnapshotDisplay");
619 snapshot_display.set_size_request (75, -1);
620 snapshot_display.set_headers_visible (false);
621 snapshot_display.set_reorderable (false);
622 snapshot_display_scroller.add (snapshot_display);
623 snapshot_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
625 snapshot_display.get_selection()->signal_changed().connect (mem_fun(*this, &Editor::snapshot_display_selection_changed));
626 snapshot_display.signal_button_press_event().connect (mem_fun (*this, &Editor::snapshot_display_button_press), false);
630 nlabel = manage (new Label (_("Regions")));
631 nlabel->set_angle (-90);
632 the_notebook.append_page (region_list_scroller, *nlabel);
633 nlabel = manage (new Label (_("Tracks/Busses")));
634 nlabel->set_angle (-90);
635 the_notebook.append_page (route_list_scroller, *nlabel);
636 nlabel = manage (new Label (_("Snapshots")));
637 nlabel->set_angle (-90);
638 the_notebook.append_page (snapshot_display_scroller, *nlabel);
639 nlabel = manage (new Label (_("Edit Groups")));
640 nlabel->set_angle (-90);
641 the_notebook.append_page (*edit_group_display_packer, *nlabel);
642 nlabel = manage (new Label (_("Chunks")));
643 nlabel->set_angle (-90);
644 the_notebook.append_page (named_selection_scroller, *nlabel);
646 the_notebook.set_show_tabs (true);
647 the_notebook.set_scrollable (true);
648 the_notebook.popup_enable ();
649 the_notebook.set_tab_pos (Gtk::POS_RIGHT);
651 post_maximal_editor_width = 0;
652 post_maximal_pane_position = 0;
653 edit_pane.pack1 (edit_packer, true, true);
654 edit_pane.pack2 (the_notebook, false, true);
656 edit_pane.signal_size_allocate().connect (bind (mem_fun(*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&edit_pane)));
658 top_hbox.pack_start (toolbar_frame, true, true);
660 HBox *hbox = manage (new HBox);
661 hbox->pack_start (edit_pane, true, true);
663 global_vpacker.pack_start (top_hbox, false, false);
664 global_vpacker.pack_start (*hbox, true, true);
666 global_hpacker.pack_start (global_vpacker, true, true);
668 set_name ("EditorWindow");
669 add_accel_group (ActionManager::ui_manager->get_accel_group());
671 vpacker.pack_end (global_hpacker, true, true);
673 /* register actions now so that set_state() can find them and set toggles/checks etc */
677 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
680 _playlist_selector = new PlaylistSelector();
681 _playlist_selector->signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
683 RegionView::RegionViewGoingAway.connect (mem_fun(*this, &Editor::catch_vanishing_regionview));
687 nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
688 nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
690 ARDOUR_UI::instance()->tooltips().set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
691 ARDOUR_UI::instance()->tooltips().set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
693 nudge_forward_button.set_name ("TransportButton");
694 nudge_backward_button.set_name ("TransportButton");
696 fade_context_menu.set_name ("ArdourContextMenu");
698 /* icons, titles, WM stuff */
700 list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
701 Glib::RefPtr<Gdk::Pixbuf> icon;
703 if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
704 window_icons.push_back (icon);
706 if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
707 window_icons.push_back (icon);
709 if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
710 window_icons.push_back (icon);
712 if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
713 window_icons.push_back (icon);
715 if (!window_icons.empty()) {
716 set_icon_list (window_icons);
717 set_default_icon_list (window_icons);
720 WindowTitle title(Glib::get_application_name());
721 title += _("Editor");
722 set_title (title.get_string());
723 set_wmclass (X_("ardour_editor"), "Ardour");
726 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
728 signal_configure_event().connect (mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
729 signal_delete_event().connect (mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
731 /* allow external control surfaces/protocols to do various things */
733 ControlProtocol::ZoomToSession.connect (mem_fun (*this, &Editor::temporal_zoom_session));
734 ControlProtocol::ZoomIn.connect (bind (mem_fun (*this, &Editor::temporal_zoom_step), false));
735 ControlProtocol::ZoomOut.connect (bind (mem_fun (*this, &Editor::temporal_zoom_step), true));
736 ControlProtocol::ScrollTimeline.connect (mem_fun (*this, &Editor::control_scroll));
738 Config->ParameterChanged.connect (mem_fun (*this, &Editor::parameter_changed));
747 if(image_socket_listener)
749 if(image_socket_listener->is_connected())
751 image_socket_listener->close_connection() ;
754 delete image_socket_listener ;
755 image_socket_listener = 0 ;
761 Editor::add_toplevel_controls (Container& cont)
763 vpacker.pack_start (cont, false, false);
768 Editor::catch_vanishing_regionview (RegionView *rv)
770 /* note: the selection will take care of the vanishing
771 audioregionview by itself.
774 if (clicked_regionview == rv) {
775 clicked_regionview = 0;
778 if (entered_regionview == rv) {
779 set_entered_regionview (0);
784 Editor::set_entered_regionview (RegionView* rv)
786 if (rv == entered_regionview) {
790 if (entered_regionview) {
791 entered_regionview->exited ();
794 if ((entered_regionview = rv) != 0) {
795 entered_regionview->entered ();
800 Editor::set_entered_track (TimeAxisView* tav)
803 entered_track->exited ();
806 if ((entered_track = tav) != 0) {
807 entered_track->entered ();
812 Editor::show_window ()
817 /* re-hide editor list if necessary */
818 editor_list_button_toggled ();
820 /* now reset all audio_time_axis heights, because widgets might need
826 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
827 tv = (static_cast<TimeAxisView*>(*i));
833 Editor::tie_vertical_scrolling ()
835 double y1 = vertical_adjustment.get_value();
836 controls_layout.get_vadjustment()->set_value (y1);
837 playhead_cursor->set_y_axis(y1);
838 edit_cursor->set_y_axis(y1);
842 Editor::instant_save ()
844 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
849 session->add_instant_xml(get_state());
851 Config->add_instant_xml(get_state());
856 Editor::edit_cursor_clock_changed()
858 if (edit_cursor->current_frame != edit_cursor_clock.current_time()) {
859 edit_cursor->set_position (edit_cursor_clock.current_time());
865 Editor::zoom_adjustment_changed ()
871 double fpu = zoom_range_clock.current_duration() / canvas_width;
875 zoom_range_clock.set ((nframes_t) floor (fpu * canvas_width));
876 } else if (fpu > session->current_end_frame() / canvas_width) {
877 fpu = session->current_end_frame() / canvas_width;
878 zoom_range_clock.set ((nframes_t) floor (fpu * canvas_width));
885 Editor::control_scroll (float fraction)
887 ENSURE_GUI_THREAD(bind (mem_fun (*this, &Editor::control_scroll), fraction));
893 double step = fraction * current_page_frames();
896 if ((fraction < 0.0f) && (session->transport_frame() < (nframes_t) fabs(step))) {
898 } else if ((fraction > 0.0f) && (max_frames - session->transport_frame() < step)) {
899 target = (max_frames - (current_page_frames()*2)); // allow room for slop in where the PH is on the screen
901 target = (session->transport_frame() + (nframes_t) floor ((fraction * current_page_frames())));
904 /* move visuals, we'll catch up with it later */
906 playhead_cursor->set_position (target);
908 if (target > (current_page_frames() / 2)) {
909 /* try to center PH in window */
910 reset_x_origin (target - (current_page_frames()/2));
915 /* cancel the existing */
917 control_scroll_connection.disconnect ();
919 /* add the next one */
921 control_scroll_connection = Glib::signal_timeout().connect (bind (mem_fun (*this, &Editor::deferred_control_scroll), target), 50);
925 Editor::deferred_control_scroll (nframes_t target)
927 session->request_locate (target);
932 Editor::on_realize ()
934 Window::on_realize ();
939 Editor::start_scrolling ()
941 scroll_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect
942 (mem_fun(*this, &Editor::update_current_screen));
946 Editor::stop_scrolling ()
948 scroll_connection.disconnect ();
952 Editor::map_position_change (nframes_t frame)
954 ENSURE_GUI_THREAD (bind (mem_fun(*this, &Editor::map_position_change), frame));
956 if (session == 0 || !_follow_playhead) {
960 center_screen (frame);
961 playhead_cursor->set_position (frame);
965 Editor::center_screen (nframes_t frame)
967 double page = canvas_width * frames_per_unit;
969 /* if we're off the page, then scroll.
972 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
973 center_screen_internal (frame, page);
978 Editor::center_screen_internal (nframes_t frame, float page)
983 frame -= (nframes_t) page;
988 reset_x_origin (frame);
992 Editor::handle_new_duration ()
994 ENSURE_GUI_THREAD (mem_fun (*this, &Editor::handle_new_duration));
996 nframes_t new_end = session->get_maximum_extent() + (nframes_t) floorf (current_page_frames() * 0.10f);
998 if (new_end > last_canvas_frame) {
999 last_canvas_frame = new_end;
1000 horizontal_adjustment.set_upper (last_canvas_frame / frames_per_unit);
1001 reset_scrolling_region ();
1004 horizontal_adjustment.set_value (leftmost_frame/frames_per_unit);
1008 Editor::update_title_s (const string & snap_name)
1010 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::update_title_s), snap_name));
1016 Editor::update_title ()
1018 ENSURE_GUI_THREAD (mem_fun(*this, &Editor::update_title));
1021 bool dirty = session->dirty();
1023 string session_name;
1025 if (session->snap_name() != session->name()) {
1026 session_name = session->snap_name();
1028 session_name = session->name();
1032 session_name = "*" + session_name;
1035 WindowTitle title(session_name);
1036 title += Glib::get_application_name();
1037 set_title (title.get_string());
1042 Editor::connect_to_session (Session *t)
1046 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1049 /* catch up with the playhead */
1051 session->request_locate (playhead_cursor->current_frame);
1053 if (first_action_message) {
1054 first_action_message->hide();
1059 session->GoingAway.connect (mem_fun(*this, &Editor::session_going_away));
1060 session->history().Changed.connect (mem_fun (*this, &Editor::history_changed));
1062 /* These signals can all be emitted by a non-GUI thread. Therefore the
1063 handlers for them must not attempt to directly interact with the GUI,
1064 but use Gtkmm2ext::UI::instance()->call_slot();
1067 session_connections.push_back (session->TransportStateChange.connect (mem_fun(*this, &Editor::map_transport_state)));
1068 session_connections.push_back (session->PositionChanged.connect (mem_fun(*this, &Editor::map_position_change)));
1069 session_connections.push_back (session->RouteAdded.connect (mem_fun(*this, &Editor::handle_new_route)));
1070 session_connections.push_back (session->RegionAdded.connect (mem_fun(*this, &Editor::handle_new_region)));
1071 session_connections.push_back (session->RegionRemoved.connect (mem_fun(*this, &Editor::handle_region_removed)));
1072 session_connections.push_back (session->DurationChanged.connect (mem_fun(*this, &Editor::handle_new_duration)));
1073 session_connections.push_back (session->edit_group_added.connect (mem_fun(*this, &Editor::add_edit_group)));
1074 session_connections.push_back (session->edit_group_removed.connect (mem_fun(*this, &Editor::edit_groups_changed)));
1075 session_connections.push_back (session->NamedSelectionAdded.connect (mem_fun(*this, &Editor::handle_new_named_selection)));
1076 session_connections.push_back (session->NamedSelectionRemoved.connect (mem_fun(*this, &Editor::handle_new_named_selection)));
1077 session_connections.push_back (session->DirtyChanged.connect (mem_fun(*this, &Editor::update_title)));
1078 session_connections.push_back (session->StateSaved.connect (mem_fun(*this, &Editor::update_title_s)));
1079 session_connections.push_back (session->AskAboutPlaylistDeletion.connect (mem_fun(*this, &Editor::playlist_deletion_dialog)));
1080 session_connections.push_back (session->RegionHiddenChange.connect (mem_fun(*this, &Editor::region_hidden)));
1082 session_connections.push_back (session->SMPTEOffsetChanged.connect (mem_fun(*this, &Editor::update_just_smpte)));
1084 session_connections.push_back (session->tempo_map().StateChanged.connect (mem_fun(*this, &Editor::tempo_map_changed)));
1086 edit_groups_changed ();
1088 edit_cursor_clock.set_session (session);
1089 zoom_range_clock.set_session (session);
1090 _playlist_selector->set_session (session);
1091 nudge_clock.set_session (session);
1094 if (analysis_window != 0)
1095 analysis_window->set_session (session);
1098 Location* loc = session->locations()->auto_loop_location();
1100 loc = new Location (0, session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1101 if (loc->start() == loc->end()) {
1102 loc->set_end (loc->start() + 1);
1104 session->locations()->add (loc, false);
1105 session->set_auto_loop_location (loc);
1108 loc->set_name (_("Loop"));
1111 loc = session->locations()->auto_punch_location();
1113 loc = new Location (0, session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1114 if (loc->start() == loc->end()) {
1115 loc->set_end (loc->start() + 1);
1117 session->locations()->add (loc, false);
1118 session->set_auto_punch_location (loc);
1121 loc->set_name (_("Punch"));
1124 Config->map_parameters (mem_fun (*this, &Editor::parameter_changed));
1126 session->StateSaved.connect (mem_fun(*this, &Editor::session_state_saved));
1128 refresh_location_display ();
1129 session->locations()->added.connect (mem_fun(*this, &Editor::add_new_location));
1130 session->locations()->removed.connect (mem_fun(*this, &Editor::location_gone));
1131 session->locations()->changed.connect (mem_fun(*this, &Editor::refresh_location_display));
1132 session->locations()->StateChanged.connect (mem_fun(*this, &Editor::refresh_location_display_s));
1133 session->locations()->end_location()->changed.connect (mem_fun(*this, &Editor::end_location_changed));
1135 handle_new_duration ();
1137 redisplay_regions ();
1138 redisplay_named_selections ();
1139 redisplay_snapshots ();
1141 initial_route_list_display ();
1143 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1144 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1147 restore_ruler_visibility ();
1148 //tempo_map_changed (Change (0));
1149 session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1153 /* don't show master bus in a new session */
1155 if (ARDOUR_UI::instance()->session_is_new ()) {
1157 TreeModel::Children rows = route_display_model->children();
1158 TreeModel::Children::iterator i;
1160 no_route_list_redisplay = true;
1162 for (i = rows.begin(); i != rows.end(); ++i) {
1163 TimeAxisView *tv = (*i)[route_display_columns.tv];
1164 RouteTimeAxisView *rtv;
1166 if ((rtv = dynamic_cast<RouteTimeAxisView*>(tv)) != 0) {
1167 if (rtv->route()->is_master()) {
1168 route_list_display.get_selection()->unselect (i);
1173 no_route_list_redisplay = false;
1174 redisplay_route_list ();
1177 /* register for undo history */
1179 session->register_with_memento_command_factory(_id, this);
1183 Editor::build_cursors ()
1185 using namespace Gdk;
1187 Gdk::Color mbg ("#000000" ); /* Black */
1188 Gdk::Color mfg ("#0000ff" ); /* Blue. */
1191 RefPtr<Bitmap> source, mask;
1192 source = Bitmap::create (mag_bits, mag_width, mag_height);
1193 mask = Bitmap::create (magmask_bits, mag_width, mag_height);
1194 zoom_cursor = new Gdk::Cursor (source, mask, mfg, mbg, mag_x_hot, mag_y_hot);
1197 Gdk::Color fbg ("#ffffff" );
1198 Gdk::Color ffg ("#000000" );
1201 RefPtr<Bitmap> source, mask;
1203 source = Bitmap::create (fader_cursor_bits, fader_cursor_width, fader_cursor_height);
1204 mask = Bitmap::create (fader_cursor_mask_bits, fader_cursor_width, fader_cursor_height);
1205 fader_cursor = new Gdk::Cursor (source, mask, ffg, fbg, fader_cursor_x_hot, fader_cursor_y_hot);
1209 RefPtr<Bitmap> source, mask;
1210 source = Bitmap::create (speaker_cursor_bits, speaker_cursor_width, speaker_cursor_height);
1211 mask = Bitmap::create (speaker_cursor_mask_bits, speaker_cursor_width, speaker_cursor_height);
1212 speaker_cursor = new Gdk::Cursor (source, mask, ffg, fbg, speaker_cursor_x_hot, speaker_cursor_y_hot);
1215 grabber_cursor = new Gdk::Cursor (HAND2);
1216 cross_hair_cursor = new Gdk::Cursor (CROSSHAIR);
1217 trimmer_cursor = new Gdk::Cursor (SB_H_DOUBLE_ARROW);
1218 selector_cursor = new Gdk::Cursor (XTERM);
1219 time_fx_cursor = new Gdk::Cursor (SIZING);
1220 wait_cursor = new Gdk::Cursor (WATCH);
1221 timebar_cursor = new Gdk::Cursor(LEFT_PTR);
1222 note_cursor = new Gdk::Cursor (PENCIL);
1225 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1227 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1229 using namespace Menu_Helpers;
1230 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1233 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1237 MenuList& items (fade_context_menu.items());
1241 switch (item_type) {
1243 case FadeInHandleItem:
1244 if (arv->audio_region()->fade_in_active()) {
1245 items.push_back (MenuElem (_("Deactivate"), bind (mem_fun (*this, &Editor::set_fade_in_active), false)));
1247 items.push_back (MenuElem (_("Activate"), bind (mem_fun (*this, &Editor::set_fade_in_active), true)));
1250 items.push_back (SeparatorElem());
1252 items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear)));
1253 items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast)));
1254 items.push_back (MenuElem (_("Slow"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogB)));
1255 items.push_back (MenuElem (_("Fast"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogA)));
1256 items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Slow)));
1260 case FadeOutHandleItem:
1261 if (arv->audio_region()->fade_out_active()) {
1262 items.push_back (MenuElem (_("Deactivate"), bind (mem_fun (*this, &Editor::set_fade_out_active), false)));
1264 items.push_back (MenuElem (_("Activate"), bind (mem_fun (*this, &Editor::set_fade_out_active), true)));
1267 items.push_back (SeparatorElem());
1269 items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear)));
1270 items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow)));
1271 items.push_back (MenuElem (_("Slow"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogA)));
1272 items.push_back (MenuElem (_("Fast"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogB)));
1273 items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Fast)));
1278 fatal << _("programming error: ")
1279 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1284 fade_context_menu.popup (button, time);
1287 /* Pop up the general track context menu for when the user clicks pretty much anywhere in a track or bus */
1289 Editor::popup_track_context_menu (int button, int32_t time, nframes_t frame)
1291 build_track_context_menu (frame)->popup (button, time);
1295 Editor::build_track_context_menu (nframes_t frame)
1297 using namespace Menu_Helpers;
1299 Menu* menu = manage (new Menu);
1300 MenuList& edit_items = menu->items();
1303 /* Build the general `track' context menu, adding what is appropriate given
1304 the current selection */
1306 /* XXX: currently crossfades can't be selected, so we can't use the selection
1307 to decide which crossfades to mention in the menu. I believe this will
1308 change at some point. For now we have to use clicked_trackview to decide. */
1309 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1312 boost::shared_ptr<Diskstream> ds;
1313 boost::shared_ptr<Playlist> pl;
1314 boost::shared_ptr<AudioPlaylist> apl;
1316 if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1318 AudioPlaylist::Crossfades xfades;
1319 apl->crossfades_at (frame, xfades);
1321 bool many = xfades.size() > 1;
1323 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1324 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1329 if (!selection->time.empty()) {
1330 add_selection_context_items (edit_items);
1333 if (!selection->regions.empty()) {
1334 add_region_context_items (edit_items);
1337 if (!selection->tracks.empty()) {
1338 add_bus_or_audio_track_context_items (edit_items);
1341 menu->set_name ("ArdourContextMenu");
1348 Editor::analyze_region_selection()
1350 if (analysis_window == 0) {
1351 analysis_window = new AnalysisWindow();
1354 analysis_window->set_session(session);
1356 analysis_window->show_all();
1359 analysis_window->set_regionmode();
1360 analysis_window->analyze();
1362 analysis_window->present();
1366 Editor::analyze_range_selection()
1368 if (analysis_window == 0) {
1369 analysis_window = new AnalysisWindow();
1372 analysis_window->set_session(session);
1374 analysis_window->show_all();
1377 analysis_window->set_rangemode();
1378 analysis_window->analyze();
1380 analysis_window->present();
1382 #endif /* FFT_ANALYSIS */
1385 /** Add context menu items relevant to crossfades.
1386 * @param edit_items List to add the items to.
1389 Editor::add_crossfade_context_items (AudioStreamView* view, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1391 using namespace Menu_Helpers;
1392 Menu *xfade_menu = manage (new Menu);
1393 MenuList& items = xfade_menu->items();
1394 xfade_menu->set_name ("ArdourContextMenu");
1397 if (xfade->active()) {
1403 items.push_back (MenuElem (str, bind (mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1404 items.push_back (MenuElem (_("Edit"), bind (mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1406 if (xfade->can_follow_overlap()) {
1408 if (xfade->following_overlap()) {
1409 str = _("Convert to short");
1411 str = _("Convert to full");
1414 items.push_back (MenuElem (str, bind (mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1418 str = xfade->out()->name();
1420 str += xfade->in()->name();
1422 str = _("Crossfade");
1425 edit_items.push_back (MenuElem (str, *xfade_menu));
1426 edit_items.push_back (SeparatorElem());
1430 Editor::xfade_edit_left_region ()
1432 if (clicked_crossfadeview) {
1433 clicked_crossfadeview->left_view.show_region_editor ();
1438 Editor::xfade_edit_right_region ()
1440 if (clicked_crossfadeview) {
1441 clicked_crossfadeview->right_view.show_region_editor ();
1445 /** Add an element to a menu, settings its sensitivity.
1446 * @param m Menu to add to.
1447 * @param e Element to add.
1448 * @param s true to make sensitive, false to make insensitive
1451 Editor::add_item_with_sensitivity (Menu_Helpers::MenuList& m, Menu_Helpers::MenuElem e, bool s) const
1455 m.back().set_sensitive (false);
1459 /** Add context menu items relevant to regions.
1460 * @param edit_items List to add the items to.
1463 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items)
1465 using namespace Menu_Helpers;
1466 Menu *region_menu = manage (new Menu);
1467 MenuList& items = region_menu->items();
1468 region_menu->set_name ("ArdourContextMenu");
1470 items.push_back (MenuElem (_("Edit..."), mem_fun(*this, &Editor::edit_region)));
1471 items.push_back (MenuElem (_("Raise to top layer"), mem_fun(*this, &Editor::raise_region_to_top)));
1472 items.push_back (MenuElem (_("Lower to bottom layer"), mem_fun (*this, &Editor::lower_region_to_bottom)));
1474 Menu* sync_point_menu = manage (new Menu);
1475 MenuList& sync_point_items = sync_point_menu->items();
1476 sync_point_menu->set_name("ArdourContextMenu");
1478 sync_point_items.push_back (MenuElem (_("Define"), mem_fun(*this, &Editor::set_region_sync_from_edit_cursor)));
1479 sync_point_items.push_back (MenuElem (_("Remove"), mem_fun(*this, &Editor::remove_region_sync)));
1481 items.push_back (MenuElem (_("Sync points"), *sync_point_menu));
1483 add_item_with_sensitivity (items, MenuElem (_("Audition"), mem_fun(*this, &Editor::audition_selected_region)), selection->regions.size() == 1);
1485 add_item_with_sensitivity (items, MenuElem (_("Export"), mem_fun(*this, &Editor::export_region)), selection->regions.size() == 1);
1487 items.push_back (MenuElem (_("Bounce"), mem_fun(*this, &Editor::bounce_region_selection)));
1490 items.push_back (MenuElem (_("Analyze region"), mem_fun(*this, &Editor::analyze_region_selection)));
1493 items.push_back (SeparatorElem());
1495 items.push_back (MenuElem (_("Lock"), bind (mem_fun (*this, &Editor::set_region_lock), true)));
1496 items.push_back (MenuElem (_("Unlock"), bind (mem_fun (*this, &Editor::set_region_lock), false)));
1497 items.push_back (MenuElem (_("Lock Position"), bind (mem_fun (*this, &Editor::set_region_position_lock), true)));
1498 items.push_back (MenuElem (_("Unlock Position"), bind (mem_fun (*this, &Editor::set_region_position_lock), false)));
1499 items.push_back (MenuElem (_("Mute"), bind (mem_fun (*this, &Editor::set_region_mute), true)));
1500 items.push_back (MenuElem (_("Unmute"), bind (mem_fun (*this, &Editor::set_region_mute), false)));
1501 items.push_back (MenuElem (_("Opaque"), bind (mem_fun (*this, &Editor::set_region_opaque), true)));
1502 items.push_back (MenuElem (_("Transparent"), bind (mem_fun (*this, &Editor::set_region_opaque), false)));
1504 /* We allow "Original position" if at least one region is not at its
1507 RegionSelection::iterator i = selection->regions.begin();
1508 while (i != selection->regions.end() && (*i)->region()->at_natural_position() == true) {
1512 add_item_with_sensitivity (items, MenuElem (_("Original position"), mem_fun(*this, &Editor::naturalize)), i != selection->regions.end());
1514 items.push_back (SeparatorElem());
1516 /* Find out if we have a selected audio region */
1517 i = selection->regions.begin();
1518 while (i != selection->regions.end() && boost::dynamic_pointer_cast<AudioRegion>((*i)->region()) == 0) {
1521 bool const have_selected_audio_region = (i != selection->regions.end());
1523 if (have_selected_audio_region) {
1525 Menu* envelopes_menu = manage (new Menu);
1526 MenuList& envelopes_items = envelopes_menu->items();
1527 envelopes_menu->set_name ("ArdourContextMenu");
1529 envelopes_items.push_back (MenuElem (_("Reset"), mem_fun(*this, &Editor::reset_region_gain_envelopes)));
1530 envelopes_items.push_back (MenuElem (_("Visible"), bind (mem_fun (*this, &Editor::set_gain_envelope_visibility), true)));
1531 envelopes_items.push_back (MenuElem (_("Invisible"), bind (mem_fun (*this, &Editor::set_gain_envelope_visibility), false)));
1532 envelopes_items.push_back (MenuElem (_("Active"), bind (mem_fun (*this, &Editor::set_gain_envelope_active), true)));
1533 envelopes_items.push_back (MenuElem (_("Inactive"), bind (mem_fun (*this, &Editor::set_gain_envelope_active), false)));
1535 items.push_back (MenuElem (_("Envelopes"), *envelopes_menu));
1537 items.push_back (MenuElem (_("Denormalize"), mem_fun (*this, &Editor::denormalize_regions)));
1538 items.push_back (MenuElem (_("Normalize"), mem_fun (*this, &Editor::normalize_regions)));
1541 items.push_back (MenuElem (_("Reverse"), mem_fun(*this, &Editor::reverse_regions)));
1542 items.push_back (SeparatorElem());
1545 /* range related stuff */
1547 add_item_with_sensitivity (items, MenuElem (_("Add range markers"), mem_fun (*this, &Editor::add_location_from_audio_region)), selection->regions.size() == 1);
1549 add_item_with_sensitivity (items, MenuElem (_("Set range selection"), mem_fun (*this, &Editor::set_selection_from_audio_region)), selection->regions.size() == 1);
1551 items.push_back (SeparatorElem());
1555 Menu *nudge_menu = manage (new Menu());
1556 MenuList& nudge_items = nudge_menu->items();
1557 nudge_menu->set_name ("ArdourContextMenu");
1559 nudge_items.push_back (MenuElem (_("Nudge forward"), (bind (mem_fun(*this, &Editor::nudge_forward), false))));
1560 nudge_items.push_back (MenuElem (_("Nudge backward"), (bind (mem_fun(*this, &Editor::nudge_backward), false))));
1561 nudge_items.push_back (MenuElem (_("Nudge forward by capture offset"), (mem_fun(*this, &Editor::nudge_forward_capture_offset))));
1562 nudge_items.push_back (MenuElem (_("Nudge backward by capture offset"), (mem_fun(*this, &Editor::nudge_backward_capture_offset))));
1564 items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1566 Menu *trim_menu = manage (new Menu);
1567 MenuList& trim_items = trim_menu->items();
1568 trim_menu->set_name ("ArdourContextMenu");
1570 trim_items.push_back (MenuElem (_("Start to edit cursor"), mem_fun(*this, &Editor::trim_region_from_edit_cursor)));
1571 trim_items.push_back (MenuElem (_("Edit cursor to end"), mem_fun(*this, &Editor::trim_region_to_edit_cursor)));
1573 items.push_back (MenuElem (_("Trim"), *trim_menu));
1574 items.push_back (MenuElem (_("Split"), (mem_fun(*this, &Editor::split_region))));
1575 items.push_back (MenuElem (_("Make mono regions"), (mem_fun(*this, &Editor::split_multichannel_region))));
1576 items.push_back (MenuElem (_("Duplicate"), (bind (mem_fun(*this, &Editor::duplicate_dialog), true))));
1577 items.push_back (MenuElem (_("Fill track"), (mem_fun(*this, &Editor::region_fill_track))));
1578 items.push_back (SeparatorElem());
1579 items.push_back (MenuElem (_("Remove"), mem_fun(*this, &Editor::remove_selected_regions)));
1581 /* OK, stick the region submenu at the top of the list, and then add
1585 string const menu_item_name = selection->regions.size() > 1 ? _("Regions") : _("Region");
1586 edit_items.push_back (MenuElem (menu_item_name, *region_menu));
1589 /** Add context menu items relevant to selection ranges.
1590 * @param edit_items List to add the items to.
1593 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1595 using namespace Menu_Helpers;
1596 Menu *selection_menu = manage (new Menu);
1597 MenuList& items = selection_menu->items();
1598 selection_menu->set_name ("ArdourContextMenu");
1600 items.push_back (MenuElem (_("Play range"), mem_fun(*this, &Editor::play_selection)));
1601 items.push_back (MenuElem (_("Loop range"), mem_fun(*this, &Editor::set_route_loop_selection)));
1604 items.push_back (SeparatorElem());
1605 items.push_back (MenuElem (_("Analyze range"), mem_fun(*this, &Editor::analyze_range_selection)));
1608 items.push_back (SeparatorElem());
1609 items.push_back (MenuElem (_("Separate range to track"), mem_fun(*this, &Editor::separate_region_from_selection)));
1610 items.push_back (MenuElem (_("Separate range to region list"), mem_fun(*this, &Editor::new_region_from_selection)));
1612 items.push_back (SeparatorElem());
1613 items.push_back (MenuElem (_("Select all in range"), mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1614 items.push_back (SeparatorElem());
1615 items.push_back (MenuElem (_("Add range markers"), mem_fun (*this, &Editor::add_location_from_selection)));
1616 items.push_back (MenuElem (_("Set range to loop range"), mem_fun(*this, &Editor::set_selection_from_loop)));
1617 items.push_back (MenuElem (_("Set range to punch range"), mem_fun(*this, &Editor::set_selection_from_punch)));
1618 items.push_back (SeparatorElem());
1619 items.push_back (MenuElem (_("Crop region to range"), mem_fun(*this, &Editor::crop_region_to_selection)));
1620 items.push_back (MenuElem (_("Fill range with region"), mem_fun(*this, &Editor::region_fill_selection)));
1621 items.push_back (MenuElem (_("Duplicate range"), bind (mem_fun(*this, &Editor::duplicate_dialog), false)));
1622 items.push_back (MenuElem (_("Create chunk from range"), mem_fun(*this, &Editor::create_named_selection)));
1623 items.push_back (SeparatorElem());
1624 items.push_back (MenuElem (_("Bounce range"), mem_fun(*this, &Editor::bounce_range_selection)));
1625 items.push_back (MenuElem (_("Export range"), mem_fun(*this, &Editor::export_selection)));
1627 edit_items.push_back (MenuElem (_("Range"), *selection_menu));
1628 edit_items.push_back (SeparatorElem());
1631 /** Add context menu items relevant to busses or audio tracks.
1632 * @param edit_items List to add the items to.
1635 Editor::add_bus_or_audio_track_context_items (Menu_Helpers::MenuList& edit_items)
1637 using namespace Menu_Helpers;
1639 /* We add every possible action here, and de-sensitize things
1640 that aren't allowed. The sensitivity logic is a bit spread out;
1641 on the one hand I'm using things like can_cut_copy (), which is
1642 reasonably complicated and so perhaps better near the function that
1643 it expresses sensitivity for, and on the other hand checks
1644 in this function as well. You can't really have can_* for everything
1645 or the number of methods would get silly. */
1647 bool const one_selected_region = selection->regions.size() == 1;
1649 /* Count the number of selected audio tracks */
1650 int n_audio_tracks = 0;
1651 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1652 RouteTimeAxisView const * r = dynamic_cast<RouteTimeAxisView*>(*i);
1653 if (r && r->is_audio_track()) {
1660 Menu *play_menu = manage (new Menu);
1661 MenuList& play_items = play_menu->items();
1662 play_menu->set_name ("ArdourContextMenu");
1664 play_items.push_back (MenuElem (_("Play from edit cursor"), mem_fun(*this, &Editor::play_from_edit_cursor)));
1665 play_items.push_back (MenuElem (_("Play from start"), mem_fun(*this, &Editor::play_from_start)));
1666 add_item_with_sensitivity (play_items, MenuElem (_("Play region"), mem_fun(*this, &Editor::play_selected_region)), one_selected_region);
1668 add_item_with_sensitivity (play_items, MenuElem (_("Loop region"), mem_fun(*this, &Editor::loop_selected_region)), one_selected_region);
1670 edit_items.push_back (MenuElem (_("Play"), *play_menu));
1674 Menu *select_menu = manage (new Menu);
1675 MenuList& select_items = select_menu->items();
1676 select_menu->set_name ("ArdourContextMenu");
1678 string str = selection->tracks.size() == 1 ? _("Select all in track") : _("Select all in tracks");
1680 select_items.push_back (MenuElem (str, bind (mem_fun(*this, &Editor::select_all_in_selected_tracks), Selection::Set)));
1682 select_items.push_back (MenuElem (_("Select all"), bind (mem_fun(*this, &Editor::select_all), Selection::Set)));
1684 str = selection->tracks.size() == 1 ? _("Invert selection in track") : _("Invert selection in tracks");
1686 select_items.push_back (MenuElem (str, mem_fun(*this, &Editor::invert_selection_in_selected_tracks)));
1688 select_items.push_back (MenuElem (_("Invert selection"), mem_fun(*this, &Editor::invert_selection)));
1689 select_items.push_back (SeparatorElem());
1691 if (n_audio_tracks) {
1692 select_items.push_back (MenuElem (_("Set range to loop range"), mem_fun(*this, &Editor::set_selection_from_loop)));
1693 select_items.push_back (MenuElem (_("Set range to punch range"), mem_fun(*this, &Editor::set_selection_from_punch)));
1696 select_items.push_back (MenuElem (_("Select all after edit cursor"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), edit_cursor, true)));
1697 select_items.push_back (MenuElem (_("Select all before edit cursor"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), edit_cursor, false)));
1698 select_items.push_back (MenuElem (_("Select all after playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1699 select_items.push_back (MenuElem (_("Select all before playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1701 if (n_audio_tracks) {
1702 select_items.push_back (MenuElem (_("Select all between cursors"), bind (mem_fun(*this, &Editor::select_all_selectables_between_cursors), playhead_cursor, edit_cursor)));
1705 edit_items.push_back (MenuElem (_("Select"), *select_menu));
1709 Menu *cutnpaste_menu = manage (new Menu);
1710 MenuList& cutnpaste_items = cutnpaste_menu->items();
1711 cutnpaste_menu->set_name ("ArdourContextMenu");
1713 add_item_with_sensitivity (cutnpaste_items, MenuElem (_("Cut"), mem_fun(*this, &Editor::cut)), can_cut_copy ());
1715 add_item_with_sensitivity (cutnpaste_items, MenuElem (_("Copy"), mem_fun(*this, &Editor::copy)), can_cut_copy ());
1717 if (n_audio_tracks) {
1718 cutnpaste_items.push_back (MenuElem (_("Paste at edit cursor"), bind (mem_fun(*this, &Editor::paste), 1.0f)));
1719 cutnpaste_items.push_back (MenuElem (_("Paste at mouse"), mem_fun(*this, &Editor::mouse_paste)));
1721 cutnpaste_items.push_back (SeparatorElem());
1723 cutnpaste_items.push_back (MenuElem (_("Align"), bind (mem_fun(*this, &Editor::align), ARDOUR::SyncPoint)));
1724 cutnpaste_items.push_back (MenuElem (_("Align relative"), bind (mem_fun(*this, &Editor::align_relative), ARDOUR::SyncPoint)));
1725 cutnpaste_items.push_back (MenuElem (_("Insert chunk"), bind (mem_fun(*this, &Editor::paste_named_selection), 1.0f)));
1727 cutnpaste_items.push_back (MenuElem (_("Paste"), bind (mem_fun(*this, &Editor::paste), 1.0f)));
1730 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
1732 if (n_audio_tracks) {
1734 Menu *track_menu = manage (new Menu);
1735 MenuList& track_items = track_menu->items();
1736 track_menu->set_name ("ArdourContextMenu");
1738 /* Adding new material */
1740 add_item_with_sensitivity (track_items, MenuElem (_("Insert selected region"), bind (mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)), n_audio_tracks == 1);
1742 add_item_with_sensitivity (track_items, MenuElem (_("Insert existing audio"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)), n_audio_tracks == 1);
1746 Menu *nudge_menu = manage (new Menu());
1747 MenuList& nudge_items = nudge_menu->items();
1748 nudge_menu->set_name ("ArdourContextMenu");
1750 str = selection->tracks.size() == 1 ? _("Nudge track forward") : _("Nude tracks forward");
1751 nudge_items.push_back (MenuElem (str, (bind (mem_fun(*this, &Editor::nudge_selected_tracks), false, true))));
1753 str = selection->tracks.size() == 1 ? _("Nudge track after edit cursor forward") : _("Nudge tracks after edit cursor forward");
1755 nudge_items.push_back (MenuElem (str, (bind (mem_fun(*this, &Editor::nudge_selected_tracks), true, true))));
1757 str = selection->tracks.size() == 1 ? _("Nudge track backward") : _("Nudge tracks backward");
1759 nudge_items.push_back (MenuElem (str, (bind (mem_fun(*this, &Editor::nudge_selected_tracks), false, false))));
1761 str = selection->tracks.size() == 1 ? _("Nudge track after edit cursor backward") : _("Nudge tracks after edit cursor backward");
1763 nudge_items.push_back (MenuElem (str, (bind (mem_fun(*this, &Editor::nudge_selected_tracks), true, false))));
1765 track_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1768 track_items.push_back (MenuElem (_("Freeze"), mem_fun(*this, &Editor::freeze_routes)));
1769 track_items.push_back (MenuElem (_("Unfreeze"), mem_fun(*this, &Editor::unfreeze_routes)));
1771 str = selection->tracks.size() == 1 ? _("Track") : _("Tracks");
1772 edit_items.push_back (MenuElem (str, *track_menu));
1776 /* CURSOR SETTING AND MARKS AND STUFF */
1779 Editor::set_snap_to (SnapType st)
1782 string str = snap_type_strings[(int) st];
1784 if (str != snap_type_selector.get_active_text()) {
1785 snap_type_selector.set_active_text (str);
1790 switch (snap_type) {
1791 case SnapToAThirtysecondBeat:
1792 case SnapToASixteenthBeat:
1793 case SnapToAEighthBeat:
1794 case SnapToAQuarterBeat:
1795 case SnapToAThirdBeat:
1796 update_tempo_based_rulers ();
1804 Editor::set_snap_mode (SnapMode mode)
1807 string str = snap_mode_strings[(int)mode];
1809 if (str != snap_mode_selector.get_active_text ()) {
1810 snap_mode_selector.set_active_text (str);
1817 Editor::set_state (const XMLNode& node)
1819 const XMLProperty* prop;
1821 int x, y, xoff, yoff;
1824 if ((prop = node.property ("id")) != 0) {
1825 _id = prop->value ();
1828 if ((geometry = find_named_node (node, "geometry")) == 0) {
1830 g.base_width = default_width;
1831 g.base_height = default_height;
1839 g.base_width = atoi(geometry->property("x_size")->value());
1840 g.base_height = atoi(geometry->property("y_size")->value());
1841 x = atoi(geometry->property("x_pos")->value());
1842 y = atoi(geometry->property("y_pos")->value());
1843 xoff = atoi(geometry->property("x_off")->value());
1844 yoff = atoi(geometry->property("y_off")->value());
1847 set_default_size (g.base_width, g.base_height);
1850 if (session && (prop = node.property ("playhead"))) {
1851 nframes_t pos = atol (prop->value().c_str());
1852 playhead_cursor->set_position (pos);
1854 playhead_cursor->set_position (0);
1856 /* reset_x_origin() doesn't work right here, since the old
1857 position may be zero already, and it does nothing in such
1862 horizontal_adjustment.set_value (0);
1865 if (session && (prop = node.property ("edit-cursor"))) {
1866 nframes_t pos = atol (prop->value().c_str());
1867 edit_cursor->set_position (pos);
1869 edit_cursor->set_position (0);
1872 if ((prop = node.property ("mixer-width"))) {
1873 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
1876 if ((prop = node.property ("zoom-focus"))) {
1877 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
1880 if ((prop = node.property ("zoom"))) {
1881 reset_zoom (PBD::atof (prop->value()));
1884 if ((prop = node.property ("snap-to"))) {
1885 set_snap_to ((SnapType) atoi (prop->value()));
1888 if ((prop = node.property ("snap-mode"))) {
1889 set_snap_mode ((SnapMode) atoi (prop->value()));
1892 if ((prop = node.property ("mouse-mode"))) {
1893 MouseMode m = str2mousemode(prop->value());
1894 mouse_mode = MouseMode ((int) m + 1); /* lie, force mode switch */
1895 set_mouse_mode (m, true);
1897 mouse_mode = MouseGain; /* lie, to force the mode switch */
1898 set_mouse_mode (MouseObject, true);
1901 if ((prop = node.property ("show-waveforms"))) {
1902 bool yn = (prop->value() == "yes");
1903 _show_waveforms = !yn;
1904 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleWaveformVisibility"));
1906 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
1907 /* do it twice to force the change */
1908 tact->set_active (!yn);
1909 tact->set_active (yn);
1913 if ((prop = node.property ("show-waveforms-recording"))) {
1914 bool yn = (prop->value() == "yes");
1915 _show_waveforms_recording = !yn;
1916 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleWaveformsWhileRecording"));
1918 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
1919 /* do it twice to force the change */
1920 tact->set_active (!yn);
1921 tact->set_active (yn);
1925 if ((prop = node.property ("show-measures"))) {
1926 bool yn = (prop->value() == "yes");
1927 _show_measures = !yn;
1928 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
1930 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
1931 /* do it twice to force the change */
1932 tact->set_active (!yn);
1933 tact->set_active (yn);
1937 if ((prop = node.property ("follow-playhead"))) {
1938 bool yn = (prop->value() == "yes");
1939 set_follow_playhead (yn);
1940 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
1942 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
1943 if (tact->get_active() != yn) {
1944 tact->set_active (yn);
1949 if ((prop = node.property ("region-list-sort-type"))) {
1950 region_list_sort_type = (Editing::RegionListSortType) -1; // force change
1951 reset_region_list_sort_type(str2regionlistsorttype(prop->value()));
1954 if ((prop = node.property ("xfades-visible"))) {
1955 bool yn = (prop->value() == "yes");
1956 _xfade_visibility = !yn;
1957 // set_xfade_visibility (yn);
1960 if ((prop = node.property ("show-editor-mixer"))) {
1962 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
1965 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
1966 bool yn = (prop->value() == X_("yes"));
1968 /* do it twice to force the change */
1970 tact->set_active (!yn);
1971 tact->set_active (yn);
1975 if ((prop = node.property ("show-editor-list"))) {
1977 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
1981 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
1982 bool yn = (prop->value() == X_("yes"));
1984 /* do it twice to force the change */
1986 tact->set_active (!yn);
1987 tact->set_active (yn);
1996 Editor::get_state ()
1998 XMLNode* node = new XMLNode ("Editor");
2001 _id.print (buf, sizeof (buf));
2002 node->add_property ("id", buf);
2004 if (is_realized()) {
2005 Glib::RefPtr<Gdk::Window> win = get_window();
2007 int x, y, xoff, yoff, width, height;
2008 win->get_root_origin(x, y);
2009 win->get_position(xoff, yoff);
2010 win->get_size(width, height);
2012 XMLNode* geometry = new XMLNode ("geometry");
2014 snprintf(buf, sizeof(buf), "%d", width);
2015 geometry->add_property("x_size", string(buf));
2016 snprintf(buf, sizeof(buf), "%d", height);
2017 geometry->add_property("y_size", string(buf));
2018 snprintf(buf, sizeof(buf), "%d", x);
2019 geometry->add_property("x_pos", string(buf));
2020 snprintf(buf, sizeof(buf), "%d", y);
2021 geometry->add_property("y_pos", string(buf));
2022 snprintf(buf, sizeof(buf), "%d", xoff);
2023 geometry->add_property("x_off", string(buf));
2024 snprintf(buf, sizeof(buf), "%d", yoff);
2025 geometry->add_property("y_off", string(buf));
2026 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2027 geometry->add_property("edit_pane_pos", string(buf));
2029 node->add_child_nocopy (*geometry);
2032 maybe_add_mixer_strip_width (*node);
2034 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2035 node->add_property ("zoom-focus", buf);
2036 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2037 node->add_property ("zoom", buf);
2038 snprintf (buf, sizeof(buf), "%d", (int) snap_type);
2039 node->add_property ("snap-to", buf);
2040 snprintf (buf, sizeof(buf), "%d", (int) snap_mode);
2041 node->add_property ("snap-mode", buf);
2043 snprintf (buf, sizeof (buf), "%" PRIu32, playhead_cursor->current_frame);
2044 node->add_property ("playhead", buf);
2045 snprintf (buf, sizeof (buf), "%" PRIu32, edit_cursor->current_frame);
2046 node->add_property ("edit-cursor", buf);
2048 node->add_property ("show-waveforms", _show_waveforms ? "yes" : "no");
2049 node->add_property ("show-waveforms-recording", _show_waveforms_recording ? "yes" : "no");
2050 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2051 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2052 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2053 node->add_property ("region-list-sort-type", enum2str(region_list_sort_type));
2054 node->add_property ("mouse-mode", enum2str(mouse_mode));
2056 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2058 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2059 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2062 act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2064 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2065 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2074 Editor::trackview_by_y_position (double y)
2076 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2080 if ((tv = (*iter)->covers_y_position (y)) != 0) {
2089 Editor::snap_to (nframes64_t& start, int32_t direction, bool for_mark)
2091 Location* before = 0;
2092 Location* after = 0;
2098 const nframes64_t one_second = session->frame_rate();
2099 const nframes64_t one_minute = session->frame_rate() * 60;
2100 const nframes64_t one_smpte_second = (nframes64_t)(rint(session->smpte_frames_per_second()) * session->frames_per_smpte_frame());
2101 nframes64_t one_smpte_minute = (nframes64_t)(rint(session->smpte_frames_per_second()) * session->frames_per_smpte_frame() * 60);
2102 nframes64_t presnap = start;
2104 switch (snap_type) {
2110 start = (nframes_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2112 start = (nframes_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2116 case SnapToSMPTEFrame:
2117 if (fmod((double)start, (double)session->frames_per_smpte_frame()) > (session->frames_per_smpte_frame() / 2)) {
2118 start = (nframes_t) (ceil ((double) start / session->frames_per_smpte_frame()) * session->frames_per_smpte_frame());
2120 start = (nframes_t) (floor ((double) start / session->frames_per_smpte_frame()) * session->frames_per_smpte_frame());
2124 case SnapToSMPTESeconds:
2125 if (session->smpte_offset_negative())
2127 start += session->smpte_offset ();
2129 start -= session->smpte_offset ();
2131 if (start % one_smpte_second > one_smpte_second / 2) {
2132 start = (nframes_t) ceil ((double) start / one_smpte_second) * one_smpte_second;
2134 start = (nframes_t) floor ((double) start / one_smpte_second) * one_smpte_second;
2137 if (session->smpte_offset_negative())
2139 start -= session->smpte_offset ();
2141 start += session->smpte_offset ();
2145 case SnapToSMPTEMinutes:
2146 if (session->smpte_offset_negative())
2148 start += session->smpte_offset ();
2150 start -= session->smpte_offset ();
2152 if (start % one_smpte_minute > one_smpte_minute / 2) {
2153 start = (nframes_t) ceil ((double) start / one_smpte_minute) * one_smpte_minute;
2155 start = (nframes_t) floor ((double) start / one_smpte_minute) * one_smpte_minute;
2157 if (session->smpte_offset_negative())
2159 start -= session->smpte_offset ();
2161 start += session->smpte_offset ();
2166 if (start % one_second > one_second / 2) {
2167 start = (nframes_t) ceil ((double) start / one_second) * one_second;
2169 start = (nframes_t) floor ((double) start / one_second) * one_second;
2174 if (start % one_minute > one_minute / 2) {
2175 start = (nframes_t) ceil ((double) start / one_minute) * one_minute;
2177 start = (nframes_t) floor ((double) start / one_minute) * one_minute;
2182 start = session->tempo_map().round_to_bar (start, direction);
2186 start = session->tempo_map().round_to_beat (start, direction);
2189 case SnapToAThirtysecondBeat:
2190 start = session->tempo_map().round_to_beat_subdivision (start, 32);
2193 case SnapToASixteenthBeat:
2194 start = session->tempo_map().round_to_beat_subdivision (start, 16);
2197 case SnapToAEighthBeat:
2198 start = session->tempo_map().round_to_beat_subdivision (start, 8);
2201 case SnapToAQuarterBeat:
2202 start = session->tempo_map().round_to_beat_subdivision (start, 4);
2205 case SnapToAThirdBeat:
2206 start = session->tempo_map().round_to_beat_subdivision (start, 3);
2209 case SnapToEditCursor:
2210 start = edit_cursor->current_frame;
2218 before = session->locations()->first_location_before (start);
2219 after = session->locations()->first_location_after (start);
2221 if (direction < 0) {
2223 start = before->start();
2227 } else if (direction > 0) {
2229 start = after->start();
2231 start = session->current_end_frame();
2236 /* find nearest of the two */
2237 if ((start - before->start()) < (after->start() - start)) {
2238 start = before->start();
2240 start = after->start();
2243 start = before->start();
2246 start = after->start();
2253 case SnapToRegionStart:
2254 case SnapToRegionEnd:
2255 case SnapToRegionSync:
2256 case SnapToRegionBoundary:
2257 if (!region_boundary_cache.empty()) {
2258 vector<nframes_t>::iterator i;
2260 if (direction > 0) {
2261 i = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2263 i = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2266 if (i != region_boundary_cache.end()) {
2269 start = region_boundary_cache.back();
2275 switch (snap_mode) {
2281 if (presnap > start) {
2282 if (presnap > (start + unit_to_frame(snap_threshold))) {
2286 } else if (presnap < start) {
2287 if (presnap < (start - unit_to_frame(snap_threshold))) {
2299 Editor::setup_toolbar ()
2303 const guint32 FUDGE = 18; // Combo's are stupid - they steal space from the entry for the button
2306 /* Mode Buttons (tool selection) */
2308 vector<ToggleButton *> mouse_mode_buttons;
2310 mouse_move_button.add (*(manage (new Image (::get_icon("tool_object")))));
2311 mouse_move_button.set_relief(Gtk::RELIEF_NONE);
2312 mouse_mode_buttons.push_back (&mouse_move_button);
2313 mouse_select_button.add (*(manage (new Image (get_xpm("tool_range.xpm")))));
2314 mouse_select_button.set_relief(Gtk::RELIEF_NONE);
2315 mouse_mode_buttons.push_back (&mouse_select_button);
2316 mouse_gain_button.add (*(manage (new Image (::get_icon("tool_gain")))));
2317 mouse_gain_button.set_relief(Gtk::RELIEF_NONE);
2318 mouse_mode_buttons.push_back (&mouse_gain_button);
2319 mouse_zoom_button.add (*(manage (new Image (::get_icon("tool_zoom")))));
2320 mouse_zoom_button.set_relief(Gtk::RELIEF_NONE);
2321 mouse_mode_buttons.push_back (&mouse_zoom_button);
2322 mouse_timefx_button.add (*(manage (new Image (::get_icon("tool_stretch")))));
2323 mouse_timefx_button.set_relief(Gtk::RELIEF_NONE);
2324 mouse_mode_buttons.push_back (&mouse_timefx_button);
2325 mouse_audition_button.add (*(manage (new Image (::get_icon("tool_audition")))));
2326 mouse_audition_button.set_relief(Gtk::RELIEF_NONE);
2327 mouse_note_button.add (*(manage (new Image (::get_icon("tool_note")))));
2328 mouse_note_button.set_relief(Gtk::RELIEF_NONE);
2329 mouse_mode_buttons.push_back (&mouse_note_button);
2330 mouse_mode_buttons.push_back (&mouse_audition_button);
2332 mouse_mode_button_set = new GroupedButtons (mouse_mode_buttons);
2334 HBox* mode_box = manage(new HBox);
2335 mode_box->set_border_width (2);
2336 mode_box->set_spacing(4);
2337 mouse_mode_button_box.set_spacing(1);
2338 mouse_mode_button_box.pack_start(mouse_move_button, true, true);
2339 mouse_mode_button_box.pack_start(mouse_select_button, true, true);
2340 mouse_mode_button_box.pack_start(mouse_zoom_button, true, true);
2341 mouse_mode_button_box.pack_start(mouse_gain_button, true, true);
2342 mouse_mode_button_box.pack_start(mouse_timefx_button, true, true);
2343 mouse_mode_button_box.pack_start(mouse_audition_button, true, true);
2344 mouse_mode_button_box.pack_start(mouse_note_button, true, true);
2345 mouse_mode_button_box.set_homogeneous(true);
2347 vector<string> edit_mode_strings;
2348 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2349 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2351 edit_mode_selector.set_name ("EditModeSelector");
2352 Gtkmm2ext::set_size_request_to_display_given_text (edit_mode_selector, longest (edit_mode_strings).c_str(), 2+FUDGE, 10);
2353 set_popdown_strings (edit_mode_selector, edit_mode_strings);
2354 edit_mode_selector.signal_changed().connect (mem_fun(*this, &Editor::edit_mode_selection_done));
2356 mode_box->pack_start(edit_mode_selector);
2357 mode_box->pack_start(mouse_mode_button_box);
2359 mouse_mode_tearoff = manage (new TearOff (*mode_box));
2360 mouse_mode_tearoff->set_name ("MouseModeBase");
2362 mouse_mode_tearoff->Detach.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2363 &mouse_mode_tearoff->tearoff_window()));
2364 mouse_mode_tearoff->Attach.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2365 &mouse_mode_tearoff->tearoff_window(), 1));
2366 mouse_mode_tearoff->Hidden.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2367 &mouse_mode_tearoff->tearoff_window()));
2368 mouse_mode_tearoff->Visible.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2369 &mouse_mode_tearoff->tearoff_window(), 1));
2371 mouse_move_button.set_name ("MouseModeButton");
2372 mouse_select_button.set_name ("MouseModeButton");
2373 mouse_gain_button.set_name ("MouseModeButton");
2374 mouse_zoom_button.set_name ("MouseModeButton");
2375 mouse_timefx_button.set_name ("MouseModeButton");
2376 mouse_audition_button.set_name ("MouseModeButton");
2377 mouse_note_button.set_name ("MouseModeButton");
2379 ARDOUR_UI::instance()->tooltips().set_tip (mouse_move_button, _("Select/Move Objects"));
2380 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Select/Move Ranges"));
2381 ARDOUR_UI::instance()->tooltips().set_tip (mouse_gain_button, _("Draw Gain Automation"));
2382 ARDOUR_UI::instance()->tooltips().set_tip (mouse_zoom_button, _("Select Zoom Range"));
2383 ARDOUR_UI::instance()->tooltips().set_tip (mouse_timefx_button, _("Stretch/Shrink Regions"));
2384 ARDOUR_UI::instance()->tooltips().set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2385 ARDOUR_UI::instance()->tooltips().set_tip (mouse_note_button, _("Edit MIDI Notes"));
2387 mouse_move_button.unset_flags (CAN_FOCUS);
2388 mouse_select_button.unset_flags (CAN_FOCUS);
2389 mouse_gain_button.unset_flags (CAN_FOCUS);
2390 mouse_zoom_button.unset_flags (CAN_FOCUS);
2391 mouse_timefx_button.unset_flags (CAN_FOCUS);
2392 mouse_audition_button.unset_flags (CAN_FOCUS);
2393 mouse_note_button.unset_flags (CAN_FOCUS);
2395 mouse_select_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseRange));
2396 mouse_select_button.signal_button_release_event().connect (mem_fun(*this, &Editor::mouse_select_button_release));
2398 mouse_move_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseObject));
2399 mouse_gain_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseGain));
2400 mouse_zoom_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseZoom));
2401 mouse_timefx_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseTimeFX));
2402 mouse_audition_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseAudition));
2403 mouse_note_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseNote));
2405 // mouse_move_button.set_active (true);
2410 zoom_box.set_spacing (1);
2411 zoom_box.set_border_width (2);
2413 zoom_in_button.set_name ("EditorTimeButton");
2414 zoom_in_button.set_size_request(-1,16);
2415 zoom_in_button.add (*(manage (new Image (::get_icon("zoom_in")))));
2416 zoom_in_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::temporal_zoom_step), false));
2417 ARDOUR_UI::instance()->tooltips().set_tip (zoom_in_button, _("Zoom In"));
2419 zoom_out_button.set_name ("EditorTimeButton");
2420 zoom_out_button.set_size_request(-1,16);
2421 zoom_out_button.add (*(manage (new Image (::get_icon("zoom_out")))));
2422 zoom_out_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::temporal_zoom_step), true));
2423 ARDOUR_UI::instance()->tooltips().set_tip (zoom_out_button, _("Zoom Out"));
2425 zoom_out_full_button.set_name ("EditorTimeButton");
2426 zoom_out_full_button.set_size_request(-1,16);
2427 zoom_out_full_button.add (*(manage (new Image (::get_icon("zoom_full")))));
2428 zoom_out_full_button.signal_clicked().connect (mem_fun(*this, &Editor::temporal_zoom_session));
2429 ARDOUR_UI::instance()->tooltips().set_tip (zoom_out_full_button, _("Zoom to Session"));
2431 zoom_focus_selector.set_name ("ZoomFocusSelector");
2432 Gtkmm2ext::set_size_request_to_display_given_text (zoom_focus_selector, "Center", FUDGE, 0);
2433 set_popdown_strings (zoom_focus_selector, zoom_focus_strings);
2434 zoom_focus_selector.signal_changed().connect (mem_fun(*this, &Editor::zoom_focus_selection_done));
2435 ARDOUR_UI::instance()->tooltips().set_tip (zoom_focus_selector, _("Zoom focus"));
2437 zoom_box.pack_start (zoom_focus_selector, true, true);
2438 zoom_box.pack_start (zoom_out_button, false, false);
2439 zoom_box.pack_start (zoom_in_button, false, false);
2440 zoom_box.pack_start (zoom_out_full_button, false, false);
2442 /* Edit Cursor / Snap */
2444 snap_box.set_spacing (1);
2445 snap_box.set_border_width (2);
2447 snap_type_selector.set_name ("SnapTypeSelector");
2448 Gtkmm2ext::set_size_request_to_display_given_text (snap_type_selector, "SMPTE Seconds", 2+FUDGE, 10);
2449 set_popdown_strings (snap_type_selector, snap_type_strings);
2450 snap_type_selector.signal_changed().connect (mem_fun(*this, &Editor::snap_type_selection_done));
2451 ARDOUR_UI::instance()->tooltips().set_tip (snap_type_selector, _("Unit to snap cursors and ranges to"));
2453 snap_mode_selector.set_name ("SnapModeSelector");
2454 Gtkmm2ext::set_size_request_to_display_given_text (snap_mode_selector, "Magnetic Snap", 2+FUDGE, 10);
2455 set_popdown_strings (snap_mode_selector, snap_mode_strings);
2456 snap_mode_selector.signal_changed().connect (mem_fun(*this, &Editor::snap_mode_selection_done));
2458 snap_box.pack_start (edit_cursor_clock, false, false);
2459 snap_box.pack_start (snap_mode_selector, false, false);
2460 snap_box.pack_start (snap_type_selector, false, false);
2465 HBox *nudge_box = manage (new HBox);
2466 nudge_box->set_spacing(1);
2467 nudge_box->set_border_width (2);
2469 nudge_forward_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::nudge_forward), false));
2470 nudge_backward_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::nudge_backward), false));
2472 nudge_box->pack_start (nudge_backward_button, false, false);
2473 nudge_box->pack_start (nudge_forward_button, false, false);
2474 nudge_box->pack_start (nudge_clock, false, false);
2477 /* Pack everything in... */
2479 HBox* hbox = new HBox;
2480 hbox->set_spacing(10);
2482 tools_tearoff = new TearOff (*hbox);
2483 tools_tearoff->set_name ("MouseModeBase");
2485 tools_tearoff->Detach.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2486 &tools_tearoff->tearoff_window()));
2487 tools_tearoff->Attach.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2488 &tools_tearoff->tearoff_window(), 0));
2489 tools_tearoff->Hidden.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2490 &tools_tearoff->tearoff_window()));
2491 tools_tearoff->Visible.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2492 &tools_tearoff->tearoff_window(), 0));
2494 toolbar_hbox.set_spacing (10);
2495 toolbar_hbox.set_border_width (1);
2497 toolbar_hbox.pack_start (*mouse_mode_tearoff, false, false);
2498 toolbar_hbox.pack_start (*tools_tearoff, false, false);
2501 hbox->pack_start (snap_box, false, false);
2502 // hbox->pack_start (zoom_box, false, false);
2503 hbox->pack_start (*nudge_box, false, false);
2507 toolbar_base.set_name ("ToolBarBase");
2508 toolbar_base.add (toolbar_hbox);
2510 toolbar_frame.set_shadow_type (SHADOW_OUT);
2511 toolbar_frame.set_name ("BaseFrame");
2512 toolbar_frame.add (toolbar_base);
2516 Editor::convert_drop_to_paths (vector<ustring>& paths,
2517 const RefPtr<Gdk::DragContext>& context,
2520 const SelectionData& data,
2529 vector<ustring> uris = data.get_uris();
2533 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
2534 are actually URI lists. So do it by hand.
2537 if (data.get_target() != "text/plain") {
2541 /* Parse the "uri-list" format that Nautilus provides,
2542 where each pathname is delimited by \r\n
2545 const char* p = data.get_text().c_str();
2552 while (g_ascii_isspace (*p))
2556 while (*q && (*q != '\n') && (*q != '\r'))
2562 while (q > p && g_ascii_isspace (*q))
2567 uris.push_back (ustring (p, q - p + 1));
2571 p = strchr (p, '\n');
2581 for (vector<ustring>::iterator i = uris.begin(); i != uris.end(); ++i) {
2582 if ((*i).substr (0,7) == "file://") {
2584 PBD::url_decode (p);
2585 paths.push_back (p.substr (7));
2593 Editor::new_tempo_section ()
2599 Editor::map_transport_state ()
2601 ENSURE_GUI_THREAD (mem_fun(*this, &Editor::map_transport_state));
2603 if (session->transport_stopped()) {
2604 have_pending_keyboard_selection = false;
2607 update_loop_range_view (true);
2612 Editor::State::State ()
2614 selection = new Selection;
2617 Editor::State::~State ()
2623 Editor::get_memento () const
2625 State *state = new State;
2627 store_state (*state);
2628 return bind (mem_fun (*(const_cast<Editor*>(this)), &Editor::restore_state), state);
2632 Editor::store_state (State& state) const
2634 *state.selection = *selection;
2638 Editor::restore_state (State *state)
2640 if (*selection == *state->selection) {
2644 *selection = *state->selection;
2645 time_selection_changed ();
2646 region_selection_changed ();
2648 /* XXX other selection change handlers? */
2652 Editor::begin_reversible_command (string name)
2655 before = &get_state();
2656 session->begin_reversible_command (name);
2661 Editor::commit_reversible_command ()
2664 session->commit_reversible_command (new MementoCommand<Editor>(*this, before, &get_state()));
2669 Editor::set_edit_group_solo (Route& route, bool yn)
2671 RouteGroup *edit_group;
2673 if ((edit_group = route.edit_group()) != 0) {
2674 edit_group->apply (&Route::set_solo, yn, this);
2676 route.set_solo (yn, this);
2681 Editor::set_edit_group_mute (Route& route, bool yn)
2683 RouteGroup *edit_group = 0;
2685 if ((edit_group == route.edit_group()) != 0) {
2686 edit_group->apply (&Route::set_mute, yn, this);
2688 route.set_mute (yn, this);
2693 Editor::history_changed ()
2697 if (undo_action && session) {
2698 if (session->undo_depth() == 0) {
2701 label = string_compose(_("Undo (%1)"), session->next_undo());
2703 undo_action->property_label() = label;
2706 if (redo_action && session) {
2707 if (session->redo_depth() == 0) {
2710 label = string_compose(_("Redo (%1)"), session->next_redo());
2712 redo_action->property_label() = label;
2717 Editor::duplicate_dialog (bool dup_region)
2719 if (selection->regions.empty() && (selection->time.length() == 0)) {
2723 ArdourDialog win ("duplicate dialog");
2724 Label label (_("Duplicate how many times?"));
2725 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
2726 SpinButton spinner (adjustment);
2728 win.get_vbox()->set_spacing (12);
2729 win.get_vbox()->pack_start (label);
2731 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
2732 place, visually. so do this by hand.
2735 win.get_vbox()->pack_start (spinner);
2736 spinner.signal_activate().connect (sigc::bind (mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
2741 win.add_button (Stock::OK, RESPONSE_ACCEPT);
2742 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
2744 win.set_position (WIN_POS_MOUSE);
2746 spinner.grab_focus ();
2748 switch (win.run ()) {
2749 case RESPONSE_ACCEPT:
2755 float times = adjustment.get_value();
2757 if (!selection->regions.empty()) {
2758 duplicate_some_regions (selection->regions, times);
2760 duplicate_selection (times);
2765 Editor::show_verbose_canvas_cursor ()
2767 verbose_canvas_cursor->raise_to_top();
2768 verbose_canvas_cursor->show();
2769 verbose_cursor_visible = true;
2773 Editor::hide_verbose_canvas_cursor ()
2775 verbose_canvas_cursor->hide();
2776 verbose_cursor_visible = false;
2780 Editor::set_verbose_canvas_cursor (const string & txt, double x, double y)
2782 /* XXX get origin of canvas relative to root window,
2783 add x and y and check compared to gdk_screen_{width,height}
2785 verbose_canvas_cursor->property_text() = txt.c_str();
2786 verbose_canvas_cursor->property_x() = x;
2787 verbose_canvas_cursor->property_y() = y;
2791 Editor::set_verbose_canvas_cursor_text (const string & txt)
2793 verbose_canvas_cursor->property_text() = txt.c_str();
2797 Editor::edit_mode_selection_done ()
2803 string choice = edit_mode_selector.get_active_text();
2804 EditMode mode = Slide;
2806 if (choice == _("Splice Edit")) {
2808 } else if (choice == _("Slide Edit")) {
2812 Config->set_edit_mode (mode);
2816 Editor::snap_type_selection_done ()
2818 string choice = snap_type_selector.get_active_text();
2819 SnapType snaptype = SnapToFrame;
2821 if (choice == _("Beats/3")) {
2822 snaptype = SnapToAThirdBeat;
2823 } else if (choice == _("Beats/4")) {
2824 snaptype = SnapToAQuarterBeat;
2825 } else if (choice == _("Beats/8")) {
2826 snaptype = SnapToAEighthBeat;
2827 } else if (choice == _("Beats/16")) {
2828 snaptype = SnapToASixteenthBeat;
2829 } else if (choice == _("Beats/32")) {
2830 snaptype = SnapToAThirtysecondBeat;
2831 } else if (choice == _("Beats")) {
2832 snaptype = SnapToBeat;
2833 } else if (choice == _("Bars")) {
2834 snaptype = SnapToBar;
2835 } else if (choice == _("Marks")) {
2836 snaptype = SnapToMark;
2837 } else if (choice == _("Edit Cursor")) {
2838 snaptype = SnapToEditCursor;
2839 } else if (choice == _("Region starts")) {
2840 snaptype = SnapToRegionStart;
2841 } else if (choice == _("Region ends")) {
2842 snaptype = SnapToRegionEnd;
2843 } else if (choice == _("Region bounds")) {
2844 snaptype = SnapToRegionBoundary;
2845 } else if (choice == _("Region syncs")) {
2846 snaptype = SnapToRegionSync;
2847 } else if (choice == _("CD Frames")) {
2848 snaptype = SnapToCDFrame;
2849 } else if (choice == _("SMPTE Frames")) {
2850 snaptype = SnapToSMPTEFrame;
2851 } else if (choice == _("SMPTE Seconds")) {
2852 snaptype = SnapToSMPTESeconds;
2853 } else if (choice == _("SMPTE Minutes")) {
2854 snaptype = SnapToSMPTEMinutes;
2855 } else if (choice == _("Seconds")) {
2856 snaptype = SnapToSeconds;
2857 } else if (choice == _("Minutes")) {
2858 snaptype = SnapToMinutes;
2859 } else if (choice == _("None")) {
2860 snaptype = SnapToFrame;
2863 RefPtr<RadioAction> ract = snap_type_action (snaptype);
2865 ract->set_active ();
2870 Editor::snap_mode_selection_done ()
2872 string choice = snap_mode_selector.get_active_text();
2873 SnapMode mode = SnapNormal;
2875 if (choice == _("Normal")) {
2877 } else if (choice == _("Magnetic")) {
2878 mode = SnapMagnetic;
2881 RefPtr<RadioAction> ract = snap_mode_action (mode);
2884 ract->set_active (true);
2889 Editor::zoom_focus_selection_done ()
2891 string choice = zoom_focus_selector.get_active_text();
2892 ZoomFocus focus_type = ZoomFocusLeft;
2894 if (choice == _("Left")) {
2895 focus_type = ZoomFocusLeft;
2896 } else if (choice == _("Right")) {
2897 focus_type = ZoomFocusRight;
2898 } else if (choice == _("Center")) {
2899 focus_type = ZoomFocusCenter;
2900 } else if (choice == _("Play")) {
2901 focus_type = ZoomFocusPlayhead;
2902 } else if (choice == _("Edit")) {
2903 focus_type = ZoomFocusEdit;
2906 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
2909 ract->set_active ();
2914 Editor::edit_controls_button_release (GdkEventButton* ev)
2916 if (Keyboard::is_context_menu_event (ev)) {
2917 ARDOUR_UI::instance()->add_route (this);
2923 Editor::mouse_select_button_release (GdkEventButton* ev)
2925 /* this handles just right-clicks */
2927 if (ev->button != 3) {
2934 Editor::TrackViewList *
2935 Editor::get_valid_views (TimeAxisView* track, RouteGroup* group)
2938 TrackViewList::iterator i;
2940 v = new TrackViewList;
2942 if (track == 0 && group == 0) {
2946 for (i = track_views.begin(); i != track_views.end (); ++i) {
2950 } else if (track != 0 && group == 0 || (track != 0 && group != 0 && !group->is_active())) {
2952 /* just the view for this track
2955 v->push_back (track);
2959 /* views for all tracks in the edit group */
2961 for (i = track_views.begin(); i != track_views.end (); ++i) {
2963 if (group == 0 || (*i)->edit_group() == group) {
2973 Editor::set_zoom_focus (ZoomFocus f)
2975 string str = zoom_focus_strings[(int)f];
2977 if (str != zoom_focus_selector.get_active_text()) {
2978 zoom_focus_selector.set_active_text (str);
2981 if (zoom_focus != f) {
2984 ZoomFocusChanged (); /* EMIT_SIGNAL */
2991 Editor::ensure_float (Window& win)
2993 win.set_transient_for (*this);
2997 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
2999 /* recover or initialize pane positions. do this here rather than earlier because
3000 we don't want the positions to change the child allocations, which they seem to do.
3006 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3008 static int32_t done;
3011 if ((geometry = find_named_node (*node, "geometry")) == 0) {
3012 width = default_width;
3013 height = default_height;
3015 width = atoi(geometry->property("x_size")->value());
3016 height = atoi(geometry->property("y_size")->value());
3019 if (which == static_cast<Paned*> (&edit_pane)) {
3025 if (!geometry || (prop = geometry->property ("edit_pane_pos")) == 0) {
3026 /* initial allocation is 90% to canvas, 10% to notebook */
3027 pos = (int) floor (alloc.get_width() * 0.90f);
3028 snprintf (buf, sizeof(buf), "%d", pos);
3030 pos = atoi (prop->value());
3033 if ((done = GTK_WIDGET(edit_pane.gobj())->allocation.width > pos)) {
3034 edit_pane.set_position (pos);
3035 pre_maximal_pane_position = pos;
3041 Editor::detach_tearoff (Box* b, Window* w)
3043 if (tools_tearoff->torn_off() &&
3044 mouse_mode_tearoff->torn_off()) {
3045 top_hbox.remove (toolbar_frame);
3050 Editor::reattach_tearoff (Box* b, Window* w, int32_t n)
3052 if (toolbar_frame.get_parent() == 0) {
3053 top_hbox.pack_end (toolbar_frame);
3058 Editor::set_show_measures (bool yn)
3060 if (_show_measures != yn) {
3063 if ((_show_measures = yn) == true) {
3071 Editor::toggle_follow_playhead ()
3073 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3075 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3076 set_follow_playhead (tact->get_active());
3081 Editor::set_follow_playhead (bool yn)
3083 if (_follow_playhead != yn) {
3084 if ((_follow_playhead = yn) == true) {
3086 update_current_screen ();
3093 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3095 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3097 xfade->set_active (!xfade->active());
3102 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3104 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3106 xfade->set_follow_overlap (!xfade->following_overlap());
3111 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3113 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3119 CrossfadeEditor cew (*session, xfade, xfade->fade_in().get_min_y(), 1.0);
3123 switch (cew.run ()) {
3124 case RESPONSE_ACCEPT:
3131 xfade->StateChanged (Change (~0));
3135 Editor::playlist_selector () const
3137 return *_playlist_selector;
3141 Editor::get_nudge_distance (nframes_t pos, nframes_t& next)
3145 ret = nudge_clock.current_duration (pos);
3146 next = ret + 1; /* XXXX fix me */
3152 Editor::end_location_changed (Location* location)
3154 ENSURE_GUI_THREAD (bind (mem_fun(*this, &Editor::end_location_changed), location));
3155 reset_scrolling_region ();
3159 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3161 ArdourDialog dialog ("playlist deletion dialog");
3162 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3163 "If left alone, no audio files used by it will be cleaned.\n"
3164 "If deleted, audio files used by it alone by will cleaned."),
3167 dialog.set_position (WIN_POS_CENTER);
3168 dialog.get_vbox()->pack_start (label);
3172 dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT);
3173 dialog.add_button (_("Keep playlist"), RESPONSE_REJECT);
3174 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3176 switch (dialog.run ()) {
3177 case RESPONSE_ACCEPT:
3178 /* delete the playlist */
3182 case RESPONSE_REJECT:
3183 /* keep the playlist */
3195 Editor::audio_region_selection_covers (nframes_t where)
3197 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3198 if ((*a)->region()->covers (where)) {
3207 Editor::prepare_for_cleanup ()
3209 cut_buffer->clear_regions ();
3210 cut_buffer->clear_playlists ();
3212 selection->clear_regions ();
3213 selection->clear_playlists ();
3217 Editor::transport_loop_location()
3220 return session->locations()->auto_loop_location();
3227 Editor::transport_punch_location()
3230 return session->locations()->auto_punch_location();
3237 Editor::control_layout_scroll (GdkEventScroll* ev)
3239 switch (ev->direction) {
3241 scroll_tracks_up_line ();
3245 case GDK_SCROLL_DOWN:
3246 scroll_tracks_down_line ();
3250 /* no left/right handling yet */
3258 /** A new snapshot has been selected.
3261 Editor::snapshot_display_selection_changed ()
3263 if (snapshot_display.get_selection()->count_selected_rows() > 0) {
3265 TreeModel::iterator i = snapshot_display.get_selection()->get_selected();
3267 Glib::ustring snap_name = (*i)[snapshot_display_columns.real_name];
3269 if (snap_name.length() == 0) {
3273 if (session->snap_name() == snap_name) {
3277 ARDOUR_UI::instance()->load_session(session->path(), string (snap_name));
3282 Editor::snapshot_display_button_press (GdkEventButton* ev)
3284 if (ev->button == 3) {
3285 /* Right-click on the snapshot list. Work out which snapshot it
3287 Gtk::TreeModel::Path path;
3288 Gtk::TreeViewColumn* col;
3291 snapshot_display.get_path_at_pos ((int) ev->x, (int) ev->y, path, col, cx, cy);
3292 Gtk::TreeModel::iterator iter = snapshot_display_model->get_iter (path);
3294 Gtk::TreeModel::Row row = *iter;
3295 popup_snapshot_context_menu (ev->button, ev->time, row[snapshot_display_columns.real_name]);
3304 /** Pop up the snapshot display context menu.
3305 * @param button Button used to open the menu.
3306 * @param time Menu open time.
3307 * @snapshot_name Name of the snapshot that the menu click was over.
3311 Editor::popup_snapshot_context_menu (int button, int32_t time, Glib::ustring snapshot_name)
3313 using namespace Menu_Helpers;
3315 MenuList& items (snapshot_context_menu.items());
3318 const bool modification_allowed = (session->snap_name() != snapshot_name && session->name() != snapshot_name);
3320 add_item_with_sensitivity (items, MenuElem (_("Remove"), bind (mem_fun (*this, &Editor::remove_snapshot), snapshot_name)), modification_allowed);
3322 add_item_with_sensitivity (items, MenuElem (_("Rename"), bind (mem_fun (*this, &Editor::rename_snapshot), snapshot_name)), modification_allowed);
3324 snapshot_context_menu.popup (button, time);
3328 Editor::rename_snapshot (Glib::ustring old_name)
3330 ArdourPrompter prompter(true);
3334 prompter.set_name ("Prompter");
3335 prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
3336 prompter.set_prompt (_("New name of snapshot"));
3337 prompter.set_initial_text (old_name);
3339 if (prompter.run() == RESPONSE_ACCEPT) {
3340 prompter.get_result (new_name);
3341 if (new_name.length()) {
3342 session->rename_state (old_name, new_name);
3343 redisplay_snapshots ();
3350 Editor::remove_snapshot (Glib::ustring name)
3352 vector<string> choices;
3354 std::string prompt = string_compose (_("Do you really want to remove snapshot \"%1\" ?\n(cannot be undone)"), name);
3356 choices.push_back (_("No, do nothing."));
3357 choices.push_back (_("Yes, remove it."));
3359 Gtkmm2ext::Choice prompter (prompt, choices);
3361 if (prompter.run () == 1) {
3362 session->remove_state (name);
3363 redisplay_snapshots ();
3368 Editor::redisplay_snapshots ()
3374 vector<string*>* states;
3376 if ((states = session->possible_states()) == 0) {
3380 snapshot_display_model->clear ();
3382 for (vector<string*>::iterator i = states->begin(); i != states->end(); ++i) {
3383 string statename = *(*i);
3384 TreeModel::Row row = *(snapshot_display_model->append());
3386 /* this lingers on in case we ever want to change the visible
3387 name of the snapshot.
3390 string display_name;
3391 display_name = statename;
3393 if (statename == session->snap_name()) {
3394 snapshot_display.get_selection()->select(row);
3397 row[snapshot_display_columns.visible_name] = display_name;
3398 row[snapshot_display_columns.real_name] = statename;
3405 Editor::session_state_saved (string snap_name)
3407 ENSURE_GUI_THREAD (bind (mem_fun(*this, &Editor::session_state_saved), snap_name));
3408 redisplay_snapshots ();
3412 Editor::maximise_editing_space ()
3414 initial_ruler_update_required = true;
3416 mouse_mode_tearoff->set_visible (false);
3417 tools_tearoff->set_visible (false);
3419 pre_maximal_pane_position = edit_pane.get_position();
3420 pre_maximal_editor_width = this->get_width();
3422 if(post_maximal_pane_position == 0) {
3423 post_maximal_pane_position = edit_pane.get_width();
3428 if(post_maximal_editor_width) {
3429 edit_pane.set_position (post_maximal_pane_position -
3430 abs(post_maximal_editor_width - pre_maximal_editor_width));
3432 edit_pane.set_position (post_maximal_pane_position);
3437 Editor::restore_editing_space ()
3439 initial_ruler_update_required = true;
3441 // user changed width of pane during fullscreen
3442 if(post_maximal_pane_position != edit_pane.get_position()) {
3443 post_maximal_pane_position = edit_pane.get_position();
3448 mouse_mode_tearoff->set_visible (true);
3449 tools_tearoff->set_visible (true);
3450 post_maximal_editor_width = this->get_width();
3453 edit_pane.set_position (
3454 pre_maximal_pane_position + abs(this->get_width() - pre_maximal_editor_width)
3459 Editor::new_playlists ()
3461 begin_reversible_command (_("new playlists"));
3462 mapover_tracks (mem_fun (*this, &Editor::mapped_use_new_playlist));
3463 commit_reversible_command ();
3467 Editor::copy_playlists ()
3469 begin_reversible_command (_("copy playlists"));
3470 mapover_tracks (mem_fun (*this, &Editor::mapped_use_copy_playlist));
3471 commit_reversible_command ();
3475 Editor::clear_playlists ()
3477 begin_reversible_command (_("clear playlists"));
3478 mapover_tracks (mem_fun (*this, &Editor::mapped_clear_playlist));
3479 commit_reversible_command ();
3483 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz)
3485 atv.use_new_playlist (sz > 1 ? false : true);
3489 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz)
3491 atv.use_copy_playlist (sz > 1 ? false : true);
3495 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t sz)
3497 atv.clear_playlist ();
3501 Editor::on_key_press_event (GdkEventKey* ev)
3503 return key_press_focus_accelerator_handler (*this, ev);
3507 Editor::reset_x_origin (nframes_t frame)
3509 queue_visual_change (frame);
3513 Editor::reset_zoom (double fpu)
3515 queue_visual_change (fpu);
3519 Editor::reposition_and_zoom (nframes_t frame, double fpu)
3521 reset_x_origin (frame);
3526 Editor::set_frames_per_unit (double fpu)
3530 /* this is the core function that controls the zoom level of the canvas. it is called
3531 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
3534 if (fpu == frames_per_unit) {
3542 // convert fpu to frame count
3544 frames = (nframes_t) floor (fpu * canvas_width);
3546 /* don't allow zooms that fit more than the maximum number
3547 of frames into an 800 pixel wide space.
3550 if (max_frames / fpu < 800.0) {
3554 if (fpu == frames_per_unit) {
3558 frames_per_unit = fpu;
3560 if (frames != zoom_range_clock.current_duration()) {
3561 zoom_range_clock.set (frames);
3564 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
3565 if (!selection->tracks.empty()) {
3566 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3567 (*i)->reshow_selection (selection->time);
3570 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3571 (*i)->reshow_selection (selection->time);
3576 ZoomChanged (); /* EMIT_SIGNAL */
3578 reset_hscrollbar_stepping ();
3579 reset_scrolling_region ();
3581 if (edit_cursor) edit_cursor->set_position (edit_cursor->current_frame);
3582 if (playhead_cursor) playhead_cursor->set_position (playhead_cursor->current_frame);
3588 Editor::queue_visual_change (nframes_t where)
3590 pending_visual_change.pending = VisualChange::Type (pending_visual_change.pending | VisualChange::TimeOrigin);
3591 pending_visual_change.time_origin = where;
3593 if (pending_visual_change.idle_handler_id < 0) {
3594 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
3599 Editor::queue_visual_change (double fpu)
3601 pending_visual_change.pending = VisualChange::Type (pending_visual_change.pending | VisualChange::ZoomLevel);
3602 pending_visual_change.frames_per_unit = fpu;
3604 if (pending_visual_change.idle_handler_id < 0) {
3605 pending_visual_change.idle_handler_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE, _idle_visual_changer, this, 0);
3610 Editor::_idle_visual_changer (void* arg)
3612 return static_cast<Editor*>(arg)->idle_visual_changer ();
3616 Editor::idle_visual_changer ()
3618 VisualChange::Type p = pending_visual_change.pending;
3620 pending_visual_change.pending = (VisualChange::Type) 0;
3621 pending_visual_change.idle_handler_id = -1;
3623 if (p & VisualChange::ZoomLevel) {
3624 set_frames_per_unit (pending_visual_change.frames_per_unit);
3627 if (p & VisualChange::TimeOrigin) {
3628 if (pending_visual_change.time_origin != leftmost_frame) {
3629 horizontal_adjustment.set_value (pending_visual_change.time_origin/frames_per_unit);
3630 /* the signal handler will do the rest */
3632 update_fixed_rulers();
3633 redisplay_tempo (true);
3637 return 0; /* this is always a one-shot call */
3640 struct EditorOrderTimeAxisSorter {
3641 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
3642 return a->order < b->order;
3647 Editor::sort_track_selection ()
3649 EditorOrderTimeAxisSorter cmp;
3650 selection->tracks.sort (cmp);
3654 Editor::edit_cursor_position(bool sync)
3656 if (sync && edit_cursor->current_frame != edit_cursor_clock.current_time()) {
3657 edit_cursor_clock.set(edit_cursor->current_frame, true);
3660 return edit_cursor->current_frame;