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