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