d45d89a96e6106db35ce1c9c694b99dc4f34d90a
[ardour.git] / gtk2_ardour / editor.cc
1 /*
2     Copyright (C) 2000-2009 Paul Davis
3
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.
8
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.
13
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.
17
18 */
19
20 /* Note: public Editor methods are documented in public_editor.h */
21
22 #include <stdint.h>
23 #include <unistd.h>
24 #include <cstdlib>
25 #include <cmath>
26 #include <string>
27 #include <algorithm>
28 #include <map>
29
30 #include "ardour_ui.h"
31 /*
32  * ardour_ui.h include was moved to the top of the list
33  * due to a conflicting definition of 'Style' between
34  * Apple's MacTypes.h and BarController.
35  */
36
37 #include <boost/none.hpp>
38
39 #include <sigc++/bind.h>
40
41 #include "pbd/convert.h"
42 #include "pbd/error.h"
43 #include "pbd/enumwriter.h"
44 #include "pbd/memento_command.h"
45 #include "pbd/unknown_type.h"
46
47 #include <glibmm/miscutils.h>
48 #include <gtkmm/image.h>
49 #include <gdkmm/color.h>
50 #include <gdkmm/bitmap.h>
51
52 #include "gtkmm2ext/bindings.h"
53 #include "gtkmm2ext/grouped_buttons.h"
54 #include "gtkmm2ext/gtk_ui.h"
55 #include "gtkmm2ext/tearoff.h"
56 #include "gtkmm2ext/utils.h"
57 #include "gtkmm2ext/window_title.h"
58 #include "gtkmm2ext/choice.h"
59 #include "gtkmm2ext/cell_renderer_pixbuf_toggle.h"
60
61 #include "ardour/audio_diskstream.h"
62 #include "ardour/audio_track.h"
63 #include "ardour/audioplaylist.h"
64 #include "ardour/audioregion.h"
65 #include "ardour/location.h"
66 #include "ardour/midi_region.h"
67 #include "ardour/plugin_manager.h"
68 #include "ardour/profile.h"
69 #include "ardour/route_group.h"
70 #include "ardour/session_directory.h"
71 #include "ardour/session_route.h"
72 #include "ardour/session_state_utils.h"
73 #include "ardour/tempo.h"
74 #include "ardour/utils.h"
75 #include "ardour/session_playlists.h"
76 #include "ardour/audioengine.h"
77
78 #include "control_protocol/control_protocol.h"
79
80 #include "actions.h"
81 #include "actions.h"
82 #include "analysis_window.h"
83 #include "audio_clock.h"
84 #include "audio_region_view.h"
85 #include "audio_streamview.h"
86 #include "audio_time_axis.h"
87 #include "automation_time_axis.h"
88 #include "bundle_manager.h"
89 #include "canvas-noevent-text.h"
90 #include "canvas_impl.h"
91 #include "crossfade_edit.h"
92 #include "crossfade_view.h"
93 #include "debug.h"
94 #include "editing.h"
95 #include "editor.h"
96 #include "editor_cursors.h"
97 #include "editor_drag.h"
98 #include "editor_group_tabs.h"
99 #include "editor_locations.h"
100 #include "editor_regions.h"
101 #include "editor_route_groups.h"
102 #include "editor_routes.h"
103 #include "editor_snapshots.h"
104 #include "editor_summary.h"
105 #include "global_port_matrix.h"
106 #include "gui_object.h"
107 #include "gui_thread.h"
108 #include "keyboard.h"
109 #include "marker.h"
110 #include "midi_time_axis.h"
111 #include "mixer_strip.h"
112 #include "mouse_cursors.h"
113 #include "playlist_selector.h"
114 #include "public_editor.h"
115 #include "region_layering_order_editor.h"
116 #include "rgb_macros.h"
117 #include "rhythm_ferret.h"
118 #include "selection.h"
119 #include "sfdb_ui.h"
120 #include "simpleline.h"
121 #include "tempo_lines.h"
122 #include "time_axis_view.h"
123 #include "utils.h"
124
125 #include "i18n.h"
126
127 #ifdef WITH_CMT
128 #include "imageframe_socket_handler.h"
129 #endif
130
131 using namespace std;
132 using namespace ARDOUR;
133 using namespace PBD;
134 using namespace Gtk;
135 using namespace Glib;
136 using namespace Gtkmm2ext;
137 using namespace Editing;
138
139 using PBD::internationalize;
140 using PBD::atoi;
141 using Gtkmm2ext::Keyboard;
142
143 const double Editor::timebar_height = 15.0;
144
145 static const gchar *_snap_type_strings[] = {
146         N_("CD Frames"),
147         N_("Timecode Frames"),
148         N_("Timecode Seconds"),
149         N_("Timecode Minutes"),
150         N_("Seconds"),
151         N_("Minutes"),
152         N_("Beats/32"),
153         N_("Beats/28"),
154         N_("Beats/24"),
155         N_("Beats/20"),
156         N_("Beats/16"),
157         N_("Beats/14"),
158         N_("Beats/12"),
159         N_("Beats/10"),
160         N_("Beats/8"),
161         N_("Beats/7"),
162         N_("Beats/6"),
163         N_("Beats/5"),
164         N_("Beats/4"),
165         N_("Beats/3"),
166         N_("Beats/2"),
167         N_("Beats"),
168         N_("Bars"),
169         N_("Marks"),
170         N_("Region starts"),
171         N_("Region ends"),
172         N_("Region syncs"),
173         N_("Region bounds"),
174         0
175 };
176
177 static const gchar *_snap_mode_strings[] = {
178         N_("No Grid"),
179         N_("Grid"),
180         N_("Magnetic"),
181         0
182 };
183
184 static const gchar *_edit_point_strings[] = {
185         N_("Playhead"),
186         N_("Marker"),
187         N_("Mouse"),
188         0
189 };
190
191 static const gchar *_zoom_focus_strings[] = {
192         N_("Left"),
193         N_("Right"),
194         N_("Center"),
195         N_("Playhead"),
196         N_("Mouse"),
197         N_("Edit point"),
198         0
199 };
200
201 #ifdef USE_RUBBERBAND
202 static const gchar *_rb_opt_strings[] = {
203         N_("Mushy"),
204         N_("Smooth"),
205         N_("Balanced multitimbral mixture"),
206         N_("Unpitched percussion with stable notes"),
207         N_("Crisp monophonic instrumental"),
208         N_("Unpitched solo percussion"),
209         N_("Resample without preserving pitch"),
210         0
211 };
212 #endif
213
214 void
215 show_me_the_size (Requisition* r, const char* what)
216 {
217         cerr << "size of " << what << " = " << r->width << " x " << r->height << endl;
218 }
219
220 #ifdef GTKOSX
221 static void
222 pane_size_watcher (Paned* pane)
223 {
224         /* if the handle of a pane vanishes into (at least) the tabs of a notebook,
225            it is no longer accessible. so stop that. this doesn't happen on X11,
226            just the quartz backend.
227
228            ugh.
229         */
230
231         int max_width_of_lhs = GTK_WIDGET(pane->gobj())->allocation.width - 25;
232
233         gint pos = pane->get_position ();
234
235         if (pos > max_width_of_lhs) {
236                 pane->set_position (max_width_of_lhs);
237         }
238 }
239 #endif
240
241 Editor::Editor ()
242         : _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
243
244           /* time display buttons */
245         , minsec_label (_("Mins:Secs"))
246         , bbt_label (_("Bars:Beats"))
247         , timecode_label (_("Timecode"))
248         , samples_label (_("Samples"))
249         , tempo_label (_("Tempo"))
250         , meter_label (_("Meter"))
251         , mark_label (_("Location Markers"))
252         , range_mark_label (_("Range Markers"))
253         , transport_mark_label (_("Loop/Punch Ranges"))
254         , cd_mark_label (_("CD Markers"))
255         , edit_packer (4, 4, true)
256
257           /* the values here don't matter: layout widgets
258              reset them as needed.
259           */
260
261         , vertical_adjustment (0.0, 0.0, 10.0, 400.0)
262
263           /* tool bar related */
264
265         , zoom_range_clock (new AudioClock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, false, true))
266
267         , toolbar_selection_clock_table (2,3)
268
269         , automation_mode_button (_("mode"))
270         , global_automation_button (_("automation"))
271
272         , _toolbar_viewport (*manage (new Gtk::Adjustment (0, 0, 1e10)), *manage (new Gtk::Adjustment (0, 0, 1e10)))
273         , midi_panic_button (_("Panic"))
274
275 #ifdef WITH_CMT
276         , image_socket_listener(0)
277 #endif
278
279           /* nudge */
280
281         , nudge_clock (new AudioClock (X_("nudge"), false, X_("NudgeClock"), true, false, true))
282         , meters_running(false)
283         , _pending_locate_request (false)
284         , _pending_initial_locate (false)
285         , _last_cut_copy_source_track (0)
286
287         , _region_selection_change_updates_region_list (true)
288 {
289         constructed = false;
290
291         /* we are a singleton */
292
293         PublicEditor::_instance = this;
294
295         _have_idled = false;
296
297         selection = new Selection (this);
298         cut_buffer = new Selection (this);
299
300         clicked_regionview = 0;
301         clicked_axisview = 0;
302         clicked_routeview = 0;
303         clicked_crossfadeview = 0;
304         clicked_control_point = 0;
305         last_update_frame = 0;
306         pre_press_cursor = 0;
307         _drags = new DragManager (this);
308         current_mixer_strip = 0;
309         current_bbt_points = 0;
310         tempo_lines = 0;
311
312         snap_type_strings =  I18N (_snap_type_strings);
313         snap_mode_strings =  I18N (_snap_mode_strings);
314         zoom_focus_strings = I18N (_zoom_focus_strings);
315         edit_point_strings = I18N (_edit_point_strings);
316 #ifdef USE_RUBBERBAND
317         rb_opt_strings = I18N (_rb_opt_strings);
318         rb_current_opt = 4;
319 #endif
320
321         snap_threshold = 5.0;
322         bbt_beat_subdivision = 4;
323         _canvas_width = 0;
324         _canvas_height = 0;
325         last_autoscroll_x = 0;
326         last_autoscroll_y = 0;
327         autoscroll_active = false;
328         autoscroll_timeout_tag = -1;
329         logo_item = 0;
330
331         analysis_window = 0;
332
333         current_interthread_info = 0;
334         _show_measures = true;
335         show_gain_after_trim = false;
336         last_item_entered = 0;
337
338         have_pending_keyboard_selection = false;
339         _follow_playhead = true;
340         _stationary_playhead = false;
341         _xfade_visibility = true;
342         editor_ruler_menu = 0;
343         no_ruler_shown_update = false;
344         marker_menu = 0;
345         range_marker_menu = 0;
346         marker_menu_item = 0;
347         tempo_or_meter_marker_menu = 0;
348         transport_marker_menu = 0;
349         new_transport_marker_menu = 0;
350         editor_mixer_strip_width = Wide;
351         show_editor_mixer_when_tracks_arrive = false;
352         region_edit_menu_split_multichannel_item = 0;
353         region_edit_menu_split_item = 0;
354         temp_location = 0;
355         leftmost_frame = 0;
356         current_stepping_trackview = 0;
357         entered_track = 0;
358         entered_regionview = 0;
359         entered_marker = 0;
360         clear_entered_track = false;
361         current_timefx = 0;
362         playhead_cursor = 0;
363         button_release_can_deselect = true;
364         _dragging_playhead = false;
365         _dragging_edit_point = false;
366         select_new_marker = false;
367         rhythm_ferret = 0;
368         layering_order_editor = 0;
369         no_save_visual = false;
370         resize_idle_id = -1;
371
372         scrubbing_direction = 0;
373
374         sfbrowser = 0;
375
376         location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get();
377         location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get();
378         location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get();
379         location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get();
380         location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get();
381
382         _edit_point = EditAtMouse;
383         _internal_editing = false;
384         current_canvas_cursor = 0;
385
386         frames_per_unit = 2048; /* too early to use reset_zoom () */
387
388         _scroll_callbacks = 0;
389
390         zoom_focus = ZoomFocusLeft;
391         set_zoom_focus (ZoomFocusLeft);
392         zoom_range_clock->ValueChanged.connect (sigc::mem_fun(*this, &Editor::zoom_adjustment_changed));
393
394         bbt_label.set_name ("EditorTimeButton");
395         bbt_label.set_size_request (-1, (int)timebar_height);
396         bbt_label.set_alignment (1.0, 0.5);
397         bbt_label.set_padding (5,0);
398         bbt_label.hide ();
399         bbt_label.set_no_show_all();
400         minsec_label.set_name ("EditorTimeButton");
401         minsec_label.set_size_request (-1, (int)timebar_height);
402         minsec_label.set_alignment (1.0, 0.5);
403         minsec_label.set_padding (5,0);
404         minsec_label.hide ();
405         minsec_label.set_no_show_all();
406         timecode_label.set_name ("EditorTimeButton");
407         timecode_label.set_size_request (-1, (int)timebar_height);
408         timecode_label.set_alignment (1.0, 0.5);
409         timecode_label.set_padding (5,0);
410         timecode_label.hide ();
411         timecode_label.set_no_show_all();
412         samples_label.set_name ("EditorTimeButton");
413         samples_label.set_size_request (-1, (int)timebar_height);
414         samples_label.set_alignment (1.0, 0.5);
415         samples_label.set_padding (5,0);
416         samples_label.hide ();
417         samples_label.set_no_show_all();
418
419         tempo_label.set_name ("EditorTimeButton");
420         tempo_label.set_size_request (-1, (int)timebar_height);
421         tempo_label.set_alignment (1.0, 0.5);
422         tempo_label.set_padding (5,0);
423         tempo_label.hide();
424         tempo_label.set_no_show_all();
425
426         meter_label.set_name ("EditorTimeButton");
427         meter_label.set_size_request (-1, (int)timebar_height);
428         meter_label.set_alignment (1.0, 0.5);
429         meter_label.set_padding (5,0);
430         meter_label.hide();
431         meter_label.set_no_show_all();
432
433         mark_label.set_name ("EditorTimeButton");
434         mark_label.set_size_request (-1, (int)timebar_height);
435         mark_label.set_alignment (1.0, 0.5);
436         mark_label.set_padding (5,0);
437         mark_label.hide();
438         mark_label.set_no_show_all();
439
440         cd_mark_label.set_name ("EditorTimeButton");
441         cd_mark_label.set_size_request (-1, (int)timebar_height);
442         cd_mark_label.set_alignment (1.0, 0.5);
443         cd_mark_label.set_padding (5,0);
444         cd_mark_label.hide();
445         cd_mark_label.set_no_show_all();
446
447         range_mark_label.set_name ("EditorTimeButton");
448         range_mark_label.set_size_request (-1, (int)timebar_height);
449         range_mark_label.set_alignment (1.0, 0.5);
450         range_mark_label.set_padding (5,0);
451         range_mark_label.hide();
452         range_mark_label.set_no_show_all();
453
454         transport_mark_label.set_name ("EditorTimeButton");
455         transport_mark_label.set_size_request (-1, (int)timebar_height);
456         transport_mark_label.set_alignment (1.0, 0.5);
457         transport_mark_label.set_padding (5,0);
458         transport_mark_label.hide();
459         transport_mark_label.set_no_show_all();
460
461         initialize_rulers ();
462         initialize_canvas ();
463
464         _summary = new EditorSummary (this);
465
466         selection->TimeChanged.connect (sigc::mem_fun(*this, &Editor::time_selection_changed));
467         selection->TracksChanged.connect (sigc::mem_fun(*this, &Editor::track_selection_changed));
468
469         editor_regions_selection_changed_connection = selection->RegionsChanged.connect (sigc::mem_fun(*this, &Editor::region_selection_changed));
470
471         selection->PointsChanged.connect (sigc::mem_fun(*this, &Editor::point_selection_changed));
472         selection->MarkersChanged.connect (sigc::mem_fun(*this, &Editor::marker_selection_changed));
473
474         edit_controls_vbox.set_spacing (0);
475         vertical_adjustment.signal_value_changed().connect (sigc::mem_fun(*this, &Editor::tie_vertical_scrolling), true);
476         track_canvas->signal_map_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_map_handler));
477
478         HBox* h = manage (new HBox);
479         _group_tabs = new EditorGroupTabs (this);
480         h->pack_start (*_group_tabs, PACK_SHRINK);
481         h->pack_start (edit_controls_vbox);
482         controls_layout.add (*h);
483
484         controls_layout.set_name ("EditControlsBase");
485         controls_layout.add_events (Gdk::SCROLL_MASK);
486         controls_layout.signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::control_layout_scroll), false);
487
488         controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
489         controls_layout.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_release));
490
491         _cursors = new MouseCursors;
492
493         ArdourCanvas::Canvas* time_pad = manage(new ArdourCanvas::Canvas());
494         ArdourCanvas::SimpleLine* pad_line_1 = manage(new ArdourCanvas::SimpleLine(*time_pad->root(),
495                         0.0, 1.0, 100.0, 1.0));
496
497         pad_line_1->property_color_rgba() = 0xFF0000FF;
498         pad_line_1->show();
499
500         time_pad->show();
501
502         time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2);
503         time_canvas_vbox.set_size_request (-1, -1);
504
505         ruler_label_event_box.add (ruler_label_vbox);
506         ruler_label_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
507         ruler_label_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
508
509         time_button_event_box.add (time_button_vbox);
510         time_button_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
511         time_button_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
512
513         /* these enable us to have a dedicated window (for cursor setting, etc.)
514            for the canvas areas.
515         */
516
517         track_canvas_event_box.add (*track_canvas);
518
519         time_canvas_event_box.add (time_canvas_vbox);
520         time_canvas_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
521
522         edit_packer.set_col_spacings (0);
523         edit_packer.set_row_spacings (0);
524         edit_packer.set_homogeneous (false);
525         edit_packer.set_border_width (0);
526         edit_packer.set_name ("EditorWindow");
527
528         /* labels for the rulers */
529         edit_packer.attach (ruler_label_event_box,   1, 2, 0, 1,    FILL,        SHRINK, 0, 0);
530         /* labels for the marker "tracks" */
531         edit_packer.attach (time_button_event_box,   1, 2, 1, 2,    FILL,        SHRINK, 0, 0);
532         /* the rulers */
533         edit_packer.attach (time_canvas_event_box,   2, 3, 0, 1,    FILL|EXPAND, FILL, 0, 0);
534         /* track controls */
535         edit_packer.attach (controls_layout,         0, 2, 2, 3,    FILL,        FILL|EXPAND, 0, 0);
536         /* main canvas */
537         edit_packer.attach (track_canvas_event_box,  2, 3, 1, 3,    FILL|EXPAND, FILL|EXPAND, 0, 0);
538
539         bottom_hbox.set_border_width (2);
540         bottom_hbox.set_spacing (3);
541
542         _route_groups = new EditorRouteGroups (this);
543         _routes = new EditorRoutes (this);
544         _regions = new EditorRegions (this);
545         _snapshots = new EditorSnapshots (this);
546         _locations = new EditorLocations (this);
547
548         add_notebook_page (_("Regions"), _regions->widget ());
549         add_notebook_page (_("Tracks & Busses"), _routes->widget ());
550         add_notebook_page (_("Snapshots"), _snapshots->widget ());
551         add_notebook_page (_("Route Groups"), _route_groups->widget ());
552         add_notebook_page (_("Ranges & Marks"), _locations->widget ());
553
554         _the_notebook.set_show_tabs (true);
555         _the_notebook.set_scrollable (true);
556         _the_notebook.popup_disable ();
557         _the_notebook.set_tab_pos (Gtk::POS_RIGHT);
558         _the_notebook.show_all ();
559
560         post_maximal_editor_width = 0;
561         post_maximal_horizontal_pane_position = 0;
562         post_maximal_editor_height = 0;
563         post_maximal_vertical_pane_position = 0;
564         _notebook_shrunk = false;
565
566         editor_summary_pane.pack1(edit_packer);
567
568         Button* summary_arrows_left_left = manage (new Button);
569         summary_arrows_left_left->add (*manage (new Arrow (ARROW_LEFT, SHADOW_NONE)));
570         summary_arrows_left_left->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), LEFT)));
571         summary_arrows_left_left->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
572
573         Button* summary_arrows_left_right = manage (new Button);
574         summary_arrows_left_right->add (*manage (new Arrow (ARROW_RIGHT, SHADOW_NONE)));
575         summary_arrows_left_right->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), RIGHT)));
576         summary_arrows_left_right->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
577
578         VBox* summary_arrows_left = manage (new VBox);
579         summary_arrows_left->pack_start (*summary_arrows_left_left);
580         summary_arrows_left->pack_start (*summary_arrows_left_right);
581
582         Button* summary_arrows_right_up = manage (new Button);
583         summary_arrows_right_up->add (*manage (new Arrow (ARROW_UP, SHADOW_NONE)));
584         summary_arrows_right_up->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), UP)));
585         summary_arrows_right_up->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
586
587         Button* summary_arrows_right_down = manage (new Button);
588         summary_arrows_right_down->add (*manage (new Arrow (ARROW_DOWN, SHADOW_NONE)));
589         summary_arrows_right_down->signal_pressed().connect (sigc::hide_return (sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), DOWN)));
590         summary_arrows_right_down->signal_released().connect (sigc::mem_fun (*this, &Editor::scroll_release));
591
592         VBox* summary_arrows_right = manage (new VBox);
593         summary_arrows_right->pack_start (*summary_arrows_right_up);
594         summary_arrows_right->pack_start (*summary_arrows_right_down);
595
596         Frame* summary_frame = manage (new Frame);
597         summary_frame->set_shadow_type (Gtk::SHADOW_ETCHED_IN);
598
599         summary_frame->add (*_summary);
600         summary_frame->show ();
601
602         _summary_hbox.pack_start (*summary_arrows_left, false, false);
603         _summary_hbox.pack_start (*summary_frame, true, true);
604         _summary_hbox.pack_start (*summary_arrows_right, false, false);
605
606         editor_summary_pane.pack2 (_summary_hbox);
607
608         edit_pane.pack1 (editor_summary_pane, true, true);
609         edit_pane.pack2 (_the_notebook, false, true);
610
611         editor_summary_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun (*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&editor_summary_pane)));
612
613         /* XXX: editor_summary_pane might need similar special OS X treatment to the edit_pane */
614
615         edit_pane.signal_size_allocate().connect (sigc::bind (sigc::mem_fun(*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&edit_pane)));
616 #ifdef GTKOSX
617         Glib::PropertyProxy<int> proxy = edit_pane.property_position();
618         proxy.signal_changed().connect (bind (sigc::ptr_fun (pane_size_watcher), static_cast<Paned*> (&edit_pane)));
619 #endif
620         top_hbox.pack_start (toolbar_frame);
621
622         HBox *hbox = manage (new HBox);
623         hbox->pack_start (edit_pane, true, true);
624
625         global_vpacker.pack_start (top_hbox, false, false);
626         global_vpacker.pack_start (*hbox, true, true);
627
628         global_hpacker.pack_start (global_vpacker, true, true);
629
630         set_name ("EditorWindow");
631         add_accel_group (ActionManager::ui_manager->get_accel_group());
632
633         status_bar_hpacker.show ();
634
635         vpacker.pack_end (status_bar_hpacker, false, false);
636         vpacker.pack_end (global_hpacker, true, true);
637
638         /* register actions now so that set_state() can find them and set toggles/checks etc */
639
640         register_actions ();
641
642         setup_toolbar ();
643         setup_midi_toolbar ();
644
645         _snap_type = SnapToBeat;
646         set_snap_to (_snap_type);
647         _snap_mode = SnapOff;
648         set_snap_mode (_snap_mode);
649         set_mouse_mode (MouseObject, true);
650         pre_internal_mouse_mode = MouseObject;
651         set_edit_point_preference (EditAtMouse, true);
652
653         _playlist_selector = new PlaylistSelector();
654         _playlist_selector->signal_delete_event().connect (sigc::bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
655
656         RegionView::RegionViewGoingAway.connect (*this, invalidator (*this),  ui_bind (&Editor::catch_vanishing_regionview, this, _1), gui_context());
657
658         /* nudge stuff */
659
660         nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
661         nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
662
663         nudge_forward_button.set_name ("TransportButton");
664         nudge_backward_button.set_name ("TransportButton");
665
666         fade_context_menu.set_name ("ArdourContextMenu");
667
668         /* icons, titles, WM stuff */
669
670         list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
671         Glib::RefPtr<Gdk::Pixbuf> icon;
672
673         if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
674                 window_icons.push_back (icon);
675         }
676         if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
677                 window_icons.push_back (icon);
678         }
679         if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
680                 window_icons.push_back (icon);
681         }
682         if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
683                 window_icons.push_back (icon);
684         }
685         if (!window_icons.empty()) {
686                 // set_icon_list (window_icons);
687                 set_default_icon_list (window_icons);
688         }
689
690         WindowTitle title(Glib::get_application_name());
691         title += _("Editor");
692         set_title (title.get_string());
693         set_wmclass (X_("ardour_editor"), PROGRAM_NAME);
694
695         add (vpacker);
696         add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
697
698         signal_configure_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
699         signal_delete_event().connect (sigc::mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
700
701         /* allow external control surfaces/protocols to do various things */
702
703         ControlProtocol::ZoomToSession.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_session, this), gui_context());
704         ControlProtocol::ZoomIn.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, false), gui_context());
705         ControlProtocol::ZoomOut.connect (*this, invalidator (*this), boost::bind (&Editor::temporal_zoom_step, this, true), gui_context());
706         ControlProtocol::ScrollTimeline.connect (*this, invalidator (*this), ui_bind (&Editor::control_scroll, this, _1), gui_context());
707         BasicUI::AccessAction.connect (*this, invalidator (*this), ui_bind (&Editor::access_action, this, _1, _2), gui_context());
708
709         /* problematic: has to return a value and thus cannot be x-thread */
710
711         Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
712
713         Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
714
715         TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
716
717         _ignore_region_action = false;
718         _last_region_menu_was_main = false;
719         _popup_region_menu_item = 0;
720
721         _show_marker_lines = false;
722         _over_region_trim_target = false;
723
724         /* Button bindings */
725
726         button_bindings = new Bindings;
727
728         XMLNode* node = button_settings();
729         if (node) {
730                 for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) {
731                         button_bindings->load (**i);
732                 }
733         }
734
735         constructed = true;
736         instant_save ();
737
738         setup_fade_images ();
739 }
740
741 Editor::~Editor()
742 {
743 #ifdef WITH_CMT
744         if(image_socket_listener) {
745                 if(image_socket_listener->is_connected())
746                 {
747                         image_socket_listener->close_connection() ;
748                 }
749
750                 delete image_socket_listener ;
751                 image_socket_listener = 0 ;
752         }
753 #endif
754
755         delete button_bindings;
756         delete _routes;
757         delete _route_groups;
758         delete track_canvas;
759         delete _drags;
760 }
761
762 XMLNode*
763 Editor::button_settings () const
764 {
765         XMLNode* settings = ARDOUR_UI::instance()->editor_settings();
766         XMLNode* node = find_named_node (*settings, X_("Buttons"));
767
768         if (!node) {
769                 cerr << "new empty Button node\n";
770                 node = new XMLNode (X_("Buttons"));
771         }
772
773         return node;
774 }
775
776 void
777 Editor::add_toplevel_controls (Container& cont)
778 {
779         vpacker.pack_start (cont, false, false);
780         cont.show_all ();
781 }
782
783 void
784 Editor::catch_vanishing_regionview (RegionView *rv)
785 {
786         /* note: the selection will take care of the vanishing
787            audioregionview by itself.
788         */
789
790         if (_drags->active() && _drags->have_item (rv->get_canvas_group()) && !_drags->ending()) {
791                 _drags->abort ();
792         }
793
794         if (clicked_regionview == rv) {
795                 clicked_regionview = 0;
796         }
797
798         if (entered_regionview == rv) {
799                 set_entered_regionview (0);
800         }
801
802         if (!_all_region_actions_sensitized) {
803                 sensitize_all_region_actions (true);
804         }
805
806         _over_region_trim_target = false;
807 }
808
809 void
810 Editor::set_entered_regionview (RegionView* rv)
811 {
812         if (rv == entered_regionview) {
813                 return;
814         }
815
816         if (entered_regionview) {
817                 entered_regionview->exited ();
818         }
819
820         if ((entered_regionview = rv) != 0) {
821                 entered_regionview->entered (internal_editing ());
822         }
823
824         if (!_all_region_actions_sensitized && _last_region_menu_was_main) {
825                 /* This RegionView entry might have changed what region actions
826                    are allowed, so sensitize them all in case a key is pressed.
827                 */
828                 sensitize_all_region_actions (true);
829         }
830 }
831
832 void
833 Editor::set_entered_track (TimeAxisView* tav)
834 {
835         if (entered_track) {
836                 entered_track->exited ();
837         }
838
839         if ((entered_track = tav) != 0) {
840                 entered_track->entered ();
841         }
842 }
843
844 void
845 Editor::show_window ()
846 {
847         if (!is_visible ()) {
848                 show_all ();
849
850                 /* XXX: this is a bit unfortunate; it would probably
851                    be nicer if we could just call show () above rather
852                    than needing the show_all ()
853                 */
854
855                 /* re-hide stuff if necessary */
856                 editor_list_button_toggled ();
857                 parameter_changed ("show-summary");
858                 parameter_changed ("show-group-tabs");
859                 parameter_changed ("show-zoom-tools");
860
861                 /* now reset all audio_time_axis heights, because widgets might need
862                    to be re-hidden
863                 */
864
865                 TimeAxisView *tv;
866
867                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
868                         tv = (static_cast<TimeAxisView*>(*i));
869                         tv->reset_height ();
870                 }
871
872                 if (current_mixer_strip) {
873                         current_mixer_strip->hide_things ();
874                 }
875         }
876
877         present ();
878 }
879
880 void
881 Editor::instant_save ()
882 {
883         if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
884                 return;
885         }
886
887         if (_session) {
888                 _session->add_instant_xml(get_state());
889         } else {
890                 Config->add_instant_xml(get_state());
891         }
892 }
893
894 void
895 Editor::zoom_adjustment_changed ()
896 {
897         if (_session == 0) {
898                 return;
899         }
900
901         double fpu = zoom_range_clock->current_duration() / _canvas_width;
902
903         if (fpu < 1.0) {
904                 fpu = 1.0;
905                 zoom_range_clock->set ((framepos_t) floor (fpu * _canvas_width));
906         } else if (fpu > _session->current_end_frame() / _canvas_width) {
907                 fpu = _session->current_end_frame() / _canvas_width;
908                 zoom_range_clock->set ((framepos_t) floor (fpu * _canvas_width));
909         }
910
911         temporal_zoom (fpu);
912 }
913
914 void
915 Editor::control_scroll (float fraction)
916 {
917         ENSURE_GUI_THREAD (*this, &Editor::control_scroll, fraction)
918
919         if (!_session) {
920                 return;
921         }
922
923         double step = fraction * current_page_frames();
924
925         /*
926                 _control_scroll_target is an optional<T>
927
928                 it acts like a pointer to an framepos_t, with
929                 a operator conversion to boolean to check
930                 that it has a value could possibly use
931                 playhead_cursor->current_frame to store the
932                 value and a boolean in the class to know
933                 when it's out of date
934         */
935
936         if (!_control_scroll_target) {
937                 _control_scroll_target = _session->transport_frame();
938                 _dragging_playhead = true;
939         }
940
941         if ((fraction < 0.0f) && (*_control_scroll_target < (framepos_t) fabs(step))) {
942                 *_control_scroll_target = 0;
943         } else if ((fraction > 0.0f) && (max_framepos - *_control_scroll_target < step)) {
944                 *_control_scroll_target = max_framepos - (current_page_frames()*2); // allow room for slop in where the PH is on the screen
945         } else {
946                 *_control_scroll_target += (framepos_t) floor (step);
947         }
948
949         /* move visuals, we'll catch up with it later */
950
951         playhead_cursor->set_position (*_control_scroll_target);
952         UpdateAllTransportClocks (*_control_scroll_target);
953
954         if (*_control_scroll_target > (current_page_frames() / 2)) {
955                 /* try to center PH in window */
956                 reset_x_origin (*_control_scroll_target - (current_page_frames()/2));
957         } else {
958                 reset_x_origin (0);
959         }
960
961         /*
962                 Now we do a timeout to actually bring the session to the right place
963                 according to the playhead. This is to avoid reading disk buffers on every
964                 call to control_scroll, which is driven by ScrollTimeline and therefore
965                 probably by a control surface wheel which can generate lots of events.
966         */
967         /* cancel the existing timeout */
968
969         control_scroll_connection.disconnect ();
970
971         /* add the next timeout */
972
973         control_scroll_connection = Glib::signal_timeout().connect (sigc::bind (sigc::mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250);
974 }
975
976 bool
977 Editor::deferred_control_scroll (framepos_t /*target*/)
978 {
979         _session->request_locate (*_control_scroll_target, _session->transport_rolling());
980         // reset for next stream
981         _control_scroll_target = boost::none;
982         _dragging_playhead = false;
983         return false;
984 }
985
986 void
987 Editor::access_action (std::string action_group, std::string action_item)
988 {
989         if (!_session) {
990                 return;
991         }
992
993         ENSURE_GUI_THREAD (*this, &Editor::access_action, action_group, action_item)
994
995         RefPtr<Action> act;
996         act = ActionManager::get_action( action_group.c_str(), action_item.c_str() );
997
998         if (act) {
999                 act->activate();
1000         }
1001 }
1002
1003 void
1004 Editor::on_realize ()
1005 {
1006         Window::on_realize ();
1007         Realized ();
1008 }
1009
1010 void
1011 Editor::map_position_change (framepos_t frame)
1012 {
1013         ENSURE_GUI_THREAD (*this, &Editor::map_position_change, frame)
1014
1015         if (_session == 0) {
1016                 return;
1017         }
1018
1019         if (_follow_playhead) {
1020                 center_screen (frame);
1021         }
1022
1023         playhead_cursor->set_position (frame);
1024 }
1025
1026 void
1027 Editor::center_screen (framepos_t frame)
1028 {
1029         double page = _canvas_width * frames_per_unit;
1030
1031         /* if we're off the page, then scroll.
1032          */
1033
1034         if (frame < leftmost_frame || frame >= leftmost_frame + page) {
1035                 center_screen_internal (frame, page);
1036         }
1037 }
1038
1039 void
1040 Editor::center_screen_internal (framepos_t frame, float page)
1041 {
1042         page /= 2;
1043
1044         if (frame > page) {
1045                 frame -= (framepos_t) page;
1046         } else {
1047                 frame = 0;
1048         }
1049
1050         reset_x_origin (frame);
1051 }
1052
1053
1054 void
1055 Editor::update_title ()
1056 {
1057         ENSURE_GUI_THREAD (*this, &Editor::update_title)
1058
1059         if (_session) {
1060                 bool dirty = _session->dirty();
1061
1062                 string session_name;
1063
1064                 if (_session->snap_name() != _session->name()) {
1065                         session_name = _session->snap_name();
1066                 } else {
1067                         session_name = _session->name();
1068                 }
1069
1070                 if (dirty) {
1071                         session_name = "*" + session_name;
1072                 }
1073
1074                 WindowTitle title(session_name);
1075                 title += Glib::get_application_name();
1076                 set_title (title.get_string());
1077         }
1078 }
1079
1080 void
1081 Editor::set_session (Session *t)
1082 {
1083         SessionHandlePtr::set_session (t);
1084
1085         if (!_session) {
1086                 return;
1087         }
1088
1089         zoom_range_clock->set_session (_session);
1090         _playlist_selector->set_session (_session);
1091         nudge_clock->set_session (_session);
1092         _summary->set_session (_session);
1093         _group_tabs->set_session (_session);
1094         _route_groups->set_session (_session);
1095         _regions->set_session (_session);
1096         _snapshots->set_session (_session);
1097         _routes->set_session (_session);
1098         _locations->set_session (_session);
1099
1100         if (rhythm_ferret) {
1101                 rhythm_ferret->set_session (_session);
1102         }
1103
1104         if (analysis_window) {
1105                 analysis_window->set_session (_session);
1106         }
1107
1108         if (sfbrowser) {
1109                 sfbrowser->set_session (_session);
1110         }
1111
1112         compute_fixed_ruler_scale ();
1113
1114         XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1115         set_state (*node, Stateful::loading_state_version);
1116
1117         /* catch up with the playhead */
1118
1119         _session->request_locate (playhead_cursor->current_frame);
1120         _pending_initial_locate = true;
1121
1122         update_title ();
1123
1124         /* These signals can all be emitted by a non-GUI thread. Therefore the
1125            handlers for them must not attempt to directly interact with the GUI,
1126            but use Gtkmm2ext::UI::instance()->call_slot();
1127         */
1128
1129         _session->StepEditStatusChange.connect (_session_connections, invalidator (*this), ui_bind(&Editor::step_edit_status_change, this, _1), gui_context());
1130         _session->TransportStateChange.connect (_session_connections, invalidator (*this), boost::bind (&Editor::map_transport_state, this), gui_context());
1131         _session->PositionChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::map_position_change, this, _1), gui_context());
1132         _session->RouteAdded.connect (_session_connections, invalidator (*this), ui_bind (&Editor::handle_new_route, this, _1), gui_context());
1133         _session->DirtyChanged.connect (_session_connections, invalidator (*this), boost::bind (&Editor::update_title, this), gui_context());
1134         _session->tempo_map().PropertyChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::tempo_map_changed, this, _1), gui_context());
1135         _session->Located.connect (_session_connections, invalidator (*this), boost::bind (&Editor::located, this), gui_context());
1136         _session->config.ParameterChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::parameter_changed, this, _1), gui_context());
1137         _session->StateSaved.connect (_session_connections, invalidator (*this), ui_bind (&Editor::session_state_saved, this, _1), gui_context());
1138         _session->locations()->added.connect (_session_connections, invalidator (*this), ui_bind (&Editor::add_new_location, this, _1), gui_context());
1139         _session->locations()->removed.connect (_session_connections, invalidator (*this), ui_bind (&Editor::location_gone, this, _1), gui_context());
1140         _session->locations()->changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::refresh_location_display, this), gui_context());
1141         _session->locations()->StateChanged.connect (_session_connections, invalidator (*this), ui_bind (&Editor::refresh_location_display, this), gui_context());
1142         _session->history().Changed.connect (_session_connections, invalidator (*this), boost::bind (&Editor::history_changed, this), gui_context());
1143
1144         if (Profile->get_sae()) {
1145                 Timecode::BBT_Time bbt;
1146                 bbt.bars = 0;
1147                 bbt.beats = 0;
1148                 bbt.ticks = 120;
1149                 framepos_t pos = _session->tempo_map().bbt_duration_at (0, bbt, 1);
1150                 nudge_clock->set_mode(AudioClock::BBT);
1151                 nudge_clock->set (pos, true, 0, AudioClock::BBT);
1152
1153         } else {
1154                 nudge_clock->set (_session->frame_rate() * 5, true, 0, AudioClock::Timecode); // default of 5 seconds
1155         }
1156
1157         playhead_cursor->canvas_item.show ();
1158
1159         Location* loc = _session->locations()->auto_loop_location();
1160         if (loc == 0) {
1161                 loc = new Location (*_session, 0, _session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1162
1163                 if (loc->start() == loc->end()) {
1164                         loc->set_end (loc->start() + 1);
1165                 }
1166
1167                 _session->locations()->add (loc, false);
1168                 _session->set_auto_loop_location (loc);
1169         } else {
1170                 // force name
1171                 loc->set_name (_("Loop"));
1172         }
1173
1174         loc = _session->locations()->auto_punch_location();
1175
1176         if (loc == 0) {
1177                 loc = new Location (*_session, 0, _session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1178
1179                 if (loc->start() == loc->end()) {
1180                         loc->set_end (loc->start() + 1);
1181                 }
1182
1183                 _session->locations()->add (loc, false);
1184                 _session->set_auto_punch_location (loc);
1185         } else {
1186                 // force name
1187                 loc->set_name (_("Punch"));
1188         }
1189
1190         boost::function<void (string)> pc (boost::bind (&Editor::parameter_changed, this, _1));
1191         Config->map_parameters (pc);
1192         _session->config.map_parameters (pc);
1193
1194         refresh_location_display ();
1195
1196         restore_ruler_visibility ();
1197         //tempo_map_changed (PropertyChange (0));
1198         _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1199
1200         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1201                 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1202         }
1203
1204         super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
1205                 sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
1206                 );
1207
1208         switch (_snap_type) {
1209         case SnapToRegionStart:
1210         case SnapToRegionEnd:
1211         case SnapToRegionSync:
1212         case SnapToRegionBoundary:
1213                 build_region_boundary_cache ();
1214                 break;
1215
1216         default:
1217                 break;
1218         }
1219
1220         /* register for undo history */
1221         _session->register_with_memento_command_factory(_id, this);
1222
1223         ActionManager::ui_manager->signal_pre_activate().connect (sigc::mem_fun (*this, &Editor::action_pre_activated));
1224
1225         start_updating_meters ();
1226 }
1227
1228 void
1229 Editor::action_pre_activated (Glib::RefPtr<Action> const & a)
1230 {
1231         if (a->get_name() == "RegionMenu") {
1232                 /* When the main menu's region menu is opened, we setup the actions so that they look right
1233                    in the menu.  I can't find a way of getting a signal when this menu is subsequently closed,
1234                    so we resensitize all region actions when the entered regionview or the region selection
1235                    changes.  HOWEVER we can't always resensitize on entered_regionview change because that
1236                    happens after the region context menu is opened.  So we set a flag here, too.
1237
1238                    What a carry on :(
1239                 */
1240                 sensitize_the_right_region_actions ();
1241                 _last_region_menu_was_main = true;
1242         }
1243 }
1244
1245 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1246 void
1247 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1248 {
1249         using namespace Menu_Helpers;
1250         AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1251
1252         if (arv == 0) {
1253                 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1254                 /*NOTREACHED*/
1255         }
1256
1257         MenuList& items (fade_context_menu.items());
1258
1259         items.clear ();
1260
1261         switch (item_type) {
1262         case FadeInItem:
1263         case FadeInHandleItem:
1264                 if (arv->audio_region()->fade_in_active()) {
1265                         items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
1266                 } else {
1267                         items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
1268                 }
1269
1270                 items.push_back (SeparatorElem());
1271
1272                 if (Profile->get_sae()) {
1273
1274                         items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)));
1275                         items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)));
1276
1277                 } else {
1278
1279                         items.push_back (
1280                                 ImageMenuElem (
1281                                         _("Linear"),
1282                                         *_fade_in_images[FadeLinear],
1283                                         sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)
1284                                         )
1285                                 );
1286
1287                         dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1288
1289                         items.push_back (
1290                                 ImageMenuElem (
1291                                         _("Slowest"),
1292                                         *_fade_in_images[FadeFast],
1293                                         sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)
1294                                         ));
1295
1296                         dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1297
1298                         items.push_back (
1299                                 ImageMenuElem (
1300                                         _("Slow"),
1301                                         *_fade_in_images[FadeLogB],
1302                                         sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogB)
1303                                         ));
1304
1305                         dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1306
1307                         items.push_back (
1308                                 ImageMenuElem (
1309                                         _("Fast"),
1310                                         *_fade_in_images[FadeLogA],
1311                                         sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLogA)
1312                                         ));
1313
1314                         dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1315
1316                         items.push_back (
1317                                 ImageMenuElem (
1318                                         _("Fastest"),
1319                                         *_fade_in_images[FadeSlow],
1320                                         sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow)
1321                                         ));
1322
1323                         dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1324                 }
1325
1326                 break;
1327
1328         case FadeOutItem:
1329         case FadeOutHandleItem:
1330                 if (arv->audio_region()->fade_out_active()) {
1331                         items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
1332                 } else {
1333                         items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
1334                 }
1335
1336                 items.push_back (SeparatorElem());
1337
1338                 if (Profile->get_sae()) {
1339                         items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)));
1340                         items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)));
1341                 } else {
1342
1343                         items.push_back (
1344                                 ImageMenuElem (
1345                                         _("Linear"),
1346                                         *_fade_out_images[FadeLinear],
1347                                         sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)
1348                                         )
1349                                 );
1350
1351                         dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1352
1353                         items.push_back (
1354                                 ImageMenuElem (
1355                                         _("Slowest"),
1356                                         *_fade_out_images[FadeFast],
1357                                         sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)
1358                                         ));
1359
1360                         dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1361
1362                         items.push_back (
1363                                 ImageMenuElem (
1364                                         _("Slow"),
1365                                         *_fade_out_images[FadeLogB],
1366                                         sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogA)
1367                                         ));
1368
1369                         dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1370
1371                         items.push_back (
1372                                 ImageMenuElem (
1373                                         _("Fast"),
1374                                         *_fade_out_images[FadeLogA],
1375                                         sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLogB)
1376                                         ));
1377
1378                         dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1379
1380                         items.push_back (
1381                                 ImageMenuElem (
1382                                         _("Fastest"),
1383                                         *_fade_out_images[FadeSlow],
1384                                         sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast)
1385                                         ));
1386
1387                         dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
1388                 }
1389
1390                 break;
1391
1392         default:
1393                 fatal << _("programming error: ")
1394                       << X_("non-fade canvas item passed to popup_fade_context_menu()")
1395                       << endmsg;
1396                 /*NOTREACHED*/
1397         }
1398
1399         fade_context_menu.popup (button, time);
1400 }
1401
1402 void
1403 Editor::popup_track_context_menu (int button, int32_t time, ItemType item_type, bool with_selection)
1404 {
1405         using namespace Menu_Helpers;
1406         Menu* (Editor::*build_menu_function)();
1407         Menu *menu;
1408
1409         switch (item_type) {
1410         case RegionItem:
1411         case RegionViewName:
1412         case RegionViewNameHighlight:
1413         case LeftFrameHandle:
1414         case RightFrameHandle:
1415                 if (with_selection) {
1416                         build_menu_function = &Editor::build_track_selection_context_menu;
1417                 } else {
1418                         build_menu_function = &Editor::build_track_region_context_menu;
1419                 }
1420                 break;
1421
1422         case SelectionItem:
1423                 if (with_selection) {
1424                         build_menu_function = &Editor::build_track_selection_context_menu;
1425                 } else {
1426                         build_menu_function = &Editor::build_track_context_menu;
1427                 }
1428                 break;
1429
1430         case CrossfadeViewItem:
1431                 build_menu_function = &Editor::build_track_crossfade_context_menu;
1432                 break;
1433
1434         case StreamItem:
1435                 if (clicked_routeview->track()) {
1436                         build_menu_function = &Editor::build_track_context_menu;
1437                 } else {
1438                         build_menu_function = &Editor::build_track_bus_context_menu;
1439                 }
1440                 break;
1441
1442         default:
1443                 /* probably shouldn't happen but if it does, we don't care */
1444                 return;
1445         }
1446
1447         menu = (this->*build_menu_function)();
1448         menu->set_name ("ArdourContextMenu");
1449
1450         /* now handle specific situations */
1451
1452         switch (item_type) {
1453         case RegionItem:
1454         case RegionViewName:
1455         case RegionViewNameHighlight:
1456         case LeftFrameHandle:
1457         case RightFrameHandle:
1458                 if (!with_selection) {
1459                         if (region_edit_menu_split_item) {
1460                                 if (clicked_regionview && clicked_regionview->region()->covers (get_preferred_edit_position())) {
1461                                         ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, true);
1462                                 } else {
1463                                         ActionManager::set_sensitive (ActionManager::edit_point_in_region_sensitive_actions, false);
1464                                 }
1465                         }
1466                         if (region_edit_menu_split_multichannel_item) {
1467                                 if (clicked_regionview && clicked_regionview->region()->n_channels() > 1) {
1468                                         region_edit_menu_split_multichannel_item->set_sensitive (true);
1469                                 } else {
1470                                         region_edit_menu_split_multichannel_item->set_sensitive (false);
1471                                 }
1472                         }
1473                 }
1474                 break;
1475
1476         case SelectionItem:
1477                 break;
1478
1479         case CrossfadeViewItem:
1480                 break;
1481
1482         case StreamItem:
1483                 break;
1484
1485         default:
1486                 /* probably shouldn't happen but if it does, we don't care */
1487                 return;
1488         }
1489
1490         if (item_type != SelectionItem && clicked_routeview && clicked_routeview->audio_track()) {
1491
1492                 /* Bounce to disk */
1493
1494                 using namespace Menu_Helpers;
1495                 MenuList& edit_items  = menu->items();
1496
1497                 edit_items.push_back (SeparatorElem());
1498
1499                 switch (clicked_routeview->audio_track()->freeze_state()) {
1500                 case AudioTrack::NoFreeze:
1501                         edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1502                         break;
1503
1504                 case AudioTrack::Frozen:
1505                         edit_items.push_back (MenuElem (_("Unfreeze"), sigc::mem_fun(*this, &Editor::unfreeze_route)));
1506                         break;
1507
1508                 case AudioTrack::UnFrozen:
1509                         edit_items.push_back (MenuElem (_("Freeze"), sigc::mem_fun(*this, &Editor::freeze_route)));
1510                         break;
1511                 }
1512
1513         }
1514
1515         if (item_type == StreamItem && clicked_routeview) {
1516                 clicked_routeview->build_underlay_menu(menu);
1517         }
1518
1519         /* When the region menu is opened, we setup the actions so that they look right
1520            in the menu.
1521         */
1522         sensitize_the_right_region_actions ();
1523         _last_region_menu_was_main = false;
1524
1525         menu->signal_hide().connect (sigc::bind (sigc::mem_fun (*this, &Editor::sensitize_all_region_actions), true));
1526         menu->popup (button, time);
1527 }
1528
1529 Menu*
1530 Editor::build_track_context_menu ()
1531 {
1532         using namespace Menu_Helpers;
1533
1534         MenuList& edit_items = track_context_menu.items();
1535         edit_items.clear();
1536
1537         add_dstream_context_items (edit_items);
1538         return &track_context_menu;
1539 }
1540
1541 Menu*
1542 Editor::build_track_bus_context_menu ()
1543 {
1544         using namespace Menu_Helpers;
1545
1546         MenuList& edit_items = track_context_menu.items();
1547         edit_items.clear();
1548
1549         add_bus_context_items (edit_items);
1550         return &track_context_menu;
1551 }
1552
1553 Menu*
1554 Editor::build_track_region_context_menu ()
1555 {
1556         using namespace Menu_Helpers;
1557         MenuList& edit_items  = track_region_context_menu.items();
1558         edit_items.clear();
1559
1560         /* we've just cleared the track region context menu, so the menu that these
1561            two items were on will have disappeared; stop them dangling.
1562         */
1563         region_edit_menu_split_item = 0;
1564         region_edit_menu_split_multichannel_item = 0;
1565
1566         /* we might try to use items that are currently attached to a crossfade menu,
1567            so clear that, too.
1568         */
1569         track_crossfade_context_menu.items().clear ();
1570
1571         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (clicked_axisview);
1572
1573         if (rtv) {
1574                 boost::shared_ptr<Track> tr;
1575                 boost::shared_ptr<Playlist> pl;
1576
1577                 if ((tr = rtv->track())) {
1578                         add_region_context_items (edit_items, tr);
1579                 }
1580         }
1581
1582         add_dstream_context_items (edit_items);
1583
1584         return &track_region_context_menu;
1585 }
1586
1587 Menu*
1588 Editor::build_track_crossfade_context_menu ()
1589 {
1590         using namespace Menu_Helpers;
1591         MenuList& edit_items  = track_crossfade_context_menu.items();
1592         edit_items.clear ();
1593
1594         /* we might try to use items that are currently attached to a crossfade menu,
1595            so clear that, too.
1596         */
1597         track_region_context_menu.items().clear ();
1598
1599         AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1600
1601         if (atv) {
1602                 boost::shared_ptr<Track> tr;
1603                 boost::shared_ptr<Playlist> pl;
1604                 boost::shared_ptr<AudioPlaylist> apl;
1605
1606                 if ((tr = atv->track()) && ((pl = tr->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1607
1608                         AudioPlaylist::Crossfades xfades;
1609                         framepos_t where;
1610                         bool ignored;
1611
1612                         /* The xfade menu is a bit of a special case, as we always use the mouse position
1613                            to decide whether or not to display it (rather than the edit point).  No particularly
1614                            strong reasons for this, other than it is a bit surprising to right-click on a xfade
1615                            and not get a menu.
1616                         */
1617                         mouse_frame (where, ignored);
1618                         apl->crossfades_at (where, xfades);
1619
1620                         bool const many = xfades.size() > 1;
1621
1622                         for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1623                                 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1624                         }
1625
1626                         add_region_context_items (edit_items, tr);
1627                 }
1628         }
1629
1630         add_dstream_context_items (edit_items);
1631
1632         return &track_crossfade_context_menu;
1633 }
1634
1635 void
1636 Editor::analyze_region_selection ()
1637 {
1638         if (analysis_window == 0) {
1639                 analysis_window = new AnalysisWindow();
1640
1641                 if (_session != 0)
1642                         analysis_window->set_session(_session);
1643
1644                 analysis_window->show_all();
1645         }
1646
1647         analysis_window->set_regionmode();
1648         analysis_window->analyze();
1649
1650         analysis_window->present();
1651 }
1652
1653 void
1654 Editor::analyze_range_selection()
1655 {
1656         if (analysis_window == 0) {
1657                 analysis_window = new AnalysisWindow();
1658
1659                 if (_session != 0)
1660                         analysis_window->set_session(_session);
1661
1662                 analysis_window->show_all();
1663         }
1664
1665         analysis_window->set_rangemode();
1666         analysis_window->analyze();
1667
1668         analysis_window->present();
1669 }
1670
1671 Menu*
1672 Editor::build_track_selection_context_menu ()
1673 {
1674         using namespace Menu_Helpers;
1675         MenuList& edit_items  = track_selection_context_menu.items();
1676         edit_items.clear ();
1677
1678         add_selection_context_items (edit_items);
1679         // edit_items.push_back (SeparatorElem());
1680         // add_dstream_context_items (edit_items);
1681
1682         return &track_selection_context_menu;
1683 }
1684
1685 /** Add context menu items relevant to crossfades.
1686  * @param edit_items List to add the items to.
1687  */
1688 void
1689 Editor::add_crossfade_context_items (AudioStreamView* /*view*/, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1690 {
1691         using namespace Menu_Helpers;
1692         Menu     *xfade_menu = manage (new Menu);
1693         MenuList& items       = xfade_menu->items();
1694         xfade_menu->set_name ("ArdourContextMenu");
1695         string str;
1696
1697         if (xfade->active()) {
1698                 str = _("Mute");
1699         } else {
1700                 str = _("Unmute");
1701         }
1702
1703         items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1704         items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1705
1706         if (xfade->can_follow_overlap()) {
1707
1708                 if (xfade->following_overlap()) {
1709                         str = _("Convert to Short");
1710                 } else {
1711                         str = _("Convert to Full");
1712                 }
1713
1714                 items.push_back (MenuElem (str, sigc::bind (sigc::mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1715         }
1716
1717         if (many) {
1718                 str = xfade->out()->name();
1719                 str += "->";
1720                 str += xfade->in()->name();
1721         } else {
1722                 str = _("Crossfade");
1723         }
1724
1725         edit_items.push_back (MenuElem (str, *xfade_menu));
1726         edit_items.push_back (SeparatorElem());
1727 }
1728
1729 void
1730 Editor::xfade_edit_left_region ()
1731 {
1732         if (clicked_crossfadeview) {
1733                 clicked_crossfadeview->left_view.show_region_editor ();
1734         }
1735 }
1736
1737 void
1738 Editor::xfade_edit_right_region ()
1739 {
1740         if (clicked_crossfadeview) {
1741                 clicked_crossfadeview->right_view.show_region_editor ();
1742         }
1743 }
1744
1745 void
1746 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::shared_ptr<Track> track)
1747 {
1748         using namespace Menu_Helpers;
1749
1750         /* OK, stick the region submenu at the top of the list, and then add
1751            the standard items.
1752         */
1753
1754         RegionSelection rs = get_regions_from_selection_and_entered ();
1755
1756         string::size_type pos = 0;
1757         string menu_item_name = (rs.size() == 1) ? rs.front()->region()->name() : _("Selected Regions");
1758
1759         /* we have to hack up the region name because "_" has a special
1760            meaning for menu titles.
1761         */
1762
1763         while ((pos = menu_item_name.find ("_", pos)) != string::npos) {
1764                 menu_item_name.replace (pos, 1, "__");
1765                 pos += 2;
1766         }
1767
1768         if (_popup_region_menu_item == 0) {
1769                 _popup_region_menu_item = new MenuItem (menu_item_name);
1770                 _popup_region_menu_item->set_submenu (*dynamic_cast<Menu*> (ActionManager::get_widget (X_("/PopupRegionMenu"))));
1771                 _popup_region_menu_item->show ();
1772         } else {
1773                 _popup_region_menu_item->set_label (menu_item_name);
1774         }
1775
1776         /* Use the mouse position rather than the edit point to decide whether to show the `choose top region'
1777            dialogue.  If we use the edit point it gets a bit messy because the user still has to click over
1778            *some* region in order to get the region context menu stuff to be displayed at all.
1779         */
1780
1781         framepos_t mouse;
1782         bool ignored;
1783         mouse_frame (mouse, ignored);
1784
1785         edit_items.push_back (*_popup_region_menu_item);
1786         if (track->playlist()->count_regions_at (mouse) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
1787                 edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region")->create_menu_item ()));
1788         }
1789         edit_items.push_back (SeparatorElem());
1790 }
1791
1792 /** Add context menu items relevant to selection ranges.
1793  * @param edit_items List to add the items to.
1794  */
1795 void
1796 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1797 {
1798         using namespace Menu_Helpers;
1799
1800         edit_items.push_back (MenuElem (_("Play Range"), sigc::mem_fun(*this, &Editor::play_selection)));
1801         edit_items.push_back (MenuElem (_("Loop Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), true)));
1802
1803         edit_items.push_back (SeparatorElem());
1804         edit_items.push_back (MenuElem (_("Spectral Analysis"), sigc::mem_fun(*this, &Editor::analyze_range_selection)));
1805
1806         edit_items.push_back (SeparatorElem());
1807
1808         edit_items.push_back (
1809                 MenuElem (
1810                         _("Move Range Start to Previous Region Boundary"),
1811                         sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, false)
1812                         )
1813                 );
1814
1815         edit_items.push_back (
1816                 MenuElem (
1817                         _("Move Range Start to Next Region Boundary"),
1818                         sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), false, true)
1819                         )
1820                 );
1821
1822         edit_items.push_back (
1823                 MenuElem (
1824                         _("Move Range End to Previous Region Boundary"),
1825                         sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, false)
1826                         )
1827                 );
1828
1829         edit_items.push_back (
1830                 MenuElem (
1831                         _("Move Range End to Next Region Boundary"),
1832                         sigc::bind (sigc::mem_fun (*this, &Editor::move_range_selection_start_or_end_to_region_boundary), true, true)
1833                         )
1834                 );
1835
1836         edit_items.push_back (SeparatorElem());
1837         edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection)));
1838         edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
1839
1840         edit_items.push_back (SeparatorElem());
1841         edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1842
1843         edit_items.push_back (SeparatorElem());
1844         edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
1845         edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
1846
1847         edit_items.push_back (SeparatorElem());
1848         edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
1849
1850         edit_items.push_back (SeparatorElem());
1851         edit_items.push_back (MenuElem (_("Crop Region to Range"), sigc::mem_fun(*this, &Editor::crop_region_to_selection)));
1852         edit_items.push_back (MenuElem (_("Fill Range with Region"), sigc::mem_fun(*this, &Editor::region_fill_selection)));
1853         edit_items.push_back (MenuElem (_("Duplicate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::duplicate_dialog), false)));
1854
1855         edit_items.push_back (SeparatorElem());
1856         edit_items.push_back (MenuElem (_("Consolidate Range"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, false)));
1857         edit_items.push_back (MenuElem (_("Consolidate Range With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), true, true)));
1858         edit_items.push_back (MenuElem (_("Bounce Range to Region List"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, false)));
1859         edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
1860         edit_items.push_back (MenuElem (_("Export Range"), sigc::mem_fun(*this, &Editor::export_selection)));
1861 }
1862
1863
1864 void
1865 Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
1866 {
1867         using namespace Menu_Helpers;
1868
1869         /* Playback */
1870
1871         Menu *play_menu = manage (new Menu);
1872         MenuList& play_items = play_menu->items();
1873         play_menu->set_name ("ArdourContextMenu");
1874
1875         play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1876         play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1877         play_items.push_back (MenuElem (_("Play Region"), sigc::mem_fun(*this, &Editor::play_selected_region)));
1878         play_items.push_back (SeparatorElem());
1879         play_items.push_back (MenuElem (_("Loop Region"), sigc::bind (sigc::mem_fun (*this, &Editor::set_loop_from_region), true)));
1880
1881         edit_items.push_back (MenuElem (_("Play"), *play_menu));
1882
1883         /* Selection */
1884
1885         Menu *select_menu = manage (new Menu);
1886         MenuList& select_items = select_menu->items();
1887         select_menu->set_name ("ArdourContextMenu");
1888
1889         select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1890         select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1891         select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1892         select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1893         select_items.push_back (SeparatorElem());
1894         select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
1895         select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
1896         select_items.push_back (SeparatorElem());
1897         select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1898         select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1899         select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1900         select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1901         select_items.push_back (MenuElem (_("Select All Between Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), false)));
1902         select_items.push_back (MenuElem (_("Select All Within Playhead and Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_between), true)));
1903         select_items.push_back (MenuElem (_("Select Range Between Playhead and Edit Point"), sigc::mem_fun(*this, &Editor::select_range_between)));
1904
1905         edit_items.push_back (MenuElem (_("Select"), *select_menu));
1906
1907         /* Cut-n-Paste */
1908
1909         Menu *cutnpaste_menu = manage (new Menu);
1910         MenuList& cutnpaste_items = cutnpaste_menu->items();
1911         cutnpaste_menu->set_name ("ArdourContextMenu");
1912
1913         cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1914         cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1915         cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1916
1917         cutnpaste_items.push_back (SeparatorElem());
1918
1919         cutnpaste_items.push_back (MenuElem (_("Align"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions), ARDOUR::SyncPoint)));
1920         cutnpaste_items.push_back (MenuElem (_("Align Relative"), sigc::bind (sigc::mem_fun (*this, &Editor::align_regions_relative), ARDOUR::SyncPoint)));
1921
1922         edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
1923
1924         /* Adding new material */
1925
1926         edit_items.push_back (SeparatorElem());
1927         edit_items.push_back (MenuElem (_("Insert Selected Region"), sigc::bind (sigc::mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)));
1928         edit_items.push_back (MenuElem (_("Insert Existing Media"), sigc::bind (sigc::mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)));
1929
1930         /* Nudge track */
1931
1932         Menu *nudge_menu = manage (new Menu());
1933         MenuList& nudge_items = nudge_menu->items();
1934         nudge_menu->set_name ("ArdourContextMenu");
1935
1936         edit_items.push_back (SeparatorElem());
1937         nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1938         nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1939         nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1940         nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1941
1942         edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1943 }
1944
1945 void
1946 Editor::add_bus_context_items (Menu_Helpers::MenuList& edit_items)
1947 {
1948         using namespace Menu_Helpers;
1949
1950         /* Playback */
1951
1952         Menu *play_menu = manage (new Menu);
1953         MenuList& play_items = play_menu->items();
1954         play_menu->set_name ("ArdourContextMenu");
1955
1956         play_items.push_back (MenuElem (_("Play From Edit Point"), sigc::mem_fun(*this, &Editor::play_from_edit_point)));
1957         play_items.push_back (MenuElem (_("Play From Start"), sigc::mem_fun(*this, &Editor::play_from_start)));
1958         edit_items.push_back (MenuElem (_("Play"), *play_menu));
1959
1960         /* Selection */
1961
1962         Menu *select_menu = manage (new Menu);
1963         MenuList& select_items = select_menu->items();
1964         select_menu->set_name ("ArdourContextMenu");
1965
1966         select_items.push_back (MenuElem (_("Select All in Track"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_in_track), Selection::Set)));
1967         select_items.push_back (MenuElem (_("Select All"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all), Selection::Set)));
1968         select_items.push_back (MenuElem (_("Invert Selection in Track"), sigc::mem_fun(*this, &Editor::invert_selection_in_track)));
1969         select_items.push_back (MenuElem (_("Invert Selection"), sigc::mem_fun(*this, &Editor::invert_selection)));
1970         select_items.push_back (SeparatorElem());
1971         select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
1972         select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
1973         select_items.push_back (MenuElem (_("Select All After Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1974         select_items.push_back (MenuElem (_("Select All Before Playhead"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1975
1976         edit_items.push_back (MenuElem (_("Select"), *select_menu));
1977
1978         /* Cut-n-Paste */
1979
1980         Menu *cutnpaste_menu = manage (new Menu);
1981         MenuList& cutnpaste_items = cutnpaste_menu->items();
1982         cutnpaste_menu->set_name ("ArdourContextMenu");
1983
1984         cutnpaste_items.push_back (MenuElem (_("Cut"), sigc::mem_fun(*this, &Editor::cut)));
1985         cutnpaste_items.push_back (MenuElem (_("Copy"), sigc::mem_fun(*this, &Editor::copy)));
1986         cutnpaste_items.push_back (MenuElem (_("Paste"), sigc::bind (sigc::mem_fun(*this, &Editor::paste), 1.0f)));
1987
1988         Menu *nudge_menu = manage (new Menu());
1989         MenuList& nudge_items = nudge_menu->items();
1990         nudge_menu->set_name ("ArdourContextMenu");
1991
1992         edit_items.push_back (SeparatorElem());
1993         nudge_items.push_back (MenuElem (_("Nudge Entire Track Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, true))));
1994         nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Forward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, true))));
1995         nudge_items.push_back (MenuElem (_("Nudge Entire Track Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), false, false))));
1996         nudge_items.push_back (MenuElem (_("Nudge Track After Edit Point Backward"), (sigc::bind (sigc::mem_fun(*this, &Editor::nudge_track), true, false))));
1997
1998         edit_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1999 }
2000
2001 SnapType
2002 Editor::snap_type() const
2003 {
2004         return _snap_type;
2005 }
2006
2007 SnapMode
2008 Editor::snap_mode() const
2009 {
2010         return _snap_mode;
2011 }
2012
2013 void
2014 Editor::set_snap_to (SnapType st)
2015 {
2016         unsigned int snap_ind = (unsigned int)st;
2017
2018         _snap_type = st;
2019
2020         if (snap_ind > snap_type_strings.size() - 1) {
2021                 snap_ind = 0;
2022                 _snap_type = (SnapType)snap_ind;
2023         }
2024
2025         string str = snap_type_strings[snap_ind];
2026
2027         if (str != snap_type_selector.get_active_text()) {
2028                 snap_type_selector.set_active_text (str);
2029         }
2030
2031         instant_save ();
2032
2033         switch (_snap_type) {
2034         case SnapToBeatDiv32:
2035         case SnapToBeatDiv28:
2036         case SnapToBeatDiv24:
2037         case SnapToBeatDiv20:
2038         case SnapToBeatDiv16:
2039         case SnapToBeatDiv14:
2040         case SnapToBeatDiv12:
2041         case SnapToBeatDiv10:
2042         case SnapToBeatDiv8:
2043         case SnapToBeatDiv7:
2044         case SnapToBeatDiv6:
2045         case SnapToBeatDiv5:
2046         case SnapToBeatDiv4:
2047         case SnapToBeatDiv3:
2048         case SnapToBeatDiv2:
2049                 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
2050                 update_tempo_based_rulers ();
2051                 break;
2052
2053         case SnapToRegionStart:
2054         case SnapToRegionEnd:
2055         case SnapToRegionSync:
2056         case SnapToRegionBoundary:
2057                 build_region_boundary_cache ();
2058                 break;
2059
2060         default:
2061                 /* relax */
2062                 break;
2063         }
2064
2065         SnapChanged (); /* EMIT SIGNAL */
2066 }
2067
2068 void
2069 Editor::set_snap_mode (SnapMode mode)
2070 {
2071         _snap_mode = mode;
2072         string str = snap_mode_strings[(int)mode];
2073
2074         if (str != snap_mode_selector.get_active_text ()) {
2075                 snap_mode_selector.set_active_text (str);
2076         }
2077
2078         instant_save ();
2079 }
2080 void
2081 Editor::set_edit_point_preference (EditPoint ep, bool force)
2082 {
2083         bool changed = (_edit_point != ep);
2084
2085         _edit_point = ep;
2086         string str = edit_point_strings[(int)ep];
2087
2088         if (str != edit_point_selector.get_active_text ()) {
2089                 edit_point_selector.set_active_text (str);
2090         }
2091
2092         set_canvas_cursor ();
2093
2094         if (!force && !changed) {
2095                 return;
2096         }
2097
2098         const char* action=NULL;
2099
2100         switch (_edit_point) {
2101         case EditAtPlayhead:
2102                 action = "edit-at-playhead";
2103                 break;
2104         case EditAtSelectedMarker:
2105                 action = "edit-at-marker";
2106                 break;
2107         case EditAtMouse:
2108                 action = "edit-at-mouse";
2109                 break;
2110         }
2111
2112         Glib::RefPtr<Action> act = ActionManager::get_action ("Editor", action);
2113         if (act) {
2114                 Glib::RefPtr<RadioAction>::cast_dynamic(act)->set_active (true);
2115         }
2116
2117         framepos_t foo;
2118         bool in_track_canvas;
2119
2120         if (!mouse_frame (foo, in_track_canvas)) {
2121                 in_track_canvas = false;
2122         }
2123
2124         reset_canvas_action_sensitivity (in_track_canvas);
2125
2126         instant_save ();
2127 }
2128
2129 int
2130 Editor::set_state (const XMLNode& node, int /*version*/)
2131 {
2132         const XMLProperty* prop;
2133         XMLNode* geometry;
2134         int x, y;
2135         Gdk::Geometry g;
2136
2137         if ((prop = node.property ("id")) != 0) {
2138                 _id = prop->value ();
2139         }
2140
2141         g.base_width = default_width;
2142         g.base_height = default_height;
2143         x = 1;
2144         y = 1;
2145
2146         if ((geometry = find_named_node (node, "geometry")) != 0) {
2147
2148                 XMLProperty* prop;
2149
2150                 if ((prop = geometry->property("x_size")) == 0) {
2151                         prop = geometry->property ("x-size");
2152                 }
2153                 if (prop) {
2154                         g.base_width = atoi(prop->value());
2155                 }
2156                 if ((prop = geometry->property("y_size")) == 0) {
2157                         prop = geometry->property ("y-size");
2158                 }
2159                 if (prop) {
2160                         g.base_height = atoi(prop->value());
2161                 }
2162
2163                 if ((prop = geometry->property ("x_pos")) == 0) {
2164                         prop = geometry->property ("x-pos");
2165                 }
2166                 if (prop) {
2167                         x = atoi (prop->value());
2168
2169                 }
2170                 if ((prop = geometry->property ("y_pos")) == 0) {
2171                         prop = geometry->property ("y-pos");
2172                 }
2173                 if (prop) {
2174                         y = atoi (prop->value());
2175                 }
2176         }
2177
2178         set_default_size (g.base_width, g.base_height);
2179         move (x, y);
2180
2181         if (_session && (prop = node.property ("playhead"))) {
2182                 framepos_t pos;
2183                 sscanf (prop->value().c_str(), "%" PRIi64, &pos);
2184                 playhead_cursor->set_position (pos);
2185         } else {
2186                 playhead_cursor->set_position (0);
2187         }
2188
2189         if ((prop = node.property ("mixer-width"))) {
2190                 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
2191         }
2192
2193         if ((prop = node.property ("zoom-focus"))) {
2194                 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
2195         }
2196
2197         if ((prop = node.property ("zoom"))) {
2198                 reset_zoom (PBD::atof (prop->value()));
2199         } else {
2200                 reset_zoom (frames_per_unit);
2201         }
2202
2203         if ((prop = node.property ("snap-to"))) {
2204                 set_snap_to ((SnapType) atoi (prop->value()));
2205         }
2206
2207         if ((prop = node.property ("snap-mode"))) {
2208                 set_snap_mode ((SnapMode) atoi (prop->value()));
2209         }
2210
2211         if ((prop = node.property ("mouse-mode"))) {
2212                 MouseMode m = str2mousemode(prop->value());
2213                 set_mouse_mode (m, true);
2214         } else {
2215                 set_mouse_mode (MouseObject, true);
2216         }
2217
2218         if ((prop = node.property ("left-frame")) != 0) {
2219                 framepos_t pos;
2220                 if (sscanf (prop->value().c_str(), "%" PRId64, &pos) == 1) {
2221                         reset_x_origin (pos);
2222                 }
2223         }
2224
2225         if ((prop = node.property ("y-origin")) != 0) {
2226                 reset_y_origin (atof (prop->value ()));
2227         }
2228
2229         if ((prop = node.property ("internal-edit"))) {
2230                 bool yn = string_is_affirmative (prop->value());
2231                 RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
2232                 if (act) {
2233                         RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2234                         tact->set_active (!yn);
2235                         tact->set_active (yn);
2236                 }
2237         }
2238
2239         if ((prop = node.property ("join-object-range"))) {
2240                 join_object_range_button.set_active (string_is_affirmative (prop->value ()));
2241         }
2242
2243         if ((prop = node.property ("edit-point"))) {
2244                 set_edit_point_preference ((EditPoint) string_2_enum (prop->value(), _edit_point), true);
2245         }
2246
2247         if ((prop = node.property ("show-measures"))) {
2248                 bool yn = string_is_affirmative (prop->value());
2249                 _show_measures = yn;
2250                 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
2251                 if (act) {
2252                         RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2253                         /* do it twice to force the change */
2254                         tact->set_active (!yn);
2255                         tact->set_active (yn);
2256                 }
2257         }
2258
2259         if ((prop = node.property ("follow-playhead"))) {
2260                 bool yn = string_is_affirmative (prop->value());
2261                 set_follow_playhead (yn);
2262                 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
2263                 if (act) {
2264                         RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2265                         if (tact->get_active() != yn) {
2266                                 tact->set_active (yn);
2267                         }
2268                 }
2269         }
2270
2271         if ((prop = node.property ("stationary-playhead"))) {
2272                 bool yn = string_is_affirmative (prop->value());
2273                 set_stationary_playhead (yn);
2274                 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
2275                 if (act) {
2276                         RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
2277                         if (tact->get_active() != yn) {
2278                                 tact->set_active (yn);
2279                         }
2280                 }
2281         }
2282
2283         if ((prop = node.property ("region-list-sort-type"))) {
2284                 RegionListSortType st;
2285                 _regions->reset_sort_type ((RegionListSortType) string_2_enum (prop->value(), st), true);
2286         }
2287
2288         if ((prop = node.property ("xfades-visible"))) {
2289                 bool yn = string_is_affirmative (prop->value());
2290                 _xfade_visibility = !yn;
2291                 // set_xfade_visibility (yn);
2292         }
2293
2294         if ((prop = node.property ("show-editor-mixer"))) {
2295
2296                 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2297                 assert (act);
2298
2299                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2300                 bool yn = string_is_affirmative (prop->value());
2301
2302                 /* do it twice to force the change */
2303
2304                 tact->set_active (!yn);
2305                 tact->set_active (yn);
2306         }
2307
2308         if ((prop = node.property ("show-editor-list"))) {
2309
2310                 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2311                 assert (act);
2312
2313                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2314                 bool yn = string_is_affirmative (prop->value());
2315
2316                 /* do it twice to force the change */
2317
2318                 tact->set_active (!yn);
2319                 tact->set_active (yn);
2320         }
2321
2322         if ((prop = node.property (X_("editor-list-page")))) {
2323                 _the_notebook.set_current_page (atoi (prop->value ()));
2324         }
2325
2326         if ((prop = node.property (X_("show-marker-lines")))) {
2327                 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-marker-lines"));
2328                 assert (act);
2329                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
2330                 bool yn = string_is_affirmative (prop->value ());
2331
2332                 tact->set_active (!yn);
2333                 tact->set_active (yn);
2334         }
2335
2336         XMLNodeList children = node.children ();
2337         for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) {
2338                 selection->set_state (**i, Stateful::current_state_version);
2339                 _regions->set_state (**i);
2340         }
2341
2342         return 0;
2343 }
2344
2345 XMLNode&
2346 Editor::get_state ()
2347 {
2348         XMLNode* node = new XMLNode ("Editor");
2349         char buf[32];
2350
2351         _id.print (buf, sizeof (buf));
2352         node->add_property ("id", buf);
2353
2354         if (is_realized()) {
2355                 Glib::RefPtr<Gdk::Window> win = get_window();
2356
2357                 int x, y, width, height;
2358                 win->get_root_origin(x, y);
2359                 win->get_size(width, height);
2360
2361                 XMLNode* geometry = new XMLNode ("geometry");
2362
2363                 snprintf(buf, sizeof(buf), "%d", width);
2364                 geometry->add_property("x-size", string(buf));
2365                 snprintf(buf, sizeof(buf), "%d", height);
2366                 geometry->add_property("y-size", string(buf));
2367                 snprintf(buf, sizeof(buf), "%d", x);
2368                 geometry->add_property("x-pos", string(buf));
2369                 snprintf(buf, sizeof(buf), "%d", y);
2370                 geometry->add_property("y-pos", string(buf));
2371                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2372                 geometry->add_property("edit-horizontal-pane-pos", string(buf));
2373                 geometry->add_property("notebook-shrunk", _notebook_shrunk ? "1" : "0");
2374                 snprintf(buf,sizeof(buf), "%d",pre_maximal_horizontal_pane_position);
2375                 geometry->add_property("pre-maximal-horizontal-pane-position", string(buf));
2376                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&editor_summary_pane)->gobj()));
2377                 geometry->add_property("edit-vertical-pane-pos", string(buf));
2378
2379                 node->add_child_nocopy (*geometry);
2380         }
2381
2382         maybe_add_mixer_strip_width (*node);
2383
2384         snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2385         node->add_property ("zoom-focus", buf);
2386         snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2387         node->add_property ("zoom", buf);
2388         snprintf (buf, sizeof(buf), "%d", (int) _snap_type);
2389         node->add_property ("snap-to", buf);
2390         snprintf (buf, sizeof(buf), "%d", (int) _snap_mode);
2391         node->add_property ("snap-mode", buf);
2392
2393         node->add_property ("edit-point", enum_2_string (_edit_point));
2394
2395         snprintf (buf, sizeof (buf), "%" PRIi64, playhead_cursor->current_frame);
2396         node->add_property ("playhead", buf);
2397         snprintf (buf, sizeof (buf), "%" PRIi64, leftmost_frame);
2398         node->add_property ("left-frame", buf);
2399         snprintf (buf, sizeof (buf), "%f", vertical_adjustment.get_value ());
2400         node->add_property ("y-origin", buf);
2401
2402         node->add_property ("show-measures", _show_measures ? "yes" : "no");
2403         node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2404         node->add_property ("stationary-playhead", _stationary_playhead ? "yes" : "no");
2405         node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2406         node->add_property ("region-list-sort-type", enum_2_string (_regions->sort_type ()));
2407         node->add_property ("mouse-mode", enum2str(mouse_mode));
2408         node->add_property ("internal-edit", _internal_editing ? "yes" : "no");
2409         node->add_property ("join-object-range", join_object_range_button.get_active () ? "yes" : "no");
2410
2411         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2412         if (act) {
2413                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2414                 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2415         }
2416
2417         act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2418         if (act) {
2419                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2420                 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2421         }
2422
2423         snprintf (buf, sizeof (buf), "%d", _the_notebook.get_current_page ());
2424         node->add_property (X_("editor-list-page"), buf);
2425
2426         if (button_bindings) {
2427                 XMLNode* bb = new XMLNode (X_("Buttons"));
2428                 button_bindings->save (*bb);
2429                 node->add_child_nocopy (*bb);
2430         }
2431
2432         node->add_property (X_("show-marker-lines"), _show_marker_lines ? "yes" : "no");
2433
2434         node->add_child_nocopy (selection->get_state ());
2435         node->add_child_nocopy (_regions->get_state ());
2436
2437         return *node;
2438 }
2439
2440
2441
2442 /** @param y y offset from the top of all trackviews.
2443  *  @return pair: TimeAxisView that y is over, layer index.
2444  *  TimeAxisView may be 0.  Layer index is the layer number if the TimeAxisView is valid and is
2445  *  in stacked region display mode, otherwise 0.
2446  */
2447 std::pair<TimeAxisView *, layer_t>
2448 Editor::trackview_by_y_position (double y)
2449 {
2450         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2451
2452                 std::pair<TimeAxisView*, int> const r = (*iter)->covers_y_position (y);
2453                 if (r.first) {
2454                         return r;
2455                 }
2456         }
2457
2458         return std::make_pair ( (TimeAxisView *) 0, 0);
2459 }
2460
2461 /** Snap a position to the grid, if appropriate, taking into account current
2462  *  grid settings and also the state of any snap modifier keys that may be pressed.
2463  *  @param start Position to snap.
2464  *  @param event Event to get current key modifier information from, or 0.
2465  */
2466 void
2467 Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, int32_t direction, bool for_mark)
2468 {
2469         if (!_session || !event) {
2470                 return;
2471         }
2472
2473         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
2474                 if (_snap_mode == SnapOff) {
2475                         snap_to_internal (start, direction, for_mark);
2476                 }
2477         } else {
2478                 if (_snap_mode != SnapOff) {
2479                         snap_to_internal (start, direction, for_mark);
2480                 }
2481         }
2482 }
2483
2484 void
2485 Editor::snap_to (framepos_t& start, int32_t direction, bool for_mark)
2486 {
2487         if (!_session || _snap_mode == SnapOff) {
2488                 return;
2489         }
2490
2491         snap_to_internal (start, direction, for_mark);
2492 }
2493
2494 void
2495 Editor::timecode_snap_to_internal (framepos_t& start, int32_t direction, bool /*for_mark*/)
2496 {
2497         const framepos_t one_timecode_second = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame());
2498         framepos_t one_timecode_minute = (framepos_t)(rint(_session->timecode_frames_per_second()) * _session->frames_per_timecode_frame() * 60);
2499
2500         switch (_snap_type) {
2501         case SnapToTimecodeFrame:
2502                 if (((direction == 0) && (fmod((double)start, (double)_session->frames_per_timecode_frame()) > (_session->frames_per_timecode_frame() / 2))) || (direction > 0)) {
2503                         start = (framepos_t) (ceil ((double) start / _session->frames_per_timecode_frame()) * _session->frames_per_timecode_frame());
2504                 } else {
2505                         start = (framepos_t) (floor ((double) start / _session->frames_per_timecode_frame()) *  _session->frames_per_timecode_frame());
2506                 }
2507                 break;
2508
2509         case SnapToTimecodeSeconds:
2510                 if (_session->config.get_timecode_offset_negative()) {
2511                         start += _session->config.get_timecode_offset ();
2512                 } else {
2513                         start -= _session->config.get_timecode_offset ();
2514                 }
2515                 if (((direction == 0) && (start % one_timecode_second > one_timecode_second / 2)) || direction > 0) {
2516                         start = (framepos_t) ceil ((double) start / one_timecode_second) * one_timecode_second;
2517                 } else {
2518                         start = (framepos_t) floor ((double) start / one_timecode_second) * one_timecode_second;
2519                 }
2520
2521                 if (_session->config.get_timecode_offset_negative()) {
2522                         start -= _session->config.get_timecode_offset ();
2523                 } else {
2524                         start += _session->config.get_timecode_offset ();
2525                 }
2526                 break;
2527
2528         case SnapToTimecodeMinutes:
2529                 if (_session->config.get_timecode_offset_negative()) {
2530                         start += _session->config.get_timecode_offset ();
2531                 } else {
2532                         start -= _session->config.get_timecode_offset ();
2533                 }
2534                 if (((direction == 0) && (start % one_timecode_minute > one_timecode_minute / 2)) || direction > 0) {
2535                         start = (framepos_t) ceil ((double) start / one_timecode_minute) * one_timecode_minute;
2536                 } else {
2537                         start = (framepos_t) floor ((double) start / one_timecode_minute) * one_timecode_minute;
2538                 }
2539                 if (_session->config.get_timecode_offset_negative()) {
2540                         start -= _session->config.get_timecode_offset ();
2541                 } else {
2542                         start += _session->config.get_timecode_offset ();
2543                 }
2544                 break;
2545         default:
2546                 fatal << "Editor::smpte_snap_to_internal() called with non-timecode snap type!" << endmsg;
2547                 /*NOTREACHED*/
2548         }
2549 }
2550
2551 void
2552 Editor::snap_to_internal (framepos_t& start, int32_t direction, bool for_mark)
2553 {
2554         const framepos_t one_second = _session->frame_rate();
2555         const framepos_t one_minute = _session->frame_rate() * 60;
2556         framepos_t presnap = start;
2557         framepos_t before;
2558         framepos_t after;
2559
2560         switch (_snap_type) {
2561         case SnapToTimecodeFrame:
2562         case SnapToTimecodeSeconds:
2563         case SnapToTimecodeMinutes:
2564                 return timecode_snap_to_internal (start, direction, for_mark);
2565
2566         case SnapToCDFrame:
2567                 if (((direction == 0) && (start % (one_second/75) > (one_second/75) / 2)) || (direction > 0)) {
2568                         start = (framepos_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2569                 } else {
2570                         start = (framepos_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2571                 }
2572                 break;
2573
2574         case SnapToSeconds:
2575                 if (((direction == 0) && (start % one_second > one_second / 2)) || (direction > 0)) {
2576                         start = (framepos_t) ceil ((double) start / one_second) * one_second;
2577                 } else {
2578                         start = (framepos_t) floor ((double) start / one_second) * one_second;
2579                 }
2580                 break;
2581
2582         case SnapToMinutes:
2583                 if (((direction == 0) && (start % one_minute > one_minute / 2)) || (direction > 0)) {
2584                         start = (framepos_t) ceil ((double) start / one_minute) * one_minute;
2585                 } else {
2586                         start = (framepos_t) floor ((double) start / one_minute) * one_minute;
2587                 }
2588                 break;
2589
2590         case SnapToBar:
2591                 start = _session->tempo_map().round_to_bar (start, direction);
2592                 break;
2593
2594         case SnapToBeat:
2595                 start = _session->tempo_map().round_to_beat (start, direction);
2596                 break;
2597
2598         case SnapToBeatDiv32:
2599                 start = _session->tempo_map().round_to_beat_subdivision (start, 32, direction);
2600                 break;
2601         case SnapToBeatDiv28:
2602                 start = _session->tempo_map().round_to_beat_subdivision (start, 28, direction);
2603                 break;
2604         case SnapToBeatDiv24:
2605                 start = _session->tempo_map().round_to_beat_subdivision (start, 24, direction);
2606                 break;
2607         case SnapToBeatDiv20:
2608                 start = _session->tempo_map().round_to_beat_subdivision (start, 20, direction);
2609                 break;
2610         case SnapToBeatDiv16:
2611                 start = _session->tempo_map().round_to_beat_subdivision (start, 16, direction);
2612                 break;
2613         case SnapToBeatDiv14:
2614                 start = _session->tempo_map().round_to_beat_subdivision (start, 14, direction);
2615                 break;
2616         case SnapToBeatDiv12:
2617                 start = _session->tempo_map().round_to_beat_subdivision (start, 12, direction);
2618                 break;
2619         case SnapToBeatDiv10:
2620                 start = _session->tempo_map().round_to_beat_subdivision (start, 10, direction);
2621                 break;
2622         case SnapToBeatDiv8:
2623                 start = _session->tempo_map().round_to_beat_subdivision (start, 8, direction);
2624                 break;
2625         case SnapToBeatDiv7:
2626                 start = _session->tempo_map().round_to_beat_subdivision (start, 7, direction);
2627                 break;
2628         case SnapToBeatDiv6:
2629                 start = _session->tempo_map().round_to_beat_subdivision (start, 6, direction);
2630                 break;
2631         case SnapToBeatDiv5:
2632                 start = _session->tempo_map().round_to_beat_subdivision (start, 5, direction);
2633                 break;
2634         case SnapToBeatDiv4:
2635                 start = _session->tempo_map().round_to_beat_subdivision (start, 4, direction);
2636                 break;
2637         case SnapToBeatDiv3:
2638                 start = _session->tempo_map().round_to_beat_subdivision (start, 3, direction);
2639                 break;
2640         case SnapToBeatDiv2:
2641                 start = _session->tempo_map().round_to_beat_subdivision (start, 2, direction);
2642                 break;
2643
2644         case SnapToMark:
2645                 if (for_mark) {
2646                         return;
2647                 }
2648
2649                 _session->locations()->marks_either_side (start, before, after);
2650
2651                 if (before == max_framepos) {
2652                         start = after;
2653                 } else if (after == max_framepos) {
2654                         start = before;
2655                 } else if (before != max_framepos && after != max_framepos) {
2656                         /* have before and after */
2657                         if ((start - before) < (after - start)) {
2658                                 start = before;
2659                         } else {
2660                                 start = after;
2661                         }
2662                 }
2663
2664                 break;
2665
2666         case SnapToRegionStart:
2667         case SnapToRegionEnd:
2668         case SnapToRegionSync:
2669         case SnapToRegionBoundary:
2670                 if (!region_boundary_cache.empty()) {
2671
2672                         vector<framepos_t>::iterator prev = region_boundary_cache.end ();
2673                         vector<framepos_t>::iterator next = region_boundary_cache.end ();
2674
2675                         if (direction > 0) {
2676                                 next = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2677                         } else {
2678                                 next = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2679                         }
2680
2681                         if (next != region_boundary_cache.begin ()) {
2682                                 prev = next;
2683                                 prev--;
2684                         }
2685
2686                         framepos_t const p = (prev == region_boundary_cache.end()) ? region_boundary_cache.front () : *prev;
2687                         framepos_t const n = (next == region_boundary_cache.end()) ? region_boundary_cache.back () : *next;
2688
2689                         if (start > (p + n) / 2) {
2690                                 start = n;
2691                         } else {
2692                                 start = p;
2693                         }
2694                 }
2695                 break;
2696         }
2697
2698         switch (_snap_mode) {
2699         case SnapNormal:
2700                 return;
2701
2702         case SnapMagnetic:
2703
2704                 if (presnap > start) {
2705                         if (presnap > (start + unit_to_frame(snap_threshold))) {
2706                                 start = presnap;
2707                         }
2708
2709                 } else if (presnap < start) {
2710                         if (presnap < (start - unit_to_frame(snap_threshold))) {
2711                                 start = presnap;
2712                         }
2713                 }
2714
2715         default:
2716                 /* handled at entry */
2717                 return;
2718
2719         }
2720 }
2721
2722
2723 void
2724 Editor::setup_toolbar ()
2725 {
2726         HBox* mode_box = manage(new HBox);
2727         mode_box->set_border_width (2);
2728         mode_box->set_spacing(4);
2729
2730         /* table containing mode buttons */
2731
2732         HBox* mouse_mode_button_box = manage (new HBox ());
2733
2734         if (Profile->get_sae()) {
2735                 mouse_mode_button_box->pack_start (mouse_move_button);
2736         } else {
2737                 mouse_mode_button_box->pack_start (mouse_move_button);
2738                 mouse_mode_button_box->pack_start (join_object_range_button);
2739                 mouse_mode_button_box->pack_start (mouse_select_button);
2740         }
2741
2742         mouse_mode_button_box->pack_start (mouse_zoom_button);
2743
2744         if (!Profile->get_sae()) {
2745                 mouse_mode_button_box->pack_start (mouse_gain_button);
2746         }
2747
2748         mouse_mode_button_box->pack_start (mouse_timefx_button);
2749         mouse_mode_button_box->pack_start (mouse_audition_button);
2750         mouse_mode_button_box->pack_start (internal_edit_button);
2751
2752         edit_mode_strings.push_back (edit_mode_to_string (Slide));
2753         if (!Profile->get_sae()) {
2754                 edit_mode_strings.push_back (edit_mode_to_string (Splice));
2755         }
2756         edit_mode_strings.push_back (edit_mode_to_string (Lock));
2757
2758         edit_mode_selector.set_name ("EditModeSelector");
2759         set_popdown_strings (edit_mode_selector, edit_mode_strings, true);
2760         edit_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_mode_selection_done));
2761
2762         mode_box->pack_start (edit_mode_selector);
2763         mode_box->pack_start (*mouse_mode_button_box);
2764
2765         _mouse_mode_tearoff = manage (new TearOff (*mode_box));
2766         _mouse_mode_tearoff->set_name ("MouseModeBase");
2767         _mouse_mode_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_mouse_mode_tearoff->tearoff_window()), false);
2768
2769         if (Profile->get_sae()) {
2770                 _mouse_mode_tearoff->set_can_be_torn_off (false);
2771         }
2772
2773         _mouse_mode_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2774                                                          &_mouse_mode_tearoff->tearoff_window()));
2775         _mouse_mode_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2776                                                          &_mouse_mode_tearoff->tearoff_window(), 1));
2777         _mouse_mode_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2778                                                          &_mouse_mode_tearoff->tearoff_window()));
2779         _mouse_mode_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2780                                                           &_mouse_mode_tearoff->tearoff_window(), 1));
2781
2782         mouse_move_button.set_mode (false);
2783         mouse_select_button.set_mode (false);
2784         mouse_gain_button.set_mode (false);
2785         mouse_zoom_button.set_mode (false);
2786         mouse_timefx_button.set_mode (false);
2787         mouse_audition_button.set_mode (false);
2788         join_object_range_button.set_mode (false);
2789
2790         mouse_move_button.set_name ("MouseModeButton");
2791         mouse_select_button.set_name ("MouseModeButton");
2792         mouse_gain_button.set_name ("MouseModeButton");
2793         mouse_zoom_button.set_name ("MouseModeButton");
2794         mouse_timefx_button.set_name ("MouseModeButton");
2795         mouse_audition_button.set_name ("MouseModeButton");
2796         internal_edit_button.set_name ("MouseModeButton");
2797         join_object_range_button.set_name ("MouseModeButton");
2798
2799         mouse_move_button.unset_flags (CAN_FOCUS);
2800         mouse_select_button.unset_flags (CAN_FOCUS);
2801         mouse_gain_button.unset_flags (CAN_FOCUS);
2802         mouse_zoom_button.unset_flags (CAN_FOCUS);
2803         mouse_timefx_button.unset_flags (CAN_FOCUS);
2804         mouse_audition_button.unset_flags (CAN_FOCUS);
2805         internal_edit_button.unset_flags (CAN_FOCUS);
2806         join_object_range_button.unset_flags (CAN_FOCUS);
2807
2808         /* Zoom */
2809
2810         _zoom_box.set_spacing (1);
2811         _zoom_box.set_border_width (0);
2812
2813         zoom_in_button.set_name ("EditorTimeButton");
2814         zoom_in_button.set_image (*(manage (new Image (::get_icon ("zoom_in")))));
2815         zoom_in_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), false));
2816
2817         zoom_out_button.set_name ("EditorTimeButton");
2818         zoom_out_button.set_image (*(manage (new Image (::get_icon ("zoom_out")))));
2819         zoom_out_button.signal_clicked().connect (sigc::bind (sigc::mem_fun(*this, &Editor::temporal_zoom_step), true));
2820
2821         zoom_out_full_button.set_name ("EditorTimeButton");
2822         zoom_out_full_button.set_image (*(manage (new Image (::get_icon ("zoom_full")))));
2823         zoom_out_full_button.signal_clicked().connect (sigc::mem_fun(*this, &Editor::temporal_zoom_session));
2824
2825         zoom_focus_selector.set_name ("ZoomFocusSelector");
2826         set_popdown_strings (zoom_focus_selector, zoom_focus_strings, true);
2827         zoom_focus_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::zoom_focus_selection_done));
2828
2829         _zoom_box.pack_start (zoom_out_button, false, false);
2830         _zoom_box.pack_start (zoom_in_button, false, false);
2831         _zoom_box.pack_start (zoom_out_full_button, false, false);
2832
2833         _zoom_box.pack_start (zoom_focus_selector);
2834
2835         /* Track zoom buttons */
2836         tav_expand_button.set_name ("TrackHeightButton");
2837         tav_expand_button.set_size_request (-1, 20);
2838         tav_expand_button.add (*(manage (new Image (::get_icon ("tav_exp")))));
2839         RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("expand-tracks"));
2840         act->connect_proxy (tav_expand_button);
2841
2842         tav_shrink_button.set_name ("TrackHeightButton");
2843         tav_shrink_button.set_size_request (-1, 20);
2844         tav_shrink_button.add (*(manage (new Image (::get_icon ("tav_shrink")))));
2845         act = ActionManager::get_action (X_("Editor"), X_("shrink-tracks"));
2846         act->connect_proxy (tav_shrink_button);
2847
2848         _zoom_box.pack_start (tav_shrink_button);
2849         _zoom_box.pack_start (tav_expand_button);
2850
2851         _zoom_tearoff = manage (new TearOff (_zoom_box));
2852
2853         _zoom_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2854                                                    &_zoom_tearoff->tearoff_window()));
2855         _zoom_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2856                                                    &_zoom_tearoff->tearoff_window(), 0));
2857         _zoom_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2858                                                    &_zoom_tearoff->tearoff_window()));
2859         _zoom_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2860                                                     &_zoom_tearoff->tearoff_window(), 0));
2861
2862         snap_box.set_spacing (1);
2863         snap_box.set_border_width (2);
2864
2865         snap_type_selector.set_name ("SnapTypeSelector");
2866         set_popdown_strings (snap_type_selector, snap_type_strings, true);
2867         snap_type_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_type_selection_done));
2868
2869         snap_mode_selector.set_name ("SnapModeSelector");
2870         set_popdown_strings (snap_mode_selector, snap_mode_strings, true);
2871         snap_mode_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::snap_mode_selection_done));
2872
2873         edit_point_selector.set_name ("EditPointSelector");
2874         set_popdown_strings (edit_point_selector, edit_point_strings, true);
2875         edit_point_selector.signal_changed().connect (sigc::mem_fun(*this, &Editor::edit_point_selection_done));
2876
2877         snap_box.pack_start (snap_mode_selector, false, false);
2878         snap_box.pack_start (snap_type_selector, false, false);
2879         snap_box.pack_start (edit_point_selector, false, false);
2880
2881         /* Nudge */
2882
2883         HBox *nudge_box = manage (new HBox);
2884         nudge_box->set_spacing(1);
2885         nudge_box->set_border_width (2);
2886
2887         nudge_forward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_forward_release), false);
2888         nudge_backward_button.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::nudge_backward_release), false);
2889
2890         nudge_box->pack_start (nudge_backward_button, false, false);
2891         nudge_box->pack_start (nudge_forward_button, false, false);
2892         nudge_box->pack_start (*nudge_clock, false, false);
2893
2894
2895         /* Pack everything in... */
2896
2897         HBox* hbox = manage (new HBox);
2898         hbox->set_spacing(10);
2899
2900         _tools_tearoff = manage (new TearOff (*hbox));
2901         _tools_tearoff->set_name ("MouseModeBase");
2902         _tools_tearoff->tearoff_window().signal_key_press_event().connect (sigc::bind (sigc::ptr_fun (relay_key_press), &_tools_tearoff->tearoff_window()), false);
2903
2904         if (Profile->get_sae()) {
2905                 _tools_tearoff->set_can_be_torn_off (false);
2906         }
2907
2908         _tools_tearoff->Detach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2909                                                     &_tools_tearoff->tearoff_window()));
2910         _tools_tearoff->Attach.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2911                                                     &_tools_tearoff->tearoff_window(), 0));
2912         _tools_tearoff->Hidden.connect (sigc::bind (sigc::mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox),
2913                                                     &_tools_tearoff->tearoff_window()));
2914         _tools_tearoff->Visible.connect (sigc::bind (sigc::mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox),
2915                                                      &_tools_tearoff->tearoff_window(), 0));
2916
2917         toolbar_hbox.set_spacing (10);
2918         toolbar_hbox.set_border_width (1);
2919
2920         toolbar_hbox.pack_start (*_mouse_mode_tearoff, false, false);
2921         toolbar_hbox.pack_start (*_zoom_tearoff, false, false);
2922         toolbar_hbox.pack_start (*_tools_tearoff, false, false);
2923
2924         hbox->pack_start (snap_box, false, false);
2925         if (!Profile->get_small_screen()) {
2926                 hbox->pack_start (*nudge_box, false, false);
2927         } else {
2928                 ARDOUR_UI::instance()->editor_transport_box().pack_start (*nudge_box, false, false);
2929         }
2930         hbox->pack_start (panic_box, false, false);
2931
2932         hbox->show_all ();
2933
2934         toolbar_base.set_name ("ToolBarBase");
2935         toolbar_base.add (toolbar_hbox);
2936
2937         _toolbar_viewport.add (toolbar_base);
2938         /* stick to the required height but allow width to vary if there's not enough room */
2939         _toolbar_viewport.set_size_request (1, -1);
2940
2941         toolbar_frame.set_shadow_type (SHADOW_OUT);
2942         toolbar_frame.set_name ("BaseFrame");
2943         toolbar_frame.add (_toolbar_viewport);
2944
2945         DPIReset.connect (sigc::mem_fun (*this, &Editor::resize_text_widgets));
2946 }
2947
2948 void
2949 Editor::setup_tooltips ()
2950 {
2951         ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Select/Move Objects"));
2952         ARDOUR_UI::instance()->set_tip (mouse_gain_button, _("Draw Region Gain"));
2953         ARDOUR_UI::instance()->set_tip (mouse_zoom_button, _("Select Zoom Range"));
2954         ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
2955         ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2956         ARDOUR_UI::instance()->set_tip (join_object_range_button, _("Select/Move Objects or Ranges"));
2957         ARDOUR_UI::instance()->set_tip (internal_edit_button, _("Edit Region Contents (e.g. notes)"));
2958         ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
2959         ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
2960         ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
2961         ARDOUR_UI::instance()->set_tip (zoom_in_button, _("Zoom In"));
2962         ARDOUR_UI::instance()->set_tip (zoom_out_button, _("Zoom Out"));
2963         ARDOUR_UI::instance()->set_tip (zoom_out_full_button, _("Zoom to Session"));
2964         ARDOUR_UI::instance()->set_tip (zoom_focus_selector, _("Zoom focus"));
2965         ARDOUR_UI::instance()->set_tip (tav_expand_button, _("Expand Tracks"));
2966         ARDOUR_UI::instance()->set_tip (tav_shrink_button, _("Shrink Tracks"));
2967         ARDOUR_UI::instance()->set_tip (snap_type_selector, _("Snap/Grid Units"));
2968         ARDOUR_UI::instance()->set_tip (snap_mode_selector, _("Snap/Grid Mode"));
2969         ARDOUR_UI::instance()->set_tip (edit_point_selector, _("Edit point"));
2970         ARDOUR_UI::instance()->set_tip (midi_sound_notes, _("Sound Notes"));
2971         ARDOUR_UI::instance()->set_tip (midi_panic_button, _("Send note off and reset controller messages on all MIDI channels"));
2972         ARDOUR_UI::instance()->set_tip (edit_mode_selector, _("Edit Mode"));
2973 }
2974
2975 void
2976 Editor::midi_panic ()
2977 {
2978         cerr << "MIDI panic\n";
2979
2980         if (_session) {
2981                 _session->midi_panic();
2982         }
2983 }
2984
2985 void
2986 Editor::setup_midi_toolbar ()
2987 {
2988         RefPtr<Action> act;
2989
2990         /* Midi sound notes */
2991         midi_sound_notes.add (*(manage (new Image (::get_icon("midi_sound_notes")))));
2992         midi_sound_notes.unset_flags (CAN_FOCUS);
2993
2994         /* Panic */
2995
2996         act = ActionManager::get_action (X_("MIDI"), X_("panic"));
2997         midi_panic_button.set_name("MidiPanicButton");
2998         act->connect_proxy (midi_panic_button);
2999
3000         panic_box.pack_start (midi_sound_notes , true, true);
3001         panic_box.pack_start (midi_panic_button, true, true);
3002 }
3003
3004 int
3005 Editor::convert_drop_to_paths (
3006                 vector<string>&                paths,
3007                 const RefPtr<Gdk::DragContext>& /*context*/,
3008                 gint                            /*x*/,
3009                 gint                            /*y*/,
3010                 const SelectionData&            data,
3011                 guint                           /*info*/,
3012                 guint                           /*time*/)
3013 {
3014         if (_session == 0) {
3015                 return -1;
3016         }
3017
3018         vector<string> uris = data.get_uris();
3019
3020         if (uris.empty()) {
3021
3022                 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
3023                    are actually URI lists. So do it by hand.
3024                 */
3025
3026                 if (data.get_target() != "text/plain") {
3027                         return -1;
3028                 }
3029
3030                 /* Parse the "uri-list" format that Nautilus provides,
3031                    where each pathname is delimited by \r\n.
3032
3033                    THERE MAY BE NO NULL TERMINATING CHAR!!!
3034                 */
3035
3036                 string txt = data.get_text();
3037                 const char* p;
3038                 const char* q;
3039
3040                 p = (const char *) malloc (txt.length() + 1);
3041                 txt.copy ((char *) p, txt.length(), 0);
3042                 ((char*)p)[txt.length()] = '\0';
3043
3044                 while (p)
3045                 {
3046                         if (*p != '#')
3047                         {
3048                                 while (g_ascii_isspace (*p))
3049                                         p++;
3050
3051                                 q = p;
3052                                 while (*q && (*q != '\n') && (*q != '\r')) {
3053                                         q++;
3054                                 }
3055
3056                                 if (q > p)
3057                                 {
3058                                         q--;
3059                                         while (q > p && g_ascii_isspace (*q))
3060                                                 q--;
3061
3062                                         if (q > p)
3063                                         {
3064                                                 uris.push_back (string (p, q - p + 1));
3065                                         }
3066                                 }
3067                         }
3068                         p = strchr (p, '\n');
3069                         if (p)
3070                                 p++;
3071                 }
3072
3073                 free ((void*)p);
3074
3075                 if (uris.empty()) {
3076                         return -1;
3077                 }
3078         }
3079
3080         for (vector<string>::iterator i = uris.begin(); i != uris.end(); ++i) {
3081
3082                 if ((*i).substr (0,7) == "file://") {
3083
3084                         string p = *i;
3085                         PBD::url_decode (p);
3086
3087                         // scan forward past three slashes
3088
3089                         string::size_type slashcnt = 0;
3090                         string::size_type n = 0;
3091                         string::iterator x = p.begin();
3092
3093                         while (slashcnt < 3 && x != p.end()) {
3094                                 if ((*x) == '/') {
3095                                         slashcnt++;
3096                                 } else if (slashcnt == 3) {
3097                                         break;
3098                                 }
3099                                 ++n;
3100                                 ++x;
3101                         }
3102
3103                         if (slashcnt != 3 || x == p.end()) {
3104                                 error << _("malformed URL passed to drag-n-drop code") << endmsg;
3105                                 continue;
3106                         }
3107
3108                         paths.push_back (p.substr (n - 1));
3109                 }
3110         }
3111
3112         return 0;
3113 }
3114
3115 void
3116 Editor::new_tempo_section ()
3117
3118 {
3119 }
3120
3121 void
3122 Editor::map_transport_state ()
3123 {
3124         ENSURE_GUI_THREAD (*this, &Editor::map_transport_state)
3125
3126         if (_session && _session->transport_stopped()) {
3127                 have_pending_keyboard_selection = false;
3128         }
3129
3130         update_loop_range_view (true);
3131 }
3132
3133 /* UNDO/REDO */
3134
3135 Editor::State::State (PublicEditor const * e)
3136 {
3137         selection = new Selection (e);
3138 }
3139
3140 Editor::State::~State ()
3141 {
3142         delete selection;
3143 }
3144
3145 void
3146 Editor::begin_reversible_command (string name)
3147 {
3148         if (_session) {
3149                 _session->begin_reversible_command (name);
3150         }
3151 }
3152
3153 void
3154 Editor::begin_reversible_command (GQuark q)
3155 {
3156         if (_session) {
3157                 _session->begin_reversible_command (q);
3158         }
3159 }
3160
3161 void
3162 Editor::commit_reversible_command ()
3163 {
3164         if (_session) {
3165                 _session->commit_reversible_command ();
3166         }
3167 }
3168
3169 void
3170 Editor::history_changed ()
3171 {
3172         string label;
3173
3174         if (undo_action && _session) {
3175                 if (_session->undo_depth() == 0) {
3176                         label = _("Undo");
3177                 } else {
3178                         label = string_compose(_("Undo (%1)"), _session->next_undo());
3179                 }
3180                 undo_action->property_label() = label;
3181         }
3182
3183         if (redo_action && _session) {
3184                 if (_session->redo_depth() == 0) {
3185                         label = _("Redo");
3186                 } else {
3187                         label = string_compose(_("Redo (%1)"), _session->next_redo());
3188                 }
3189                 redo_action->property_label() = label;
3190         }
3191 }
3192
3193 void
3194 Editor::duplicate_dialog (bool with_dialog)
3195 {
3196         float times = 1.0f;
3197
3198         if (mouse_mode == MouseRange) {
3199                 if (selection->time.length() == 0) {
3200                         return;
3201                 }
3202         }
3203
3204         RegionSelection rs = get_regions_from_selection_and_entered ();
3205
3206         if (mouse_mode != MouseRange && rs.empty()) {
3207                 return;
3208         }
3209
3210         if (with_dialog) {
3211
3212                 ArdourDialog win (_("Duplicate"));
3213                 Label label (_("Number of duplications:"));
3214                 Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
3215                 SpinButton spinner (adjustment, 0.0, 1);
3216                 HBox hbox;
3217
3218                 win.get_vbox()->set_spacing (12);
3219                 win.get_vbox()->pack_start (hbox);
3220                 hbox.set_border_width (6);
3221                 hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
3222
3223                 /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
3224                    place, visually. so do this by hand.
3225                 */
3226
3227                 hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
3228                 spinner.signal_activate().connect (sigc::bind (sigc::mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
3229                 spinner.grab_focus();
3230
3231                 hbox.show ();
3232                 label.show ();
3233                 spinner.show ();
3234
3235                 win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
3236                 win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
3237                 win.set_default_response (RESPONSE_ACCEPT);
3238
3239                 win.set_position (WIN_POS_MOUSE);
3240
3241                 spinner.grab_focus ();
3242
3243                 switch (win.run ()) {
3244                 case RESPONSE_ACCEPT:
3245                         break;
3246                 default:
3247                         return;
3248                 }
3249
3250                 times = adjustment.get_value();
3251         }
3252
3253         if (mouse_mode == MouseRange) {
3254                 duplicate_selection (times);
3255         } else {
3256                 duplicate_some_regions (rs, times);
3257         }
3258 }
3259
3260 void
3261 Editor::set_edit_mode (EditMode m)
3262 {
3263         Config->set_edit_mode (m);
3264 }
3265
3266 void
3267 Editor::cycle_edit_mode ()
3268 {
3269         switch (Config->get_edit_mode()) {
3270         case Slide:
3271                 if (Profile->get_sae()) {
3272                         Config->set_edit_mode (Lock);
3273                 } else {
3274                         Config->set_edit_mode (Splice);
3275                 }
3276                 break;
3277         case Splice:
3278                 Config->set_edit_mode (Lock);
3279                 break;
3280         case Lock:
3281                 Config->set_edit_mode (Slide);
3282                 break;
3283         }
3284 }
3285
3286 void
3287 Editor::edit_mode_selection_done ()
3288 {
3289         string s = edit_mode_selector.get_active_text ();
3290
3291         if (!s.empty()) {
3292                 Config->set_edit_mode (string_to_edit_mode (s));
3293         }
3294 }
3295
3296 void
3297 Editor::snap_type_selection_done ()
3298 {
3299         string choice = snap_type_selector.get_active_text();
3300         SnapType snaptype = SnapToBeat;
3301
3302         if (choice == _("Beats/2")) {
3303                 snaptype = SnapToBeatDiv2;
3304         } else if (choice == _("Beats/3")) {
3305                 snaptype = SnapToBeatDiv3;
3306         } else if (choice == _("Beats/4")) {
3307                 snaptype = SnapToBeatDiv4;
3308         } else if (choice == _("Beats/5")) {
3309                 snaptype = SnapToBeatDiv5;
3310         } else if (choice == _("Beats/6")) {
3311                 snaptype = SnapToBeatDiv6;
3312         } else if (choice == _("Beats/7")) {
3313                 snaptype = SnapToBeatDiv7;
3314         } else if (choice == _("Beats/8")) {
3315                 snaptype = SnapToBeatDiv8;
3316         } else if (choice == _("Beats/10")) {
3317                 snaptype = SnapToBeatDiv10;
3318         } else if (choice == _("Beats/12")) {
3319                 snaptype = SnapToBeatDiv12;
3320         } else if (choice == _("Beats/14")) {
3321                 snaptype = SnapToBeatDiv14;
3322         } else if (choice == _("Beats/16")) {
3323                 snaptype = SnapToBeatDiv16;
3324         } else if (choice == _("Beats/20")) {
3325                 snaptype = SnapToBeatDiv20;
3326         } else if (choice == _("Beats/24")) {
3327                 snaptype = SnapToBeatDiv24;
3328         } else if (choice == _("Beats/28")) {
3329                 snaptype = SnapToBeatDiv28;
3330         } else if (choice == _("Beats/32")) {
3331                 snaptype = SnapToBeatDiv32;
3332         } else if (choice == _("Beats")) {
3333                 snaptype = SnapToBeat;
3334         } else if (choice == _("Bars")) {
3335                 snaptype = SnapToBar;
3336         } else if (choice == _("Marks")) {
3337                 snaptype = SnapToMark;
3338         } else if (choice == _("Region starts")) {
3339                 snaptype = SnapToRegionStart;
3340         } else if (choice == _("Region ends")) {
3341                 snaptype = SnapToRegionEnd;
3342         } else if (choice == _("Region bounds")) {
3343                 snaptype = SnapToRegionBoundary;
3344         } else if (choice == _("Region syncs")) {
3345                 snaptype = SnapToRegionSync;
3346         } else if (choice == _("CD Frames")) {
3347                 snaptype = SnapToCDFrame;
3348         } else if (choice == _("Timecode Frames")) {
3349                 snaptype = SnapToTimecodeFrame;
3350         } else if (choice == _("Timecode Seconds")) {
3351                 snaptype = SnapToTimecodeSeconds;
3352         } else if (choice == _("Timecode Minutes")) {
3353                 snaptype = SnapToTimecodeMinutes;
3354         } else if (choice == _("Seconds")) {
3355                 snaptype = SnapToSeconds;
3356         } else if (choice == _("Minutes")) {
3357                 snaptype = SnapToMinutes;
3358         }
3359
3360         RefPtr<RadioAction> ract = snap_type_action (snaptype);
3361         if (ract) {
3362                 ract->set_active ();
3363         }
3364 }
3365
3366 void
3367 Editor::snap_mode_selection_done ()
3368 {
3369         string choice = snap_mode_selector.get_active_text();
3370         SnapMode mode = SnapNormal;
3371
3372         if (choice == _("No Grid")) {
3373                 mode = SnapOff;
3374         } else if (choice == _("Grid")) {
3375                 mode = SnapNormal;
3376         } else if (choice == _("Magnetic")) {
3377                 mode = SnapMagnetic;
3378         }
3379
3380         RefPtr<RadioAction> ract = snap_mode_action (mode);
3381
3382         if (ract) {
3383                 ract->set_active (true);
3384         }
3385 }
3386
3387 void
3388 Editor::cycle_edit_point (bool with_marker)
3389 {
3390         switch (_edit_point) {
3391         case EditAtMouse:
3392                 set_edit_point_preference (EditAtPlayhead);
3393                 break;
3394         case EditAtPlayhead:
3395                 if (with_marker) {
3396                         set_edit_point_preference (EditAtSelectedMarker);
3397                 } else {
3398                         set_edit_point_preference (EditAtMouse);
3399                 }
3400                 break;
3401         case EditAtSelectedMarker:
3402                 set_edit_point_preference (EditAtMouse);
3403                 break;
3404         }
3405 }
3406
3407 void
3408 Editor::edit_point_selection_done ()
3409 {
3410         string choice = edit_point_selector.get_active_text();
3411         EditPoint ep = EditAtSelectedMarker;
3412
3413         if (choice == _("Marker")) {
3414                 set_edit_point_preference (EditAtSelectedMarker);
3415         } else if (choice == _("Playhead")) {
3416                 set_edit_point_preference (EditAtPlayhead);
3417         } else {
3418                 set_edit_point_preference (EditAtMouse);
3419         }
3420
3421         RefPtr<RadioAction> ract = edit_point_action (ep);
3422
3423         if (ract) {
3424                 ract->set_active (true);
3425         }
3426 }
3427
3428 void
3429 Editor::zoom_focus_selection_done ()
3430 {
3431         string choice = zoom_focus_selector.get_active_text();
3432         ZoomFocus focus_type = ZoomFocusLeft;
3433
3434         if (choice == _("Left")) {
3435                 focus_type = ZoomFocusLeft;
3436         } else if (choice == _("Right")) {
3437                 focus_type = ZoomFocusRight;
3438         } else if (choice == _("Center")) {
3439                 focus_type = ZoomFocusCenter;
3440         } else if (choice == _("Playhead")) {
3441                 focus_type = ZoomFocusPlayhead;
3442         } else if (choice == _("Mouse")) {
3443                 focus_type = ZoomFocusMouse;
3444         } else if (choice == _("Edit point")) {
3445                 focus_type = ZoomFocusEdit;
3446         }
3447
3448         RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3449
3450         if (ract) {
3451                 ract->set_active ();
3452         }
3453 }
3454
3455 bool
3456 Editor::edit_controls_button_release (GdkEventButton* ev)
3457 {
3458         if (Keyboard::is_context_menu_event (ev)) {
3459                 ARDOUR_UI::instance()->add_route (this);
3460         } else if (ev->button == 1) {
3461                 selection->clear_tracks ();
3462         }
3463
3464         return true;
3465 }
3466
3467 bool
3468 Editor::mouse_select_button_release (GdkEventButton* ev)
3469 {
3470         /* this handles just right-clicks */
3471
3472         if (ev->button != 3) {
3473                 return false;
3474         }
3475
3476         return true;
3477 }
3478
3479 void
3480 Editor::set_zoom_focus (ZoomFocus f)
3481 {
3482         string str = zoom_focus_strings[(int)f];
3483
3484         if (str != zoom_focus_selector.get_active_text()) {
3485                 zoom_focus_selector.set_active_text (str);
3486         }
3487
3488         if (zoom_focus != f) {
3489                 zoom_focus = f;
3490                 instant_save ();
3491         }
3492 }
3493
3494 void
3495 Editor::ensure_float (Window& win)
3496 {
3497         win.set_transient_for (*this);
3498 }
3499
3500 void
3501 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3502 {
3503         /* recover or initialize pane positions. do this here rather than earlier because
3504            we don't want the positions to change the child allocations, which they seem to do.
3505          */
3506
3507         int pos;
3508         XMLProperty* prop;
3509         char buf[32];
3510         XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3511
3512         enum Pane {
3513                 Horizontal = 0x1,
3514                 Vertical = 0x2
3515         };
3516
3517         static Pane done;
3518
3519         XMLNode* geometry = find_named_node (*node, "geometry");
3520
3521         if (which == static_cast<Paned*> (&edit_pane)) {
3522
3523                 if (done & Horizontal) {
3524                         return;
3525                 }
3526
3527                 if (geometry && (prop = geometry->property ("notebook-shrunk"))) {
3528                         _notebook_shrunk = string_is_affirmative (prop->value ());
3529                 }
3530
3531                 if (geometry && (prop = geometry->property ("pre-maximal-horizontal-pane-position"))) {
3532                         pre_maximal_horizontal_pane_position = atoi (prop->value ());
3533                 }
3534
3535                 if (!geometry || (prop = geometry->property ("edit-horizontal-pane-pos")) == 0) {
3536                         /* initial allocation is 90% to canvas, 10% to notebook */
3537                         pos = (int) floor (alloc.get_width() * 0.90f);
3538                         snprintf (buf, sizeof(buf), "%d", pos);
3539                 } else {
3540                         pos = atoi (prop->value());
3541                 }
3542
3543                 if (GTK_WIDGET(edit_pane.gobj())->allocation.width > pos) {
3544                         edit_pane.set_position (pos);
3545                         if (pre_maximal_horizontal_pane_position == 0) {
3546                                 pre_maximal_horizontal_pane_position = pos;
3547                         }
3548                 }
3549
3550                 done = (Pane) (done | Horizontal);
3551
3552         } else if (which == static_cast<Paned*> (&editor_summary_pane)) {
3553
3554                 if (done & Vertical) {
3555                         return;
3556                 }
3557
3558                 if (!geometry || (prop = geometry->property ("edit-vertical-pane-pos")) == 0) {
3559                         /* initial allocation is 90% to canvas, 10% to summary */
3560                         pos = (int) floor (alloc.get_height() * 0.90f);
3561                         snprintf (buf, sizeof(buf), "%d", pos);
3562                 } else {
3563                         pos = atoi (prop->value());
3564                 }
3565
3566                 if (GTK_WIDGET(editor_summary_pane.gobj())->allocation.height > pos) {
3567                         editor_summary_pane.set_position (pos);
3568                         pre_maximal_vertical_pane_position = pos;
3569                 }
3570
3571                 done = (Pane) (done | Vertical);
3572         }
3573 }
3574
3575 void
3576 Editor::detach_tearoff (Box* /*b*/, Window* /*w*/)
3577 {
3578         if (_tools_tearoff->torn_off() && _mouse_mode_tearoff->torn_off()) {
3579                 top_hbox.remove (toolbar_frame);
3580         }
3581 }
3582
3583 void
3584 Editor::reattach_tearoff (Box* /*b*/, Window* /*w*/, int32_t /*n*/)
3585 {
3586         if (toolbar_frame.get_parent() == 0) {
3587                 top_hbox.pack_end (toolbar_frame);
3588         }
3589 }
3590
3591 void
3592 Editor::set_show_measures (bool yn)
3593 {
3594         if (_show_measures != yn) {
3595                 hide_measures ();
3596
3597                 if ((_show_measures = yn) == true) {
3598                         if (tempo_lines)
3599                                 tempo_lines->show();
3600                         draw_measures ();
3601                 }
3602                 instant_save ();
3603         }
3604 }
3605
3606 void
3607 Editor::toggle_follow_playhead ()
3608 {
3609         RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3610         if (act) {
3611                 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3612                 set_follow_playhead (tact->get_active());
3613         }
3614 }
3615
3616 /** @param yn true to follow playhead, otherwise false.
3617  *  @param catch_up true to reset the editor view to show the playhead (if yn == true), otherwise false.
3618  */
3619 void
3620 Editor::set_follow_playhead (bool yn, bool catch_up)
3621 {
3622         if (_follow_playhead != yn) {
3623                 if ((_follow_playhead = yn) == true && catch_up) {
3624                         /* catch up */
3625                         reset_x_origin_to_follow_playhead ();
3626                 }
3627                 instant_save ();
3628         }
3629 }
3630
3631 void
3632 Editor::toggle_stationary_playhead ()
3633 {
3634         RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
3635         if (act) {
3636                 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3637                 set_stationary_playhead (tact->get_active());
3638         }
3639 }
3640
3641 void
3642 Editor::set_stationary_playhead (bool yn)
3643 {
3644         if (_stationary_playhead != yn) {
3645                 if ((_stationary_playhead = yn) == true) {
3646                         /* catch up */
3647                         // FIXME need a 3.0 equivalent of this 2.X call
3648                         // update_current_screen ();
3649                 }
3650                 instant_save ();
3651         }
3652 }
3653
3654 void
3655 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3656 {
3657         boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3658         if (xfade) {
3659                 xfade->set_active (!xfade->active());
3660         }
3661 }
3662
3663 void
3664 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3665 {
3666         boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3667         if (xfade) {
3668                 xfade->set_follow_overlap (!xfade->following_overlap());
3669         }
3670 }
3671
3672 void
3673 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3674 {
3675         boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3676
3677         if (!xfade) {
3678                 return;
3679         }
3680
3681         CrossfadeEditor cew (_session, xfade, xfade->fade_in().get_min_y(), 1.0);
3682
3683         ensure_float (cew);
3684
3685         switch (cew.run ()) {
3686         case RESPONSE_ACCEPT:
3687                 break;
3688         default:
3689                 return;
3690         }
3691
3692         cew.apply ();
3693         PropertyChange all_crossfade_properties;
3694         all_crossfade_properties.add (ARDOUR::Properties::active);
3695         all_crossfade_properties.add (ARDOUR::Properties::follow_overlap);
3696         xfade->PropertyChanged (all_crossfade_properties);
3697 }
3698
3699 PlaylistSelector&
3700 Editor::playlist_selector () const
3701 {
3702         return *_playlist_selector;
3703 }
3704
3705 Evoral::MusicalTime
3706 Editor::get_grid_type_as_beats (bool& success, framepos_t position)
3707 {
3708         success = true;
3709
3710         switch (_snap_type) {
3711         case SnapToBeat:
3712                 return 1.0;
3713                 break;
3714
3715         case SnapToBeatDiv32:
3716                 return 1.0/32.0;
3717                 break;
3718         case SnapToBeatDiv28:
3719                 return 1.0/28.0;
3720                 break;
3721         case SnapToBeatDiv24:
3722                 return 1.0/24.0;
3723                 break;
3724         case SnapToBeatDiv20:
3725                 return 1.0/20.0;
3726                 break;
3727         case SnapToBeatDiv16:
3728                 return 1.0/16.0;
3729                 break;
3730         case SnapToBeatDiv14:
3731                 return 1.0/14.0;
3732                 break;
3733         case SnapToBeatDiv12:
3734                 return 1.0/12.0;
3735                 break;
3736         case SnapToBeatDiv10:
3737                 return 1.0/10.0;
3738                 break;
3739         case SnapToBeatDiv8:
3740                 return 1.0/8.0;
3741                 break;
3742         case SnapToBeatDiv7:
3743                 return 1.0/7.0;
3744                 break;
3745         case SnapToBeatDiv6:
3746                 return 1.0/6.0;
3747                 break;
3748         case SnapToBeatDiv5:
3749                 return 1.0/5.0;
3750                 break;
3751         case SnapToBeatDiv4:
3752                 return 1.0/4.0;
3753                 break;
3754         case SnapToBeatDiv3:
3755                 return 1.0/3.0;
3756                 break;
3757         case SnapToBeatDiv2:
3758                 return 1.0/2.0;
3759                 break;
3760
3761         case SnapToBar:
3762                 if (_session) {
3763                         return _session->tempo_map().meter_at (position).beats_per_bar();
3764                 }
3765                 break;
3766
3767         case SnapToCDFrame:
3768         case SnapToTimecodeFrame:
3769         case SnapToTimecodeSeconds:
3770         case SnapToTimecodeMinutes:
3771         case SnapToSeconds:
3772         case SnapToMinutes:
3773         case SnapToRegionStart:
3774         case SnapToRegionEnd:
3775         case SnapToRegionSync:
3776         case SnapToRegionBoundary:
3777         default:
3778                 success = false;
3779                 break;
3780         }
3781
3782         return 0.0;
3783 }
3784
3785 framecnt_t
3786 Editor::get_nudge_distance (framepos_t pos, framecnt_t& next)
3787 {
3788         framecnt_t ret;
3789
3790         ret = nudge_clock->current_duration (pos);
3791         next = ret + 1; /* XXXX fix me */
3792
3793         return ret;
3794 }
3795
3796 int
3797 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3798 {
3799         ArdourDialog dialog (_("Playlist Deletion"));
3800         Label  label (string_compose (_("Playlist %1 is currently unused.\n"
3801                                         "If it is kept, its audio files will not be cleaned.\n"
3802                                         "If it is deleted, audio files used by it alone will be cleaned."),
3803                                       pl->name()));
3804
3805         dialog.set_position (WIN_POS_CENTER);
3806         dialog.get_vbox()->pack_start (label);
3807
3808         label.show ();
3809
3810         dialog.add_button (_("Delete Playlist"), RESPONSE_ACCEPT);
3811         dialog.add_button (_("Keep Playlist"), RESPONSE_REJECT);
3812         dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3813
3814         switch (dialog.run ()) {
3815         case RESPONSE_ACCEPT:
3816                 /* delete the playlist */
3817                 return 0;
3818                 break;
3819
3820         case RESPONSE_REJECT:
3821                 /* keep the playlist */
3822                 return 1;
3823                 break;
3824
3825         default:
3826                 break;
3827         }
3828
3829         return -1;
3830 }
3831
3832 bool
3833 Editor::audio_region_selection_covers (framepos_t where)
3834 {
3835         for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3836                 if ((*a)->region()->covers (where)) {
3837                         return true;
3838                 }
3839         }
3840
3841         return false;
3842 }
3843
3844 void
3845 Editor::prepare_for_cleanup ()
3846 {
3847         cut_buffer->clear_regions ();
3848         cut_buffer->clear_playlists ();
3849
3850         selection->clear_regions ();
3851         selection->clear_playlists ();
3852
3853         _regions->suspend_redisplay ();
3854 }
3855
3856 void
3857 Editor::finish_cleanup ()
3858 {
3859         _regions->resume_redisplay ();
3860 }
3861
3862 Location*
3863 Editor::transport_loop_location()
3864 {
3865         if (_session) {
3866                 return _session->locations()->auto_loop_location();
3867         } else {
3868                 return 0;
3869         }
3870 }
3871
3872 Location*
3873 Editor::transport_punch_location()
3874 {
3875         if (_session) {
3876                 return _session->locations()->auto_punch_location();
3877         } else {
3878                 return 0;
3879         }
3880 }
3881
3882 bool
3883 Editor::control_layout_scroll (GdkEventScroll* ev)
3884 {
3885         if (Keyboard::some_magic_widget_has_focus()) {
3886                 return false;
3887         }
3888
3889         switch (ev->direction) {
3890         case GDK_SCROLL_UP:
3891                 scroll_tracks_up_line ();
3892                 return true;
3893                 break;
3894
3895         case GDK_SCROLL_DOWN:
3896                 scroll_tracks_down_line ();
3897                 return true;
3898
3899         default:
3900                 /* no left/right handling yet */
3901                 break;
3902         }
3903
3904         return false;
3905 }
3906
3907 void
3908 Editor::session_state_saved (string)
3909 {
3910         update_title ();
3911         _snapshots->redisplay ();
3912 }
3913
3914 void
3915 Editor::maximise_editing_space ()
3916 {
3917         _mouse_mode_tearoff->set_visible (false);
3918         _tools_tearoff->set_visible (false);
3919         _zoom_tearoff->set_visible (false);
3920
3921         pre_maximal_horizontal_pane_position = edit_pane.get_position ();
3922         pre_maximal_vertical_pane_position = editor_summary_pane.get_position ();
3923         pre_maximal_editor_width = this->get_width ();
3924         pre_maximal_editor_height = this->get_height ();
3925
3926         if (post_maximal_horizontal_pane_position == 0) {
3927                 post_maximal_horizontal_pane_position = edit_pane.get_width();
3928         }
3929
3930         if (post_maximal_vertical_pane_position == 0) {
3931                 post_maximal_vertical_pane_position = editor_summary_pane.get_height();
3932         }
3933
3934         fullscreen ();
3935
3936         if (post_maximal_editor_width) {
3937                 edit_pane.set_position (post_maximal_horizontal_pane_position -
3938                         abs(post_maximal_editor_width - pre_maximal_editor_width));
3939         } else {
3940                 edit_pane.set_position (post_maximal_horizontal_pane_position);
3941         }
3942
3943         /* Hack: we must do this in an idle handler for it to work; see comment in
3944            restore_editing_space()
3945         */
3946            
3947         Glib::signal_idle().connect (
3948                 sigc::bind (
3949                         sigc::mem_fun (*this, &Editor::idle_reset_vertical_pane_position),
3950                         post_maximal_vertical_pane_position
3951                         )
3952                 );
3953
3954         if (Config->get_keep_tearoffs()) {
3955                 _mouse_mode_tearoff->set_visible (true);
3956                 _tools_tearoff->set_visible (true);
3957                 if (Config->get_show_zoom_tools ()) {
3958                         _zoom_tearoff->set_visible (true);
3959                 }
3960         }
3961
3962 }
3963
3964 bool
3965 Editor::idle_reset_vertical_pane_position (int p)
3966 {
3967         editor_summary_pane.set_position (p);
3968         return false;
3969 }
3970
3971 void
3972 Editor::restore_editing_space ()
3973 {
3974         // user changed width/height of panes during fullscreen
3975
3976         if (post_maximal_horizontal_pane_position != edit_pane.get_position()) {
3977                 post_maximal_horizontal_pane_position = edit_pane.get_position();
3978         }
3979
3980         if (post_maximal_vertical_pane_position != editor_summary_pane.get_position()) {
3981                 post_maximal_vertical_pane_position = editor_summary_pane.get_position();
3982         }
3983
3984         unfullscreen();
3985
3986         _mouse_mode_tearoff->set_visible (true);
3987         _tools_tearoff->set_visible (true);
3988         if (Config->get_show_zoom_tools ()) {
3989                 _zoom_tearoff->set_visible (true);
3990         }
3991         post_maximal_editor_width = this->get_width();
3992         post_maximal_editor_height = this->get_height();
3993
3994         edit_pane.set_position (pre_maximal_horizontal_pane_position + abs(this->get_width() - pre_maximal_editor_width));
3995
3996         /* This is a bit of a hack, but it seems that if you set the vertical pane position
3997            here it gets reset to some wrong value after this method has finished.  Doing
3998            the setup in an idle callback seems to work.
3999         */
4000         Glib::signal_idle().connect (
4001                 sigc::bind (
4002                         sigc::mem_fun (*this, &Editor::idle_reset_vertical_pane_position),
4003                         pre_maximal_vertical_pane_position
4004                         )
4005                 );
4006 }
4007
4008 /**
4009  *  Make new playlists for a given track and also any others that belong
4010  *  to the same active route group with the `edit' property.
4011  *  @param v Track.
4012  */
4013
4014 void
4015 Editor::new_playlists (TimeAxisView* v)
4016 {
4017         begin_reversible_command (_("new playlists"));
4018         vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4019         _session->playlists->get (playlists);
4020         mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_new_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4021         commit_reversible_command ();
4022 }
4023
4024 /**
4025  *  Use a copy of the current playlist for a given track and also any others that belong
4026  *  to the same active route group with the `edit' property.
4027  *  @param v Track.
4028  */
4029
4030 void
4031 Editor::copy_playlists (TimeAxisView* v)
4032 {
4033         begin_reversible_command (_("copy playlists"));
4034         vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4035         _session->playlists->get (playlists);
4036         mapover_tracks (sigc::bind (sigc::mem_fun (*this, &Editor::mapped_use_copy_playlist), playlists), v, ARDOUR::Properties::edit.property_id);
4037         commit_reversible_command ();
4038 }
4039
4040 /** Clear the current playlist for a given track and also any others that belong
4041  *  to the same active route group with the `edit' property.
4042  *  @param v Track.
4043  */
4044
4045 void
4046 Editor::clear_playlists (TimeAxisView* v)
4047 {
4048         begin_reversible_command (_("clear playlists"));
4049         vector<boost::shared_ptr<ARDOUR::Playlist> > playlists;
4050         _session->playlists->get (playlists);
4051         mapover_tracks (sigc::mem_fun (*this, &Editor::mapped_clear_playlist), v, ARDOUR::Properties::edit.property_id);
4052         commit_reversible_command ();
4053 }
4054
4055 void
4056 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4057 {
4058         atv.use_new_playlist (sz > 1 ? false : true, playlists);
4059 }
4060
4061 void
4062 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz, vector<boost::shared_ptr<ARDOUR::Playlist> > const & playlists)
4063 {
4064         atv.use_copy_playlist (sz > 1 ? false : true, playlists);
4065 }
4066
4067 void
4068 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
4069 {
4070         atv.clear_playlist ();
4071 }
4072
4073 bool
4074 Editor::on_key_press_event (GdkEventKey* ev)
4075 {
4076         return key_press_focus_accelerator_handler (*this, ev);
4077 }
4078
4079 bool
4080 Editor::on_key_release_event (GdkEventKey* ev)
4081 {
4082         return Gtk::Window::on_key_release_event (ev);
4083         // return key_press_focus_accelerator_handler (*this, ev);
4084 }
4085
4086 /** Queue up a change to the viewport x origin.
4087  *  @param frame New x origin.
4088  */
4089 void
4090 Editor::reset_x_origin (framepos_t frame)
4091 {
4092         queue_visual_change (frame);
4093 }
4094
4095 void
4096 Editor::reset_y_origin (double y)
4097 {
4098         queue_visual_change_y (y);
4099 }
4100
4101 void
4102 Editor::reset_zoom (double fpu)
4103 {
4104         queue_visual_change (fpu);
4105 }
4106
4107 void
4108 Editor::reposition_and_zoom (framepos_t frame, double fpu)
4109 {
4110         reset_x_origin (frame);
4111         reset_zoom (fpu);
4112
4113         if (!no_save_visual) {
4114                 undo_visual_stack.push_back (current_visual_state(false));
4115         }
4116 }
4117
4118 Editor::VisualState::VisualState ()
4119         : gui_state (new GUIObjectState)
4120 {
4121 }
4122
4123 Editor::VisualState::~VisualState ()
4124 {
4125         delete gui_state;
4126 }
4127
4128 Editor::VisualState*
4129 Editor::current_visual_state (bool with_tracks)
4130 {
4131         VisualState* vs = new VisualState;
4132         vs->y_position = vertical_adjustment.get_value();
4133         vs->frames_per_unit = frames_per_unit;
4134         vs->leftmost_frame = leftmost_frame;
4135         vs->zoom_focus = zoom_focus;
4136
4137         if (with_tracks) {      
4138                 *(vs->gui_state) = *ARDOUR_UI::instance()->gui_object_state;
4139         }
4140
4141         return vs;
4142 }
4143
4144 void
4145 Editor::undo_visual_state ()
4146 {
4147         if (undo_visual_stack.empty()) {
4148                 return;
4149         }
4150
4151         redo_visual_stack.push_back (current_visual_state());
4152
4153         VisualState* vs = undo_visual_stack.back();
4154         undo_visual_stack.pop_back();
4155         use_visual_state (*vs);
4156 }
4157
4158 void
4159 Editor::redo_visual_state ()
4160 {
4161         if (redo_visual_stack.empty()) {
4162                 return;
4163         }
4164
4165         undo_visual_stack.push_back (current_visual_state());
4166
4167         VisualState* vs = redo_visual_stack.back();
4168         redo_visual_stack.pop_back();
4169         use_visual_state (*vs);
4170 }
4171
4172 void
4173 Editor::swap_visual_state ()
4174 {
4175         if (undo_visual_stack.empty()) {
4176                 redo_visual_state ();
4177         } else {
4178                 undo_visual_state ();
4179         }
4180 }
4181
4182 void
4183 Editor::use_visual_state (VisualState& vs)
4184 {
4185         no_save_visual = true;
4186
4187         _routes->suspend_redisplay ();
4188
4189         vertical_adjustment.set_value (vs.y_position);
4190
4191         set_zoom_focus (vs.zoom_focus);
4192         reposition_and_zoom (vs.leftmost_frame, vs.frames_per_unit);
4193         
4194         *ARDOUR_UI::instance()->gui_object_state = *vs.gui_state;
4195
4196         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {    
4197                 (*i)->reset_visual_state ();
4198         }
4199
4200         _routes->update_visibility ();
4201         _routes->resume_redisplay ();
4202
4203         no_save_visual = false;
4204 }
4205
4206 void
4207 Editor::set_frames_per_unit (double fpu)
4208 {
4209         /* this is the core function that controls the zoom level of the canvas. it is called
4210            whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
4211         */
4212
4213         if (fpu == frames_per_unit) {
4214                 return;
4215         }
4216
4217         if (fpu < 2.0) {
4218                 fpu = 2.0;
4219         }
4220
4221
4222         /* don't allow zooms that fit more than the maximum number
4223            of frames into an 800 pixel wide space.
4224         */
4225
4226         if (max_framepos / fpu < 800.0) {
4227                 return;
4228         }
4229
4230         if (tempo_lines)
4231                 tempo_lines->tempo_map_changed();
4232
4233         frames_per_unit = fpu;
4234         post_zoom ();
4235 }
4236
4237 void
4238 Editor::post_zoom ()
4239 {
4240         // convert fpu to frame count
4241
4242         framepos_t frames = (framepos_t) floor (frames_per_unit * _canvas_width);
4243
4244         if (frames_per_unit != zoom_range_clock->current_duration()) {
4245                 zoom_range_clock->set (frames);
4246         }
4247
4248         if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
4249                 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4250                         (*i)->reshow_selection (selection->time);
4251                 }
4252         }
4253
4254         ZoomChanged (); /* EMIT_SIGNAL */
4255
4256         //reset_scrolling_region ();
4257
4258         if (playhead_cursor) {
4259                 playhead_cursor->set_position (playhead_cursor->current_frame);
4260         }
4261
4262         refresh_location_display();
4263         _summary->set_overlays_dirty ();
4264
4265         update_marker_labels ();
4266
4267         instant_save ();
4268 }
4269
4270 void
4271 Editor::queue_visual_change (framepos_t where)
4272 {
4273         pending_visual_change.add (VisualChange::TimeOrigin);
4274         pending_visual_change.time_origin = where;
4275         ensure_visual_change_idle_handler ();
4276 }
4277
4278 void
4279 Editor::queue_visual_change (double fpu)
4280 {
4281         pending_visual_change.add (VisualChange::ZoomLevel);
4282         pending_visual_change.frames_per_unit = fpu;
4283
4284         ensure_visual_change_idle_handler ();
4285 }
4286
4287 void
4288 Editor::queue_visual_change_y (double y)
4289 {
4290         pending_visual_change.add (VisualChange::YOrigin);
4291         pending_visual_change.y_origin = y;
4292
4293         ensure_visual_change_idle_handler ();
4294 }
4295
4296 void
4297 Editor::ensure_visual_change_idle_handler ()
4298 {
4299         if (pending_visual_change.idle_handler_id < 0) {
4300                 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
4301         }
4302 }
4303
4304 int
4305 Editor::_idle_visual_changer (void* arg)
4306 {
4307         return static_cast<Editor*>(arg)->idle_visual_changer ();
4308 }
4309
4310 int
4311 Editor::idle_visual_changer ()
4312 {
4313         VisualChange::Type p = pending_visual_change.pending;
4314         pending_visual_change.pending = (VisualChange::Type) 0;
4315
4316         double const last_time_origin = horizontal_position ();
4317
4318         if (p & VisualChange::TimeOrigin) {
4319                 /* This is a bit of a hack, but set_frames_per_unit
4320                    below will (if called) end up with the
4321                    CrossfadeViews looking at Editor::leftmost_frame,
4322                    and if we're changing origin and zoom in the same
4323                    operation it will be the wrong value unless we
4324                    update it here.
4325                 */
4326
4327                 leftmost_frame = pending_visual_change.time_origin;
4328         }
4329
4330         if (p & VisualChange::ZoomLevel) {
4331                 set_frames_per_unit (pending_visual_change.frames_per_unit);
4332
4333                 compute_fixed_ruler_scale ();
4334                 compute_current_bbt_points(pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4335                 compute_bbt_ruler_scale (pending_visual_change.time_origin, pending_visual_change.time_origin + current_page_frames());
4336                 update_tempo_based_rulers ();
4337         }
4338         if (p & VisualChange::TimeOrigin) {
4339                 set_horizontal_position (pending_visual_change.time_origin / frames_per_unit);
4340         }
4341         if (p & VisualChange::YOrigin) {
4342                 vertical_adjustment.set_value (pending_visual_change.y_origin);
4343         }
4344
4345         if (last_time_origin == horizontal_position ()) {
4346                 /* changed signal not emitted */
4347                 update_fixed_rulers ();
4348                 redisplay_tempo (true);
4349         }
4350
4351         _summary->set_overlays_dirty ();
4352
4353         pending_visual_change.idle_handler_id = -1;
4354         return 0; /* this is always a one-shot call */
4355 }
4356
4357 struct EditorOrderTimeAxisSorter {
4358     bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
4359             return a->order () < b->order ();
4360     }
4361 };
4362
4363 void
4364 Editor::sort_track_selection (TrackViewList* sel)
4365 {
4366         EditorOrderTimeAxisSorter cmp;
4367
4368         if (sel) {
4369                 sel->sort (cmp);
4370         } else {
4371                 selection->tracks.sort (cmp);
4372         }
4373 }
4374
4375 framepos_t
4376 Editor::get_preferred_edit_position (bool ignore_playhead)
4377 {
4378         bool ignored;
4379         framepos_t where = 0;
4380         EditPoint ep = _edit_point;
4381
4382         if (entered_marker) {
4383                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use entered marker @ %1\n", entered_marker->position()));
4384                 return entered_marker->position();
4385         }
4386
4387         if (ignore_playhead && ep == EditAtPlayhead) {
4388                 ep = EditAtSelectedMarker;
4389         }
4390
4391         switch (ep) {
4392         case EditAtPlayhead:
4393                 where = _session->audible_frame();
4394                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where));
4395                 break;
4396
4397         case EditAtSelectedMarker:
4398                 if (!selection->markers.empty()) {
4399                         bool is_start;
4400                         Location* loc = find_location_from_marker (selection->markers.front(), is_start);
4401                         if (loc) {
4402                                 if (is_start) {
4403                                         where =  loc->start();
4404                                 } else {
4405                                         where = loc->end();
4406                                 }
4407                                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use selected marker @ %1\n", where));
4408                                 break;
4409                         }
4410                 }
4411                 /* fallthru */
4412
4413         default:
4414         case EditAtMouse:
4415                 if (!mouse_frame (where, ignored)) {
4416                         /* XXX not right but what can we do ? */
4417                         return 0;
4418                 }
4419                 snap_to (where);
4420                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use mouse @ %1\n", where));
4421                 break;
4422         }
4423
4424         return where;
4425 }
4426
4427 void
4428 Editor::set_loop_range (framepos_t start, framepos_t end, string cmd)
4429 {
4430         if (!_session) return;
4431
4432         begin_reversible_command (cmd);
4433
4434         Location* tll;
4435
4436         if ((tll = transport_loop_location()) == 0) {
4437                 Location* loc = new Location (*_session, start, end, _("Loop"),  Location::IsAutoLoop);
4438                 XMLNode &before = _session->locations()->get_state();
4439                 _session->locations()->add (loc, true);
4440                 _session->set_auto_loop_location (loc);
4441                 XMLNode &after = _session->locations()->get_state();
4442                 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4443         } else {
4444                 XMLNode &before = tll->get_state();
4445                 tll->set_hidden (false, this);
4446                 tll->set (start, end);
4447                 XMLNode &after = tll->get_state();
4448                 _session->add_command (new MementoCommand<Location>(*tll, &before, &after));
4449         }
4450
4451         commit_reversible_command ();
4452 }
4453
4454 void
4455 Editor::set_punch_range (framepos_t start, framepos_t end, string cmd)
4456 {
4457         if (!_session) return;
4458
4459         begin_reversible_command (cmd);
4460
4461         Location* tpl;
4462
4463         if ((tpl = transport_punch_location()) == 0) {
4464                 Location* loc = new Location (*_session, start, end, _("Loop"),  Location::IsAutoPunch);
4465                 XMLNode &before = _session->locations()->get_state();
4466                 _session->locations()->add (loc, true);
4467                 _session->set_auto_loop_location (loc);
4468                 XMLNode &after = _session->locations()->get_state();
4469                 _session->add_command (new MementoCommand<Locations>(*(_session->locations()), &before, &after));
4470         }
4471         else {
4472                 XMLNode &before = tpl->get_state();
4473                 tpl->set_hidden (false, this);
4474                 tpl->set (start, end);
4475                 XMLNode &after = tpl->get_state();
4476                 _session->add_command (new MementoCommand<Location>(*tpl, &before, &after));
4477         }
4478
4479         commit_reversible_command ();
4480 }
4481
4482 /** Find regions which exist at a given time, and optionally on a given list of tracks.
4483  *  @param rs List to which found regions are added.
4484  *  @param where Time to look at.
4485  *  @param ts Tracks to look on; if this is empty, all tracks are examined.
4486  */
4487 void
4488 Editor::get_regions_at (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4489 {
4490         const TrackViewList* tracks;
4491
4492         if (ts.empty()) {
4493                 tracks = &track_views;
4494         } else {
4495                 tracks = &ts;
4496         }
4497
4498         for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4499
4500                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4501
4502                 if (rtv) {
4503                         boost::shared_ptr<Track> tr;
4504                         boost::shared_ptr<Playlist> pl;
4505
4506                         if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4507
4508                                 Playlist::RegionList* regions = pl->regions_at (
4509                                                 (framepos_t) floor ( (double) where * tr->speed()));
4510
4511                                 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4512                                         RegionView* rv = rtv->view()->find_view (*i);
4513                                         if (rv) {
4514                                                 rs.add (rv);
4515                                         }
4516                                 }
4517
4518                                 delete regions;
4519                         }
4520                 }
4521         }
4522 }
4523
4524 void
4525 Editor::get_regions_after (RegionSelection& rs, framepos_t where, const TrackViewList& ts) const
4526 {
4527         const TrackViewList* tracks;
4528
4529         if (ts.empty()) {
4530                 tracks = &track_views;
4531         } else {
4532                 tracks = &ts;
4533         }
4534
4535         for (TrackViewList::const_iterator t = tracks->begin(); t != tracks->end(); ++t) {
4536                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(*t);
4537                 if (rtv) {
4538                         boost::shared_ptr<Track> tr;
4539                         boost::shared_ptr<Playlist> pl;
4540
4541                         if ((tr = rtv->track()) && ((pl = tr->playlist()))) {
4542
4543                                 Playlist::RegionList* regions = pl->regions_touched (
4544                                         (framepos_t) floor ( (double)where * tr->speed()), max_framepos);
4545
4546                                 for (Playlist::RegionList::iterator i = regions->begin(); i != regions->end(); ++i) {
4547
4548                                         RegionView* rv = rtv->view()->find_view (*i);
4549
4550                                         if (rv) {
4551                                                 rs.push_back (rv);
4552                                         }
4553                                 }
4554
4555                                 delete regions;
4556                         }
4557                 }
4558         }
4559 }
4560
4561 /** Start with regions that are selected.  Then add equivalent regions
4562  *  on tracks in the same active edit-enabled route group as any of
4563  *  the regions that we started with.
4564  */
4565
4566 RegionSelection
4567 Editor::get_regions_from_selection ()
4568 {
4569         return get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
4570 }
4571
4572 /** Get regions using the following method:
4573  *
4574  *  Make an initial region list using the selected regions, unless
4575  *  the edit point is `mouse' and the mouse is over an unselected
4576  *  region.  In this case, start with just that region.
4577  *
4578  *  Then, make an initial track list of the tracks that these
4579  *  regions are on, and if the edit point is not `mouse', add the
4580  *  selected tracks.
4581  *
4582  *  Look at this track list and add any other tracks that are on the
4583  *  same active edit-enabled route group as one of the initial tracks.
4584  *
4585  *  Finally take the initial region list and add any regions that are
4586  *  under the edit point on one of the tracks on the track list to get
4587  *  the returned region list.
4588  *
4589  *  The rationale here is that the mouse edit point is special in that
4590  *  its position describes both a time and a track; the other edit
4591  *  modes only describe a time.  Hence if the edit point is `mouse' we
4592  *  ignore selected tracks, as we assume the user means something by
4593  *  pointing at a particular track.  Also in this case we take note of
4594  *  the region directly under the edit point, as there is always just one
4595  *  (rather than possibly several with non-mouse edit points).
4596  */
4597
4598 RegionSelection
4599 Editor::get_regions_from_selection_and_edit_point ()
4600 {
4601         RegionSelection regions;
4602
4603         if (_edit_point == EditAtMouse && entered_regionview && !selection->regions.contains (entered_regionview)) {
4604                 regions.add (entered_regionview);
4605         } else {
4606                 regions = selection->regions;
4607         }
4608
4609         TrackViewList tracks;
4610
4611         if (_edit_point != EditAtMouse) {
4612                 tracks = selection->tracks;
4613         }
4614
4615         /* Add any other tracks that have regions that are in the same
4616            edit-activated route group as one of our regions.
4617          */
4618         for (RegionSelection::iterator i = regions.begin (); i != regions.end(); ++i) {
4619
4620                 RouteGroup* g = (*i)->get_time_axis_view().route_group ();
4621
4622                 if (g && g->is_active() && g->is_edit()) {
4623                         tracks.add (axis_views_from_routes (g->route_list()));
4624                 }
4625         }
4626
4627         if (!tracks.empty()) {
4628                 /* now find regions that are at the edit position on those tracks */
4629                 framepos_t const where = get_preferred_edit_position ();
4630                 get_regions_at (regions, where, tracks);
4631         }
4632
4633         return regions;
4634 }
4635
4636 /** Start with regions that are selected, or the entered regionview if none are selected.
4637  *  Then add equivalent regions on tracks in the same active edit-enabled route group as any
4638  *  of the regions that we started with.
4639  */
4640
4641 RegionSelection
4642 Editor::get_regions_from_selection_and_entered ()
4643 {
4644         RegionSelection regions = selection->regions;
4645
4646         if (regions.empty() && entered_regionview) {
4647                 regions.add (entered_regionview);
4648         }
4649
4650         return get_equivalent_regions (regions, ARDOUR::Properties::edit.property_id);
4651 }
4652
4653 void
4654 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions)
4655 {
4656         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
4657
4658                 RouteTimeAxisView* tatv;
4659
4660                 if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
4661
4662                         boost::shared_ptr<Playlist> pl;
4663                         vector<boost::shared_ptr<Region> > results;
4664                         RegionView* marv;
4665                         boost::shared_ptr<Track> tr;
4666
4667                         if ((tr = tatv->track()) == 0) {
4668                                 /* bus */
4669                                 continue;
4670                         }
4671
4672                         if ((pl = (tr->playlist())) != 0) {
4673                                 pl->get_region_list_equivalent_regions (region, results);
4674                         }
4675
4676                         for (vector<boost::shared_ptr<Region> >::iterator ir = results.begin(); ir != results.end(); ++ir) {
4677                                 if ((marv = tatv->view()->find_view (*ir)) != 0) {
4678                                         regions.push_back (marv);
4679                                 }
4680                         }
4681
4682                 }
4683         }
4684 }
4685
4686 void
4687 Editor::show_rhythm_ferret ()
4688 {
4689         if (rhythm_ferret == 0) {
4690                 rhythm_ferret = new RhythmFerret(*this);
4691         }
4692
4693         rhythm_ferret->set_session (_session);
4694         rhythm_ferret->show ();
4695         rhythm_ferret->present ();
4696 }
4697
4698 void
4699 Editor::first_idle ()
4700 {
4701         MessageDialog* dialog = 0;
4702
4703         if (track_views.size() > 1) {
4704                 dialog = new MessageDialog (*this,
4705                                             string_compose (_("Please wait while %1 loads visual data"), PROGRAM_NAME),
4706                                             true,
4707                                             Gtk::MESSAGE_INFO,
4708                                             Gtk::BUTTONS_NONE);
4709                 dialog->present ();
4710                 ARDOUR_UI::instance()->flush_pending ();
4711         }
4712
4713         for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
4714                 (*t)->first_idle();
4715         }
4716
4717         // first idle adds route children (automation tracks), so we need to redisplay here
4718         _routes->redisplay ();
4719
4720         delete dialog;
4721
4722         _have_idled = true;
4723 }
4724
4725 gboolean
4726 Editor::_idle_resize (gpointer arg)
4727 {
4728         return ((Editor*)arg)->idle_resize ();
4729 }
4730
4731 void
4732 Editor::add_to_idle_resize (TimeAxisView* view, int32_t h)
4733 {
4734         if (resize_idle_id < 0) {
4735                 resize_idle_id = g_idle_add (_idle_resize, this);
4736                 _pending_resize_amount = 0;
4737         }
4738
4739         /* make a note of the smallest resulting height, so that we can clamp the
4740            lower limit at TimeAxisView::hSmall */
4741
4742         int32_t min_resulting = INT32_MAX;
4743
4744         _pending_resize_amount += h;
4745         _pending_resize_view = view;
4746
4747         min_resulting = min (min_resulting, int32_t (_pending_resize_view->current_height()) + _pending_resize_amount);
4748
4749         if (selection->tracks.contains (_pending_resize_view)) {
4750                 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4751                         min_resulting = min (min_resulting, int32_t ((*i)->current_height()) + _pending_resize_amount);
4752                 }
4753         }
4754
4755         if (min_resulting < 0) {
4756                 min_resulting = 0;
4757         }
4758
4759         /* clamp */
4760         if (uint32_t (min_resulting) < TimeAxisView::preset_height (HeightSmall)) {
4761                 _pending_resize_amount += TimeAxisView::preset_height (HeightSmall) - min_resulting;
4762         }
4763 }
4764
4765 /** Handle pending resizing of tracks */
4766 bool
4767 Editor::idle_resize ()
4768 {
4769         _pending_resize_view->idle_resize (_pending_resize_view->current_height() + _pending_resize_amount);
4770
4771         if (dynamic_cast<AutomationTimeAxisView*> (_pending_resize_view) == 0 &&
4772             selection->tracks.contains (_pending_resize_view)) {
4773
4774                 for (TrackViewList::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
4775                         if (*i != _pending_resize_view) {
4776                                 (*i)->idle_resize ((*i)->current_height() + _pending_resize_amount);
4777                         }
4778                 }
4779         }
4780
4781         _pending_resize_amount = 0;
4782         flush_canvas ();
4783         _group_tabs->set_dirty ();
4784         resize_idle_id = -1;
4785
4786         return false;
4787 }
4788
4789 void
4790 Editor::located ()
4791 {
4792         ENSURE_GUI_THREAD (*this, &Editor::located);
4793
4794         playhead_cursor->set_position (_session->audible_frame ());
4795         if (_follow_playhead && !_pending_initial_locate) {
4796                 reset_x_origin_to_follow_playhead ();
4797         }
4798
4799         _pending_locate_request = false;
4800         _pending_initial_locate = false;
4801 }
4802
4803 void
4804 Editor::region_view_added (RegionView *)
4805 {
4806         _summary->set_dirty ();
4807 }
4808
4809 void
4810 Editor::region_view_removed ()
4811 {
4812         _summary->set_dirty ();
4813 }
4814
4815 TimeAxisView*
4816 Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
4817 {
4818         TrackViewList::const_iterator j = track_views.begin ();
4819         while (j != track_views.end()) {
4820                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*j);
4821                 if (rtv && rtv->route() == r) {
4822                         return rtv;
4823                 }
4824                 ++j;
4825         }
4826
4827         return 0;
4828 }
4829
4830
4831 TrackViewList
4832 Editor::axis_views_from_routes (boost::shared_ptr<RouteList> r) const
4833 {
4834         TrackViewList t;
4835
4836         for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
4837                 TimeAxisView* tv = axis_view_from_route (*i);
4838                 if (tv) {
4839                         t.push_back (tv);
4840                 }
4841         }
4842
4843         return t;
4844 }
4845
4846
4847 void
4848 Editor::handle_new_route (RouteList& routes)
4849 {
4850         ENSURE_GUI_THREAD (*this, &Editor::handle_new_route, routes)
4851
4852         RouteTimeAxisView *rtv;
4853         list<RouteTimeAxisView*> new_views;
4854
4855         for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
4856                 boost::shared_ptr<Route> route = (*x);
4857
4858                 if (route->is_hidden() || route->is_monitor()) {
4859                         continue;
4860                 }
4861
4862                 DataType dt = route->input()->default_type();
4863
4864                 if (dt == ARDOUR::DataType::AUDIO) {
4865                         rtv = new AudioTimeAxisView (*this, _session, *track_canvas);
4866                         rtv->set_route (route);
4867                 } else if (dt == ARDOUR::DataType::MIDI) {
4868                         rtv = new MidiTimeAxisView (*this, _session, *track_canvas);
4869                         rtv->set_route (route);
4870                 } else {
4871                         throw unknown_type();
4872                 }
4873
4874                 new_views.push_back (rtv);
4875                 track_views.push_back (rtv);
4876
4877                 rtv->effective_gain_display ();
4878
4879                 if (internal_editing()) {
4880                         rtv->enter_internal_edit_mode ();
4881                 } else {
4882                         rtv->leave_internal_edit_mode ();
4883                 }
4884
4885                 rtv->view()->RegionViewAdded.connect (sigc::mem_fun (*this, &Editor::region_view_added));
4886                 rtv->view()->RegionViewRemoved.connect (sigc::mem_fun (*this, &Editor::region_view_removed));
4887         }
4888
4889         _routes->routes_added (new_views);
4890         _summary->routes_added (new_views);
4891
4892         if (show_editor_mixer_when_tracks_arrive) {
4893                 show_editor_mixer (true);
4894         }
4895
4896         editor_list_button.set_sensitive (true);
4897 }
4898
4899 void
4900 Editor::timeaxisview_deleted (TimeAxisView *tv)
4901 {
4902         if (_session && _session->deletion_in_progress()) {
4903                 /* the situation is under control */
4904                 return;
4905         }
4906
4907         ENSURE_GUI_THREAD (*this, &Editor::timeaxisview_deleted, tv);
4908
4909         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (tv);
4910
4911         _routes->route_removed (tv);
4912
4913         if (tv == entered_track) {
4914                 entered_track = 0;
4915         }
4916
4917         TimeAxisView::Children c = tv->get_child_list ();
4918         for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
4919                 if (entered_track == i->get()) {
4920                         entered_track = 0;
4921                 }
4922         }
4923
4924         /* remove it from the list of track views */
4925
4926         TrackViewList::iterator i;
4927
4928         if ((i = find (track_views.begin(), track_views.end(), tv)) != track_views.end()) {
4929                 i = track_views.erase (i);
4930         }
4931
4932         /* update whatever the current mixer strip is displaying, if revelant */
4933
4934         boost::shared_ptr<Route> route;
4935
4936         if (rtav) {
4937                 route = rtav->route ();
4938         }
4939
4940         if (current_mixer_strip && current_mixer_strip->route() == route) {
4941
4942                 TimeAxisView* next_tv;
4943
4944                 if (track_views.empty()) {
4945                         next_tv = 0;
4946                 } else if (i == track_views.end()) {
4947                         next_tv = track_views.front();
4948                 } else {
4949                         next_tv = (*i);
4950                 }
4951
4952
4953                 if (next_tv) {
4954                         set_selected_mixer_strip (*next_tv);
4955                 } else {
4956                         /* make the editor mixer strip go away setting the
4957                          * button to inactive (which also unticks the menu option)
4958                          */
4959
4960                         ActionManager::uncheck_toggleaction ("<Actions>/Editor/show-editor-mixer");
4961                 }
4962         }
4963 }
4964
4965 void
4966 Editor::hide_track_in_display (TimeAxisView* tv, bool apply_to_selection)
4967 {
4968         if (apply_to_selection) {
4969                 for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ) {
4970
4971                         TrackSelection::iterator j = i;
4972                         ++j;
4973
4974                         hide_track_in_display (*i, false);
4975
4976                         i = j;
4977                 }
4978         } else {
4979                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
4980
4981                 if (rtv && current_mixer_strip && (rtv->route() == current_mixer_strip->route())) {
4982                         // this will hide the mixer strip
4983                         set_selected_mixer_strip (*tv);
4984                 }
4985
4986                 _routes->hide_track_in_display (*tv);
4987         }
4988 }
4989
4990 bool
4991 Editor::sync_track_view_list_and_routes ()
4992 {
4993         track_views = TrackViewList (_routes->views ());
4994
4995         _summary->set_dirty ();
4996         _group_tabs->set_dirty ();
4997
4998         return false; // do not call again (until needed)
4999 }
5000
5001 void
5002 Editor::foreach_time_axis_view (sigc::slot<void,TimeAxisView&> theslot)
5003 {
5004         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5005                 theslot (**i);
5006         }
5007 }
5008
5009 /** Find a RouteTimeAxisView by the ID of its route */
5010 RouteTimeAxisView*
5011 Editor::get_route_view_by_route_id (PBD::ID& id) const
5012 {
5013         RouteTimeAxisView* v;
5014
5015         for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
5016                 if((v = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5017                         if(v->route()->id() == id) {
5018                                 return v;
5019                         }
5020                 }
5021         }
5022
5023         return 0;
5024 }
5025
5026 void
5027 Editor::fit_route_group (RouteGroup *g)
5028 {
5029         TrackViewList ts = axis_views_from_routes (g->route_list ());
5030         fit_tracks (ts);
5031 }
5032
5033 void
5034 Editor::consider_auditioning (boost::shared_ptr<Region> region)
5035 {
5036         boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
5037
5038         if (r == 0) {
5039                 _session->cancel_audition ();
5040                 return;
5041         }
5042
5043         if (_session->is_auditioning()) {
5044                 _session->cancel_audition ();
5045                 if (r == last_audition_region) {
5046                         return;
5047                 }
5048         }
5049
5050         _session->audition_region (r);
5051         last_audition_region = r;
5052 }
5053
5054
5055 void
5056 Editor::hide_a_region (boost::shared_ptr<Region> r)
5057 {
5058         r->set_hidden (true);
5059 }
5060
5061 void
5062 Editor::show_a_region (boost::shared_ptr<Region> r)
5063 {
5064         r->set_hidden (false);
5065 }
5066
5067 void
5068 Editor::audition_region_from_region_list ()
5069 {
5070         _regions->selection_mapover (sigc::mem_fun (*this, &Editor::consider_auditioning));
5071 }
5072
5073 void
5074 Editor::hide_region_from_region_list ()
5075 {
5076         _regions->selection_mapover (sigc::mem_fun (*this, &Editor::hide_a_region));
5077 }
5078
5079 void
5080 Editor::show_region_in_region_list ()
5081 {
5082         _regions->selection_mapover (sigc::mem_fun (*this, &Editor::show_a_region));
5083 }
5084
5085 void
5086 Editor::step_edit_status_change (bool yn)
5087 {
5088         if (yn) {
5089                 start_step_editing ();
5090         } else {
5091                 stop_step_editing ();
5092         }
5093 }
5094
5095 void
5096 Editor::start_step_editing ()
5097 {
5098         step_edit_connection = Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::check_step_edit), 20);
5099 }
5100
5101 void
5102 Editor::stop_step_editing ()
5103 {
5104         step_edit_connection.disconnect ();
5105 }
5106
5107 bool
5108 Editor::check_step_edit ()
5109 {
5110         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5111                 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (*i);
5112                 if (mtv) {
5113                         mtv->check_step_edit ();
5114                 }
5115         }
5116
5117         return true; // do it again, till we stop
5118 }
5119
5120 bool
5121 Editor::scroll_press (Direction dir)
5122 {
5123         ++_scroll_callbacks;
5124
5125         if (_scroll_connection.connected() && _scroll_callbacks < 5) {
5126                 /* delay the first auto-repeat */
5127                 return true;
5128         }
5129
5130         switch (dir) {
5131         case LEFT:
5132                 scroll_backward (1);
5133                 break;
5134
5135         case RIGHT:
5136                 scroll_forward (1);
5137                 break;
5138
5139         case UP:
5140                 scroll_tracks_up_line ();
5141                 break;
5142
5143         case DOWN:
5144                 scroll_tracks_down_line ();
5145                 break;
5146         }
5147
5148         /* do hacky auto-repeat */
5149         if (!_scroll_connection.connected ()) {
5150
5151                 _scroll_connection = Glib::signal_timeout().connect (
5152                         sigc::bind (sigc::mem_fun (*this, &Editor::scroll_press), dir), 100
5153                         );
5154
5155                 _scroll_callbacks = 0;
5156         }
5157
5158         return true;
5159 }
5160
5161 void
5162 Editor::scroll_release ()
5163 {
5164         _scroll_connection.disconnect ();
5165 }
5166
5167 /** Queue a change for the Editor viewport x origin to follow the playhead */
5168 void
5169 Editor::reset_x_origin_to_follow_playhead ()
5170 {
5171         framepos_t const frame = playhead_cursor->current_frame;
5172
5173         if (frame < leftmost_frame || frame > leftmost_frame + current_page_frames()) {
5174
5175                 if (_session->transport_speed() < 0) {
5176
5177                         if (frame > (current_page_frames() / 2)) {
5178                                 center_screen (frame-(current_page_frames()/2));
5179                         } else {
5180                                 center_screen (current_page_frames()/2);
5181                         }
5182
5183                 } else {
5184
5185                         if (frame < leftmost_frame) {
5186                                 /* moving left */
5187                                 framepos_t l = 0;
5188                                 if (_session->transport_rolling()) {
5189                                         /* rolling; end up with the playhead at the right of the page */
5190                                         l = frame - current_page_frames ();
5191                                 } else {
5192                                         /* not rolling: end up with the playhead 3/4 of the way along the page */
5193                                         l = frame - (3 * current_page_frames() / 4);
5194                                 }
5195
5196                                 if (l < 0) {
5197                                         l = 0;
5198                                 }
5199
5200                                 center_screen_internal (l + (current_page_frames() / 2), current_page_frames ());
5201                         } else {
5202                                 /* moving right */
5203                                 if (_session->transport_rolling()) {
5204                                         /* rolling: end up with the playhead on the left of the page */
5205                                         center_screen_internal (frame + (current_page_frames() / 2), current_page_frames ());
5206                                 } else {
5207                                         /* not rolling: end up with the playhead 1/4 of the way along the page */
5208                                         center_screen_internal (frame + (current_page_frames() / 4), current_page_frames ());
5209                                 }
5210                         }
5211                 }
5212         }
5213 }
5214
5215 void
5216 Editor::super_rapid_screen_update ()
5217 {
5218         if (!_session || !_session->engine().running()) {
5219                 return;
5220         }
5221
5222         /* METERING / MIXER STRIPS */
5223
5224         /* update track meters, if required */
5225         if (is_mapped() && meters_running) {
5226                 RouteTimeAxisView* rtv;
5227                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5228                         if ((rtv = dynamic_cast<RouteTimeAxisView*>(*i)) != 0) {
5229                                 rtv->fast_update ();
5230                         }
5231                 }
5232         }
5233
5234         /* and any current mixer strip */
5235         if (current_mixer_strip) {
5236                 current_mixer_strip->fast_update ();
5237         }
5238
5239         /* PLAYHEAD AND VIEWPORT */
5240
5241         framepos_t const frame = _session->audible_frame();
5242
5243         /* There are a few reasons why we might not update the playhead / viewport stuff:
5244          *
5245          * 1.  we don't update things when there's a pending locate request, otherwise
5246          *     when the editor requests a locate there is a chance that this method
5247          *     will move the playhead before the locate request is processed, causing
5248          *     a visual glitch.
5249          * 2.  if we're not rolling, there's nothing to do here (locates are handled elsewhere).
5250          * 3.  if we're still at the same frame that we were last time, there's nothing to do.
5251          */
5252
5253         if (!_pending_locate_request && _session->transport_speed() != 0 && frame != last_update_frame) {
5254
5255                 last_update_frame = frame;
5256
5257                 if (!_dragging_playhead) {
5258                         playhead_cursor->set_position (frame);
5259                 }
5260
5261                 if (!_stationary_playhead) {
5262
5263                         if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0) {
5264                                 reset_x_origin_to_follow_playhead ();
5265                         }
5266
5267                 } else {
5268
5269                         /* don't do continuous scroll till the new position is in the rightmost quarter of the
5270                            editor canvas
5271                         */
5272 #if 0
5273                         // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
5274                         double target = ((double)frame - (double)current_page_frames()/2.0) / frames_per_unit;
5275                         if (target <= 0.0) {
5276                                 target = 0.0;
5277                         }
5278                         if (fabs(target - current) < current_page_frames() / frames_per_unit) {
5279                                 target = (target * 0.15) + (current * 0.85);
5280                         } else {
5281                                 /* relax */
5282                         }
5283
5284                         current = target;
5285                         set_horizontal_position (current);
5286 #endif
5287                 }
5288
5289         }
5290 }
5291
5292
5293 void
5294 Editor::session_going_away ()
5295 {
5296         _have_idled = false;
5297
5298         _session_connections.drop_connections ();
5299
5300         super_rapid_screen_update_connection.disconnect ();
5301
5302         selection->clear ();
5303         cut_buffer->clear ();
5304
5305         clicked_regionview = 0;
5306         clicked_axisview = 0;
5307         clicked_routeview = 0;
5308         clicked_crossfadeview = 0;
5309         entered_regionview = 0;
5310         entered_track = 0;
5311         last_update_frame = 0;
5312         _drags->abort ();
5313
5314         playhead_cursor->canvas_item.hide ();
5315
5316         /* rip everything out of the list displays */
5317
5318         _regions->clear ();
5319         _routes->clear ();
5320         _route_groups->clear ();
5321
5322         /* do this first so that deleting a track doesn't reset cms to null
5323            and thus cause a leak.
5324         */
5325
5326         if (current_mixer_strip) {
5327                 if (current_mixer_strip->get_parent() != 0) {
5328                         global_hpacker.remove (*current_mixer_strip);
5329                 }
5330                 delete current_mixer_strip;
5331                 current_mixer_strip = 0;
5332         }
5333
5334         /* delete all trackviews */
5335
5336         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
5337                 delete *i;
5338         }
5339         track_views.clear ();
5340
5341         zoom_range_clock->set_session (0);
5342         nudge_clock->set_session (0);
5343
5344         editor_list_button.set_active(false);
5345         editor_list_button.set_sensitive(false);
5346
5347         /* clear tempo/meter rulers */
5348         remove_metric_marks ();
5349         hide_measures ();
5350         clear_marker_display ();
5351
5352         delete current_bbt_points;
5353         current_bbt_points = 0;
5354
5355         /* get rid of any existing editor mixer strip */
5356
5357         WindowTitle title(Glib::get_application_name());
5358         title += _("Editor");
5359
5360         set_title (title.get_string());
5361
5362         SessionHandlePtr::session_going_away ();
5363 }
5364
5365
5366 void
5367 Editor::show_editor_list (bool yn)
5368 {
5369         if (yn) {
5370                 _the_notebook.show ();
5371         } else {
5372                 _the_notebook.hide ();
5373         }
5374 }
5375
5376 void
5377 Editor::change_region_layering_order ()
5378 {
5379         framepos_t const position = get_preferred_edit_position ();
5380
5381         if (!clicked_routeview) {
5382                 if (layering_order_editor) {
5383                         layering_order_editor->hide ();
5384                 }
5385                 return;
5386         }
5387
5388         boost::shared_ptr<Track> track = boost::dynamic_pointer_cast<Track> (clicked_routeview->route());
5389
5390         if (!track) {
5391                 return;
5392         }
5393
5394         boost::shared_ptr<Playlist> pl = track->playlist();
5395
5396         if (!pl) {
5397                 return;
5398         }
5399
5400         if (layering_order_editor == 0) {
5401                 layering_order_editor = new RegionLayeringOrderEditor(*this);
5402         }
5403
5404         layering_order_editor->set_context (clicked_routeview->name(), _session, pl, position);
5405         layering_order_editor->maybe_present ();
5406 }
5407
5408 void
5409 Editor::update_region_layering_order_editor ()
5410 {
5411         if (layering_order_editor && layering_order_editor->is_visible ()) {
5412                 change_region_layering_order ();
5413         }
5414 }
5415
5416 void
5417 Editor::setup_fade_images ()
5418 {
5419         _fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-in-linear")));
5420         _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-in-short-cut")));
5421         _fade_in_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-in-slow-cut")));
5422         _fade_in_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-in-fast-cut")));
5423         _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-in-long-cut")));
5424
5425         _fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("crossfade-out-linear")));
5426         _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("crossfade-out-short-cut")));
5427         _fade_out_images[FadeLogB] = new Gtk::Image (get_icon_path (X_("crossfade-out-slow-cut")));
5428         _fade_out_images[FadeLogA] = new Gtk::Image (get_icon_path (X_("crossfade-out-fast-cut")));
5429         _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("crossfade-out-long-cut")));
5430 }
5431
5432
5433 /** @return Gtk::manage()d menu item for a given action from `editor_actions' */
5434 Gtk::MenuItem&
5435 Editor::action_menu_item (std::string const & name)
5436 {
5437         Glib::RefPtr<Action> a = editor_actions->get_action (name);
5438         assert (a);
5439
5440         return *manage (a->create_menu_item ());
5441 }
5442
5443 void
5444 Editor::resize_text_widgets ()
5445 {
5446         set_size_request_to_display_given_text (edit_mode_selector, edit_mode_strings, COMBO_FUDGE+10, 15);
5447         set_size_request_to_display_given_text (zoom_focus_selector, zoom_focus_strings, COMBO_FUDGE+10, 15);
5448         set_size_request_to_display_given_text (snap_type_selector, snap_type_strings, COMBO_FUDGE+10, 15);
5449         set_size_request_to_display_given_text (snap_mode_selector, snap_mode_strings, COMBO_FUDGE+10, 15);
5450         set_size_request_to_display_given_text (edit_point_selector, edit_point_strings, COMBO_FUDGE+10, 15);
5451 }
5452
5453 void
5454 Editor::add_notebook_page (string const & name, Gtk::Widget& widget)
5455 {
5456         EventBox* b = manage (new EventBox);
5457         b->signal_button_press_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::notebook_tab_clicked), &widget));
5458         Label* l = manage (new Label (name));
5459         l->set_angle (-90);
5460         b->add (*l);
5461         b->show_all ();
5462         _the_notebook.append_page (widget, *b);
5463 }
5464
5465 bool
5466 Editor::notebook_tab_clicked (GdkEventButton* ev, Gtk::Widget* page)
5467 {
5468         if (ev->type == GDK_BUTTON_PRESS || ev->type == GDK_2BUTTON_PRESS) {
5469                 _the_notebook.set_current_page (_the_notebook.page_num (*page));
5470         }
5471
5472         if (ev->type == GDK_2BUTTON_PRESS) {
5473
5474                 /* double-click on a notebook tab shrinks or expands the notebook */
5475
5476                 if (_notebook_shrunk) {
5477                         edit_pane.set_position (pre_maximal_horizontal_pane_position);
5478                         _notebook_shrunk = false;
5479                 } else {
5480                         pre_maximal_horizontal_pane_position = edit_pane.get_position ();
5481                         edit_pane.set_position (edit_pane.get_position() + page->get_width());
5482                         _notebook_shrunk = true;
5483                 }
5484         }
5485
5486         return true;
5487 }
5488
5489 void
5490 Editor::popup_control_point_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
5491 {
5492         using namespace Menu_Helpers;
5493         
5494         MenuList& items = _control_point_context_menu.items ();
5495         items.clear ();
5496         
5497         items.push_back (MenuElem (_("Edit..."), sigc::bind (sigc::mem_fun (*this, &Editor::edit_control_point), item)));
5498         items.push_back (MenuElem (_("Delete"), sigc::bind (sigc::mem_fun (*this, &Editor::remove_control_point), item)));
5499         if (!can_remove_control_point (item)) {
5500                 items.back().set_sensitive (false);
5501         }
5502
5503         _control_point_context_menu.popup (event->button.button, event->button.time);
5504 }