Per-region MIDI CC "automation".
[ardour.git] / gtk2_ardour / editor.cc
1 /*
2     Copyright (C) 2000-2007 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 <unistd.h>
23 #include <cstdlib>
24 #include <cmath>
25 #include <string>
26 #include <algorithm>
27
28 #include <sigc++/bind.h>
29
30 #include <pbd/convert.h>
31 #include <pbd/error.h>
32 #include <pbd/enumwriter.h>
33 #include <pbd/memento_command.h>
34
35 #include <glibmm/miscutils.h>
36 #include <gtkmm/image.h>
37 #include <gdkmm/color.h>
38 #include <gdkmm/bitmap.h>
39
40 #include <gtkmm2ext/grouped_buttons.h>
41 #include <gtkmm2ext/gtk_ui.h>
42 #include <gtkmm2ext/tearoff.h>
43 #include <gtkmm2ext/utils.h>
44 #include <gtkmm2ext/window_title.h>
45 #include <gtkmm2ext/choice.h>
46
47 #include <ardour/audio_track.h>
48 #include <ardour/audio_diskstream.h>
49 #include <ardour/plugin_manager.h>
50 #include <ardour/location.h>
51 #include <ardour/audioplaylist.h>
52 #include <ardour/audioregion.h>
53 #include <ardour/midi_region.h>
54 #include <ardour/session_route.h>
55 #include <ardour/tempo.h>
56 #include <ardour/utils.h>
57
58 #include <control_protocol/control_protocol.h>
59
60 #include "ardour_ui.h"
61 #include "editor.h"
62 #include "keyboard.h"
63 #include "marker.h"
64 #include "playlist_selector.h"
65 #include "audio_region_view.h"
66 #include "rgb_macros.h"
67 #include "selection.h"
68 #include "audio_streamview.h"
69 #include "time_axis_view.h"
70 #include "audio_time_axis.h"
71 #include "utils.h"
72 #include "crossfade_view.h"
73 #include "editing.h"
74 #include "public_editor.h"
75 #include "crossfade_edit.h"
76 #include "canvas_impl.h"
77 #include "actions.h"
78 #include "gui_thread.h"
79
80 #ifdef FFT_ANALYSIS
81 #include "analysis_window.h"
82 #endif
83
84 #include "i18n.h"
85
86 #ifdef WITH_CMT
87 #include "imageframe_socket_handler.h"
88 #endif
89
90 using namespace std;
91 using namespace sigc;
92 using namespace ARDOUR;
93 using namespace PBD;
94 using namespace Gtk;
95 using namespace Glib;
96 using namespace Gtkmm2ext;
97 using namespace Editing;
98
99 using PBD::internationalize;
100 using PBD::atoi;
101
102 const double Editor::timebar_height = 15.0;
103
104 #include "editor_xpms"
105
106 static const gchar *_snap_type_strings[] = {
107         N_("None"),
108         N_("CD Frames"),
109         N_("SMPTE Frames"),
110         N_("SMPTE Seconds"),
111         N_("SMPTE Minutes"),
112         N_("Seconds"),
113         N_("Minutes"),
114         N_("Beats/32"),
115         N_("Beats/16"),
116         N_("Beats/8"),
117         N_("Beats/4"),
118         N_("Beats/3"),
119         N_("Beats"),
120         N_("Bars"),
121         N_("Marks"),
122         N_("Edit Cursor"),
123         N_("Region starts"),
124         N_("Region ends"),
125         N_("Region syncs"),
126         N_("Region bounds"),
127         0
128 };
129
130 static const gchar *_snap_mode_strings[] = {
131         N_("Normal"),
132         N_("Magnetic"),
133         0
134 };
135
136 static const gchar *_zoom_focus_strings[] = {
137         N_("Left"),
138         N_("Right"),
139         N_("Center"),
140         N_("Play"),
141         N_("Edit"),
142         0
143 };
144
145 /* Soundfile  drag-n-drop */
146
147 Gdk::Cursor* Editor::cross_hair_cursor = 0;
148 Gdk::Cursor* Editor::selector_cursor = 0;
149 Gdk::Cursor* Editor::trimmer_cursor = 0;
150 Gdk::Cursor* Editor::grabber_cursor = 0;
151 Gdk::Cursor* Editor::zoom_cursor = 0;
152 Gdk::Cursor* Editor::time_fx_cursor = 0;
153 Gdk::Cursor* Editor::fader_cursor = 0;
154 Gdk::Cursor* Editor::speaker_cursor = 0;
155 Gdk::Cursor* Editor::midi_select_cursor = 0;
156 Gdk::Cursor* Editor::midi_pencil_cursor = 0;
157 Gdk::Cursor* Editor::midi_erase_cursor = 0;
158 Gdk::Cursor* Editor::wait_cursor = 0;
159 Gdk::Cursor* Editor::timebar_cursor = 0;
160
161 void
162 show_me_the_size (Requisition* r, const char* what)
163 {
164         cerr << "size of " << what << " = " << r->width << " x " << r->height << endl;
165 }
166
167 void 
168 check_adjustment (Gtk::Adjustment* adj)
169 {
170         cerr << "CHANGE adj  = " 
171              << adj->get_lower () <<  ' '
172              << adj->get_upper () <<  ' '
173              << adj->get_value () <<  ' '
174              << adj->get_step_increment () <<  ' '
175              << adj->get_page_increment () <<  ' '
176              << adj->get_page_size () <<  ' '
177              << endl;
178
179 }
180
181 Editor::Editor ()
182         : 
183           /* time display buttons */
184
185           minsec_label (_("Mins:Secs")),
186           bbt_label (_("Bars:Beats")),
187           smpte_label (_("Timecode")),
188           frame_label (_("Frames")),
189           tempo_label (_("Tempo")),
190           meter_label (_("Meter")),
191           mark_label (_("Location Markers")),
192           range_mark_label (_("Range Markers")),
193           transport_mark_label (_("Loop/Punch Ranges")),
194
195           edit_packer (3, 3, false),
196
197           /* the values here don't matter: layout widgets
198              reset them as needed.
199           */
200
201           vertical_adjustment (0.0, 0.0, 10.0, 400.0),
202           horizontal_adjustment (0.0, 0.0, 20.0, 1200.0),
203
204           tempo_lines(0),
205           marker_tempo_lines(0),
206
207           /* tool bar related */
208
209           edit_cursor_clock (X_("editcursor"), false, X_("EditCursorClock"), true),
210           zoom_range_clock (X_("zoomrange"), false, X_("ZoomRangeClock"), true, true),
211           
212           toolbar_selection_clock_table (2,3),
213           
214           automation_mode_button (_("mode")),
215           global_automation_button (_("automation")),
216
217 #ifdef WITH_CMT
218           image_socket_listener(0),
219 #endif
220
221           /* nudge */
222
223           nudge_clock (X_("nudge"), false, X_("NudgeClock"), true, true)
224
225 {
226         constructed = false;
227
228         /* we are a singleton */
229
230         PublicEditor::_instance = this;
231
232         session = 0;
233
234         selection = new Selection;
235         cut_buffer = new Selection;
236
237         selection->TimeChanged.connect (mem_fun(*this, &Editor::time_selection_changed));
238         selection->TracksChanged.connect (mem_fun(*this, &Editor::track_selection_changed));
239         selection->RegionsChanged.connect (mem_fun(*this, &Editor::region_selection_changed));
240         selection->PointsChanged.connect (mem_fun(*this, &Editor::point_selection_changed));
241
242         clicked_regionview = 0;
243         clicked_axisview = 0;
244         clicked_routeview = 0;
245         clicked_crossfadeview = 0;
246         clicked_control_point = 0;
247         latest_regionview = 0;
248         last_update_frame = 0;
249         drag_info.item = 0;
250         current_mixer_strip = 0;
251         current_bbt_points = 0;
252
253         snap_type_strings = I18N (_snap_type_strings);
254         snap_mode_strings = I18N (_snap_mode_strings);
255         zoom_focus_strings = I18N(_zoom_focus_strings);
256
257         snap_type = SnapToFrame;
258         set_snap_to (snap_type);
259         snap_mode = SnapNormal;
260         set_snap_mode (snap_mode);
261         snap_threshold = 5.0;
262         bbt_beat_subdivision = 4;
263         canvas_width = 0;
264         canvas_height = 0;
265         autoscroll_active = false;
266         autoscroll_timeout_tag = -1;
267         interthread_progress_window = 0;
268
269 #ifdef FFT_ANALYSIS
270         analysis_window = 0;
271 #endif
272
273         current_interthread_info = 0;
274         _show_measures = true;
275         _show_waveforms = true;
276         _show_waveforms_recording = true;
277         first_action_message = 0;
278         export_dialog = 0;
279         show_gain_after_trim = false;
280         ignore_route_list_reorder = false;
281         no_route_list_redisplay = false;
282         verbose_cursor_on = true;
283         route_removal = false;
284         show_automatic_regions_in_region_list = true;
285         region_list_sort_type = (Editing::RegionListSortType) 0; 
286         have_pending_keyboard_selection = false;
287         _follow_playhead = true;
288         _xfade_visibility = true;
289         editor_ruler_menu = 0;
290         no_ruler_shown_update = false;
291         edit_group_list_menu = 0;
292         route_list_menu = 0;
293         region_list_menu = 0;
294         marker_menu = 0;
295         start_end_marker_menu = 0;
296         range_marker_menu = 0;
297         marker_menu_item = 0;
298         tm_marker_menu = 0;
299         transport_marker_menu = 0;
300         new_transport_marker_menu = 0;
301         editor_mixer_strip_width = Wide;
302         show_editor_mixer_when_tracks_arrive = false;
303         temp_location = 0;
304         leftmost_frame = 0;
305         ignore_mouse_mode_toggle = false;
306         ignore_midi_edit_mode_toggle = false;
307         current_stepping_trackview = 0;
308         entered_track = 0;
309         entered_regionview = 0;
310         clear_entered_track = false;
311         _new_regionviews_show_envelope = false;
312         current_timestretch = 0;
313         in_edit_group_row_change = false;
314         last_canvas_frame = 0;
315         edit_cursor = 0;
316         playhead_cursor = 0;
317         button_release_can_deselect = true;
318         canvas_idle_queued = false;
319         _dragging_playhead = false;
320         _dragging_hscrollbar = false;
321
322         location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get();
323         location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get();
324         location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get();
325         location_loop_color = ARDOUR_UI::config()->canvasvar_LocationLoop.get();
326         location_punch_color = ARDOUR_UI::config()->canvasvar_LocationPunch.get();
327
328         range_marker_drag_rect = 0;
329         marker_drag_line = 0;
330         
331         set_midi_edit_mode (MidiEditSelect, true);
332         set_mouse_mode (MouseObject, true);
333
334         frames_per_unit = 2048; /* too early to use reset_zoom () */
335         reset_hscrollbar_stepping ();
336         
337         zoom_focus = ZoomFocusLeft;
338         set_zoom_focus (ZoomFocusLeft);
339         zoom_range_clock.ValueChanged.connect (mem_fun(*this, &Editor::zoom_adjustment_changed));
340
341         initialize_rulers ();
342         initialize_canvas ();
343
344         edit_controls_vbox.set_spacing (0);
345         horizontal_adjustment.signal_value_changed().connect (mem_fun(*this, &Editor::canvas_horizontally_scrolled));
346         vertical_adjustment.signal_value_changed().connect (mem_fun(*this, &Editor::tie_vertical_scrolling));
347         
348         track_canvas.set_hadjustment (horizontal_adjustment);
349         track_canvas.set_vadjustment (vertical_adjustment);
350         time_canvas.set_hadjustment (horizontal_adjustment);
351
352         track_canvas.signal_map_event().connect (mem_fun (*this, &Editor::track_canvas_map_handler));
353         time_canvas.signal_map_event().connect (mem_fun (*this, &Editor::time_canvas_map_handler));
354         
355         controls_layout.add (edit_controls_vbox);
356         controls_layout.set_name ("EditControlsBase");
357         controls_layout.add_events (Gdk::SCROLL_MASK);
358         controls_layout.signal_scroll_event().connect (mem_fun(*this, &Editor::control_layout_scroll), false);
359         
360         controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
361         controls_layout.signal_button_release_event().connect (mem_fun(*this, &Editor::edit_controls_button_release));
362         controls_layout.signal_size_request().connect (mem_fun (*this, &Editor::controls_layout_size_request));
363
364         edit_vscrollbar.set_adjustment (vertical_adjustment);
365         edit_hscrollbar.set_adjustment (horizontal_adjustment);
366
367         edit_hscrollbar.signal_button_press_event().connect (mem_fun(*this, &Editor::hscrollbar_button_press), false);
368         edit_hscrollbar.signal_button_release_event().connect (mem_fun(*this, &Editor::hscrollbar_button_release), false);
369         edit_hscrollbar.signal_size_allocate().connect (mem_fun(*this, &Editor::hscrollbar_allocate));
370
371         edit_hscrollbar.set_name ("EditorHScrollbar");
372
373         build_cursors ();
374         setup_toolbar ();
375         setup_midi_toolbar ();
376
377         edit_cursor_clock.ValueChanged.connect (mem_fun(*this, &Editor::edit_cursor_clock_changed));
378         
379         ArdourCanvas::Canvas* time_pad = manage(new ArdourCanvas::Canvas());
380         ArdourCanvas::SimpleLine* pad_line_1 = manage(new ArdourCanvas::SimpleLine(*time_pad->root(),
381                         0.0, 1.0, 100.0, 1.0));
382         pad_line_1->property_color_rgba() = 0xFF0000FF;
383         pad_line_1->show();
384         time_pad->show();
385
386         time_canvas_vbox.pack_start (*_ruler_separator, false, false);
387         time_canvas_vbox.pack_start (*minsec_ruler, false, false);
388         time_canvas_vbox.pack_start (*smpte_ruler, false, false);
389         time_canvas_vbox.pack_start (*frames_ruler, false, false);
390         time_canvas_vbox.pack_start (*bbt_ruler, false, false);
391         time_canvas_vbox.pack_start (time_canvas, true, true);
392         time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 5);
393
394         bbt_label.set_name ("EditorTimeButton");
395         bbt_label.set_size_request (-1, (int)timebar_height);
396         bbt_label.set_alignment (1.0, 0.5);
397         bbt_label.set_padding (5,0);
398         minsec_label.set_name ("EditorTimeButton");
399         minsec_label.set_size_request (-1, (int)timebar_height);
400         minsec_label.set_alignment (1.0, 0.5);
401         minsec_label.set_padding (5,0);
402         smpte_label.set_name ("EditorTimeButton");
403         smpte_label.set_size_request (-1, (int)timebar_height);
404         smpte_label.set_alignment (1.0, 0.5);
405         smpte_label.set_padding (5,0);
406         frame_label.set_name ("EditorTimeButton");
407         frame_label.set_size_request (-1, (int)timebar_height);
408         frame_label.set_alignment (1.0, 0.5);
409         frame_label.set_padding (5,0);
410         tempo_label.set_name ("EditorTimeButton");
411         tempo_label.set_size_request (-1, (int)timebar_height);
412         tempo_label.set_alignment (1.0, 0.5);
413         tempo_label.set_padding (5,0);
414         meter_label.set_name ("EditorTimeButton");
415         meter_label.set_size_request (-1, (int)timebar_height);
416         meter_label.set_alignment (1.0, 0.5);
417         meter_label.set_padding (5,0);
418         mark_label.set_name ("EditorTimeButton");
419         mark_label.set_size_request (-1, (int)timebar_height);
420         mark_label.set_alignment (1.0, 0.5);
421         mark_label.set_padding (5,0);
422         range_mark_label.set_name ("EditorTimeButton");
423         range_mark_label.set_size_request (-1, (int)timebar_height);
424         range_mark_label.set_alignment (1.0, 0.5);
425         range_mark_label.set_padding (5,0);
426         transport_mark_label.set_name ("EditorTimeButton");
427         transport_mark_label.set_size_request (-1, (int)timebar_height);
428         transport_mark_label.set_alignment (1.0, 0.5);
429         transport_mark_label.set_padding (5,0);
430         
431         time_button_vbox.pack_start (minsec_label, false, false);
432         time_button_vbox.pack_start (smpte_label, false, false);
433         time_button_vbox.pack_start (frame_label, false, false);
434         time_button_vbox.pack_start (bbt_label, false, false);
435         time_button_vbox.pack_start (meter_label, false, false);
436         time_button_vbox.pack_start (tempo_label, false, false);
437         time_button_vbox.pack_start (mark_label, false, false);
438
439         time_button_event_box.add (time_button_vbox);
440         time_button_event_box.set_name ("TimebarLabelBase");
441         time_button_frame.set_shadow_type (Gtk::SHADOW_NONE);
442         
443         time_button_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
444         time_button_event_box.signal_button_release_event().connect (mem_fun(*this, &Editor::ruler_label_button_release));
445
446         time_button_frame.add (time_button_event_box);
447         time_button_frame.set_name ("TimebarLabelBase");
448         time_button_frame.set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
449
450         /* these enable us to have a dedicated window (for cursor setting, etc.) 
451            for the canvas areas.
452         */
453
454         track_canvas_event_box.add (track_canvas);
455
456         time_canvas_event_box.add (time_canvas_vbox);
457         time_canvas_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
458         
459         edit_packer.set_col_spacings (0);
460         edit_packer.set_row_spacings (0);
461         edit_packer.set_homogeneous (false);
462         edit_packer.set_border_width (0);
463         edit_packer.set_name ("EditorWindow");
464         
465         edit_packer.attach (edit_vscrollbar,         3, 4, 1, 2,    FILL,        FILL|EXPAND, 0, 0);
466
467         edit_packer.attach (time_button_frame,       0, 2, 0, 1,    FILL,        SHRINK, 0, 0);
468         edit_packer.attach (time_canvas_event_box,   2, 4, 0, 1,    FILL|EXPAND, FILL, 0, 0);
469
470         edit_packer.attach (controls_layout,         1, 2, 1, 2,    FILL,        FILL|EXPAND, 0, 0);
471         edit_packer.attach (track_canvas_event_box,  2, 3, 1, 2,    FILL|EXPAND, FILL|EXPAND, 0, 0);
472
473         edit_packer.attach (zoom_box,                1, 2, 2, 3,    FILL,         FILL, 0, 0);
474         edit_packer.attach (edit_hscrollbar,         2, 3, 2, 3,    FILL|EXPAND,  FILL, 0, 0);
475
476         bottom_hbox.set_border_width (2);
477         bottom_hbox.set_spacing (3);
478
479         route_display_model = ListStore::create(route_display_columns);
480         route_list_display.set_model (route_display_model);
481         route_list_display.append_column (_("Show"), route_display_columns.visible);
482         route_list_display.append_column (_("Name"), route_display_columns.text);
483         route_list_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
484         route_list_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
485         route_list_display.set_headers_visible (true);
486         route_list_display.set_name ("TrackListDisplay");
487         route_list_display.get_selection()->set_mode (SELECTION_NONE);
488         route_list_display.set_reorderable (true);
489         route_list_display.set_size_request (100,-1);
490
491         CellRendererToggle* route_list_visible_cell = dynamic_cast<CellRendererToggle*>(route_list_display.get_column_cell_renderer (0));
492         route_list_visible_cell->property_activatable() = true;
493         route_list_visible_cell->property_radio() = false;
494         
495         route_display_model->signal_row_deleted().connect (mem_fun (*this, &Editor::route_list_delete));
496         route_display_model->signal_row_changed().connect (mem_fun (*this, &Editor::route_list_change));
497
498         route_list_display.signal_button_press_event().connect (mem_fun (*this, &Editor::route_list_display_button_press), false);
499
500         route_list_scroller.add (route_list_display);
501         route_list_scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
502
503         group_model = ListStore::create(group_columns);
504         edit_group_display.set_model (group_model);
505         edit_group_display.append_column (_("Name"), group_columns.text);
506         edit_group_display.append_column (_("Active"), group_columns.is_active);
507         edit_group_display.append_column (_("Show"), group_columns.is_visible);
508         edit_group_display.get_column (0)->set_data (X_("colnum"), GUINT_TO_POINTER(0));
509         edit_group_display.get_column (1)->set_data (X_("colnum"), GUINT_TO_POINTER(1));
510         edit_group_display.get_column (2)->set_data (X_("colnum"), GUINT_TO_POINTER(2));
511         edit_group_display.get_column (0)->set_expand (true);
512         edit_group_display.get_column (1)->set_expand (false);
513         edit_group_display.get_column (2)->set_expand (false);
514         edit_group_display.set_headers_visible (true);
515
516         /* name is directly editable */
517
518         CellRendererText* name_cell = dynamic_cast<CellRendererText*>(edit_group_display.get_column_cell_renderer (0));
519         name_cell->property_editable() = true;
520         name_cell->signal_edited().connect (mem_fun (*this, &Editor::edit_group_name_edit));
521
522         /* use checkbox for the active + visible columns */
523
524         CellRendererToggle* active_cell = dynamic_cast<CellRendererToggle*>(edit_group_display.get_column_cell_renderer (1));
525         active_cell->property_activatable() = true;
526         active_cell->property_radio() = false;
527
528         active_cell = dynamic_cast<CellRendererToggle*>(edit_group_display.get_column_cell_renderer (1));
529         active_cell->property_activatable() = true;
530         active_cell->property_radio() = false;
531
532         group_model->signal_row_changed().connect (mem_fun (*this, &Editor::edit_group_row_change));
533
534         edit_group_display.set_name ("EditGroupList");
535         edit_group_display.get_selection()->set_mode (SELECTION_SINGLE);
536         edit_group_display.set_headers_visible (true);
537         edit_group_display.set_reorderable (false);
538         edit_group_display.set_rules_hint (true);
539         edit_group_display.set_size_request (75, -1);
540
541         edit_group_display_scroller.add (edit_group_display);
542         edit_group_display_scroller.set_policy (POLICY_AUTOMATIC, POLICY_AUTOMATIC);
543
544         edit_group_display.signal_button_press_event().connect (mem_fun(*this, &Editor::edit_group_list_button_press_event), false);
545
546         VBox* edit_group_display_packer = manage (new VBox());
547         HBox* edit_group_display_button_box = manage (new HBox());
548         edit_group_display_button_box->set_homogeneous (true);
549
550         Button* edit_group_add_button = manage (new Button ());
551         Button* edit_group_remove_button = manage (new Button ());
552
553         Widget* w;
554
555         w = manage (new Image (Stock::ADD, ICON_SIZE_BUTTON));
556         w->show();
557         edit_group_add_button->add (*w);
558
559         w = manage (new Image (Stock::REMOVE, ICON_SIZE_BUTTON));
560         w->show();
561         edit_group_remove_button->add (*w);
562
563         edit_group_add_button->signal_clicked().connect (mem_fun (*this, &Editor::new_edit_group));
564         edit_group_remove_button->signal_clicked().connect (mem_fun (*this, &Editor::remove_selected_edit_group));
565         
566         edit_group_display_button_box->pack_start (*edit_group_add_button);
567         edit_group_display_button_box->pack_start (*edit_group_remove_button);
568
569         edit_group_display_packer->pack_start (edit_group_display_scroller, true, true);
570         edit_group_display_packer->pack_start (*edit_group_display_button_box, false, false);
571
572         region_list_display.set_size_request (100, -1);
573         region_list_display.set_name ("RegionListDisplay");
574
575         region_list_model = TreeStore::create (region_list_columns);
576         region_list_model->set_sort_func (0, mem_fun (*this, &Editor::region_list_sorter));
577         region_list_model->set_sort_column (0, SORT_ASCENDING);
578
579         region_list_display.set_model (region_list_model);
580         region_list_display.append_column (_("Regions"), region_list_columns.name);
581         region_list_display.set_headers_visible (false);
582
583         region_list_display.get_selection()->set_select_function (mem_fun (*this, &Editor::region_list_selection_filter));
584         
585         TreeViewColumn* tv_col = region_list_display.get_column(0);
586         CellRendererText* renderer = dynamic_cast<CellRendererText*>(region_list_display.get_column_cell_renderer (0));
587         tv_col->add_attribute(renderer->property_text(), region_list_columns.name);
588         tv_col->add_attribute(renderer->property_foreground_gdk(), region_list_columns.color_);
589         
590         region_list_display.get_selection()->set_mode (SELECTION_MULTIPLE);
591         region_list_display.add_object_drag (region_list_columns.region.index(), "regions");
592
593         /* setup DnD handling */
594         
595         list<TargetEntry> region_list_target_table;
596         
597         region_list_target_table.push_back (TargetEntry ("text/plain"));
598         region_list_target_table.push_back (TargetEntry ("text/uri-list"));
599         region_list_target_table.push_back (TargetEntry ("application/x-rootwin-drop"));
600         
601         region_list_display.add_drop_targets (region_list_target_table);
602         region_list_display.signal_drag_data_received().connect (mem_fun(*this, &Editor::region_list_display_drag_data_received));
603
604         region_list_scroller.add (region_list_display);
605         region_list_scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
606
607         region_list_display.signal_key_press_event().connect (mem_fun(*this, &Editor::region_list_display_key_press));
608         region_list_display.signal_key_release_event().connect (mem_fun(*this, &Editor::region_list_display_key_release));
609         region_list_display.signal_button_press_event().connect (mem_fun(*this, &Editor::region_list_display_button_press), false);
610         region_list_display.signal_button_release_event().connect (mem_fun(*this, &Editor::region_list_display_button_release));
611         region_list_display.get_selection()->signal_changed().connect (mem_fun(*this, &Editor::region_list_selection_changed));
612         // region_list_display.signal_popup_menu().connect (bind (mem_fun (*this, &Editor::show_region_list_display_context_menu), 1, 0));
613         
614         named_selection_scroller.add (named_selection_display);
615         named_selection_scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC);
616
617         named_selection_model = TreeStore::create (named_selection_columns);
618         named_selection_display.set_model (named_selection_model);
619         named_selection_display.append_column (_("Chunks"), named_selection_columns.text);
620         named_selection_display.set_headers_visible (false);
621         named_selection_display.set_size_request (100, -1);
622         named_selection_display.set_name ("NamedSelectionDisplay");
623         
624         named_selection_display.get_selection()->set_mode (SELECTION_SINGLE);
625         named_selection_display.set_size_request (100, -1);
626         named_selection_display.signal_button_release_event().connect (mem_fun(*this, &Editor::named_selection_display_button_release), false);
627         named_selection_display.signal_key_release_event().connect (mem_fun(*this, &Editor::named_selection_display_key_release), false);
628         named_selection_display.get_selection()->signal_changed().connect (mem_fun (*this, &Editor::named_selection_display_selection_changed));
629
630         /* SNAPSHOTS */
631
632         snapshot_display_model = ListStore::create (snapshot_display_columns);
633         snapshot_display.set_model (snapshot_display_model);
634         snapshot_display.append_column (X_("snapshot"), snapshot_display_columns.visible_name);
635         snapshot_display.set_name ("SnapshotDisplay");
636         snapshot_display.set_size_request (75, -1);
637         snapshot_display.set_headers_visible (false);
638         snapshot_display.set_reorderable (false);
639         snapshot_display_scroller.add (snapshot_display);
640         snapshot_display_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
641
642         snapshot_display.get_selection()->signal_changed().connect (mem_fun(*this, &Editor::snapshot_display_selection_changed));
643         snapshot_display.signal_button_press_event().connect (mem_fun (*this, &Editor::snapshot_display_button_press), false);
644
645         Gtk::Label* nlabel;
646
647         nlabel = manage (new Label (_("Regions")));
648         nlabel->set_angle (-90);
649         the_notebook.append_page (region_list_scroller, *nlabel);
650         nlabel = manage (new Label (_("Tracks/Busses")));
651         nlabel->set_angle (-90);
652         the_notebook.append_page (route_list_scroller, *nlabel);
653         nlabel = manage (new Label (_("Snapshots")));
654         nlabel->set_angle (-90);
655         the_notebook.append_page (snapshot_display_scroller, *nlabel);
656         nlabel = manage (new Label (_("Edit Groups")));
657         nlabel->set_angle (-90);
658         the_notebook.append_page (*edit_group_display_packer, *nlabel);
659         nlabel = manage (new Label (_("Chunks")));
660         nlabel->set_angle (-90);
661         the_notebook.append_page (named_selection_scroller, *nlabel);
662
663         the_notebook.set_show_tabs (true);
664         the_notebook.set_scrollable (true);
665         the_notebook.popup_enable ();
666         the_notebook.set_tab_pos (Gtk::POS_RIGHT);
667
668         post_maximal_editor_width = 0;
669         post_maximal_pane_position = 0;
670         edit_pane.pack1 (edit_packer, true, true);
671         edit_pane.pack2 (the_notebook, false, true);
672         
673         edit_pane.signal_size_allocate().connect (bind (mem_fun(*this, &Editor::pane_allocation_handler), static_cast<Paned*> (&edit_pane)));
674
675         top_hbox.pack_start (toolbar_frame, false, true);
676         top_hbox.pack_start (midi_toolbar_frame, false, true);
677
678         HBox *hbox = manage (new HBox);
679         hbox->pack_start (edit_pane, true, true);
680
681         global_vpacker.pack_start (top_hbox, false, false);
682         global_vpacker.pack_start (*hbox, true, true);
683
684         global_hpacker.pack_start (global_vpacker, true, true);
685
686         set_name ("EditorWindow");
687         add_accel_group (ActionManager::ui_manager->get_accel_group());
688
689         vpacker.pack_end (global_hpacker, true, true);
690
691         /* register actions now so that set_state() can find them and set toggles/checks etc */
692         
693         register_actions ();
694         
695         XMLNode* node = ARDOUR_UI::instance()->editor_settings();
696         set_state (*node);
697
698         _playlist_selector = new PlaylistSelector();
699         _playlist_selector->signal_delete_event().connect (bind (sigc::ptr_fun (just_hide_it), static_cast<Window *> (_playlist_selector)));
700
701         RegionView::RegionViewGoingAway.connect (mem_fun(*this, &Editor::catch_vanishing_regionview));
702
703         /* nudge stuff */
704
705         nudge_forward_button.add (*(manage (new Image (::get_icon("nudge_right")))));
706         nudge_backward_button.add (*(manage (new Image (::get_icon("nudge_left")))));
707
708         ARDOUR_UI::instance()->tooltips().set_tip (nudge_forward_button, _("Nudge Region/Selection Forwards"));
709         ARDOUR_UI::instance()->tooltips().set_tip (nudge_backward_button, _("Nudge Region/Selection Backwards"));
710
711         nudge_forward_button.set_name ("TransportButton");
712         nudge_backward_button.set_name ("TransportButton");
713
714         fade_context_menu.set_name ("ArdourContextMenu");
715
716         /* icons, titles, WM stuff */
717
718         list<Glib::RefPtr<Gdk::Pixbuf> > window_icons;
719         Glib::RefPtr<Gdk::Pixbuf> icon;
720
721         if ((icon = ::get_icon ("ardour_icon_16px")) != 0) {
722                 window_icons.push_back (icon);
723         }
724         if ((icon = ::get_icon ("ardour_icon_22px")) != 0) {
725                 window_icons.push_back (icon);
726         }
727         if ((icon = ::get_icon ("ardour_icon_32px")) != 0) {
728                 window_icons.push_back (icon);
729         }
730         if ((icon = ::get_icon ("ardour_icon_48px")) != 0) {
731                 window_icons.push_back (icon);
732         }
733         if (!window_icons.empty()) {
734                 set_icon_list (window_icons);
735                 set_default_icon_list (window_icons);
736         }
737
738         WindowTitle title(Glib::get_application_name());
739         title += _("Editor");
740         set_title (title.get_string());
741         set_wmclass (X_("ardour_editor"), "Ardour");
742
743         add (vpacker);
744         add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
745
746         signal_configure_event().connect (mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::configure_handler));
747         signal_delete_event().connect (mem_fun (*ARDOUR_UI::instance(), &ARDOUR_UI::exit_on_main_window_close));
748
749         /* allow external control surfaces/protocols to do various things */
750
751         ControlProtocol::ZoomToSession.connect (mem_fun (*this, &Editor::temporal_zoom_session));
752         ControlProtocol::ZoomIn.connect (bind (mem_fun (*this, &Editor::temporal_zoom_step), false));
753         ControlProtocol::ZoomOut.connect (bind (mem_fun (*this, &Editor::temporal_zoom_step), true));
754         ControlProtocol::ScrollTimeline.connect (mem_fun (*this, &Editor::control_scroll));
755
756         Config->ParameterChanged.connect (mem_fun (*this, &Editor::parameter_changed));
757
758         constructed = true;
759         instant_save ();
760 }
761
762 Editor::~Editor()
763 {
764 #ifdef WITH_CMT
765         if(image_socket_listener)
766         {
767                 if(image_socket_listener->is_connected())
768                 {
769                         image_socket_listener->close_connection() ;
770                 }
771                 
772                 delete image_socket_listener ;
773                 image_socket_listener = 0 ;
774         }
775 #endif
776 }
777
778 void
779 Editor::add_toplevel_controls (Container& cont)
780 {
781         vpacker.pack_start (cont, false, false);
782         cont.show_all ();
783 }
784
785 void
786 Editor::catch_vanishing_regionview (RegionView *rv)
787 {
788         /* note: the selection will take care of the vanishing
789            audioregionview by itself.
790         */
791
792         if (clicked_regionview == rv) {
793                 clicked_regionview = 0;
794         }
795
796         if (entered_regionview == rv) {
797                 set_entered_regionview (0);
798         }
799 }
800
801 void
802 Editor::set_entered_regionview (RegionView* rv)
803 {
804         if (rv == entered_regionview) {
805                 return;
806         }
807
808         if (entered_regionview) {
809                 entered_regionview->exited ();
810         }
811
812         if ((entered_regionview = rv) != 0) {
813                 entered_regionview->entered ();
814         }
815 }
816
817 void
818 Editor::set_entered_track (TimeAxisView* tav)
819 {
820         if (entered_track) {
821                 entered_track->exited ();
822         }
823
824         if ((entered_track = tav) != 0) {
825                 entered_track->entered ();
826         }
827 }
828
829 void
830 Editor::show_window ()
831 {
832         show_all_children ();
833         
834         /* re-hide editor list if necessary */
835         editor_list_button_toggled ();
836
837         /* now reset all audio_time_axis heights, because widgets might need
838            to be re-hidden
839         */
840         
841         TimeAxisView *tv;
842         
843         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
844                 tv = (static_cast<TimeAxisView*>(*i));
845                 tv->reset_height ();
846         }
847
848         present ();
849 }
850
851 void
852 Editor::tie_vertical_scrolling ()
853 {
854         double y1 = vertical_adjustment.get_value();
855         controls_layout.get_vadjustment()->set_value (y1);
856         playhead_cursor->set_y_axis(y1);
857         edit_cursor->set_y_axis(y1);
858 }
859
860 void
861 Editor::instant_save ()
862 {
863         if (!constructed || !ARDOUR_UI::instance()->session_loaded) {
864                 return;
865         }
866
867         if (session) {
868                 session->add_instant_xml(get_state());
869         } else {
870                 Config->add_instant_xml(get_state());
871         }
872 }
873
874 void
875 Editor::edit_cursor_clock_changed()
876 {
877         if (edit_cursor->current_frame != edit_cursor_clock.current_time()) {
878                 edit_cursor->set_position (edit_cursor_clock.current_time());
879         }
880 }
881
882
883 void
884 Editor::zoom_adjustment_changed ()
885 {
886         if (session == 0) {
887                 return;
888         }
889
890         double fpu = zoom_range_clock.current_duration() / canvas_width;
891
892         if (fpu < 1.0) {
893                 fpu = 1.0;
894                 zoom_range_clock.set ((nframes_t) floor (fpu * canvas_width));
895         } else if (fpu > session->current_end_frame() / canvas_width) {
896                 fpu = session->current_end_frame() / canvas_width;
897                 zoom_range_clock.set ((nframes_t) floor (fpu * canvas_width));
898         }
899         
900         temporal_zoom (fpu);
901 }
902
903 void
904 Editor::control_scroll (float fraction)
905 {
906         ENSURE_GUI_THREAD(bind (mem_fun (*this, &Editor::control_scroll), fraction));
907
908         if (!session) {
909                 return;
910         }
911
912         double step = fraction * current_page_frames();
913         nframes_t target;
914
915         if ((fraction < 0.0f) && (session->transport_frame() < (nframes_t) fabs(step))) {
916                 target = 0;
917         } else if ((fraction > 0.0f) && (max_frames - session->transport_frame() < step)) {
918                 target = (max_frames - (current_page_frames()*2)); // allow room for slop in where the PH is on the screen
919         } else {
920                 target = (session->transport_frame() + (nframes_t) floor ((fraction * current_page_frames())));
921         }
922
923         /* move visuals, we'll catch up with it later */
924
925         playhead_cursor->set_position (target);
926
927         if (target > (current_page_frames() / 2)) {
928                 /* try to center PH in window */
929                 reset_x_origin (target - (current_page_frames()/2));
930         } else {
931                 reset_x_origin (0);
932         }
933
934         /* cancel the existing */
935
936         control_scroll_connection.disconnect ();
937
938         /* add the next one */
939
940         control_scroll_connection = Glib::signal_timeout().connect (bind (mem_fun (*this, &Editor::deferred_control_scroll), target), 50);
941 }
942
943 bool
944 Editor::deferred_control_scroll (nframes_t target)
945 {
946         session->request_locate (target);
947         return false;
948 }
949
950 void
951 Editor::on_realize ()
952 {
953         Window::on_realize ();
954         Realized ();
955 }
956
957 void
958 Editor::start_scrolling ()
959 {
960         scroll_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect 
961                 (mem_fun(*this, &Editor::update_current_screen));
962 }
963
964 void
965 Editor::stop_scrolling ()
966 {
967         scroll_connection.disconnect ();
968 }
969
970 void
971 Editor::map_position_change (nframes_t frame)
972 {
973         ENSURE_GUI_THREAD (bind (mem_fun(*this, &Editor::map_position_change), frame));
974
975         if (session == 0 || !_follow_playhead) {
976                 return;
977         }
978
979         center_screen (frame);
980         playhead_cursor->set_position (frame);
981 }       
982
983 void
984 Editor::center_screen (nframes_t frame)
985 {
986         double page = canvas_width * frames_per_unit;
987
988         /* if we're off the page, then scroll.
989          */
990         
991         if (frame < leftmost_frame || frame >= leftmost_frame + page) {
992                 center_screen_internal (frame, page);
993         }
994 }
995
996 void
997 Editor::center_screen_internal (nframes_t frame, float page)
998 {
999         page /= 2;
1000                 
1001         if (frame > page) {
1002                 frame -= (nframes_t) page;
1003         } else {
1004                 frame = 0;
1005         }
1006
1007         reset_x_origin (frame);
1008 }
1009
1010 void
1011 Editor::handle_new_duration ()
1012 {
1013         ENSURE_GUI_THREAD (mem_fun (*this, &Editor::handle_new_duration));
1014
1015         nframes_t new_end = session->get_maximum_extent() + (nframes_t) floorf (current_page_frames() * 0.10f);
1016                                   
1017         if (new_end > last_canvas_frame) {
1018                 last_canvas_frame = new_end;
1019                 horizontal_adjustment.set_upper (last_canvas_frame / frames_per_unit);
1020                 reset_scrolling_region ();
1021         }
1022
1023         horizontal_adjustment.set_value (leftmost_frame/frames_per_unit);
1024 }
1025
1026 void
1027 Editor::update_title_s (const string & snap_name)
1028 {
1029         ENSURE_GUI_THREAD(bind (mem_fun(*this, &Editor::update_title_s), snap_name));
1030         
1031         update_title ();
1032 }
1033
1034 void
1035 Editor::update_title ()
1036 {
1037         ENSURE_GUI_THREAD (mem_fun(*this, &Editor::update_title));
1038
1039         if (session) {
1040                 bool dirty = session->dirty();
1041
1042                 string session_name;
1043
1044                 if (session->snap_name() != session->name()) {
1045                         session_name = session->snap_name();
1046                 } else {
1047                         session_name = session->name();
1048                 }
1049
1050                 if (dirty) {
1051                         session_name = "*" + session_name;
1052                 }
1053
1054                 WindowTitle title(session_name);
1055                 title += Glib::get_application_name();
1056                 set_title (title.get_string());
1057         }
1058 }
1059
1060 void
1061 Editor::connect_to_session (Session *t)
1062 {
1063         session = t;
1064
1065         XMLNode* node = ARDOUR_UI::instance()->editor_settings();
1066         set_state (*node);
1067
1068         /* catch up with the playhead */
1069
1070         session->request_locate (playhead_cursor->current_frame);
1071
1072         if (first_action_message) {
1073                 first_action_message->hide();
1074         }
1075
1076         update_title ();
1077
1078         session->GoingAway.connect (mem_fun(*this, &Editor::session_going_away));
1079         session->history().Changed.connect (mem_fun (*this, &Editor::history_changed));
1080
1081         /* These signals can all be emitted by a non-GUI thread. Therefore the
1082            handlers for them must not attempt to directly interact with the GUI,
1083            but use Gtkmm2ext::UI::instance()->call_slot();
1084         */
1085
1086         session_connections.push_back (session->TransportStateChange.connect (mem_fun(*this, &Editor::map_transport_state)));
1087         session_connections.push_back (session->PositionChanged.connect (mem_fun(*this, &Editor::map_position_change)));
1088         session_connections.push_back (session->RouteAdded.connect (mem_fun(*this, &Editor::handle_new_route)));
1089         session_connections.push_back (session->RegionAdded.connect (mem_fun(*this, &Editor::handle_new_region)));
1090         session_connections.push_back (session->RegionRemoved.connect (mem_fun(*this, &Editor::handle_region_removed)));
1091         session_connections.push_back (session->DurationChanged.connect (mem_fun(*this, &Editor::handle_new_duration)));
1092         session_connections.push_back (session->edit_group_added.connect (mem_fun(*this, &Editor::add_edit_group)));
1093         session_connections.push_back (session->edit_group_removed.connect (mem_fun(*this, &Editor::edit_groups_changed)));
1094         session_connections.push_back (session->NamedSelectionAdded.connect (mem_fun(*this, &Editor::handle_new_named_selection)));
1095         session_connections.push_back (session->NamedSelectionRemoved.connect (mem_fun(*this, &Editor::handle_new_named_selection)));
1096         session_connections.push_back (session->DirtyChanged.connect (mem_fun(*this, &Editor::update_title)));
1097         session_connections.push_back (session->StateSaved.connect (mem_fun(*this, &Editor::update_title_s)));
1098         session_connections.push_back (session->AskAboutPlaylistDeletion.connect (mem_fun(*this, &Editor::playlist_deletion_dialog)));
1099         session_connections.push_back (session->RegionHiddenChange.connect (mem_fun(*this, &Editor::region_hidden)));
1100
1101         session_connections.push_back (session->SMPTEOffsetChanged.connect (mem_fun(*this, &Editor::update_just_smpte)));
1102
1103         session_connections.push_back (session->tempo_map().StateChanged.connect (mem_fun(*this, &Editor::tempo_map_changed)));
1104
1105         edit_groups_changed ();
1106
1107         edit_cursor_clock.set_session (session);
1108         zoom_range_clock.set_session (session);
1109         _playlist_selector->set_session (session);
1110         nudge_clock.set_session (session);
1111
1112 #ifdef FFT_ANALYSIS
1113         if (analysis_window != 0)
1114                 analysis_window->set_session (session);
1115 #endif
1116
1117         Location* loc = session->locations()->auto_loop_location();
1118         if (loc == 0) {
1119                 loc = new Location (0, session->current_end_frame(), _("Loop"),(Location::Flags) (Location::IsAutoLoop | Location::IsHidden));
1120                 if (loc->start() == loc->end()) {
1121                         loc->set_end (loc->start() + 1);
1122                 }
1123                 session->locations()->add (loc, false);
1124                 session->set_auto_loop_location (loc);
1125         } else {
1126                 // force name
1127                 loc->set_name (_("Loop"));
1128         }
1129         
1130         loc = session->locations()->auto_punch_location();
1131         if (loc == 0) {
1132                 loc = new Location (0, session->current_end_frame(), _("Punch"), (Location::Flags) (Location::IsAutoPunch | Location::IsHidden));
1133                 if (loc->start() == loc->end()) {
1134                         loc->set_end (loc->start() + 1);
1135                 }
1136                 session->locations()->add (loc, false);
1137                 session->set_auto_punch_location (loc);
1138         } else {
1139                 // force name
1140                 loc->set_name (_("Punch"));
1141         }
1142
1143         Config->map_parameters (mem_fun (*this, &Editor::parameter_changed));
1144         
1145         session->StateSaved.connect (mem_fun(*this, &Editor::session_state_saved));
1146         
1147         refresh_location_display ();
1148         session->locations()->added.connect (mem_fun(*this, &Editor::add_new_location));
1149         session->locations()->removed.connect (mem_fun(*this, &Editor::location_gone));
1150         session->locations()->changed.connect (mem_fun(*this, &Editor::refresh_location_display));
1151         session->locations()->StateChanged.connect (mem_fun(*this, &Editor::refresh_location_display_s));
1152         session->locations()->end_location()->changed.connect (mem_fun(*this, &Editor::end_location_changed));
1153
1154         handle_new_duration ();
1155
1156         redisplay_regions ();
1157         redisplay_named_selections ();
1158         redisplay_snapshots ();
1159
1160         initial_route_list_display ();
1161
1162         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
1163                 (static_cast<TimeAxisView*>(*i))->set_samples_per_unit (frames_per_unit);
1164         }
1165
1166         restore_ruler_visibility ();
1167         //tempo_map_changed (Change (0));
1168         session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks);
1169
1170         start_scrolling ();
1171
1172         /* don't show master bus in a new session */
1173
1174         if (ARDOUR_UI::instance()->session_is_new ()) {
1175
1176                 TreeModel::Children rows = route_display_model->children();
1177                 TreeModel::Children::iterator i;
1178         
1179                 no_route_list_redisplay = true;
1180                 
1181                 for (i = rows.begin(); i != rows.end(); ++i) {
1182                         TimeAxisView *tv =  (*i)[route_display_columns.tv];
1183                         RouteTimeAxisView *rtv;
1184                         
1185                         if ((rtv = dynamic_cast<RouteTimeAxisView*>(tv)) != 0) {
1186                                 if (rtv->route()->is_master()) {
1187                                         route_list_display.get_selection()->unselect (i);
1188                                 }
1189                         }
1190                 }
1191                 
1192                 no_route_list_redisplay = false;
1193                 redisplay_route_list ();
1194         }
1195
1196         /* register for undo history */
1197
1198         session->register_with_memento_command_factory(_id, this);
1199 }
1200
1201 void
1202 Editor::build_cursors ()
1203 {
1204         using namespace Gdk;
1205         
1206         Gdk::Color mbg ("#000000" ); /* Black */
1207         Gdk::Color mfg ("#0000ff" ); /* Blue. */
1208
1209         {
1210                 RefPtr<Bitmap> source, mask;
1211                 source = Bitmap::create (mag_bits, mag_width, mag_height);
1212                 mask = Bitmap::create (magmask_bits, mag_width, mag_height);
1213                 zoom_cursor = new Gdk::Cursor (source, mask, mfg, mbg, mag_x_hot, mag_y_hot);
1214         }
1215
1216         Gdk::Color fbg ("#ffffff" );
1217         Gdk::Color ffg  ("#000000" );
1218         
1219         {
1220                 RefPtr<Bitmap> source, mask;
1221                 
1222                 source = Bitmap::create (fader_cursor_bits, fader_cursor_width, fader_cursor_height);
1223                 mask = Bitmap::create (fader_cursor_mask_bits, fader_cursor_width, fader_cursor_height);
1224                 fader_cursor = new Gdk::Cursor (source, mask, ffg, fbg, fader_cursor_x_hot, fader_cursor_y_hot);
1225         }
1226         
1227         { 
1228                 RefPtr<Bitmap> source, mask;
1229                 source = Bitmap::create (speaker_cursor_bits, speaker_cursor_width, speaker_cursor_height);
1230                 mask = Bitmap::create (speaker_cursor_mask_bits, speaker_cursor_width, speaker_cursor_height);
1231                 speaker_cursor = new Gdk::Cursor (source, mask, ffg, fbg, speaker_cursor_x_hot, speaker_cursor_y_hot);
1232         }
1233         
1234         grabber_cursor = new Gdk::Cursor (HAND2);
1235         cross_hair_cursor = new Gdk::Cursor (CROSSHAIR);
1236         trimmer_cursor =  new Gdk::Cursor (SB_H_DOUBLE_ARROW);
1237         selector_cursor = new Gdk::Cursor (XTERM);
1238         time_fx_cursor = new Gdk::Cursor (SIZING);
1239         wait_cursor = new Gdk::Cursor  (WATCH);
1240         timebar_cursor = new Gdk::Cursor(LEFT_PTR);
1241         midi_select_cursor = new Gdk::Cursor (CENTER_PTR);
1242         midi_pencil_cursor = new Gdk::Cursor (PENCIL);
1243         midi_erase_cursor = new Gdk::Cursor (DRAPED_BOX);
1244 }
1245
1246 /** Pop up a context menu for when the user clicks on a fade in or fade out */
1247 void
1248 Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
1249 {
1250         using namespace Menu_Helpers;
1251         AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
1252
1253         if (arv == 0) {
1254                 fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
1255                 /*NOTREACHED*/
1256         }
1257
1258         MenuList& items (fade_context_menu.items());
1259
1260         items.clear ();
1261
1262         switch (item_type) {
1263         case FadeInItem:
1264         case FadeInHandleItem:
1265                 if (arv->audio_region()->fade_in_active()) {
1266                         items.push_back (MenuElem (_("Deactivate"), bind (mem_fun (*this, &Editor::set_fade_in_active), false)));
1267                 } else {
1268                         items.push_back (MenuElem (_("Activate"), bind (mem_fun (*this, &Editor::set_fade_in_active), true)));
1269                 }
1270                 
1271                 items.push_back (SeparatorElem());
1272                 
1273                 items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear)));
1274                 items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast)));
1275                 items.push_back (MenuElem (_("Slow"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogB)));
1276                 items.push_back (MenuElem (_("Fast"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogA)));
1277                 items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Slow)));
1278                 break;
1279
1280         case FadeOutItem:
1281         case FadeOutHandleItem:
1282                 if (arv->audio_region()->fade_out_active()) {
1283                         items.push_back (MenuElem (_("Deactivate"), bind (mem_fun (*this, &Editor::set_fade_out_active), false)));
1284                 } else {
1285                         items.push_back (MenuElem (_("Activate"), bind (mem_fun (*this, &Editor::set_fade_out_active), true)));
1286                 }
1287                 
1288                 items.push_back (SeparatorElem());
1289                 
1290                 items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear)));
1291                 items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow)));
1292                 items.push_back (MenuElem (_("Slow"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogA)));
1293                 items.push_back (MenuElem (_("Fast"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogB)));
1294                 items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Fast)));
1295
1296                 break;
1297
1298         default:
1299                 fatal << _("programming error: ")
1300                       << X_("non-fade canvas item passed to popup_fade_context_menu()")
1301                       << endmsg;
1302                 /*NOTREACHED*/
1303         }
1304
1305         fade_context_menu.popup (button, time);
1306 }
1307
1308 /* Pop up the general track context menu for when the user clicks pretty much anywhere in a track or bus */
1309 void
1310 Editor::popup_track_context_menu (int button, int32_t time, nframes_t frame)
1311 {
1312         build_track_context_menu (frame)->popup (button, time);
1313 }
1314
1315 Menu*
1316 Editor::build_track_context_menu (nframes_t frame)
1317 {
1318         using namespace Menu_Helpers;
1319
1320         Menu* menu = manage (new Menu);
1321         MenuList& edit_items = menu->items();
1322         edit_items.clear();
1323
1324         /* Build the general `track' context menu, adding what is appropriate given
1325            the current selection */
1326
1327         /* XXX: currently crossfades can't be selected, so we can't use the selection
1328            to decide which crossfades to mention in the menu.  I believe this will
1329            change at some point.  For now we have to use clicked_trackview to decide. */
1330         AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*> (clicked_axisview);
1331
1332         if (atv) {
1333                 boost::shared_ptr<Diskstream> ds;
1334                 boost::shared_ptr<Playlist> pl;
1335                 boost::shared_ptr<AudioPlaylist> apl;
1336
1337                 if ((ds = atv->get_diskstream()) && ((pl = ds->playlist()) != 0) && ((apl = boost::dynamic_pointer_cast<AudioPlaylist> (pl)) != 0)) {
1338
1339                         AudioPlaylist::Crossfades xfades;
1340                         apl->crossfades_at (frame, xfades);
1341
1342                         bool many = xfades.size() > 1;
1343
1344                         for (AudioPlaylist::Crossfades::iterator i = xfades.begin(); i != xfades.end(); ++i) {
1345                                 add_crossfade_context_items (atv->audio_view(), (*i), edit_items, many);
1346                         }
1347                 }
1348         }       
1349
1350         if (!selection->time.empty()) {
1351                 add_selection_context_items (edit_items);
1352         }
1353
1354         if (!selection->regions.empty()) {
1355                 add_region_context_items (edit_items);
1356         }
1357
1358         if (!selection->tracks.empty()) {
1359                 add_bus_or_audio_track_context_items (edit_items);
1360         }
1361         
1362         menu->set_name ("ArdourContextMenu");
1363
1364         return menu;
1365 }
1366
1367 #ifdef FFT_ANALYSIS
1368 void
1369 Editor::analyze_region_selection()
1370 {
1371         if (analysis_window == 0) {
1372                 analysis_window = new AnalysisWindow();
1373
1374                 if (session != 0)
1375                         analysis_window->set_session(session);
1376
1377                 analysis_window->show_all();
1378         }
1379
1380         analysis_window->set_regionmode();
1381         analysis_window->analyze();
1382         
1383         analysis_window->present();
1384 }
1385
1386 void
1387 Editor::analyze_range_selection()
1388 {
1389         if (analysis_window == 0) {
1390                 analysis_window = new AnalysisWindow();
1391
1392                 if (session != 0)
1393                         analysis_window->set_session(session);
1394
1395                 analysis_window->show_all();
1396         }
1397
1398         analysis_window->set_rangemode();
1399         analysis_window->analyze();
1400         
1401         analysis_window->present();
1402 }
1403 #endif /* FFT_ANALYSIS */
1404
1405
1406 /** Add context menu items relevant to crossfades.
1407  * @param edit_items List to add the items to.
1408  */
1409 void
1410 Editor::add_crossfade_context_items (AudioStreamView* view, boost::shared_ptr<Crossfade> xfade, Menu_Helpers::MenuList& edit_items, bool many)
1411 {
1412         using namespace Menu_Helpers;
1413         Menu     *xfade_menu = manage (new Menu);
1414         MenuList& items       = xfade_menu->items();
1415         xfade_menu->set_name ("ArdourContextMenu");
1416         string str;
1417
1418         if (xfade->active()) {
1419                 str = _("Mute");
1420         } else { 
1421                 str = _("Unmute");
1422         }
1423
1424         items.push_back (MenuElem (str, bind (mem_fun(*this, &Editor::toggle_xfade_active), boost::weak_ptr<Crossfade> (xfade))));
1425         items.push_back (MenuElem (_("Edit"), bind (mem_fun(*this, &Editor::edit_xfade), boost::weak_ptr<Crossfade> (xfade))));
1426
1427         if (xfade->can_follow_overlap()) {
1428
1429                 if (xfade->following_overlap()) {
1430                         str = _("Convert to short");
1431                 } else {
1432                         str = _("Convert to full");
1433                 }
1434
1435                 items.push_back (MenuElem (str, bind (mem_fun(*this, &Editor::toggle_xfade_length), xfade)));
1436         }
1437
1438         if (many) {
1439                 str = xfade->out()->name();
1440                 str += "->";
1441                 str += xfade->in()->name();
1442         } else {
1443                 str = _("Crossfade");
1444         }
1445
1446         edit_items.push_back (MenuElem (str, *xfade_menu));
1447         edit_items.push_back (SeparatorElem());
1448 }
1449
1450 void
1451 Editor::xfade_edit_left_region ()
1452 {
1453         if (clicked_crossfadeview) {
1454                 clicked_crossfadeview->left_view.show_region_editor ();
1455         }
1456 }
1457
1458 void
1459 Editor::xfade_edit_right_region ()
1460 {
1461         if (clicked_crossfadeview) {
1462                 clicked_crossfadeview->right_view.show_region_editor ();
1463         }
1464 }
1465
1466 /** Add an element to a menu, settings its sensitivity.
1467  * @param m Menu to add to.
1468  * @param e Element to add.
1469  * @param s true to make sensitive, false to make insensitive
1470  */
1471 void
1472 Editor::add_item_with_sensitivity (Menu_Helpers::MenuList& m, Menu_Helpers::MenuElem e, bool s) const
1473 {
1474         m.push_back (e);
1475         if (!s) {
1476                 m.back().set_sensitive (false);
1477         }
1478 }
1479
1480 /** Add context menu items relevant to regions.
1481  * @param edit_items List to add the items to.
1482  */
1483 void
1484 Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items)
1485 {
1486         using namespace Menu_Helpers;
1487         Menu *region_menu = manage (new Menu);
1488         MenuList& items = region_menu->items();
1489         region_menu->set_name ("ArdourContextMenu");
1490         
1491         items.push_back (MenuElem (_("Edit..."), mem_fun(*this, &Editor::edit_region)));
1492         items.push_back (MenuElem (_("Raise to top layer"), mem_fun(*this, &Editor::raise_region_to_top)));
1493         items.push_back (MenuElem (_("Lower to bottom layer"), mem_fun  (*this, &Editor::lower_region_to_bottom)));
1494
1495         Menu* sync_point_menu = manage (new Menu);
1496         MenuList& sync_point_items = sync_point_menu->items();
1497         sync_point_menu->set_name("ArdourContextMenu");
1498         
1499         sync_point_items.push_back (MenuElem (_("Define"), mem_fun(*this, &Editor::set_region_sync_from_edit_cursor)));
1500         sync_point_items.push_back (MenuElem (_("Remove"), mem_fun(*this, &Editor::remove_region_sync)));
1501
1502         items.push_back (MenuElem (_("Sync points"), *sync_point_menu));
1503
1504         add_item_with_sensitivity (items, MenuElem (_("Audition"), mem_fun(*this, &Editor::audition_selected_region)), selection->regions.size() == 1);
1505         
1506         add_item_with_sensitivity (items, MenuElem (_("Export"), mem_fun(*this, &Editor::export_region)), selection->regions.size() == 1);
1507
1508         items.push_back (MenuElem (_("Bounce"), mem_fun(*this, &Editor::bounce_region_selection)));
1509
1510 #ifdef FFT_ANALYSIS
1511         items.push_back (MenuElem (_("Analyze region"), mem_fun(*this, &Editor::analyze_region_selection)));
1512 #endif
1513
1514         items.push_back (SeparatorElem());
1515
1516         items.push_back (MenuElem (_("Lock"), bind (mem_fun (*this, &Editor::set_region_lock), true)));
1517         items.push_back (MenuElem (_("Unlock"), bind (mem_fun (*this, &Editor::set_region_lock), false)));
1518         items.push_back (MenuElem (_("Lock Position"), bind (mem_fun (*this, &Editor::set_region_position_lock), true)));
1519         items.push_back (MenuElem (_("Unlock Position"), bind (mem_fun (*this, &Editor::set_region_position_lock), false)));
1520         items.push_back (MenuElem (_("Mute"), bind (mem_fun (*this, &Editor::set_region_mute), true)));
1521         items.push_back (MenuElem (_("Unmute"), bind (mem_fun (*this, &Editor::set_region_mute), false)));
1522         items.push_back (MenuElem (_("Opaque"), bind (mem_fun (*this, &Editor::set_region_opaque), true)));
1523         items.push_back (MenuElem (_("Transparent"), bind (mem_fun (*this, &Editor::set_region_opaque), false)));
1524
1525         /* We allow "Original position" if at least one region is not at its
1526            natural position 
1527         */
1528         RegionSelection::iterator i = selection->regions.begin();
1529         while (i != selection->regions.end() && (*i)->region()->at_natural_position() == true) {
1530                 ++i;
1531         }
1532
1533         add_item_with_sensitivity (items, MenuElem (_("Original position"), mem_fun(*this, &Editor::naturalize)), i != selection->regions.end());
1534         
1535         items.push_back (SeparatorElem());
1536
1537         /* Find out if we have a selected audio region */
1538         i = selection->regions.begin();
1539         while (i != selection->regions.end() && boost::dynamic_pointer_cast<AudioRegion>((*i)->region()) == 0) {
1540                 ++i;
1541         }
1542         const bool have_selected_audio_region = (i != selection->regions.end());
1543
1544         if (have_selected_audio_region) {
1545
1546                 Menu* envelopes_menu = manage (new Menu);
1547                 MenuList& envelopes_items = envelopes_menu->items();
1548                 envelopes_menu->set_name ("ArdourContextMenu");
1549
1550                 envelopes_items.push_back (MenuElem (_("Reset"), mem_fun(*this, &Editor::reset_region_gain_envelopes)));
1551                 envelopes_items.push_back (MenuElem (_("Visible"), bind (mem_fun (*this, &Editor::set_gain_envelope_visibility), true)));
1552                 envelopes_items.push_back (MenuElem (_("Invisible"), bind (mem_fun (*this, &Editor::set_gain_envelope_visibility), false)));
1553                 envelopes_items.push_back (MenuElem (_("Active"), bind (mem_fun (*this, &Editor::set_gain_envelope_active), true)));
1554                 envelopes_items.push_back (MenuElem (_("Inactive"), bind (mem_fun (*this, &Editor::set_gain_envelope_active), false)));
1555
1556                 items.push_back (MenuElem (_("Envelopes"), *envelopes_menu));
1557                 
1558                 items.push_back (MenuElem (_("Denormalize"), mem_fun (*this, &Editor::denormalize_regions)));
1559                 items.push_back (MenuElem (_("Normalize"), mem_fun (*this, &Editor::normalize_regions)));
1560         }
1561         
1562         /* Find out if we have a selected MIDI region */
1563         i = selection->regions.begin();
1564         while (i != selection->regions.end() && boost::dynamic_pointer_cast<MidiRegion>((*i)->region()) == 0) {
1565                 ++i;
1566         }
1567         const bool have_selected_midi_region = (i != selection->regions.end());
1568         
1569         if (have_selected_midi_region) {
1570
1571                 items.push_back (MenuElem (_("Quantize"), mem_fun(*this, &Editor::quantize_regions)));
1572                 items.push_back (SeparatorElem());
1573
1574         }
1575
1576         /* range related stuff */
1577         
1578         add_item_with_sensitivity (items, MenuElem (_("Add range markers"), mem_fun (*this, &Editor::add_location_from_audio_region)), selection->regions.size() == 1);
1579         
1580         add_item_with_sensitivity (items, MenuElem (_("Set range selection"), mem_fun (*this, &Editor::set_selection_from_audio_region)), selection->regions.size() == 1);
1581         
1582         items.push_back (SeparatorElem());
1583                          
1584         /* Nudge region */
1585
1586         Menu *nudge_menu = manage (new Menu());
1587         MenuList& nudge_items = nudge_menu->items();
1588         nudge_menu->set_name ("ArdourContextMenu");
1589         
1590         nudge_items.push_back (MenuElem (_("Nudge forward"), (bind (mem_fun(*this, &Editor::nudge_forward), false))));
1591         nudge_items.push_back (MenuElem (_("Nudge backward"), (bind (mem_fun(*this, &Editor::nudge_backward), false))));
1592         nudge_items.push_back (MenuElem (_("Nudge forward by capture offset"), (mem_fun(*this, &Editor::nudge_forward_capture_offset))));
1593         nudge_items.push_back (MenuElem (_("Nudge backward by capture offset"), (mem_fun(*this, &Editor::nudge_backward_capture_offset))));
1594
1595         items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1596
1597         Menu *trim_menu = manage (new Menu);
1598         MenuList& trim_items = trim_menu->items();
1599         trim_menu->set_name ("ArdourContextMenu");
1600         
1601         trim_items.push_back (MenuElem (_("Start to edit cursor"), mem_fun(*this, &Editor::trim_region_from_edit_cursor)));
1602         trim_items.push_back (MenuElem (_("Edit cursor to end"), mem_fun(*this, &Editor::trim_region_to_edit_cursor)));
1603                              
1604         items.push_back (MenuElem (_("Trim"), *trim_menu));
1605         items.push_back (MenuElem (_("Split"), (mem_fun(*this, &Editor::split_region))));
1606         items.push_back (MenuElem (_("Make mono regions"), (mem_fun(*this, &Editor::split_multichannel_region))));
1607         items.push_back (MenuElem (_("Duplicate"), (bind (mem_fun(*this, &Editor::duplicate_dialog), true))));
1608         items.push_back (MenuElem (_("Fill track"), (mem_fun(*this, &Editor::region_fill_track))));
1609         items.push_back (SeparatorElem());
1610         items.push_back (MenuElem (_("Remove"), mem_fun(*this, &Editor::remove_selected_regions)));
1611
1612         /* OK, stick the region submenu at the top of the list, and then add
1613            the standard items.
1614         */
1615
1616         string const menu_item_name = selection->regions.size() > 1 ? _("Regions") : _("Region");
1617         edit_items.push_back (MenuElem (menu_item_name, *region_menu));
1618 }
1619
1620 /** Add context menu items relevant to selection ranges.
1621  * @param edit_items List to add the items to.
1622  */
1623 void
1624 Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
1625 {
1626         using namespace Menu_Helpers;
1627         Menu     *selection_menu = manage (new Menu);
1628         MenuList& items       = selection_menu->items();
1629         selection_menu->set_name ("ArdourContextMenu");
1630
1631         items.push_back (MenuElem (_("Play range"), mem_fun(*this, &Editor::play_selection)));
1632         items.push_back (MenuElem (_("Loop range"), mem_fun(*this, &Editor::set_route_loop_selection)));
1633
1634 #ifdef FFT_ANALYSIS
1635         items.push_back (SeparatorElem());
1636         items.push_back (MenuElem (_("Analyze range"), mem_fun(*this, &Editor::analyze_range_selection)));
1637 #endif
1638         
1639         items.push_back (SeparatorElem());
1640         items.push_back (MenuElem (_("Separate range to track"), mem_fun(*this, &Editor::separate_region_from_selection)));
1641         items.push_back (MenuElem (_("Separate range to region list"), mem_fun(*this, &Editor::new_region_from_selection)));
1642         
1643         items.push_back (SeparatorElem());
1644         items.push_back (MenuElem (_("Select all in range"), mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
1645         items.push_back (SeparatorElem());
1646         items.push_back (MenuElem (_("Add range markers"), mem_fun (*this, &Editor::add_location_from_selection)));
1647         items.push_back (MenuElem (_("Set range to loop range"), mem_fun(*this, &Editor::set_selection_from_loop)));
1648         items.push_back (MenuElem (_("Set range to punch range"), mem_fun(*this, &Editor::set_selection_from_punch)));
1649         items.push_back (SeparatorElem());
1650         items.push_back (MenuElem (_("Crop region to range"), mem_fun(*this, &Editor::crop_region_to_selection)));
1651         items.push_back (MenuElem (_("Fill range with region"), mem_fun(*this, &Editor::region_fill_selection)));
1652         items.push_back (MenuElem (_("Duplicate range"), bind (mem_fun(*this, &Editor::duplicate_dialog), false)));
1653         items.push_back (MenuElem (_("Create chunk from range"), mem_fun(*this, &Editor::create_named_selection)));
1654         items.push_back (SeparatorElem());
1655         items.push_back (MenuElem (_("Bounce range"), mem_fun(*this, &Editor::bounce_range_selection)));
1656         items.push_back (MenuElem (_("Export range"), mem_fun(*this, &Editor::export_selection)));
1657
1658         edit_items.push_back (MenuElem (_("Range"), *selection_menu));
1659         edit_items.push_back (SeparatorElem());
1660 }
1661
1662 /** Add context menu items relevant to busses or audio tracks.
1663  * @param edit_items List to add the items to.
1664  */
1665 void
1666 Editor::add_bus_or_audio_track_context_items (Menu_Helpers::MenuList& edit_items)
1667 {
1668         using namespace Menu_Helpers;
1669
1670         /* We add every possible action here, and de-sensitize things
1671            that aren't allowed.  The sensitivity logic is a bit spread out;
1672            on the one hand I'm using things like can_cut_copy (), which is
1673            reasonably complicated and so perhaps better near the function that
1674            it expresses sensitivity for, and on the other hand checks
1675            in this function as well.  You can't really have can_* for everything
1676            or the number of methods would get silly. */
1677
1678         bool const one_selected_region = selection->regions.size() == 1;
1679
1680         /* Count the number of selected audio tracks */
1681         int n_audio_tracks = 0;
1682         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
1683                 RouteTimeAxisView const * r = dynamic_cast<RouteTimeAxisView*>(*i);
1684                 if (r && r->is_audio_track()) {
1685                         n_audio_tracks++;
1686                 }
1687         }
1688
1689         /* Playback */
1690
1691         Menu *play_menu = manage (new Menu);
1692         MenuList& play_items = play_menu->items();
1693         play_menu->set_name ("ArdourContextMenu");
1694         
1695         play_items.push_back (MenuElem (_("Play from edit cursor"), mem_fun(*this, &Editor::play_from_edit_cursor)));
1696         play_items.push_back (MenuElem (_("Play from start"), mem_fun(*this, &Editor::play_from_start)));
1697         add_item_with_sensitivity (play_items, MenuElem (_("Play region"), mem_fun(*this, &Editor::play_selected_region)), one_selected_region);
1698         
1699         add_item_with_sensitivity (play_items, MenuElem (_("Loop region"), mem_fun(*this, &Editor::loop_selected_region)), one_selected_region);
1700         
1701         edit_items.push_back (MenuElem (_("Play"), *play_menu));
1702
1703         /* Selection */
1704
1705         Menu *select_menu = manage (new Menu);
1706         MenuList& select_items = select_menu->items();
1707         select_menu->set_name ("ArdourContextMenu");
1708
1709         string str = selection->tracks.size() == 1 ? _("Select all in track") : _("Select all in tracks");
1710
1711         select_items.push_back (MenuElem (str, bind (mem_fun(*this, &Editor::select_all_in_selected_tracks), Selection::Set)));
1712         
1713         select_items.push_back (MenuElem (_("Select all"), bind (mem_fun(*this, &Editor::select_all), Selection::Set)));
1714
1715         str = selection->tracks.size() == 1 ? _("Invert selection in track") : _("Invert selection in tracks");
1716         
1717         select_items.push_back (MenuElem (str, mem_fun(*this, &Editor::invert_selection_in_selected_tracks)));
1718         
1719         select_items.push_back (MenuElem (_("Invert selection"), mem_fun(*this, &Editor::invert_selection)));
1720         select_items.push_back (SeparatorElem());
1721
1722         if (n_audio_tracks) {
1723                 select_items.push_back (MenuElem (_("Set range to loop range"), mem_fun(*this, &Editor::set_selection_from_loop)));
1724                 select_items.push_back (MenuElem (_("Set range to punch range"), mem_fun(*this, &Editor::set_selection_from_punch)));
1725         }
1726         
1727         select_items.push_back (MenuElem (_("Select all after edit cursor"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), edit_cursor, true)));
1728         select_items.push_back (MenuElem (_("Select all before edit cursor"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), edit_cursor, false)));
1729         select_items.push_back (MenuElem (_("Select all after playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, true)));
1730         select_items.push_back (MenuElem (_("Select all before playhead"), bind (mem_fun(*this, &Editor::select_all_selectables_using_cursor), playhead_cursor, false)));
1731
1732         if (n_audio_tracks) {
1733                 select_items.push_back (MenuElem (_("Select all between cursors"), bind (mem_fun(*this, &Editor::select_all_selectables_between_cursors), playhead_cursor, edit_cursor)));
1734         }
1735
1736         edit_items.push_back (MenuElem (_("Select"), *select_menu));
1737
1738         /* Cut-n-Paste */
1739
1740         Menu *cutnpaste_menu = manage (new Menu);
1741         MenuList& cutnpaste_items = cutnpaste_menu->items();
1742         cutnpaste_menu->set_name ("ArdourContextMenu");
1743
1744         add_item_with_sensitivity (cutnpaste_items, MenuElem (_("Cut"), mem_fun(*this, &Editor::cut)), can_cut_copy ());
1745         
1746         add_item_with_sensitivity (cutnpaste_items, MenuElem (_("Copy"), mem_fun(*this, &Editor::copy)), can_cut_copy ());
1747         
1748         if (n_audio_tracks) {
1749                 cutnpaste_items.push_back (MenuElem (_("Paste at edit cursor"), bind (mem_fun(*this, &Editor::paste), 1.0f)));
1750                 cutnpaste_items.push_back (MenuElem (_("Paste at mouse"), mem_fun(*this, &Editor::mouse_paste)));
1751                 
1752                 cutnpaste_items.push_back (SeparatorElem());
1753                 
1754                 cutnpaste_items.push_back (MenuElem (_("Align"), bind (mem_fun(*this, &Editor::align), ARDOUR::SyncPoint)));
1755                 cutnpaste_items.push_back (MenuElem (_("Align relative"), bind (mem_fun(*this, &Editor::align_relative), ARDOUR::SyncPoint)));
1756                 cutnpaste_items.push_back (MenuElem (_("Insert chunk"), bind (mem_fun(*this, &Editor::paste_named_selection), 1.0f)));
1757         } else {
1758                 cutnpaste_items.push_back (MenuElem (_("Paste"), bind (mem_fun(*this, &Editor::paste), 1.0f)));
1759         }
1760
1761         edit_items.push_back (MenuElem (_("Edit"), *cutnpaste_menu));
1762                 
1763         if (n_audio_tracks) {
1764
1765                 Menu *track_menu = manage (new Menu);
1766                 MenuList& track_items = track_menu->items();
1767                 track_menu->set_name ("ArdourContextMenu");
1768                 
1769                 /* Adding new material */
1770         
1771                 add_item_with_sensitivity (track_items, MenuElem (_("Insert selected region"), bind (mem_fun(*this, &Editor::insert_region_list_selection), 1.0f)), n_audio_tracks == 1);
1772                 
1773                 add_item_with_sensitivity (track_items, MenuElem (_("Insert existing audio"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)), n_audio_tracks == 1);
1774                 
1775                 /* Nudge */
1776
1777                 Menu *nudge_menu = manage (new Menu());
1778                 MenuList& nudge_items = nudge_menu->items();
1779                 nudge_menu->set_name ("ArdourContextMenu");
1780
1781                 str = selection->tracks.size() == 1 ? _("Nudge track forward") : _("Nude tracks forward");
1782                 nudge_items.push_back (MenuElem (str, (bind (mem_fun(*this, &Editor::nudge_selected_tracks), false, true))));
1783
1784                 str = selection->tracks.size() == 1 ? _("Nudge track after edit cursor forward") : _("Nudge tracks after edit cursor forward");
1785                 
1786                 nudge_items.push_back (MenuElem (str, (bind (mem_fun(*this, &Editor::nudge_selected_tracks), true, true))));
1787
1788                 str = selection->tracks.size() == 1 ? _("Nudge track backward") : _("Nudge tracks backward");
1789                 
1790                 nudge_items.push_back (MenuElem (str, (bind (mem_fun(*this, &Editor::nudge_selected_tracks), false, false))));
1791
1792                 str = selection->tracks.size() == 1 ? _("Nudge track after edit cursor backward") : _("Nudge tracks after edit cursor backward");
1793                 
1794                 nudge_items.push_back (MenuElem (str, (bind (mem_fun(*this, &Editor::nudge_selected_tracks), true, false))));
1795                 
1796                 track_items.push_back (MenuElem (_("Nudge"), *nudge_menu));
1797
1798                 /* Freeze */
1799                 track_items.push_back (MenuElem (_("Freeze"), mem_fun(*this, &Editor::freeze_routes)));
1800                 track_items.push_back (MenuElem (_("Unfreeze"), mem_fun(*this, &Editor::unfreeze_routes)));
1801
1802                 str = selection->tracks.size() == 1 ? _("Track") : _("Tracks");
1803                 edit_items.push_back (MenuElem (str, *track_menu));
1804         }
1805 }
1806
1807 /* CURSOR SETTING AND MARKS AND STUFF */
1808
1809 void
1810 Editor::set_snap_to (SnapType st)
1811 {       
1812         snap_type = st;
1813         string str = snap_type_strings[(int) st];
1814
1815         if (str != snap_type_selector.get_active_text()) {
1816                 snap_type_selector.set_active_text (str);
1817         }
1818
1819         instant_save ();
1820
1821         switch (snap_type) {
1822         case SnapToAThirtysecondBeat:
1823         case SnapToASixteenthBeat:
1824         case SnapToAEighthBeat:
1825         case SnapToAQuarterBeat:
1826         case SnapToAThirdBeat:
1827                 update_tempo_based_rulers ();
1828         default:
1829                 /* relax */
1830                 break;
1831     }
1832 }
1833
1834 void
1835 Editor::set_snap_mode (SnapMode mode)
1836 {
1837         snap_mode = mode;
1838         string str = snap_mode_strings[(int)mode];
1839
1840         if (str != snap_mode_selector.get_active_text ()) {
1841                 snap_mode_selector.set_active_text (str);
1842         }
1843
1844         instant_save ();
1845 }
1846
1847 int
1848 Editor::set_state (const XMLNode& node)
1849 {
1850         const XMLProperty* prop;
1851         XMLNode* geometry;
1852         int x, y, xoff, yoff;
1853         Gdk::Geometry g;
1854
1855         if ((prop = node.property ("id")) != 0) {
1856                 _id = prop->value ();
1857         }
1858
1859         if ((geometry = find_named_node (node, "geometry")) == 0) {
1860
1861                 g.base_width = default_width;
1862                 g.base_height = default_height;
1863                 x = 1;
1864                 y = 1;
1865                 xoff = 0;
1866                 yoff = 21;
1867
1868         } else {
1869
1870                 g.base_width = atoi(geometry->property("x_size")->value());
1871                 g.base_height = atoi(geometry->property("y_size")->value());
1872                 x = atoi(geometry->property("x_pos")->value());
1873                 y = atoi(geometry->property("y_pos")->value());
1874                 xoff = atoi(geometry->property("x_off")->value());
1875                 yoff = atoi(geometry->property("y_off")->value());
1876         }
1877
1878         set_default_size (g.base_width, g.base_height);
1879         move (x, y);
1880
1881         if (session && (prop = node.property ("playhead"))) {
1882                 nframes_t pos = atol (prop->value().c_str());
1883                 playhead_cursor->set_position (pos);
1884         } else {
1885                 playhead_cursor->set_position (0);
1886
1887                 /* reset_x_origin() doesn't work right here, since the old
1888                    position may be zero already, and it does nothing in such
1889                    circumstances.
1890                 */
1891                 
1892                 leftmost_frame = 0;
1893                 horizontal_adjustment.set_value (0);
1894         }
1895
1896         if (session && (prop = node.property ("edit-cursor"))) {
1897                 nframes_t pos = atol (prop->value().c_str());
1898                 edit_cursor->set_position (pos);
1899         } else {
1900                 edit_cursor->set_position (0);
1901         }
1902
1903         if ((prop = node.property ("mixer-width"))) {
1904                 editor_mixer_strip_width = Width (string_2_enum (prop->value(), editor_mixer_strip_width));
1905         }
1906
1907         if ((prop = node.property ("zoom-focus"))) {
1908                 set_zoom_focus ((ZoomFocus) atoi (prop->value()));
1909         }
1910
1911         if ((prop = node.property ("zoom"))) {
1912                 reset_zoom (PBD::atof (prop->value()));
1913         }
1914
1915         if ((prop = node.property ("snap-to"))) {
1916                 set_snap_to ((SnapType) atoi (prop->value()));
1917         }
1918
1919         if ((prop = node.property ("snap-mode"))) {
1920                 set_snap_mode ((SnapMode) atoi (prop->value()));
1921         }
1922
1923         if ((prop = node.property ("mouse-mode"))) {
1924                 MouseMode m = str2mousemode(prop->value());
1925                 mouse_mode = MouseMode ((int) m + 1); /* lie, force mode switch */
1926                 set_mouse_mode (m, true);
1927         } else {
1928                 mouse_mode = MouseGain; /* lie, to force the mode switch */
1929                 set_mouse_mode (MouseObject, true);
1930         }
1931
1932         if ((prop = node.property ("show-waveforms"))) {
1933                 bool yn = (prop->value() == "yes");
1934                 _show_waveforms = !yn;
1935                 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleWaveformVisibility"));
1936                 if (act) {
1937                         RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
1938                         /* do it twice to force the change */
1939                         tact->set_active (!yn);
1940                         tact->set_active (yn);
1941                 }
1942         }
1943
1944         if ((prop = node.property ("show-waveforms-recording"))) {
1945                 bool yn = (prop->value() == "yes");
1946                 _show_waveforms_recording = !yn;
1947                 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleWaveformsWhileRecording"));
1948                 if (act) {
1949                         RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
1950                         /* do it twice to force the change */
1951                         tact->set_active (!yn);
1952                         tact->set_active (yn);
1953                 }
1954         }
1955         
1956         if ((prop = node.property ("show-measures"))) {
1957                 bool yn = (prop->value() == "yes");
1958                 _show_measures = !yn;
1959                 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
1960                 if (act) {
1961                         RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
1962                         /* do it twice to force the change */
1963                         tact->set_active (!yn);
1964                         tact->set_active (yn);
1965                 }
1966         }
1967
1968         if ((prop = node.property ("follow-playhead"))) {
1969                 bool yn = (prop->value() == "yes");
1970                 set_follow_playhead (yn);
1971                 RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
1972                 if (act) {
1973                         RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
1974                         if (tact->get_active() != yn) {
1975                                 tact->set_active (yn);
1976                         }
1977                 }
1978         }
1979
1980         if ((prop = node.property ("region-list-sort-type"))) {
1981                 region_list_sort_type = (Editing::RegionListSortType) -1; // force change 
1982                 reset_region_list_sort_type(str2regionlistsorttype(prop->value()));
1983         }
1984
1985         if ((prop = node.property ("xfades-visible"))) {
1986                 bool yn = (prop->value() == "yes");
1987                 _xfade_visibility = !yn;
1988                 // set_xfade_visibility (yn);
1989         }
1990
1991         if ((prop = node.property ("show-editor-mixer"))) {
1992
1993                 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
1994                 if (act) {
1995
1996                         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
1997                         bool yn = (prop->value() == X_("yes"));
1998
1999                         /* do it twice to force the change */
2000                         
2001                         tact->set_active (!yn);
2002                         tact->set_active (yn);
2003                 }
2004         }
2005         
2006         if ((prop = node.property ("show-editor-list"))) {
2007
2008                 Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2009                 assert(act);
2010                 if (act) {
2011
2012                         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2013                         bool yn = (prop->value() == X_("yes"));
2014
2015                         /* do it twice to force the change */
2016                         
2017                         tact->set_active (!yn);
2018                         tact->set_active (yn);
2019                 }
2020         }
2021
2022
2023         return 0;
2024 }
2025
2026 XMLNode&
2027 Editor::get_state ()
2028 {
2029         XMLNode* node = new XMLNode ("Editor");
2030         char buf[32];
2031
2032         _id.print (buf, sizeof (buf));
2033         node->add_property ("id", buf);
2034         
2035         if (is_realized()) {
2036                 Glib::RefPtr<Gdk::Window> win = get_window();
2037                 
2038                 int x, y, xoff, yoff, width, height;
2039                 win->get_root_origin(x, y);
2040                 win->get_position(xoff, yoff);
2041                 win->get_size(width, height);
2042                 
2043                 XMLNode* geometry = new XMLNode ("geometry");
2044
2045                 snprintf(buf, sizeof(buf), "%d", width);
2046                 geometry->add_property("x_size", string(buf));
2047                 snprintf(buf, sizeof(buf), "%d", height);
2048                 geometry->add_property("y_size", string(buf));
2049                 snprintf(buf, sizeof(buf), "%d", x);
2050                 geometry->add_property("x_pos", string(buf));
2051                 snprintf(buf, sizeof(buf), "%d", y);
2052                 geometry->add_property("y_pos", string(buf));
2053                 snprintf(buf, sizeof(buf), "%d", xoff);
2054                 geometry->add_property("x_off", string(buf));
2055                 snprintf(buf, sizeof(buf), "%d", yoff);
2056                 geometry->add_property("y_off", string(buf));
2057                 snprintf(buf,sizeof(buf), "%d",gtk_paned_get_position (static_cast<Paned*>(&edit_pane)->gobj()));
2058                 geometry->add_property("edit_pane_pos", string(buf));
2059
2060                 node->add_child_nocopy (*geometry);
2061         }
2062
2063         maybe_add_mixer_strip_width (*node);
2064         
2065         snprintf (buf, sizeof(buf), "%d", (int) zoom_focus);
2066         node->add_property ("zoom-focus", buf);
2067         snprintf (buf, sizeof(buf), "%f", frames_per_unit);
2068         node->add_property ("zoom", buf);
2069         snprintf (buf, sizeof(buf), "%d", (int) snap_type);
2070         node->add_property ("snap-to", buf);
2071         snprintf (buf, sizeof(buf), "%d", (int) snap_mode);
2072         node->add_property ("snap-mode", buf);
2073
2074         snprintf (buf, sizeof (buf), "%" PRIu32, playhead_cursor->current_frame);
2075         node->add_property ("playhead", buf);
2076         snprintf (buf, sizeof (buf), "%" PRIu32, edit_cursor->current_frame);
2077         node->add_property ("edit-cursor", buf);
2078
2079         node->add_property ("show-waveforms", _show_waveforms ? "yes" : "no");
2080         node->add_property ("show-waveforms-recording", _show_waveforms_recording ? "yes" : "no");
2081         node->add_property ("show-measures", _show_measures ? "yes" : "no");
2082         node->add_property ("follow-playhead", _follow_playhead ? "yes" : "no");
2083         node->add_property ("xfades-visible", _xfade_visibility ? "yes" : "no");
2084         node->add_property ("region-list-sort-type", enum2str(region_list_sort_type));
2085         node->add_property ("mouse-mode", enum2str(mouse_mode));
2086         
2087         Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("show-editor-mixer"));
2088         if (act) {
2089                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2090                 node->add_property (X_("show-editor-mixer"), tact->get_active() ? "yes" : "no");
2091         }
2092         
2093         act = ActionManager::get_action (X_("Editor"), X_("show-editor-list"));
2094         if (act) {
2095                 Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
2096                 node->add_property (X_("show-editor-list"), tact->get_active() ? "yes" : "no");
2097         }
2098
2099         return *node;
2100 }
2101
2102
2103
2104 TimeAxisView *
2105 Editor::trackview_by_y_position (double y)
2106 {
2107         for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
2108
2109                 TimeAxisView *tv;
2110
2111                 if ((tv = (*iter)->covers_y_position (y)) != 0) {
2112                         return tv;
2113                 }
2114         }
2115
2116         return 0;
2117 }
2118
2119 void
2120 Editor::snap_to (nframes64_t& start, int32_t direction, bool for_mark)
2121 {
2122         Location* before = 0;
2123         Location* after = 0;
2124
2125         if (!session) {
2126                 return;
2127         }
2128
2129         const nframes64_t one_second = session->frame_rate();
2130         const nframes64_t one_minute = session->frame_rate() * 60;
2131         const nframes64_t one_smpte_second = (nframes64_t)(rint(session->smpte_frames_per_second()) * session->frames_per_smpte_frame());
2132         nframes64_t one_smpte_minute = (nframes64_t)(rint(session->smpte_frames_per_second()) * session->frames_per_smpte_frame() * 60);
2133         nframes64_t presnap = start;
2134
2135         switch (snap_type) {
2136         case SnapToFrame:
2137                 break;
2138
2139         case SnapToCDFrame:
2140                 if (direction) {
2141                         start = (nframes_t) ceil ((double) start / (one_second / 75)) * (one_second / 75);
2142                 } else {
2143                         start = (nframes_t) floor ((double) start / (one_second / 75)) * (one_second / 75);
2144                 }
2145                 break;
2146
2147         case SnapToSMPTEFrame:
2148                 if (fmod((double)start, (double)session->frames_per_smpte_frame()) > (session->frames_per_smpte_frame() / 2)) {
2149                         start = (nframes_t) (ceil ((double) start / session->frames_per_smpte_frame()) * session->frames_per_smpte_frame());
2150                 } else {
2151                         start = (nframes_t) (floor ((double) start / session->frames_per_smpte_frame()) *  session->frames_per_smpte_frame());
2152                 }
2153                 break;
2154
2155         case SnapToSMPTESeconds:
2156                 if (session->smpte_offset_negative())
2157                 {
2158                         start += session->smpte_offset ();
2159                 } else {
2160                         start -= session->smpte_offset ();
2161                 }    
2162                 if (start % one_smpte_second > one_smpte_second / 2) {
2163                         start = (nframes_t) ceil ((double) start / one_smpte_second) * one_smpte_second;
2164                 } else {
2165                         start = (nframes_t) floor ((double) start / one_smpte_second) * one_smpte_second;
2166                 }
2167                 
2168                 if (session->smpte_offset_negative())
2169                 {
2170                         start -= session->smpte_offset ();
2171                 } else {
2172                         start += session->smpte_offset ();
2173                 }
2174                 break;
2175                 
2176         case SnapToSMPTEMinutes:
2177                 if (session->smpte_offset_negative())
2178                 {
2179                         start += session->smpte_offset ();
2180                 } else {
2181                         start -= session->smpte_offset ();
2182                 }
2183                 if (start % one_smpte_minute > one_smpte_minute / 2) {
2184                         start = (nframes_t) ceil ((double) start / one_smpte_minute) * one_smpte_minute;
2185                 } else {
2186                         start = (nframes_t) floor ((double) start / one_smpte_minute) * one_smpte_minute;
2187                 }
2188                 if (session->smpte_offset_negative())
2189                 {
2190                         start -= session->smpte_offset ();
2191                 } else {
2192                         start += session->smpte_offset ();
2193                 }
2194                 break;
2195                 
2196         case SnapToSeconds:
2197                 if (start % one_second > one_second / 2) {
2198                         start = (nframes_t) ceil ((double) start / one_second) * one_second;
2199                 } else {
2200                         start = (nframes_t) floor ((double) start / one_second) * one_second;
2201                 }
2202                 break;
2203                 
2204         case SnapToMinutes:
2205                 if (start % one_minute > one_minute / 2) {
2206                         start = (nframes_t) ceil ((double) start / one_minute) * one_minute;
2207                 } else {
2208                         start = (nframes_t) floor ((double) start / one_minute) * one_minute;
2209                 }
2210                 break;
2211
2212         case SnapToBar:
2213                 start = session->tempo_map().round_to_bar (start, direction);
2214                 break;
2215
2216         case SnapToBeat:
2217                 start = session->tempo_map().round_to_beat (start, direction);
2218                 break;
2219
2220         case SnapToAThirtysecondBeat:
2221                 start = session->tempo_map().round_to_beat_subdivision (start, 32);
2222                 break;
2223
2224         case SnapToASixteenthBeat:
2225                 start = session->tempo_map().round_to_beat_subdivision (start, 16);
2226                 break;
2227
2228         case SnapToAEighthBeat:
2229                 start = session->tempo_map().round_to_beat_subdivision (start, 8);
2230                 break;
2231
2232         case SnapToAQuarterBeat:
2233                 start = session->tempo_map().round_to_beat_subdivision (start, 4);
2234                 break;
2235
2236         case SnapToAThirdBeat:
2237                 start = session->tempo_map().round_to_beat_subdivision (start, 3);
2238                 break;
2239
2240         case SnapToEditCursor:
2241                 start = edit_cursor->current_frame;
2242                 break;
2243
2244         case SnapToMark:
2245                 if (for_mark) {
2246                         return;
2247                 }
2248
2249                 before = session->locations()->first_location_before (start);
2250                 after = session->locations()->first_location_after (start);
2251
2252                 if (direction < 0) {
2253                         if (before) {
2254                                 start = before->start();
2255                         } else {
2256                                 start = 0;
2257                         }
2258                 } else if (direction > 0) {
2259                         if (after) {
2260                                 start = after->start();
2261                         } else {
2262                                 start = session->current_end_frame();
2263                         }
2264                 } else {
2265                         if (before) {
2266                                 if (after) {
2267                                         /* find nearest of the two */
2268                                         if ((start - before->start()) < (after->start() - start)) {
2269                                                 start = before->start();
2270                                         } else {
2271                                                 start = after->start();
2272                                         }
2273                                 } else {
2274                                         start = before->start();
2275                                 }
2276                         } else if (after) {
2277                                 start = after->start();
2278                         } else {
2279                                 /* relax */
2280                         }
2281                 }
2282                 break;
2283
2284         case SnapToRegionStart:
2285         case SnapToRegionEnd:
2286         case SnapToRegionSync:
2287         case SnapToRegionBoundary:
2288                 if (!region_boundary_cache.empty()) {
2289                         vector<nframes_t>::iterator i;
2290
2291                         if (direction > 0) {
2292                                 i = std::upper_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2293                         } else {
2294                                 i = std::lower_bound (region_boundary_cache.begin(), region_boundary_cache.end(), start);
2295                         }
2296                         
2297                         if (i != region_boundary_cache.end()) {
2298                                 start = *i;
2299                         } else {
2300                                 start = region_boundary_cache.back();
2301                         }
2302                 }
2303                 break;
2304         }
2305
2306         switch (snap_mode) {
2307         case SnapNormal:
2308                 return;                 
2309                 
2310         case SnapMagnetic:
2311                 
2312                 if (presnap > start) {
2313                         if (presnap > (start + unit_to_frame(snap_threshold))) {
2314                                 start = presnap;
2315                         }
2316                         
2317                 } else if (presnap < start) {
2318                         if (presnap < (start - unit_to_frame(snap_threshold))) {
2319                                 start = presnap;
2320                         }
2321                 }
2322                 
2323         default:
2324                 return;
2325                 
2326         }
2327 }
2328
2329 double
2330 Editor::snap_length_beats (nframes_t start)
2331 {
2332         if (!session) {
2333                 return 1.0;
2334         }
2335
2336         /* FIXME: This could/should also work with non-tempo based snap settings (ie seconds) */
2337
2338         switch (snap_type) {
2339         case SnapToBar:
2340                 return session->tempo_map().meter_at(start).beats_per_bar();
2341
2342         case SnapToBeat:
2343                 return 1.0;
2344
2345         case SnapToAThirtysecondBeat:
2346                 return 1.0 / (double)32.0;
2347                 break;
2348
2349         case SnapToASixteenthBeat:
2350                 return 1.0 / (double)16.0;
2351                 break;
2352
2353         case SnapToAEighthBeat:
2354                 return 1.0 / (double)8.0;
2355                 break;
2356
2357         case SnapToAQuarterBeat:
2358                 return 1.0 / (double)4.0;
2359                 break;
2360
2361         case SnapToAThirdBeat:
2362                 return 1.0 / (double)3.0;
2363
2364         default:
2365                 return 1.0;
2366         }
2367 }
2368
2369 void
2370 Editor::setup_toolbar ()
2371 {
2372         string pixmap_path;
2373
2374         const guint32 FUDGE = 18; // Combo's are stupid - they steal space from the entry for the button
2375
2376
2377         /* Mode Buttons (tool selection) */
2378
2379         vector<ToggleButton *> mouse_mode_buttons;
2380
2381         mouse_move_button.add (*(manage (new Image (::get_icon("tool_object")))));
2382         mouse_move_button.set_relief(Gtk::RELIEF_NONE);
2383         mouse_mode_buttons.push_back (&mouse_move_button);
2384         mouse_select_button.add (*(manage (new Image (get_xpm("tool_range.xpm")))));
2385         mouse_select_button.set_relief(Gtk::RELIEF_NONE);
2386         mouse_mode_buttons.push_back (&mouse_select_button);
2387         mouse_gain_button.add (*(manage (new Image (::get_icon("tool_gain")))));
2388         mouse_gain_button.set_relief(Gtk::RELIEF_NONE);
2389         mouse_mode_buttons.push_back (&mouse_gain_button);
2390         mouse_zoom_button.add (*(manage (new Image (::get_icon("tool_zoom")))));
2391         mouse_zoom_button.set_relief(Gtk::RELIEF_NONE);
2392         mouse_mode_buttons.push_back (&mouse_zoom_button);
2393         mouse_timefx_button.add (*(manage (new Image (::get_icon("tool_stretch")))));
2394         mouse_timefx_button.set_relief(Gtk::RELIEF_NONE);
2395         mouse_mode_buttons.push_back (&mouse_timefx_button);
2396         mouse_audition_button.add (*(manage (new Image (::get_icon("tool_audition")))));
2397         mouse_audition_button.set_relief(Gtk::RELIEF_NONE);
2398         mouse_note_button.add (*(manage (new Image (::get_icon("tool_note")))));
2399         mouse_note_button.set_relief(Gtk::RELIEF_NONE);
2400         mouse_mode_buttons.push_back (&mouse_note_button);
2401         mouse_mode_buttons.push_back (&mouse_audition_button);
2402         
2403         mouse_mode_button_set = new GroupedButtons (mouse_mode_buttons);
2404
2405         HBox* mode_box = manage(new HBox);
2406         mode_box->set_border_width (2);
2407         mode_box->set_spacing(4);
2408         mouse_mode_button_box.set_spacing(1);
2409         mouse_mode_button_box.pack_start(mouse_move_button, true, true);
2410         mouse_mode_button_box.pack_start(mouse_select_button, true, true);
2411         mouse_mode_button_box.pack_start(mouse_zoom_button, true, true);
2412         mouse_mode_button_box.pack_start(mouse_gain_button, true, true);
2413         mouse_mode_button_box.pack_start(mouse_timefx_button, true, true);
2414         mouse_mode_button_box.pack_start(mouse_audition_button, true, true);
2415         mouse_mode_button_box.pack_start(mouse_note_button, true, true);
2416         mouse_mode_button_box.set_homogeneous(true);
2417
2418         vector<string> edit_mode_strings;
2419         edit_mode_strings.push_back (edit_mode_to_string (Splice));
2420         edit_mode_strings.push_back (edit_mode_to_string (Slide));
2421
2422         edit_mode_selector.set_name ("EditModeSelector");
2423         Gtkmm2ext::set_size_request_to_display_given_text (edit_mode_selector, longest (edit_mode_strings).c_str(), 2+FUDGE, 10);
2424         set_popdown_strings (edit_mode_selector, edit_mode_strings);
2425         edit_mode_selector.signal_changed().connect (mem_fun(*this, &Editor::edit_mode_selection_done));
2426
2427         mode_box->pack_start(edit_mode_selector);
2428         mode_box->pack_start(mouse_mode_button_box);
2429         
2430         mouse_mode_tearoff = manage (new TearOff (*mode_box));
2431         mouse_mode_tearoff->set_name ("MouseModeBase");
2432
2433         mouse_mode_tearoff->Detach.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox), 
2434                                                   &mouse_mode_tearoff->tearoff_window()));
2435         mouse_mode_tearoff->Attach.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox), 
2436                                                   &mouse_mode_tearoff->tearoff_window(), 1));
2437         mouse_mode_tearoff->Hidden.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox), 
2438                                                   &mouse_mode_tearoff->tearoff_window()));
2439         mouse_mode_tearoff->Visible.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox), 
2440                                                    &mouse_mode_tearoff->tearoff_window(), 1));
2441
2442         mouse_move_button.set_name ("MouseModeButton");
2443         mouse_select_button.set_name ("MouseModeButton");
2444         mouse_gain_button.set_name ("MouseModeButton");
2445         mouse_zoom_button.set_name ("MouseModeButton");
2446         mouse_timefx_button.set_name ("MouseModeButton");
2447         mouse_audition_button.set_name ("MouseModeButton");
2448         mouse_note_button.set_name ("MouseModeButton");
2449
2450         ARDOUR_UI::instance()->tooltips().set_tip (mouse_move_button, _("Select/Move Objects"));
2451         ARDOUR_UI::instance()->tooltips().set_tip (mouse_select_button, _("Select/Move Ranges"));
2452         ARDOUR_UI::instance()->tooltips().set_tip (mouse_gain_button, _("Draw Gain Automation"));
2453         ARDOUR_UI::instance()->tooltips().set_tip (mouse_zoom_button, _("Select Zoom Range"));
2454         ARDOUR_UI::instance()->tooltips().set_tip (mouse_timefx_button, _("Stretch/Shrink Regions"));
2455         ARDOUR_UI::instance()->tooltips().set_tip (mouse_audition_button, _("Listen to Specific Regions"));
2456         ARDOUR_UI::instance()->tooltips().set_tip (mouse_note_button, _("Edit MIDI Notes"));
2457
2458         mouse_move_button.unset_flags (CAN_FOCUS);
2459         mouse_select_button.unset_flags (CAN_FOCUS);
2460         mouse_gain_button.unset_flags (CAN_FOCUS);
2461         mouse_zoom_button.unset_flags (CAN_FOCUS);
2462         mouse_timefx_button.unset_flags (CAN_FOCUS);
2463         mouse_audition_button.unset_flags (CAN_FOCUS);
2464         mouse_note_button.unset_flags (CAN_FOCUS);
2465
2466         mouse_select_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseRange));
2467         mouse_select_button.signal_button_release_event().connect (mem_fun(*this, &Editor::mouse_select_button_release));
2468
2469         mouse_move_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseObject));
2470         mouse_gain_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseGain));
2471         mouse_zoom_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseZoom));
2472         mouse_timefx_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseTimeFX));
2473         mouse_audition_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseAudition));
2474         mouse_note_button.signal_toggled().connect (bind (mem_fun(*this, &Editor::mouse_mode_toggled), Editing::MouseNote));
2475
2476         // mouse_move_button.set_active (true);
2477         
2478
2479         /* Zoom */
2480         
2481         zoom_box.set_spacing (1);
2482         zoom_box.set_border_width (2);
2483
2484         zoom_in_button.set_name ("EditorTimeButton");
2485         zoom_in_button.set_size_request(-1,16);
2486         zoom_in_button.add (*(manage (new Image (::get_icon("zoom_in")))));
2487         zoom_in_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::temporal_zoom_step), false));
2488         ARDOUR_UI::instance()->tooltips().set_tip (zoom_in_button, _("Zoom In"));
2489         
2490         zoom_out_button.set_name ("EditorTimeButton");
2491         zoom_out_button.set_size_request(-1,16);
2492         zoom_out_button.add (*(manage (new Image (::get_icon("zoom_out")))));
2493         zoom_out_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::temporal_zoom_step), true));
2494         ARDOUR_UI::instance()->tooltips().set_tip (zoom_out_button, _("Zoom Out"));
2495
2496         zoom_out_full_button.set_name ("EditorTimeButton");
2497         zoom_out_full_button.set_size_request(-1,16);
2498         zoom_out_full_button.add (*(manage (new Image (::get_icon("zoom_full")))));
2499         zoom_out_full_button.signal_clicked().connect (mem_fun(*this, &Editor::temporal_zoom_session));
2500         ARDOUR_UI::instance()->tooltips().set_tip (zoom_out_full_button, _("Zoom to Session"));
2501
2502         zoom_focus_selector.set_name ("ZoomFocusSelector");
2503         Gtkmm2ext::set_size_request_to_display_given_text (zoom_focus_selector, "Center", FUDGE, 0);
2504         set_popdown_strings (zoom_focus_selector, zoom_focus_strings);
2505         zoom_focus_selector.signal_changed().connect (mem_fun(*this, &Editor::zoom_focus_selection_done));
2506         ARDOUR_UI::instance()->tooltips().set_tip (zoom_focus_selector, _("Zoom focus"));
2507
2508         zoom_box.pack_start (zoom_focus_selector, true, true);
2509         zoom_box.pack_start (zoom_out_button, false, false);
2510         zoom_box.pack_start (zoom_in_button, false, false);
2511         zoom_box.pack_start (zoom_out_full_button, false, false);
2512
2513         /* Edit Cursor / Snap */
2514
2515         snap_box.set_spacing (1);
2516         snap_box.set_border_width (2);
2517
2518         snap_type_selector.set_name ("SnapTypeSelector");
2519         Gtkmm2ext::set_size_request_to_display_given_text (snap_type_selector, "SMPTE Seconds", 2+FUDGE, 10);
2520         set_popdown_strings (snap_type_selector, snap_type_strings);
2521         snap_type_selector.signal_changed().connect (mem_fun(*this, &Editor::snap_type_selection_done));
2522         ARDOUR_UI::instance()->tooltips().set_tip (snap_type_selector, _("Unit to snap cursors and ranges to"));
2523
2524         snap_mode_selector.set_name ("SnapModeSelector");
2525         Gtkmm2ext::set_size_request_to_display_given_text (snap_mode_selector, "Magnetic Snap", 2+FUDGE, 10);
2526         set_popdown_strings (snap_mode_selector, snap_mode_strings);
2527         snap_mode_selector.signal_changed().connect (mem_fun(*this, &Editor::snap_mode_selection_done));
2528
2529         snap_box.pack_start (edit_cursor_clock, false, false);
2530         snap_box.pack_start (snap_mode_selector, false, false);
2531         snap_box.pack_start (snap_type_selector, false, false);
2532
2533
2534         /* Nudge */
2535
2536         HBox *nudge_box = manage (new HBox);
2537         nudge_box->set_spacing(1);
2538         nudge_box->set_border_width (2);
2539
2540         nudge_forward_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::nudge_forward), false));
2541         nudge_backward_button.signal_clicked().connect (bind (mem_fun(*this, &Editor::nudge_backward), false));
2542
2543         nudge_box->pack_start (nudge_backward_button, false, false);
2544         nudge_box->pack_start (nudge_forward_button, false, false);
2545         nudge_box->pack_start (nudge_clock, false, false);
2546
2547
2548         /* Pack everything in... */
2549
2550         HBox* hbox = new HBox;
2551         hbox->set_spacing(10);
2552
2553         tools_tearoff = new TearOff (*hbox);
2554         tools_tearoff->set_name ("MouseModeBase");
2555
2556         tools_tearoff->Detach.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox), 
2557                                              &tools_tearoff->tearoff_window()));
2558         tools_tearoff->Attach.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox), 
2559                                              &tools_tearoff->tearoff_window(), 0));
2560         tools_tearoff->Hidden.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&toolbar_hbox), 
2561                                              &tools_tearoff->tearoff_window()));
2562         tools_tearoff->Visible.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&toolbar_hbox), 
2563                                               &tools_tearoff->tearoff_window(), 0));
2564
2565         toolbar_hbox.set_spacing (10);
2566         toolbar_hbox.set_border_width (1);
2567
2568         toolbar_hbox.pack_start (*mouse_mode_tearoff, false, false);
2569         toolbar_hbox.pack_start (*tools_tearoff, false, false);
2570
2571         
2572         hbox->pack_start (snap_box, false, false);
2573         // hbox->pack_start (zoom_box, false, false); 
2574         hbox->pack_start (*nudge_box, false, false);
2575
2576         hbox->show_all ();
2577         
2578         toolbar_base.set_name ("ToolBarBase");
2579         toolbar_base.add (toolbar_hbox);
2580
2581         toolbar_frame.set_shadow_type (SHADOW_OUT);
2582         toolbar_frame.set_name ("BaseFrame");
2583         toolbar_frame.add (toolbar_base);
2584 }
2585
2586
2587 void
2588 Editor::setup_midi_toolbar ()
2589 {
2590         string pixmap_path;
2591
2592         /* Mode Buttons (tool selection) */
2593
2594         vector<ToggleButton *> midi_tool_buttons;
2595
2596         midi_tool_select_button.add (*(manage (new Image (::get_icon("midi_tool_select")))));
2597         midi_tool_select_button.set_relief(Gtk::RELIEF_NONE);
2598         midi_tool_buttons.push_back (&midi_tool_select_button);
2599         midi_tool_pencil_button.add (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2600         midi_tool_pencil_button.set_relief(Gtk::RELIEF_NONE);
2601         midi_tool_buttons.push_back (&midi_tool_pencil_button);
2602         midi_tool_erase_button.add (*(manage (new Image (::get_icon("midi_tool_erase")))));
2603         midi_tool_erase_button.set_relief(Gtk::RELIEF_NONE);
2604         midi_tool_buttons.push_back (&midi_tool_erase_button);
2605
2606         midi_tool_select_button.set_active(true);
2607         
2608         midi_tool_button_set = new GroupedButtons (midi_tool_buttons);
2609
2610         midi_tool_button_box.set_border_width (2);
2611         midi_tool_button_box.set_spacing(4);
2612         midi_tool_button_box.set_spacing(1);
2613         midi_tool_button_box.pack_start(midi_tool_select_button, true, true);
2614         midi_tool_button_box.pack_start(midi_tool_pencil_button, true, true);
2615         midi_tool_button_box.pack_start(midi_tool_erase_button, true, true);
2616         midi_tool_button_box.set_homogeneous(true);
2617
2618         midi_tool_select_button.set_name ("MouseModeButton");
2619         midi_tool_pencil_button.set_name ("MouseModeButton");
2620         midi_tool_erase_button.set_name ("MouseModeButton");
2621
2622         ARDOUR_UI::instance()->tooltips().set_tip (midi_tool_select_button, _("Select/Move Notes"));
2623         ARDOUR_UI::instance()->tooltips().set_tip (midi_tool_pencil_button, _("Add/Move/Stretch Notes"));
2624         ARDOUR_UI::instance()->tooltips().set_tip (midi_tool_erase_button, _("Erase Notes"));
2625
2626         midi_tool_select_button.unset_flags (CAN_FOCUS);
2627         midi_tool_pencil_button.unset_flags (CAN_FOCUS);
2628         midi_tool_erase_button.unset_flags (CAN_FOCUS);
2629         
2630         midi_tool_select_button.signal_toggled().connect (bind (mem_fun(*this,
2631                                 &Editor::midi_edit_mode_toggled), Editing::MidiEditSelect));
2632         midi_tool_pencil_button.signal_toggled().connect (bind (mem_fun(*this,
2633                                 &Editor::midi_edit_mode_toggled), Editing::MidiEditPencil));
2634         midi_tool_erase_button.signal_toggled().connect (bind (mem_fun(*this,
2635                                 &Editor::midi_edit_mode_toggled), Editing::MidiEditErase));
2636         
2637         /* Pack everything in... */
2638
2639         midi_tools_tearoff = new TearOff (midi_tool_button_box);
2640         midi_tools_tearoff->set_name ("MouseModeBase");
2641
2642         /*
2643         midi_tools_tearoff->Detach.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&midi_toolbar_hbox), 
2644                                              &midi_tools_tearoff->tearoff_window()));
2645         midi_tools_tearoff->Attach.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&midi_toolbar_hbox), 
2646                                              &midi_tools_tearoff->tearoff_window(), 0));
2647         midi_tools_tearoff->Hidden.connect (bind (mem_fun(*this, &Editor::detach_tearoff), static_cast<Box*>(&midi_toolbar_hbox), 
2648                                              &midi_tools_tearoff->tearoff_window()));
2649         midi_tools_tearoff->Visible.connect (bind (mem_fun(*this, &Editor::reattach_tearoff), static_cast<Box*> (&midi_toolbar_hbox), 
2650                                               &midi_tools_tearoff->tearoff_window(), 0));
2651         */
2652
2653         midi_toolbar_hbox.set_spacing (10);
2654         midi_toolbar_hbox.set_border_width (1);
2655
2656         midi_toolbar_hbox.pack_start (*midi_tools_tearoff, false, true);
2657
2658         midi_tool_button_box.show_all ();
2659         midi_toolbar_hbox.show_all();
2660         midi_tools_tearoff->show_all();
2661         
2662         midi_toolbar_base.set_name ("ToolBarBase");
2663         midi_toolbar_base.add (midi_toolbar_hbox);
2664
2665         midi_toolbar_frame.set_shadow_type (SHADOW_OUT);
2666         midi_toolbar_frame.set_name ("BaseFrame");
2667         midi_toolbar_frame.add (midi_toolbar_base);
2668 }
2669
2670 int
2671 Editor::convert_drop_to_paths (vector<ustring>& paths, 
2672                                const RefPtr<Gdk::DragContext>& context,
2673                                gint                x,
2674                                gint                y,
2675                                const SelectionData& data,
2676                                guint               info,
2677                                guint               time)                               
2678
2679 {       
2680         if (session == 0) {
2681                 return -1;
2682         }
2683
2684         vector<ustring> uris = data.get_uris();
2685
2686         if (uris.empty()) {
2687                 
2688                 /* This is seriously fucked up. Nautilus doesn't say that its URI lists
2689                    are actually URI lists. So do it by hand.
2690                 */
2691
2692                 if (data.get_target() != "text/plain") {
2693                         return -1;
2694                 }
2695   
2696                 /* Parse the "uri-list" format that Nautilus provides, 
2697                    where each pathname is delimited by \r\n
2698                 */
2699         
2700                 const char* p = data.get_text().c_str();
2701                 const char* q;
2702
2703                 while (p)
2704                 {
2705                         if (*p != '#')
2706                         {
2707                                 while (g_ascii_isspace (*p))
2708                                         p++;
2709                                 
2710                                 q = p;
2711                                 while (*q && (*q != '\n') && (*q != '\r'))
2712                                         q++;
2713                                 
2714                                 if (q > p)
2715                                 {
2716                                         q--;
2717                                         while (q > p && g_ascii_isspace (*q))
2718                                                 q--;
2719                                         
2720                                         if (q > p)
2721                                         {
2722                                                 uris.push_back (ustring (p, q - p + 1));
2723                                         }
2724                                 }
2725                         }
2726                         p = strchr (p, '\n');
2727                         if (p)
2728                                 p++;
2729                 }
2730
2731                 if (uris.empty()) {
2732                         return -1;
2733                 }
2734         }
2735         
2736         for (vector<ustring>::iterator i = uris.begin(); i != uris.end(); ++i) {
2737                 if ((*i).substr (0,7) == "file://") {
2738                         string p = *i;
2739                         PBD::url_decode (p);
2740                         paths.push_back (p.substr (7));
2741                 }
2742         }
2743
2744         return 0;
2745 }
2746
2747 void
2748 Editor::new_tempo_section ()
2749
2750 {
2751 }
2752
2753 void
2754 Editor::map_transport_state ()
2755 {
2756         ENSURE_GUI_THREAD (mem_fun(*this, &Editor::map_transport_state));
2757
2758         if (session->transport_stopped()) {
2759                 have_pending_keyboard_selection = false;
2760         }
2761
2762         update_loop_range_view (true);
2763 }
2764
2765 /* UNDO/REDO */
2766
2767 Editor::State::State ()
2768 {
2769         selection = new Selection;
2770 }
2771
2772 Editor::State::~State ()
2773 {
2774         delete selection;
2775 }
2776
2777 UndoAction
2778 Editor::get_memento () const
2779 {
2780         State *state = new State;
2781
2782         store_state (*state);
2783         return bind (mem_fun (*(const_cast<Editor*>(this)), &Editor::restore_state), state);
2784 }
2785
2786 void
2787 Editor::store_state (State& state) const
2788 {
2789         *state.selection = *selection;
2790 }
2791
2792 void
2793 Editor::restore_state (State *state)
2794 {
2795         if (*selection == *state->selection) {
2796                 return;
2797         }
2798
2799         *selection = *state->selection;
2800         time_selection_changed ();
2801         region_selection_changed ();   
2802
2803         /* XXX other selection change handlers? */
2804 }
2805
2806 void
2807 Editor::begin_reversible_command (string name)
2808 {
2809         if (session) {
2810                 before = &get_state();
2811                 session->begin_reversible_command (name);
2812         }
2813 }
2814
2815 void
2816 Editor::commit_reversible_command ()
2817 {
2818         if (session) {
2819                 session->commit_reversible_command (new MementoCommand<Editor>(*this, before, &get_state()));
2820         }
2821 }
2822
2823 void
2824 Editor::set_edit_group_solo (Route& route, bool yn)
2825 {
2826         RouteGroup *edit_group;
2827
2828         if ((edit_group = route.edit_group()) != 0) {
2829                 edit_group->apply (&Route::set_solo, yn, this);
2830         } else {
2831                 route.set_solo (yn, this);
2832         }
2833 }
2834
2835 void
2836 Editor::set_edit_group_mute (Route& route, bool yn)
2837 {
2838         RouteGroup *edit_group = 0;
2839
2840         if ((edit_group == route.edit_group()) != 0) {
2841                 edit_group->apply (&Route::set_mute, yn, this);
2842         } else {
2843                 route.set_mute (yn, this);
2844         }
2845 }
2846                 
2847 void
2848 Editor::history_changed ()
2849 {
2850         string label;
2851
2852         if (undo_action && session) {
2853                 if (session->undo_depth() == 0) {
2854                         label = _("Undo");
2855                 } else {
2856                         label = string_compose(_("Undo (%1)"), session->next_undo());
2857                 }
2858                 undo_action->property_label() = label;
2859         }
2860
2861         if (redo_action && session) {
2862                 if (session->redo_depth() == 0) {
2863                         label = _("Redo");
2864                 } else {
2865                         label = string_compose(_("Redo (%1)"), session->next_redo());
2866                 }
2867                 redo_action->property_label() = label;
2868         }
2869 }
2870
2871 void
2872 Editor::duplicate_dialog (bool dup_region)
2873 {
2874         if (selection->regions.empty() && (selection->time.length() == 0)) {
2875                 return;
2876         }
2877
2878         ArdourDialog win ("duplicate dialog");
2879         Label  label (_("Duplicate how many times?"));
2880         Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
2881         SpinButton spinner (adjustment);
2882
2883         win.get_vbox()->set_spacing (12);
2884         win.get_vbox()->pack_start (label);
2885
2886         /* dialogs have ::add_action_widget() but that puts the spinner in the wrong
2887            place, visually. so do this by hand.
2888         */
2889
2890         win.get_vbox()->pack_start (spinner);
2891         spinner.signal_activate().connect (sigc::bind (mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
2892
2893         label.show ();
2894         spinner.show ();
2895
2896         win.add_button (Stock::OK, RESPONSE_ACCEPT);
2897         win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
2898
2899         win.set_position (WIN_POS_MOUSE);
2900
2901         spinner.grab_focus ();
2902
2903         switch (win.run ()) {
2904         case RESPONSE_ACCEPT:
2905                 break;
2906         default:
2907                 return;
2908         }
2909
2910         float times = adjustment.get_value();
2911
2912         if (!selection->regions.empty()) {
2913                 duplicate_some_regions (selection->regions, times);
2914         } else {
2915                 duplicate_selection (times);
2916         }
2917 }
2918
2919 void
2920 Editor::show_verbose_canvas_cursor ()
2921 {
2922         verbose_canvas_cursor->raise_to_top();
2923         verbose_canvas_cursor->show();
2924         verbose_cursor_visible = true;
2925 }
2926
2927 void
2928 Editor::hide_verbose_canvas_cursor ()
2929 {
2930         verbose_canvas_cursor->hide();
2931         verbose_cursor_visible = false;
2932 }
2933
2934 void
2935 Editor::set_verbose_canvas_cursor (const string & txt, double x, double y)
2936 {
2937         /* XXX get origin of canvas relative to root window,
2938            add x and y and check compared to gdk_screen_{width,height}
2939         */
2940         verbose_canvas_cursor->property_text() = txt.c_str();
2941         verbose_canvas_cursor->property_x() = x;
2942         verbose_canvas_cursor->property_y() = y;
2943 }
2944
2945 void
2946 Editor::set_verbose_canvas_cursor_text (const string & txt)
2947 {
2948         verbose_canvas_cursor->property_text() = txt.c_str();
2949 }
2950
2951 void
2952 Editor::edit_mode_selection_done ()
2953 {
2954         if (session == 0) {
2955                 return;
2956         }
2957
2958         string choice = edit_mode_selector.get_active_text();
2959         EditMode mode = Slide;
2960
2961         if (choice == _("Splice Edit")) {
2962                 mode = Splice;
2963         } else if (choice == _("Slide Edit")) {
2964                 mode = Slide;
2965         }
2966
2967         Config->set_edit_mode (mode);
2968 }       
2969
2970 void
2971 Editor::snap_type_selection_done ()
2972 {
2973         string choice = snap_type_selector.get_active_text();
2974         SnapType snaptype = SnapToFrame;
2975
2976         if (choice == _("Beats/3")) {
2977                 snaptype = SnapToAThirdBeat;
2978         } else if (choice == _("Beats/4")) {
2979                 snaptype = SnapToAQuarterBeat;
2980         } else if (choice == _("Beats/8")) {
2981                 snaptype = SnapToAEighthBeat;
2982         } else if (choice == _("Beats/16")) {
2983                 snaptype = SnapToASixteenthBeat;
2984         } else if (choice == _("Beats/32")) {
2985                 snaptype = SnapToAThirtysecondBeat;
2986         } else if (choice == _("Beats")) {
2987                 snaptype = SnapToBeat;
2988         } else if (choice == _("Bars")) {
2989                 snaptype = SnapToBar;
2990         } else if (choice == _("Marks")) {
2991                 snaptype = SnapToMark;
2992         } else if (choice == _("Edit Cursor")) {
2993                 snaptype = SnapToEditCursor;
2994         } else if (choice == _("Region starts")) {
2995                 snaptype = SnapToRegionStart;
2996         } else if (choice == _("Region ends")) {
2997                 snaptype = SnapToRegionEnd;
2998         } else if (choice == _("Region bounds")) {
2999                 snaptype = SnapToRegionBoundary;
3000         } else if (choice == _("Region syncs")) {
3001                 snaptype = SnapToRegionSync;
3002         } else if (choice == _("CD Frames")) {
3003                 snaptype = SnapToCDFrame;
3004         } else if (choice == _("SMPTE Frames")) {
3005                 snaptype = SnapToSMPTEFrame;
3006         } else if (choice == _("SMPTE Seconds")) {
3007                 snaptype = SnapToSMPTESeconds;
3008         } else if (choice == _("SMPTE Minutes")) {
3009                 snaptype = SnapToSMPTEMinutes;
3010         } else if (choice == _("Seconds")) {
3011                 snaptype = SnapToSeconds;
3012         } else if (choice == _("Minutes")) {
3013                 snaptype = SnapToMinutes;
3014         } else if (choice == _("None")) {
3015                 snaptype = SnapToFrame;
3016         }
3017
3018         RefPtr<RadioAction> ract = snap_type_action (snaptype);
3019         if (ract) {
3020                 ract->set_active ();
3021         }
3022 }       
3023
3024 void
3025 Editor::snap_mode_selection_done ()
3026 {
3027         string choice = snap_mode_selector.get_active_text();
3028         SnapMode mode = SnapNormal;
3029
3030         if (choice == _("Normal")) {
3031                 mode = SnapNormal;
3032         } else if (choice == _("Magnetic")) {
3033                 mode = SnapMagnetic;
3034         }
3035
3036         RefPtr<RadioAction> ract = snap_mode_action (mode);
3037
3038         if (ract) {
3039                 ract->set_active (true);
3040         }
3041 }
3042
3043 void
3044 Editor::zoom_focus_selection_done ()
3045 {
3046         string choice = zoom_focus_selector.get_active_text();
3047         ZoomFocus focus_type = ZoomFocusLeft;
3048
3049         if (choice == _("Left")) {
3050                 focus_type = ZoomFocusLeft;
3051         } else if (choice == _("Right")) {
3052                 focus_type = ZoomFocusRight;
3053         } else if (choice == _("Center")) {
3054                 focus_type = ZoomFocusCenter;
3055         } else if (choice == _("Play")) {
3056                 focus_type = ZoomFocusPlayhead;
3057         } else if (choice == _("Edit")) {
3058                 focus_type = ZoomFocusEdit;
3059         } 
3060         
3061         RefPtr<RadioAction> ract = zoom_focus_action (focus_type);
3062
3063         if (ract) {
3064                 ract->set_active ();
3065         }
3066 }       
3067
3068 gint
3069 Editor::edit_controls_button_release (GdkEventButton* ev)
3070 {
3071         if (Keyboard::is_context_menu_event (ev)) {
3072                 ARDOUR_UI::instance()->add_route (this);
3073         }
3074         return TRUE;
3075 }
3076
3077 gint
3078 Editor::mouse_select_button_release (GdkEventButton* ev)
3079 {
3080         /* this handles just right-clicks */
3081
3082         if (ev->button != 3) {
3083                 return false;
3084         }
3085
3086         return true;
3087 }
3088
3089 Editor::TrackViewList *
3090 Editor::get_valid_views (TimeAxisView* track, RouteGroup* group)
3091 {
3092         TrackViewList *v;
3093         TrackViewList::iterator i;
3094
3095         v = new TrackViewList;
3096
3097         if (track == 0 && group == 0) {
3098
3099                 /* all views */
3100
3101                 for (i = track_views.begin(); i != track_views.end (); ++i) {
3102                         v->push_back (*i);
3103                 }
3104
3105         } else if (track != 0 && group == 0 || (track != 0 && group != 0 && !group->is_active())) {
3106                 
3107                 /* just the view for this track
3108                  */
3109
3110                 v->push_back (track);
3111
3112         } else {
3113                 
3114                 /* views for all tracks in the edit group */
3115                 
3116                 for (i  = track_views.begin(); i != track_views.end (); ++i) {
3117
3118                         if (group == 0 || (*i)->edit_group() == group) {
3119                                 v->push_back (*i);
3120                         }
3121                 }
3122         }
3123         
3124         return v;
3125 }
3126
3127 void
3128 Editor::set_zoom_focus (ZoomFocus f)
3129 {
3130         string str = zoom_focus_strings[(int)f];
3131
3132         if (str != zoom_focus_selector.get_active_text()) {
3133                 zoom_focus_selector.set_active_text (str);
3134         }
3135         
3136         if (zoom_focus != f) {
3137                 zoom_focus = f;
3138
3139                 ZoomFocusChanged (); /* EMIT_SIGNAL */
3140
3141                 instant_save ();
3142         }
3143 }
3144
3145 void
3146 Editor::ensure_float (Window& win)
3147 {
3148         win.set_transient_for (*this);
3149 }
3150
3151 void 
3152 Editor::pane_allocation_handler (Allocation &alloc, Paned* which)
3153 {
3154         /* recover or initialize pane positions. do this here rather than earlier because
3155            we don't want the positions to change the child allocations, which they seem to do.
3156          */
3157
3158         int pos;
3159         XMLProperty* prop;
3160         char buf[32];
3161         XMLNode* node = ARDOUR_UI::instance()->editor_settings();
3162         int width, height;
3163         static int32_t done;
3164         XMLNode* geometry;
3165
3166         if ((geometry = find_named_node (*node, "geometry")) == 0) {
3167                 width = default_width;
3168                 height = default_height;
3169         } else {
3170                 width = atoi(geometry->property("x_size")->value());
3171                 height = atoi(geometry->property("y_size")->value());
3172         }
3173
3174         if (which == static_cast<Paned*> (&edit_pane)) {
3175
3176                 if (done) {
3177                         return;
3178                 }
3179
3180                 if (!geometry || (prop = geometry->property ("edit_pane_pos")) == 0) {
3181                         /* initial allocation is 90% to canvas, 10% to notebook */
3182                         pos = (int) floor (alloc.get_width() * 0.90f);
3183                         snprintf (buf, sizeof(buf), "%d", pos);
3184                 } else {
3185                         pos = atoi (prop->value());
3186                 }
3187                 
3188                 if ((done = GTK_WIDGET(edit_pane.gobj())->allocation.width > pos)) {
3189                         edit_pane.set_position (pos);
3190                         pre_maximal_pane_position = pos;
3191                 }
3192         }
3193 }
3194
3195 void
3196 Editor::detach_tearoff (Box* b, Window* w)
3197 {
3198         if (tools_tearoff->torn_off() && 
3199             mouse_mode_tearoff->torn_off()) {
3200                 top_hbox.remove (toolbar_frame);
3201         }
3202 }
3203
3204 void
3205 Editor::reattach_tearoff (Box* b, Window* w, int32_t n)
3206 {
3207         if (toolbar_frame.get_parent() == 0) {
3208                 top_hbox.pack_end (toolbar_frame);
3209         }
3210 }
3211
3212 void
3213 Editor::set_show_measures (bool yn)
3214 {
3215         if (_show_measures != yn) {
3216                 hide_measures ();
3217
3218                 if ((_show_measures = yn) == true) {
3219                         draw_measures ();
3220                 }
3221                 instant_save ();
3222         }
3223 }
3224
3225 void
3226 Editor::toggle_follow_playhead ()
3227 {
3228         RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
3229         if (act) {
3230                 RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
3231                 set_follow_playhead (tact->get_active());
3232         }
3233 }
3234
3235 void
3236 Editor::set_follow_playhead (bool yn)
3237 {
3238         if (_follow_playhead != yn) {
3239                 if ((_follow_playhead = yn) == true) {
3240                         /* catch up */
3241                         update_current_screen ();
3242                 }
3243                 instant_save ();
3244         }
3245 }
3246
3247 void
3248 Editor::toggle_xfade_active (boost::weak_ptr<Crossfade> wxfade)
3249 {
3250         boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3251         if (xfade) {
3252                 xfade->set_active (!xfade->active());
3253         }
3254 }
3255
3256 void
3257 Editor::toggle_xfade_length (boost::weak_ptr<Crossfade> wxfade)
3258 {
3259         boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3260         if (xfade) {
3261                 xfade->set_follow_overlap (!xfade->following_overlap());
3262         }
3263 }
3264
3265 void
3266 Editor::edit_xfade (boost::weak_ptr<Crossfade> wxfade)
3267 {
3268         boost::shared_ptr<Crossfade> xfade (wxfade.lock());
3269
3270         if (!xfade) {
3271                 return;
3272         }
3273
3274         CrossfadeEditor cew (*session, xfade, xfade->fade_in().get_min_y(), 1.0);
3275                 
3276         ensure_float (cew);
3277         
3278         switch (cew.run ()) {
3279         case RESPONSE_ACCEPT:
3280                 break;
3281         default:
3282                 return;
3283         }
3284         
3285         cew.apply ();
3286         xfade->StateChanged (Change (~0));
3287 }
3288
3289 PlaylistSelector&
3290 Editor::playlist_selector () const
3291 {
3292         return *_playlist_selector;
3293 }
3294
3295 nframes_t
3296 Editor::get_nudge_distance (nframes_t pos, nframes_t& next)
3297 {
3298         nframes_t ret;
3299
3300         ret = nudge_clock.current_duration (pos);
3301         next = ret + 1; /* XXXX fix me */
3302
3303         return ret;
3304 }
3305
3306 void
3307 Editor::end_location_changed (Location* location)
3308 {
3309         ENSURE_GUI_THREAD (bind (mem_fun(*this, &Editor::end_location_changed), location));
3310         reset_scrolling_region ();
3311 }
3312
3313 int
3314 Editor::playlist_deletion_dialog (boost::shared_ptr<Playlist> pl)
3315 {
3316         ArdourDialog dialog ("playlist deletion dialog");
3317         Label  label (string_compose (_("Playlist %1 is currently unused.\n"
3318                                         "If left alone, no audio files used by it will be cleaned.\n"
3319                                         "If deleted, audio files used by it alone by will cleaned."),
3320                                       pl->name()));
3321         
3322         dialog.set_position (WIN_POS_CENTER);
3323         dialog.get_vbox()->pack_start (label);
3324
3325         label.show ();
3326
3327         dialog.add_button (_("Delete playlist"), RESPONSE_ACCEPT);
3328         dialog.add_button (_("Keep playlist"), RESPONSE_REJECT);
3329         dialog.add_button (_("Cancel"), RESPONSE_CANCEL);
3330
3331         switch (dialog.run ()) {
3332         case RESPONSE_ACCEPT:
3333                 /* delete the playlist */
3334                 return 0;
3335                 break;
3336
3337         case RESPONSE_REJECT:
3338                 /* keep the playlist */
3339                 return 1;
3340                 break;
3341
3342         default:
3343                 break;
3344         }
3345
3346         return -1;
3347 }
3348
3349 bool
3350 Editor::audio_region_selection_covers (nframes_t where)
3351 {
3352         for (RegionSelection::iterator a = selection->regions.begin(); a != selection->regions.end(); ++a) {
3353                 if ((*a)->region()->covers (where)) {
3354                         return true;
3355                 }
3356         }
3357
3358         return false;
3359 }
3360
3361 void
3362 Editor::prepare_for_cleanup ()
3363 {
3364         cut_buffer->clear_regions ();
3365         cut_buffer->clear_playlists ();
3366
3367         selection->clear_regions ();
3368         selection->clear_playlists ();
3369 }
3370
3371 Location*
3372 Editor::transport_loop_location()
3373 {
3374         if (session) {
3375                 return session->locations()->auto_loop_location();
3376         } else {
3377                 return 0;
3378         }
3379 }
3380
3381 Location*
3382 Editor::transport_punch_location()
3383 {
3384         if (session) {
3385                 return session->locations()->auto_punch_location();
3386         } else {
3387                 return 0;
3388         }
3389 }
3390
3391 bool
3392 Editor::control_layout_scroll (GdkEventScroll* ev)
3393 {
3394         switch (ev->direction) {
3395         case GDK_SCROLL_UP:
3396                 scroll_tracks_up_line ();
3397                 return true;
3398                 break;
3399
3400         case GDK_SCROLL_DOWN:
3401                 scroll_tracks_down_line ();
3402                 return true;
3403                 
3404         default:
3405                 /* no left/right handling yet */
3406                 break;
3407         }
3408
3409         return false;
3410 }
3411
3412
3413 /** A new snapshot has been selected.
3414  */
3415 void
3416 Editor::snapshot_display_selection_changed ()
3417 {
3418         if (snapshot_display.get_selection()->count_selected_rows() > 0) {
3419
3420                 TreeModel::iterator i = snapshot_display.get_selection()->get_selected();
3421                 
3422                 Glib::ustring snap_name = (*i)[snapshot_display_columns.real_name];
3423
3424                 if (snap_name.length() == 0) {
3425                         return;
3426                 }
3427                 
3428                 if (session->snap_name() == snap_name) {
3429                         return;
3430                 }
3431                 
3432                 ARDOUR_UI::instance()->load_session(session->path(), string (snap_name));
3433         }
3434 }
3435
3436 bool
3437 Editor::snapshot_display_button_press (GdkEventButton* ev)
3438 {
3439         if (ev->button == 3) {
3440                 /* Right-click on the snapshot list. Work out which snapshot it
3441                    was over. */
3442                 Gtk::TreeModel::Path path;
3443                 Gtk::TreeViewColumn* col;
3444                 int cx;
3445                 int cy;
3446                 snapshot_display.get_path_at_pos ((int) ev->x, (int) ev->y, path, col, cx, cy);
3447                 Gtk::TreeModel::iterator iter = snapshot_display_model->get_iter (path);
3448                 if (iter) {
3449                         Gtk::TreeModel::Row row = *iter;
3450                         popup_snapshot_context_menu (ev->button, ev->time, row[snapshot_display_columns.real_name]);
3451                 }
3452                 return true;
3453         }
3454
3455         return false;
3456 }
3457
3458
3459 /** Pop up the snapshot display context menu.
3460  * @param button Button used to open the menu.
3461  * @param time Menu open time.
3462  * @snapshot_name Name of the snapshot that the menu click was over.
3463  */
3464
3465 void
3466 Editor::popup_snapshot_context_menu (int button, int32_t time, Glib::ustring snapshot_name)
3467 {
3468         using namespace Menu_Helpers;
3469
3470         MenuList& items (snapshot_context_menu.items());
3471         items.clear ();
3472
3473         const bool modification_allowed = (session->snap_name() != snapshot_name && session->name() != snapshot_name);
3474
3475         add_item_with_sensitivity (items, MenuElem (_("Remove"), bind (mem_fun (*this, &Editor::remove_snapshot), snapshot_name)), modification_allowed);
3476
3477         add_item_with_sensitivity (items, MenuElem (_("Rename"), bind (mem_fun (*this, &Editor::rename_snapshot), snapshot_name)), modification_allowed);
3478
3479         snapshot_context_menu.popup (button, time);
3480 }
3481
3482 void
3483 Editor::rename_snapshot (Glib::ustring old_name)
3484 {
3485         ArdourPrompter prompter(true);
3486
3487         string new_name;
3488
3489         prompter.set_name ("Prompter");
3490         prompter.add_button (Gtk::Stock::SAVE, Gtk::RESPONSE_ACCEPT);
3491         prompter.set_prompt (_("New name of snapshot"));
3492         prompter.set_initial_text (old_name);
3493         
3494         if (prompter.run() == RESPONSE_ACCEPT) {
3495                 prompter.get_result (new_name);
3496                 if (new_name.length()) {
3497                         session->rename_state (old_name, new_name);
3498                         redisplay_snapshots ();
3499                 }
3500         }
3501 }
3502
3503
3504 void
3505 Editor::remove_snapshot (Glib::ustring name)
3506 {
3507         vector<string> choices;
3508
3509         std::string prompt  = string_compose (_("Do you really want to remove snapshot \"%1\" ?\n(cannot be undone)"), name);
3510
3511         choices.push_back (_("No, do nothing."));
3512         choices.push_back (_("Yes, remove it."));
3513
3514         Gtkmm2ext::Choice prompter (prompt, choices);
3515
3516         if (prompter.run () == 1) {
3517                 session->remove_state (name);
3518                 redisplay_snapshots ();
3519         }
3520 }
3521
3522 void
3523 Editor::redisplay_snapshots ()
3524 {
3525         if (session == 0) {
3526                 return;
3527         }
3528
3529         vector<string*>* states;
3530
3531         if ((states = session->possible_states()) == 0) {
3532                 return;
3533         }
3534
3535         snapshot_display_model->clear ();
3536
3537         for (vector<string*>::iterator i = states->begin(); i != states->end(); ++i) {
3538                 string statename = *(*i);
3539                 TreeModel::Row row = *(snapshot_display_model->append());
3540                 
3541                 /* this lingers on in case we ever want to change the visible
3542                    name of the snapshot.
3543                 */
3544                 
3545                 string display_name;
3546                 display_name = statename;
3547
3548                 if (statename == session->snap_name()) {
3549                         snapshot_display.get_selection()->select(row);
3550                 } 
3551                 
3552                 row[snapshot_display_columns.visible_name] = display_name;
3553                 row[snapshot_display_columns.real_name] = statename;
3554         }
3555
3556         delete states;
3557 }
3558
3559 void
3560 Editor::session_state_saved (string snap_name)
3561 {
3562         ENSURE_GUI_THREAD (bind (mem_fun(*this, &Editor::session_state_saved), snap_name));
3563         redisplay_snapshots ();
3564 }
3565
3566 void
3567 Editor::maximise_editing_space ()
3568 {
3569         initial_ruler_update_required = true;
3570
3571         mouse_mode_tearoff->set_visible (false);
3572         tools_tearoff->set_visible (false);
3573
3574         pre_maximal_pane_position = edit_pane.get_position();
3575         pre_maximal_editor_width = this->get_width();
3576
3577         if(post_maximal_pane_position == 0) {
3578                 post_maximal_pane_position = edit_pane.get_width();
3579         }
3580
3581
3582         fullscreen();
3583         if(post_maximal_editor_width) {
3584                 edit_pane.set_position (post_maximal_pane_position - 
3585                         abs(post_maximal_editor_width - pre_maximal_editor_width));
3586         } else {
3587                 edit_pane.set_position (post_maximal_pane_position);
3588         }
3589 }
3590
3591 void
3592 Editor::restore_editing_space ()
3593 {
3594         initial_ruler_update_required = true;
3595
3596         // user changed width of pane during fullscreen
3597         if(post_maximal_pane_position != edit_pane.get_position()) {
3598                 post_maximal_pane_position = edit_pane.get_position();
3599         }
3600
3601         unfullscreen();
3602
3603         mouse_mode_tearoff->set_visible (true);
3604         tools_tearoff->set_visible (true);
3605         post_maximal_editor_width = this->get_width();
3606
3607
3608         edit_pane.set_position (
3609                 pre_maximal_pane_position + abs(this->get_width() - pre_maximal_editor_width)
3610         );
3611 }
3612
3613 void 
3614 Editor::new_playlists ()
3615 {
3616         begin_reversible_command (_("new playlists"));
3617         mapover_tracks (mem_fun (*this, &Editor::mapped_use_new_playlist));
3618         commit_reversible_command ();
3619 }
3620
3621 void
3622 Editor::copy_playlists ()
3623 {
3624         begin_reversible_command (_("copy playlists"));
3625         mapover_tracks (mem_fun (*this, &Editor::mapped_use_copy_playlist));
3626         commit_reversible_command ();
3627 }
3628
3629 void 
3630 Editor::clear_playlists ()
3631 {
3632         begin_reversible_command (_("clear playlists"));
3633         mapover_tracks (mem_fun (*this, &Editor::mapped_clear_playlist));
3634         commit_reversible_command ();
3635 }
3636
3637 void 
3638 Editor::mapped_use_new_playlist (RouteTimeAxisView& atv, uint32_t sz)
3639 {
3640         atv.use_new_playlist (sz > 1 ? false : true);
3641 }
3642
3643 void
3644 Editor::mapped_use_copy_playlist (RouteTimeAxisView& atv, uint32_t sz)
3645 {
3646         atv.use_copy_playlist (sz > 1 ? false : true);
3647 }
3648
3649 void 
3650 Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t sz)
3651 {
3652         atv.clear_playlist ();
3653 }
3654
3655 bool
3656 Editor::on_key_press_event (GdkEventKey* ev)
3657 {
3658         return key_press_focus_accelerator_handler (*this, ev);
3659 }
3660
3661 void
3662 Editor::reset_x_origin (nframes_t frame)
3663 {
3664         queue_visual_change (frame);
3665 }
3666
3667 void
3668 Editor::reset_zoom (double fpu)
3669 {
3670         queue_visual_change (fpu);
3671 }
3672
3673 void
3674 Editor::reposition_and_zoom (nframes_t frame, double fpu)
3675 {
3676         reset_x_origin (frame);
3677         reset_zoom (fpu);
3678 }
3679
3680 void
3681 Editor::set_frames_per_unit (double fpu)
3682 {
3683         nframes_t frames;
3684
3685         /* this is the core function that controls the zoom level of the canvas. it is called
3686            whenever one or more calls are made to reset_zoom(). it executes in an idle handler.
3687         */
3688
3689         if (fpu == frames_per_unit) {
3690                 return;
3691         }
3692
3693         if (fpu < 2.0) {
3694                 fpu = 2.0;
3695         }
3696
3697         // convert fpu to frame count
3698
3699         frames = (nframes_t) floor (fpu * canvas_width);
3700         
3701         /* don't allow zooms that fit more than the maximum number
3702            of frames into an 800 pixel wide space.
3703         */
3704
3705         if (max_frames / fpu < 800.0) {
3706                 return;
3707         }
3708
3709         if (fpu == frames_per_unit) {
3710                 return;
3711         }
3712
3713         frames_per_unit = fpu;
3714
3715         if (frames != zoom_range_clock.current_duration()) {
3716                 zoom_range_clock.set (frames);
3717         }
3718
3719         if (mouse_mode == MouseRange && selection->time.start () != selection->time.end_frame ()) {
3720                 if (!selection->tracks.empty()) {
3721                         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
3722                                 (*i)->reshow_selection (selection->time);
3723                         }
3724                 } else {
3725                         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
3726                                 (*i)->reshow_selection (selection->time);
3727                         }
3728                 }
3729         }
3730
3731         ZoomChanged (); /* EMIT_SIGNAL */
3732
3733         reset_hscrollbar_stepping ();
3734         reset_scrolling_region ();
3735         
3736         if (edit_cursor) edit_cursor->set_position (edit_cursor->current_frame);
3737         if (playhead_cursor) playhead_cursor->set_position (playhead_cursor->current_frame);
3738
3739         instant_save ();
3740 }
3741
3742 void
3743 Editor::queue_visual_change (nframes_t where)
3744 {
3745         pending_visual_change.pending = VisualChange::Type (pending_visual_change.pending | VisualChange::TimeOrigin);
3746         pending_visual_change.time_origin = where;
3747
3748         if (pending_visual_change.idle_handler_id < 0) {
3749                 pending_visual_change.idle_handler_id = g_idle_add (_idle_visual_changer, this);
3750         }
3751 }
3752
3753 void
3754 Editor::queue_visual_change (double fpu)
3755 {
3756         pending_visual_change.pending = VisualChange::Type (pending_visual_change.pending | VisualChange::ZoomLevel);
3757         pending_visual_change.frames_per_unit = fpu;
3758
3759         if (pending_visual_change.idle_handler_id < 0) {
3760                 pending_visual_change.idle_handler_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE, _idle_visual_changer, this, 0);
3761         }
3762 }
3763
3764 int
3765 Editor::_idle_visual_changer (void* arg)
3766 {
3767         return static_cast<Editor*>(arg)->idle_visual_changer ();
3768 }
3769
3770 int
3771 Editor::idle_visual_changer ()
3772 {
3773         VisualChange::Type p = pending_visual_change.pending;
3774
3775         pending_visual_change.pending = (VisualChange::Type) 0;
3776         pending_visual_change.idle_handler_id = -1;
3777         
3778         if (p & VisualChange::ZoomLevel) {
3779                 set_frames_per_unit (pending_visual_change.frames_per_unit);
3780         }
3781
3782         if (p & VisualChange::TimeOrigin) {
3783                 if (pending_visual_change.time_origin != leftmost_frame) {
3784                         horizontal_adjustment.set_value (pending_visual_change.time_origin/frames_per_unit);
3785                         /* the signal handler will do the rest */
3786                 } else {
3787                         update_fixed_rulers();
3788                         redisplay_tempo (true);
3789                 }
3790         }
3791
3792         return 0; /* this is always a one-shot call */
3793 }
3794
3795 struct EditorOrderTimeAxisSorter {
3796     bool operator() (const TimeAxisView* a, const TimeAxisView* b) const {
3797             return a->order < b->order;
3798     }
3799 };
3800         
3801 void
3802 Editor::sort_track_selection ()
3803 {
3804         EditorOrderTimeAxisSorter cmp;
3805         selection->tracks.sort (cmp);
3806 }
3807
3808 nframes_t
3809 Editor::edit_cursor_position(bool sync)
3810 {
3811         if (sync && edit_cursor->current_frame != edit_cursor_clock.current_time()) {
3812                 edit_cursor_clock.set(edit_cursor->current_frame, true);
3813         }
3814
3815         return edit_cursor->current_frame;
3816 }
3817