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