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