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