splash override env var patch from nedko
[ardour2.git] / gtk2_ardour / editor.cc
blobf5aa512eab85d055c490f6f2df3e67bd2774ba1d
1 /*
2 Copyright (C) 2000-2007 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include <sys/time.h>
21 #include <unistd.h>
22 #include <cstdlib>
23 #include <cmath>
24 #include <string>
25 #include <algorithm>
27 #include <boost/none.hpp>
29 #include <sigc++/bind.h>
31 #include <pbd/convert.h>
32 #include <pbd/error.h>
33 #include <pbd/enumwriter.h>
34 #include <pbd/memento_command.h>
36 #include <glibmm/miscutils.h>
37 #include <gtkmm/image.h>
38 #include <gdkmm/color.h>
39 #include <gdkmm/bitmap.h>
41 #include <gtkmm2ext/grouped_buttons.h>
42 #include <gtkmm2ext/gtk_ui.h>
43 #include <gtkmm2ext/tearoff.h>
44 #include <gtkmm2ext/utils.h>
45 #include <gtkmm2ext/window_title.h>
46 #include <gtkmm2ext/choice.h>
48 #include <ardour/audio_track.h>
49 #include <ardour/audio_diskstream.h>
50 #include <ardour/plugin_manager.h>
51 #include <ardour/location.h>
52 #include <ardour/audioplaylist.h>
53 #include <ardour/audioregion.h>
54 #include <ardour/region.h>
55 #include <ardour/session_route.h>
56 #include <ardour/tempo.h>
57 #include <ardour/utils.h>
58 #include <ardour/profile.h>
60 #include <control_protocol/control_protocol.h>
62 #include "ardour_ui.h"
63 #include "editor.h"
64 #include "keyboard.h"
65 #include "marker.h"
66 #include "playlist_selector.h"
67 #include "audio_region_view.h"
68 #include "rgb_macros.h"
69 #include "selection.h"
70 #include "audio_streamview.h"
71 #include "time_axis_view.h"
72 #include "audio_time_axis.h"
73 #include "utils.h"
74 #include "crossfade_view.h"
75 #include "editing.h"
76 #include "public_editor.h"
77 #include "crossfade_edit.h"
78 #include "canvas_impl.h"
79 #include "actions.h"
80 #include "tempo_lines.h"
81 #include "gui_thread.h"
82 #include "sfdb_ui.h"
83 #include "rhythm_ferret.h"
84 #include "actions.h"
86 #ifdef FFT_ANALYSIS
87 #include "analysis_window.h"
88 #endif
90 #include "i18n.h"
92 /* <CMT Additions> */
93 #include "imageframe_socket_handler.h"
94 /* </CMT Additions> */
96 using namespace std;
97 using namespace sigc;
98 using namespace ARDOUR;
99 using namespace PBD;
100 using namespace Gtk;
101 using namespace Glib;
102 using namespace Gtkmm2ext;
103 using namespace Editing;
105 using PBD::atoi;
107 const double Editor::timebar_height = 15.0;
109 #include "editor_xpms"
111 static const gchar *_snap_type_strings[] = {
112 N_("CD Frames"),
113 N_("SMPTE Frames"),
114 N_("SMPTE Seconds"),
115 N_("SMPTE Minutes"),
116 N_("Seconds"),
117 N_("Minutes"),
118 N_("Beats/32"),
119 N_("Beats/16"),
120 N_("Beats/8"),
121 N_("Beats/4"),
122 N_("Beats/3"),
123 N_("Beats"),
124 N_("Bars"),
125 N_("Marks"),
126 N_("Region starts"),
127 N_("Region ends"),
128 N_("Region syncs"),
129 N_("Region bounds"),
133 static const gchar *_snap_mode_strings[] = {
134 N_("No Grid"),
135 N_("Grid"),
136 N_("Magnetic"),
140 static const gchar *_edit_point_strings[] = {
141 N_("Playhead"),
142 N_("Marker"),
143 N_("Mouse"),
147 static const gchar *_zoom_focus_strings[] = {
148 N_("Left"),
149 N_("Right"),
150 N_("Center"),
151 N_("Playhead"),
152 N_("Mouse"),
153 N_("Active Mark"),
157 #ifdef USE_RUBBERBAND
158 static const gchar *_rb_opt_strings[] = {
159 N_("Mushy"),
160 N_("Smooth"),
161 N_("Balanced multitimbral mixture"),
162 N_("Unpitched percussion with stable notes"),
163 N_("Crisp monophonic instrumental"),
164 N_("Unpitched solo percussion"),
167 #endif
169 /* Soundfile drag-n-drop */
171 Gdk::Cursor* Editor::cross_hair_cursor = 0;
172 Gdk::Cursor* Editor::selector_cursor = 0;
173 Gdk::Cursor* Editor::trimmer_cursor = 0;
174 Gdk::Cursor* Editor::grabber_cursor = 0;
175 Gdk::Cursor* Editor::grabber_edit_point_cursor = 0;
176 Gdk::Cursor* Editor::zoom_cursor = 0;
177 Gdk::Cursor* Editor::time_fx_cursor = 0;
178 Gdk::Cursor* Editor::fader_cursor = 0;
179 Gdk::Cursor* Editor::speaker_cursor = 0;
180 Gdk::Cursor* Editor::wait_cursor = 0;
181 Gdk::Cursor* Editor::timebar_cursor = 0;
182 Gdk::Cursor* Editor::transparent_cursor = 0;
184 void
185 show_me_the_size (Requisition* r, const char* what)
187 cerr << "size of " << what << " = " << r->width << " x " << r->height << endl;
190 void
191 DragInfo::clear_copied_locations ()
193 for (list<Location*>::iterator i = copied_locations.begin(); i != copied_locations.end(); ++i) {
194 delete *i;
196 copied_locations.clear ();
199 Editor::Editor ()
201 /* time display buttons */
202 minsec_label (_("Mins:Secs")),
203 bbt_label (_("Bars:Beats")),
204 smpte_label (_("Timecode")),
205 frame_label (_("Samples")),
206 tempo_label (_("Tempo")),
207 meter_label (_("Meter")),
208 mark_label (_("Location Markers")),
209 range_mark_label (_("Range Markers")),
210 transport_mark_label (_("Loop/Punch Ranges")),
211 cd_mark_label (_("CD Markers")),
212 edit_packer (3, 4, true),
214 /* the values here don't matter: layout widgets
215 reset them as needed.
218 vertical_adjustment (0.0, 0.0, 10.0, 400.0),
219 horizontal_adjustment (0.0, 0.0, 20.0, 1200.0),
221 /* tool bar related */
223 edit_point_clock (X_("editpoint"), false, X_("EditPointClock"), true),
224 zoom_range_clock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, true),
226 toolbar_selection_clock_table (2,3),
228 automation_mode_button (_("mode")),
229 global_automation_button (_("automation")),
231 /* <CMT Additions> */
232 image_socket_listener(0),
233 /* </CMT Additions> */
235 /* nudge */
237 nudge_clock (X_("nudge"), false, X_("NudgeClock"), true, true),
238 meters_running(false)
241 constructed = false;
243 /* we are a singleton */
245 PublicEditor::_instance = this;
247 session = 0;
248 _have_idled = false;
250 selection = new Selection;
251 cut_buffer = new Selection;
253 selection->TimeChanged.connect (mem_fun(*this, &Editor::time_selection_changed));
254 selection->TracksChanged.connect (mem_fun(*this, &Editor::track_selection_changed));
255 selection->RegionsChanged.connect (mem_fun(*this, &Editor::region_selection_changed));
256 selection->PointsChanged.connect (mem_fun(*this, &Editor::point_selection_changed));
257 selection->MarkersChanged.connect (mem_fun(*this, &Editor::marker_selection_changed));
259 clicked_regionview = 0;
260 clicked_trackview = 0;
261 clicked_audio_trackview = 0;
262 clicked_crossfadeview = 0;
263 clicked_control_point = 0;
264 last_update_frame = 0;
265 drag_info.item = 0;
266 current_mixer_strip = 0;
267 current_bbt_points = 0;
268 tempo_lines = 0;
270 snap_type_strings = I18N (_snap_type_strings);
271 snap_mode_strings = I18N (_snap_mode_strings);
272 zoom_focus_strings = I18N (_zoom_focus_strings);
273 edit_point_strings = I18N (_edit_point_strings);
274 #ifdef USE_RUBBERBAND
275 rb_opt_strings = I18N (_rb_opt_strings);
276 #endif
278 snap_threshold = 5.0;
279 bbt_beat_subdivision = 4;
280 canvas_width = 0;
281 canvas_height = 0;
282 last_autoscroll_x = 0;
283 last_autoscroll_y = 0;
284 autoscroll_active = false;
285 autoscroll_timeout_tag = -1;
286 interthread_progress_window = 0;
287 logo_item = 0;
289 #ifdef FFT_ANALYSIS
290 analysis_window = 0;
291 #endif
293 current_interthread_info = 0;
294 _show_measures = true;
295 _show_waveforms = true;
296 _show_waveforms_rectified = false;
297 _show_waveforms_recording = true;
298 export_dialog = 0;
299 export_range_markers_dialog = 0;
300 show_gain_after_trim = false;
301 route_redisplay_does_not_sync_order_keys = false;
302 route_redisplay_does_not_reset_order_keys = false;
303 no_route_list_redisplay = false;
304 verbose_cursor_on = true;
305 route_removal = false;
306 show_automatic_regions_in_region_list = true;
307 last_item_entered = 0;
308 last_item_entered_n = 0;
310 region_list_sort_type = (Editing::RegionListSortType) 0;
311 have_pending_keyboard_selection = false;
312 _follow_playhead = true;
313 _stationary_playhead = false;
314 _xfade_visibility = true;
315 editor_ruler_menu = 0;
316 no_ruler_shown_update = false;
317 edit_group_list_menu = 0;
318 route_list_menu = 0;
319 region_list_menu = 0;
320 marker_menu = 0;
321 start_end_marker_menu = 0;
322 range_marker_menu = 0;
323 marker_menu_item = 0;
324 tm_marker_menu = 0;
325 transport_marker_menu = 0;
326 new_transport_marker_menu = 0;
327 editor_mixer_strip_width = Wide;
328 show_editor_mixer_when_tracks_arrive = false;
329 region_edit_menu_split_item = 0;
330 temp_location = 0;
331 region_edit_menu_split_multichannel_item = 0;
332 leftmost_frame = 0;
333 ignore_mouse_mode_toggle = false;
334 current_stepping_trackview = 0;
335 entered_track = 0;
336 entered_regionview = 0;
337 entered_marker = 0;
338 clear_entered_track = false;
339 _new_regionviews_show_envelope = false;
340 current_timefx = 0;
341 in_edit_group_row_change = false;
342 playhead_cursor = 0;
343 button_release_can_deselect = true;
344 _dragging_playhead = false;
345 _dragging_edit_point = false;
346 _dragging_hscrollbar = false;
347 select_new_marker = false;
348 rhythm_ferret = 0;
349 allow_vertical_scroll = false;
350 no_save_visual = false;
351 need_resize_line = false;
352 resize_line_y = 0;
353 old_resize_line_y = -1;
354 no_region_list_redisplay = false;
355 resize_idle_id = -1;
357 _scrubbing = false;
358 scrubbing_direction = 0;
360 sfbrowser = 0;
362 location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get();
363 location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get();
364 location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get();
365 location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get();
366 location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get();
368 range_marker_drag_rect = 0;
369 marker_drag_line = 0;
371 _edit_point = EditAtMouse;
372 set_mouse_mode (MouseObject, true);
374 frames_per_unit = 2048; /* too early to use reset_zoom () */
375 reset_hscrollbar_stepping ();
377 zoom_focus = ZoomFocusLeft;
378 set_zoom_focus (ZoomFocusLeft);
379 zoom_range_clock.ValueChanged.connect (mem_fun(*this, &Editor::zoom_adjustment_changed));
381 bbt_label.set_name ("EditorTimeButton");
382 bbt_label.set_size_request (-1, (int)timebar_height);
383 bbt_label.set_alignment (1.0, 0.5);
384 bbt_label.set_padding (5,0);
385 bbt_label.hide ();
386 bbt_label.set_no_show_all();
387 minsec_label.set_name ("EditorTimeButton");
388 minsec_label.set_size_request (-1, (int)timebar_height);
389 minsec_label.set_alignment (1.0, 0.5);
390 minsec_label.set_padding (5,0);
391 minsec_label.hide ();
392 minsec_label.set_no_show_all();
393 smpte_label.set_name ("EditorTimeButton");
394 smpte_label.set_size_request (-1, (int)timebar_height);
395 smpte_label.set_alignment (1.0, 0.5);
396 smpte_label.set_padding (5,0);
397 smpte_label.hide ();
398 smpte_label.set_no_show_all();
399 frame_label.set_name ("EditorTimeButton");
400 frame_label.set_size_request (-1, (int)timebar_height);
401 frame_label.set_alignment (1.0, 0.5);
402 frame_label.set_padding (5,0);
403 frame_label.hide ();
404 frame_label.set_no_show_all();
406 tempo_label.set_name ("EditorTimeButton");
407 tempo_label.set_size_request (-1, (int)timebar_height);
408 tempo_label.set_alignment (1.0, 0.5);
409 tempo_label.set_padding (5,0);
410 tempo_label.hide();
411 tempo_label.set_no_show_all();
412 meter_label.set_name ("EditorTimeButton");
413 meter_label.set_size_request (-1, (int)timebar_height);
414 meter_label.set_alignment (1.0, 0.5);
415 meter_label.set_padding (5,0);
416 meter_label.hide();
417 meter_label.set_no_show_all();
418 mark_label.set_name ("EditorTimeButton");
419 mark_label.set_size_request (-1, (int)timebar_height);
420 mark_label.set_alignment (1.0, 0.5);
421 mark_label.set_padding (5,0);
422 mark_label.hide();
423 mark_label.set_no_show_all();
424 cd_mark_label.set_name ("EditorTimeButton");
425 cd_mark_label.set_size_request (-1, (int)timebar_height);
426 cd_mark_label.set_alignment (1.0, 0.5);
427 cd_mark_label.set_padding (5,0);
428 cd_mark_label.hide();
429 cd_mark_label.set_no_show_all();
430 range_mark_label.set_name ("EditorTimeButton");
431 range_mark_label.set_size_request (-1, (int)timebar_height);
432 range_mark_label.set_alignment (1.0, 0.5);
433 range_mark_label.set_padding (5,0);
434 range_mark_label.hide();
435 range_mark_label.set_no_show_all();
436 transport_mark_label.set_name ("EditorTimeButton");
437 transport_mark_label.set_size_request (-1, (int)timebar_height);
438 transport_mark_label.set_alignment (1.0, 0.5);
439 transport_mark_label.set_padding (5,0);
440 transport_mark_label.hide();
441 transport_mark_label.set_no_show_all();
443 initialize_rulers ();
444 initialize_canvas ();
446 edit_controls_vbox.set_spacing (0);
447 horizontal_adjustment.signal_value_changed().connect (mem_fun(*this, &Editor::scroll_canvas_horizontally), false);
448 vertical_adjustment.signal_value_changed().connect (mem_fun(*this, &Editor::tie_vertical_scrolling), true);
449 track_canvas->signal_map_event().connect (mem_fun (*this, &Editor::track_canvas_map_handler));
451 controls_layout.add (edit_controls_vbox);
452 controls_layout.set_name ("EditControlsBase");
453 controls_layout.add_events (Gdk::SCROLL_MASK);
454 controls_layout.signal_scroll_event().connect (mem_fun(*this, &Editor::control_layout_scroll), false);
456 controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
457 controls_layout.signal_button_release_event().connect (mem_fun(*this, &Editor::edit_controls_button_release));
458 controls_layout_size_request_connection = controls_layout.signal_size_request().connect (mem_fun (*this, &Editor::controls_layout_size_request));
460 edit_vscrollbar.set_adjustment (vertical_adjustment);
461 edit_hscrollbar.set_adjustment (horizontal_adjustment);
463 edit_hscrollbar.signal_button_press_event().connect (mem_fun(*this, &Editor::hscrollbar_button_press), false);
464 edit_hscrollbar.signal_button_release_event().connect (mem_fun(*this, &Editor::hscrollbar_button_release), false);
465 edit_hscrollbar.signal_size_allocate().connect (mem_fun(*this, &Editor::hscrollbar_allocate));
467 edit_hscrollbar.set_name ("EditorHScrollbar");
469 build_cursors ();
470 setup_toolbar ();
472 edit_point_clock.ValueChanged.connect (mem_fun(*this, &Editor::edit_point_clock_changed));
474 //time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2);
475 time_canvas_vbox.set_size_request (-1, -1);
477 ruler_label_event_box.add (ruler_label_vbox);
478 ruler_label_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
479 ruler_label_event_box.set_name ("TimebarLabelBase");
480 ruler_label_event_box.signal_button_release_event().connect (mem_fun(*this, &Editor::ruler_label_button_release));
482 time_button_event_box.add (time_button_vbox);
484 time_button_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
485 time_button_event_box.set_name ("TimebarLabelBase");
486 time_button_event_box.signal_button_release_event().connect (mem_fun(*this, &Editor::ruler_label_button_release));
488 /* these enable us to have a dedicated window (for cursor setting, etc.)
489 for the canvas areas.
492 track_canvas_event_box.add (*track_canvas);
494 time_canvas_event_box.add (time_canvas_vbox);
495 time_canvas_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
497 edit_packer.set_col_spacings (0);
498 edit_packer.set_row_spacings (0);
499 edit_packer.set_homogeneous (false);
500 edit_packer.set_border_width (0);
501 edit_packer.set_name ("EditorWindow");
503 edit_packer.attach (edit_vscrollbar, 0, 1, 0, 4, FILL, FILL|EXPAND, 0, 0);
505 edit_packer.attach (ruler_label_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0);
506 edit_packer.attach (time_button_event_box, 1, 2, 1, 2, FILL, SHRINK, 0, 0);
507 edit_packer.attach (time_canvas_event_box, 2, 3, 0, 1, FILL|EXPAND, FILL, 0, 0);
509 edit_packer.attach (controls_layout, 1, 2, 2, 3, FILL, FILL|EXPAND, 0, 0);
510 edit_packer.attach (track_canvas_event_box, 2, 3, 1, 3, FILL|EXPAND, FILL|EXPAND, 0, 0);
512 edit_packer.attach (zoom_box, 1, 2, 3, 4, FILL, FILL, 0, 0);
513 edit_packer.attach (edit_hscrollbar, 2, 3, 3, 4, FILL|EXPAND, FILL, 0, 0);
515 bottom_hbox.set_border_width (2);
516 bottom_hbox.set_spacing (3);
518 route_display_model = ListStore::create(route_display_columns);
519 route_list_display.set_model (route_display_model);
520 route_list_display.append_column (_("Show"), route_display_columns.visible);
521 route_list_display.append_column (_("Name"), route_display_columns.text);
522 route_list_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
523 route_list_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
524 route_list_display.set_headers_visible (true);
525 route_list_display.set_name ("TrackListDisplay");
526 route_list_display.get_selection()->set_mode (SELECTION_NONE);
527 route_list_display.set_reorderable (true);
528 route_list_display.set_size_request (100,-1);
530 CellRendererToggle* route_list_visible_cell = dynamic_cast<CellRendererToggle*>(route_list_display.get_column_cell_renderer (0));
531 route_list_visible_cell->property_activatable() = true;
532 route_list_visible_cell->property_radio() = false;
534 route_display_model->signal_row_deleted().connect (mem_fun (*this, &Editor::route_list_delete));
535 route_display_model->signal_row_changed().connect (mem_fun (*this, &Editor::route_list_change));
536 route_display_model->signal_rows_reordered().connect (mem_fun (*this, &Editor::track_list_reorder));
538 route_list_display.signal_button_press_event().connect (mem_fun (*this, &Editor::route_list_display_button_press), false);
540 route_list_scroller.add (route_list_display);
541 route_list_scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
543 group_model = ListStore::create(group_columns);
544 edit_group_display.set_model (group_model);
545 edit_group_display.append_column (_("Name"), group_columns.text);
546 edit_group_display.append_column (_("Active"), group_columns.is_active);
547 edit_group_display.append_column (_("Show"), group_columns.is_visible);
548 edit_group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
549 edit_group_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
550 edit_group_display.get_column (2)->set_data (X_("colnum"), GUINT_TO_POINTER(2));
551 edit_group_display.get_column (0)->set_expand (true);
552 edit_group_display.get_column (1)->set_expand (false);
553 edit_group_display.get_column (2)->set_expand (false);
554 edit_group_display.set_headers_visible (true);
556 /* name is directly editable */
558 CellRendererText* name_cell = dynamic_cast<CellRendererText*>(edit_group_display.get_column_cell_renderer (0));
559 name_cell->property_editable() = true;
560 name_cell->signal_edited().connect (mem_fun (*this, &Editor::edit_group_name_edit));
562 /* use checkbox for the active + visible columns */
564 CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*>(edit_group_display.get_column_cell_renderer (1));
565 active_cell->property_activatable() = true;
566 active_cell->property_radio() = false;
568 active_cell = dynamic_cast<CellRendererToggle*>(edit_group_display.get_column_cell_renderer (1));
569 active_cell->property_activatable() = true;
570 active_cell->property_radio() = false;
572 group_model->signal_row_changed().connect (mem_fun (*this, &Editor::edit_group_row_change));
574 edit_group_display.set_name ("EditGroupList");
575 edit_group_display.get_selection()->set_mode (SELECTION_SINGLE);
576 edit_group_display.set_headers_visible (true);
577 edit_group_display.set_reorderable (false);
578 edit_group_display.set_rules_hint (true);
579 edit_group_display.set_size_request (75, -1);
581 edit_group_display_scroller.add (edit_group_display);
582 edit_group_display_scroller.set_policy (POLICY_AUTOMATIC, POLICY_AUTOMATIC);
584 edit_group_display.signal_button_press_event().connect (mem_fun(*this, &Editor::edit_group_list_button_press_event), false);
586 VBox* edit_group_display_packer = manage (new VBox());
587 HBox* edit_group_display_button_box = manage (new HBox());
588 edit_group_display_button_box->set_homogeneous (true);
590 Button* edit_group_add_button = manage (new Button ());
591 Button* edit_group_remove_button = manage (new Button ());
593 Widget* w;
595 w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
596 w->show();
597 edit_group_add_button->add (*w);
599 w = manage (new Image (Stock::REMOVE, ICON_SIZE_BUTTON));
600 w->show();
601 edit_group_remove_button->add (*w);
603 edit_group_add_button->signal_clicked().connect (mem_fun (*this, &Editor::new_edit_group));
604 edit_group_remove_button->signal_clicked().connect (mem_fun (*this, &Editor::remove_selected_edit_group));
606 edit_group_display_button_box->pack_start (*edit_group_add_button);
607 edit_group_display_button_box->pack_start (*edit_group_remove_button);
609 edit_group_display_packer->pack_start (edit_group_display_scroller, true, true);
610 edit_group_display_packer->pack_start (*edit_group_display_button_box, false, false);
612 region_list_display.set_size_request (100, -1);
613 region_list_display.set_name ("RegionListDisplay");
614 /* Try to prevent single mouse presses from initiating edits.
615 This relies on a hack in gtktreeview.c:gtk_treeview_button_press()
617 region_list_display.set_data ("mouse-edits-require-mod1", (gpointer) 0x1);
619 region_list_model = TreeStore::create (region_list_columns);
620 region_list_model->set_sort_func (0, mem_fun (*this, &Editor::region_list_sorter));
621 region_list_model->set_sort_column (0, SORT_ASCENDING);
623 region_list_display.set_model (region_list_model);
624 region_list_display.append_column (_("Regions"), region_list_columns.name);
625 region_list_display.set_headers_visible (false);
627 CellRendererText* region_name_cell = dynamic_cast<CellRendererText*>(region_list_display.get_column_cell_renderer (0));
628 region_name_cell->property_editable() = true;
629 region_name_cell->signal_edited().connect (mem_fun (*this, &Editor::region_name_edit));
631 region_list_display.get_selection()->set_select_function (mem_fun (*this, &Editor::region_list_selection_filter));
633 TreeViewColumn* tv_col = region_list_display.get_column(0);
634 CellRendererText* renderer = dynamic_cast<CellRendererText*>(region_list_display.get_column_cell_renderer (0));
635 tv_col->add_attribute(renderer->property_text(), region_list_columns.name);
636 tv_col->add_attribute(renderer->property_foreground_gdk(), region_list_columns.color_);
638 region_list_display.get_selection()->set_mode (SELECTION_MULTIPLE);
639 region_list_display.add_object_drag (region_list_columns.region.index(), "regions");
641 /* setup DnD handling */
643 list<TargetEntry> region_list_target_table;
645 region_list_target_table.push_back (TargetEntry ("text/plain"));
646 region_list_target_table.push_back (TargetEntry ("text/uri-list"));
647 region_list_target_table.push_back (TargetEntry ("application/x-rootwin-drop"));
649 region_list_display.add_drop_targets (region_list_target_table);
650 region_list_display.signal_drag_data_received().connect (mem_fun(*this, &Editor::region_list_display_drag_data_received));
652 region_list_scroller.add (region_list_display);
653 region_list_scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
655 region_list_display.signal_key_press_event().connect (mem_fun(*this, &Editor::region_list_display_key_press));
656 region_list_display.signal_key_release_event().connect (mem_fun(*this, &Editor::region_list_display_key_release));
657 region_list_display.signal_button_press_event().connect (mem_fun(*this, &Editor::region_list_display_button_press), false);
658 region_list_display.signal_button_release_event().connect (mem_fun(*this, &Editor::region_list_display_button_release));
659 region_list_display.get_selection()->signal_changed().connect (mem_fun(*this, &Editor::region_list_selection_changed));
660 // region_list_display.signal_popup_menu().connect (bind (mem_fun (*this, &Editor::show_region_list_display_context_menu), 1, 0));
662 named_selection_scroller.add (named_selection_display);
663 named_selection_scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
665 named_selection_model = TreeStore::create (named_selection_columns);
666 named_selection_display.set_model (named_selection_model);
667 named_selection_display.append_column (_("Chunks"), named_selection_columns.text);
668 named_selection_display.set_headers_visible (false);
669 named_selection_display.set_size_request (100, -1);
670 named_selection_display.set_name ("NamedSelectionDisplay");
672 named_selection_display.get_selection()->set_mode (SELECTION_SINGLE);
673 named_selection_display.set_size_request (100, -1);
674 named_selection_display.signal_button_release_event().connect (mem_fun(*this, &Editor::named_selection_display_button_release), false);
675 named_selection_display.signal_key_release_event().connect (mem_fun(*this, &Editor::named_selection_display_key_release), false);
676 named_selection_display.get_selection()->signal_changed().connect (mem_fun (*this, &Editor::named_selection_display_selection_changed));
678 /* SNAPSHOTS */
680 snapshot_display_model = ListStore::create (snapshot_display_columns);
681 snapshot_display.set_model (snapshot_display_model);
682 snapshot_display.append_column (X_("snapshot"), snapshot_display_columns.visible_name);
683 snapshot_display.set_name ("SnapshotDisplay");
684 snapshot_display.set_size_request (75, -1);
685 snapshot_display.set_headers_visible (false);
686 snapshot_display.set_reorderable (false);
687 snapshot_display_scroller.add (snapshot_display);
688 snapshot_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
690 snapshot_display.get_selection()->signal_changed().connect (mem_fun(*this, &Editor::snapshot_display_selection_changed));
691 snapshot_display.signal_button_press_event().connect (mem_fun (*this, &Editor::snapshot_display_button_press), false);
693 Gtk::Label* nlabel;
695 nlabel = manage (new Label (_("Regions")));
696 nlabel->set_angle (-90);
697 the_notebook.append_page (region_list_scroller, *nlabel);
698 nlabel = manage (new Label (_("Tracks/Busses")));
699 nlabel->set_angle (-90);
700 the_notebook.append_page (route_list_scroller, *nlabel);
701 nlabel = manage (new Label (_("Snapshots")));
702 nlabel->set_angle (-90);
703 the_notebook.append_page (snapshot_display_scroller, *nlabel);
704 nlabel = manage (new Label (_("Edit Groups")));
705 nlabel->set_angle (-90);
706 the_notebook.append_page (*edit_group_display_packer, *nlabel);
708 if (!Profile->get_sae()) {
709 nlabel = manage (new Label (_("Chunks")));
710 nlabel->set_angle (-90);
711 the_notebook.append_page (named_selection_scroller, *nlabel);
714 the_notebook.set_show_tabs (true);
715 the_notebook.set_scrollable (true);
716 the_notebook.popup_enable ();
717 the_notebook.set_tab_pos (Gtk::POS_RIGHT);
719 post_maximal_editor_width = 0;
720 post_maximal_pane_position = 0;
721 edit_pane.pack1 (edit_packer, true, true);
722 edit_pane.pack2 (the_notebook, false, true);
724 edit_pane.signal_size_allocate().connect (bind (mem_fun(*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&edit_pane)));
726 top_hbox.pack_start (toolbar_frame, true, true);
728 HBox *hbox = manage (new HBox);
729 hbox->pack_start (edit_pane, true, true);
731 global_vpacker.pack_start (top_hbox, false, false);
732 global_vpacker.pack_start (*hbox, true, true);
734 global_hpacker.pack_start (global_vpacker, true, true);
736 set_name ("EditorWindow");
737 add_accel_group (ActionManager::ui_manager->get_accel_group());
739 status_bar_hpacker.show ();
741 vpacker.pack_end (status_bar_hpacker, false, false);
742 vpacker.pack_end (global_hpacker, true, true);
744 /* register actions now so that set_state() can find them and set toggles/checks etc */
746 register_actions ();
748 snap_type = SnapToBeat;
749 set_snap_to (snap_type);
750 snap_mode = SnapOff;
751 set_snap_mode (snap_mode);
752 set_edit_point_preference (EditAtMouse, true);
754 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
755 set_state (*node);
757 _playlist_selector = new PlaylistSelector();
758 _playlist_selector->signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
760 RegionView::RegionViewGoingAway.connect (mem_fun(*this, &Editor::catch_vanishing_regionview));
762 /* nudge stuff */
764 nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
765 nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
767 ARDOUR_UI::instance()->tooltips().set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
768 ARDOUR_UI::instance()->tooltips().set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
770 nudge_forward_button.set_name ("TransportButton");
771 nudge_backward_button.set_name ("TransportButton");
773 fade_context_menu.set_name ("ArdourContextMenu");
775 /* icons, titles, WM stuff */
777 list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
778 Glib::RefPtr<Gdk::Pixbuf> icon;
780 if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
781 window_icons.push_back (icon);
783 if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
784 window_icons.push_back (icon);
786 if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
787 window_icons.push_back (icon);
789 if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
790 window_icons.push_back (icon);
792 if (!window_icons.empty()) {
793 set_icon_list (window_icons);
794 set_default_icon_list (window_icons);
797 WindowTitle title(Glib::get_application_name());
798 title += _("Editor");
799 set_title (title.get_string());
800 set_wmclass (X_("ardour_editor"), "Ardour");
802 add (vpacker);
803 add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
805 signal_configure_event().connect (mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
806 signal_delete_event().connect (mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
808 /* allow external control surfaces/protocols to do various things */
810 ControlProtocol::ZoomToSession.connect (mem_fun (*this, &Editor::temporal_zoom_session));
811 ControlProtocol::ZoomIn.connect (bind (mem_fun (*this, &Editor::temporal_zoom_step), false));
812 ControlProtocol::ZoomOut.connect (bind (mem_fun (*this, &Editor::temporal_zoom_step), true));
813 ControlProtocol::ScrollTimeline.connect (mem_fun (*this, &Editor::control_scroll));
814 BasicUI::AccessAction.connect (mem_fun (*this, &Editor::access_action));
816 Config->ParameterChanged.connect (mem_fun (*this, &Editor::parameter_changed));
817 Route::SyncOrderKeys.connect (mem_fun (*this, &Editor::sync_order_keys));
819 constructed = true;
820 instant_save ();
823 Editor::~Editor()
825 /* <CMT Additions> */
826 if(image_socket_listener)
828 if(image_socket_listener->is_connected())
830 image_socket_listener->close_connection() ;
833 delete image_socket_listener ;
834 image_socket_listener = 0 ;
836 /* </CMT Additions> */
838 if (track_canvas) {
839 delete track_canvas;
840 track_canvas = 0;
844 void
845 Editor::add_toplevel_controls (Container& cont)
847 vpacker.pack_start (cont, false, false);
848 cont.show_all ();
851 void
852 Editor::catch_vanishing_regionview (RegionView *rv)
854 /* note: the selection will take care of the vanishing
855 audioregionview by itself.
858 if (rv->get_canvas_group() == drag_info.item) {
859 end_grab (drag_info.item, 0);
862 if (clicked_regionview == rv) {
863 clicked_regionview = 0;
866 if (entered_regionview == rv) {
867 set_entered_regionview (0);
871 void
872 Editor::set_entered_regionview (RegionView* rv)
874 if (rv == entered_regionview) {
875 return;
878 if (entered_regionview) {
879 entered_regionview->exited ();
882 if ((entered_regionview = rv) != 0) {
883 entered_regionview->entered ();
887 void
888 Editor::set_entered_track (TimeAxisView* tav)
890 if (entered_track) {
891 entered_track->exited ();
894 if ((entered_track = tav) != 0) {
895 entered_track->entered ();
899 void
900 Editor::show_window ()
902 if (! is_visible ()) {
903 show_all ();
905 /* now reset all audio_time_axis heights, because widgets might need
906 to be re-hidden
909 TimeAxisView *tv;
911 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
912 tv = (static_cast<TimeAxisView*>(*i));
913 tv->reset_height ();
916 present ();
919 void
920 Editor::instant_save ()
922 if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
923 return;
926 if (session) {
927 session->add_instant_xml(get_state(), session->path());
928 } else {
929 Config->add_instant_xml(get_state(), get_user_ardour_path());
933 void
934 Editor::edit_point_clock_changed()
936 if (_dragging_edit_point) {
937 return;
940 if (selection->markers.empty()) {
941 return;
944 bool ignored;
945 Location* loc = find_location_from_marker (selection->markers.front(), ignored);
947 if (!loc) {
948 return;
951 loc->move_to (edit_point_clock.current_time());
954 void
955 Editor::zoom_adjustment_changed ()
957 if (session == 0) {
958 return;
961 double fpu = zoom_range_clock.current_duration() / canvas_width;
963 if (fpu < 1.0) {
964 fpu = 1.0;
965 zoom_range_clock.set ((nframes64_t) floor (fpu * canvas_width));
966 } else if (fpu > session->current_end_frame() / canvas_width) {
967 fpu = session->current_end_frame() / canvas_width;
968 zoom_range_clock.set ((nframes64_t) floor (fpu * canvas_width));
971 temporal_zoom (fpu);
974 void
975 Editor::control_scroll (float fraction)
977 ENSURE_GUI_THREAD(bind (mem_fun (*this, &Editor::control_scroll), fraction));
979 if (!session) {
980 return;
983 double step = fraction * current_page_frames();
986 _control_scroll_target is an optional<T>
988 it acts like a pointer to an nframes64_t, with
989 a operator conversion to boolean to check
990 that it has a value could possibly use
991 playhead_cursor->current_frame to store the
992 value and a boolean in the class to know
993 when it's out of date
996 if (!_control_scroll_target) {
997 _control_scroll_target = session->transport_frame();
998 _dragging_playhead = true;
1001 if ((fraction < 0.0f) && (*_control_scroll_target < (nframes64_t) fabs(step))) {
1002 *_control_scroll_target = 0;
1003 } else if ((fraction > 0.0f) && (max_frames - *_control_scroll_target < step)) {
1004 *_control_scroll_target = max_frames - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
1005 } else {
1006 *_control_scroll_target += (nframes64_t) floor (step);
1009 /* move visuals, we'll catch up with it later */
1011 playhead_cursor->set_position (*_control_scroll_target);
1012 UpdateAllTransportClocks (*_control_scroll_target);
1014 if (*_control_scroll_target > (current_page_frames() / 2)) {
1015 /* try to center PH in window */
1016 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
1017 } else {
1018 reset_x_origin (0);
1022 Now we do a timeout to actually bring the session to the right place
1023 according to the playhead. This is to avoid reading disk buffers on every
1024 call to control_scroll, which is driven by ScrollTimeline and therefore
1025 probably by a control surface wheel which can generate lots of events.
1027 /* cancel the existing timeout */
1029 control_scroll_connection.disconnect ();
1031 /* add the next timeout */
1033 control_scroll_connection = Glib::signal_timeout().connect (bind (mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
1036 bool
1037 Editor::deferred_control_scroll (nframes64_t target)
1039 session->request_locate (*_control_scroll_target, session->transport_rolling());
1040 // reset for next stream
1041 _control_scroll_target = boost::none;
1042 _dragging_playhead = false;
1043 return false;
1046 void
1047 Editor::access_action (std::string action_group, std::string action_item)
1049 if (!session) {
1050 return;
1053 ENSURE_GUI_THREAD (bind (mem_fun (*this, &Editor::access_action), action_group, action_item));
1055 RefPtr<Action> act;
1056 act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
1058 if (act) {
1059 act->activate();
1065 void
1066 Editor::on_realize ()
1068 Window::on_realize ();
1069 Realized ();
1072 void
1073 Editor::start_scrolling ()
1075 scroll_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect
1076 (mem_fun(*this, &Editor::update_current_screen));
1080 void
1081 Editor::stop_scrolling ()
1083 scroll_connection.disconnect ();
1086 void
1087 Editor::map_position_change (nframes64_t frame)
1089 ENSURE_GUI_THREAD (bind (mem_fun(*this, &Editor::map_position_change), frame));
1091 if (session == 0 || !_follow_playhead) {
1092 return;
1095 center_screen (frame);
1096 playhead_cursor->set_position (frame);
1099 void
1100 Editor::center_screen (nframes64_t frame)
1102 double page = canvas_width * frames_per_unit;
1104 /* if we're off the page, then scroll.
1107 if (frame < leftmost_frame || frame >= leftmost_frame + page) {
1108 center_screen_internal (frame, page);
1112 void
1113 Editor::center_screen_internal (nframes64_t frame, float page)
1115 page /= 2;
1117 if (frame > page) {
1118 frame -= (nframes64_t) page;
1119 } else {
1120 frame = 0;
1123 reset_x_origin (frame);
1126 void
1127 Editor::handle_new_duration ()
1129 if (!session) {
1130 return;
1133 ENSURE_GUI_THREAD (mem_fun (*this, &Editor::handle_new_duration));
1135 nframes64_t new_end = session->current_end_frame() + (nframes64_t) floorf (current_page_frames() * 0.10f);
1137 horizontal_adjustment.set_upper (new_end / frames_per_unit);
1138 horizontal_adjustment.set_page_size (current_page_frames()/frames_per_unit);
1140 if (horizontal_adjustment.get_value() + canvas_width > horizontal_adjustment.get_upper()) {
1141 horizontal_adjustment.set_value (horizontal_adjustment.get_upper() - canvas_width);
1143 //cerr << "Editor::handle_new_duration () called ha v:l:u:ps:lcf = " << horizontal_adjustment.get_value() << ":" << horizontal_adjustment.get_lower() << ":" << horizontal_adjustment.get_upper() << ":" << horizontal_adjustment.get_page_size() << ":" << endl;//DEBUG
1146 void
1147 Editor::update_title_s (const string & snap_name)
1149 ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::update_title_s), snap_name));
1151 update_title ();
1154 void
1155 Editor::update_title ()
1157 ENSURE_GUI_THREAD (mem_fun(*this, &Editor::update_title));
1159 if (session) {
1160 bool dirty = session->dirty();
1162 string session_name;
1164 if (session->snap_name() != session->name()) {
1165 session_name = session->snap_name();
1166 } else {
1167 session_name = session->name();
1170 if (dirty) {
1171 session_name = "*" + session_name;
1174 WindowTitle title(session_name);
1175 title += Glib::get_application_name();
1176 set_title (title.get_string());
1180 void
1181 Editor::connect_to_session (Session *t)
1183 session = t;
1185 /* there are never any selected regions at startup */
1187 sensitize_the_right_region_actions (false);
1189 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1190 set_state (*node);
1192 /* catch up with the playhead */
1194 session->request_locate (playhead_cursor->current_frame);
1196 update_title ();
1198 session->GoingAway.connect (mem_fun(*this, &Editor::session_going_away));
1199 session->history().Changed.connect (mem_fun (*this, &Editor::history_changed));
1201 /* These signals can all be emitted by a non-GUI thread. Therefore the
1202 handlers for them must not attempt to directly interact with the GUI,
1203 but use Gtkmm2ext::UI::instance()->call_slot();
1206 session_connections.push_back (session->TransportStateChange.connect (mem_fun(*this, &Editor::map_transport_state)));
1207 session_connections.push_back (session->PositionChanged.connect (mem_fun(*this, &Editor::map_position_change)));
1208 session_connections.push_back (session->RouteAdded.connect (mem_fun(*this, &Editor::handle_new_route)));
1209 session_connections.push_back (session->AudioRegionsAdded.connect (mem_fun(*this, &Editor::handle_new_audio_regions)));
1210 session_connections.push_back (session->AudioRegionRemoved.connect (mem_fun(*this, &Editor::handle_audio_region_removed)));
1211 session_connections.push_back (session->DurationChanged.connect (mem_fun(*this, &Editor::handle_new_duration)));
1212 session_connections.push_back (session->edit_group_added.connect (mem_fun(*this, &Editor::add_edit_group)));
1213 session_connections.push_back (session->edit_group_removed.connect (mem_fun(*this, &Editor::edit_groups_changed)));
1214 session_connections.push_back (session->NamedSelectionAdded.connect (mem_fun(*this, &Editor::handle_new_named_selection)));
1215 session_connections.push_back (session->NamedSelectionRemoved.connect (mem_fun(*this, &Editor::handle_new_named_selection)));
1216 session_connections.push_back (session->DirtyChanged.connect (mem_fun(*this, &Editor::update_title)));
1217 session_connections.push_back (session->StateSaved.connect (mem_fun(*this, &Editor::update_title_s)));
1218 session_connections.push_back (session->AskAboutPlaylistDeletion.connect (mem_fun(*this, &Editor::playlist_deletion_dialog)));
1219 session_connections.push_back (session->RegionHiddenChange.connect (mem_fun(*this, &Editor::region_hidden)));
1221 session_connections.push_back (session->SMPTEOffsetChanged.connect (mem_fun(*this, &Editor::update_just_smpte)));
1223 session_connections.push_back (session->tempo_map().StateChanged.connect (mem_fun(*this, &Editor::tempo_map_changed)));
1225 edit_groups_changed ();
1227 edit_point_clock.set_mode(AudioClock::BBT);
1228 edit_point_clock.set_session (session);
1229 zoom_range_clock.set_session (session);
1230 _playlist_selector->set_session (session);
1231 nudge_clock.set_session (session);
1232 if (Profile->get_sae()) {
1233 BBT_Time bbt;
1234 bbt.bars = 0;
1235 bbt.beats = 0;
1236 bbt.ticks = 120;
1237 nframes_t pos = session->tempo_map().bbt_duration_at (0, bbt, 1);
1238 nudge_clock.set_mode(AudioClock::BBT);
1239 nudge_clock.set (pos, true, 0, AudioClock::BBT);
1241 } else {
1242 nudge_clock.set (session->frame_rate() * 5, true, 0, AudioClock::SMPTE); // default of 5 seconds
1245 playhead_cursor->canvas_item.show ();
1247 if (rhythm_ferret) {
1248 rhythm_ferret->set_session (session);
1251 #ifdef FFT_ANALYSIS
1252 if (analysis_window != 0)
1253 analysis_window->set_session (session);
1254 #endif
1256 Location* loc = session->locations()->auto_loop_location();
1257 if (loc == 0) {
1258 loc = new Location (0, session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1259 if (loc->start() == loc->end()) {
1260 loc->set_end (loc->start() + 1);
1262 session->locations()->add (loc, false);
1263 session->set_auto_loop_location (loc);
1264 } else {
1265 // force name
1266 loc->set_name (_("Loop"));
1269 loc = session->locations()->auto_punch_location();
1270 if (loc == 0) {
1271 loc = new Location (0, session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1272 if (loc->start() == loc->end()) {
1273 loc->set_end (loc->start() + 1);
1275 session->locations()->add (loc, false);
1276 session->set_auto_punch_location (loc);
1277 } else {
1278 // force name
1279 loc->set_name (_("Punch"));
1282 Config->map_parameters (mem_fun (*this, &Editor::parameter_changed));
1284 session->StateSaved.connect (mem_fun(*this, &Editor::session_state_saved));
1286 refresh_location_display ();
1287 session->locations()->added.connect (mem_fun(*this, &Editor::add_new_location));
1288 session->locations()->removed.connect (mem_fun(*this, &Editor::location_gone));
1289 session->locations()->changed.connect (mem_fun(*this, &Editor::refresh_location_display));
1290 session->locations()->StateChanged.connect (mem_fun(*this, &Editor::refresh_location_display_s));
1291 session->locations()->end_location()->changed.connect (mem_fun(*this, &Editor::end_location_changed));
1293 if (sfbrowser) {
1294 sfbrowser->set_session (session);
1297 handle_new_duration ();
1299 redisplay_regions ();
1300 redisplay_named_selections ();
1301 redisplay_snapshots ();
1303 restore_ruler_visibility ();
1304 //tempo_map_changed (Change (0));
1305 session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1307 initial_route_list_display ();
1309 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1310 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1313 start_scrolling ();
1315 /* don't show master bus in a new session */
1317 if (ARDOUR_UI::instance()->session_is_new ()) {
1319 TreeModel::Children rows = route_display_model->children();
1320 TreeModel::Children::iterator i;
1322 no_route_list_redisplay = true;
1324 for (i = rows.begin(); i != rows.end(); ++i) {
1325 TimeAxisView *tv = (*i)[route_display_columns.tv];
1326 AudioTimeAxisView *atv;
1328 if ((atv = dynamic_cast<AudioTimeAxisView*>(tv)) != 0) {
1329 if (atv->route()->master()) {
1330 route_list_display.get_selection()->unselect (i);
1335 no_route_list_redisplay = false;
1336 redisplay_route_list ();
1339 switch (snap_type) {
1340 case SnapToRegionStart:
1341 case SnapToRegionEnd:
1342 case SnapToRegionSync:
1343 case SnapToRegionBoundary:
1344 build_region_boundary_cache ();
1345 break;
1347 default:
1348 break;
1351 /* register for undo history */
1353 session->register_with_memento_command_factory(_id, this);
1355 start_updating ();
1358 void
1359 Editor::build_cursors ()
1361 using namespace Gdk;
1363 Gdk::Color mbg ("#000000" ); /* Black */
1364 Gdk::Color mfg ("#0000ff" ); /* Blue. */
1367 RefPtr<Bitmap> source, mask;
1368 source = Bitmap::create (mag_bits, mag_width, mag_height);
1369 mask = Bitmap::create (magmask_bits, mag_width, mag_height);
1370 zoom_cursor = new Gdk::Cursor (source, mask, mfg, mbg, mag_x_hot, mag_y_hot);
1373 Gdk::Color fbg ("#ffffff" );
1374 Gdk::Color ffg ("#000000" );
1377 RefPtr<Bitmap> source, mask;
1379 source = Bitmap::create (fader_cursor_bits, fader_cursor_width, fader_cursor_height);
1380 mask = Bitmap::create (fader_cursor_mask_bits, fader_cursor_width, fader_cursor_height);
1381 fader_cursor = new Gdk::Cursor (source, mask, ffg, fbg, fader_cursor_x_hot, fader_cursor_y_hot);
1385 RefPtr<Bitmap> source, mask;
1386 source = Bitmap::create (speaker_cursor_bits, speaker_cursor_width, speaker_cursor_height);
1387 mask = Bitmap::create (speaker_cursor_mask_bits, speaker_cursor_width, speaker_cursor_height);
1388 speaker_cursor = new Gdk::Cursor (source, mask, ffg, fbg, speaker_cursor_x_hot, speaker_cursor_y_hot);
1392 RefPtr<Bitmap> bits;
1393 char pix[4] = { 0, 0, 0, 0 };
1394 bits = Bitmap::create (pix, 2, 2);
1395 Gdk::Color c;
1396 transparent_cursor = new Gdk::Cursor (bits, bits, c, c, 0, 0);
1400 grabber_cursor = new Gdk::Cursor (HAND2);
1403 Glib::RefPtr<Gdk::Pixbuf> grabber_edit_point_pixbuf (::get_icon ("grabber_edit_point"));
1404 grabber_edit_point_cursor = new Gdk::Cursor (Gdk::Display::get_default(), grabber_edit_point_pixbuf, 5, 17);
1407 cross_hair_cursor = new Gdk::Cursor (CROSSHAIR);
1408 trimmer_cursor = new Gdk::Cursor (SB_H_DOUBLE_ARROW);
1409 selector_cursor = new Gdk::Cursor (XTERM);
1410 time_fx_cursor = new Gdk::Cursor (SIZING);
1411 wait_cursor = new Gdk::Cursor (WATCH);
1412 timebar_cursor = new Gdk::Cursor(LEFT_PTR);
1415 void
1416 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1418 using namespace Menu_Helpers;
1419 AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1421 if (arv == 0) {
1422 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1423 /*NOTREACHED*/
1426 MenuList& items (fade_context_menu.items());
1428 items.clear ();
1430 switch (item_type) {
1431 case FadeInItem:
1432 case FadeInHandleItem:
1433 if (arv->audio_region()->fade_in_active()) {
1434 items.push_back (MenuElem (_("Deactivate"), bind (mem_fun (*this, &Editor::set_fade_in_active), false)));
1435 } else {
1436 items.push_back (MenuElem (_("Activate"), bind (mem_fun (*this, &Editor::set_fade_in_active), true)));
1439 items.push_back (SeparatorElem());
1441 if (Profile->get_sae()) {
1442 items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear)));
1443 items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast)));
1444 } else {
1445 items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear)));
1446 items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast)));
1447 items.push_back (MenuElem (_("Slow"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogB)));
1448 items.push_back (MenuElem (_("Fast"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogA)));
1449 items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Slow)));
1451 break;
1453 case FadeOutItem:
1454 case FadeOutHandleItem:
1455 if (arv->audio_region()->fade_out_active()) {
1456 items.push_back (MenuElem (_("Deactivate"), bind (mem_fun (*this, &Editor::set_fade_out_active), false)));
1457 } else {
1458 items.push_back (MenuElem (_("Activate"), bind (mem_fun (*this, &Editor::set_fade_out_active), true)));
1461 items.push_back (SeparatorElem());
1463 if (Profile->get_sae()) {
1464 items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear)));
1465 items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow)));
1466 } else {
1467 items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear)));
1468 items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow)));
1469 items.push_back (MenuElem (_("Slow"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogA)));
1470 items.push_back (MenuElem (_("Fast"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogB)));
1471 items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Fast)));
1473 break;
1475 default:
1476 fatal << _("programming error: ")
1477 << X_("non-fade canvas item passed to popup_fade_context_menu()")
1478 << endmsg;
1479 /*NOTREACHED*/
1482 fade_context_menu.popup (button, time);
1485 void
1486 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection, nframes64_t frame)
1488 using namespace Menu_Helpers;
1489 Menu* (Editor::*build_menu_function)(nframes64_t);
1490 Menu *menu;
1492 switch (item_type) {
1493 case RegionItem:
1494 case RegionViewName:
1495 case RegionViewNameHighlight:
1496 if (with_selection) {
1497 build_menu_function = &Editor::build_track_selection_context_menu;
1498 } else {
1499 build_menu_function = &Editor::build_track_region_context_menu;
1501 break;
1503 case SelectionItem:
1504 if (with_selection) {
1505 build_menu_function = &Editor::build_track_selection_context_menu;
1506 } else {
1507 build_menu_function = &Editor::build_track_context_menu;
1509 break;
1511 case CrossfadeViewItem:
1512 build_menu_function = &Editor::build_track_crossfade_context_menu;
1513 break;
1515 case StreamItem:
1516 if (clicked_audio_trackview->get_diskstream()) {
1517 build_menu_function = &Editor::build_track_context_menu;
1518 } else {
1519 build_menu_function = &Editor::build_track_bus_context_menu;
1521 break;
1523 default:
1524 /* probably shouldn't happen but if it does, we don't care */
1525 return;
1528 menu = (this->*build_menu_function)(frame);
1529 menu->set_name ("ArdourContextMenu");
1531 /* now handle specific situations */
1533 switch (item_type) {
1534 case RegionItem:
1535 case RegionViewName:
1536 case RegionViewNameHighlight:
1537 if (!with_selection) {
1538 if (region_edit_menu_split_item) {
1539 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1540 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1541 } else {
1542 ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1546 if (region_edit_menu_split_multichannel_item) {
1547 if (clicked_regionview && clicked_regionview->region().n_channels() > 1) {
1548 // GTK2FIX find the action, change its sensitivity
1549 // region_edit_menu_split_multichannel_item->set_sensitive (true);
1550 } else {
1551 // GTK2FIX see above
1552 // region_edit_menu_split_multichannel_item->set_sensitive (false);
1556 break;
1558 case SelectionItem:
1559 break;
1561 case CrossfadeViewItem:
1562 break;
1564 case StreamItem:
1565 break;
1567 default:
1568 /* probably shouldn't happen but if it does, we don't care */
1569 return;
1572 if (item_type != SelectionItem && clicked_audio_trackview && clicked_audio_trackview->audio_track()) {
1574 /* Bounce to disk */
1576 using namespace Menu_Helpers;
1577 MenuList& edit_items = menu->items();
1579 edit_items.push_back (SeparatorElem());
1581 switch (clicked_audio_trackview->audio_track()->freeze_state()) {
1582 case AudioTrack::NoFreeze:
1583 edit_items.push_back (MenuElem (_("Freeze"), mem_fun(*this, &Editor::freeze_route)));
1584 break;
1586 case AudioTrack::Frozen:
1587 edit_items.push_back (MenuElem (_("Unfreeze"), mem_fun(*this, &Editor::unfreeze_route)));
1588 break;
1590 case AudioTrack::UnFrozen:
1591 edit_items.push_back (MenuElem (_("Freeze"), mem_fun(*this, &Editor::freeze_route)));
1592 break;
1597 menu->popup (button, time);
1600 Menu*
1601 Editor::build_track_context_menu (nframes64_t ignored)
1603 using namespace Menu_Helpers;
1605 MenuList& edit_items = track_context_menu.items();
1606 edit_items.clear();
1608 add_dstream_context_items (edit_items);
1609 return &track_context_menu;
1612 Menu*
1613 Editor::build_track_bus_context_menu (nframes64_t ignored)
1615 using namespace Menu_Helpers;
1617 MenuList& edit_items = track_context_menu.items();
1618 edit_items.clear();
1620 add_bus_context_items (edit_items);
1621 return &track_context_menu;
1624 Menu*
1625 Editor::build_track_region_context_menu (nframes64_t frame)
1627 using namespace Menu_Helpers;
1628 MenuList& edit_items = track_region_context_menu.items();
1629 edit_items.clear();
1631 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_trackview);
1633 if (atv) {
1634 boost::shared_ptr<Diskstream> ds;
1635 boost::shared_ptr<Playlist> pl;
1637 if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()))) {
1638 Playlist::RegionList* regions = pl->regions_at ((nframes64_t) floor ( (double)frame * ds->speed()));
1640 if (selection->regions.size() > 1) {
1641 // there's already a multiple selection: just add a
1642 // single region context menu that will act on all
1643 // selected regions
1644 boost::shared_ptr<Region> dummy_region; // = NULL
1645 add_region_context_items (atv->audio_view(), dummy_region, edit_items);
1646 } else {
1647 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
1648 add_region_context_items (atv->audio_view(), (*i), edit_items);
1652 delete regions;
1656 add_dstream_context_items (edit_items);
1658 return &track_region_context_menu;
1661 Menu*
1662 Editor::build_track_crossfade_context_menu (nframes64_t frame)
1664 using namespace Menu_Helpers;
1665 MenuList& edit_items = track_crossfade_context_menu.items();
1666 edit_items.clear ();
1668 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_trackview);
1670 if (atv) {
1671 boost::shared_ptr<Diskstream> ds;
1672 boost::shared_ptr<Playlist> pl;
1673 boost::shared_ptr<AudioPlaylist> apl;
1675 if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1677 Playlist::RegionList* regions = pl->regions_at (frame);
1678 AudioPlaylist::Crossfades xfades;
1680 apl->crossfades_at (frame, xfades);
1682 bool many = xfades.size() > 1;
1684 for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1685 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1688 if (selection->regions.size() > 1) {
1689 // there's already a multiple selection: just add a
1690 // single region context menu that will act on all
1691 // selected regions
1692 boost::shared_ptr<Region> dummy_region; // = NULL
1693 add_region_context_items (atv->audio_view(), dummy_region, edit_items);
1694 } else {
1695 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
1696 add_region_context_items (atv->audio_view(), (*i), edit_items);
1699 delete regions;
1703 add_dstream_context_items (edit_items);
1705 return &track_crossfade_context_menu;
1708 #ifdef FFT_ANALYSIS
1709 void
1710 Editor::analyze_region_selection()
1712 if (analysis_window == 0) {
1713 analysis_window = new AnalysisWindow();
1715 if (session != 0)
1716 analysis_window->set_session(session);
1718 analysis_window->show_all();
1721 analysis_window->set_regionmode();
1722 analysis_window->analyze();
1724 analysis_window->present();
1727 void
1728 Editor::analyze_range_selection()
1730 if (analysis_window == 0) {
1731 analysis_window = new AnalysisWindow();
1733 if (session != 0)
1734 analysis_window->set_session(session);
1736 analysis_window->show_all();
1739 analysis_window->set_rangemode();
1740 analysis_window->analyze();
1742 analysis_window->present();
1744 #endif /* FFT_ANALYSIS */
1748 Menu*
1749 Editor::build_track_selection_context_menu (nframes64_t ignored)
1751 using namespace Menu_Helpers;
1752 MenuList& edit_items = track_selection_context_menu.items();
1753 edit_items.clear ();
1755 add_selection_context_items (edit_items);
1756 // edit_items.push_back (SeparatorElem());
1757 // add_dstream_context_items (edit_items);
1759 return &track_selection_context_menu;
1762 void
1763 Editor::add_crossfade_context_items (AudioStreamView* view, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1765 using namespace Menu_Helpers;
1766 Menu *xfade_menu = manage (new Menu);
1767 MenuList& items = xfade_menu->items();
1768 xfade_menu->set_name ("ArdourContextMenu");
1769 string str;
1771 if (xfade->active()) {
1772 str = _("Mute");
1773 } else {
1774 str = _("Unmute");
1777 items.push_back (MenuElem (str, bind (mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1778 items.push_back (MenuElem (_("Edit"), bind (mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1780 if (xfade->can_follow_overlap()) {
1782 if (xfade->following_overlap()) {
1783 str = _("Convert to short");
1784 } else {
1785 str = _("Convert to full");
1788 items.push_back (MenuElem (str, bind (mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1791 if (many) {
1792 str = xfade->out()->name();
1793 str += "->";
1794 str += xfade->in()->name();
1795 } else {
1796 str = _("Crossfade");
1799 edit_items.push_back (MenuElem (str, *xfade_menu));
1800 edit_items.push_back (SeparatorElem());
1803 void
1804 Editor::xfade_edit_left_region ()
1806 if (clicked_crossfadeview) {
1807 clicked_crossfadeview->left_view.show_region_editor ();
1811 void
1812 Editor::xfade_edit_right_region ()
1814 if (clicked_crossfadeview) {
1815 clicked_crossfadeview->right_view.show_region_editor ();
1819 void
1820 Editor::add_region_context_items (AudioStreamView* sv, boost::shared_ptr<Region> region, Menu_Helpers::MenuList& edit_items)
1822 using namespace Menu_Helpers;
1823 Gtk::MenuItem* foo_item;
1824 Menu *region_menu = manage (new Menu);
1825 MenuList& items = region_menu->items();
1826 region_menu->set_name ("ArdourContextMenu");
1828 boost::shared_ptr<AudioRegion> ar;
1830 if (region) {
1831 ar = boost::dynamic_pointer_cast<AudioRegion> (region);
1833 /* when this particular menu pops up, make the relevant region
1834 become selected.
1837 region_menu->signal_map_event().connect (
1838 bind (
1839 mem_fun(*this, &Editor::set_selected_regionview_from_map_event),
1840 sv,
1841 boost::weak_ptr<Region>(region)
1845 items.push_back (MenuElem (_("Rename"), mem_fun(*this, &Editor::rename_region)));
1846 items.push_back (MenuElem (_("Popup region editor"), mem_fun(*this, &Editor::edit_region)));
1849 items.push_back (MenuElem (_("Raise to top layer"), mem_fun(*this, &Editor::raise_region_to_top)));
1850 items.push_back (MenuElem (_("Lower to bottom layer"), mem_fun (*this, &Editor::lower_region_to_bottom)));
1851 items.push_back (SeparatorElem());
1852 items.push_back (MenuElem (_("Define sync point"), mem_fun(*this, &Editor::set_region_sync_from_edit_point)));
1853 items.push_back (MenuElem (_("Remove sync point"), mem_fun(*this, &Editor::remove_region_sync)));
1854 items.push_back (SeparatorElem());
1856 items.push_back (MenuElem (_("Audition"), mem_fun(*this, &Editor::play_selected_region)));
1857 items.push_back (MenuElem (_("Export"), mem_fun(*this, &Editor::export_region)));
1858 items.push_back (MenuElem (_("Bounce"), mem_fun(*this, &Editor::bounce_region_selection)));
1860 #ifdef FFT_ANALYSIS
1861 items.push_back (MenuElem (_("Spectral Analysis"), mem_fun(*this, &Editor::analyze_region_selection)));
1862 #endif
1864 items.push_back (SeparatorElem());
1866 sigc::connection fooc;
1867 boost::shared_ptr<Region> region_to_check;
1869 if (region) {
1870 region_to_check = region;
1871 } else {
1872 region_to_check = selection->regions.front()->region();
1875 items.push_back (CheckMenuElem (_("Lock")));
1876 CheckMenuItem* region_lock_item = static_cast<CheckMenuItem*>(&items.back());
1877 if (region_to_check->locked()) {
1878 region_lock_item->set_active();
1880 region_lock_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_lock));
1882 items.push_back (CheckMenuElem (_("Glue to Bars&Beats")));
1883 CheckMenuItem* bbt_glue_item = static_cast<CheckMenuItem*>(&items.back());
1885 switch (region_to_check->positional_lock_style()) {
1886 case Region::MusicTime:
1887 bbt_glue_item->set_active (true);
1888 break;
1889 default:
1890 bbt_glue_item->set_active (false);
1891 break;
1894 bbt_glue_item->signal_activate().connect (bind (mem_fun (*this, &Editor::set_region_lock_style), Region::MusicTime));
1896 items.push_back (CheckMenuElem (_("Mute")));
1897 CheckMenuItem* region_mute_item = static_cast<CheckMenuItem*>(&items.back());
1898 fooc = region_mute_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_mute));
1899 if (region_to_check->muted()) {
1900 fooc.block (true);
1901 region_mute_item->set_active();
1902 fooc.block (false);
1905 items.push_back (MenuElem (_("Transpose"), mem_fun(*this, &Editor::pitch_shift_regions)));
1907 if (!Profile->get_sae()) {
1908 items.push_back (CheckMenuElem (_("Opaque")));
1909 CheckMenuItem* region_opaque_item = static_cast<CheckMenuItem*>(&items.back());
1910 fooc = region_opaque_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_opaque));
1911 if (region_to_check->opaque()) {
1912 fooc.block (true);
1913 region_opaque_item->set_active();
1914 fooc.block (false);
1918 items.push_back (CheckMenuElem (_("Original position"), mem_fun(*this, &Editor::naturalize)));
1919 if (region_to_check->at_natural_position()) {
1920 items.back().set_sensitive (false);
1923 items.push_back (SeparatorElem());
1925 if (ar) {
1927 RegionView* rv = sv->find_view (ar);
1928 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1930 if (!Profile->get_sae()) {
1931 items.push_back (MenuElem (_("Reset Envelope"), mem_fun(*this, &Editor::reset_region_gain_envelopes)));
1933 items.push_back (CheckMenuElem (_("Envelope Visible")));
1934 CheckMenuItem* region_envelope_visible_item = static_cast<CheckMenuItem*> (&items.back());
1935 fooc = region_envelope_visible_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_gain_envelope_visibility));
1936 if (arv->envelope_visible()) {
1937 fooc.block (true);
1938 region_envelope_visible_item->set_active (true);
1939 fooc.block (false);
1942 items.push_back (CheckMenuElem (_("Envelope Active")));
1943 CheckMenuItem* region_envelope_active_item = static_cast<CheckMenuItem*> (&items.back());
1944 fooc = region_envelope_active_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_gain_envelope_active));
1946 if (ar->envelope_active()) {
1947 fooc.block (true);
1948 region_envelope_active_item->set_active (true);
1949 fooc.block (false);
1952 items.push_back (SeparatorElem());
1955 if (ar->scale_amplitude() != 1.0f) {
1956 items.push_back (MenuElem (_("DeNormalize"), mem_fun(*this, &Editor::denormalize_region)));
1957 } else {
1958 items.push_back (MenuElem (_("Normalize"), mem_fun(*this, &Editor::normalize_region)));
1962 items.push_back (MenuElem (_("Reverse"), mem_fun(*this, &Editor::reverse_region)));
1963 items.push_back (SeparatorElem());
1965 /* range related stuff */
1967 items.push_back (MenuElem (_("Add Single Range"), mem_fun (*this, &Editor::add_location_from_audio_region)));
1968 items.push_back (MenuElem (_("Add Range Markers"), mem_fun (*this, &Editor::add_locations_from_audio_region)));
1969 if (selection->regions.size() < 2) {
1970 items.back().set_sensitive (false);
1973 items.push_back (MenuElem (_("Set Range Selection"), mem_fun (*this, &Editor::set_selection_from_audio_region)));
1974 items.push_back (SeparatorElem());
1976 /* Nudge region */
1978 Menu *nudge_menu = manage (new Menu());
1979 MenuList& nudge_items = nudge_menu->items();
1980 nudge_menu->set_name ("ArdourContextMenu");
1982 nudge_items.push_back (MenuElem (_("Nudge fwd"), (bind (mem_fun(*this, &Editor::nudge_forward), false, false))));
1983 nudge_items.push_back (MenuElem (_("Nudge bwd"), (bind (mem_fun(*this, &Editor::nudge_backward), false, false))));
1984 nudge_items.push_back (MenuElem (_("Nudge fwd by capture offset"), (mem_fun(*this, &Editor::nudge_forward_capture_offset))));
1985 nudge_items.push_back (MenuElem (_("Nudge bwd by capture offset"), (mem_fun(*this, &Editor::nudge_backward_capture_offset))));
1987 items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1988 items.push_back (SeparatorElem());
1990 Menu *trim_menu = manage (new Menu);
1991 MenuList& trim_items = trim_menu->items();
1992 trim_menu->set_name ("ArdourContextMenu");
1994 trim_items.push_back (MenuElem (_("Start to edit point"), mem_fun(*this, &Editor::trim_region_from_edit_point)));
1995 foo_item = &trim_items.back();
1996 if (_edit_point == EditAtMouse) {
1997 foo_item->set_sensitive (false);
1999 trim_items.push_back (MenuElem (_("Edit point to end"), mem_fun(*this, &Editor::trim_region_to_edit_point)));
2000 foo_item = &trim_items.back();
2001 if (_edit_point == EditAtMouse) {
2002 foo_item->set_sensitive (false);
2004 trim_items.push_back (MenuElem (_("Trim To Loop"), mem_fun(*this, &Editor::trim_region_to_loop)));
2005 trim_items.push_back (MenuElem (_("Trim To Punch"), mem_fun(*this, &Editor::trim_region_to_punch)));
2007 items.push_back (MenuElem (_("Trim"), *trim_menu));
2008 items.push_back (SeparatorElem());
2010 items.push_back (MenuElem (_("Split"), (mem_fun(*this, &Editor::split_region))));
2011 region_edit_menu_split_item = &items.back();
2013 if (_edit_point == EditAtMouse) {
2014 region_edit_menu_split_item->set_sensitive (false);
2017 items.push_back (MenuElem (_("Make mono regions"), (mem_fun(*this, &Editor::split_multichannel_region))));
2018 region_edit_menu_split_multichannel_item = &items.back();
2020 items.push_back (MenuElem (_("Duplicate"), (bind (mem_fun(*this, &Editor::duplicate_dialog), false))));
2021 items.push_back (MenuElem (_("Multi-Duplicate"), (bind (mem_fun(*this, &Editor::duplicate_dialog), true))));
2022 items.push_back (MenuElem (_("Fill Track"), (mem_fun(*this, &Editor::region_fill_track))));
2023 items.push_back (SeparatorElem());
2024 items.push_back (MenuElem (_("Remove"), mem_fun(*this, &Editor::remove_region)));
2026 /* OK, stick the region submenu at the top of the list, and then add
2027 the standard items.
2030 /* we have to hack up the region name because "_" has a special
2031 meaning for menu titles.
2034 string::size_type pos = 0;
2035 string menu_item_name = (region) ? region->name() : _("Selected regions");
2037 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
2038 menu_item_name.replace (pos, 1, "__");
2039 pos += 2;
2042 edit_items.push_back (MenuElem (menu_item_name, *region_menu));
2043 edit_items.push_back (SeparatorElem());
2046 void
2047 Editor::add_selection_context_items (Menu_Helpers::MenuList& items)
2049 using namespace Menu_Helpers;
2051 items.push_back (MenuElem (_("Play range"), mem_fun(*this, &Editor::play_selection)));
2052 items.push_back (MenuElem (_("Loop range"), bind (mem_fun(*this, &Editor::set_loop_from_selection), true)));
2054 #ifdef FFT_ANALYSIS
2055 items.push_back (SeparatorElem());
2056 items.push_back (MenuElem (_("Spectral Analysis"), mem_fun(*this, &Editor::analyze_range_selection)));
2057 #endif
2059 items.push_back (SeparatorElem());
2060 items.push_back (MenuElem (_("Extend Range to End of Region"), bind (mem_fun(*this, &Editor::extend_selection_to_end_of_region), false)));
2061 items.push_back (MenuElem (_("Extend Range to Start of Region"), bind (mem_fun(*this, &Editor::extend_selection_to_start_of_region), false)));
2063 items.push_back (SeparatorElem());
2064 items.push_back (MenuElem (_("Convert to region in-place"), mem_fun(*this, &Editor::separate_region_from_selection)));
2065 items.push_back (MenuElem (_("Convert to region in region list"), mem_fun(*this, &Editor::new_region_from_selection)));
2067 items.push_back (SeparatorElem());
2068 items.push_back (MenuElem (_("Select all in range"), mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
2070 items.push_back (SeparatorElem());
2071 items.push_back (MenuElem (_("Set loop from selection"), bind (mem_fun(*this, &Editor::set_loop_from_selection), false)));
2072 items.push_back (MenuElem (_("Set punch from selection"), mem_fun(*this, &Editor::set_punch_from_selection)));
2074 items.push_back (SeparatorElem());
2075 items.push_back (MenuElem (_("Add Range Markers"), mem_fun (*this, &Editor::add_location_from_selection)));
2076 items.push_back (SeparatorElem());
2077 items.push_back (MenuElem (_("Crop region to range"), mem_fun(*this, &Editor::crop_region_to_selection)));
2078 items.push_back (MenuElem (_("Fill range with region"), mem_fun(*this, &Editor::region_fill_selection)));
2079 items.push_back (MenuElem (_("Duplicate range"), bind (mem_fun(*this, &Editor::duplicate_dialog), false)));
2080 items.push_back (MenuElem (_("Create chunk from range"), mem_fun(*this, &Editor::create_named_selection)));
2081 items.push_back (SeparatorElem());
2082 items.push_back (MenuElem (_("Consolidate range"), bind (mem_fun(*this, &Editor::bounce_range_selection), true, false)));
2083 items.push_back (MenuElem (_("Consolidate range with processing"), bind (mem_fun(*this, &Editor::bounce_range_selection), true, true)));
2084 items.push_back (MenuElem (_("Bounce range to region list"), bind (mem_fun(*this, &Editor::bounce_range_selection), false, false)));
2085 items.push_back (MenuElem (_("Bounce range to region list with processing"), bind (mem_fun(*this, &Editor::bounce_range_selection), false, true)));
2086 items.push_back (MenuElem (_("Export range"), mem_fun(*this, &Editor::export_selection)));
2089 void
2090 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
2092 using namespace Menu_Helpers;
2094 /* Playback */
2096 Menu *play_menu = manage (new Menu);
2097 MenuList& play_items = play_menu->items();
2098 play_menu->set_name ("ArdourContextMenu");
2100 play_items.push_back (MenuElem (_("Play from edit point"), mem_fun(*this, &Editor::play_from_edit_point)));
2101 play_items.push_back (MenuElem (_("Play from start"), mem_fun(*this, &Editor::play_from_start)));
2102 play_items.push_back (MenuElem (_("Play region"), mem_fun(*this, &Editor::play_selected_region)));
2103 play_items.push_back (SeparatorElem());
2104 play_items.push_back (MenuElem (_("Loop Region"), mem_fun(*this, &Editor::loop_selected_region)));
2106 edit_items.push_back (MenuElem (_("Play"), *play_menu));
2108 /* Selection */
2110 Menu *select_menu = manage (new Menu);
2111 MenuList& select_items = select_menu->items();
2112 select_menu->set_name ("ArdourContextMenu");
2114 select_items.push_back (MenuElem (_("Select All in track"), bind (mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2115 select_items.push_back (MenuElem (_("Select All"), bind (mem_fun(*this, &Editor::select_all), Selection::Set)));
2116 select_items.push_back (MenuElem (_("Invert selection in track"), mem_fun(*this, &Editor::invert_selection_in_track)));
2117 select_items.push_back (MenuElem (_("Invert selection"), mem_fun(*this, &Editor::invert_selection)));
2118 select_items.push_back (SeparatorElem());
2119 select_items.push_back (MenuElem (_("Set range to loop range"), mem_fun(*this, &Editor::set_selection_from_loop)));
2120 select_items.push_back (MenuElem (_("Set range to punch range"), mem_fun(*this, &Editor::set_selection_from_punch)));
2121 select_items.push_back (SeparatorElem());
2122 select_items.push_back (MenuElem (_("Select All After Edit Point"), bind (mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
2123 select_items.push_back (MenuElem (_("Select All Before Edit Point"), bind (mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
2124 select_items.push_back (MenuElem (_("Select All After Playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2125 select_items.push_back (MenuElem (_("Select All Before Playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2126 select_items.push_back (MenuElem (_("Select All Between Playhead & Edit Point"), bind (mem_fun(*this, &Editor::select_all_selectables_between), false)));
2127 select_items.push_back (MenuElem (_("Select All Within Playhead & Edit Point"), bind (mem_fun(*this, &Editor::select_all_selectables_between), true)));
2128 select_items.push_back (MenuElem (_("Select Range Between Playhead & Edit Point"), mem_fun(*this, &Editor::select_range_between)));
2130 select_items.push_back (SeparatorElem());
2132 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2134 /* Cut-n-Paste */
2136 Menu *cutnpaste_menu = manage (new Menu);
2137 MenuList& cutnpaste_items = cutnpaste_menu->items();
2138 cutnpaste_menu->set_name ("ArdourContextMenu");
2140 cutnpaste_items.push_back (MenuElem (_("Cut"), mem_fun(*this, &Editor::cut)));
2141 cutnpaste_items.push_back (MenuElem (_("Copy"), mem_fun(*this, &Editor::copy)));
2142 cutnpaste_items.push_back (MenuElem (_("Paste"), bind (mem_fun(*this, &Editor::paste), 1.0f)));
2144 cutnpaste_items.push_back (SeparatorElem());
2146 cutnpaste_items.push_back (MenuElem (_("Align"), bind (mem_fun(*this, &Editor::align), ARDOUR::SyncPoint)));
2147 cutnpaste_items.push_back (MenuElem (_("Align Relative"), bind (mem_fun(*this, &Editor::align_relative), ARDOUR::SyncPoint)));
2149 cutnpaste_items.push_back (SeparatorElem());
2151 cutnpaste_items.push_back (MenuElem (_("Insert chunk"), bind (mem_fun(*this, &Editor::paste_named_selection), 1.0f)));
2153 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
2155 /* Adding new material */
2157 edit_items.push_back (SeparatorElem());
2158 edit_items.push_back (MenuElem (_("Insert Selected Region"), bind (mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
2159 edit_items.push_back (MenuElem (_("Insert Existing Audio"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
2161 /* Nudge track */
2163 Menu *nudge_menu = manage (new Menu());
2164 MenuList& nudge_items = nudge_menu->items();
2165 nudge_menu->set_name ("ArdourContextMenu");
2167 edit_items.push_back (SeparatorElem());
2168 nudge_items.push_back (MenuElem (_("Nudge entire track fwd"), (bind (mem_fun(*this, &Editor::nudge_track), false, true))));
2169 nudge_items.push_back (MenuElem (_("Nudge track after edit point fwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, true))));
2170 nudge_items.push_back (MenuElem (_("Nudge entire track bwd"), (bind (mem_fun(*this, &Editor::nudge_track), false, false))));
2171 nudge_items.push_back (MenuElem (_("Nudge track after edit point bwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, false))));
2173 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2176 void
2177 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
2179 using namespace Menu_Helpers;
2181 /* Playback */
2183 Menu *play_menu = manage (new Menu);
2184 MenuList& play_items = play_menu->items();
2185 play_menu->set_name ("ArdourContextMenu");
2187 play_items.push_back (MenuElem (_("Play from edit point"), mem_fun(*this, &Editor::play_from_edit_point)));
2188 play_items.push_back (MenuElem (_("Play from start"), mem_fun(*this, &Editor::play_from_start)));
2189 edit_items.push_back (MenuElem (_("Play"), *play_menu));
2191 /* Selection */
2193 Menu *select_menu = manage (new Menu);
2194 MenuList& select_items = select_menu->items();
2195 select_menu->set_name ("ArdourContextMenu");
2197 select_items.push_back (MenuElem (_("Select All in track"), bind (mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2198 select_items.push_back (MenuElem (_("Select All"), bind (mem_fun(*this, &Editor::select_all), Selection::Set)));
2199 select_items.push_back (MenuElem (_("Invert selection in track"), mem_fun(*this, &Editor::invert_selection_in_track)));
2200 select_items.push_back (MenuElem (_("Invert selection"), mem_fun(*this, &Editor::invert_selection)));
2201 select_items.push_back (SeparatorElem());
2202 select_items.push_back (MenuElem (_("Select all after edit point"), bind (mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
2203 select_items.push_back (MenuElem (_("Select all before edit point"), bind (mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
2204 select_items.push_back (MenuElem (_("Select all after playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2205 select_items.push_back (MenuElem (_("Select all before playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2207 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2209 /* Cut-n-Paste */
2211 Menu *cutnpaste_menu = manage (new Menu);
2212 MenuList& cutnpaste_items = cutnpaste_menu->items();
2213 cutnpaste_menu->set_name ("ArdourContextMenu");
2215 cutnpaste_items.push_back (MenuElem (_("Cut"), mem_fun(*this, &Editor::cut)));
2216 cutnpaste_items.push_back (MenuElem (_("Copy"), mem_fun(*this, &Editor::copy)));
2217 cutnpaste_items.push_back (MenuElem (_("Paste"), bind (mem_fun(*this, &Editor::paste), 1.0f)));
2219 Menu *nudge_menu = manage (new Menu());
2220 MenuList& nudge_items = nudge_menu->items();
2221 nudge_menu->set_name ("ArdourContextMenu");
2223 edit_items.push_back (SeparatorElem());
2224 nudge_items.push_back (MenuElem (_("Nudge entire track fwd"), (bind (mem_fun(*this, &Editor::nudge_track), false, true))));
2225 nudge_items.push_back (MenuElem (_("Nudge track after edit point fwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, true))));
2226 nudge_items.push_back (MenuElem (_("Nudge entire track bwd"), (bind (mem_fun(*this, &Editor::nudge_track), false, false))));
2227 nudge_items.push_back (MenuElem (_("Nudge track after edit point bwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, false))));
2229 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2232 void
2233 Editor::set_snap_to (SnapType st)
2235 unsigned int snap_ind = (unsigned int)st;
2236 snap_type = st;
2238 if (snap_ind > snap_type_strings.size() - 1) {
2239 snap_ind = 0;
2240 snap_type = (SnapType)snap_ind;
2243 string str = snap_type_strings[snap_ind];
2245 if (str != snap_type_selector.get_active_text()) {
2246 snap_type_selector.set_active_text (str);
2249 instant_save ();
2251 switch (snap_type) {
2252 case SnapToAThirtysecondBeat:
2253 case SnapToASixteenthBeat:
2254 case SnapToAEighthBeat:
2255 case SnapToAQuarterBeat:
2256 case SnapToAThirdBeat:
2257 update_tempo_based_rulers ();
2258 break;
2260 case SnapToRegionStart:
2261 case SnapToRegionEnd:
2262 case SnapToRegionSync:
2263 case SnapToRegionBoundary:
2264 build_region_boundary_cache ();
2265 break;
2267 default:
2268 /* relax */
2269 break;
2273 void
2274 Editor::set_snap_mode (SnapMode mode)
2276 snap_mode = mode;
2277 string str = snap_mode_strings[(int)mode];
2279 if (str != snap_mode_selector.get_active_text ()) {
2280 snap_mode_selector.set_active_text (str);
2283 instant_save ();
2285 void
2286 Editor::set_edit_point_preference (EditPoint ep, bool force)
2288 bool changed = (_edit_point != ep);
2290 _edit_point = ep;
2291 string str = edit_point_strings[(int)ep];
2293 if (str != edit_point_selector.get_active_text ()) {
2294 edit_point_selector.set_active_text (str);
2297 set_canvas_cursor ();
2299 if (!force && !changed) {
2300 return;
2303 switch (zoom_focus) {
2304 case ZoomFocusMouse:
2305 case ZoomFocusPlayhead:
2306 case ZoomFocusEdit:
2307 switch (_edit_point) {
2308 case EditAtMouse:
2309 set_zoom_focus (ZoomFocusMouse);
2310 break;
2311 case EditAtPlayhead:
2312 set_zoom_focus (ZoomFocusPlayhead);
2313 break;
2314 case EditAtSelectedMarker:
2315 set_zoom_focus (ZoomFocusEdit);
2316 break;
2318 break;
2319 default:
2320 break;
2323 const char* action=NULL;
2325 switch (_edit_point) {
2326 case EditAtPlayhead:
2327 action = "edit-at-playhead";
2328 break;
2329 case EditAtSelectedMarker:
2330 action = "edit-at-marker";
2331 break;
2332 case EditAtMouse:
2333 action = "edit-at-mouse";
2334 break;
2337 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2338 if (act) {
2339 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2342 nframes64_t foo;
2343 bool in_track_canvas;
2345 if (!mouse_frame (foo, in_track_canvas)) {
2346 in_track_canvas = false;
2349 reset_canvas_action_sensitivity (in_track_canvas);
2351 instant_save ();
2355 Editor::set_state (const XMLNode& node)
2357 const XMLProperty* prop;
2358 XMLNode* geometry;
2359 int x, y, xoff, yoff;
2360 Gdk::Geometry g;
2362 if ((prop = node.property ("id")) != 0) {
2363 _id = prop->value ();
2366 g.base_width = default_width;
2367 g.base_height = default_height;
2368 x = 1;
2369 y = 1;
2370 xoff = 0;
2371 yoff = 21;
2373 if ((geometry = find_named_node (node, "geometry")) != 0) {
2375 XMLProperty* prop;
2377 if ((prop = geometry->property("x_size")) == 0) {
2378 prop = geometry->property ("x-size");
2380 if (prop) {
2381 g.base_width = atoi(prop->value());
2383 if ((prop = geometry->property("y_size")) == 0) {
2384 prop = geometry->property ("y-size");
2386 if (prop) {
2387 g.base_height = atoi(prop->value());
2390 if ((prop = geometry->property ("x_pos")) == 0) {
2391 prop = geometry->property ("x-pos");
2393 if (prop) {
2394 x = atoi (prop->value());
2397 if ((prop = geometry->property ("y_pos")) == 0) {
2398 prop = geometry->property ("y-pos");
2400 if (prop) {
2401 y = atoi (prop->value());
2404 if ((prop = geometry->property ("x_off")) == 0) {
2405 prop = geometry->property ("x-off");
2407 if (prop) {
2408 xoff = atoi (prop->value());
2410 if ((prop = geometry->property ("y_off")) == 0) {
2411 prop = geometry->property ("y-off");
2413 if (prop) {
2414 yoff = atoi (prop->value());
2419 set_default_size (g.base_width, g.base_height);
2420 move (x, y);
2422 if (session && (prop = node.property ("playhead"))) {
2423 nframes64_t pos;
2424 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2425 playhead_cursor->set_position (pos);
2426 } else {
2427 playhead_cursor->set_position (0);
2429 /* reset_x_origin() doesn't work right here, since the old
2430 position may be zero already, and it does nothing in such
2431 circumstances.
2434 leftmost_frame = 0;
2435 horizontal_adjustment.set_value (0);
2438 if ((prop = node.property ("mixer-width"))) {
2439 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2442 if ((prop = node.property ("zoom-focus"))) {
2443 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2446 if ((prop = node.property ("zoom"))) {
2447 reset_zoom (PBD::atof (prop->value()));
2450 if ((prop = node.property ("snap-to"))) {
2451 set_snap_to ((SnapType) atoi (prop->value()));
2454 if ((prop = node.property ("snap-mode"))) {
2455 set_snap_mode ((SnapMode) atoi (prop->value()));
2458 if ((prop = node.property ("edit-point"))) {
2459 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2462 if ((prop = node.property ("mouse-mode"))) {
2463 MouseMode m = str2mousemode(prop->value());
2464 mouse_mode = MouseMode ((int) m + 1); /* lie, force mode switch */
2465 set_mouse_mode (m, true);
2466 } else {
2467 mouse_mode = MouseGain; /* lie, to force the mode switch */
2468 set_mouse_mode (MouseObject, true);
2471 if ((prop = node.property ("show-waveforms"))) {
2472 bool yn = (prop->value() == "yes");
2473 _show_waveforms = !yn;
2474 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-waveform-visible"));
2475 if (act) {
2476 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2477 /* do it twice to force the change */
2478 tact->set_active (!yn);
2479 tact->set_active (yn);
2483 if ((prop = node.property ("show-waveforms-rectified"))) {
2484 bool yn = (prop->value() == "yes");
2485 _show_waveforms_rectified = !yn;
2486 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-waveform-rectified"));
2487 if (act) {
2488 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2489 /* do it twice to force the change */
2490 tact->set_active (!yn);
2491 tact->set_active (yn);
2495 if ((prop = node.property ("show-waveforms-recording"))) {
2496 bool yn = (prop->value() == "yes");
2497 _show_waveforms_recording = !yn;
2498 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleWaveformsWhileRecording"));
2499 if (act) {
2500 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2501 /* do it twice to force the change */
2502 tact->set_active (!yn);
2503 tact->set_active (yn);
2507 if ((prop = node.property ("show-measures"))) {
2508 bool yn = (prop->value() == "yes");
2509 _show_measures = !yn;
2510 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2511 if (act) {
2512 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2513 /* do it twice to force the change */
2514 tact->set_active (!yn);
2515 tact->set_active (yn);
2519 if ((prop = node.property ("follow-playhead"))) {
2520 bool yn = (prop->value() == "yes");
2521 set_follow_playhead (yn);
2522 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2523 if (act) {
2524 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2525 if (tact->get_active() != yn) {
2526 tact->set_active (yn);
2531 if ((prop = node.property ("stationary-playhead"))) {
2532 bool yn = (prop->value() == "yes");
2533 set_stationary_playhead (yn);
2534 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2535 if (act) {
2536 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2537 if (tact->get_active() != yn) {
2538 tact->set_active (yn);
2543 if ((prop = node.property ("region-list-sort-type"))) {
2544 region_list_sort_type = (Editing::RegionListSortType) -1; // force change
2545 reset_region_list_sort_type(str2regionlistsorttype(prop->value()));
2548 if ((prop = node.property ("xfades-visible"))) {
2549 bool yn = (prop->value() == "yes");
2550 _xfade_visibility = !yn;
2551 // set_xfade_visibility (yn);
2554 if ((prop = node.property ("show-editor-mixer"))) {
2556 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2557 if (act) {
2559 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2560 bool yn = (prop->value() == X_("yes"));
2562 /* do it twice to force the change */
2564 tact->set_active (!yn);
2565 tact->set_active (yn);
2570 return 0;
2573 XMLNode&
2574 Editor::get_state ()
2576 XMLNode* node = new XMLNode ("Editor");
2577 char buf[32];
2579 _id.print (buf, sizeof (buf));
2580 node->add_property ("id", buf);
2582 if (is_realized()) {
2583 Glib::RefPtr<Gdk::Window> win = get_window();
2585 int x, y, xoff, yoff, width, height;
2586 win->get_root_origin(x, y);
2587 win->get_position(xoff, yoff);
2588 win->get_size(width, height);
2590 XMLNode* geometry = new XMLNode ("geometry");
2592 snprintf(buf, sizeof(buf), "%d", width);
2593 geometry->add_property("x_size", string(buf));
2594 snprintf(buf, sizeof(buf), "%d", height);
2595 geometry->add_property("y_size", string(buf));
2596 snprintf(buf, sizeof(buf), "%d", x);
2597 geometry->add_property("x_pos", string(buf));
2598 snprintf(buf, sizeof(buf), "%d", y);
2599 geometry->add_property("y_pos", string(buf));
2600 snprintf(buf, sizeof(buf), "%d", xoff);
2601 geometry->add_property("x_off", string(buf));
2602 snprintf(buf, sizeof(buf), "%d", yoff);
2603 geometry->add_property("y_off", string(buf));
2604 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2605 geometry->add_property("edit_pane_pos", string(buf));
2607 node->add_child_nocopy (*geometry);
2610 maybe_add_mixer_strip_width (*node);
2612 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2613 node->add_property ("zoom-focus", buf);
2614 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2615 node->add_property ("zoom", buf);
2616 snprintf (buf, sizeof(buf), "%d", (int) snap_type);
2617 node->add_property ("snap-to", buf);
2618 snprintf (buf, sizeof(buf), "%d", (int) snap_mode);
2619 node->add_property ("snap-mode", buf);
2621 node->add_property ("edit-point", enum_2_string (_edit_point));
2623 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2624 node->add_property ("playhead", buf);
2626 node->add_property ("show-waveforms", _show_waveforms ? "yes" : "no");
2627 node->add_property ("show-waveforms-rectified", _show_waveforms_rectified ? "yes" : "no");
2628 node->add_property ("show-waveforms-recording", _show_waveforms_recording ? "yes" : "no");
2629 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2630 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2631 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2632 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2633 node->add_property ("region-list-sort-type", enum2str(region_list_sort_type));
2634 node->add_property ("mouse-mode", enum2str(mouse_mode));
2636 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2637 if (act) {
2638 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2639 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2642 return *node;
2647 TimeAxisView *
2648 Editor::trackview_by_y_position (double y)
2650 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2652 TimeAxisView *tv;
2654 if ((tv = (*iter)->covers_y_position (y)) != 0) {
2655 return tv;
2659 return 0;
2662 void
2663 Editor::snap_to (nframes64_t& start, int32_t direction, bool for_mark)
2665 if (!session || snap_mode == SnapOff) {
2666 return;
2669 snap_to_internal (start, direction, for_mark);
2672 void
2673 Editor::snap_to_internal (nframes64_t& start, int32_t direction, bool for_mark)
2675 Location* before = 0;
2676 Location* after = 0;
2678 const nframes64_t one_second = session->frame_rate();
2679 const nframes64_t one_minute = session->frame_rate() * 60;
2680 const nframes64_t one_smpte_second = (nframes64_t)(rint(session->smpte_frames_per_second()) * session->frames_per_smpte_frame());
2681 nframes64_t one_smpte_minute = (nframes64_t)(rint(session->smpte_frames_per_second()) * session->frames_per_smpte_frame() * 60);
2682 nframes64_t presnap = start;
2684 switch (snap_type) {
2685 case SnapToCDFrame:
2686 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2687 start = (nframes64_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2688 } else {
2689 start = (nframes64_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2691 break;
2693 case SnapToSMPTEFrame:
2694 if (((direction == 0) && (fmod((double)start, (double)session->frames_per_smpte_frame()) > (session->frames_per_smpte_frame() / 2))) || (direction > 0)) {
2695 start = (nframes64_t) (ceil ((double) start / session->frames_per_smpte_frame()) * session->frames_per_smpte_frame());
2696 } else {
2697 start = (nframes64_t) (floor ((double) start / session->frames_per_smpte_frame()) * session->frames_per_smpte_frame());
2699 break;
2701 case SnapToSMPTESeconds:
2702 if (session->smpte_offset_negative())
2704 start += session->smpte_offset ();
2705 } else {
2706 start -= session->smpte_offset ();
2708 if (((direction == 0) && (start % one_smpte_second > one_smpte_second / 2)) || direction > 0) {
2709 start = (nframes64_t) ceil ((double) start / one_smpte_second) * one_smpte_second;
2710 } else {
2711 start = (nframes64_t) floor ((double) start / one_smpte_second) * one_smpte_second;
2714 if (session->smpte_offset_negative())
2716 start -= session->smpte_offset ();
2717 } else {
2718 start += session->smpte_offset ();
2720 break;
2722 case SnapToSMPTEMinutes:
2723 if (session->smpte_offset_negative())
2725 start += session->smpte_offset ();
2726 } else {
2727 start -= session->smpte_offset ();
2729 if (((direction == 0) && (start % one_smpte_minute > one_smpte_minute / 2)) || direction > 0) {
2730 start = (nframes64_t) ceil ((double) start / one_smpte_minute) * one_smpte_minute;
2731 } else {
2732 start = (nframes64_t) floor ((double) start / one_smpte_minute) * one_smpte_minute;
2734 if (session->smpte_offset_negative())
2736 start -= session->smpte_offset ();
2737 } else {
2738 start += session->smpte_offset ();
2740 break;
2742 case SnapToSeconds:
2743 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2744 start = (nframes64_t) ceil ((double) start / one_second) * one_second;
2745 } else {
2746 start = (nframes64_t) floor ((double) start / one_second) * one_second;
2748 break;
2750 case SnapToMinutes:
2751 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2752 start = (nframes64_t) ceil ((double) start / one_minute) * one_minute;
2753 } else {
2754 start = (nframes64_t) floor ((double) start / one_minute) * one_minute;
2756 break;
2758 case SnapToBar:
2759 start = session->tempo_map().round_to_bar (start, direction);
2760 break;
2762 case SnapToBeat:
2763 start = session->tempo_map().round_to_beat (start, direction);
2764 break;
2766 case SnapToAThirtysecondBeat:
2767 start = session->tempo_map().round_to_beat_subdivision (start, 32);
2768 break;
2770 case SnapToASixteenthBeat:
2771 start = session->tempo_map().round_to_beat_subdivision (start, 16);
2772 break;
2774 case SnapToAEighthBeat:
2775 start = session->tempo_map().round_to_beat_subdivision (start, 8);
2776 break;
2778 case SnapToAQuarterBeat:
2779 start = session->tempo_map().round_to_beat_subdivision (start, 4);
2780 break;
2782 case SnapToAThirdBeat:
2783 start = session->tempo_map().round_to_beat_subdivision (start, 3);
2784 break;
2786 case SnapToMark:
2787 if (for_mark) {
2788 return;
2791 before = session->locations()->first_location_before (start);
2792 after = session->locations()->first_location_after (start);
2794 if (direction < 0) {
2795 if (before) {
2796 start = before->start();
2797 } else {
2798 start = 0;
2800 } else if (direction > 0) {
2801 if (after) {
2802 start = after->start();
2803 } else {
2804 start = session->current_end_frame();
2806 } else {
2807 if (before) {
2808 if (after) {
2809 /* find nearest of the two */
2810 if ((start - before->start()) < (after->start() - start)) {
2811 start = before->start();
2812 } else {
2813 start = after->start();
2815 } else {
2816 start = before->start();
2818 } else if (after) {
2819 start = after->start();
2820 } else {
2821 /* relax */
2824 break;
2826 case SnapToRegionStart:
2827 case SnapToRegionEnd:
2828 case SnapToRegionSync:
2829 case SnapToRegionBoundary:
2830 if (!region_boundary_cache.empty()) {
2831 vector<nframes64_t>::iterator i;
2833 if (direction > 0) {
2834 i = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2835 } else {
2836 i = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2839 if (i != region_boundary_cache.end()) {
2841 /* lower bound doesn't quite to the right thing for our purposes */
2843 if (direction < 0 && i != region_boundary_cache.begin()) {
2844 --i;
2847 start = *i;
2849 } else {
2850 start = region_boundary_cache.back();
2853 break;
2856 switch (snap_mode) {
2857 case SnapNormal:
2858 return;
2860 case SnapMagnetic:
2862 if (presnap > start) {
2863 if (presnap > (start + unit_to_frame(snap_threshold))) {
2864 start = presnap;
2867 } else if (presnap < start) {
2868 if (presnap < (start - unit_to_frame(snap_threshold))) {
2869 start = presnap;
2873 default:
2874 /* handled at entry */
2875 return;
2880 void
2881 Editor::setup_toolbar ()
2883 string pixmap_path;
2885 /* Mode Buttons (tool selection) */
2887 vector<ToggleButton *> mouse_mode_buttons;
2889 mouse_move_button.add (*(manage (new Image (::get_icon("tool_object")))));
2890 mouse_move_button.set_relief(Gtk::RELIEF_NONE);
2891 mouse_mode_buttons.push_back (&mouse_move_button);
2893 if (!Profile->get_sae()) {
2894 mouse_select_button.add (*(manage (new Image (get_xpm("tool_range.xpm")))));
2895 mouse_select_button.set_relief(Gtk::RELIEF_NONE);
2896 mouse_mode_buttons.push_back (&mouse_select_button);
2898 mouse_gain_button.add (*(manage (new Image (::get_icon("tool_gain")))));
2899 mouse_gain_button.set_relief(Gtk::RELIEF_NONE);
2900 mouse_mode_buttons.push_back (&mouse_gain_button);
2903 mouse_zoom_button.add (*(manage (new Image (::get_icon("tool_zoom")))));
2904 mouse_zoom_button.set_relief(Gtk::RELIEF_NONE);
2905 mouse_mode_buttons.push_back (&mouse_zoom_button);
2906 mouse_timefx_button.add (*(manage (new Image (::get_icon("tool_stretch")))));
2907 mouse_timefx_button.set_relief(Gtk::RELIEF_NONE);
2908 mouse_mode_buttons.push_back (&mouse_timefx_button);
2909 mouse_audition_button.add (*(manage (new Image (::get_icon("tool_audition")))));
2910 mouse_audition_button.set_relief(Gtk::RELIEF_NONE);
2911 mouse_mode_buttons.push_back (&mouse_audition_button);
2913 mouse_mode_button_set = new GroupedButtons (mouse_mode_buttons);
2915 HBox* mode_box = manage(new HBox);
2916 mode_box->set_border_width (2);
2917 mode_box->set_spacing(4);
2918 mouse_mode_button_box.set_spacing(1);
2919 mouse_mode_button_box.pack_start(mouse_move_button, true, true);
2920 if (!Profile->get_sae()) {
2921 mouse_mode_button_box.pack_start(mouse_select_button, true, true);
2923 mouse_mode_button_box.pack_start(mouse_zoom_button, true, true);
2924 if (!Profile->get_sae()) {
2925 mouse_mode_button_box.pack_start(mouse_gain_button, true, true);
2927 mouse_mode_button_box.pack_start(mouse_timefx_button, true, true);
2928 mouse_mode_button_box.pack_start(mouse_audition_button, true, true);
2929 mouse_mode_button_box.set_homogeneous(true);
2931 vector<string> edit_mode_strings;
2932 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2933 if (!Profile->get_sae()) {
2934 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2936 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2938 edit_mode_selector.set_name ("EditModeSelector");
2939 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2940 edit_mode_selector.signal_changed().connect (mem_fun(*this, &Editor::edit_mode_selection_done));
2942 mode_box->pack_start(edit_mode_selector);
2943 mode_box->pack_start(mouse_mode_button_box);
2945 mouse_mode_tearoff = manage (new TearOff (*mode_box));
2946 mouse_mode_tearoff->set_name ("MouseModeBase");
2947 mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (bind (sigc::ptr_fun (relay_key_press), &mouse_mode_tearoff->tearoff_window()));
2949 if (Profile->get_sae()) {
2950 mouse_mode_tearoff->set_can_be_torn_off (false);
2953 mouse_mode_tearoff->Detach.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2954 &mouse_mode_tearoff->tearoff_window()));
2955 mouse_mode_tearoff->Attach.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2956 &mouse_mode_tearoff->tearoff_window(), 1));
2957 mouse_mode_tearoff->Hidden.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2958 &mouse_mode_tearoff->tearoff_window()));
2959 mouse_mode_tearoff->Visible.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2960 &mouse_mode_tearoff->tearoff_window(), 1));
2962 mouse_move_button.set_name ("MouseModeButton");
2963 mouse_select_button.set_name ("MouseModeButton");
2964 mouse_gain_button.set_name ("MouseModeButton");
2965 mouse_zoom_button.set_name ("MouseModeButton");
2966 mouse_timefx_button.set_name ("MouseModeButton");
2967 mouse_audition_button.set_name ("MouseModeButton");
2969 ARDOUR_UI::instance()->tooltips().set_tip (mouse_move_button, _("Select/Move Objects"));
2970 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Select/Move Ranges"));
2971 ARDOUR_UI::instance()->tooltips().set_tip (mouse_gain_button, _("Draw Gain Automation"));
2972 ARDOUR_UI::instance()->tooltips().set_tip (mouse_zoom_button, _("Select Zoom Range"));
2973 ARDOUR_UI::instance()->tooltips().set_tip (mouse_timefx_button, _("Stretch/Shrink Regions"));
2974 ARDOUR_UI::instance()->tooltips().set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2976 mouse_move_button.unset_flags (CAN_FOCUS);
2977 mouse_select_button.unset_flags (CAN_FOCUS);
2978 mouse_gain_button.unset_flags (CAN_FOCUS);
2979 mouse_zoom_button.unset_flags (CAN_FOCUS);
2980 mouse_timefx_button.unset_flags (CAN_FOCUS);
2981 mouse_audition_button.unset_flags (CAN_FOCUS);
2983 mouse_select_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseRange));
2984 mouse_select_button.signal_button_release_event().connect (mem_fun(*this, &Editor::mouse_select_button_release));
2986 mouse_move_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseObject));
2987 mouse_gain_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseGain));
2988 mouse_zoom_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseZoom));
2989 mouse_timefx_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseTimeFX));
2990 mouse_audition_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseAudition));
2992 // mouse_move_button.set_active (true);
2995 /* Zoom */
2997 zoom_box.set_spacing (1);
2998 zoom_box.set_border_width (0);
3000 zoom_in_button.set_name ("EditorTimeButton");
3001 zoom_in_button.set_size_request(-1,16);
3002 zoom_in_button.add (*(manage (new Image (::get_icon("zoom_in")))));
3003 zoom_in_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::temporal_zoom_step), false));
3004 ARDOUR_UI::instance()->tooltips().set_tip (zoom_in_button, _("Zoom In"));
3006 zoom_out_button.set_name ("EditorTimeButton");
3007 zoom_out_button.set_size_request(-1,16);
3008 zoom_out_button.add (*(manage (new Image (::get_icon("zoom_out")))));
3009 zoom_out_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::temporal_zoom_step), true));
3010 ARDOUR_UI::instance()->tooltips().set_tip (zoom_out_button, _("Zoom Out"));
3012 zoom_out_full_button.set_name ("EditorTimeButton");
3013 zoom_out_full_button.set_size_request(-1,16);
3014 zoom_out_full_button.add (*(manage (new Image (::get_icon("zoom_full")))));
3015 zoom_out_full_button.signal_clicked().connect (mem_fun(*this, &Editor::temporal_zoom_session));
3016 ARDOUR_UI::instance()->tooltips().set_tip (zoom_out_full_button, _("Zoom to Session"));
3018 zoom_focus_selector.set_name ("ZoomFocusSelector");
3019 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
3020 zoom_focus_selector.signal_changed().connect (mem_fun(*this, &Editor::zoom_focus_selection_done));
3021 ARDOUR_UI::instance()->tooltips().set_tip (zoom_focus_selector, _("Zoom focus"));
3023 zoom_box.pack_start (zoom_focus_selector, true, true);
3024 zoom_box.pack_start (zoom_out_button, false, false);
3025 zoom_box.pack_start (zoom_in_button, false, false);
3026 zoom_box.pack_start (zoom_out_full_button, false, false);
3028 snap_box.set_spacing (1);
3029 snap_box.set_border_width (2);
3031 snap_type_selector.set_name ("SnapTypeSelector");
3032 set_popdown_strings (snap_type_selector, snap_type_strings, true);
3033 snap_type_selector.signal_changed().connect (mem_fun(*this, &Editor::snap_type_selection_done));
3034 ARDOUR_UI::instance()->tooltips().set_tip (snap_type_selector, _("Snap/Grid Units"));
3036 snap_mode_selector.set_name ("SnapModeSelector");
3037 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
3038 snap_mode_selector.signal_changed().connect (mem_fun(*this, &Editor::snap_mode_selection_done));
3039 ARDOUR_UI::instance()->tooltips().set_tip (snap_mode_selector, _("Snap/Grid Mode"));
3041 edit_point_selector.set_name ("EditPointSelector");
3042 set_popdown_strings (edit_point_selector, edit_point_strings, true);
3043 edit_point_selector.signal_changed().connect (mem_fun(*this, &Editor::edit_point_selection_done));
3044 ARDOUR_UI::instance()->tooltips().set_tip (edit_point_selector, _("Edit point"));
3046 snap_box.pack_start (edit_point_clock, false, false);
3047 snap_box.pack_start (snap_mode_selector, false, false);
3048 snap_box.pack_start (snap_type_selector, false, false);
3049 snap_box.pack_start (edit_point_selector, false, false);
3051 /* Nudge */
3053 HBox *nudge_box = manage (new HBox);
3054 nudge_box->set_spacing(1);
3055 nudge_box->set_border_width (2);
3057 nudge_forward_button.signal_button_release_event().connect (mem_fun(*this, &Editor::nudge_forward_release), false);
3058 nudge_backward_button.signal_button_release_event().connect (mem_fun(*this, &Editor::nudge_backward_release), false);
3060 nudge_box->pack_start (nudge_backward_button, false, false);
3061 nudge_box->pack_start (nudge_forward_button, false, false);
3062 nudge_box->pack_start (nudge_clock, false, false);
3065 /* Pack everything in... */
3067 HBox* hbox = new HBox;
3068 hbox->set_spacing(10);
3070 tools_tearoff = new TearOff (*hbox);
3071 tools_tearoff->set_name ("MouseModeBase");
3072 tools_tearoff->tearoff_window().signal_key_press_event().connect (bind (sigc::ptr_fun (relay_key_press), &tools_tearoff->tearoff_window()));
3074 if (Profile->get_sae()) {
3075 tools_tearoff->set_can_be_torn_off (false);
3078 tools_tearoff->Detach.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
3079 &tools_tearoff->tearoff_window()));
3080 tools_tearoff->Attach.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
3081 &tools_tearoff->tearoff_window(), 0));
3082 tools_tearoff->Hidden.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
3083 &tools_tearoff->tearoff_window()));
3084 tools_tearoff->Visible.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
3085 &tools_tearoff->tearoff_window(), 0));
3087 toolbar_hbox.set_spacing (10);
3088 toolbar_hbox.set_border_width (1);
3090 toolbar_hbox.pack_start (*mouse_mode_tearoff, false, false);
3091 toolbar_hbox.pack_start (*tools_tearoff, false, false);
3094 hbox->pack_start (snap_box, false, false);
3095 // hbox->pack_start (zoom_box, false, false);
3096 hbox->pack_start (*nudge_box, false, false);
3098 hbox->show_all ();
3100 toolbar_base.set_name ("ToolBarBase");
3101 toolbar_base.add (toolbar_hbox);
3103 toolbar_frame.set_shadow_type (SHADOW_OUT);
3104 toolbar_frame.set_name ("BaseFrame");
3105 toolbar_frame.add (toolbar_base);
3109 Editor::convert_drop_to_paths (vector<ustring>& paths,
3110 const RefPtr<Gdk::DragContext>& context,
3111 gint x,
3112 gint y,
3113 const SelectionData& data,
3114 guint info,
3115 guint time)
3118 if (session == 0) {
3119 return -1;
3122 vector<ustring> uris = data.get_uris();
3124 if (uris.empty()) {
3126 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3127 are actually URI lists. So do it by hand.
3130 if (data.get_target() != "text/plain") {
3131 return -1;
3134 /* Parse the "uri-list" format that Nautilus provides,
3135 where each pathname is delimited by \r\n.
3137 THERE MAY BE NO NULL TERMINATING CHAR!!!
3140 ustring txt = data.get_text();
3141 const char* p;
3142 const char* q;
3144 p = (const char *) malloc (txt.length() + 1);
3145 txt.copy ((char *) p, txt.length(), 0);
3146 ((char*)p)[txt.length()] = '\0';
3148 while (p)
3150 if (*p != '#')
3152 while (g_ascii_isspace (*p))
3153 p++;
3155 q = p;
3156 while (*q && (*q != '\n') && (*q != '\r')) {
3157 q++;
3160 if (q > p)
3162 q--;
3163 while (q > p && g_ascii_isspace (*q))
3164 q--;
3166 if (q > p)
3168 uris.push_back (ustring (p, q - p + 1));
3172 p = strchr (p, '\n');
3173 if (p)
3174 p++;
3177 free ((void*)p);
3179 if (uris.empty()) {
3180 return -1;
3184 for (vector<ustring>::iterator i = uris.begin(); i != uris.end(); ++i) {
3186 if ((*i).substr (0,7) == "file://") {
3189 ustring p = *i;
3190 PBD::url_decode (p);
3192 // scan forward past three slashes
3194 ustring::size_type slashcnt = 0;
3195 ustring::size_type n = 0;
3196 ustring::iterator x = p.begin();
3198 while (slashcnt < 3 && x != p.end()) {
3199 if ((*x) == '/') {
3200 slashcnt++;
3201 } else if (slashcnt == 3) {
3202 break;
3204 ++n;
3205 ++x;
3208 if (slashcnt != 3 || x == p.end()) {
3209 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3210 continue;
3213 paths.push_back (p.substr (n - 1));
3217 return 0;
3220 void
3221 Editor::new_tempo_section ()
3226 void
3227 Editor::map_transport_state ()
3229 ENSURE_GUI_THREAD (mem_fun(*this, &Editor::map_transport_state));
3231 if (session->transport_stopped()) {
3232 have_pending_keyboard_selection = false;
3235 update_loop_range_view (true);
3238 /* UNDO/REDO */
3240 Editor::State::State ()
3242 selection = new Selection;
3245 Editor::State::~State ()
3247 delete selection;
3250 UndoAction
3251 Editor::get_memento () const
3253 State *state = new State;
3255 store_state (*state);
3256 return bind (mem_fun (*(const_cast<Editor*>(this)), &Editor::restore_state), state);
3259 void
3260 Editor::store_state (State& state) const
3262 *state.selection = *selection;
3265 void
3266 Editor::restore_state (State *state)
3268 if (*selection == *state->selection) {
3269 return;
3272 *selection = *state->selection;
3273 time_selection_changed ();
3274 region_selection_changed ();
3276 /* XXX other selection change handlers? */
3279 void
3280 Editor::begin_reversible_command (string name)
3282 if (session) {
3283 // before = &get_state();
3284 session->begin_reversible_command (name);
3288 void
3289 Editor::commit_reversible_command ()
3291 if (session) {
3292 // session->commit_reversible_command (new MementoCommand<Editor>(*this, before, &get_state()));
3293 session->commit_reversible_command ();
3297 void
3298 Editor::set_edit_group_solo (Route& route, bool yn)
3300 RouteGroup *edit_group;
3302 if ((edit_group = route.edit_group()) != 0) {
3303 edit_group->apply (&Route::set_solo, yn, this);
3304 } else {
3305 route.set_solo (yn, this);
3309 void
3310 Editor::set_edit_group_mute (Route& route, bool yn)
3312 RouteGroup *edit_group = 0;
3314 if ((edit_group == route.edit_group()) != 0) {
3315 edit_group->apply (&Route::set_mute, yn, this);
3316 } else {
3317 route.set_mute (yn, this);
3321 void
3322 Editor::history_changed ()
3324 string label;
3326 if (undo_action && session) {
3327 if (session->undo_depth() == 0) {
3328 label = _("Undo");
3329 } else {
3330 label = string_compose(_("Undo (%1)"), session->next_undo());
3332 undo_action->property_label() = label;
3335 if (redo_action && session) {
3336 if (session->redo_depth() == 0) {
3337 label = _("Redo");
3338 } else {
3339 label = string_compose(_("Redo (%1)"), session->next_redo());
3341 redo_action->property_label() = label;
3345 void
3346 Editor::duplicate_dialog (bool with_dialog)
3348 float times = 1.0f;
3350 if (mouse_mode == MouseRange) {
3351 if (selection->time.length() == 0) {
3352 return;
3356 RegionSelection rs;
3357 get_regions_for_action (rs);
3359 if (mouse_mode != MouseRange) {
3361 if (rs.empty()) {
3362 return;
3366 if (with_dialog) {
3368 ArdourDialog win ("Duplication Dialog");
3369 Label label (_("Number of Duplications:"));
3370 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3371 SpinButton spinner (adjustment, 0.0, 1);
3372 HBox hbox;
3374 win.get_vbox()->set_spacing (12);
3375 win.get_vbox()->pack_start (hbox);
3376 hbox.set_border_width (6);
3377 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3379 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3380 place, visually. so do this by hand.
3383 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3384 spinner.signal_activate().connect (sigc::bind (mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3385 spinner.grab_focus();
3387 hbox.show ();
3388 label.show ();
3389 spinner.show ();
3391 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3392 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3393 win.set_default_response (RESPONSE_ACCEPT);
3395 win.set_position (WIN_POS_MOUSE);
3397 spinner.grab_focus ();
3399 switch (win.run ()) {
3400 case RESPONSE_ACCEPT:
3401 break;
3402 default:
3403 return;
3406 times = adjustment.get_value();
3409 if (mouse_mode == MouseRange) {
3410 duplicate_selection (times);
3411 } else {
3412 duplicate_some_regions (rs, times);
3416 void
3417 Editor::show_verbose_canvas_cursor ()
3419 verbose_canvas_cursor->raise_to_top();
3420 verbose_canvas_cursor->show();
3421 verbose_cursor_visible = true;
3424 void
3425 Editor::hide_verbose_canvas_cursor ()
3427 verbose_canvas_cursor->hide();
3428 verbose_cursor_visible = false;
3431 double
3432 Editor::clamp_verbose_cursor_x (double x)
3434 if (x < 0) {
3435 x = 0;
3436 } else {
3437 x = min (canvas_width - 200.0, x);
3439 return x;
3442 double
3443 Editor::clamp_verbose_cursor_y (double y)
3445 if (y < canvas_timebars_vsize) {
3446 y = canvas_timebars_vsize;
3447 } else {
3448 y = min (canvas_height - 50, y);
3450 return y;
3453 void
3454 Editor::set_verbose_canvas_cursor (const string & txt, double x, double y)
3456 verbose_canvas_cursor->property_text() = txt.c_str();
3457 /* don't get too close to the edge */
3458 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (x);
3459 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (y);
3462 void
3463 Editor::set_verbose_canvas_cursor_text (const string & txt)
3465 verbose_canvas_cursor->property_text() = txt.c_str();
3468 void
3469 Editor::set_edit_mode (EditMode m)
3471 Config->set_edit_mode (m);
3474 void
3475 Editor::cycle_edit_mode ()
3477 switch (Config->get_edit_mode()) {
3478 case Slide:
3479 if (Profile->get_sae()) {
3480 Config->set_edit_mode (Lock);
3481 } else {
3482 Config->set_edit_mode (Splice);
3484 break;
3485 case Splice:
3486 Config->set_edit_mode (Lock);
3487 break;
3488 case Lock:
3489 Config->set_edit_mode (Slide);
3490 break;
3494 void
3495 Editor::edit_mode_selection_done ()
3497 if (session == 0) {
3498 return;
3501 string choice = edit_mode_selector.get_active_text();
3502 EditMode mode = Slide;
3504 if (choice == _("Splice Edit")) {
3505 mode = Splice;
3506 } else if (choice == _("Slide Edit")) {
3507 mode = Slide;
3508 } else if (choice == _("Lock Edit")) {
3509 mode = Lock;
3512 Config->set_edit_mode (mode);
3515 void
3516 Editor::snap_type_selection_done ()
3518 string choice = snap_type_selector.get_active_text();
3519 SnapType snaptype = SnapToBeat;
3521 if (choice == _("Beats/3")) {
3522 snaptype = SnapToAThirdBeat;
3523 } else if (choice == _("Beats/4")) {
3524 snaptype = SnapToAQuarterBeat;
3525 } else if (choice == _("Beats/8")) {
3526 snaptype = SnapToAEighthBeat;
3527 } else if (choice == _("Beats/16")) {
3528 snaptype = SnapToASixteenthBeat;
3529 } else if (choice == _("Beats/32")) {
3530 snaptype = SnapToAThirtysecondBeat;
3531 } else if (choice == _("Beats")) {
3532 snaptype = SnapToBeat;
3533 } else if (choice == _("Bars")) {
3534 snaptype = SnapToBar;
3535 } else if (choice == _("Marks")) {
3536 snaptype = SnapToMark;
3537 } else if (choice == _("Region starts")) {
3538 snaptype = SnapToRegionStart;
3539 } else if (choice == _("Region ends")) {
3540 snaptype = SnapToRegionEnd;
3541 } else if (choice == _("Region bounds")) {
3542 snaptype = SnapToRegionBoundary;
3543 } else if (choice == _("Region syncs")) {
3544 snaptype = SnapToRegionSync;
3545 } else if (choice == _("CD Frames")) {
3546 snaptype = SnapToCDFrame;
3547 } else if (choice == _("SMPTE Frames")) {
3548 snaptype = SnapToSMPTEFrame;
3549 } else if (choice == _("SMPTE Seconds")) {
3550 snaptype = SnapToSMPTESeconds;
3551 } else if (choice == _("SMPTE Minutes")) {
3552 snaptype = SnapToSMPTEMinutes;
3553 } else if (choice == _("Seconds")) {
3554 snaptype = SnapToSeconds;
3555 } else if (choice == _("Minutes")) {
3556 snaptype = SnapToMinutes;
3559 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3560 if (ract) {
3561 ract->set_active ();
3565 void
3566 Editor::snap_mode_selection_done ()
3568 string choice = snap_mode_selector.get_active_text();
3569 SnapMode mode = SnapNormal;
3571 if (choice == _("No Grid")) {
3572 mode = SnapOff;
3573 } else if (choice == _("Grid")) {
3574 mode = SnapNormal;
3575 } else if (choice == _("Magnetic")) {
3576 mode = SnapMagnetic;
3579 RefPtr<RadioAction> ract = snap_mode_action (mode);
3581 if (ract) {
3582 ract->set_active (true);
3586 void
3587 Editor::cycle_edit_point (bool with_marker)
3589 switch (_edit_point) {
3590 case EditAtMouse:
3591 set_edit_point_preference (EditAtPlayhead);
3592 break;
3593 case EditAtPlayhead:
3594 if (with_marker) {
3595 set_edit_point_preference (EditAtSelectedMarker);
3596 } else {
3597 set_edit_point_preference (EditAtMouse);
3599 break;
3600 case EditAtSelectedMarker:
3601 set_edit_point_preference (EditAtMouse);
3602 break;
3606 void
3607 Editor::edit_point_selection_done ()
3609 string choice = edit_point_selector.get_active_text();
3610 EditPoint ep = EditAtSelectedMarker;
3612 if (choice == _("Marker")) {
3613 set_edit_point_preference (EditAtSelectedMarker);
3614 } else if (choice == _("Playhead")) {
3615 set_edit_point_preference (EditAtPlayhead);
3616 } else {
3617 set_edit_point_preference (EditAtMouse);
3620 RefPtr<RadioAction> ract = edit_point_action (ep);
3622 if (ract) {
3623 ract->set_active (true);
3627 void
3628 Editor::zoom_focus_selection_done ()
3630 string choice = zoom_focus_selector.get_active_text();
3631 ZoomFocus focus_type = ZoomFocusLeft;
3633 if (choice == _("Left")) {
3634 focus_type = ZoomFocusLeft;
3635 } else if (choice == _("Right")) {
3636 focus_type = ZoomFocusRight;
3637 } else if (choice == _("Center")) {
3638 focus_type = ZoomFocusCenter;
3639 } else if (choice == _("Playhead")) {
3640 focus_type = ZoomFocusPlayhead;
3641 } else if (choice == _("Mouse")) {
3642 focus_type = ZoomFocusMouse;
3643 } else if (choice == _("Active Mark")) {
3644 focus_type = ZoomFocusEdit;
3647 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3649 if (ract) {
3650 ract->set_active ();
3654 gint
3655 Editor::edit_controls_button_release (GdkEventButton* ev)
3657 if (Keyboard::is_context_menu_event (ev)) {
3658 ARDOUR_UI::instance()->add_route (this);
3660 return TRUE;
3663 gint
3664 Editor::mouse_select_button_release (GdkEventButton* ev)
3666 /* this handles just right-clicks */
3668 if (ev->button != 3) {
3669 return false;
3672 return true;
3675 Editor::TrackViewList *
3676 Editor::get_valid_views (TimeAxisView* track, RouteGroup* group)
3678 TrackViewList *v;
3679 TrackViewList::iterator i;
3681 v = new TrackViewList;
3683 if (track == 0 && group == 0) {
3685 /* all views */
3687 for (i = track_views.begin(); i != track_views.end (); ++i) {
3688 v->push_back (*i);
3691 } else if ((track != 0 && group == 0) || (track != 0 && group != 0 && !group->is_active())) {
3693 /* just the view for this track
3696 v->push_back (track);
3698 } else {
3700 /* views for all tracks in the edit group */
3702 for (i = track_views.begin(); i != track_views.end (); ++i) {
3704 if (group == 0 || (*i)->edit_group() == group) {
3705 v->push_back (*i);
3710 return v;
3713 void
3714 Editor::set_zoom_focus (ZoomFocus f)
3716 string str = zoom_focus_strings[(int)f];
3718 if (str != zoom_focus_selector.get_active_text()) {
3719 zoom_focus_selector.set_active_text (str);
3722 if (zoom_focus != f) {
3723 zoom_focus = f;
3725 ZoomFocusChanged (); /* EMIT_SIGNAL */
3727 instant_save ();
3731 void
3732 Editor::ensure_float (Window& win)
3734 win.set_transient_for (*this);
3737 void
3738 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3740 /* recover or initialize pane positions. do this here rather than earlier because
3741 we don't want the positions to change the child allocations, which they seem to do.
3744 int pos;
3745 XMLProperty* prop;
3746 char buf[32];
3747 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3748 int width, height;
3749 static int32_t done;
3750 XMLNode* geometry;
3752 width = default_width;
3753 height = default_height;
3755 if ((geometry = find_named_node (*node, "geometry")) != 0) {
3757 if ((prop = geometry->property ("x_size")) == 0) {
3758 prop = geometry->property ("x-size");
3760 if (prop) {
3761 width = atoi (prop->value());
3763 if ((prop = geometry->property ("y_size")) == 0) {
3764 prop = geometry->property ("y-size");
3766 if (prop) {
3767 height = atoi (prop->value());
3771 if (which == static_cast<Paned*> (&edit_pane)) {
3773 if (done) {
3774 return;
3777 if (!geometry || (prop = geometry->property ("edit_pane_pos")) == 0) {
3778 /* initial allocation is 90% to canvas, 10% to notebook */
3779 pos = (int) floor (alloc.get_width() * 0.90f);
3780 snprintf (buf, sizeof(buf), "%d", pos);
3781 } else {
3782 pos = atoi (prop->value());
3785 if ((done = GTK_WIDGET(edit_pane.gobj())->allocation.width > pos)) {
3786 edit_pane.set_position (pos);
3787 pre_maximal_pane_position = pos;
3792 void
3793 Editor::detach_tearoff (Box* b, Window* w)
3795 if (tools_tearoff->torn_off() &&
3796 mouse_mode_tearoff->torn_off()) {
3797 top_hbox.remove (toolbar_frame);
3801 void
3802 Editor::reattach_tearoff (Box* b, Window* w, int32_t n)
3804 if (toolbar_frame.get_parent() == 0) {
3805 top_hbox.pack_end (toolbar_frame);
3809 void
3810 Editor::set_show_measures (bool yn)
3812 if (_show_measures != yn) {
3813 hide_measures ();
3815 if ((_show_measures = yn) == true) {
3816 if (tempo_lines) {
3817 tempo_lines->show();
3819 draw_measures ();
3821 instant_save ();
3825 void
3826 Editor::toggle_follow_playhead ()
3828 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3829 if (act) {
3830 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3831 set_follow_playhead (tact->get_active());
3835 void
3836 Editor::set_follow_playhead (bool yn)
3838 if (_follow_playhead != yn) {
3839 if ((_follow_playhead = yn) == true) {
3840 /* catch up */
3841 update_current_screen ();
3843 instant_save ();
3847 void
3848 Editor::toggle_stationary_playhead ()
3850 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3851 if (act) {
3852 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3853 set_stationary_playhead (tact->get_active());
3857 void
3858 Editor::set_stationary_playhead (bool yn)
3860 if (_stationary_playhead != yn) {
3861 if ((_stationary_playhead = yn) == true) {
3862 /* catch up */
3863 update_current_screen ();
3865 instant_save ();
3869 void
3870 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3872 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3873 if (xfade) {
3874 xfade->set_active (!xfade->active());
3878 void
3879 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3881 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3882 if (xfade) {
3883 xfade->set_follow_overlap (!xfade->following_overlap());
3887 void
3888 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3890 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3892 if (!xfade) {
3893 return;
3896 CrossfadeEditor cew (*session, xfade, xfade->fade_in().get_min_y(), 1.0);
3898 ensure_float (cew);
3900 switch (cew.run ()) {
3901 case RESPONSE_ACCEPT:
3902 break;
3903 default:
3904 return;
3907 cew.apply ();
3908 xfade->StateChanged (Change (~0));
3911 PlaylistSelector&
3912 Editor::playlist_selector () const
3914 return *_playlist_selector;
3917 nframes64_t
3918 Editor::get_nudge_distance (nframes64_t pos, nframes64_t& next)
3920 nframes64_t ret;
3922 ret = nudge_clock.current_duration (pos);
3923 next = ret + 1; /* XXXX fix me */
3925 return ret;
3928 void
3929 Editor::end_location_changed (Location* location)
3931 ENSURE_GUI_THREAD (bind (mem_fun(*this, &Editor::end_location_changed), location));
3932 //reset_scrolling_region ();
3933 nframes64_t session_span = location->start() + (nframes64_t) floorf (current_page_frames() * 0.10f);
3934 horizontal_adjustment.set_upper (session_span / frames_per_unit);
3938 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3940 ArdourDialog dialog ("playlist deletion dialog");
3941 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3942 "If left alone, no audio files used by it will be cleaned.\n"
3943 "If deleted, audio files used by it alone by will cleaned."),
3944 pl->name()));
3946 dialog.set_position (WIN_POS_CENTER);
3947 dialog.get_vbox()->pack_start (label);
3949 label.show ();
3951 dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT);
3952 dialog.add_button (_("Keep playlist"), RESPONSE_REJECT);
3953 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3955 switch (dialog.run ()) {
3956 case RESPONSE_ACCEPT:
3957 /* delete the playlist */
3958 return 0;
3959 break;
3961 case RESPONSE_REJECT:
3962 /* keep the playlist */
3963 return 1;
3964 break;
3966 default:
3967 break;
3970 return -1;
3973 bool
3974 Editor::audio_region_selection_covers (nframes64_t where)
3976 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3977 if ((*a)->region()->covers (where)) {
3978 return true;
3982 return false;
3985 void
3986 Editor::prepare_for_cleanup ()
3988 cut_buffer->clear_regions ();
3989 cut_buffer->clear_playlists ();
3991 selection->clear_regions ();
3992 selection->clear_playlists ();
3994 no_region_list_redisplay = true;
3997 void
3998 Editor::finish_cleanup ()
4000 no_region_list_redisplay = false;
4001 redisplay_regions ();
4004 Location*
4005 Editor::transport_loop_location()
4007 if (session) {
4008 return session->locations()->auto_loop_location();
4009 } else {
4010 return 0;
4014 Location*
4015 Editor::transport_punch_location()
4017 if (session) {
4018 return session->locations()->auto_punch_location();
4019 } else {
4020 return 0;
4024 bool
4025 Editor::control_layout_scroll (GdkEventScroll* ev)
4027 switch (ev->direction) {
4028 case GDK_SCROLL_UP:
4029 scroll_tracks_up_line ();
4030 return true;
4031 break;
4033 case GDK_SCROLL_DOWN:
4034 scroll_tracks_down_line ();
4035 return true;
4037 default:
4038 /* no left/right handling yet */
4039 break;
4042 return false;
4046 /** A new snapshot has been selected.
4048 void
4049 Editor::snapshot_display_selection_changed ()
4051 if (snapshot_display.get_selection()->count_selected_rows() > 0) {
4053 TreeModel::iterator i = snapshot_display.get_selection()->get_selected();
4055 Glib::ustring snap_name = (*i)[snapshot_display_columns.real_name];
4057 if (snap_name.length() == 0) {
4058 return;
4061 if (session->snap_name() == snap_name) {
4062 return;
4065 ARDOUR_UI::instance()->load_session(session->path(), string (snap_name));
4069 bool
4070 Editor::snapshot_display_button_press (GdkEventButton* ev)
4072 if (ev->button == 3) {
4073 /* Right-click on the snapshot list. Work out which snapshot it
4074 was over. */
4075 Gtk::TreeModel::Path path;
4076 Gtk::TreeViewColumn* col;
4077 int cx;
4078 int cy;
4079 snapshot_display.get_path_at_pos ((int) ev->x, (int) ev->y, path, col, cx, cy);
4080 Gtk::TreeModel::iterator iter = snapshot_display_model->get_iter (path);
4081 if (iter) {
4082 Gtk::TreeModel::Row row = *iter;
4083 popup_snapshot_context_menu (ev->button, ev->time, row[snapshot_display_columns.real_name]);
4085 return true;
4088 return false;
4092 /** Pop up the snapshot display context menu.
4093 * @param button Button used to open the menu.
4094 * @param time Menu open time.
4095 * @snapshot_name Name of the snapshot that the menu click was over.
4098 void
4099 Editor::popup_snapshot_context_menu (int button, int32_t time, Glib::ustring snapshot_name)
4101 using namespace Menu_Helpers;
4103 MenuList& items (snapshot_context_menu.items());
4104 items.clear ();
4106 const bool modification_allowed = (session->snap_name() != snapshot_name && session->name() != snapshot_name);
4108 items.push_back (MenuElem (_("Remove"), bind (mem_fun (*this, &Editor::remove_snapshot), snapshot_name)));
4109 if (!modification_allowed) {
4110 items.back().set_sensitive (false);
4113 items.push_back (MenuElem (_("Rename"), bind (mem_fun (*this, &Editor::rename_snapshot), snapshot_name)));
4114 if (!modification_allowed) {
4115 items.back().set_sensitive (false);
4118 snapshot_context_menu.popup (button, time);
4121 void
4122 Editor::rename_snapshot (Glib::ustring old_name)
4124 ArdourPrompter prompter(true);
4126 string new_name;
4128 prompter.set_name ("Prompter");
4129 prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
4130 prompter.set_prompt (_("New name of snapshot"));
4131 prompter.set_initial_text (old_name);
4133 if (prompter.run() == RESPONSE_ACCEPT) {
4134 prompter.get_result (new_name);
4135 if (new_name.length()) {
4136 session->rename_state (old_name, new_name);
4137 redisplay_snapshots ();
4143 void
4144 Editor::remove_snapshot (Glib::ustring name)
4146 vector<string> choices;
4148 std::string prompt = string_compose (_("Do you really want to remove snapshot \"%1\" ?\n(cannot be undone)"), name);
4150 choices.push_back (_("No, do nothing."));
4151 choices.push_back (_("Yes, remove it."));
4153 Gtkmm2ext::Choice prompter (prompt, choices);
4155 if (prompter.run () == 1) {
4156 session->remove_state (name);
4157 redisplay_snapshots ();
4161 void
4162 Editor::redisplay_snapshots ()
4164 if (session == 0) {
4165 return;
4168 vector<string*>* states;
4170 if ((states = session->possible_states()) == 0) {
4171 return;
4174 snapshot_display_model->clear ();
4176 for (vector<string*>::iterator i = states->begin(); i != states->end(); ++i) {
4177 string statename = *(*i);
4178 TreeModel::Row row = *(snapshot_display_model->append());
4180 /* this lingers on in case we ever want to change the visible
4181 name of the snapshot.
4184 string display_name;
4185 display_name = statename;
4187 if (statename == session->snap_name()) {
4188 snapshot_display.get_selection()->select(row);
4191 row[snapshot_display_columns.visible_name] = display_name;
4192 row[snapshot_display_columns.real_name] = statename;
4195 delete states;
4198 void
4199 Editor::session_state_saved (string snap_name)
4201 ENSURE_GUI_THREAD (bind (mem_fun(*this, &Editor::session_state_saved), snap_name));
4202 redisplay_snapshots ();
4205 void
4206 Editor::maximise_editing_space ()
4208 mouse_mode_tearoff->set_visible (false);
4209 tools_tearoff->set_visible (false);
4211 pre_maximal_pane_position = edit_pane.get_position();
4212 pre_maximal_editor_width = this->get_width();
4214 if(post_maximal_pane_position == 0) {
4215 post_maximal_pane_position = edit_pane.get_width();
4218 fullscreen();
4220 if(post_maximal_editor_width) {
4221 edit_pane.set_position (post_maximal_pane_position -
4222 abs(post_maximal_editor_width - pre_maximal_editor_width));
4223 } else {
4224 edit_pane.set_position (post_maximal_pane_position);
4228 void
4229 Editor::restore_editing_space ()
4231 // user changed width of pane during fullscreen
4232 if(post_maximal_pane_position != edit_pane.get_position()) {
4233 post_maximal_pane_position = edit_pane.get_position();
4236 unfullscreen();
4238 mouse_mode_tearoff->set_visible (true);
4239 tools_tearoff->set_visible (true);
4240 post_maximal_editor_width = this->get_width();
4243 edit_pane.set_position (
4244 pre_maximal_pane_position + abs(this->get_width() - pre_maximal_editor_width)
4249 * Make new playlists for a given track and also any others that belong
4250 * to the same active edit group.
4251 * @param v Track.
4254 void
4255 Editor::new_playlists (TimeAxisView* v)
4257 begin_reversible_command (_("new playlists"));
4258 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4259 session->get_playlists(playlists);
4260 mapover_audio_tracks ( bind(mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v );
4261 commit_reversible_command ();
4266 * Use a copy of the current playlist for a given track and also any others that belong
4267 * to the same active edit group.
4268 * @param v Track.
4271 void
4272 Editor::copy_playlists (TimeAxisView* v)
4274 begin_reversible_command (_("copy playlists"));
4275 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4276 session->get_playlists(playlists);
4277 mapover_audio_tracks ( bind(mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v );
4278 commit_reversible_command ();
4283 * Clear the current playlist for a given track and also any others that belong
4284 * to the same active edit group.
4285 * @param v Track.
4288 void
4289 Editor::clear_playlists (TimeAxisView* v)
4291 begin_reversible_command (_("clear playlists"));
4292 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4293 session->get_playlists(playlists);
4294 mapover_audio_tracks ( mem_fun (*this, &Editor::mapped_clear_playlist), v );
4295 commit_reversible_command ();
4298 void
4299 Editor::mapped_use_new_playlist (AudioTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4301 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4304 void
4305 Editor::mapped_use_copy_playlist (AudioTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4307 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4310 void
4311 Editor::mapped_clear_playlist (AudioTimeAxisView& atv, uint32_t sz)
4313 atv.clear_playlist ();
4316 bool
4317 Editor::on_key_press_event (GdkEventKey* ev)
4319 return key_press_focus_accelerator_handler (*this, ev);
4322 bool
4323 Editor::on_key_release_event (GdkEventKey* ev)
4325 return Gtk::Window::on_key_release_event (ev);
4326 // return key_press_focus_accelerator_handler (*this, ev);
4329 void
4330 Editor::reset_x_origin (nframes64_t frame)
4332 queue_visual_change (frame);
4335 void
4336 Editor::reset_zoom (double fpu)
4338 queue_visual_change (fpu);
4341 void
4342 Editor::reposition_and_zoom (nframes64_t frame, double fpu)
4344 //cerr << "Editor::reposition_and_zoom () called ha v:l:u:ps:fpu = " << horizontal_adjustment.get_value() << ":" << horizontal_adjustment.get_lower() << ":" << horizontal_adjustment.get_upper() << ":" << horizontal_adjustment.get_page_size() << ":" << frames_per_unit << endl;//DEBUG
4345 reset_x_origin (frame);
4346 reset_zoom (fpu);
4348 if (!no_save_visual) {
4349 undo_visual_stack.push_back (current_visual_state(false));
4353 Editor::VisualState*
4354 Editor::current_visual_state (bool with_tracks)
4356 VisualState* vs = new VisualState;
4357 vs->y_position = vertical_adjustment.get_value();
4358 vs->frames_per_unit = frames_per_unit;
4359 vs->leftmost_frame = leftmost_frame;
4360 vs->zoom_focus = zoom_focus;
4362 if (with_tracks) {
4363 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4364 vs->track_states.push_back (TAVState ((*i), &(*i)->get_state()));
4368 return vs;
4371 void
4372 Editor::undo_visual_state ()
4374 if (undo_visual_stack.empty()) {
4375 return;
4378 redo_visual_stack.push_back (current_visual_state());
4380 VisualState* vs = undo_visual_stack.back();
4381 undo_visual_stack.pop_back();
4382 use_visual_state (*vs);
4385 void
4386 Editor::redo_visual_state ()
4388 if (redo_visual_stack.empty()) {
4389 return;
4392 undo_visual_stack.push_back (current_visual_state());
4394 VisualState* vs = redo_visual_stack.back();
4395 redo_visual_stack.pop_back();
4396 use_visual_state (*vs);
4399 void
4400 Editor::swap_visual_state ()
4402 if (undo_visual_stack.empty()) {
4403 redo_visual_state ();
4404 } else {
4405 undo_visual_state ();
4406 undo_visual_stack.clear(); //swap_visual_state truncates the undo stack so we are just bouncing between 2 states
4410 void
4411 Editor::use_visual_state (VisualState& vs)
4413 no_save_visual = true;
4414 no_route_list_redisplay = true;
4416 vertical_adjustment.set_value (vs.y_position);
4418 set_zoom_focus (vs.zoom_focus);
4419 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4421 for (list<TAVState>::iterator i = vs.track_states.begin(); i != vs.track_states.end(); ++i) {
4422 TrackViewList::iterator t;
4424 /* check if the track still exists - it could have been deleted */
4426 if ((t = find (track_views.begin(), track_views.end(), i->first)) != track_views.end()) {
4427 (*t)->set_state (*(i->second));
4431 if (!vs.track_states.empty()) {
4432 update_route_visibility ();
4435 no_route_list_redisplay = false;
4436 redisplay_route_list ();
4438 no_save_visual = false;
4441 void
4442 Editor::set_frames_per_unit (double fpu)
4444 /* this is the core function that controls the zoom level of the canvas. it is called
4445 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4448 if (fpu == frames_per_unit) {
4449 return;
4452 if (fpu < 1.0) {
4453 fpu = 1.0;
4457 /* don't allow zooms that fit more than the maximum number
4458 of frames into an 800 pixel wide space.
4461 if (max_frames / fpu < 800.0) {
4462 return;
4465 if (tempo_lines) {
4466 tempo_lines->tempo_map_changed();
4469 frames_per_unit = fpu;
4470 post_zoom ();
4473 void
4474 Editor::post_zoom ()
4476 nframes64_t cef = 0;
4478 // convert fpu to frame count
4480 nframes64_t frames = (nframes64_t) floor (frames_per_unit * canvas_width);
4482 if (frames_per_unit != zoom_range_clock.current_duration()) {
4483 zoom_range_clock.set (frames);
4486 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4487 if (!selection->tracks.empty()) {
4488 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4489 (*i)->reshow_selection (selection->time);
4491 } else {
4492 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4493 (*i)->reshow_selection (selection->time);
4497 update_loop_range_view (false);
4498 update_punch_range_view (false);
4500 if (playhead_cursor) {
4501 playhead_cursor->set_position (playhead_cursor->current_frame);
4504 leftmost_frame = (nframes64_t) floor (horizontal_adjustment.get_value() * frames_per_unit);
4506 ZoomChanged (); /* EMIT_SIGNAL */
4508 reset_hscrollbar_stepping ();
4510 if (session) {
4511 cef = session->current_end_frame() + (current_page_frames() / 10);// Add a little extra so we can see the end marker
4513 horizontal_adjustment.set_upper (cef / frames_per_unit);
4515 //reset_scrolling_region ();
4517 instant_save ();
4520 void
4521 Editor::queue_visual_change (nframes64_t where)
4523 pending_visual_change.pending = VisualChange::Type (pending_visual_change.pending | VisualChange::TimeOrigin);
4525 /* if we're moving beyond the end, make sure the upper limit of the horizontal adjustment
4526 can reach.
4529 if (where > session->current_end_frame()) {
4530 horizontal_adjustment.set_upper ((where + current_page_frames()) / frames_per_unit);
4533 pending_visual_change.time_origin = where;
4535 if (pending_visual_change.idle_handler_id < 0) {
4536 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4540 void
4541 Editor::queue_visual_change (double fpu)
4543 pending_visual_change.pending = VisualChange::Type (pending_visual_change.pending | VisualChange::ZoomLevel);
4544 pending_visual_change.frames_per_unit = fpu;
4546 if (pending_visual_change.idle_handler_id < 0) {
4547 pending_visual_change.idle_handler_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE, _idle_visual_changer, this, 0);
4552 Editor::_idle_visual_changer (void* arg)
4554 return static_cast<Editor*>(arg)->idle_visual_changer ();
4558 Editor::idle_visual_changer ()
4560 VisualChange::Type p = pending_visual_change.pending;
4561 pending_visual_change.pending = (VisualChange::Type) 0;
4563 double last_time_origin = horizontal_adjustment.get_value();
4564 if (p & VisualChange::ZoomLevel) {
4565 set_frames_per_unit (pending_visual_change.frames_per_unit);
4568 if (p & VisualChange::TimeOrigin) {
4569 horizontal_adjustment.set_value (pending_visual_change.time_origin / frames_per_unit);
4572 if (last_time_origin == horizontal_adjustment.get_value() ) {
4573 /* changed signal not emitted */
4574 update_fixed_rulers ();
4575 redisplay_tempo (true);
4577 // cerr << "Editor::idle_visual_changer () called ha v:l:u:ps:fpu = " << horizontal_adjustment.get_value() << ":" << horizontal_adjustment.get_lower() << ":" << horizontal_adjustment.get_upper() << ":" << horizontal_adjustment.get_page_size() << ":" << frames_per_unit << endl;//DEBUG
4578 pending_visual_change.idle_handler_id = -1;
4579 return 0; /* this is always a one-shot call */
4582 struct EditorOrderTimeAxisSorter {
4583 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4584 return a->order < b->order;
4588 void
4589 Editor::sort_track_selection (TrackSelection* sel)
4591 EditorOrderTimeAxisSorter cmp;
4593 if (sel) {
4594 sel->sort (cmp);
4595 } else {
4596 selection->tracks.sort (cmp);
4600 nframes64_t
4601 Editor::get_preferred_edit_position (bool ignore_playhead)
4603 bool ignored;
4604 nframes64_t where = 0;
4605 EditPoint ep = _edit_point;
4607 if (entered_marker) {
4608 return entered_marker->position();
4611 if (ignore_playhead && ep == EditAtPlayhead) {
4612 ep = EditAtSelectedMarker;
4615 switch (ep) {
4616 case EditAtPlayhead:
4617 where = session->audible_frame();
4618 break;
4620 case EditAtSelectedMarker:
4621 if (!selection->markers.empty()) {
4622 bool is_start;
4623 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4624 if (loc) {
4625 if (is_start) {
4626 where = loc->start();
4627 } else {
4628 where = loc->end();
4630 break;
4633 /* fallthru */
4635 default:
4636 case EditAtMouse:
4637 if (!mouse_frame (where, ignored)) {
4638 /* XXX not right but what can we do ? */
4639 return 0;
4641 snap_to (where);
4642 break;
4645 return where;
4648 void
4649 Editor::set_loop_range (nframes64_t start, nframes64_t end, string cmd)
4651 if (!session) return;
4653 begin_reversible_command (cmd);
4655 Location* tll;
4657 if ((tll = transport_loop_location()) == 0) {
4658 Location* loc = new Location (start, end, _("Loop"), Location::IsAutoLoop);
4659 XMLNode &before = session->locations()->get_state();
4660 session->locations()->add (loc, true);
4661 session->set_auto_loop_location (loc);
4662 XMLNode &after = session->locations()->get_state();
4663 session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after));
4664 } else {
4665 XMLNode &before = tll->get_state();
4666 tll->set_hidden (false, this);
4667 tll->set (start, end);
4668 XMLNode &after = tll->get_state();
4669 session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4672 commit_reversible_command ();
4675 void
4676 Editor::set_punch_range (nframes64_t start, nframes64_t end, string cmd)
4678 if (!session) return;
4680 begin_reversible_command (cmd);
4682 Location* tpl;
4684 if ((tpl = transport_punch_location()) == 0) {
4685 Location* loc = new Location (start, end, _("Loop"), Location::IsAutoPunch);
4686 XMLNode &before = session->locations()->get_state();
4687 session->locations()->add (loc, true);
4688 session->set_auto_loop_location (loc);
4689 XMLNode &after = session->locations()->get_state();
4690 session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after));
4692 else {
4693 XMLNode &before = tpl->get_state();
4694 tpl->set_hidden (false, this);
4695 tpl->set (start, end);
4696 XMLNode &after = tpl->get_state();
4697 session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4700 commit_reversible_command ();
4703 void
4704 Editor::get_regions_at (RegionSelection& rs, nframes64_t where, const TrackSelection& ts) const
4706 const TrackSelection* tracks;
4708 if (ts.empty()) {
4709 tracks = &track_views;
4710 } else {
4711 tracks = &ts;
4714 for (TrackSelection::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4716 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*t);
4718 if (atv) {
4719 boost::shared_ptr<Diskstream> ds;
4720 boost::shared_ptr<Playlist> pl;
4722 if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()))) {
4724 Playlist::RegionList* regions = pl->regions_at ((nframes64_t) floor ( (double)where * ds->speed()));
4726 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4728 RegionView* rv = atv->audio_view()->find_view (*i);
4730 if (rv) {
4731 rs.add (rv);
4735 delete regions;
4741 void
4742 Editor::get_regions_after (RegionSelection& rs, nframes64_t where, const TrackSelection& ts) const
4744 const TrackSelection* tracks;
4746 if (ts.empty()) {
4747 tracks = &track_views;
4748 } else {
4749 tracks = &ts;
4752 for (TrackSelection::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4754 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*t);
4756 if (atv) {
4757 boost::shared_ptr<Diskstream> ds;
4758 boost::shared_ptr<Playlist> pl;
4760 if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()))) {
4762 Playlist::RegionList* regions = pl->regions_touched ((nframes64_t) floor ( (double)where * ds->speed()), max_frames);
4764 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4766 RegionView* rv = atv->audio_view()->find_view (*i);
4768 if (rv) {
4769 rs.push_back (rv);
4773 delete regions;
4779 void
4780 Editor::get_regions_for_action (RegionSelection& rs, bool allow_entered)
4782 if (selection->regions.empty()) {
4784 if (selection->tracks.empty()) {
4786 /* no regions or tracks selected
4789 if (entered_regionview && mouse_mode == Editing::MouseObject) {
4791 /* entered regionview is valid and we're in object mode -
4792 just use entered regionview
4795 rs.add (entered_regionview);
4798 return;
4800 } else {
4802 /* no regions selected, so get all regions at the edit point across
4803 all selected tracks.
4806 nframes64_t where = get_preferred_edit_position();
4807 get_regions_at (rs, where, selection->tracks);
4809 /* if the entered regionview wasn't selected and neither was its track
4810 then add it.
4813 if (entered_regionview != 0 &&
4814 !selection->selected (entered_regionview) &&
4815 !selection->selected (&entered_regionview->get_time_axis_view())) {
4816 rs.add (entered_regionview);
4820 } else {
4822 /* just use the selected regions */
4824 rs = selection->regions;
4826 /* if the entered regionview wasn't selected and we allow this sort of thing,
4827 then add it.
4830 if (allow_entered && entered_regionview && !selection->selected (entered_regionview)) {
4831 rs.add (entered_regionview);
4837 void
4838 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4841 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4843 RouteTimeAxisView* tatv;
4845 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4847 boost::shared_ptr<Playlist> pl;
4848 vector<boost::shared_ptr<Region> > results;
4849 RegionView* marv;
4850 boost::shared_ptr<Diskstream> ds;
4852 if ((ds = tatv->get_diskstream()) == 0) {
4853 /* bus */
4854 continue;
4857 if ((pl = (ds->playlist())) != 0) {
4858 pl->get_region_list_equivalent_regions (region, results);
4861 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4862 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4863 regions.push_back (marv);
4871 void
4872 Editor::show_rhythm_ferret ()
4874 if (rhythm_ferret == 0) {
4875 rhythm_ferret = new RhythmFerret(*this);
4878 rhythm_ferret->set_session (session);
4879 rhythm_ferret->show ();
4880 rhythm_ferret->present ();
4883 void
4884 Editor::first_idle ()
4886 MessageDialog* dialog = 0;
4888 if (track_views.size() > 1) {
4889 dialog = new MessageDialog (*this,
4890 _("Please wait while Ardour loads visual data"),
4891 true,
4892 Gtk::MESSAGE_INFO,
4893 Gtk::BUTTONS_NONE);
4894 dialog->present ();
4895 ARDOUR_UI::instance()->flush_pending ();
4898 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4899 (*t)->first_idle();
4902 // first idle adds route children (automation tracks), so we need to redisplay here
4903 redisplay_route_list();
4905 if (dialog) {
4906 delete dialog;
4909 _have_idled = true;
4912 void
4913 Editor::start_resize_line_ops ()
4915 #if 0
4916 old_resize_line_y = -1;
4917 resize_line_y = -1;
4918 need_resize_line = true;
4919 #endif
4922 void
4923 Editor::end_resize_line_ops ()
4925 #if 0
4926 need_resize_line = false;
4928 if (old_resize_line_y >= 0) {
4929 Gdk::Rectangle r (0, old_resize_line_y, (int) canvas_width, 3);
4930 Glib::RefPtr<Gdk::Window> win = get_window();
4931 cerr << "Final invalidation at " << old_resize_line_y << endl;
4932 win->invalidate_rect (r, false);
4934 #endif
4937 void
4938 Editor::queue_draw_resize_line (int at)
4940 #if 0
4941 Glib::RefPtr<Gdk::Window> win = get_window();
4943 resize_line_y = at;
4945 if (win && canvas_width) {
4947 int controls_width = controls_layout.get_width();
4948 int xroot, discard;
4950 controls_layout.get_window()->get_origin (xroot, discard);
4952 if (old_resize_line_y >= 0) {
4954 /* redraw where it used to be */
4957 Gdk::Rectangle r (0, old_resize_line_y - 1, controls_width + (int) canvas_width, 3);
4958 win->invalidate_rect (r, true);
4959 cerr << "invalidate " << xroot << "," << old_resize_line_y - 1 << ' '
4960 << controls_width + canvas_width << " x 3\n";
4963 /* draw where it is */
4965 Gdk::Rectangle r (0, at - 1, controls_width + (int) canvas_width, 3);
4966 win->invalidate_rect (r, true);
4968 #endif
4971 bool
4972 Editor::on_expose_event (GdkEventExpose* ev)
4974 /* cerr << "+++ editor expose "
4975 << ev->area.x << ',' << ev->area.y
4976 << ' '
4977 << ev->area.width << " x " << ev->area.height
4978 << " need reize ? " << need_resize_line
4979 << endl;
4981 bool ret = Window::on_expose_event (ev);
4983 #if 0
4984 if (need_resize_line) {
4986 int xroot, yroot, discard;
4987 int controls_width;
4989 /* Our root coordinates for drawing the line will be the left edge
4990 of the track controls, and the upper left edge of our own window.
4993 get_window()->get_origin (discard, yroot);
4994 controls_layout.get_window()->get_origin (xroot, discard);
4995 controls_width = controls_layout.get_width();
4997 GdkRectangle lr;
4998 GdkRectangle intersection;
5000 lr.x = 0;
5001 lr.y = resize_line_y;
5002 lr.width = controls_width + (int) canvas_width;
5003 lr.height = 3;
5005 if (gdk_rectangle_intersect (&lr, &ev->area, &intersection)) {
5007 Glib::RefPtr<Gtk::Style> style (get_style());
5008 Glib::RefPtr<Gdk::GC> black_gc (style->get_black_gc ());
5009 Glib::RefPtr<Gdk::GC> gc = wrap (black_gc->gobj_copy(), false);
5011 /* draw on root window */
5013 GdkWindow* win = gdk_get_default_root_window();
5015 gc->set_subwindow (Gdk::INCLUDE_INFERIORS);
5016 gc->set_line_attributes (3, Gdk::LINE_SOLID,
5017 Gdk::CAP_NOT_LAST,
5018 Gdk::JOIN_MITER);
5020 gdk_draw_line (win, gc->gobj(),
5022 resize_line_y,
5023 (int) canvas_width + controls_width,
5024 resize_line_y);
5025 #if 0
5026 cerr << "drew line @ " << xroot << ", " << yroot + resize_line_y
5027 << " to " << xroot + (int) canvas_width + controls_width
5028 << ", " << yroot + resize_line_y
5029 << endl;
5030 #endif
5031 old_resize_line_y = resize_line_y;
5032 cerr << "NEXT EXPOSE SHOULD BE AT " << old_resize_line_y << endl;
5033 } else {
5034 cerr << "no intersect with "
5035 << lr.x << ',' << lr.y
5036 << ' '
5037 << lr.width << " x " << lr.height
5038 << endl;
5042 //cerr << "--- editor expose\n";
5043 #endif
5045 return ret;
5048 static gboolean
5049 _idle_resizer (gpointer arg)
5051 return ((Editor*)arg)->idle_resize ();
5054 void
5055 Editor::add_to_idle_resize (TimeAxisView* view, uint32_t h)
5057 if (resize_idle_id < 0) {
5058 resize_idle_id = g_idle_add (_idle_resizer, this);
5061 resize_idle_target = h;
5063 pending_resizes.push_back (view);
5065 if (selection->selected (view) && !selection->tracks.empty()) {
5066 pending_resizes.insert (pending_resizes.end(), selection->tracks.begin(), selection->tracks.end());
5070 bool
5071 Editor::idle_resize ()
5073 for (vector<TimeAxisView*>::iterator i = pending_resizes.begin(); i != pending_resizes.end(); ++i) {
5074 (*i)->idle_resize (resize_idle_target);
5076 pending_resizes.clear();
5077 //flush_canvas ();
5078 resize_idle_id = -1;
5079 return false;