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