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