use sscanf with the correct type conversion and not atoi/atol to load nframes_t and...
[ardour2.git] / gtk2_ardour / editor.cc
blob55c1b1a7eba8318d3e75dfc3ef7917c4e43de2ef
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_recording = true;
297 export_dialog = 0;
298 export_range_markers_dialog = 0;
299 show_gain_after_trim = false;
300 route_redisplay_does_not_sync_order_keys = false;
301 route_redisplay_does_not_reset_order_keys = false;
302 no_route_list_redisplay = false;
303 verbose_cursor_on = true;
304 route_removal = false;
305 show_automatic_regions_in_region_list = true;
306 last_item_entered = 0;
307 last_item_entered_n = 0;
309 region_list_sort_type = (Editing::RegionListSortType) 0;
310 have_pending_keyboard_selection = false;
311 _follow_playhead = true;
312 _stationary_playhead = false;
313 _xfade_visibility = true;
314 editor_ruler_menu = 0;
315 no_ruler_shown_update = false;
316 edit_group_list_menu = 0;
317 route_list_menu = 0;
318 region_list_menu = 0;
319 marker_menu = 0;
320 start_end_marker_menu = 0;
321 range_marker_menu = 0;
322 marker_menu_item = 0;
323 tm_marker_menu = 0;
324 transport_marker_menu = 0;
325 new_transport_marker_menu = 0;
326 editor_mixer_strip_width = Wide;
327 show_editor_mixer_when_tracks_arrive = false;
328 region_edit_menu_split_item = 0;
329 temp_location = 0;
330 region_edit_menu_split_multichannel_item = 0;
331 leftmost_frame = 0;
332 ignore_mouse_mode_toggle = false;
333 current_stepping_trackview = 0;
334 entered_track = 0;
335 entered_regionview = 0;
336 entered_marker = 0;
337 clear_entered_track = false;
338 _new_regionviews_show_envelope = false;
339 current_timefx = 0;
340 in_edit_group_row_change = false;
341 playhead_cursor = 0;
342 button_release_can_deselect = true;
343 _dragging_playhead = false;
344 _dragging_edit_point = false;
345 _dragging_hscrollbar = false;
346 select_new_marker = false;
347 zoomed_to_region = 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 if (!Profile->get_sae()) {
1906 items.push_back (CheckMenuElem (_("Opaque")));
1907 CheckMenuItem* region_opaque_item = static_cast<CheckMenuItem*>(&items.back());
1908 fooc = region_opaque_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_opaque));
1909 if (region_to_check->opaque()) {
1910 fooc.block (true);
1911 region_opaque_item->set_active();
1912 fooc.block (false);
1916 items.push_back (CheckMenuElem (_("Original position"), mem_fun(*this, &Editor::naturalize)));
1917 if (region_to_check->at_natural_position()) {
1918 items.back().set_sensitive (false);
1921 items.push_back (SeparatorElem());
1923 if (ar) {
1925 RegionView* rv = sv->find_view (ar);
1926 AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv);
1928 if (!Profile->get_sae()) {
1929 items.push_back (MenuElem (_("Reset Envelope"), mem_fun(*this, &Editor::reset_region_gain_envelopes)));
1931 items.push_back (CheckMenuElem (_("Envelope Visible")));
1932 CheckMenuItem* region_envelope_visible_item = static_cast<CheckMenuItem*> (&items.back());
1933 fooc = region_envelope_visible_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_gain_envelope_visibility));
1934 if (arv->envelope_visible()) {
1935 fooc.block (true);
1936 region_envelope_visible_item->set_active (true);
1937 fooc.block (false);
1940 items.push_back (CheckMenuElem (_("Envelope Active")));
1941 CheckMenuItem* region_envelope_active_item = static_cast<CheckMenuItem*> (&items.back());
1942 fooc = region_envelope_active_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_gain_envelope_active));
1944 if (ar->envelope_active()) {
1945 fooc.block (true);
1946 region_envelope_active_item->set_active (true);
1947 fooc.block (false);
1950 items.push_back (SeparatorElem());
1953 if (ar->scale_amplitude() != 1.0f) {
1954 items.push_back (MenuElem (_("DeNormalize"), mem_fun(*this, &Editor::denormalize_region)));
1955 } else {
1956 items.push_back (MenuElem (_("Normalize"), mem_fun(*this, &Editor::normalize_region)));
1960 items.push_back (MenuElem (_("Reverse"), mem_fun(*this, &Editor::reverse_region)));
1961 items.push_back (SeparatorElem());
1963 /* range related stuff */
1965 items.push_back (MenuElem (_("Add Single Range"), mem_fun (*this, &Editor::add_location_from_audio_region)));
1966 items.push_back (MenuElem (_("Add Range Markers"), mem_fun (*this, &Editor::add_locations_from_audio_region)));
1967 if (selection->regions.size() < 2) {
1968 items.back().set_sensitive (false);
1971 items.push_back (MenuElem (_("Set Range Selection"), mem_fun (*this, &Editor::set_selection_from_audio_region)));
1972 items.push_back (SeparatorElem());
1974 /* Nudge region */
1976 Menu *nudge_menu = manage (new Menu());
1977 MenuList& nudge_items = nudge_menu->items();
1978 nudge_menu->set_name ("ArdourContextMenu");
1980 nudge_items.push_back (MenuElem (_("Nudge fwd"), (bind (mem_fun(*this, &Editor::nudge_forward), false, false))));
1981 nudge_items.push_back (MenuElem (_("Nudge bwd"), (bind (mem_fun(*this, &Editor::nudge_backward), false, false))));
1982 nudge_items.push_back (MenuElem (_("Nudge fwd by capture offset"), (mem_fun(*this, &Editor::nudge_forward_capture_offset))));
1983 nudge_items.push_back (MenuElem (_("Nudge bwd by capture offset"), (mem_fun(*this, &Editor::nudge_backward_capture_offset))));
1985 items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1986 items.push_back (SeparatorElem());
1988 Menu *trim_menu = manage (new Menu);
1989 MenuList& trim_items = trim_menu->items();
1990 trim_menu->set_name ("ArdourContextMenu");
1992 trim_items.push_back (MenuElem (_("Start to edit point"), mem_fun(*this, &Editor::trim_region_from_edit_point)));
1993 foo_item = &trim_items.back();
1994 if (_edit_point == EditAtMouse) {
1995 foo_item->set_sensitive (false);
1997 trim_items.push_back (MenuElem (_("Edit point to end"), mem_fun(*this, &Editor::trim_region_to_edit_point)));
1998 foo_item = &trim_items.back();
1999 if (_edit_point == EditAtMouse) {
2000 foo_item->set_sensitive (false);
2002 trim_items.push_back (MenuElem (_("Trim To Loop"), mem_fun(*this, &Editor::trim_region_to_loop)));
2003 trim_items.push_back (MenuElem (_("Trim To Punch"), mem_fun(*this, &Editor::trim_region_to_punch)));
2005 items.push_back (MenuElem (_("Trim"), *trim_menu));
2006 items.push_back (SeparatorElem());
2008 items.push_back (MenuElem (_("Split"), (mem_fun(*this, &Editor::split_region))));
2009 region_edit_menu_split_item = &items.back();
2011 if (_edit_point == EditAtMouse) {
2012 region_edit_menu_split_item->set_sensitive (false);
2015 items.push_back (MenuElem (_("Make mono regions"), (mem_fun(*this, &Editor::split_multichannel_region))));
2016 region_edit_menu_split_multichannel_item = &items.back();
2018 items.push_back (MenuElem (_("Duplicate"), (bind (mem_fun(*this, &Editor::duplicate_dialog), false))));
2019 items.push_back (MenuElem (_("Multi-Duplicate"), (bind (mem_fun(*this, &Editor::duplicate_dialog), true))));
2020 items.push_back (MenuElem (_("Fill Track"), (mem_fun(*this, &Editor::region_fill_track))));
2021 items.push_back (SeparatorElem());
2022 items.push_back (MenuElem (_("Remove"), mem_fun(*this, &Editor::remove_region)));
2024 /* OK, stick the region submenu at the top of the list, and then add
2025 the standard items.
2028 /* we have to hack up the region name because "_" has a special
2029 meaning for menu titles.
2032 string::size_type pos = 0;
2033 string menu_item_name = (region) ? region->name() : _("Selected regions");
2035 while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
2036 menu_item_name.replace (pos, 1, "__");
2037 pos += 2;
2040 edit_items.push_back (MenuElem (menu_item_name, *region_menu));
2041 edit_items.push_back (SeparatorElem());
2044 void
2045 Editor::add_selection_context_items (Menu_Helpers::MenuList& items)
2047 using namespace Menu_Helpers;
2049 items.push_back (MenuElem (_("Play range"), mem_fun(*this, &Editor::play_selection)));
2050 items.push_back (MenuElem (_("Loop range"), bind (mem_fun(*this, &Editor::set_loop_from_selection), true)));
2052 #ifdef FFT_ANALYSIS
2053 items.push_back (SeparatorElem());
2054 items.push_back (MenuElem (_("Spectral Analysis"), mem_fun(*this, &Editor::analyze_range_selection)));
2055 #endif
2057 items.push_back (SeparatorElem());
2058 items.push_back (MenuElem (_("Extend Range to End of Region"), bind (mem_fun(*this, &Editor::extend_selection_to_end_of_region), false)));
2059 items.push_back (MenuElem (_("Extend Range to Start of Region"), bind (mem_fun(*this, &Editor::extend_selection_to_start_of_region), false)));
2061 items.push_back (SeparatorElem());
2062 items.push_back (MenuElem (_("Convert to region in-place"), mem_fun(*this, &Editor::separate_region_from_selection)));
2063 items.push_back (MenuElem (_("Convert to region in region list"), mem_fun(*this, &Editor::new_region_from_selection)));
2065 items.push_back (SeparatorElem());
2066 items.push_back (MenuElem (_("Select all in range"), mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
2068 items.push_back (SeparatorElem());
2069 items.push_back (MenuElem (_("Set loop from selection"), bind (mem_fun(*this, &Editor::set_loop_from_selection), false)));
2070 items.push_back (MenuElem (_("Set punch from selection"), mem_fun(*this, &Editor::set_punch_from_selection)));
2072 items.push_back (SeparatorElem());
2073 items.push_back (MenuElem (_("Add Range Markers"), mem_fun (*this, &Editor::add_location_from_selection)));
2074 items.push_back (SeparatorElem());
2075 items.push_back (MenuElem (_("Crop region to range"), mem_fun(*this, &Editor::crop_region_to_selection)));
2076 items.push_back (MenuElem (_("Fill range with region"), mem_fun(*this, &Editor::region_fill_selection)));
2077 items.push_back (MenuElem (_("Duplicate range"), bind (mem_fun(*this, &Editor::duplicate_dialog), false)));
2078 items.push_back (MenuElem (_("Create chunk from range"), mem_fun(*this, &Editor::create_named_selection)));
2079 items.push_back (SeparatorElem());
2080 items.push_back (MenuElem (_("Consolidate range"), bind (mem_fun(*this, &Editor::bounce_range_selection), true, false)));
2081 items.push_back (MenuElem (_("Consolidate range with processing"), bind (mem_fun(*this, &Editor::bounce_range_selection), true, true)));
2082 items.push_back (MenuElem (_("Bounce range to region list"), bind (mem_fun(*this, &Editor::bounce_range_selection), false, false)));
2083 items.push_back (MenuElem (_("Bounce range to region list with processing"), bind (mem_fun(*this, &Editor::bounce_range_selection), false, true)));
2084 items.push_back (MenuElem (_("Export range"), mem_fun(*this, &Editor::export_selection)));
2087 void
2088 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
2090 using namespace Menu_Helpers;
2092 /* Playback */
2094 Menu *play_menu = manage (new Menu);
2095 MenuList& play_items = play_menu->items();
2096 play_menu->set_name ("ArdourContextMenu");
2098 play_items.push_back (MenuElem (_("Play from edit point"), mem_fun(*this, &Editor::play_from_edit_point)));
2099 play_items.push_back (MenuElem (_("Play from start"), mem_fun(*this, &Editor::play_from_start)));
2100 play_items.push_back (MenuElem (_("Play region"), mem_fun(*this, &Editor::play_selected_region)));
2101 play_items.push_back (SeparatorElem());
2102 play_items.push_back (MenuElem (_("Loop Region"), mem_fun(*this, &Editor::loop_selected_region)));
2104 edit_items.push_back (MenuElem (_("Play"), *play_menu));
2106 /* Selection */
2108 Menu *select_menu = manage (new Menu);
2109 MenuList& select_items = select_menu->items();
2110 select_menu->set_name ("ArdourContextMenu");
2112 select_items.push_back (MenuElem (_("Select All in track"), bind (mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2113 select_items.push_back (MenuElem (_("Select All"), bind (mem_fun(*this, &Editor::select_all), Selection::Set)));
2114 select_items.push_back (MenuElem (_("Invert selection in track"), mem_fun(*this, &Editor::invert_selection_in_track)));
2115 select_items.push_back (MenuElem (_("Invert selection"), mem_fun(*this, &Editor::invert_selection)));
2116 select_items.push_back (SeparatorElem());
2117 select_items.push_back (MenuElem (_("Set range to loop range"), mem_fun(*this, &Editor::set_selection_from_loop)));
2118 select_items.push_back (MenuElem (_("Set range to punch range"), mem_fun(*this, &Editor::set_selection_from_punch)));
2119 select_items.push_back (SeparatorElem());
2120 select_items.push_back (MenuElem (_("Select All After Edit Point"), bind (mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
2121 select_items.push_back (MenuElem (_("Select All Before Edit Point"), bind (mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
2122 select_items.push_back (MenuElem (_("Select All After Playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2123 select_items.push_back (MenuElem (_("Select All Before Playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2124 select_items.push_back (MenuElem (_("Select All Between Playhead & Edit Point"), bind (mem_fun(*this, &Editor::select_all_selectables_between), false)));
2125 select_items.push_back (MenuElem (_("Select All Within Playhead & Edit Point"), bind (mem_fun(*this, &Editor::select_all_selectables_between), true)));
2126 select_items.push_back (MenuElem (_("Select Range Between Playhead & Edit Point"), mem_fun(*this, &Editor::select_range_between)));
2128 select_items.push_back (SeparatorElem());
2130 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2132 /* Cut-n-Paste */
2134 Menu *cutnpaste_menu = manage (new Menu);
2135 MenuList& cutnpaste_items = cutnpaste_menu->items();
2136 cutnpaste_menu->set_name ("ArdourContextMenu");
2138 cutnpaste_items.push_back (MenuElem (_("Cut"), mem_fun(*this, &Editor::cut)));
2139 cutnpaste_items.push_back (MenuElem (_("Copy"), mem_fun(*this, &Editor::copy)));
2140 cutnpaste_items.push_back (MenuElem (_("Paste"), bind (mem_fun(*this, &Editor::paste), 1.0f)));
2142 cutnpaste_items.push_back (SeparatorElem());
2144 cutnpaste_items.push_back (MenuElem (_("Align"), bind (mem_fun(*this, &Editor::align), ARDOUR::SyncPoint)));
2145 cutnpaste_items.push_back (MenuElem (_("Align Relative"), bind (mem_fun(*this, &Editor::align_relative), ARDOUR::SyncPoint)));
2147 cutnpaste_items.push_back (SeparatorElem());
2149 cutnpaste_items.push_back (MenuElem (_("Insert chunk"), bind (mem_fun(*this, &Editor::paste_named_selection), 1.0f)));
2151 edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
2153 /* Adding new material */
2155 edit_items.push_back (SeparatorElem());
2156 edit_items.push_back (MenuElem (_("Insert Selected Region"), bind (mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
2157 edit_items.push_back (MenuElem (_("Insert Existing Audio"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
2159 /* Nudge track */
2161 Menu *nudge_menu = manage (new Menu());
2162 MenuList& nudge_items = nudge_menu->items();
2163 nudge_menu->set_name ("ArdourContextMenu");
2165 edit_items.push_back (SeparatorElem());
2166 nudge_items.push_back (MenuElem (_("Nudge entire track fwd"), (bind (mem_fun(*this, &Editor::nudge_track), false, true))));
2167 nudge_items.push_back (MenuElem (_("Nudge track after edit point fwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, true))));
2168 nudge_items.push_back (MenuElem (_("Nudge entire track bwd"), (bind (mem_fun(*this, &Editor::nudge_track), false, false))));
2169 nudge_items.push_back (MenuElem (_("Nudge track after edit point bwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, false))));
2171 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2174 void
2175 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
2177 using namespace Menu_Helpers;
2179 /* Playback */
2181 Menu *play_menu = manage (new Menu);
2182 MenuList& play_items = play_menu->items();
2183 play_menu->set_name ("ArdourContextMenu");
2185 play_items.push_back (MenuElem (_("Play from edit point"), mem_fun(*this, &Editor::play_from_edit_point)));
2186 play_items.push_back (MenuElem (_("Play from start"), mem_fun(*this, &Editor::play_from_start)));
2187 edit_items.push_back (MenuElem (_("Play"), *play_menu));
2189 /* Selection */
2191 Menu *select_menu = manage (new Menu);
2192 MenuList& select_items = select_menu->items();
2193 select_menu->set_name ("ArdourContextMenu");
2195 select_items.push_back (MenuElem (_("Select All in track"), bind (mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
2196 select_items.push_back (MenuElem (_("Select All"), bind (mem_fun(*this, &Editor::select_all), Selection::Set)));
2197 select_items.push_back (MenuElem (_("Invert selection in track"), mem_fun(*this, &Editor::invert_selection_in_track)));
2198 select_items.push_back (MenuElem (_("Invert selection"), mem_fun(*this, &Editor::invert_selection)));
2199 select_items.push_back (SeparatorElem());
2200 select_items.push_back (MenuElem (_("Select all after edit point"), bind (mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
2201 select_items.push_back (MenuElem (_("Select all before edit point"), bind (mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
2202 select_items.push_back (MenuElem (_("Select all after playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
2203 select_items.push_back (MenuElem (_("Select all before playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
2205 edit_items.push_back (MenuElem (_("Select"), *select_menu));
2207 /* Cut-n-Paste */
2209 Menu *cutnpaste_menu = manage (new Menu);
2210 MenuList& cutnpaste_items = cutnpaste_menu->items();
2211 cutnpaste_menu->set_name ("ArdourContextMenu");
2213 cutnpaste_items.push_back (MenuElem (_("Cut"), mem_fun(*this, &Editor::cut)));
2214 cutnpaste_items.push_back (MenuElem (_("Copy"), mem_fun(*this, &Editor::copy)));
2215 cutnpaste_items.push_back (MenuElem (_("Paste"), bind (mem_fun(*this, &Editor::paste), 1.0f)));
2217 Menu *nudge_menu = manage (new Menu());
2218 MenuList& nudge_items = nudge_menu->items();
2219 nudge_menu->set_name ("ArdourContextMenu");
2221 edit_items.push_back (SeparatorElem());
2222 nudge_items.push_back (MenuElem (_("Nudge entire track fwd"), (bind (mem_fun(*this, &Editor::nudge_track), false, true))));
2223 nudge_items.push_back (MenuElem (_("Nudge track after edit point fwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, true))));
2224 nudge_items.push_back (MenuElem (_("Nudge entire track bwd"), (bind (mem_fun(*this, &Editor::nudge_track), false, false))));
2225 nudge_items.push_back (MenuElem (_("Nudge track after edit point bwd"), (bind (mem_fun(*this, &Editor::nudge_track), true, false))));
2227 edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
2230 void
2231 Editor::set_snap_to (SnapType st)
2233 unsigned int snap_ind = (unsigned int)st;
2234 snap_type = st;
2236 if (snap_ind > snap_type_strings.size() - 1) {
2237 snap_ind = 0;
2238 snap_type = (SnapType)snap_ind;
2241 string str = snap_type_strings[snap_ind];
2243 if (str != snap_type_selector.get_active_text()) {
2244 snap_type_selector.set_active_text (str);
2247 instant_save ();
2249 switch (snap_type) {
2250 case SnapToAThirtysecondBeat:
2251 case SnapToASixteenthBeat:
2252 case SnapToAEighthBeat:
2253 case SnapToAQuarterBeat:
2254 case SnapToAThirdBeat:
2255 update_tempo_based_rulers ();
2256 break;
2258 case SnapToRegionStart:
2259 case SnapToRegionEnd:
2260 case SnapToRegionSync:
2261 case SnapToRegionBoundary:
2262 build_region_boundary_cache ();
2263 break;
2265 default:
2266 /* relax */
2267 break;
2271 void
2272 Editor::set_snap_mode (SnapMode mode)
2274 snap_mode = mode;
2275 string str = snap_mode_strings[(int)mode];
2277 if (str != snap_mode_selector.get_active_text ()) {
2278 snap_mode_selector.set_active_text (str);
2281 instant_save ();
2283 void
2284 Editor::set_edit_point_preference (EditPoint ep, bool force)
2286 bool changed = (_edit_point != ep);
2288 _edit_point = ep;
2289 string str = edit_point_strings[(int)ep];
2291 if (str != edit_point_selector.get_active_text ()) {
2292 edit_point_selector.set_active_text (str);
2295 set_canvas_cursor ();
2297 if (!force && !changed) {
2298 return;
2301 switch (zoom_focus) {
2302 case ZoomFocusMouse:
2303 case ZoomFocusPlayhead:
2304 case ZoomFocusEdit:
2305 switch (_edit_point) {
2306 case EditAtMouse:
2307 set_zoom_focus (ZoomFocusMouse);
2308 break;
2309 case EditAtPlayhead:
2310 set_zoom_focus (ZoomFocusPlayhead);
2311 break;
2312 case EditAtSelectedMarker:
2313 set_zoom_focus (ZoomFocusEdit);
2314 break;
2316 break;
2317 default:
2318 break;
2321 const char* action=NULL;
2323 switch (_edit_point) {
2324 case EditAtPlayhead:
2325 action = "edit-at-playhead";
2326 break;
2327 case EditAtSelectedMarker:
2328 action = "edit-at-marker";
2329 break;
2330 case EditAtMouse:
2331 action = "edit-at-mouse";
2332 break;
2335 Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2336 if (act) {
2337 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2340 nframes64_t foo;
2341 bool in_track_canvas;
2343 if (!mouse_frame (foo, in_track_canvas)) {
2344 in_track_canvas = false;
2347 reset_canvas_action_sensitivity (in_track_canvas);
2349 instant_save ();
2353 Editor::set_state (const XMLNode& node)
2355 const XMLProperty* prop;
2356 XMLNode* geometry;
2357 int x, y, xoff, yoff;
2358 Gdk::Geometry g;
2360 if ((prop = node.property ("id")) != 0) {
2361 _id = prop->value ();
2364 g.base_width = default_width;
2365 g.base_height = default_height;
2366 x = 1;
2367 y = 1;
2368 xoff = 0;
2369 yoff = 21;
2371 if ((geometry = find_named_node (node, "geometry")) != 0) {
2373 XMLProperty* prop;
2375 if ((prop = geometry->property("x_size")) == 0) {
2376 prop = geometry->property ("x-size");
2378 if (prop) {
2379 g.base_width = atoi(prop->value());
2381 if ((prop = geometry->property("y_size")) == 0) {
2382 prop = geometry->property ("y-size");
2384 if (prop) {
2385 g.base_height = atoi(prop->value());
2388 if ((prop = geometry->property ("x_pos")) == 0) {
2389 prop = geometry->property ("x-pos");
2391 if (prop) {
2392 x = atoi (prop->value());
2395 if ((prop = geometry->property ("y_pos")) == 0) {
2396 prop = geometry->property ("y-pos");
2398 if (prop) {
2399 y = atoi (prop->value());
2402 if ((prop = geometry->property ("x_off")) == 0) {
2403 prop = geometry->property ("x-off");
2405 if (prop) {
2406 xoff = atoi (prop->value());
2408 if ((prop = geometry->property ("y_off")) == 0) {
2409 prop = geometry->property ("y-off");
2411 if (prop) {
2412 yoff = atoi (prop->value());
2417 set_default_size (g.base_width, g.base_height);
2418 move (x, y);
2420 if (session && (prop = node.property ("playhead"))) {
2421 nframes64_t pos;
2422 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2423 playhead_cursor->set_position (pos);
2424 } else {
2425 playhead_cursor->set_position (0);
2427 /* reset_x_origin() doesn't work right here, since the old
2428 position may be zero already, and it does nothing in such
2429 circumstances.
2432 leftmost_frame = 0;
2433 horizontal_adjustment.set_value (0);
2436 if ((prop = node.property ("mixer-width"))) {
2437 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2440 if ((prop = node.property ("zoom-focus"))) {
2441 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2444 if ((prop = node.property ("zoom"))) {
2445 reset_zoom (PBD::atof (prop->value()));
2448 if ((prop = node.property ("snap-to"))) {
2449 set_snap_to ((SnapType) atoi (prop->value()));
2452 if ((prop = node.property ("snap-mode"))) {
2453 set_snap_mode ((SnapMode) atoi (prop->value()));
2456 if ((prop = node.property ("edit-point"))) {
2457 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2460 if ((prop = node.property ("mouse-mode"))) {
2461 MouseMode m = str2mousemode(prop->value());
2462 mouse_mode = MouseMode ((int) m + 1); /* lie, force mode switch */
2463 set_mouse_mode (m, true);
2464 } else {
2465 mouse_mode = MouseGain; /* lie, to force the mode switch */
2466 set_mouse_mode (MouseObject, true);
2469 if ((prop = node.property ("show-waveforms"))) {
2470 bool yn = (prop->value() == "yes");
2471 _show_waveforms = !yn;
2472 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-waveform-visible"));
2473 if (act) {
2474 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2475 /* do it twice to force the change */
2476 tact->set_active (!yn);
2477 tact->set_active (yn);
2481 if ((prop = node.property ("show-waveforms-recording"))) {
2482 bool yn = (prop->value() == "yes");
2483 _show_waveforms_recording = !yn;
2484 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleWaveformsWhileRecording"));
2485 if (act) {
2486 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2487 /* do it twice to force the change */
2488 tact->set_active (!yn);
2489 tact->set_active (yn);
2493 if ((prop = node.property ("show-measures"))) {
2494 bool yn = (prop->value() == "yes");
2495 _show_measures = !yn;
2496 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2497 if (act) {
2498 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2499 /* do it twice to force the change */
2500 tact->set_active (!yn);
2501 tact->set_active (yn);
2505 if ((prop = node.property ("follow-playhead"))) {
2506 bool yn = (prop->value() == "yes");
2507 set_follow_playhead (yn);
2508 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2509 if (act) {
2510 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2511 if (tact->get_active() != yn) {
2512 tact->set_active (yn);
2517 if ((prop = node.property ("stationary-playhead"))) {
2518 bool yn = (prop->value() == "yes");
2519 set_stationary_playhead (yn);
2520 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2521 if (act) {
2522 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2523 if (tact->get_active() != yn) {
2524 tact->set_active (yn);
2529 if ((prop = node.property ("region-list-sort-type"))) {
2530 region_list_sort_type = (Editing::RegionListSortType) -1; // force change
2531 reset_region_list_sort_type(str2regionlistsorttype(prop->value()));
2534 if ((prop = node.property ("xfades-visible"))) {
2535 bool yn = (prop->value() == "yes");
2536 _xfade_visibility = !yn;
2537 // set_xfade_visibility (yn);
2540 if ((prop = node.property ("show-editor-mixer"))) {
2542 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2543 if (act) {
2545 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2546 bool yn = (prop->value() == X_("yes"));
2548 /* do it twice to force the change */
2550 tact->set_active (!yn);
2551 tact->set_active (yn);
2556 return 0;
2559 XMLNode&
2560 Editor::get_state ()
2562 XMLNode* node = new XMLNode ("Editor");
2563 char buf[32];
2565 _id.print (buf, sizeof (buf));
2566 node->add_property ("id", buf);
2568 if (is_realized()) {
2569 Glib::RefPtr<Gdk::Window> win = get_window();
2571 int x, y, xoff, yoff, width, height;
2572 win->get_root_origin(x, y);
2573 win->get_position(xoff, yoff);
2574 win->get_size(width, height);
2576 XMLNode* geometry = new XMLNode ("geometry");
2578 snprintf(buf, sizeof(buf), "%d", width);
2579 geometry->add_property("x_size", string(buf));
2580 snprintf(buf, sizeof(buf), "%d", height);
2581 geometry->add_property("y_size", string(buf));
2582 snprintf(buf, sizeof(buf), "%d", x);
2583 geometry->add_property("x_pos", string(buf));
2584 snprintf(buf, sizeof(buf), "%d", y);
2585 geometry->add_property("y_pos", string(buf));
2586 snprintf(buf, sizeof(buf), "%d", xoff);
2587 geometry->add_property("x_off", string(buf));
2588 snprintf(buf, sizeof(buf), "%d", yoff);
2589 geometry->add_property("y_off", string(buf));
2590 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2591 geometry->add_property("edit_pane_pos", string(buf));
2593 node->add_child_nocopy (*geometry);
2596 maybe_add_mixer_strip_width (*node);
2598 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2599 node->add_property ("zoom-focus", buf);
2600 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2601 node->add_property ("zoom", buf);
2602 snprintf (buf, sizeof(buf), "%d", (int) snap_type);
2603 node->add_property ("snap-to", buf);
2604 snprintf (buf, sizeof(buf), "%d", (int) snap_mode);
2605 node->add_property ("snap-mode", buf);
2607 node->add_property ("edit-point", enum_2_string (_edit_point));
2609 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2610 node->add_property ("playhead", buf);
2612 node->add_property ("show-waveforms", _show_waveforms ? "yes" : "no");
2613 node->add_property ("show-waveforms-recording", _show_waveforms_recording ? "yes" : "no");
2614 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2615 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2616 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2617 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2618 node->add_property ("region-list-sort-type", enum2str(region_list_sort_type));
2619 node->add_property ("mouse-mode", enum2str(mouse_mode));
2621 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2622 if (act) {
2623 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2624 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2627 return *node;
2632 TimeAxisView *
2633 Editor::trackview_by_y_position (double y)
2635 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2637 TimeAxisView *tv;
2639 if ((tv = (*iter)->covers_y_position (y)) != 0) {
2640 return tv;
2644 return 0;
2647 void
2648 Editor::snap_to (nframes64_t& start, int32_t direction, bool for_mark)
2650 if (!session || snap_mode == SnapOff) {
2651 return;
2654 snap_to_internal (start, direction, for_mark);
2657 void
2658 Editor::snap_to_internal (nframes64_t& start, int32_t direction, bool for_mark)
2660 Location* before = 0;
2661 Location* after = 0;
2663 const nframes64_t one_second = session->frame_rate();
2664 const nframes64_t one_minute = session->frame_rate() * 60;
2665 const nframes64_t one_smpte_second = (nframes64_t)(rint(session->smpte_frames_per_second()) * session->frames_per_smpte_frame());
2666 nframes64_t one_smpte_minute = (nframes64_t)(rint(session->smpte_frames_per_second()) * session->frames_per_smpte_frame() * 60);
2667 nframes64_t presnap = start;
2669 switch (snap_type) {
2670 case SnapToCDFrame:
2671 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2672 start = (nframes64_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2673 } else {
2674 start = (nframes64_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2676 break;
2678 case SnapToSMPTEFrame:
2679 if (((direction == 0) && (fmod((double)start, (double)session->frames_per_smpte_frame()) > (session->frames_per_smpte_frame() / 2))) || (direction > 0)) {
2680 start = (nframes64_t) (ceil ((double) start / session->frames_per_smpte_frame()) * session->frames_per_smpte_frame());
2681 } else {
2682 start = (nframes64_t) (floor ((double) start / session->frames_per_smpte_frame()) * session->frames_per_smpte_frame());
2684 break;
2686 case SnapToSMPTESeconds:
2687 if (session->smpte_offset_negative())
2689 start += session->smpte_offset ();
2690 } else {
2691 start -= session->smpte_offset ();
2693 if (((direction == 0) && (start % one_smpte_second > one_smpte_second / 2)) || direction > 0) {
2694 start = (nframes64_t) ceil ((double) start / one_smpte_second) * one_smpte_second;
2695 } else {
2696 start = (nframes64_t) floor ((double) start / one_smpte_second) * one_smpte_second;
2699 if (session->smpte_offset_negative())
2701 start -= session->smpte_offset ();
2702 } else {
2703 start += session->smpte_offset ();
2705 break;
2707 case SnapToSMPTEMinutes:
2708 if (session->smpte_offset_negative())
2710 start += session->smpte_offset ();
2711 } else {
2712 start -= session->smpte_offset ();
2714 if (((direction == 0) && (start % one_smpte_minute > one_smpte_minute / 2)) || direction > 0) {
2715 start = (nframes64_t) ceil ((double) start / one_smpte_minute) * one_smpte_minute;
2716 } else {
2717 start = (nframes64_t) floor ((double) start / one_smpte_minute) * one_smpte_minute;
2719 if (session->smpte_offset_negative())
2721 start -= session->smpte_offset ();
2722 } else {
2723 start += session->smpte_offset ();
2725 break;
2727 case SnapToSeconds:
2728 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2729 start = (nframes64_t) ceil ((double) start / one_second) * one_second;
2730 } else {
2731 start = (nframes64_t) floor ((double) start / one_second) * one_second;
2733 break;
2735 case SnapToMinutes:
2736 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2737 start = (nframes64_t) ceil ((double) start / one_minute) * one_minute;
2738 } else {
2739 start = (nframes64_t) floor ((double) start / one_minute) * one_minute;
2741 break;
2743 case SnapToBar:
2744 start = session->tempo_map().round_to_bar (start, direction);
2745 break;
2747 case SnapToBeat:
2748 start = session->tempo_map().round_to_beat (start, direction);
2749 break;
2751 case SnapToAThirtysecondBeat:
2752 start = session->tempo_map().round_to_beat_subdivision (start, 32);
2753 break;
2755 case SnapToASixteenthBeat:
2756 start = session->tempo_map().round_to_beat_subdivision (start, 16);
2757 break;
2759 case SnapToAEighthBeat:
2760 start = session->tempo_map().round_to_beat_subdivision (start, 8);
2761 break;
2763 case SnapToAQuarterBeat:
2764 start = session->tempo_map().round_to_beat_subdivision (start, 4);
2765 break;
2767 case SnapToAThirdBeat:
2768 start = session->tempo_map().round_to_beat_subdivision (start, 3);
2769 break;
2771 case SnapToMark:
2772 if (for_mark) {
2773 return;
2776 before = session->locations()->first_location_before (start);
2777 after = session->locations()->first_location_after (start);
2779 if (direction < 0) {
2780 if (before) {
2781 start = before->start();
2782 } else {
2783 start = 0;
2785 } else if (direction > 0) {
2786 if (after) {
2787 start = after->start();
2788 } else {
2789 start = session->current_end_frame();
2791 } else {
2792 if (before) {
2793 if (after) {
2794 /* find nearest of the two */
2795 if ((start - before->start()) < (after->start() - start)) {
2796 start = before->start();
2797 } else {
2798 start = after->start();
2800 } else {
2801 start = before->start();
2803 } else if (after) {
2804 start = after->start();
2805 } else {
2806 /* relax */
2809 break;
2811 case SnapToRegionStart:
2812 case SnapToRegionEnd:
2813 case SnapToRegionSync:
2814 case SnapToRegionBoundary:
2815 if (!region_boundary_cache.empty()) {
2816 vector<nframes64_t>::iterator i;
2818 if (direction > 0) {
2819 i = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2820 } else {
2821 i = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2824 if (i != region_boundary_cache.end()) {
2826 /* lower bound doesn't quite to the right thing for our purposes */
2828 if (direction < 0 && i != region_boundary_cache.begin()) {
2829 --i;
2832 start = *i;
2834 } else {
2835 start = region_boundary_cache.back();
2838 break;
2841 switch (snap_mode) {
2842 case SnapNormal:
2843 return;
2845 case SnapMagnetic:
2847 if (presnap > start) {
2848 if (presnap > (start + unit_to_frame(snap_threshold))) {
2849 start = presnap;
2852 } else if (presnap < start) {
2853 if (presnap < (start - unit_to_frame(snap_threshold))) {
2854 start = presnap;
2858 default:
2859 /* handled at entry */
2860 return;
2865 void
2866 Editor::setup_toolbar ()
2868 string pixmap_path;
2870 /* Mode Buttons (tool selection) */
2872 vector<ToggleButton *> mouse_mode_buttons;
2874 mouse_move_button.add (*(manage (new Image (::get_icon("tool_object")))));
2875 mouse_move_button.set_relief(Gtk::RELIEF_NONE);
2876 mouse_mode_buttons.push_back (&mouse_move_button);
2878 if (!Profile->get_sae()) {
2879 mouse_select_button.add (*(manage (new Image (get_xpm("tool_range.xpm")))));
2880 mouse_select_button.set_relief(Gtk::RELIEF_NONE);
2881 mouse_mode_buttons.push_back (&mouse_select_button);
2883 mouse_gain_button.add (*(manage (new Image (::get_icon("tool_gain")))));
2884 mouse_gain_button.set_relief(Gtk::RELIEF_NONE);
2885 mouse_mode_buttons.push_back (&mouse_gain_button);
2888 mouse_zoom_button.add (*(manage (new Image (::get_icon("tool_zoom")))));
2889 mouse_zoom_button.set_relief(Gtk::RELIEF_NONE);
2890 mouse_mode_buttons.push_back (&mouse_zoom_button);
2891 mouse_timefx_button.add (*(manage (new Image (::get_icon("tool_stretch")))));
2892 mouse_timefx_button.set_relief(Gtk::RELIEF_NONE);
2893 mouse_mode_buttons.push_back (&mouse_timefx_button);
2894 mouse_audition_button.add (*(manage (new Image (::get_icon("tool_audition")))));
2895 mouse_audition_button.set_relief(Gtk::RELIEF_NONE);
2896 mouse_mode_buttons.push_back (&mouse_audition_button);
2898 mouse_mode_button_set = new GroupedButtons (mouse_mode_buttons);
2900 HBox* mode_box = manage(new HBox);
2901 mode_box->set_border_width (2);
2902 mode_box->set_spacing(4);
2903 mouse_mode_button_box.set_spacing(1);
2904 mouse_mode_button_box.pack_start(mouse_move_button, true, true);
2905 if (!Profile->get_sae()) {
2906 mouse_mode_button_box.pack_start(mouse_select_button, true, true);
2908 mouse_mode_button_box.pack_start(mouse_zoom_button, true, true);
2909 if (!Profile->get_sae()) {
2910 mouse_mode_button_box.pack_start(mouse_gain_button, true, true);
2912 mouse_mode_button_box.pack_start(mouse_timefx_button, true, true);
2913 mouse_mode_button_box.pack_start(mouse_audition_button, true, true);
2914 mouse_mode_button_box.set_homogeneous(true);
2916 vector<string> edit_mode_strings;
2917 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2918 if (!Profile->get_sae()) {
2919 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2921 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2923 edit_mode_selector.set_name ("EditModeSelector");
2924 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2925 edit_mode_selector.signal_changed().connect (mem_fun(*this, &Editor::edit_mode_selection_done));
2927 mode_box->pack_start(edit_mode_selector);
2928 mode_box->pack_start(mouse_mode_button_box);
2930 mouse_mode_tearoff = manage (new TearOff (*mode_box));
2931 mouse_mode_tearoff->set_name ("MouseModeBase");
2933 if (Profile->get_sae()) {
2934 mouse_mode_tearoff->set_can_be_torn_off (false);
2937 mouse_mode_tearoff->Detach.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2938 &mouse_mode_tearoff->tearoff_window()));
2939 mouse_mode_tearoff->Attach.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2940 &mouse_mode_tearoff->tearoff_window(), 1));
2941 mouse_mode_tearoff->Hidden.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2942 &mouse_mode_tearoff->tearoff_window()));
2943 mouse_mode_tearoff->Visible.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2944 &mouse_mode_tearoff->tearoff_window(), 1));
2946 mouse_move_button.set_name ("MouseModeButton");
2947 mouse_select_button.set_name ("MouseModeButton");
2948 mouse_gain_button.set_name ("MouseModeButton");
2949 mouse_zoom_button.set_name ("MouseModeButton");
2950 mouse_timefx_button.set_name ("MouseModeButton");
2951 mouse_audition_button.set_name ("MouseModeButton");
2953 ARDOUR_UI::instance()->tooltips().set_tip (mouse_move_button, _("Select/Move Objects"));
2954 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Select/Move Ranges"));
2955 ARDOUR_UI::instance()->tooltips().set_tip (mouse_gain_button, _("Draw Gain Automation"));
2956 ARDOUR_UI::instance()->tooltips().set_tip (mouse_zoom_button, _("Select Zoom Range"));
2957 ARDOUR_UI::instance()->tooltips().set_tip (mouse_timefx_button, _("Stretch/Shrink Regions"));
2958 ARDOUR_UI::instance()->tooltips().set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2960 mouse_move_button.unset_flags (CAN_FOCUS);
2961 mouse_select_button.unset_flags (CAN_FOCUS);
2962 mouse_gain_button.unset_flags (CAN_FOCUS);
2963 mouse_zoom_button.unset_flags (CAN_FOCUS);
2964 mouse_timefx_button.unset_flags (CAN_FOCUS);
2965 mouse_audition_button.unset_flags (CAN_FOCUS);
2967 mouse_select_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseRange));
2968 mouse_select_button.signal_button_release_event().connect (mem_fun(*this, &Editor::mouse_select_button_release));
2970 mouse_move_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseObject));
2971 mouse_gain_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseGain));
2972 mouse_zoom_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseZoom));
2973 mouse_timefx_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseTimeFX));
2974 mouse_audition_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseAudition));
2976 // mouse_move_button.set_active (true);
2979 /* Zoom */
2981 zoom_box.set_spacing (1);
2982 zoom_box.set_border_width (0);
2984 zoom_in_button.set_name ("EditorTimeButton");
2985 zoom_in_button.set_size_request(-1,16);
2986 zoom_in_button.add (*(manage (new Image (::get_icon("zoom_in")))));
2987 zoom_in_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::temporal_zoom_step), false));
2988 ARDOUR_UI::instance()->tooltips().set_tip (zoom_in_button, _("Zoom In"));
2990 zoom_out_button.set_name ("EditorTimeButton");
2991 zoom_out_button.set_size_request(-1,16);
2992 zoom_out_button.add (*(manage (new Image (::get_icon("zoom_out")))));
2993 zoom_out_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::temporal_zoom_step), true));
2994 ARDOUR_UI::instance()->tooltips().set_tip (zoom_out_button, _("Zoom Out"));
2996 zoom_out_full_button.set_name ("EditorTimeButton");
2997 zoom_out_full_button.set_size_request(-1,16);
2998 zoom_out_full_button.add (*(manage (new Image (::get_icon("zoom_full")))));
2999 zoom_out_full_button.signal_clicked().connect (mem_fun(*this, &Editor::temporal_zoom_session));
3000 ARDOUR_UI::instance()->tooltips().set_tip (zoom_out_full_button, _("Zoom to Session"));
3002 zoom_focus_selector.set_name ("ZoomFocusSelector");
3003 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
3004 zoom_focus_selector.signal_changed().connect (mem_fun(*this, &Editor::zoom_focus_selection_done));
3005 ARDOUR_UI::instance()->tooltips().set_tip (zoom_focus_selector, _("Zoom focus"));
3007 zoom_box.pack_start (zoom_focus_selector, true, true);
3008 zoom_box.pack_start (zoom_out_button, false, false);
3009 zoom_box.pack_start (zoom_in_button, false, false);
3010 zoom_box.pack_start (zoom_out_full_button, false, false);
3012 snap_box.set_spacing (1);
3013 snap_box.set_border_width (2);
3015 snap_type_selector.set_name ("SnapTypeSelector");
3016 set_popdown_strings (snap_type_selector, snap_type_strings, true);
3017 snap_type_selector.signal_changed().connect (mem_fun(*this, &Editor::snap_type_selection_done));
3018 ARDOUR_UI::instance()->tooltips().set_tip (snap_type_selector, _("Snap/Grid Units"));
3020 snap_mode_selector.set_name ("SnapModeSelector");
3021 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
3022 snap_mode_selector.signal_changed().connect (mem_fun(*this, &Editor::snap_mode_selection_done));
3023 ARDOUR_UI::instance()->tooltips().set_tip (snap_mode_selector, _("Snap/Grid Mode"));
3025 edit_point_selector.set_name ("EditPointSelector");
3026 set_popdown_strings (edit_point_selector, edit_point_strings, true);
3027 edit_point_selector.signal_changed().connect (mem_fun(*this, &Editor::edit_point_selection_done));
3028 ARDOUR_UI::instance()->tooltips().set_tip (edit_point_selector, _("Edit point"));
3030 snap_box.pack_start (edit_point_clock, false, false);
3031 snap_box.pack_start (snap_mode_selector, false, false);
3032 snap_box.pack_start (snap_type_selector, false, false);
3033 snap_box.pack_start (edit_point_selector, false, false);
3035 /* Nudge */
3037 HBox *nudge_box = manage (new HBox);
3038 nudge_box->set_spacing(1);
3039 nudge_box->set_border_width (2);
3041 nudge_forward_button.signal_button_release_event().connect (mem_fun(*this, &Editor::nudge_forward_release), false);
3042 nudge_backward_button.signal_button_release_event().connect (mem_fun(*this, &Editor::nudge_backward_release), false);
3044 nudge_box->pack_start (nudge_backward_button, false, false);
3045 nudge_box->pack_start (nudge_forward_button, false, false);
3046 nudge_box->pack_start (nudge_clock, false, false);
3049 /* Pack everything in... */
3051 HBox* hbox = new HBox;
3052 hbox->set_spacing(10);
3054 tools_tearoff = new TearOff (*hbox);
3055 tools_tearoff->set_name ("MouseModeBase");
3057 if (Profile->get_sae()) {
3058 tools_tearoff->set_can_be_torn_off (false);
3061 tools_tearoff->Detach.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
3062 &tools_tearoff->tearoff_window()));
3063 tools_tearoff->Attach.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
3064 &tools_tearoff->tearoff_window(), 0));
3065 tools_tearoff->Hidden.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
3066 &tools_tearoff->tearoff_window()));
3067 tools_tearoff->Visible.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
3068 &tools_tearoff->tearoff_window(), 0));
3070 toolbar_hbox.set_spacing (10);
3071 toolbar_hbox.set_border_width (1);
3073 toolbar_hbox.pack_start (*mouse_mode_tearoff, false, false);
3074 toolbar_hbox.pack_start (*tools_tearoff, false, false);
3077 hbox->pack_start (snap_box, false, false);
3078 // hbox->pack_start (zoom_box, false, false);
3079 hbox->pack_start (*nudge_box, false, false);
3081 hbox->show_all ();
3083 toolbar_base.set_name ("ToolBarBase");
3084 toolbar_base.add (toolbar_hbox);
3086 toolbar_frame.set_shadow_type (SHADOW_OUT);
3087 toolbar_frame.set_name ("BaseFrame");
3088 toolbar_frame.add (toolbar_base);
3092 Editor::convert_drop_to_paths (vector<ustring>& paths,
3093 const RefPtr<Gdk::DragContext>& context,
3094 gint x,
3095 gint y,
3096 const SelectionData& data,
3097 guint info,
3098 guint time)
3101 if (session == 0) {
3102 return -1;
3105 vector<ustring> uris = data.get_uris();
3107 if (uris.empty()) {
3109 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3110 are actually URI lists. So do it by hand.
3113 if (data.get_target() != "text/plain") {
3114 return -1;
3117 /* Parse the "uri-list" format that Nautilus provides,
3118 where each pathname is delimited by \r\n.
3120 THERE MAY BE NO NULL TERMINATING CHAR!!!
3123 ustring txt = data.get_text();
3124 const char* p;
3125 const char* q;
3127 p = (const char *) malloc (txt.length() + 1);
3128 txt.copy ((char *) p, txt.length(), 0);
3129 ((char*)p)[txt.length()] = '\0';
3131 while (p)
3133 if (*p != '#')
3135 while (g_ascii_isspace (*p))
3136 p++;
3138 q = p;
3139 while (*q && (*q != '\n') && (*q != '\r')) {
3140 q++;
3143 if (q > p)
3145 q--;
3146 while (q > p && g_ascii_isspace (*q))
3147 q--;
3149 if (q > p)
3151 uris.push_back (ustring (p, q - p + 1));
3155 p = strchr (p, '\n');
3156 if (p)
3157 p++;
3160 free ((void*)p);
3162 if (uris.empty()) {
3163 return -1;
3167 for (vector<ustring>::iterator i = uris.begin(); i != uris.end(); ++i) {
3169 if ((*i).substr (0,7) == "file://") {
3172 ustring p = *i;
3173 PBD::url_decode (p);
3175 // scan forward past three slashes
3177 ustring::size_type slashcnt = 0;
3178 ustring::size_type n = 0;
3179 ustring::iterator x = p.begin();
3181 while (slashcnt < 3 && x != p.end()) {
3182 if ((*x) == '/') {
3183 slashcnt++;
3184 } else if (slashcnt == 3) {
3185 break;
3187 ++n;
3188 ++x;
3191 if (slashcnt != 3 || x == p.end()) {
3192 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3193 continue;
3196 paths.push_back (p.substr (n - 1));
3200 return 0;
3203 void
3204 Editor::new_tempo_section ()
3209 void
3210 Editor::map_transport_state ()
3212 ENSURE_GUI_THREAD (mem_fun(*this, &Editor::map_transport_state));
3214 if (session->transport_stopped()) {
3215 have_pending_keyboard_selection = false;
3218 update_loop_range_view (true);
3221 /* UNDO/REDO */
3223 Editor::State::State ()
3225 selection = new Selection;
3228 Editor::State::~State ()
3230 delete selection;
3233 UndoAction
3234 Editor::get_memento () const
3236 State *state = new State;
3238 store_state (*state);
3239 return bind (mem_fun (*(const_cast<Editor*>(this)), &Editor::restore_state), state);
3242 void
3243 Editor::store_state (State& state) const
3245 *state.selection = *selection;
3248 void
3249 Editor::restore_state (State *state)
3251 if (*selection == *state->selection) {
3252 return;
3255 *selection = *state->selection;
3256 time_selection_changed ();
3257 region_selection_changed ();
3259 /* XXX other selection change handlers? */
3262 void
3263 Editor::begin_reversible_command (string name)
3265 if (session) {
3266 // before = &get_state();
3267 session->begin_reversible_command (name);
3271 void
3272 Editor::commit_reversible_command ()
3274 if (session) {
3275 // session->commit_reversible_command (new MementoCommand<Editor>(*this, before, &get_state()));
3276 session->commit_reversible_command ();
3280 void
3281 Editor::set_edit_group_solo (Route& route, bool yn)
3283 RouteGroup *edit_group;
3285 if ((edit_group = route.edit_group()) != 0) {
3286 edit_group->apply (&Route::set_solo, yn, this);
3287 } else {
3288 route.set_solo (yn, this);
3292 void
3293 Editor::set_edit_group_mute (Route& route, bool yn)
3295 RouteGroup *edit_group = 0;
3297 if ((edit_group == route.edit_group()) != 0) {
3298 edit_group->apply (&Route::set_mute, yn, this);
3299 } else {
3300 route.set_mute (yn, this);
3304 void
3305 Editor::history_changed ()
3307 string label;
3309 if (undo_action && session) {
3310 if (session->undo_depth() == 0) {
3311 label = _("Undo");
3312 } else {
3313 label = string_compose(_("Undo (%1)"), session->next_undo());
3315 undo_action->property_label() = label;
3318 if (redo_action && session) {
3319 if (session->redo_depth() == 0) {
3320 label = _("Redo");
3321 } else {
3322 label = string_compose(_("Redo (%1)"), session->next_redo());
3324 redo_action->property_label() = label;
3328 void
3329 Editor::duplicate_dialog (bool with_dialog)
3331 float times = 1.0f;
3333 if (mouse_mode == MouseRange) {
3334 if (selection->time.length() == 0) {
3335 return;
3339 RegionSelection rs;
3340 get_regions_for_action (rs);
3342 if (mouse_mode != MouseRange) {
3344 if (rs.empty()) {
3345 return;
3349 if (with_dialog) {
3351 ArdourDialog win ("Duplication Dialog");
3352 Label label (_("Number of Duplications:"));
3353 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3354 SpinButton spinner (adjustment, 0.0, 1);
3355 HBox hbox;
3357 win.get_vbox()->set_spacing (12);
3358 win.get_vbox()->pack_start (hbox);
3359 hbox.set_border_width (6);
3360 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3362 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3363 place, visually. so do this by hand.
3366 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3367 spinner.signal_activate().connect (sigc::bind (mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3368 spinner.grab_focus();
3370 hbox.show ();
3371 label.show ();
3372 spinner.show ();
3374 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3375 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3376 win.set_default_response (RESPONSE_ACCEPT);
3378 win.set_position (WIN_POS_MOUSE);
3380 spinner.grab_focus ();
3382 switch (win.run ()) {
3383 case RESPONSE_ACCEPT:
3384 break;
3385 default:
3386 return;
3389 times = adjustment.get_value();
3392 if (mouse_mode == MouseRange) {
3393 duplicate_selection (times);
3394 } else {
3395 duplicate_some_regions (rs, times);
3399 void
3400 Editor::show_verbose_canvas_cursor ()
3402 verbose_canvas_cursor->raise_to_top();
3403 verbose_canvas_cursor->show();
3404 verbose_cursor_visible = true;
3407 void
3408 Editor::hide_verbose_canvas_cursor ()
3410 verbose_canvas_cursor->hide();
3411 verbose_cursor_visible = false;
3414 double
3415 Editor::clamp_verbose_cursor_x (double x)
3417 if (x < 0) {
3418 x = 0;
3419 } else {
3420 x = min (canvas_width - 200.0, x);
3422 return x;
3425 double
3426 Editor::clamp_verbose_cursor_y (double y)
3428 if (y < canvas_timebars_vsize) {
3429 y = canvas_timebars_vsize;
3430 } else {
3431 y = min (canvas_height - 50, y);
3433 return y;
3436 void
3437 Editor::set_verbose_canvas_cursor (const string & txt, double x, double y)
3439 verbose_canvas_cursor->property_text() = txt.c_str();
3440 /* don't get too close to the edge */
3441 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (x);
3442 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (y);
3445 void
3446 Editor::set_verbose_canvas_cursor_text (const string & txt)
3448 verbose_canvas_cursor->property_text() = txt.c_str();
3451 void
3452 Editor::set_edit_mode (EditMode m)
3454 Config->set_edit_mode (m);
3457 void
3458 Editor::cycle_edit_mode ()
3460 switch (Config->get_edit_mode()) {
3461 case Slide:
3462 if (Profile->get_sae()) {
3463 Config->set_edit_mode (Lock);
3464 } else {
3465 Config->set_edit_mode (Splice);
3467 break;
3468 case Splice:
3469 Config->set_edit_mode (Lock);
3470 break;
3471 case Lock:
3472 Config->set_edit_mode (Slide);
3473 break;
3477 void
3478 Editor::edit_mode_selection_done ()
3480 if (session == 0) {
3481 return;
3484 string choice = edit_mode_selector.get_active_text();
3485 EditMode mode = Slide;
3487 if (choice == _("Splice Edit")) {
3488 mode = Splice;
3489 } else if (choice == _("Slide Edit")) {
3490 mode = Slide;
3491 } else if (choice == _("Lock Edit")) {
3492 mode = Lock;
3495 Config->set_edit_mode (mode);
3498 void
3499 Editor::snap_type_selection_done ()
3501 string choice = snap_type_selector.get_active_text();
3502 SnapType snaptype = SnapToBeat;
3504 if (choice == _("Beats/3")) {
3505 snaptype = SnapToAThirdBeat;
3506 } else if (choice == _("Beats/4")) {
3507 snaptype = SnapToAQuarterBeat;
3508 } else if (choice == _("Beats/8")) {
3509 snaptype = SnapToAEighthBeat;
3510 } else if (choice == _("Beats/16")) {
3511 snaptype = SnapToASixteenthBeat;
3512 } else if (choice == _("Beats/32")) {
3513 snaptype = SnapToAThirtysecondBeat;
3514 } else if (choice == _("Beats")) {
3515 snaptype = SnapToBeat;
3516 } else if (choice == _("Bars")) {
3517 snaptype = SnapToBar;
3518 } else if (choice == _("Marks")) {
3519 snaptype = SnapToMark;
3520 } else if (choice == _("Region starts")) {
3521 snaptype = SnapToRegionStart;
3522 } else if (choice == _("Region ends")) {
3523 snaptype = SnapToRegionEnd;
3524 } else if (choice == _("Region bounds")) {
3525 snaptype = SnapToRegionBoundary;
3526 } else if (choice == _("Region syncs")) {
3527 snaptype = SnapToRegionSync;
3528 } else if (choice == _("CD Frames")) {
3529 snaptype = SnapToCDFrame;
3530 } else if (choice == _("SMPTE Frames")) {
3531 snaptype = SnapToSMPTEFrame;
3532 } else if (choice == _("SMPTE Seconds")) {
3533 snaptype = SnapToSMPTESeconds;
3534 } else if (choice == _("SMPTE Minutes")) {
3535 snaptype = SnapToSMPTEMinutes;
3536 } else if (choice == _("Seconds")) {
3537 snaptype = SnapToSeconds;
3538 } else if (choice == _("Minutes")) {
3539 snaptype = SnapToMinutes;
3542 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3543 if (ract) {
3544 ract->set_active ();
3548 void
3549 Editor::snap_mode_selection_done ()
3551 string choice = snap_mode_selector.get_active_text();
3552 SnapMode mode = SnapNormal;
3554 if (choice == _("No Grid")) {
3555 mode = SnapOff;
3556 } else if (choice == _("Grid")) {
3557 mode = SnapNormal;
3558 } else if (choice == _("Magnetic")) {
3559 mode = SnapMagnetic;
3562 RefPtr<RadioAction> ract = snap_mode_action (mode);
3564 if (ract) {
3565 ract->set_active (true);
3569 void
3570 Editor::cycle_edit_point (bool with_marker)
3572 switch (_edit_point) {
3573 case EditAtMouse:
3574 set_edit_point_preference (EditAtPlayhead);
3575 break;
3576 case EditAtPlayhead:
3577 if (with_marker) {
3578 set_edit_point_preference (EditAtSelectedMarker);
3579 } else {
3580 set_edit_point_preference (EditAtMouse);
3582 break;
3583 case EditAtSelectedMarker:
3584 set_edit_point_preference (EditAtMouse);
3585 break;
3589 void
3590 Editor::edit_point_selection_done ()
3592 string choice = edit_point_selector.get_active_text();
3593 EditPoint ep = EditAtSelectedMarker;
3595 if (choice == _("Marker")) {
3596 set_edit_point_preference (EditAtSelectedMarker);
3597 } else if (choice == _("Playhead")) {
3598 set_edit_point_preference (EditAtPlayhead);
3599 } else {
3600 set_edit_point_preference (EditAtMouse);
3603 RefPtr<RadioAction> ract = edit_point_action (ep);
3605 if (ract) {
3606 ract->set_active (true);
3610 void
3611 Editor::zoom_focus_selection_done ()
3613 string choice = zoom_focus_selector.get_active_text();
3614 ZoomFocus focus_type = ZoomFocusLeft;
3616 if (choice == _("Left")) {
3617 focus_type = ZoomFocusLeft;
3618 } else if (choice == _("Right")) {
3619 focus_type = ZoomFocusRight;
3620 } else if (choice == _("Center")) {
3621 focus_type = ZoomFocusCenter;
3622 } else if (choice == _("Playhead")) {
3623 focus_type = ZoomFocusPlayhead;
3624 } else if (choice == _("Mouse")) {
3625 focus_type = ZoomFocusMouse;
3626 } else if (choice == _("Active Mark")) {
3627 focus_type = ZoomFocusEdit;
3630 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3632 if (ract) {
3633 ract->set_active ();
3637 gint
3638 Editor::edit_controls_button_release (GdkEventButton* ev)
3640 if (Keyboard::is_context_menu_event (ev)) {
3641 ARDOUR_UI::instance()->add_route (this);
3643 return TRUE;
3646 gint
3647 Editor::mouse_select_button_release (GdkEventButton* ev)
3649 /* this handles just right-clicks */
3651 if (ev->button != 3) {
3652 return false;
3655 return true;
3658 Editor::TrackViewList *
3659 Editor::get_valid_views (TimeAxisView* track, RouteGroup* group)
3661 TrackViewList *v;
3662 TrackViewList::iterator i;
3664 v = new TrackViewList;
3666 if (track == 0 && group == 0) {
3668 /* all views */
3670 for (i = track_views.begin(); i != track_views.end (); ++i) {
3671 v->push_back (*i);
3674 } else if ((track != 0 && group == 0) || (track != 0 && group != 0 && !group->is_active())) {
3676 /* just the view for this track
3679 v->push_back (track);
3681 } else {
3683 /* views for all tracks in the edit group */
3685 for (i = track_views.begin(); i != track_views.end (); ++i) {
3687 if (group == 0 || (*i)->edit_group() == group) {
3688 v->push_back (*i);
3693 return v;
3696 void
3697 Editor::set_zoom_focus (ZoomFocus f)
3699 string str = zoom_focus_strings[(int)f];
3701 if (str != zoom_focus_selector.get_active_text()) {
3702 zoom_focus_selector.set_active_text (str);
3705 if (zoom_focus != f) {
3706 zoom_focus = f;
3708 ZoomFocusChanged (); /* EMIT_SIGNAL */
3710 instant_save ();
3714 void
3715 Editor::ensure_float (Window& win)
3717 win.set_transient_for (*this);
3720 void
3721 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3723 /* recover or initialize pane positions. do this here rather than earlier because
3724 we don't want the positions to change the child allocations, which they seem to do.
3727 int pos;
3728 XMLProperty* prop;
3729 char buf[32];
3730 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3731 int width, height;
3732 static int32_t done;
3733 XMLNode* geometry;
3735 width = default_width;
3736 height = default_height;
3738 if ((geometry = find_named_node (*node, "geometry")) != 0) {
3740 if ((prop = geometry->property ("x_size")) == 0) {
3741 prop = geometry->property ("x-size");
3743 if (prop) {
3744 width = atoi (prop->value());
3746 if ((prop = geometry->property ("y_size")) == 0) {
3747 prop = geometry->property ("y-size");
3749 if (prop) {
3750 height = atoi (prop->value());
3754 if (which == static_cast<Paned*> (&edit_pane)) {
3756 if (done) {
3757 return;
3760 if (!geometry || (prop = geometry->property ("edit_pane_pos")) == 0) {
3761 /* initial allocation is 90% to canvas, 10% to notebook */
3762 pos = (int) floor (alloc.get_width() * 0.90f);
3763 snprintf (buf, sizeof(buf), "%d", pos);
3764 } else {
3765 pos = atoi (prop->value());
3768 if ((done = GTK_WIDGET(edit_pane.gobj())->allocation.width > pos)) {
3769 edit_pane.set_position (pos);
3770 pre_maximal_pane_position = pos;
3775 void
3776 Editor::detach_tearoff (Box* b, Window* w)
3778 if (tools_tearoff->torn_off() &&
3779 mouse_mode_tearoff->torn_off()) {
3780 top_hbox.remove (toolbar_frame);
3784 void
3785 Editor::reattach_tearoff (Box* b, Window* w, int32_t n)
3787 if (toolbar_frame.get_parent() == 0) {
3788 top_hbox.pack_end (toolbar_frame);
3792 void
3793 Editor::set_show_measures (bool yn)
3795 if (_show_measures != yn) {
3796 hide_measures ();
3798 if ((_show_measures = yn) == true) {
3799 if (tempo_lines) {
3800 tempo_lines->show();
3802 draw_measures ();
3804 instant_save ();
3808 void
3809 Editor::toggle_follow_playhead ()
3811 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3812 if (act) {
3813 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3814 set_follow_playhead (tact->get_active());
3818 void
3819 Editor::set_follow_playhead (bool yn)
3821 if (_follow_playhead != yn) {
3822 if ((_follow_playhead = yn) == true) {
3823 /* catch up */
3824 update_current_screen ();
3826 instant_save ();
3830 void
3831 Editor::toggle_stationary_playhead ()
3833 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3834 if (act) {
3835 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3836 set_stationary_playhead (tact->get_active());
3840 void
3841 Editor::set_stationary_playhead (bool yn)
3843 if (_stationary_playhead != yn) {
3844 if ((_stationary_playhead = yn) == true) {
3845 /* catch up */
3846 update_current_screen ();
3848 instant_save ();
3852 void
3853 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3855 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3856 if (xfade) {
3857 xfade->set_active (!xfade->active());
3861 void
3862 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3864 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3865 if (xfade) {
3866 xfade->set_follow_overlap (!xfade->following_overlap());
3870 void
3871 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3873 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3875 if (!xfade) {
3876 return;
3879 CrossfadeEditor cew (*session, xfade, xfade->fade_in().get_min_y(), 1.0);
3881 ensure_float (cew);
3883 switch (cew.run ()) {
3884 case RESPONSE_ACCEPT:
3885 break;
3886 default:
3887 return;
3890 cew.apply ();
3891 xfade->StateChanged (Change (~0));
3894 PlaylistSelector&
3895 Editor::playlist_selector () const
3897 return *_playlist_selector;
3900 nframes64_t
3901 Editor::get_nudge_distance (nframes64_t pos, nframes64_t& next)
3903 nframes64_t ret;
3905 ret = nudge_clock.current_duration (pos);
3906 next = ret + 1; /* XXXX fix me */
3908 return ret;
3911 void
3912 Editor::end_location_changed (Location* location)
3914 ENSURE_GUI_THREAD (bind (mem_fun(*this, &Editor::end_location_changed), location));
3915 //reset_scrolling_region ();
3916 nframes64_t session_span = location->start() + (nframes64_t) floorf (current_page_frames() * 0.10f);
3917 horizontal_adjustment.set_upper (session_span / frames_per_unit);
3921 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3923 ArdourDialog dialog ("playlist deletion dialog");
3924 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3925 "If left alone, no audio files used by it will be cleaned.\n"
3926 "If deleted, audio files used by it alone by will cleaned."),
3927 pl->name()));
3929 dialog.set_position (WIN_POS_CENTER);
3930 dialog.get_vbox()->pack_start (label);
3932 label.show ();
3934 dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT);
3935 dialog.add_button (_("Keep playlist"), RESPONSE_REJECT);
3936 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3938 switch (dialog.run ()) {
3939 case RESPONSE_ACCEPT:
3940 /* delete the playlist */
3941 return 0;
3942 break;
3944 case RESPONSE_REJECT:
3945 /* keep the playlist */
3946 return 1;
3947 break;
3949 default:
3950 break;
3953 return -1;
3956 bool
3957 Editor::audio_region_selection_covers (nframes64_t where)
3959 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3960 if ((*a)->region()->covers (where)) {
3961 return true;
3965 return false;
3968 void
3969 Editor::prepare_for_cleanup ()
3971 cut_buffer->clear_regions ();
3972 cut_buffer->clear_playlists ();
3974 selection->clear_regions ();
3975 selection->clear_playlists ();
3977 no_region_list_redisplay = true;
3980 void
3981 Editor::finish_cleanup ()
3983 no_region_list_redisplay = false;
3984 redisplay_regions ();
3987 Location*
3988 Editor::transport_loop_location()
3990 if (session) {
3991 return session->locations()->auto_loop_location();
3992 } else {
3993 return 0;
3997 Location*
3998 Editor::transport_punch_location()
4000 if (session) {
4001 return session->locations()->auto_punch_location();
4002 } else {
4003 return 0;
4007 bool
4008 Editor::control_layout_scroll (GdkEventScroll* ev)
4010 switch (ev->direction) {
4011 case GDK_SCROLL_UP:
4012 scroll_tracks_up_line ();
4013 return true;
4014 break;
4016 case GDK_SCROLL_DOWN:
4017 scroll_tracks_down_line ();
4018 return true;
4020 default:
4021 /* no left/right handling yet */
4022 break;
4025 return false;
4029 /** A new snapshot has been selected.
4031 void
4032 Editor::snapshot_display_selection_changed ()
4034 if (snapshot_display.get_selection()->count_selected_rows() > 0) {
4036 TreeModel::iterator i = snapshot_display.get_selection()->get_selected();
4038 Glib::ustring snap_name = (*i)[snapshot_display_columns.real_name];
4040 if (snap_name.length() == 0) {
4041 return;
4044 if (session->snap_name() == snap_name) {
4045 return;
4048 ARDOUR_UI::instance()->load_session(session->path(), string (snap_name));
4052 bool
4053 Editor::snapshot_display_button_press (GdkEventButton* ev)
4055 if (ev->button == 3) {
4056 /* Right-click on the snapshot list. Work out which snapshot it
4057 was over. */
4058 Gtk::TreeModel::Path path;
4059 Gtk::TreeViewColumn* col;
4060 int cx;
4061 int cy;
4062 snapshot_display.get_path_at_pos ((int) ev->x, (int) ev->y, path, col, cx, cy);
4063 Gtk::TreeModel::iterator iter = snapshot_display_model->get_iter (path);
4064 if (iter) {
4065 Gtk::TreeModel::Row row = *iter;
4066 popup_snapshot_context_menu (ev->button, ev->time, row[snapshot_display_columns.real_name]);
4068 return true;
4071 return false;
4075 /** Pop up the snapshot display context menu.
4076 * @param button Button used to open the menu.
4077 * @param time Menu open time.
4078 * @snapshot_name Name of the snapshot that the menu click was over.
4081 void
4082 Editor::popup_snapshot_context_menu (int button, int32_t time, Glib::ustring snapshot_name)
4084 using namespace Menu_Helpers;
4086 MenuList& items (snapshot_context_menu.items());
4087 items.clear ();
4089 const bool modification_allowed = (session->snap_name() != snapshot_name && session->name() != snapshot_name);
4091 items.push_back (MenuElem (_("Remove"), bind (mem_fun (*this, &Editor::remove_snapshot), snapshot_name)));
4092 if (!modification_allowed) {
4093 items.back().set_sensitive (false);
4096 items.push_back (MenuElem (_("Rename"), bind (mem_fun (*this, &Editor::rename_snapshot), snapshot_name)));
4097 if (!modification_allowed) {
4098 items.back().set_sensitive (false);
4101 snapshot_context_menu.popup (button, time);
4104 void
4105 Editor::rename_snapshot (Glib::ustring old_name)
4107 ArdourPrompter prompter(true);
4109 string new_name;
4111 prompter.set_name ("Prompter");
4112 prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
4113 prompter.set_prompt (_("New name of snapshot"));
4114 prompter.set_initial_text (old_name);
4116 if (prompter.run() == RESPONSE_ACCEPT) {
4117 prompter.get_result (new_name);
4118 if (new_name.length()) {
4119 session->rename_state (old_name, new_name);
4120 redisplay_snapshots ();
4126 void
4127 Editor::remove_snapshot (Glib::ustring name)
4129 vector<string> choices;
4131 std::string prompt = string_compose (_("Do you really want to remove snapshot \"%1\" ?\n(cannot be undone)"), name);
4133 choices.push_back (_("No, do nothing."));
4134 choices.push_back (_("Yes, remove it."));
4136 Gtkmm2ext::Choice prompter (prompt, choices);
4138 if (prompter.run () == 1) {
4139 session->remove_state (name);
4140 redisplay_snapshots ();
4144 void
4145 Editor::redisplay_snapshots ()
4147 if (session == 0) {
4148 return;
4151 vector<string*>* states;
4153 if ((states = session->possible_states()) == 0) {
4154 return;
4157 snapshot_display_model->clear ();
4159 for (vector<string*>::iterator i = states->begin(); i != states->end(); ++i) {
4160 string statename = *(*i);
4161 TreeModel::Row row = *(snapshot_display_model->append());
4163 /* this lingers on in case we ever want to change the visible
4164 name of the snapshot.
4167 string display_name;
4168 display_name = statename;
4170 if (statename == session->snap_name()) {
4171 snapshot_display.get_selection()->select(row);
4174 row[snapshot_display_columns.visible_name] = display_name;
4175 row[snapshot_display_columns.real_name] = statename;
4178 delete states;
4181 void
4182 Editor::session_state_saved (string snap_name)
4184 ENSURE_GUI_THREAD (bind (mem_fun(*this, &Editor::session_state_saved), snap_name));
4185 redisplay_snapshots ();
4188 void
4189 Editor::maximise_editing_space ()
4191 mouse_mode_tearoff->set_visible (false);
4192 tools_tearoff->set_visible (false);
4194 pre_maximal_pane_position = edit_pane.get_position();
4195 pre_maximal_editor_width = this->get_width();
4197 if(post_maximal_pane_position == 0) {
4198 post_maximal_pane_position = edit_pane.get_width();
4201 fullscreen();
4203 if(post_maximal_editor_width) {
4204 edit_pane.set_position (post_maximal_pane_position -
4205 abs(post_maximal_editor_width - pre_maximal_editor_width));
4206 } else {
4207 edit_pane.set_position (post_maximal_pane_position);
4211 void
4212 Editor::restore_editing_space ()
4214 // user changed width of pane during fullscreen
4215 if(post_maximal_pane_position != edit_pane.get_position()) {
4216 post_maximal_pane_position = edit_pane.get_position();
4219 unfullscreen();
4221 mouse_mode_tearoff->set_visible (true);
4222 tools_tearoff->set_visible (true);
4223 post_maximal_editor_width = this->get_width();
4226 edit_pane.set_position (
4227 pre_maximal_pane_position + abs(this->get_width() - pre_maximal_editor_width)
4232 * Make new playlists for a given track and also any others that belong
4233 * to the same active edit group.
4234 * @param v Track.
4237 void
4238 Editor::new_playlists (TimeAxisView* v)
4240 begin_reversible_command (_("new playlists"));
4241 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4242 session->get_playlists(playlists);
4243 mapover_audio_tracks ( bind(mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v );
4244 commit_reversible_command ();
4249 * Use a copy of the current playlist 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::copy_playlists (TimeAxisView* v)
4257 begin_reversible_command (_("copy 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_copy_playlist), playlists), v );
4261 commit_reversible_command ();
4266 * Clear 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::clear_playlists (TimeAxisView* v)
4274 begin_reversible_command (_("clear playlists"));
4275 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4276 session->get_playlists(playlists);
4277 mapover_audio_tracks ( mem_fun (*this, &Editor::mapped_clear_playlist), v );
4278 commit_reversible_command ();
4281 void
4282 Editor::mapped_use_new_playlist (AudioTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4284 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4287 void
4288 Editor::mapped_use_copy_playlist (AudioTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4290 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4293 void
4294 Editor::mapped_clear_playlist (AudioTimeAxisView& atv, uint32_t sz)
4296 atv.clear_playlist ();
4299 bool
4300 Editor::on_key_press_event (GdkEventKey* ev)
4302 return key_press_focus_accelerator_handler (*this, ev);
4305 bool
4306 Editor::on_key_release_event (GdkEventKey* ev)
4308 return Gtk::Window::on_key_release_event (ev);
4309 // return key_press_focus_accelerator_handler (*this, ev);
4312 void
4313 Editor::reset_x_origin (nframes64_t frame)
4315 queue_visual_change (frame);
4318 void
4319 Editor::reset_zoom (double fpu)
4321 queue_visual_change (fpu);
4324 void
4325 Editor::reposition_and_zoom (nframes64_t frame, double fpu)
4327 //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
4328 reset_x_origin (frame);
4329 reset_zoom (fpu);
4331 if (!no_save_visual) {
4332 undo_visual_stack.push_back (current_visual_state(false));
4336 Editor::VisualState*
4337 Editor::current_visual_state (bool with_tracks)
4339 VisualState* vs = new VisualState;
4340 vs->y_position = vertical_adjustment.get_value();
4341 vs->frames_per_unit = frames_per_unit;
4342 vs->leftmost_frame = leftmost_frame;
4343 vs->zoom_focus = zoom_focus;
4344 vs->zoomed_to_region = zoomed_to_region;
4346 if (with_tracks) {
4347 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4348 vs->track_states.push_back (TAVState ((*i), &(*i)->get_state()));
4352 return vs;
4355 void
4356 Editor::undo_visual_state ()
4358 if (undo_visual_stack.empty()) {
4359 return;
4362 VisualState* vs = undo_visual_stack.back();
4363 undo_visual_stack.pop_back();
4364 use_visual_state (*vs);
4365 redo_visual_stack.push_back (vs);
4368 void
4369 Editor::redo_visual_state ()
4371 if (redo_visual_stack.empty()) {
4372 return;
4375 VisualState* vs = redo_visual_stack.back();
4376 redo_visual_stack.pop_back();
4377 use_visual_state (*vs);
4378 undo_visual_stack.push_back (vs);
4381 void
4382 Editor::swap_visual_state ()
4384 if (undo_visual_stack.empty()) {
4385 redo_visual_state ();
4386 } else {
4387 undo_visual_state ();
4391 void
4392 Editor::use_visual_state (VisualState& vs)
4394 no_save_visual = true;
4395 no_route_list_redisplay = true;
4397 vertical_adjustment.set_value (vs.y_position);
4399 set_zoom_focus (vs.zoom_focus);
4400 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4401 zoomed_to_region = vs.zoomed_to_region;
4403 for (list<TAVState>::iterator i = vs.track_states.begin(); i != vs.track_states.end(); ++i) {
4404 TrackViewList::iterator t;
4406 /* check if the track still exists - it could have been deleted */
4408 if ((t = find (track_views.begin(), track_views.end(), i->first)) != track_views.end()) {
4409 (*t)->set_state (*(i->second));
4413 if (!vs.track_states.empty()) {
4414 update_route_visibility ();
4417 no_route_list_redisplay = false;
4418 redisplay_route_list ();
4420 no_save_visual = false;
4423 void
4424 Editor::set_frames_per_unit (double fpu)
4426 /* this is the core function that controls the zoom level of the canvas. it is called
4427 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4430 if (fpu == frames_per_unit) {
4431 return;
4434 if (fpu < 1.0) {
4435 fpu = 1.0;
4439 /* don't allow zooms that fit more than the maximum number
4440 of frames into an 800 pixel wide space.
4443 if (max_frames / fpu < 800.0) {
4444 return;
4447 if (tempo_lines) {
4448 tempo_lines->tempo_map_changed();
4451 frames_per_unit = fpu;
4452 post_zoom ();
4455 void
4456 Editor::post_zoom ()
4458 nframes64_t cef = 0;
4460 // convert fpu to frame count
4462 nframes64_t frames = (nframes64_t) floor (frames_per_unit * canvas_width);
4464 if (frames_per_unit != zoom_range_clock.current_duration()) {
4465 zoom_range_clock.set (frames);
4468 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4469 if (!selection->tracks.empty()) {
4470 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4471 (*i)->reshow_selection (selection->time);
4473 } else {
4474 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4475 (*i)->reshow_selection (selection->time);
4479 update_loop_range_view (false);
4480 update_punch_range_view (false);
4482 if (playhead_cursor) {
4483 playhead_cursor->set_position (playhead_cursor->current_frame);
4486 leftmost_frame = (nframes64_t) floor (horizontal_adjustment.get_value() * frames_per_unit);
4488 ZoomChanged (); /* EMIT_SIGNAL */
4490 reset_hscrollbar_stepping ();
4492 if (session) {
4493 cef = session->current_end_frame() + (current_page_frames() / 10);// Add a little extra so we can see the end marker
4495 horizontal_adjustment.set_upper (cef / frames_per_unit);
4497 //reset_scrolling_region ();
4499 instant_save ();
4502 void
4503 Editor::queue_visual_change (nframes64_t where)
4505 pending_visual_change.pending = VisualChange::Type (pending_visual_change.pending | VisualChange::TimeOrigin);
4507 /* if we're moving beyond the end, make sure the upper limit of the horizontal adjustment
4508 can reach.
4511 if (where > session->current_end_frame()) {
4512 horizontal_adjustment.set_upper ((where + current_page_frames()) / frames_per_unit);
4515 pending_visual_change.time_origin = where;
4517 if (pending_visual_change.idle_handler_id < 0) {
4518 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4522 void
4523 Editor::queue_visual_change (double fpu)
4525 pending_visual_change.pending = VisualChange::Type (pending_visual_change.pending | VisualChange::ZoomLevel);
4526 pending_visual_change.frames_per_unit = fpu;
4528 if (pending_visual_change.idle_handler_id < 0) {
4529 pending_visual_change.idle_handler_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE, _idle_visual_changer, this, 0);
4534 Editor::_idle_visual_changer (void* arg)
4536 return static_cast<Editor*>(arg)->idle_visual_changer ();
4540 Editor::idle_visual_changer ()
4542 VisualChange::Type p = pending_visual_change.pending;
4543 pending_visual_change.pending = (VisualChange::Type) 0;
4545 double last_time_origin = horizontal_adjustment.get_value();
4546 if (p & VisualChange::ZoomLevel) {
4547 set_frames_per_unit (pending_visual_change.frames_per_unit);
4550 if (p & VisualChange::TimeOrigin) {
4551 horizontal_adjustment.set_value (pending_visual_change.time_origin / frames_per_unit);
4554 if (last_time_origin == horizontal_adjustment.get_value() ) {
4555 /* changed signal not emitted */
4556 update_fixed_rulers ();
4557 redisplay_tempo (true);
4559 // 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
4560 pending_visual_change.idle_handler_id = -1;
4561 return 0; /* this is always a one-shot call */
4564 struct EditorOrderTimeAxisSorter {
4565 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4566 return a->order < b->order;
4570 void
4571 Editor::sort_track_selection (TrackSelection* sel)
4573 EditorOrderTimeAxisSorter cmp;
4575 if (sel) {
4576 sel->sort (cmp);
4577 } else {
4578 selection->tracks.sort (cmp);
4582 nframes64_t
4583 Editor::get_preferred_edit_position (bool ignore_playhead)
4585 bool ignored;
4586 nframes64_t where = 0;
4587 EditPoint ep = _edit_point;
4589 if (entered_marker) {
4590 return entered_marker->position();
4593 if (ignore_playhead && ep == EditAtPlayhead) {
4594 ep = EditAtSelectedMarker;
4597 switch (ep) {
4598 case EditAtPlayhead:
4599 where = session->audible_frame();
4600 break;
4602 case EditAtSelectedMarker:
4603 if (!selection->markers.empty()) {
4604 bool is_start;
4605 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4606 if (loc) {
4607 if (is_start) {
4608 where = loc->start();
4609 } else {
4610 where = loc->end();
4612 break;
4615 /* fallthru */
4617 default:
4618 case EditAtMouse:
4619 if (!mouse_frame (where, ignored)) {
4620 /* XXX not right but what can we do ? */
4621 return 0;
4623 snap_to (where);
4624 break;
4627 return where;
4630 void
4631 Editor::set_loop_range (nframes64_t start, nframes64_t end, string cmd)
4633 if (!session) return;
4635 begin_reversible_command (cmd);
4637 Location* tll;
4639 if ((tll = transport_loop_location()) == 0) {
4640 Location* loc = new Location (start, end, _("Loop"), Location::IsAutoLoop);
4641 XMLNode &before = session->locations()->get_state();
4642 session->locations()->add (loc, true);
4643 session->set_auto_loop_location (loc);
4644 XMLNode &after = session->locations()->get_state();
4645 session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after));
4646 } else {
4647 XMLNode &before = tll->get_state();
4648 tll->set_hidden (false, this);
4649 tll->set (start, end);
4650 XMLNode &after = tll->get_state();
4651 session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4654 commit_reversible_command ();
4657 void
4658 Editor::set_punch_range (nframes64_t start, nframes64_t end, string cmd)
4660 if (!session) return;
4662 begin_reversible_command (cmd);
4664 Location* tpl;
4666 if ((tpl = transport_punch_location()) == 0) {
4667 Location* loc = new Location (start, end, _("Loop"), Location::IsAutoPunch);
4668 XMLNode &before = session->locations()->get_state();
4669 session->locations()->add (loc, true);
4670 session->set_auto_loop_location (loc);
4671 XMLNode &after = session->locations()->get_state();
4672 session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after));
4674 else {
4675 XMLNode &before = tpl->get_state();
4676 tpl->set_hidden (false, this);
4677 tpl->set (start, end);
4678 XMLNode &after = tpl->get_state();
4679 session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4682 commit_reversible_command ();
4685 void
4686 Editor::get_regions_at (RegionSelection& rs, nframes64_t where, const TrackSelection& ts) const
4688 const TrackSelection* tracks;
4690 if (ts.empty()) {
4691 tracks = &track_views;
4692 } else {
4693 tracks = &ts;
4696 for (TrackSelection::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4698 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*t);
4700 if (atv) {
4701 boost::shared_ptr<Diskstream> ds;
4702 boost::shared_ptr<Playlist> pl;
4704 if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()))) {
4706 Playlist::RegionList* regions = pl->regions_at ((nframes64_t) floor ( (double)where * ds->speed()));
4708 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4710 RegionView* rv = atv->audio_view()->find_view (*i);
4712 if (rv) {
4713 rs.add (rv);
4717 delete regions;
4723 void
4724 Editor::get_regions_after (RegionSelection& rs, nframes64_t where, const TrackSelection& ts) const
4726 const TrackSelection* tracks;
4728 if (ts.empty()) {
4729 tracks = &track_views;
4730 } else {
4731 tracks = &ts;
4734 for (TrackSelection::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4736 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*t);
4738 if (atv) {
4739 boost::shared_ptr<Diskstream> ds;
4740 boost::shared_ptr<Playlist> pl;
4742 if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()))) {
4744 Playlist::RegionList* regions = pl->regions_touched ((nframes64_t) floor ( (double)where * ds->speed()), max_frames);
4746 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4748 RegionView* rv = atv->audio_view()->find_view (*i);
4750 if (rv) {
4751 rs.push_back (rv);
4755 delete regions;
4761 void
4762 Editor::get_regions_for_action (RegionSelection& rs, bool allow_entered)
4764 if (selection->regions.empty()) {
4766 if (selection->tracks.empty()) {
4768 /* no regions or tracks selected
4771 if (entered_regionview && mouse_mode == Editing::MouseObject) {
4773 /* entered regionview is valid and we're in object mode -
4774 just use entered regionview
4777 rs.add (entered_regionview);
4780 return;
4782 } else {
4784 /* no regions selected, so get all regions at the edit point across
4785 all selected tracks.
4788 nframes64_t where = get_preferred_edit_position();
4789 get_regions_at (rs, where, selection->tracks);
4791 /* if the entered regionview wasn't selected and neither was its track
4792 then add it.
4795 if (entered_regionview != 0 &&
4796 !selection->selected (entered_regionview) &&
4797 !selection->selected (&entered_regionview->get_time_axis_view())) {
4798 rs.add (entered_regionview);
4802 } else {
4804 /* just use the selected regions */
4806 rs = selection->regions;
4808 /* if the entered regionview wasn't selected and we allow this sort of thing,
4809 then add it.
4812 if (allow_entered && entered_regionview && !selection->selected (entered_regionview)) {
4813 rs.add (entered_regionview);
4819 void
4820 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4823 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4825 RouteTimeAxisView* tatv;
4827 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4829 boost::shared_ptr<Playlist> pl;
4830 vector<boost::shared_ptr<Region> > results;
4831 RegionView* marv;
4832 boost::shared_ptr<Diskstream> ds;
4834 if ((ds = tatv->get_diskstream()) == 0) {
4835 /* bus */
4836 continue;
4839 if ((pl = (ds->playlist())) != 0) {
4840 pl->get_region_list_equivalent_regions (region, results);
4843 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4844 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4845 regions.push_back (marv);
4853 void
4854 Editor::show_rhythm_ferret ()
4856 if (rhythm_ferret == 0) {
4857 rhythm_ferret = new RhythmFerret(*this);
4860 rhythm_ferret->set_session (session);
4861 rhythm_ferret->show ();
4862 rhythm_ferret->present ();
4865 void
4866 Editor::first_idle ()
4868 MessageDialog* dialog = 0;
4870 if (track_views.size() > 1) {
4871 dialog = new MessageDialog (*this,
4872 _("Please wait while Ardour loads visual data"),
4873 true,
4874 Gtk::MESSAGE_INFO,
4875 Gtk::BUTTONS_NONE);
4876 dialog->present ();
4877 ARDOUR_UI::instance()->flush_pending ();
4880 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4881 (*t)->first_idle();
4884 // first idle adds route children (automation tracks), so we need to redisplay here
4885 redisplay_route_list();
4887 if (dialog) {
4888 delete dialog;
4891 _have_idled = true;
4894 void
4895 Editor::start_resize_line_ops ()
4897 #if 0
4898 old_resize_line_y = -1;
4899 resize_line_y = -1;
4900 need_resize_line = true;
4901 #endif
4904 void
4905 Editor::end_resize_line_ops ()
4907 #if 0
4908 need_resize_line = false;
4910 if (old_resize_line_y >= 0) {
4911 Gdk::Rectangle r (0, old_resize_line_y, (int) canvas_width, 3);
4912 Glib::RefPtr<Gdk::Window> win = get_window();
4913 cerr << "Final invalidation at " << old_resize_line_y << endl;
4914 win->invalidate_rect (r, false);
4916 #endif
4919 void
4920 Editor::queue_draw_resize_line (int at)
4922 #if 0
4923 Glib::RefPtr<Gdk::Window> win = get_window();
4925 resize_line_y = at;
4927 if (win && canvas_width) {
4929 int controls_width = controls_layout.get_width();
4930 int xroot, discard;
4932 controls_layout.get_window()->get_origin (xroot, discard);
4934 if (old_resize_line_y >= 0) {
4936 /* redraw where it used to be */
4939 Gdk::Rectangle r (0, old_resize_line_y - 1, controls_width + (int) canvas_width, 3);
4940 win->invalidate_rect (r, true);
4941 cerr << "invalidate " << xroot << "," << old_resize_line_y - 1 << ' '
4942 << controls_width + canvas_width << " x 3\n";
4945 /* draw where it is */
4947 Gdk::Rectangle r (0, at - 1, controls_width + (int) canvas_width, 3);
4948 win->invalidate_rect (r, true);
4950 #endif
4953 bool
4954 Editor::on_expose_event (GdkEventExpose* ev)
4956 /* cerr << "+++ editor expose "
4957 << ev->area.x << ',' << ev->area.y
4958 << ' '
4959 << ev->area.width << " x " << ev->area.height
4960 << " need reize ? " << need_resize_line
4961 << endl;
4963 bool ret = Window::on_expose_event (ev);
4965 #if 0
4966 if (need_resize_line) {
4968 int xroot, yroot, discard;
4969 int controls_width;
4971 /* Our root coordinates for drawing the line will be the left edge
4972 of the track controls, and the upper left edge of our own window.
4975 get_window()->get_origin (discard, yroot);
4976 controls_layout.get_window()->get_origin (xroot, discard);
4977 controls_width = controls_layout.get_width();
4979 GdkRectangle lr;
4980 GdkRectangle intersection;
4982 lr.x = 0;
4983 lr.y = resize_line_y;
4984 lr.width = controls_width + (int) canvas_width;
4985 lr.height = 3;
4987 if (gdk_rectangle_intersect (&lr, &ev->area, &intersection)) {
4989 Glib::RefPtr<Gtk::Style> style (get_style());
4990 Glib::RefPtr<Gdk::GC> black_gc (style->get_black_gc ());
4991 Glib::RefPtr<Gdk::GC> gc = wrap (black_gc->gobj_copy(), false);
4993 /* draw on root window */
4995 GdkWindow* win = gdk_get_default_root_window();
4997 gc->set_subwindow (Gdk::INCLUDE_INFERIORS);
4998 gc->set_line_attributes (3, Gdk::LINE_SOLID,
4999 Gdk::CAP_NOT_LAST,
5000 Gdk::JOIN_MITER);
5002 gdk_draw_line (win, gc->gobj(),
5004 resize_line_y,
5005 (int) canvas_width + controls_width,
5006 resize_line_y);
5007 #if 0
5008 cerr << "drew line @ " << xroot << ", " << yroot + resize_line_y
5009 << " to " << xroot + (int) canvas_width + controls_width
5010 << ", " << yroot + resize_line_y
5011 << endl;
5012 #endif
5013 old_resize_line_y = resize_line_y;
5014 cerr << "NEXT EXPOSE SHOULD BE AT " << old_resize_line_y << endl;
5015 } else {
5016 cerr << "no intersect with "
5017 << lr.x << ',' << lr.y
5018 << ' '
5019 << lr.width << " x " << lr.height
5020 << endl;
5024 //cerr << "--- editor expose\n";
5025 #endif
5027 return ret;
5030 static gboolean
5031 _idle_resizer (gpointer arg)
5033 return ((Editor*)arg)->idle_resize ();
5036 void
5037 Editor::add_to_idle_resize (TimeAxisView* view, uint32_t h)
5039 if (resize_idle_id < 0) {
5040 resize_idle_id = g_idle_add (_idle_resizer, this);
5043 resize_idle_target = h;
5045 pending_resizes.push_back (view);
5047 if (selection->selected (view) && !selection->tracks.empty()) {
5048 pending_resizes.insert (pending_resizes.end(), selection->tracks.begin(), selection->tracks.end());
5052 bool
5053 Editor::idle_resize ()
5055 for (vector<TimeAxisView*>::iterator i = pending_resizes.begin(); i != pending_resizes.end(); ++i) {
5056 (*i)->idle_resize (resize_idle_target);
5058 pending_resizes.clear();
5059 //flush_canvas ();
5060 resize_idle_id = -1;
5061 return false;