try to name/number new routes-from-templates to avoid colliding names if adding more...
[ardour2.git] / gtk2_ardour / editor.cc
blob0865854cf98afc0a67d4fd633f20ddd477fee1f3
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 = atol (prop->value().c_str());
2422 playhead_cursor->set_position (pos);
2423 } else {
2424 playhead_cursor->set_position (0);
2426 /* reset_x_origin() doesn't work right here, since the old
2427 position may be zero already, and it does nothing in such
2428 circumstances.
2431 leftmost_frame = 0;
2432 horizontal_adjustment.set_value (0);
2435 if ((prop = node.property ("mixer-width"))) {
2436 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2439 if ((prop = node.property ("zoom-focus"))) {
2440 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2443 if ((prop = node.property ("zoom"))) {
2444 reset_zoom (PBD::atof (prop->value()));
2447 if ((prop = node.property ("snap-to"))) {
2448 set_snap_to ((SnapType) atoi (prop->value()));
2451 if ((prop = node.property ("snap-mode"))) {
2452 set_snap_mode ((SnapMode) atoi (prop->value()));
2455 if ((prop = node.property ("edit-point"))) {
2456 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2459 if ((prop = node.property ("mouse-mode"))) {
2460 MouseMode m = str2mousemode(prop->value());
2461 mouse_mode = MouseMode ((int) m + 1); /* lie, force mode switch */
2462 set_mouse_mode (m, true);
2463 } else {
2464 mouse_mode = MouseGain; /* lie, to force the mode switch */
2465 set_mouse_mode (MouseObject, true);
2468 if ((prop = node.property ("show-waveforms"))) {
2469 bool yn = (prop->value() == "yes");
2470 _show_waveforms = !yn;
2471 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-waveform-visible"));
2472 if (act) {
2473 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2474 /* do it twice to force the change */
2475 tact->set_active (!yn);
2476 tact->set_active (yn);
2480 if ((prop = node.property ("show-waveforms-recording"))) {
2481 bool yn = (prop->value() == "yes");
2482 _show_waveforms_recording = !yn;
2483 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleWaveformsWhileRecording"));
2484 if (act) {
2485 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2486 /* do it twice to force the change */
2487 tact->set_active (!yn);
2488 tact->set_active (yn);
2492 if ((prop = node.property ("show-measures"))) {
2493 bool yn = (prop->value() == "yes");
2494 _show_measures = !yn;
2495 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2496 if (act) {
2497 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2498 /* do it twice to force the change */
2499 tact->set_active (!yn);
2500 tact->set_active (yn);
2504 if ((prop = node.property ("follow-playhead"))) {
2505 bool yn = (prop->value() == "yes");
2506 set_follow_playhead (yn);
2507 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2508 if (act) {
2509 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2510 if (tact->get_active() != yn) {
2511 tact->set_active (yn);
2516 if ((prop = node.property ("stationary-playhead"))) {
2517 bool yn = (prop->value() == "yes");
2518 set_stationary_playhead (yn);
2519 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2520 if (act) {
2521 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2522 if (tact->get_active() != yn) {
2523 tact->set_active (yn);
2528 if ((prop = node.property ("region-list-sort-type"))) {
2529 region_list_sort_type = (Editing::RegionListSortType) -1; // force change
2530 reset_region_list_sort_type(str2regionlistsorttype(prop->value()));
2533 if ((prop = node.property ("xfades-visible"))) {
2534 bool yn = (prop->value() == "yes");
2535 _xfade_visibility = !yn;
2536 // set_xfade_visibility (yn);
2539 if ((prop = node.property ("show-editor-mixer"))) {
2541 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2542 if (act) {
2544 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2545 bool yn = (prop->value() == X_("yes"));
2547 /* do it twice to force the change */
2549 tact->set_active (!yn);
2550 tact->set_active (yn);
2555 return 0;
2558 XMLNode&
2559 Editor::get_state ()
2561 XMLNode* node = new XMLNode ("Editor");
2562 char buf[32];
2564 _id.print (buf, sizeof (buf));
2565 node->add_property ("id", buf);
2567 if (is_realized()) {
2568 Glib::RefPtr<Gdk::Window> win = get_window();
2570 int x, y, xoff, yoff, width, height;
2571 win->get_root_origin(x, y);
2572 win->get_position(xoff, yoff);
2573 win->get_size(width, height);
2575 XMLNode* geometry = new XMLNode ("geometry");
2577 snprintf(buf, sizeof(buf), "%d", width);
2578 geometry->add_property("x_size", string(buf));
2579 snprintf(buf, sizeof(buf), "%d", height);
2580 geometry->add_property("y_size", string(buf));
2581 snprintf(buf, sizeof(buf), "%d", x);
2582 geometry->add_property("x_pos", string(buf));
2583 snprintf(buf, sizeof(buf), "%d", y);
2584 geometry->add_property("y_pos", string(buf));
2585 snprintf(buf, sizeof(buf), "%d", xoff);
2586 geometry->add_property("x_off", string(buf));
2587 snprintf(buf, sizeof(buf), "%d", yoff);
2588 geometry->add_property("y_off", string(buf));
2589 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2590 geometry->add_property("edit_pane_pos", string(buf));
2592 node->add_child_nocopy (*geometry);
2595 maybe_add_mixer_strip_width (*node);
2597 snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2598 node->add_property ("zoom-focus", buf);
2599 snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2600 node->add_property ("zoom", buf);
2601 snprintf (buf, sizeof(buf), "%d", (int) snap_type);
2602 node->add_property ("snap-to", buf);
2603 snprintf (buf, sizeof(buf), "%d", (int) snap_mode);
2604 node->add_property ("snap-mode", buf);
2606 node->add_property ("edit-point", enum_2_string (_edit_point));
2608 snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2609 node->add_property ("playhead", buf);
2611 node->add_property ("show-waveforms", _show_waveforms ? "yes" : "no");
2612 node->add_property ("show-waveforms-recording", _show_waveforms_recording ? "yes" : "no");
2613 node->add_property ("show-measures", _show_measures ? "yes" : "no");
2614 node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2615 node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2616 node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2617 node->add_property ("region-list-sort-type", enum2str(region_list_sort_type));
2618 node->add_property ("mouse-mode", enum2str(mouse_mode));
2620 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2621 if (act) {
2622 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2623 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2626 return *node;
2631 TimeAxisView *
2632 Editor::trackview_by_y_position (double y)
2634 for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2636 TimeAxisView *tv;
2638 if ((tv = (*iter)->covers_y_position (y)) != 0) {
2639 return tv;
2643 return 0;
2646 void
2647 Editor::snap_to (nframes64_t& start, int32_t direction, bool for_mark)
2649 if (!session || snap_mode == SnapOff) {
2650 return;
2653 snap_to_internal (start, direction, for_mark);
2656 void
2657 Editor::snap_to_internal (nframes64_t& start, int32_t direction, bool for_mark)
2659 Location* before = 0;
2660 Location* after = 0;
2662 const nframes64_t one_second = session->frame_rate();
2663 const nframes64_t one_minute = session->frame_rate() * 60;
2664 const nframes64_t one_smpte_second = (nframes64_t)(rint(session->smpte_frames_per_second()) * session->frames_per_smpte_frame());
2665 nframes64_t one_smpte_minute = (nframes64_t)(rint(session->smpte_frames_per_second()) * session->frames_per_smpte_frame() * 60);
2666 nframes64_t presnap = start;
2668 switch (snap_type) {
2669 case SnapToCDFrame:
2670 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2671 start = (nframes64_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2672 } else {
2673 start = (nframes64_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2675 break;
2677 case SnapToSMPTEFrame:
2678 if (((direction == 0) && (fmod((double)start, (double)session->frames_per_smpte_frame()) > (session->frames_per_smpte_frame() / 2))) || (direction > 0)) {
2679 start = (nframes64_t) (ceil ((double) start / session->frames_per_smpte_frame()) * session->frames_per_smpte_frame());
2680 } else {
2681 start = (nframes64_t) (floor ((double) start / session->frames_per_smpte_frame()) * session->frames_per_smpte_frame());
2683 break;
2685 case SnapToSMPTESeconds:
2686 if (session->smpte_offset_negative())
2688 start += session->smpte_offset ();
2689 } else {
2690 start -= session->smpte_offset ();
2692 if (((direction == 0) && (start % one_smpte_second > one_smpte_second / 2)) || direction > 0) {
2693 start = (nframes64_t) ceil ((double) start / one_smpte_second) * one_smpte_second;
2694 } else {
2695 start = (nframes64_t) floor ((double) start / one_smpte_second) * one_smpte_second;
2698 if (session->smpte_offset_negative())
2700 start -= session->smpte_offset ();
2701 } else {
2702 start += session->smpte_offset ();
2704 break;
2706 case SnapToSMPTEMinutes:
2707 if (session->smpte_offset_negative())
2709 start += session->smpte_offset ();
2710 } else {
2711 start -= session->smpte_offset ();
2713 if (((direction == 0) && (start % one_smpte_minute > one_smpte_minute / 2)) || direction > 0) {
2714 start = (nframes64_t) ceil ((double) start / one_smpte_minute) * one_smpte_minute;
2715 } else {
2716 start = (nframes64_t) floor ((double) start / one_smpte_minute) * one_smpte_minute;
2718 if (session->smpte_offset_negative())
2720 start -= session->smpte_offset ();
2721 } else {
2722 start += session->smpte_offset ();
2724 break;
2726 case SnapToSeconds:
2727 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2728 start = (nframes64_t) ceil ((double) start / one_second) * one_second;
2729 } else {
2730 start = (nframes64_t) floor ((double) start / one_second) * one_second;
2732 break;
2734 case SnapToMinutes:
2735 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2736 start = (nframes64_t) ceil ((double) start / one_minute) * one_minute;
2737 } else {
2738 start = (nframes64_t) floor ((double) start / one_minute) * one_minute;
2740 break;
2742 case SnapToBar:
2743 start = session->tempo_map().round_to_bar (start, direction);
2744 break;
2746 case SnapToBeat:
2747 start = session->tempo_map().round_to_beat (start, direction);
2748 break;
2750 case SnapToAThirtysecondBeat:
2751 start = session->tempo_map().round_to_beat_subdivision (start, 32);
2752 break;
2754 case SnapToASixteenthBeat:
2755 start = session->tempo_map().round_to_beat_subdivision (start, 16);
2756 break;
2758 case SnapToAEighthBeat:
2759 start = session->tempo_map().round_to_beat_subdivision (start, 8);
2760 break;
2762 case SnapToAQuarterBeat:
2763 start = session->tempo_map().round_to_beat_subdivision (start, 4);
2764 break;
2766 case SnapToAThirdBeat:
2767 start = session->tempo_map().round_to_beat_subdivision (start, 3);
2768 break;
2770 case SnapToMark:
2771 if (for_mark) {
2772 return;
2775 before = session->locations()->first_location_before (start);
2776 after = session->locations()->first_location_after (start);
2778 if (direction < 0) {
2779 if (before) {
2780 start = before->start();
2781 } else {
2782 start = 0;
2784 } else if (direction > 0) {
2785 if (after) {
2786 start = after->start();
2787 } else {
2788 start = session->current_end_frame();
2790 } else {
2791 if (before) {
2792 if (after) {
2793 /* find nearest of the two */
2794 if ((start - before->start()) < (after->start() - start)) {
2795 start = before->start();
2796 } else {
2797 start = after->start();
2799 } else {
2800 start = before->start();
2802 } else if (after) {
2803 start = after->start();
2804 } else {
2805 /* relax */
2808 break;
2810 case SnapToRegionStart:
2811 case SnapToRegionEnd:
2812 case SnapToRegionSync:
2813 case SnapToRegionBoundary:
2814 if (!region_boundary_cache.empty()) {
2815 vector<nframes64_t>::iterator i;
2817 if (direction > 0) {
2818 i = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2819 } else {
2820 i = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2823 if (i != region_boundary_cache.end()) {
2825 /* lower bound doesn't quite to the right thing for our purposes */
2827 if (direction < 0 && i != region_boundary_cache.begin()) {
2828 --i;
2831 start = *i;
2833 } else {
2834 start = region_boundary_cache.back();
2837 break;
2840 switch (snap_mode) {
2841 case SnapNormal:
2842 return;
2844 case SnapMagnetic:
2846 if (presnap > start) {
2847 if (presnap > (start + unit_to_frame(snap_threshold))) {
2848 start = presnap;
2851 } else if (presnap < start) {
2852 if (presnap < (start - unit_to_frame(snap_threshold))) {
2853 start = presnap;
2857 default:
2858 /* handled at entry */
2859 return;
2864 void
2865 Editor::setup_toolbar ()
2867 string pixmap_path;
2869 /* Mode Buttons (tool selection) */
2871 vector<ToggleButton *> mouse_mode_buttons;
2873 mouse_move_button.add (*(manage (new Image (::get_icon("tool_object")))));
2874 mouse_move_button.set_relief(Gtk::RELIEF_NONE);
2875 mouse_mode_buttons.push_back (&mouse_move_button);
2877 if (!Profile->get_sae()) {
2878 mouse_select_button.add (*(manage (new Image (get_xpm("tool_range.xpm")))));
2879 mouse_select_button.set_relief(Gtk::RELIEF_NONE);
2880 mouse_mode_buttons.push_back (&mouse_select_button);
2882 mouse_gain_button.add (*(manage (new Image (::get_icon("tool_gain")))));
2883 mouse_gain_button.set_relief(Gtk::RELIEF_NONE);
2884 mouse_mode_buttons.push_back (&mouse_gain_button);
2887 mouse_zoom_button.add (*(manage (new Image (::get_icon("tool_zoom")))));
2888 mouse_zoom_button.set_relief(Gtk::RELIEF_NONE);
2889 mouse_mode_buttons.push_back (&mouse_zoom_button);
2890 mouse_timefx_button.add (*(manage (new Image (::get_icon("tool_stretch")))));
2891 mouse_timefx_button.set_relief(Gtk::RELIEF_NONE);
2892 mouse_mode_buttons.push_back (&mouse_timefx_button);
2893 mouse_audition_button.add (*(manage (new Image (::get_icon("tool_audition")))));
2894 mouse_audition_button.set_relief(Gtk::RELIEF_NONE);
2895 mouse_mode_buttons.push_back (&mouse_audition_button);
2897 mouse_mode_button_set = new GroupedButtons (mouse_mode_buttons);
2899 HBox* mode_box = manage(new HBox);
2900 mode_box->set_border_width (2);
2901 mode_box->set_spacing(4);
2902 mouse_mode_button_box.set_spacing(1);
2903 mouse_mode_button_box.pack_start(mouse_move_button, true, true);
2904 if (!Profile->get_sae()) {
2905 mouse_mode_button_box.pack_start(mouse_select_button, true, true);
2907 mouse_mode_button_box.pack_start(mouse_zoom_button, true, true);
2908 if (!Profile->get_sae()) {
2909 mouse_mode_button_box.pack_start(mouse_gain_button, true, true);
2911 mouse_mode_button_box.pack_start(mouse_timefx_button, true, true);
2912 mouse_mode_button_box.pack_start(mouse_audition_button, true, true);
2913 mouse_mode_button_box.set_homogeneous(true);
2915 vector<string> edit_mode_strings;
2916 edit_mode_strings.push_back (edit_mode_to_string (Slide));
2917 if (!Profile->get_sae()) {
2918 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2920 edit_mode_strings.push_back (edit_mode_to_string (Lock));
2922 edit_mode_selector.set_name ("EditModeSelector");
2923 set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2924 edit_mode_selector.signal_changed().connect (mem_fun(*this, &Editor::edit_mode_selection_done));
2926 mode_box->pack_start(edit_mode_selector);
2927 mode_box->pack_start(mouse_mode_button_box);
2929 mouse_mode_tearoff = manage (new TearOff (*mode_box));
2930 mouse_mode_tearoff->set_name ("MouseModeBase");
2932 if (Profile->get_sae()) {
2933 mouse_mode_tearoff->set_can_be_torn_off (false);
2936 mouse_mode_tearoff->Detach.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2937 &mouse_mode_tearoff->tearoff_window()));
2938 mouse_mode_tearoff->Attach.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2939 &mouse_mode_tearoff->tearoff_window(), 1));
2940 mouse_mode_tearoff->Hidden.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2941 &mouse_mode_tearoff->tearoff_window()));
2942 mouse_mode_tearoff->Visible.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2943 &mouse_mode_tearoff->tearoff_window(), 1));
2945 mouse_move_button.set_name ("MouseModeButton");
2946 mouse_select_button.set_name ("MouseModeButton");
2947 mouse_gain_button.set_name ("MouseModeButton");
2948 mouse_zoom_button.set_name ("MouseModeButton");
2949 mouse_timefx_button.set_name ("MouseModeButton");
2950 mouse_audition_button.set_name ("MouseModeButton");
2952 ARDOUR_UI::instance()->tooltips().set_tip (mouse_move_button, _("Select/Move Objects"));
2953 ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Select/Move Ranges"));
2954 ARDOUR_UI::instance()->tooltips().set_tip (mouse_gain_button, _("Draw Gain Automation"));
2955 ARDOUR_UI::instance()->tooltips().set_tip (mouse_zoom_button, _("Select Zoom Range"));
2956 ARDOUR_UI::instance()->tooltips().set_tip (mouse_timefx_button, _("Stretch/Shrink Regions"));
2957 ARDOUR_UI::instance()->tooltips().set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2959 mouse_move_button.unset_flags (CAN_FOCUS);
2960 mouse_select_button.unset_flags (CAN_FOCUS);
2961 mouse_gain_button.unset_flags (CAN_FOCUS);
2962 mouse_zoom_button.unset_flags (CAN_FOCUS);
2963 mouse_timefx_button.unset_flags (CAN_FOCUS);
2964 mouse_audition_button.unset_flags (CAN_FOCUS);
2966 mouse_select_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseRange));
2967 mouse_select_button.signal_button_release_event().connect (mem_fun(*this, &Editor::mouse_select_button_release));
2969 mouse_move_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseObject));
2970 mouse_gain_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseGain));
2971 mouse_zoom_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseZoom));
2972 mouse_timefx_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseTimeFX));
2973 mouse_audition_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseAudition));
2975 // mouse_move_button.set_active (true);
2978 /* Zoom */
2980 zoom_box.set_spacing (1);
2981 zoom_box.set_border_width (0);
2983 zoom_in_button.set_name ("EditorTimeButton");
2984 zoom_in_button.set_size_request(-1,16);
2985 zoom_in_button.add (*(manage (new Image (::get_icon("zoom_in")))));
2986 zoom_in_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::temporal_zoom_step), false));
2987 ARDOUR_UI::instance()->tooltips().set_tip (zoom_in_button, _("Zoom In"));
2989 zoom_out_button.set_name ("EditorTimeButton");
2990 zoom_out_button.set_size_request(-1,16);
2991 zoom_out_button.add (*(manage (new Image (::get_icon("zoom_out")))));
2992 zoom_out_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::temporal_zoom_step), true));
2993 ARDOUR_UI::instance()->tooltips().set_tip (zoom_out_button, _("Zoom Out"));
2995 zoom_out_full_button.set_name ("EditorTimeButton");
2996 zoom_out_full_button.set_size_request(-1,16);
2997 zoom_out_full_button.add (*(manage (new Image (::get_icon("zoom_full")))));
2998 zoom_out_full_button.signal_clicked().connect (mem_fun(*this, &Editor::temporal_zoom_session));
2999 ARDOUR_UI::instance()->tooltips().set_tip (zoom_out_full_button, _("Zoom to Session"));
3001 zoom_focus_selector.set_name ("ZoomFocusSelector");
3002 set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
3003 zoom_focus_selector.signal_changed().connect (mem_fun(*this, &Editor::zoom_focus_selection_done));
3004 ARDOUR_UI::instance()->tooltips().set_tip (zoom_focus_selector, _("Zoom focus"));
3006 zoom_box.pack_start (zoom_focus_selector, true, true);
3007 zoom_box.pack_start (zoom_out_button, false, false);
3008 zoom_box.pack_start (zoom_in_button, false, false);
3009 zoom_box.pack_start (zoom_out_full_button, false, false);
3011 snap_box.set_spacing (1);
3012 snap_box.set_border_width (2);
3014 snap_type_selector.set_name ("SnapTypeSelector");
3015 set_popdown_strings (snap_type_selector, snap_type_strings, true);
3016 snap_type_selector.signal_changed().connect (mem_fun(*this, &Editor::snap_type_selection_done));
3017 ARDOUR_UI::instance()->tooltips().set_tip (snap_type_selector, _("Snap/Grid Units"));
3019 snap_mode_selector.set_name ("SnapModeSelector");
3020 set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
3021 snap_mode_selector.signal_changed().connect (mem_fun(*this, &Editor::snap_mode_selection_done));
3022 ARDOUR_UI::instance()->tooltips().set_tip (snap_mode_selector, _("Snap/Grid Mode"));
3024 edit_point_selector.set_name ("EditPointSelector");
3025 set_popdown_strings (edit_point_selector, edit_point_strings, true);
3026 edit_point_selector.signal_changed().connect (mem_fun(*this, &Editor::edit_point_selection_done));
3027 ARDOUR_UI::instance()->tooltips().set_tip (edit_point_selector, _("Edit point"));
3029 snap_box.pack_start (edit_point_clock, false, false);
3030 snap_box.pack_start (snap_mode_selector, false, false);
3031 snap_box.pack_start (snap_type_selector, false, false);
3032 snap_box.pack_start (edit_point_selector, false, false);
3034 /* Nudge */
3036 HBox *nudge_box = manage (new HBox);
3037 nudge_box->set_spacing(1);
3038 nudge_box->set_border_width (2);
3040 nudge_forward_button.signal_button_release_event().connect (mem_fun(*this, &Editor::nudge_forward_release), false);
3041 nudge_backward_button.signal_button_release_event().connect (mem_fun(*this, &Editor::nudge_backward_release), false);
3043 nudge_box->pack_start (nudge_backward_button, false, false);
3044 nudge_box->pack_start (nudge_forward_button, false, false);
3045 nudge_box->pack_start (nudge_clock, false, false);
3048 /* Pack everything in... */
3050 HBox* hbox = new HBox;
3051 hbox->set_spacing(10);
3053 tools_tearoff = new TearOff (*hbox);
3054 tools_tearoff->set_name ("MouseModeBase");
3056 if (Profile->get_sae()) {
3057 tools_tearoff->set_can_be_torn_off (false);
3060 tools_tearoff->Detach.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
3061 &tools_tearoff->tearoff_window()));
3062 tools_tearoff->Attach.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
3063 &tools_tearoff->tearoff_window(), 0));
3064 tools_tearoff->Hidden.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
3065 &tools_tearoff->tearoff_window()));
3066 tools_tearoff->Visible.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
3067 &tools_tearoff->tearoff_window(), 0));
3069 toolbar_hbox.set_spacing (10);
3070 toolbar_hbox.set_border_width (1);
3072 toolbar_hbox.pack_start (*mouse_mode_tearoff, false, false);
3073 toolbar_hbox.pack_start (*tools_tearoff, false, false);
3076 hbox->pack_start (snap_box, false, false);
3077 // hbox->pack_start (zoom_box, false, false);
3078 hbox->pack_start (*nudge_box, false, false);
3080 hbox->show_all ();
3082 toolbar_base.set_name ("ToolBarBase");
3083 toolbar_base.add (toolbar_hbox);
3085 toolbar_frame.set_shadow_type (SHADOW_OUT);
3086 toolbar_frame.set_name ("BaseFrame");
3087 toolbar_frame.add (toolbar_base);
3091 Editor::convert_drop_to_paths (vector<ustring>& paths,
3092 const RefPtr<Gdk::DragContext>& context,
3093 gint x,
3094 gint y,
3095 const SelectionData& data,
3096 guint info,
3097 guint time)
3100 if (session == 0) {
3101 return -1;
3104 vector<ustring> uris = data.get_uris();
3106 if (uris.empty()) {
3108 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3109 are actually URI lists. So do it by hand.
3112 if (data.get_target() != "text/plain") {
3113 return -1;
3116 /* Parse the "uri-list" format that Nautilus provides,
3117 where each pathname is delimited by \r\n.
3119 THERE MAY BE NO NULL TERMINATING CHAR!!!
3122 ustring txt = data.get_text();
3123 const char* p;
3124 const char* q;
3126 p = (const char *) malloc (txt.length() + 1);
3127 txt.copy ((char *) p, txt.length(), 0);
3128 ((char*)p)[txt.length()] = '\0';
3130 while (p)
3132 if (*p != '#')
3134 while (g_ascii_isspace (*p))
3135 p++;
3137 q = p;
3138 while (*q && (*q != '\n') && (*q != '\r')) {
3139 q++;
3142 if (q > p)
3144 q--;
3145 while (q > p && g_ascii_isspace (*q))
3146 q--;
3148 if (q > p)
3150 uris.push_back (ustring (p, q - p + 1));
3154 p = strchr (p, '\n');
3155 if (p)
3156 p++;
3159 free ((void*)p);
3161 if (uris.empty()) {
3162 return -1;
3166 for (vector<ustring>::iterator i = uris.begin(); i != uris.end(); ++i) {
3168 if ((*i).substr (0,7) == "file://") {
3171 ustring p = *i;
3172 PBD::url_decode (p);
3174 // scan forward past three slashes
3176 ustring::size_type slashcnt = 0;
3177 ustring::size_type n = 0;
3178 ustring::iterator x = p.begin();
3180 while (slashcnt < 3 && x != p.end()) {
3181 if ((*x) == '/') {
3182 slashcnt++;
3183 } else if (slashcnt == 3) {
3184 break;
3186 ++n;
3187 ++x;
3190 if (slashcnt != 3 || x == p.end()) {
3191 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3192 continue;
3195 paths.push_back (p.substr (n - 1));
3199 return 0;
3202 void
3203 Editor::new_tempo_section ()
3208 void
3209 Editor::map_transport_state ()
3211 ENSURE_GUI_THREAD (mem_fun(*this, &Editor::map_transport_state));
3213 if (session->transport_stopped()) {
3214 have_pending_keyboard_selection = false;
3217 update_loop_range_view (true);
3220 /* UNDO/REDO */
3222 Editor::State::State ()
3224 selection = new Selection;
3227 Editor::State::~State ()
3229 delete selection;
3232 UndoAction
3233 Editor::get_memento () const
3235 State *state = new State;
3237 store_state (*state);
3238 return bind (mem_fun (*(const_cast<Editor*>(this)), &Editor::restore_state), state);
3241 void
3242 Editor::store_state (State& state) const
3244 *state.selection = *selection;
3247 void
3248 Editor::restore_state (State *state)
3250 if (*selection == *state->selection) {
3251 return;
3254 *selection = *state->selection;
3255 time_selection_changed ();
3256 region_selection_changed ();
3258 /* XXX other selection change handlers? */
3261 void
3262 Editor::begin_reversible_command (string name)
3264 if (session) {
3265 // before = &get_state();
3266 session->begin_reversible_command (name);
3270 void
3271 Editor::commit_reversible_command ()
3273 if (session) {
3274 // session->commit_reversible_command (new MementoCommand<Editor>(*this, before, &get_state()));
3275 session->commit_reversible_command ();
3279 void
3280 Editor::set_edit_group_solo (Route& route, bool yn)
3282 RouteGroup *edit_group;
3284 if ((edit_group = route.edit_group()) != 0) {
3285 edit_group->apply (&Route::set_solo, yn, this);
3286 } else {
3287 route.set_solo (yn, this);
3291 void
3292 Editor::set_edit_group_mute (Route& route, bool yn)
3294 RouteGroup *edit_group = 0;
3296 if ((edit_group == route.edit_group()) != 0) {
3297 edit_group->apply (&Route::set_mute, yn, this);
3298 } else {
3299 route.set_mute (yn, this);
3303 void
3304 Editor::history_changed ()
3306 string label;
3308 if (undo_action && session) {
3309 if (session->undo_depth() == 0) {
3310 label = _("Undo");
3311 } else {
3312 label = string_compose(_("Undo (%1)"), session->next_undo());
3314 undo_action->property_label() = label;
3317 if (redo_action && session) {
3318 if (session->redo_depth() == 0) {
3319 label = _("Redo");
3320 } else {
3321 label = string_compose(_("Redo (%1)"), session->next_redo());
3323 redo_action->property_label() = label;
3327 void
3328 Editor::duplicate_dialog (bool with_dialog)
3330 float times = 1.0f;
3332 if (mouse_mode == MouseRange) {
3333 if (selection->time.length() == 0) {
3334 return;
3338 RegionSelection rs;
3339 get_regions_for_action (rs);
3341 if (mouse_mode != MouseRange) {
3343 if (rs.empty()) {
3344 return;
3348 if (with_dialog) {
3350 ArdourDialog win ("Duplication Dialog");
3351 Label label (_("Number of Duplications:"));
3352 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3353 SpinButton spinner (adjustment, 0.0, 1);
3354 HBox hbox;
3356 win.get_vbox()->set_spacing (12);
3357 win.get_vbox()->pack_start (hbox);
3358 hbox.set_border_width (6);
3359 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3361 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3362 place, visually. so do this by hand.
3365 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3366 spinner.signal_activate().connect (sigc::bind (mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3367 spinner.grab_focus();
3369 hbox.show ();
3370 label.show ();
3371 spinner.show ();
3373 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3374 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3375 win.set_default_response (RESPONSE_ACCEPT);
3377 win.set_position (WIN_POS_MOUSE);
3379 spinner.grab_focus ();
3381 switch (win.run ()) {
3382 case RESPONSE_ACCEPT:
3383 break;
3384 default:
3385 return;
3388 times = adjustment.get_value();
3391 if (mouse_mode == MouseRange) {
3392 duplicate_selection (times);
3393 } else {
3394 duplicate_some_regions (rs, times);
3398 void
3399 Editor::show_verbose_canvas_cursor ()
3401 verbose_canvas_cursor->raise_to_top();
3402 verbose_canvas_cursor->show();
3403 verbose_cursor_visible = true;
3406 void
3407 Editor::hide_verbose_canvas_cursor ()
3409 verbose_canvas_cursor->hide();
3410 verbose_cursor_visible = false;
3413 double
3414 Editor::clamp_verbose_cursor_x (double x)
3416 if (x < 0) {
3417 x = 0;
3418 } else {
3419 x = min (canvas_width - 200.0, x);
3421 return x;
3424 double
3425 Editor::clamp_verbose_cursor_y (double y)
3427 if (y < canvas_timebars_vsize) {
3428 y = canvas_timebars_vsize;
3429 } else {
3430 y = min (canvas_height - 50, y);
3432 return y;
3435 void
3436 Editor::set_verbose_canvas_cursor (const string & txt, double x, double y)
3438 verbose_canvas_cursor->property_text() = txt.c_str();
3439 /* don't get too close to the edge */
3440 verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (x);
3441 verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (y);
3444 void
3445 Editor::set_verbose_canvas_cursor_text (const string & txt)
3447 verbose_canvas_cursor->property_text() = txt.c_str();
3450 void
3451 Editor::set_edit_mode (EditMode m)
3453 Config->set_edit_mode (m);
3456 void
3457 Editor::cycle_edit_mode ()
3459 switch (Config->get_edit_mode()) {
3460 case Slide:
3461 if (Profile->get_sae()) {
3462 Config->set_edit_mode (Lock);
3463 } else {
3464 Config->set_edit_mode (Splice);
3466 break;
3467 case Splice:
3468 Config->set_edit_mode (Lock);
3469 break;
3470 case Lock:
3471 Config->set_edit_mode (Slide);
3472 break;
3476 void
3477 Editor::edit_mode_selection_done ()
3479 if (session == 0) {
3480 return;
3483 string choice = edit_mode_selector.get_active_text();
3484 EditMode mode = Slide;
3486 if (choice == _("Splice Edit")) {
3487 mode = Splice;
3488 } else if (choice == _("Slide Edit")) {
3489 mode = Slide;
3490 } else if (choice == _("Lock Edit")) {
3491 mode = Lock;
3494 Config->set_edit_mode (mode);
3497 void
3498 Editor::snap_type_selection_done ()
3500 string choice = snap_type_selector.get_active_text();
3501 SnapType snaptype = SnapToBeat;
3503 if (choice == _("Beats/3")) {
3504 snaptype = SnapToAThirdBeat;
3505 } else if (choice == _("Beats/4")) {
3506 snaptype = SnapToAQuarterBeat;
3507 } else if (choice == _("Beats/8")) {
3508 snaptype = SnapToAEighthBeat;
3509 } else if (choice == _("Beats/16")) {
3510 snaptype = SnapToASixteenthBeat;
3511 } else if (choice == _("Beats/32")) {
3512 snaptype = SnapToAThirtysecondBeat;
3513 } else if (choice == _("Beats")) {
3514 snaptype = SnapToBeat;
3515 } else if (choice == _("Bars")) {
3516 snaptype = SnapToBar;
3517 } else if (choice == _("Marks")) {
3518 snaptype = SnapToMark;
3519 } else if (choice == _("Region starts")) {
3520 snaptype = SnapToRegionStart;
3521 } else if (choice == _("Region ends")) {
3522 snaptype = SnapToRegionEnd;
3523 } else if (choice == _("Region bounds")) {
3524 snaptype = SnapToRegionBoundary;
3525 } else if (choice == _("Region syncs")) {
3526 snaptype = SnapToRegionSync;
3527 } else if (choice == _("CD Frames")) {
3528 snaptype = SnapToCDFrame;
3529 } else if (choice == _("SMPTE Frames")) {
3530 snaptype = SnapToSMPTEFrame;
3531 } else if (choice == _("SMPTE Seconds")) {
3532 snaptype = SnapToSMPTESeconds;
3533 } else if (choice == _("SMPTE Minutes")) {
3534 snaptype = SnapToSMPTEMinutes;
3535 } else if (choice == _("Seconds")) {
3536 snaptype = SnapToSeconds;
3537 } else if (choice == _("Minutes")) {
3538 snaptype = SnapToMinutes;
3541 RefPtr<RadioAction> ract = snap_type_action (snaptype);
3542 if (ract) {
3543 ract->set_active ();
3547 void
3548 Editor::snap_mode_selection_done ()
3550 string choice = snap_mode_selector.get_active_text();
3551 SnapMode mode = SnapNormal;
3553 if (choice == _("No Grid")) {
3554 mode = SnapOff;
3555 } else if (choice == _("Grid")) {
3556 mode = SnapNormal;
3557 } else if (choice == _("Magnetic")) {
3558 mode = SnapMagnetic;
3561 RefPtr<RadioAction> ract = snap_mode_action (mode);
3563 if (ract) {
3564 ract->set_active (true);
3568 void
3569 Editor::cycle_edit_point (bool with_marker)
3571 switch (_edit_point) {
3572 case EditAtMouse:
3573 set_edit_point_preference (EditAtPlayhead);
3574 break;
3575 case EditAtPlayhead:
3576 if (with_marker) {
3577 set_edit_point_preference (EditAtSelectedMarker);
3578 } else {
3579 set_edit_point_preference (EditAtMouse);
3581 break;
3582 case EditAtSelectedMarker:
3583 set_edit_point_preference (EditAtMouse);
3584 break;
3588 void
3589 Editor::edit_point_selection_done ()
3591 string choice = edit_point_selector.get_active_text();
3592 EditPoint ep = EditAtSelectedMarker;
3594 if (choice == _("Marker")) {
3595 set_edit_point_preference (EditAtSelectedMarker);
3596 } else if (choice == _("Playhead")) {
3597 set_edit_point_preference (EditAtPlayhead);
3598 } else {
3599 set_edit_point_preference (EditAtMouse);
3602 RefPtr<RadioAction> ract = edit_point_action (ep);
3604 if (ract) {
3605 ract->set_active (true);
3609 void
3610 Editor::zoom_focus_selection_done ()
3612 string choice = zoom_focus_selector.get_active_text();
3613 ZoomFocus focus_type = ZoomFocusLeft;
3615 if (choice == _("Left")) {
3616 focus_type = ZoomFocusLeft;
3617 } else if (choice == _("Right")) {
3618 focus_type = ZoomFocusRight;
3619 } else if (choice == _("Center")) {
3620 focus_type = ZoomFocusCenter;
3621 } else if (choice == _("Playhead")) {
3622 focus_type = ZoomFocusPlayhead;
3623 } else if (choice == _("Mouse")) {
3624 focus_type = ZoomFocusMouse;
3625 } else if (choice == _("Active Mark")) {
3626 focus_type = ZoomFocusEdit;
3629 RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3631 if (ract) {
3632 ract->set_active ();
3636 gint
3637 Editor::edit_controls_button_release (GdkEventButton* ev)
3639 if (Keyboard::is_context_menu_event (ev)) {
3640 ARDOUR_UI::instance()->add_route (this);
3642 return TRUE;
3645 gint
3646 Editor::mouse_select_button_release (GdkEventButton* ev)
3648 /* this handles just right-clicks */
3650 if (ev->button != 3) {
3651 return false;
3654 return true;
3657 Editor::TrackViewList *
3658 Editor::get_valid_views (TimeAxisView* track, RouteGroup* group)
3660 TrackViewList *v;
3661 TrackViewList::iterator i;
3663 v = new TrackViewList;
3665 if (track == 0 && group == 0) {
3667 /* all views */
3669 for (i = track_views.begin(); i != track_views.end (); ++i) {
3670 v->push_back (*i);
3673 } else if ((track != 0 && group == 0) || (track != 0 && group != 0 && !group->is_active())) {
3675 /* just the view for this track
3678 v->push_back (track);
3680 } else {
3682 /* views for all tracks in the edit group */
3684 for (i = track_views.begin(); i != track_views.end (); ++i) {
3686 if (group == 0 || (*i)->edit_group() == group) {
3687 v->push_back (*i);
3692 return v;
3695 void
3696 Editor::set_zoom_focus (ZoomFocus f)
3698 string str = zoom_focus_strings[(int)f];
3700 if (str != zoom_focus_selector.get_active_text()) {
3701 zoom_focus_selector.set_active_text (str);
3704 if (zoom_focus != f) {
3705 zoom_focus = f;
3707 ZoomFocusChanged (); /* EMIT_SIGNAL */
3709 instant_save ();
3713 void
3714 Editor::ensure_float (Window& win)
3716 win.set_transient_for (*this);
3719 void
3720 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3722 /* recover or initialize pane positions. do this here rather than earlier because
3723 we don't want the positions to change the child allocations, which they seem to do.
3726 int pos;
3727 XMLProperty* prop;
3728 char buf[32];
3729 XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3730 int width, height;
3731 static int32_t done;
3732 XMLNode* geometry;
3734 width = default_width;
3735 height = default_height;
3737 if ((geometry = find_named_node (*node, "geometry")) != 0) {
3739 if ((prop = geometry->property ("x_size")) == 0) {
3740 prop = geometry->property ("x-size");
3742 if (prop) {
3743 width = atoi (prop->value());
3745 if ((prop = geometry->property ("y_size")) == 0) {
3746 prop = geometry->property ("y-size");
3748 if (prop) {
3749 height = atoi (prop->value());
3753 if (which == static_cast<Paned*> (&edit_pane)) {
3755 if (done) {
3756 return;
3759 if (!geometry || (prop = geometry->property ("edit_pane_pos")) == 0) {
3760 /* initial allocation is 90% to canvas, 10% to notebook */
3761 pos = (int) floor (alloc.get_width() * 0.90f);
3762 snprintf (buf, sizeof(buf), "%d", pos);
3763 } else {
3764 pos = atoi (prop->value());
3767 if ((done = GTK_WIDGET(edit_pane.gobj())->allocation.width > pos)) {
3768 edit_pane.set_position (pos);
3769 pre_maximal_pane_position = pos;
3774 void
3775 Editor::detach_tearoff (Box* b, Window* w)
3777 if (tools_tearoff->torn_off() &&
3778 mouse_mode_tearoff->torn_off()) {
3779 top_hbox.remove (toolbar_frame);
3783 void
3784 Editor::reattach_tearoff (Box* b, Window* w, int32_t n)
3786 if (toolbar_frame.get_parent() == 0) {
3787 top_hbox.pack_end (toolbar_frame);
3791 void
3792 Editor::set_show_measures (bool yn)
3794 if (_show_measures != yn) {
3795 hide_measures ();
3797 if ((_show_measures = yn) == true) {
3798 if (tempo_lines) {
3799 tempo_lines->show();
3801 draw_measures ();
3803 instant_save ();
3807 void
3808 Editor::toggle_follow_playhead ()
3810 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3811 if (act) {
3812 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3813 set_follow_playhead (tact->get_active());
3817 void
3818 Editor::set_follow_playhead (bool yn)
3820 if (_follow_playhead != yn) {
3821 if ((_follow_playhead = yn) == true) {
3822 /* catch up */
3823 update_current_screen ();
3825 instant_save ();
3829 void
3830 Editor::toggle_stationary_playhead ()
3832 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3833 if (act) {
3834 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3835 set_stationary_playhead (tact->get_active());
3839 void
3840 Editor::set_stationary_playhead (bool yn)
3842 if (_stationary_playhead != yn) {
3843 if ((_stationary_playhead = yn) == true) {
3844 /* catch up */
3845 update_current_screen ();
3847 instant_save ();
3851 void
3852 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3854 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3855 if (xfade) {
3856 xfade->set_active (!xfade->active());
3860 void
3861 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3863 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3864 if (xfade) {
3865 xfade->set_follow_overlap (!xfade->following_overlap());
3869 void
3870 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3872 boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3874 if (!xfade) {
3875 return;
3878 CrossfadeEditor cew (*session, xfade, xfade->fade_in().get_min_y(), 1.0);
3880 ensure_float (cew);
3882 switch (cew.run ()) {
3883 case RESPONSE_ACCEPT:
3884 break;
3885 default:
3886 return;
3889 cew.apply ();
3890 xfade->StateChanged (Change (~0));
3893 PlaylistSelector&
3894 Editor::playlist_selector () const
3896 return *_playlist_selector;
3899 nframes64_t
3900 Editor::get_nudge_distance (nframes64_t pos, nframes64_t& next)
3902 nframes64_t ret;
3904 ret = nudge_clock.current_duration (pos);
3905 next = ret + 1; /* XXXX fix me */
3907 return ret;
3910 void
3911 Editor::end_location_changed (Location* location)
3913 ENSURE_GUI_THREAD (bind (mem_fun(*this, &Editor::end_location_changed), location));
3914 //reset_scrolling_region ();
3915 nframes64_t session_span = location->start() + (nframes64_t) floorf (current_page_frames() * 0.10f);
3916 horizontal_adjustment.set_upper (session_span / frames_per_unit);
3920 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3922 ArdourDialog dialog ("playlist deletion dialog");
3923 Label label (string_compose (_("Playlist %1 is currently unused.\n"
3924 "If left alone, no audio files used by it will be cleaned.\n"
3925 "If deleted, audio files used by it alone by will cleaned."),
3926 pl->name()));
3928 dialog.set_position (WIN_POS_CENTER);
3929 dialog.get_vbox()->pack_start (label);
3931 label.show ();
3933 dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT);
3934 dialog.add_button (_("Keep playlist"), RESPONSE_REJECT);
3935 dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3937 switch (dialog.run ()) {
3938 case RESPONSE_ACCEPT:
3939 /* delete the playlist */
3940 return 0;
3941 break;
3943 case RESPONSE_REJECT:
3944 /* keep the playlist */
3945 return 1;
3946 break;
3948 default:
3949 break;
3952 return -1;
3955 bool
3956 Editor::audio_region_selection_covers (nframes64_t where)
3958 for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3959 if ((*a)->region()->covers (where)) {
3960 return true;
3964 return false;
3967 void
3968 Editor::prepare_for_cleanup ()
3970 cut_buffer->clear_regions ();
3971 cut_buffer->clear_playlists ();
3973 selection->clear_regions ();
3974 selection->clear_playlists ();
3976 no_region_list_redisplay = true;
3979 void
3980 Editor::finish_cleanup ()
3982 no_region_list_redisplay = false;
3983 redisplay_regions ();
3986 Location*
3987 Editor::transport_loop_location()
3989 if (session) {
3990 return session->locations()->auto_loop_location();
3991 } else {
3992 return 0;
3996 Location*
3997 Editor::transport_punch_location()
3999 if (session) {
4000 return session->locations()->auto_punch_location();
4001 } else {
4002 return 0;
4006 bool
4007 Editor::control_layout_scroll (GdkEventScroll* ev)
4009 switch (ev->direction) {
4010 case GDK_SCROLL_UP:
4011 scroll_tracks_up_line ();
4012 return true;
4013 break;
4015 case GDK_SCROLL_DOWN:
4016 scroll_tracks_down_line ();
4017 return true;
4019 default:
4020 /* no left/right handling yet */
4021 break;
4024 return false;
4028 /** A new snapshot has been selected.
4030 void
4031 Editor::snapshot_display_selection_changed ()
4033 if (snapshot_display.get_selection()->count_selected_rows() > 0) {
4035 TreeModel::iterator i = snapshot_display.get_selection()->get_selected();
4037 Glib::ustring snap_name = (*i)[snapshot_display_columns.real_name];
4039 if (snap_name.length() == 0) {
4040 return;
4043 if (session->snap_name() == snap_name) {
4044 return;
4047 ARDOUR_UI::instance()->load_session(session->path(), string (snap_name));
4051 bool
4052 Editor::snapshot_display_button_press (GdkEventButton* ev)
4054 if (ev->button == 3) {
4055 /* Right-click on the snapshot list. Work out which snapshot it
4056 was over. */
4057 Gtk::TreeModel::Path path;
4058 Gtk::TreeViewColumn* col;
4059 int cx;
4060 int cy;
4061 snapshot_display.get_path_at_pos ((int) ev->x, (int) ev->y, path, col, cx, cy);
4062 Gtk::TreeModel::iterator iter = snapshot_display_model->get_iter (path);
4063 if (iter) {
4064 Gtk::TreeModel::Row row = *iter;
4065 popup_snapshot_context_menu (ev->button, ev->time, row[snapshot_display_columns.real_name]);
4067 return true;
4070 return false;
4074 /** Pop up the snapshot display context menu.
4075 * @param button Button used to open the menu.
4076 * @param time Menu open time.
4077 * @snapshot_name Name of the snapshot that the menu click was over.
4080 void
4081 Editor::popup_snapshot_context_menu (int button, int32_t time, Glib::ustring snapshot_name)
4083 using namespace Menu_Helpers;
4085 MenuList& items (snapshot_context_menu.items());
4086 items.clear ();
4088 const bool modification_allowed = (session->snap_name() != snapshot_name && session->name() != snapshot_name);
4090 items.push_back (MenuElem (_("Remove"), bind (mem_fun (*this, &Editor::remove_snapshot), snapshot_name)));
4091 if (!modification_allowed) {
4092 items.back().set_sensitive (false);
4095 items.push_back (MenuElem (_("Rename"), bind (mem_fun (*this, &Editor::rename_snapshot), snapshot_name)));
4096 if (!modification_allowed) {
4097 items.back().set_sensitive (false);
4100 snapshot_context_menu.popup (button, time);
4103 void
4104 Editor::rename_snapshot (Glib::ustring old_name)
4106 ArdourPrompter prompter(true);
4108 string new_name;
4110 prompter.set_name ("Prompter");
4111 prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
4112 prompter.set_prompt (_("New name of snapshot"));
4113 prompter.set_initial_text (old_name);
4115 if (prompter.run() == RESPONSE_ACCEPT) {
4116 prompter.get_result (new_name);
4117 if (new_name.length()) {
4118 session->rename_state (old_name, new_name);
4119 redisplay_snapshots ();
4125 void
4126 Editor::remove_snapshot (Glib::ustring name)
4128 vector<string> choices;
4130 std::string prompt = string_compose (_("Do you really want to remove snapshot \"%1\" ?\n(cannot be undone)"), name);
4132 choices.push_back (_("No, do nothing."));
4133 choices.push_back (_("Yes, remove it."));
4135 Gtkmm2ext::Choice prompter (prompt, choices);
4137 if (prompter.run () == 1) {
4138 session->remove_state (name);
4139 redisplay_snapshots ();
4143 void
4144 Editor::redisplay_snapshots ()
4146 if (session == 0) {
4147 return;
4150 vector<string*>* states;
4152 if ((states = session->possible_states()) == 0) {
4153 return;
4156 snapshot_display_model->clear ();
4158 for (vector<string*>::iterator i = states->begin(); i != states->end(); ++i) {
4159 string statename = *(*i);
4160 TreeModel::Row row = *(snapshot_display_model->append());
4162 /* this lingers on in case we ever want to change the visible
4163 name of the snapshot.
4166 string display_name;
4167 display_name = statename;
4169 if (statename == session->snap_name()) {
4170 snapshot_display.get_selection()->select(row);
4173 row[snapshot_display_columns.visible_name] = display_name;
4174 row[snapshot_display_columns.real_name] = statename;
4177 delete states;
4180 void
4181 Editor::session_state_saved (string snap_name)
4183 ENSURE_GUI_THREAD (bind (mem_fun(*this, &Editor::session_state_saved), snap_name));
4184 redisplay_snapshots ();
4187 void
4188 Editor::maximise_editing_space ()
4190 mouse_mode_tearoff->set_visible (false);
4191 tools_tearoff->set_visible (false);
4193 pre_maximal_pane_position = edit_pane.get_position();
4194 pre_maximal_editor_width = this->get_width();
4196 if(post_maximal_pane_position == 0) {
4197 post_maximal_pane_position = edit_pane.get_width();
4200 fullscreen();
4202 if(post_maximal_editor_width) {
4203 edit_pane.set_position (post_maximal_pane_position -
4204 abs(post_maximal_editor_width - pre_maximal_editor_width));
4205 } else {
4206 edit_pane.set_position (post_maximal_pane_position);
4210 void
4211 Editor::restore_editing_space ()
4213 // user changed width of pane during fullscreen
4214 if(post_maximal_pane_position != edit_pane.get_position()) {
4215 post_maximal_pane_position = edit_pane.get_position();
4218 unfullscreen();
4220 mouse_mode_tearoff->set_visible (true);
4221 tools_tearoff->set_visible (true);
4222 post_maximal_editor_width = this->get_width();
4225 edit_pane.set_position (
4226 pre_maximal_pane_position + abs(this->get_width() - pre_maximal_editor_width)
4231 * Make new playlists for a given track and also any others that belong
4232 * to the same active edit group.
4233 * @param v Track.
4236 void
4237 Editor::new_playlists (TimeAxisView* v)
4239 begin_reversible_command (_("new playlists"));
4240 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4241 session->get_playlists(playlists);
4242 mapover_audio_tracks ( bind(mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v );
4243 commit_reversible_command ();
4248 * Use a copy of the current playlist for a given track and also any others that belong
4249 * to the same active edit group.
4250 * @param v Track.
4253 void
4254 Editor::copy_playlists (TimeAxisView* v)
4256 begin_reversible_command (_("copy playlists"));
4257 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4258 session->get_playlists(playlists);
4259 mapover_audio_tracks ( bind(mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v );
4260 commit_reversible_command ();
4265 * Clear the current playlist for a given track and also any others that belong
4266 * to the same active edit group.
4267 * @param v Track.
4270 void
4271 Editor::clear_playlists (TimeAxisView* v)
4273 begin_reversible_command (_("clear playlists"));
4274 vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4275 session->get_playlists(playlists);
4276 mapover_audio_tracks ( mem_fun (*this, &Editor::mapped_clear_playlist), v );
4277 commit_reversible_command ();
4280 void
4281 Editor::mapped_use_new_playlist (AudioTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4283 atv.use_new_playlist (sz > 1 ? false : true, playlists);
4286 void
4287 Editor::mapped_use_copy_playlist (AudioTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4289 atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4292 void
4293 Editor::mapped_clear_playlist (AudioTimeAxisView& atv, uint32_t sz)
4295 atv.clear_playlist ();
4298 bool
4299 Editor::on_key_press_event (GdkEventKey* ev)
4301 return key_press_focus_accelerator_handler (*this, ev);
4304 bool
4305 Editor::on_key_release_event (GdkEventKey* ev)
4307 return Gtk::Window::on_key_release_event (ev);
4308 // return key_press_focus_accelerator_handler (*this, ev);
4311 void
4312 Editor::reset_x_origin (nframes64_t frame)
4314 queue_visual_change (frame);
4317 void
4318 Editor::reset_zoom (double fpu)
4320 queue_visual_change (fpu);
4323 void
4324 Editor::reposition_and_zoom (nframes64_t frame, double fpu)
4326 //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
4327 reset_x_origin (frame);
4328 reset_zoom (fpu);
4330 if (!no_save_visual) {
4331 undo_visual_stack.push_back (current_visual_state(false));
4335 Editor::VisualState*
4336 Editor::current_visual_state (bool with_tracks)
4338 VisualState* vs = new VisualState;
4339 vs->y_position = vertical_adjustment.get_value();
4340 vs->frames_per_unit = frames_per_unit;
4341 vs->leftmost_frame = leftmost_frame;
4342 vs->zoom_focus = zoom_focus;
4343 vs->zoomed_to_region = zoomed_to_region;
4345 if (with_tracks) {
4346 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4347 vs->track_states.push_back (TAVState ((*i), &(*i)->get_state()));
4351 return vs;
4354 void
4355 Editor::undo_visual_state ()
4357 if (undo_visual_stack.empty()) {
4358 return;
4361 VisualState* vs = undo_visual_stack.back();
4362 undo_visual_stack.pop_back();
4363 use_visual_state (*vs);
4364 redo_visual_stack.push_back (vs);
4367 void
4368 Editor::redo_visual_state ()
4370 if (redo_visual_stack.empty()) {
4371 return;
4374 VisualState* vs = redo_visual_stack.back();
4375 redo_visual_stack.pop_back();
4376 use_visual_state (*vs);
4377 undo_visual_stack.push_back (vs);
4380 void
4381 Editor::swap_visual_state ()
4383 if (undo_visual_stack.empty()) {
4384 redo_visual_state ();
4385 } else {
4386 undo_visual_state ();
4390 void
4391 Editor::use_visual_state (VisualState& vs)
4393 no_save_visual = true;
4394 no_route_list_redisplay = true;
4396 vertical_adjustment.set_value (vs.y_position);
4398 set_zoom_focus (vs.zoom_focus);
4399 reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4400 zoomed_to_region = vs.zoomed_to_region;
4402 for (list<TAVState>::iterator i = vs.track_states.begin(); i != vs.track_states.end(); ++i) {
4403 TrackViewList::iterator t;
4405 /* check if the track still exists - it could have been deleted */
4407 if ((t = find (track_views.begin(), track_views.end(), i->first)) != track_views.end()) {
4408 (*t)->set_state (*(i->second));
4412 if (!vs.track_states.empty()) {
4413 update_route_visibility ();
4416 no_route_list_redisplay = false;
4417 redisplay_route_list ();
4419 no_save_visual = false;
4422 void
4423 Editor::set_frames_per_unit (double fpu)
4425 /* this is the core function that controls the zoom level of the canvas. it is called
4426 whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4429 if (fpu == frames_per_unit) {
4430 return;
4433 if (fpu < 1.0) {
4434 fpu = 1.0;
4438 /* don't allow zooms that fit more than the maximum number
4439 of frames into an 800 pixel wide space.
4442 if (max_frames / fpu < 800.0) {
4443 return;
4446 if (tempo_lines) {
4447 tempo_lines->tempo_map_changed();
4450 frames_per_unit = fpu;
4451 post_zoom ();
4454 void
4455 Editor::post_zoom ()
4457 nframes64_t cef = 0;
4459 // convert fpu to frame count
4461 nframes64_t frames = (nframes64_t) floor (frames_per_unit * canvas_width);
4463 if (frames_per_unit != zoom_range_clock.current_duration()) {
4464 zoom_range_clock.set (frames);
4467 if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4468 if (!selection->tracks.empty()) {
4469 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4470 (*i)->reshow_selection (selection->time);
4472 } else {
4473 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4474 (*i)->reshow_selection (selection->time);
4478 update_loop_range_view (false);
4479 update_punch_range_view (false);
4481 if (playhead_cursor) {
4482 playhead_cursor->set_position (playhead_cursor->current_frame);
4485 leftmost_frame = (nframes64_t) floor (horizontal_adjustment.get_value() * frames_per_unit);
4487 ZoomChanged (); /* EMIT_SIGNAL */
4489 reset_hscrollbar_stepping ();
4491 if (session) {
4492 cef = session->current_end_frame() + (current_page_frames() / 10);// Add a little extra so we can see the end marker
4494 horizontal_adjustment.set_upper (cef / frames_per_unit);
4496 //reset_scrolling_region ();
4498 instant_save ();
4501 void
4502 Editor::queue_visual_change (nframes64_t where)
4504 pending_visual_change.pending = VisualChange::Type (pending_visual_change.pending | VisualChange::TimeOrigin);
4506 /* if we're moving beyond the end, make sure the upper limit of the horizontal adjustment
4507 can reach.
4510 if (where > session->current_end_frame()) {
4511 horizontal_adjustment.set_upper ((where + current_page_frames()) / frames_per_unit);
4514 pending_visual_change.time_origin = where;
4516 if (pending_visual_change.idle_handler_id < 0) {
4517 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4521 void
4522 Editor::queue_visual_change (double fpu)
4524 pending_visual_change.pending = VisualChange::Type (pending_visual_change.pending | VisualChange::ZoomLevel);
4525 pending_visual_change.frames_per_unit = fpu;
4527 if (pending_visual_change.idle_handler_id < 0) {
4528 pending_visual_change.idle_handler_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE, _idle_visual_changer, this, 0);
4533 Editor::_idle_visual_changer (void* arg)
4535 return static_cast<Editor*>(arg)->idle_visual_changer ();
4539 Editor::idle_visual_changer ()
4541 VisualChange::Type p = pending_visual_change.pending;
4542 pending_visual_change.pending = (VisualChange::Type) 0;
4544 double last_time_origin = horizontal_adjustment.get_value();
4545 if (p & VisualChange::ZoomLevel) {
4546 set_frames_per_unit (pending_visual_change.frames_per_unit);
4549 if (p & VisualChange::TimeOrigin) {
4550 horizontal_adjustment.set_value (pending_visual_change.time_origin / frames_per_unit);
4553 if (last_time_origin == horizontal_adjustment.get_value() ) {
4554 /* changed signal not emitted */
4555 update_fixed_rulers ();
4556 redisplay_tempo (true);
4558 // 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
4559 pending_visual_change.idle_handler_id = -1;
4560 return 0; /* this is always a one-shot call */
4563 struct EditorOrderTimeAxisSorter {
4564 bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4565 return a->order < b->order;
4569 void
4570 Editor::sort_track_selection (TrackSelection* sel)
4572 EditorOrderTimeAxisSorter cmp;
4574 if (sel) {
4575 sel->sort (cmp);
4576 } else {
4577 selection->tracks.sort (cmp);
4581 nframes64_t
4582 Editor::get_preferred_edit_position (bool ignore_playhead)
4584 bool ignored;
4585 nframes64_t where = 0;
4586 EditPoint ep = _edit_point;
4588 if (entered_marker) {
4589 return entered_marker->position();
4592 if (ignore_playhead && ep == EditAtPlayhead) {
4593 ep = EditAtSelectedMarker;
4596 switch (ep) {
4597 case EditAtPlayhead:
4598 where = session->audible_frame();
4599 break;
4601 case EditAtSelectedMarker:
4602 if (!selection->markers.empty()) {
4603 bool is_start;
4604 Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4605 if (loc) {
4606 if (is_start) {
4607 where = loc->start();
4608 } else {
4609 where = loc->end();
4611 break;
4614 /* fallthru */
4616 default:
4617 case EditAtMouse:
4618 if (!mouse_frame (where, ignored)) {
4619 /* XXX not right but what can we do ? */
4620 return 0;
4622 snap_to (where);
4623 break;
4626 return where;
4629 void
4630 Editor::set_loop_range (nframes64_t start, nframes64_t end, string cmd)
4632 if (!session) return;
4634 begin_reversible_command (cmd);
4636 Location* tll;
4638 if ((tll = transport_loop_location()) == 0) {
4639 Location* loc = new Location (start, end, _("Loop"), Location::IsAutoLoop);
4640 XMLNode &before = session->locations()->get_state();
4641 session->locations()->add (loc, true);
4642 session->set_auto_loop_location (loc);
4643 XMLNode &after = session->locations()->get_state();
4644 session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after));
4645 } else {
4646 XMLNode &before = tll->get_state();
4647 tll->set_hidden (false, this);
4648 tll->set (start, end);
4649 XMLNode &after = tll->get_state();
4650 session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4653 commit_reversible_command ();
4656 void
4657 Editor::set_punch_range (nframes64_t start, nframes64_t end, string cmd)
4659 if (!session) return;
4661 begin_reversible_command (cmd);
4663 Location* tpl;
4665 if ((tpl = transport_punch_location()) == 0) {
4666 Location* loc = new Location (start, end, _("Loop"), Location::IsAutoPunch);
4667 XMLNode &before = session->locations()->get_state();
4668 session->locations()->add (loc, true);
4669 session->set_auto_loop_location (loc);
4670 XMLNode &after = session->locations()->get_state();
4671 session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after));
4673 else {
4674 XMLNode &before = tpl->get_state();
4675 tpl->set_hidden (false, this);
4676 tpl->set (start, end);
4677 XMLNode &after = tpl->get_state();
4678 session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4681 commit_reversible_command ();
4684 void
4685 Editor::get_regions_at (RegionSelection& rs, nframes64_t where, const TrackSelection& ts) const
4687 const TrackSelection* tracks;
4689 if (ts.empty()) {
4690 tracks = &track_views;
4691 } else {
4692 tracks = &ts;
4695 for (TrackSelection::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4697 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*t);
4699 if (atv) {
4700 boost::shared_ptr<Diskstream> ds;
4701 boost::shared_ptr<Playlist> pl;
4703 if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()))) {
4705 Playlist::RegionList* regions = pl->regions_at ((nframes64_t) floor ( (double)where * ds->speed()));
4707 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4709 RegionView* rv = atv->audio_view()->find_view (*i);
4711 if (rv) {
4712 rs.add (rv);
4716 delete regions;
4722 void
4723 Editor::get_regions_after (RegionSelection& rs, nframes64_t where, const TrackSelection& ts) const
4725 const TrackSelection* tracks;
4727 if (ts.empty()) {
4728 tracks = &track_views;
4729 } else {
4730 tracks = &ts;
4733 for (TrackSelection::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4735 AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*t);
4737 if (atv) {
4738 boost::shared_ptr<Diskstream> ds;
4739 boost::shared_ptr<Playlist> pl;
4741 if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()))) {
4743 Playlist::RegionList* regions = pl->regions_touched ((nframes64_t) floor ( (double)where * ds->speed()), max_frames);
4745 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4747 RegionView* rv = atv->audio_view()->find_view (*i);
4749 if (rv) {
4750 rs.push_back (rv);
4754 delete regions;
4760 void
4761 Editor::get_regions_for_action (RegionSelection& rs, bool allow_entered)
4763 if (selection->regions.empty()) {
4765 if (selection->tracks.empty()) {
4767 /* no regions or tracks selected
4770 if (entered_regionview && mouse_mode == Editing::MouseObject) {
4772 /* entered regionview is valid and we're in object mode -
4773 just use entered regionview
4776 rs.add (entered_regionview);
4779 return;
4781 } else {
4783 /* no regions selected, so get all regions at the edit point across
4784 all selected tracks.
4787 nframes64_t where = get_preferred_edit_position();
4788 get_regions_at (rs, where, selection->tracks);
4790 /* if the entered regionview wasn't selected and neither was its track
4791 then add it.
4794 if (entered_regionview != 0 &&
4795 !selection->selected (entered_regionview) &&
4796 !selection->selected (&entered_regionview->get_time_axis_view())) {
4797 rs.add (entered_regionview);
4801 } else {
4803 /* just use the selected regions */
4805 rs = selection->regions;
4807 /* if the entered regionview wasn't selected and we allow this sort of thing,
4808 then add it.
4811 if (allow_entered && entered_regionview && !selection->selected (entered_regionview)) {
4812 rs.add (entered_regionview);
4818 void
4819 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4822 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4824 RouteTimeAxisView* tatv;
4826 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4828 boost::shared_ptr<Playlist> pl;
4829 vector<boost::shared_ptr<Region> > results;
4830 RegionView* marv;
4831 boost::shared_ptr<Diskstream> ds;
4833 if ((ds = tatv->get_diskstream()) == 0) {
4834 /* bus */
4835 continue;
4838 if ((pl = (ds->playlist())) != 0) {
4839 pl->get_region_list_equivalent_regions (region, results);
4842 for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4843 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4844 regions.push_back (marv);
4852 void
4853 Editor::show_rhythm_ferret ()
4855 if (rhythm_ferret == 0) {
4856 rhythm_ferret = new RhythmFerret(*this);
4859 rhythm_ferret->set_session (session);
4860 rhythm_ferret->show ();
4861 rhythm_ferret->present ();
4864 void
4865 Editor::first_idle ()
4867 MessageDialog* dialog = 0;
4869 if (track_views.size() > 1) {
4870 dialog = new MessageDialog (*this,
4871 _("Please wait while Ardour loads visual data"),
4872 true,
4873 Gtk::MESSAGE_INFO,
4874 Gtk::BUTTONS_NONE);
4875 dialog->present ();
4876 ARDOUR_UI::instance()->flush_pending ();
4879 for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4880 (*t)->first_idle();
4883 // first idle adds route children (automation tracks), so we need to redisplay here
4884 redisplay_route_list();
4886 if (dialog) {
4887 delete dialog;
4890 _have_idled = true;
4893 void
4894 Editor::start_resize_line_ops ()
4896 #if 0
4897 old_resize_line_y = -1;
4898 resize_line_y = -1;
4899 need_resize_line = true;
4900 #endif
4903 void
4904 Editor::end_resize_line_ops ()
4906 #if 0
4907 need_resize_line = false;
4909 if (old_resize_line_y >= 0) {
4910 Gdk::Rectangle r (0, old_resize_line_y, (int) canvas_width, 3);
4911 Glib::RefPtr<Gdk::Window> win = get_window();
4912 cerr << "Final invalidation at " << old_resize_line_y << endl;
4913 win->invalidate_rect (r, false);
4915 #endif
4918 void
4919 Editor::queue_draw_resize_line (int at)
4921 #if 0
4922 Glib::RefPtr<Gdk::Window> win = get_window();
4924 resize_line_y = at;
4926 if (win && canvas_width) {
4928 int controls_width = controls_layout.get_width();
4929 int xroot, discard;
4931 controls_layout.get_window()->get_origin (xroot, discard);
4933 if (old_resize_line_y >= 0) {
4935 /* redraw where it used to be */
4938 Gdk::Rectangle r (0, old_resize_line_y - 1, controls_width + (int) canvas_width, 3);
4939 win->invalidate_rect (r, true);
4940 cerr << "invalidate " << xroot << "," << old_resize_line_y - 1 << ' '
4941 << controls_width + canvas_width << " x 3\n";
4944 /* draw where it is */
4946 Gdk::Rectangle r (0, at - 1, controls_width + (int) canvas_width, 3);
4947 win->invalidate_rect (r, true);
4949 #endif
4952 bool
4953 Editor::on_expose_event (GdkEventExpose* ev)
4955 /* cerr << "+++ editor expose "
4956 << ev->area.x << ',' << ev->area.y
4957 << ' '
4958 << ev->area.width << " x " << ev->area.height
4959 << " need reize ? " << need_resize_line
4960 << endl;
4962 bool ret = Window::on_expose_event (ev);
4964 #if 0
4965 if (need_resize_line) {
4967 int xroot, yroot, discard;
4968 int controls_width;
4970 /* Our root coordinates for drawing the line will be the left edge
4971 of the track controls, and the upper left edge of our own window.
4974 get_window()->get_origin (discard, yroot);
4975 controls_layout.get_window()->get_origin (xroot, discard);
4976 controls_width = controls_layout.get_width();
4978 GdkRectangle lr;
4979 GdkRectangle intersection;
4981 lr.x = 0;
4982 lr.y = resize_line_y;
4983 lr.width = controls_width + (int) canvas_width;
4984 lr.height = 3;
4986 if (gdk_rectangle_intersect (&lr, &ev->area, &intersection)) {
4988 Glib::RefPtr<Gtk::Style> style (get_style());
4989 Glib::RefPtr<Gdk::GC> black_gc (style->get_black_gc ());
4990 Glib::RefPtr<Gdk::GC> gc = wrap (black_gc->gobj_copy(), false);
4992 /* draw on root window */
4994 GdkWindow* win = gdk_get_default_root_window();
4996 gc->set_subwindow (Gdk::INCLUDE_INFERIORS);
4997 gc->set_line_attributes (3, Gdk::LINE_SOLID,
4998 Gdk::CAP_NOT_LAST,
4999 Gdk::JOIN_MITER);
5001 gdk_draw_line (win, gc->gobj(),
5003 resize_line_y,
5004 (int) canvas_width + controls_width,
5005 resize_line_y);
5006 #if 0
5007 cerr << "drew line @ " << xroot << ", " << yroot + resize_line_y
5008 << " to " << xroot + (int) canvas_width + controls_width
5009 << ", " << yroot + resize_line_y
5010 << endl;
5011 #endif
5012 old_resize_line_y = resize_line_y;
5013 cerr << "NEXT EXPOSE SHOULD BE AT " << old_resize_line_y << endl;
5014 } else {
5015 cerr << "no intersect with "
5016 << lr.x << ',' << lr.y
5017 << ' '
5018 << lr.width << " x " << lr.height
5019 << endl;
5023 //cerr << "--- editor expose\n";
5024 #endif
5026 return ret;
5029 static gboolean
5030 _idle_resizer (gpointer arg)
5032 return ((Editor*)arg)->idle_resize ();
5035 void
5036 Editor::add_to_idle_resize (TimeAxisView* view, uint32_t h)
5038 if (resize_idle_id < 0) {
5039 resize_idle_id = g_idle_add (_idle_resizer, this);
5042 resize_idle_target = h;
5044 pending_resizes.push_back (view);
5046 if (selection->selected (view) && !selection->tracks.empty()) {
5047 pending_resizes.insert (pending_resizes.end(), selection->tracks.begin(), selection->tracks.end());
5051 bool
5052 Editor::idle_resize ()
5054 for (vector<TimeAxisView*>::iterator i = pending_resizes.begin(); i != pending_resizes.end(); ++i) {
5055 (*i)->idle_resize (resize_idle_target);
5057 pending_resizes.clear();
5058 //flush_canvas ();
5059 resize_idle_id = -1;
5060 return false;