Make draw tool add points to track automation.
[ardour.git] / gtk2_ardour / editor_mouse.cc
1 /*
2     Copyright (C) 2000-2001 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 #include <cassert>
21 #include <cstdlib>
22 #include <stdint.h>
23 #include <cmath>
24 #include <set>
25 #include <string>
26 #include <algorithm>
27 #include <bitset>
28
29 #include "pbd/error.h"
30 #include "pbd/enumwriter.h"
31 #include "pbd/memento_command.h"
32 #include "pbd/basename.h"
33 #include "pbd/stateful_diff_command.h"
34
35 #include "gtkmm2ext/bindings.h"
36 #include "gtkmm2ext/utils.h"
37 #include "gtkmm2ext/tearoff.h"
38
39 #include "canvas/canvas.h"
40
41 #include "ardour/audioregion.h"
42 #include "ardour/operations.h"
43 #include "ardour/playlist.h"
44 #include "ardour/profile.h"
45 #include "ardour/region_factory.h"
46 #include "ardour/route.h"
47 #include "ardour/session.h"
48 #include "ardour/types.h"
49
50 #include "ardour_ui.h"
51 #include "actions.h"
52 #include "editor.h"
53 #include "time_axis_view.h"
54 #include "audio_time_axis.h"
55 #include "audio_region_view.h"
56 #include "midi_region_view.h"
57 #include "marker.h"
58 #include "streamview.h"
59 #include "region_gain_line.h"
60 #include "automation_time_axis.h"
61 #include "control_point.h"
62 #include "prompter.h"
63 #include "selection.h"
64 #include "keyboard.h"
65 #include "editing.h"
66 #include "rgb_macros.h"
67 #include "control_point_dialog.h"
68 #include "editor_drag.h"
69 #include "automation_region_view.h"
70 #include "edit_note_dialog.h"
71 #include "mouse_cursors.h"
72 #include "editor_cursors.h"
73 #include "verbose_cursor.h"
74 #include "note.h"
75
76 #include "i18n.h"
77
78 using namespace std;
79 using namespace ARDOUR;
80 using namespace PBD;
81 using namespace Gtk;
82 using namespace Editing;
83 using Gtkmm2ext::Keyboard;
84
85 bool
86 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
87 {
88         /* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
89            pays attentions to subwindows. this means that menu windows are ignored, and 
90            if the pointer is in a menu, the return window from the call will be the
91            the regular subwindow *under* the menu.
92
93            this matters quite a lot if the pointer is moving around in a menu that overlaps
94            the track canvas because we will believe that we are within the track canvas
95            when we are not. therefore, we track enter/leave events for the track canvas
96            and allow that to override the result of gdk_window_get_pointer().
97         */
98
99         if (!within_track_canvas) {
100                 return false;
101         }
102
103         int x, y;
104         Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas->get_window();
105
106         if (!canvas_window) {
107                 return false;
108         }
109
110         Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
111
112         if (!pointer_window) {
113                 return false;
114         }
115
116         if (pointer_window != canvas_window) {
117                 in_track_canvas = false;
118                 return false;
119         }
120
121         in_track_canvas = true;
122
123         GdkEvent event;
124         event.type = GDK_BUTTON_RELEASE;
125         event.button.x = x;
126         event.button.y = y;
127
128         where = window_event_sample (&event, 0, 0);
129
130         return true;
131 }
132
133 framepos_t
134 Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
135 {
136         ArdourCanvas::Duple d;
137
138         if (!gdk_event_get_coords (event, &d.x, &d.y)) {
139                 return 0;
140         }
141
142         /* event coordinates are in window units, so convert to canvas
143          */
144
145         d = _track_canvas->window_to_canvas (d);
146
147         if (pcx) {
148                 *pcx = d.x;
149         }
150
151         if (pcy) {
152                 *pcy = d.y;
153         }
154
155         return pixel_to_sample (d.x);
156 }
157
158 framepos_t
159 Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
160 {
161         double x;
162         double y;
163
164         /* event coordinates are already in canvas units */
165
166         if (!gdk_event_get_coords (event, &x, &y)) {
167                 cerr << "!NO c COORDS for event type " << event->type << endl;
168                 return 0;
169         }
170
171         if (pcx) {
172                 *pcx = x;
173         }
174
175         if (pcy) {
176                 *pcy = y;
177         }
178
179         /* note that pixel_to_sample_from_event() never returns less than zero, so even if the pixel
180            position is negative (as can be the case with motion events in particular),
181            the frame location is always positive.
182         */
183
184         return pixel_to_sample_from_event (x);
185 }
186
187 void
188 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
189 {
190         boost::shared_ptr<Trimmable> st = _trimmable.lock();
191
192         if (!st || st == t) {
193                 _trimmable = t;
194         }
195 }
196
197 void
198 Editor::set_current_movable (boost::shared_ptr<Movable> m)
199 {
200         boost::shared_ptr<Movable> sm = _movable.lock();
201
202         if (!sm || sm != m) {
203                 _movable = m;
204         }
205 }
206
207 void
208 Editor::mouse_mode_object_range_toggled()
209 {
210         MouseMode m = mouse_mode;
211         
212         Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
213         assert (act);
214         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
215         assert (tact);
216
217         if (tact->get_active()) {
218                 m = MouseObject;  //Smart mode turned to ON, force editing to Object mode
219         }
220
221         set_mouse_mode(m, true);  //call this so the button styles can get updated
222 }
223
224 void
225 Editor::set_mouse_mode (MouseMode m, bool force)
226 {
227         if (_drags->active ()) {
228                 return;
229         }
230
231         if (!force && m == mouse_mode) {
232                 return;
233         }
234
235         if (ARDOUR::Profile->get_mixbus()) {
236                 if ( m == MouseCut) m = MouseObject;
237         }
238
239         Glib::RefPtr<Action> act;
240
241         switch (m) {
242         case MouseRange:
243                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
244                 break;
245
246         case MouseCut:
247                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
248                 break;
249
250         case MouseObject:
251                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
252                 break;
253
254         case MouseDraw:
255                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
256                 break;
257
258         case MouseGain:
259                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
260                 break;
261
262         case MouseTimeFX:
263                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
264                 break;
265
266         case MouseAudition:
267                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
268                 break;
269         }
270
271         assert (act);
272
273         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
274         assert (tact);
275
276         /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
277         tact->set_active (false);
278         tact->set_active (true);
279
280         //NOTE:  this will result in a call to mouse_mode_toggled which does the heavy lifting
281 }
282
283 void
284 Editor::mouse_mode_toggled (MouseMode m)
285 {
286         Glib::RefPtr<Action> act;
287         Glib::RefPtr<ToggleAction> tact;
288
289         if (ARDOUR::Profile->get_mixbus()) {
290                 if ( m == MouseCut)  m = MouseObject;
291         }
292
293         switch (m) {
294         case MouseRange:
295                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
296                 break;
297
298         case MouseObject:
299                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
300                 break;
301
302         case MouseCut:
303                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
304                 break;
305
306         case MouseDraw:
307                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
308                 break;
309
310         case MouseGain:
311                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
312                 break;
313
314         case MouseTimeFX:
315                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
316                 break;
317
318         case MouseAudition:
319                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
320                 break;
321         }
322
323         assert (act);
324
325         tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
326         assert (tact);
327
328         if (!tact->get_active()) {
329                 /* this was just the notification that the old mode has been
330                  * left. we'll get called again with the new mode active in a
331                  * jiffy.
332                  */
333                 return;
334         }
335
336         switch (m) {
337         case MouseDraw:
338                 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
339                 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
340                 tact->set_active (true);
341                 break;
342         default:
343                 break;
344         }
345         
346         if (_session && mouse_mode == MouseAudition) {
347                 /* stop transport and reset default speed to avoid oddness with
348                    auditioning */
349                 _session->request_transport_speed (0.0, true);
350         }
351
352         mouse_mode = m;
353
354         instant_save ();
355
356         /* this should generate a new enter event which will
357            trigger the appropiate cursor.
358         */
359
360         if (_track_canvas) {
361                 _track_canvas->re_enter ();
362         }
363
364         set_gain_envelope_visibility ();
365         
366         update_time_selection_display ();
367
368         MouseModeChanged (); /* EMIT SIGNAL */
369 }
370
371 void
372 Editor::update_time_selection_display ()
373 {
374         if (smart_mode_action->get_active()) {
375                 /* not sure what to do here */
376                 if (mouse_mode == MouseObject) {
377                 } else {
378                 }
379         } else {
380                 switch (mouse_mode) {
381                 case MouseRange:
382                         selection->clear_objects ();
383                         break;
384                 default:
385                         selection->clear_time ();
386                         break;
387                 }
388         }
389 }
390
391 void
392 Editor::step_mouse_mode (bool next)
393 {
394         switch (current_mouse_mode()) {
395         case MouseObject:
396                 if (next) {
397                         set_mouse_mode (MouseRange);
398                 } else {
399                         set_mouse_mode (MouseTimeFX);
400                 }
401                 break;
402
403         case MouseRange:
404                 if (next) set_mouse_mode (MouseDraw);
405                 else set_mouse_mode (MouseCut);
406                 break;
407
408         case MouseCut:
409                 if (next) set_mouse_mode (MouseRange);
410                 else set_mouse_mode (MouseDraw);
411                 break;
412
413         case MouseDraw:
414                 if (next) set_mouse_mode (MouseCut);
415                 else set_mouse_mode (MouseRange);
416                 break;
417
418         case MouseGain:
419                 if (next) set_mouse_mode (MouseTimeFX);
420                 else set_mouse_mode (MouseDraw);
421                 break;
422
423         case MouseTimeFX:
424                 if (next) {
425                         set_mouse_mode (MouseAudition);
426                 } else {
427                         set_mouse_mode (MouseGain);
428                 }
429                 break;
430
431         case MouseAudition:
432                 if (next) set_mouse_mode (MouseObject);
433                 else set_mouse_mode (MouseTimeFX);
434                 break;
435         }
436 }
437
438 bool
439 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
440 {
441         if (_drags->active()) {
442                 _drags->end_grab (event);
443         } 
444
445         ActionManager::do_action ("MouseMode", "toggle-internal-edit");
446
447         /* prevent reversion of edit cursor on button release */
448         
449         pre_press_cursor = 0;
450
451         return true;
452 }
453
454 void
455 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
456 {
457         /* in object/audition/timefx/gain-automation mode,
458            any button press sets the selection if the object
459            can be selected. this is a bit of hack, because
460            we want to avoid this if the mouse operation is a
461            region alignment.
462
463            note: not dbl-click or triple-click
464
465            Also note that there is no region selection in internal edit mode, otherwise
466            for operations operating on the selection (e.g. cut) it is not obvious whether
467            to cut notes or regions.
468         */
469
470         MouseMode eff_mouse_mode = effective_mouse_mode ();
471
472         if (eff_mouse_mode == MouseCut) {
473                 /* never change selection in cut mode */
474                 return;
475         }
476
477         if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
478                 /* context clicks are always about object properties, even if
479                    we're in range mode within smart mode.
480                 */
481                 eff_mouse_mode = MouseObject;
482         }
483
484         /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
485         if (get_smart_mode()) {
486                 switch (item_type) {
487                   case FadeInHandleItem:
488                   case FadeInTrimHandleItem:
489                   case FadeOutHandleItem:
490                   case FadeOutTrimHandleItem:
491                           eff_mouse_mode = MouseObject;
492                           break;
493                 default:
494                         break;
495                 }
496         }
497
498         if (((mouse_mode != MouseObject) &&
499              (mouse_mode != MouseAudition || item_type != RegionItem) &&
500              (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
501              (mouse_mode != MouseGain) &&
502              (mouse_mode != MouseDraw)) ||
503             ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
504             (internal_editing() && mouse_mode != MouseTimeFX)) {
505
506                 return;
507         }
508
509         if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
510
511                 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
512
513                         /* almost no selection action on modified button-2 or button-3 events */
514
515                         if (item_type != RegionItem && event->button.button != 2) {
516                                 return;
517                         }
518                 }
519         }
520
521         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
522         bool press = (event->type == GDK_BUTTON_PRESS);
523
524         switch (item_type) {
525         case RegionItem:
526                 if (press) {
527                         if (eff_mouse_mode != MouseRange) {
528                                 set_selected_regionview_from_click (press, op);
529                         } else {
530                                 /* don't change the selection unless the
531                                    clicked track is not currently selected. if
532                                    so, "collapse" the selection to just this
533                                    track
534                                 */
535                                 if (!selection->selected (clicked_axisview)) {
536                                         set_selected_track_as_side_effect (Selection::Set);
537                                 }
538                         }
539                 } else {
540                         if (eff_mouse_mode != MouseRange) {
541                                 set_selected_regionview_from_click (press, op);
542                         }
543                 }
544                 break;
545
546         case RegionViewNameHighlight:
547         case RegionViewName:
548         case LeftFrameHandle:
549         case RightFrameHandle:
550         case FadeInHandleItem:
551         case FadeInTrimHandleItem:
552         case FadeInItem:
553         case FadeOutHandleItem:
554         case FadeOutTrimHandleItem:
555         case FadeOutItem:
556         case StartCrossFadeItem:
557         case EndCrossFadeItem:
558                 if (get_smart_mode() || eff_mouse_mode != MouseRange) {
559                         set_selected_regionview_from_click (press, op);
560                 } else if (event->type == GDK_BUTTON_PRESS) {
561                         set_selected_track_as_side_effect (op);
562                 }
563                 break;
564
565         case ControlPointItem:
566                 set_selected_track_as_side_effect (op);
567                 if (eff_mouse_mode != MouseRange) {
568                         set_selected_control_point_from_click (press, op);
569                 }
570                 break;
571
572         case StreamItem:
573                 /* for context click, select track */
574                 if (event->button.button == 3) {
575                         selection->clear_tracks ();
576                         set_selected_track_as_side_effect (op);
577                 }
578                 break;
579
580         case AutomationTrackItem:
581                 set_selected_track_as_side_effect (op);
582                 break;
583
584         default:
585                 break;
586         }
587 }
588
589 bool
590 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
591 {
592         /* single mouse clicks on any of these item types operate
593            independent of mouse mode, mostly because they are
594            not on the main track canvas or because we want
595            them to be modeless.
596         */
597
598         switch (item_type) {
599         case PlayheadCursorItem:
600                 _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
601                 return true;
602
603         case MarkerItem:
604                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
605                         hide_marker (item, event);
606                 } else {
607                         _drags->set (new MarkerDrag (this, item), event);
608                 }
609                 return true;
610
611         case TempoMarkerItem:
612         {
613                 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
614                 assert (m);
615                 _drags->set (
616                         new TempoMarkerDrag (
617                                 this,
618                                 item,
619                                 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
620                                 ),
621                         event
622                         );
623                 return true;
624         }
625
626         case MeterMarkerItem:
627         {
628                 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
629                 assert (m);
630                 _drags->set (
631                         new MeterMarkerDrag (
632                                 this,
633                                 item,
634                                 Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
635                                 ),
636                         event
637                         );
638                 return true;
639         }
640
641         case VideoBarItem:
642                 _drags->set (new VideoTimeLineDrag (this, item), event);
643                 return true;
644                 break;
645
646         case MarkerBarItem:
647         case TempoBarItem:
648         case MeterBarItem:
649         case TimecodeRulerItem:
650         case SamplesRulerItem:
651         case MinsecRulerItem:
652         case BBTRulerItem:
653                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
654                         _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
655                 }
656                 return true;
657                 break;
658
659
660         case RangeMarkerBarItem:
661                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
662                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateSkipMarker), event);
663                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
664                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
665                 } else {
666                         _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
667                 }
668                 return true;
669                 break;
670
671         case CdMarkerBarItem:
672                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
673                         _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
674                 } else {
675                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
676                 }
677                 return true;
678                 break;
679
680         case TransportMarkerBarItem:
681                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
682                         _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
683                 } else {
684                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
685                 }
686                 return true;
687                 break;
688
689         default:
690                 break;
691         }
692
693         if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
694                 /* special case: allow trim of range selections in joined object mode;
695                    in theory eff should equal MouseRange in this case, but it doesn't
696                    because entering the range selection canvas item results in entered_regionview
697                    being set to 0, so update_join_object_range_location acts as if we aren't
698                    over a region.
699                 */
700                 if (item_type == StartSelectionTrimItem) {
701                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
702                 } else if (item_type == EndSelectionTrimItem) {
703                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
704                 }
705         }
706
707         Editing::MouseMode eff = effective_mouse_mode ();
708
709         /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
710         if (get_smart_mode()) { 
711                 switch (item_type) {
712                   case FadeInHandleItem:
713                   case FadeInTrimHandleItem:
714                   case FadeOutHandleItem:
715                   case FadeOutTrimHandleItem:
716                         eff = MouseObject;
717                         break;
718                 default:
719                         break;
720                 }
721         }
722
723         /* there is no Range mode when in internal edit mode */
724         if (eff == MouseRange && internal_editing()) {
725                 eff = MouseObject;
726         }
727
728         switch (eff) {
729         case MouseRange:
730                 switch (item_type) {
731                 case StartSelectionTrimItem:
732                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
733                         break;
734
735                 case EndSelectionTrimItem:
736                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
737                         break;
738
739                 case SelectionItem:
740                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
741                                 start_selection_grab (item, event);
742                                 return true;
743                         } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
744                                 /* grab selection for moving */
745                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
746                         } else {
747                                 /* this was debated, but decided the more common action was to
748                                    make a new selection */
749                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
750                         }
751                         break;
752
753                 case StreamItem:
754                         if (internal_editing()) {
755                                 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
756                                         _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
757                                         return true;
758                                 } 
759                         } else {
760                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
761                                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
762                                 } else {
763                                         _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
764                                 }
765                                 return true;
766                         }
767                         break;
768
769                 case RegionViewNameHighlight:
770                         if (!clicked_regionview->region()->locked()) {
771                                 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
772                                 return true;
773                         }
774                         break;
775
776                 default:
777                         if (!internal_editing()) {
778                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
779                                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
780                                 } else {
781                                         _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
782                                 }
783                         }
784                 }
785                 return true;
786                 break;
787
788         case MouseDraw:
789                 switch (item_type) {
790                 case NoteItem:
791                         /* Existing note: allow trimming/motion */
792                         if (internal_editing()) {
793                                 /* trim notes if we're in internal edit mode and near the ends of the note */
794                                 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
795                                 assert (cn);
796                                 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
797                                         _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
798                                 } else {
799                                         _drags->set (new NoteDrag (this, item), event);
800                                 }
801                                 return true;
802                         } 
803                         break;
804                 case StreamItem:
805                         if (internal_editing()) {
806                                 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
807                                         _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
808                                 }
809                                 return true;
810                         }
811                         break;
812
813                 default:
814                         break;
815                 }
816                 break;
817
818         case MouseCut:
819                 switch (item_type) {
820                 case RegionItem:
821                 case FadeInHandleItem:
822                 case FadeOutHandleItem:
823                 case LeftFrameHandle:
824                 case RightFrameHandle:
825                 case FeatureLineItem:
826                 case RegionViewNameHighlight:
827                 case RegionViewName:
828                 case StreamItem:
829                 case AutomationTrackItem:
830                         _drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, current_canvas_cursor);
831                         return true;
832                         break;
833                 default:
834                         break;
835                 }
836                 break;
837
838         case MouseObject:
839                 switch (item_type) {
840                 case NoteItem:
841                         /* Existing note: allow trimming/motion */
842                         if (internal_editing()) {
843                                 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
844                                 assert (cn);
845                                 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
846                                         _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
847                                 } else {
848                                         _drags->set (new NoteDrag (this, item), event);
849                                 }
850                                 return true;
851                         }
852                         break;
853
854                 default:
855                         break;
856                 }
857
858                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
859                     event->type == GDK_BUTTON_PRESS) {
860
861                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
862
863                 } else if (event->type == GDK_BUTTON_PRESS) {
864
865                         switch (item_type) {
866                         case FadeInHandleItem:
867                         {
868                                 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
869                                 return true;
870                         }
871
872                         case FadeOutHandleItem:
873                         {
874                                 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
875                                 return true;
876                         }
877
878                         case StartCrossFadeItem:
879                         case EndCrossFadeItem:
880                                 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor.  for not this is not fully implemented */ 
881 //                              if (!clicked_regionview->region()->locked()) {
882 //                                      _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
883 //                                      return true;
884 //                              }
885                                 break;
886
887                         case FeatureLineItem:
888                         {
889                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
890                                         remove_transient(item);
891                                         return true;
892                                 }
893
894                                 _drags->set (new FeatureLineDrag (this, item), event);
895                                 return true;
896                                 break;
897                         }
898
899                         case RegionItem:
900                                 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
901                                         /* click on an automation region view; do nothing here and let the ARV's signal handler
902                                            sort it out.
903                                         */
904                                         break;
905                                 }
906
907                                 if (internal_editing ()) {
908                                         break;
909                                 }
910
911                                 /* click on a normal region view */
912                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
913                                         add_region_copy_drag (item, event, clicked_regionview);
914                                 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
915                                         add_region_brush_drag (item, event, clicked_regionview);
916                                 } else {
917                                         add_region_drag (item, event, clicked_regionview);
918                                 }
919
920
921 //                              if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
922 //                                      _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
923 //                              }
924
925                                 _drags->start_grab (event);
926                                 return true;
927                                 break;
928
929                         case RegionViewNameHighlight:
930                         case LeftFrameHandle:
931                         case RightFrameHandle:
932                                 if (!clicked_regionview->region()->locked()) {
933                                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
934                                         return true;
935                                 }
936                                 break;
937
938                         case FadeInTrimHandleItem:
939                         case FadeOutTrimHandleItem:
940                                 if (!clicked_regionview->region()->locked()) {
941                                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
942                                         return true;
943                                 }
944                                 break;
945
946                         case RegionViewName:
947                         {
948                                 /* rename happens on edit clicks */
949                                 if (clicked_regionview->get_name_highlight()) {
950                                         _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
951                                         return true;
952                                 }
953                                 break;
954                         }
955
956                         case ControlPointItem:
957                                 _drags->set (new ControlPointDrag (this, item), event);
958                                 return true;
959                                 break;
960
961                         case AutomationLineItem:
962                                 _drags->set (new LineDrag (this, item), event);
963                                 return true;
964                                 break;
965
966                         case StreamItem:
967                                 if (internal_editing()) {
968                                         if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
969                                                 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
970                                         }
971                                         return true;
972                                 } else {
973                                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
974                                 }
975                                 break;
976
977                         case AutomationTrackItem:
978                         {
979                                 TimeAxisView* parent = clicked_axisview->get_parent ();
980                                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
981                                 assert (atv);
982                                 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
983
984                                         RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
985                                         assert (p);
986                                         boost::shared_ptr<Playlist> pl = p->track()->playlist ();
987                                         if (pl->n_regions() == 0) {
988                                                 /* Parent has no regions; create one so that we have somewhere to put automation */
989                                                 _drags->set (new RegionCreateDrag (this, item, parent), event);
990                                         } else {
991                                                 /* See if there's a region before the click that we can extend, and extend it if so */
992                                                 framepos_t const t = canvas_event_sample (event);
993                                                 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
994                                                 if (!prev) {
995                                                         _drags->set (new RegionCreateDrag (this, item, parent), event);
996                                                 } else {
997                                                         prev->set_length (t - prev->position ());
998                                                 }
999                                         }
1000                                 } else {
1001                                         /* rubberband drag to select automation points */
1002                                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1003                                 }
1004                                 break;
1005                         }
1006
1007                         case SelectionItem:
1008                         {
1009                                 break;
1010                         }
1011
1012                         case MarkerBarItem:
1013
1014                                 break;
1015
1016                         default:
1017                                 break;
1018                         }
1019                 }
1020                 return true;
1021                 break;
1022
1023         case MouseGain:
1024                 switch (item_type) {
1025                 case GainLineItem:
1026                         _drags->set (new LineDrag (this, item), event);
1027                         return true;
1028
1029                 case ControlPointItem:
1030                         _drags->set (new ControlPointDrag (this, item), event);
1031                         return true;
1032                         break;
1033
1034                 case SelectionItem:
1035                 {
1036                         AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1037                         if (arv) {
1038                                 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1039                         } else {
1040                                 double const y = event->button.y;
1041                                 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1042                                 if (tvp.first) {
1043                                         AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1044                                         if ( atv) {
1045                                                 /* smart "join" mode: drag automation */
1046                                                 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1047                                         }
1048                                 }
1049                         }
1050                         return true;
1051                         break;
1052                 }
1053
1054                 case AutomationLineItem:
1055                         _drags->set (new LineDrag (this, item), event);
1056                         break;
1057                         
1058                 default:
1059                         break;
1060                 }
1061                 return true;
1062                 break;
1063
1064         case MouseTimeFX:
1065                 if (internal_editing() && item_type == NoteItem ) {
1066                         /* drag notes if we're in internal edit mode */
1067                         NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
1068                         assert (cn);
1069                         if (cn->big_enough_to_trim()) {
1070                                 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1071                         }
1072                         return true;
1073                 } else if (clicked_regionview) {
1074                         /* do time-FX  */
1075                         _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1076                         return true;
1077                 }
1078                 break;
1079
1080         case MouseAudition:
1081                 _drags->set (new ScrubDrag (this, item), event);
1082                 scrub_reversals = 0;
1083                 scrub_reverse_distance = 0;
1084                 last_scrub_x = event->button.x;
1085                 scrubbing_direction = 0;
1086                 push_canvas_cursor (_cursors->transparent);
1087                 return true;
1088                 break;
1089
1090         default:
1091                 break;
1092         }
1093
1094         return false;
1095 }
1096
1097 bool
1098 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1099 {
1100         Editing::MouseMode const eff = effective_mouse_mode ();
1101         switch (eff) {
1102         case MouseObject:
1103                 switch (item_type) {
1104                 case RegionItem:
1105                         if (internal_editing ()) {
1106                                 /* no region drags in internal edit mode */
1107                                 return false;
1108                         }
1109
1110                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1111                                 add_region_copy_drag (item, event, clicked_regionview);
1112                         } else {
1113                                 add_region_drag (item, event, clicked_regionview);
1114                         }
1115                         _drags->start_grab (event);
1116                         return true;
1117                         break;
1118                 case ControlPointItem:
1119                         _drags->set (new ControlPointDrag (this, item), event);
1120                         return true;
1121                         break;
1122
1123                 default:
1124                         break;
1125                 }
1126
1127                 switch (item_type) {
1128                 case RegionViewNameHighlight:
1129                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1130                         return true;
1131                         break;
1132
1133                 case LeftFrameHandle:
1134                 case RightFrameHandle:
1135                         if (!internal_editing ()) {
1136                                 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1137                         }
1138                         return true;
1139                         break;
1140
1141                 case RegionViewName:
1142                         _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1143                         return true;
1144                         break;
1145
1146                 default:
1147                         break;
1148                 }
1149
1150                 break;
1151
1152         case MouseDraw:
1153                 return false;
1154
1155         case MouseRange:
1156                 /* relax till release */
1157                 return true;
1158                 break;
1159
1160         default:
1161                 break;
1162         }
1163
1164         return false;
1165 }
1166    
1167 bool
1168 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1169 {
1170         if (event->type == GDK_2BUTTON_PRESS) {
1171                 _drags->mark_double_click ();
1172                 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1173                 return true;
1174         }
1175
1176         if (event->type != GDK_BUTTON_PRESS) {
1177                 return false;
1178         }
1179
1180         pre_press_cursor = current_canvas_cursor;
1181
1182         _track_canvas->grab_focus();
1183
1184         if (_session && _session->actively_recording()) {
1185                 return true;
1186         }
1187
1188         if (internal_editing()) {
1189                 bool leave_internal_edit_mode = false;
1190
1191                 switch (item_type) {
1192                 case NoteItem:
1193                         break;
1194
1195                 case RegionItem:
1196                         if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1197                                 leave_internal_edit_mode = true;
1198                         }
1199                         break;
1200
1201                 case PlayheadCursorItem:
1202                 case MarkerItem:
1203                 case TempoMarkerItem:
1204                 case MeterMarkerItem:
1205                 case MarkerBarItem:
1206                 case TempoBarItem:
1207                 case MeterBarItem:
1208                 case RangeMarkerBarItem:
1209                 case CdMarkerBarItem:
1210                 case TransportMarkerBarItem:
1211                 case StreamItem:
1212                 case TimecodeRulerItem:
1213                 case SamplesRulerItem:
1214                 case MinsecRulerItem:
1215                 case BBTRulerItem:
1216                         /* button press on these items never does anything to
1217                            change the editing mode.
1218                         */
1219                         break;
1220
1221                 default:
1222                         break;
1223                 }
1224                 
1225                 if (leave_internal_edit_mode) {
1226                         ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1227                 }
1228         }
1229
1230         button_selection (item, event, item_type);
1231
1232         if (!_drags->active () &&
1233             (Keyboard::is_delete_event (&event->button) ||
1234              Keyboard::is_context_menu_event (&event->button) ||
1235              Keyboard::is_edit_event (&event->button))) {
1236
1237                 /* handled by button release */
1238                 return true;
1239         }
1240
1241         //not rolling, range mode click + join_play_range :  locate the PH here
1242         if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_follow_edits() ) {
1243                 framepos_t where = canvas_event_sample (event);
1244                 snap_to(where);
1245                 _session->request_locate (where, false);
1246         }
1247
1248         switch (event->button.button) {
1249         case 1:
1250                 return button_press_handler_1 (item, event, item_type);
1251                 break;
1252
1253         case 2:
1254                 return button_press_handler_2 (item, event, item_type);
1255                 break;
1256
1257         case 3:
1258                 break;
1259
1260         default:
1261                 return button_press_dispatch (&event->button);
1262                 break;
1263
1264         }
1265
1266         return false;
1267 }
1268
1269 bool
1270 Editor::button_press_dispatch (GdkEventButton* ev)
1271 {
1272         /* this function is intended only for buttons 4 and above.
1273          */
1274
1275         Gtkmm2ext::MouseButton b (ev->state, ev->button);
1276         return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1277 }
1278
1279 bool
1280 Editor::button_release_dispatch (GdkEventButton* ev)
1281 {
1282         /* this function is intended only for buttons 4 and above.
1283          */
1284
1285         Gtkmm2ext::MouseButton b (ev->state, ev->button);
1286         return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1287 }
1288
1289 bool
1290 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1291 {
1292         framepos_t where = canvas_event_sample (event);
1293         AutomationTimeAxisView* atv = 0;
1294
1295         if (pre_press_cursor) {
1296                 set_canvas_cursor (pre_press_cursor);
1297                 pre_press_cursor = 0;
1298         }
1299
1300         /* no action if we're recording */
1301
1302         if (_session && _session->actively_recording()) {
1303                 return true;
1304         }
1305
1306         /* see if we're finishing a drag */
1307
1308         bool were_dragging = false;
1309         if (_drags->active ()) {
1310                 bool const r = _drags->end_grab (event);
1311                 if (r) {
1312                         /* grab dragged, so do nothing else */
1313                         return true;
1314                 }
1315
1316                 were_dragging = true;
1317         }
1318
1319         update_region_layering_order_editor ();
1320
1321         /* edit events get handled here */
1322
1323         if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1324                 switch (item_type) {
1325                 case RegionItem:
1326                         show_region_properties ();
1327                         break;
1328
1329                 case TempoMarkerItem: {
1330                         Marker* marker;
1331                         TempoMarker* tempo_marker;
1332                         
1333                         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1334                                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1335                                 abort(); /*NOTREACHED*/
1336                         }
1337                         
1338                         if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1339                                 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1340                                 abort(); /*NOTREACHED*/
1341                         }
1342                         
1343                         edit_tempo_marker (*tempo_marker);
1344                         break;
1345                 }
1346
1347                 case MeterMarkerItem: {
1348                         Marker* marker;
1349                         MeterMarker* meter_marker;
1350                         
1351                         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1352                                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1353                                 abort(); /*NOTREACHED*/
1354                         }
1355                         
1356                         if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1357                                 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1358                                 abort(); /*NOTREACHED*/
1359                         }
1360                         edit_meter_marker (*meter_marker);
1361                         break;
1362                 }
1363
1364                 case RegionViewName:
1365                         if (clicked_regionview->name_active()) {
1366                                 return mouse_rename_region (item, event);
1367                         }
1368                         break;
1369
1370                 case ControlPointItem:
1371                         edit_control_point (item);
1372                         break;
1373
1374                 default:
1375                         break;
1376                 }
1377                 return true;
1378         }
1379
1380         /* context menu events get handled here */
1381         if (Keyboard::is_context_menu_event (&event->button)) {
1382
1383                 context_click_event = *event;
1384
1385                 if (!_drags->active ()) {
1386
1387                         /* no matter which button pops up the context menu, tell the menu
1388                            widget to use button 1 to drive menu selection.
1389                         */
1390
1391                         switch (item_type) {
1392                         case FadeInItem:
1393                         case FadeInHandleItem:
1394                         case FadeInTrimHandleItem:
1395                         case StartCrossFadeItem:
1396                                 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1397                                 break;
1398
1399                         case FadeOutItem:
1400                         case FadeOutHandleItem:
1401                         case FadeOutTrimHandleItem:
1402                         case EndCrossFadeItem:
1403                                 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1404                                 break;
1405
1406                         case LeftFrameHandle:
1407                         case RightFrameHandle:
1408                                 break;
1409
1410                         case StreamItem:
1411                                 popup_track_context_menu (1, event->button.time, item_type, false);
1412                                 break;
1413
1414                         case RegionItem:
1415                         case RegionViewNameHighlight:
1416                         case RegionViewName:
1417                                 popup_track_context_menu (1, event->button.time, item_type, false);
1418                                 break;
1419
1420                         case SelectionItem:
1421                                 popup_track_context_menu (1, event->button.time, item_type, true);
1422                                 break;
1423                                 
1424                         case AutomationTrackItem:
1425                                 popup_track_context_menu (1, event->button.time, item_type, false);
1426                                 break;
1427
1428                         case MarkerBarItem:
1429                         case RangeMarkerBarItem:
1430                         case TransportMarkerBarItem:
1431                         case CdMarkerBarItem:
1432                         case TempoBarItem:
1433                         case MeterBarItem:
1434                         case VideoBarItem:
1435                         case TimecodeRulerItem:
1436                         case SamplesRulerItem:
1437                         case MinsecRulerItem:
1438                         case BBTRulerItem:
1439                                 popup_ruler_menu (where, item_type);
1440                                 break;
1441
1442                         case MarkerItem:
1443                                 marker_context_menu (&event->button, item);
1444                                 break;
1445
1446                         case TempoMarkerItem:
1447                                 tempo_or_meter_marker_context_menu (&event->button, item);
1448                                 break;
1449
1450                         case MeterMarkerItem:
1451                                 tempo_or_meter_marker_context_menu (&event->button, item);
1452                                 break;
1453
1454                         case CrossfadeViewItem:
1455                                 popup_track_context_menu (1, event->button.time, item_type, false);
1456                                 break;
1457
1458                         case ControlPointItem:
1459                                 popup_control_point_context_menu (item, event);
1460                                 break;
1461
1462                         default:
1463                                 break;
1464                         }
1465
1466                         return true;
1467                 }
1468         }
1469
1470         /* delete events get handled here */
1471
1472         Editing::MouseMode const eff = effective_mouse_mode ();
1473
1474         if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1475
1476                 switch (item_type) {
1477                 case TempoMarkerItem:
1478                         remove_tempo_marker (item);
1479                         break;
1480
1481                 case MeterMarkerItem:
1482                         remove_meter_marker (item);
1483                         break;
1484
1485                 case MarkerItem:
1486                         remove_marker (*item, event);
1487                         break;
1488
1489                 case RegionItem:
1490                         if (eff == MouseObject) {
1491                                 remove_clicked_region ();
1492                         }
1493                         break;
1494
1495                 case ControlPointItem:
1496                         remove_control_point (item);
1497                         break;
1498
1499                 case NoteItem:
1500                         remove_midi_note (item, event);
1501                         break;
1502
1503                 default:
1504                         break;
1505                 }
1506                 return true;
1507         }
1508
1509         switch (event->button.button) {
1510         case 1:
1511
1512                 switch (item_type) {
1513                 /* see comments in button_press_handler */
1514                 case PlayheadCursorItem:
1515                 case MarkerItem:
1516                 case GainLineItem:
1517                 case AutomationLineItem:
1518                 case StartSelectionTrimItem:
1519                 case EndSelectionTrimItem:
1520                         return true;
1521
1522                 case MarkerBarItem:
1523                         if (!_dragging_playhead) {
1524                                 snap_to_with_modifier (where, event, RoundNearest, true);
1525                                 mouse_add_new_marker (where);
1526                         }
1527                         return true;
1528
1529                 case CdMarkerBarItem:
1530                         if (!_dragging_playhead) {
1531                                 // if we get here then a dragged range wasn't done
1532                                 snap_to_with_modifier (where, event, RoundNearest, true);
1533                                 mouse_add_new_marker (where, true);
1534                         }
1535                         return true;
1536
1537                 case TempoBarItem:
1538                         if (!_dragging_playhead) {
1539                                 snap_to_with_modifier (where, event);
1540                                 mouse_add_new_tempo_event (where);
1541                         }
1542                         return true;
1543
1544                 case MeterBarItem:
1545                         if (!_dragging_playhead) {
1546                                 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1547                         }
1548                         return true;
1549                         break;
1550
1551                 case TimecodeRulerItem:
1552                 case SamplesRulerItem:
1553                 case MinsecRulerItem:
1554                 case BBTRulerItem:
1555                         return true;
1556                         break;
1557
1558                 default:
1559                         break;
1560                 }
1561
1562                 switch (eff) {
1563                 case MouseObject:
1564                 case MouseDraw:
1565                         switch (item_type) {
1566                         case AutomationTrackItem:
1567                                 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1568                                 if (atv) {
1569                                         bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1570                                         atv->add_automation_event (event, where, event->button.y, with_guard_points);
1571                                 }
1572                                 return true;
1573                                 break;
1574                         default:
1575                                 break;
1576                         }
1577                         break;
1578
1579                 case MouseGain:
1580                         switch (item_type) {
1581                         case RegionItem:
1582                         {
1583                                 /* check that we didn't drag before releasing, since
1584                                    its really annoying to create new control
1585                                    points when doing this.
1586                                 */
1587                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1588                                 if (!were_dragging && arv) {
1589                                         bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1590                                         arv->add_gain_point_event (item, event, with_guard_points);
1591                                 }
1592                                 return true;
1593                                 break;
1594                         }
1595
1596                         case AutomationTrackItem: {
1597                                 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1598                                 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1599                                         add_automation_event (event, where, event->button.y, with_guard_points);
1600                                 return true;
1601                                 break;
1602                         }
1603                         default:
1604                                 break;
1605                         }
1606                         break;
1607
1608                 case MouseAudition:
1609                         pop_canvas_cursor ();
1610                         if (scrubbing_direction == 0) {
1611                                 /* no drag, just a click */
1612                                 switch (item_type) {
1613                                 case RegionItem:
1614                                         play_selected_region ();
1615                                         break;
1616                                 default:
1617                                         break;
1618                                 }
1619                         } else {
1620                                 /* make sure we stop */
1621                                 _session->request_transport_speed (0.0);
1622                         }
1623                         break;
1624
1625                 default:
1626                         break;
1627
1628                 }
1629
1630                 /* do any (de)selection operations that should occur on button release */
1631                 button_selection (item, event, item_type);
1632                 return true;
1633                 break;
1634
1635
1636         case 2:
1637                 switch (eff) {
1638
1639                 case MouseObject:
1640                         switch (item_type) {
1641                         case RegionItem:
1642                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1643                                         raise_region ();
1644                                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1645                                         lower_region ();
1646                                 } else {
1647                                         // Button2 click is unused
1648                                 }
1649                                 return true;
1650
1651                                 break;
1652
1653                         default:
1654                                 break;
1655                         }
1656                         break;
1657
1658                 case MouseDraw:
1659                         return true;
1660                         
1661                 case MouseRange:
1662                         // x_style_paste (where, 1.0);
1663                         return true;
1664                         break;
1665
1666                 default:
1667                         break;
1668                 }
1669
1670                 break;
1671
1672         case 3:
1673                 break;
1674
1675         default:
1676                 break;
1677         }
1678
1679         return false;
1680 }
1681
1682 bool
1683 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1684 {
1685         ControlPoint* cp;
1686         Marker * marker;
1687         double fraction;
1688         bool ret = true;
1689
1690         /* by the time we reach here, entered_regionview and entered trackview
1691          * will have already been set as appropriate. Things are done this 
1692          * way because this method isn't passed a pointer to a variable type of
1693          * thing that is entered (which may or may not be canvas item).
1694          * (e.g. the actual entered regionview)
1695          */
1696
1697         choose_canvas_cursor_on_entry (&event->crossing, item_type);
1698
1699         switch (item_type) {
1700         case ControlPointItem:
1701                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1702                         cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1703                         cp->show ();
1704
1705                         fraction = 1.0 - (cp->get_y() / cp->line().height());
1706
1707                         _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1708                         _verbose_cursor->show ();
1709                 }
1710                 break;
1711
1712         case GainLineItem:
1713                 if (mouse_mode == MouseGain) {
1714                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1715                         if (line) {
1716                                 line->set_outline_color (ARDOUR_UI::config()->get_EnteredGainLine());
1717                         }
1718                 }
1719                 break;
1720
1721         case AutomationLineItem:
1722                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1723                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1724                         if (line) {
1725                                 line->set_outline_color (ARDOUR_UI::config()->get_EnteredAutomationLine());
1726                         }
1727                 }
1728                 break;
1729
1730         case AutomationTrackItem:
1731                 AutomationTimeAxisView* atv;
1732                 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1733                         clear_entered_track = false;
1734                         set_entered_track (atv);
1735                 }
1736                 break;
1737
1738         case MarkerItem:
1739                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1740                         break;
1741                 }
1742                 entered_marker = marker;
1743                 marker->set_color_rgba (ARDOUR_UI::config()->get_EnteredMarker());
1744                 // fall through
1745         case MeterMarkerItem:
1746         case TempoMarkerItem:
1747                 break;
1748
1749         case FadeInHandleItem:
1750         case FadeInTrimHandleItem:
1751                 if (mouse_mode == MouseObject && !internal_editing()) {
1752                         ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1753                         if (rect) {
1754                                 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1755                                 rect->set_fill_color (rv->get_fill_color());
1756                         }
1757                 }
1758                 break;
1759
1760         case FadeOutHandleItem:
1761         case FadeOutTrimHandleItem:
1762                 if (mouse_mode == MouseObject && !internal_editing()) {
1763                         ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1764                         if (rect) {
1765                                 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1766                                 rect->set_fill_color (rv->get_fill_color ());
1767                         }
1768                 }
1769                 break;
1770
1771         case FeatureLineItem:
1772         {
1773                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1774                 line->set_outline_color (0xFF0000FF);
1775         }
1776         break;
1777
1778         case SelectionItem:
1779                 break;
1780
1781         default:
1782                 break;
1783         }
1784
1785         /* third pass to handle entered track status in a comprehensible way.
1786          */
1787
1788         switch (item_type) {
1789         case GainLineItem:
1790         case AutomationLineItem:
1791         case ControlPointItem:
1792                 /* these do not affect the current entered track state */
1793                 clear_entered_track = false;
1794                 break;
1795
1796         case AutomationTrackItem:
1797                 /* handled above already */
1798                 break;
1799
1800         default:
1801
1802                 break;
1803         }
1804
1805         return ret;
1806 }
1807
1808 bool
1809 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1810 {
1811         AutomationLine* al;
1812         Marker *marker;
1813         Location *loc;
1814         bool is_start;
1815         bool ret = true;
1816
1817         reset_canvas_cursor ();
1818
1819         switch (item_type) {
1820         case ControlPointItem:
1821                 _verbose_cursor->hide (); 
1822                 break;
1823
1824         case GainLineItem:
1825         case AutomationLineItem:
1826                 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1827                 {
1828                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1829                         if (line) {
1830                                 line->set_outline_color (al->get_line_color());
1831                         }
1832                 }
1833                 break;
1834
1835         case MarkerItem:
1836                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1837                         break;
1838                 }
1839                 entered_marker = 0;
1840                 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1841                         location_flags_changed (loc);
1842                 }
1843                 // fall through
1844         case MeterMarkerItem:
1845         case TempoMarkerItem:
1846                 break;
1847
1848         case FadeInTrimHandleItem:
1849         case FadeOutTrimHandleItem:
1850         case FadeInHandleItem:
1851         case FadeOutHandleItem:
1852         {
1853                 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1854                 if (rect) {
1855                         rect->set_fill_color (ARDOUR_UI::config()->get_InactiveFadeHandle());
1856                 }
1857         }
1858         break;
1859
1860         case AutomationTrackItem:
1861                 break;
1862
1863         case FeatureLineItem:
1864         {
1865                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1866                 line->set_outline_color (ARDOUR_UI::config()->get_ZeroLine());
1867         }
1868         break;
1869
1870         default:
1871                 break;
1872         }
1873
1874         return ret;
1875 }
1876
1877 void
1878 Editor::scrub (framepos_t frame, double current_x)
1879 {
1880         double delta;
1881
1882         if (scrubbing_direction == 0) {
1883                 /* first move */
1884                 _session->request_locate (frame, false);
1885                 _session->request_transport_speed (0.1);
1886                 scrubbing_direction = 1;
1887
1888         } else {
1889
1890                 if (last_scrub_x > current_x) {
1891
1892                         /* pointer moved to the left */
1893
1894                         if (scrubbing_direction > 0) {
1895
1896                                 /* we reversed direction to go backwards */
1897
1898                                 scrub_reversals++;
1899                                 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1900
1901                         } else {
1902
1903                                 /* still moving to the left (backwards) */
1904
1905                                 scrub_reversals = 0;
1906                                 scrub_reverse_distance = 0;
1907
1908                                 delta = 0.01 * (last_scrub_x - current_x);
1909                                 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1910                         }
1911
1912                 } else {
1913                         /* pointer moved to the right */
1914
1915                         if (scrubbing_direction < 0) {
1916                                 /* we reversed direction to go forward */
1917
1918                                 scrub_reversals++;
1919                                 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1920
1921                         } else {
1922                                 /* still moving to the right */
1923
1924                                 scrub_reversals = 0;
1925                                 scrub_reverse_distance = 0;
1926
1927                                 delta = 0.01 * (current_x - last_scrub_x);
1928                                 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1929                         }
1930                 }
1931
1932                 /* if there have been more than 2 opposite motion moves detected, or one that moves
1933                    back more than 10 pixels, reverse direction
1934                 */
1935
1936                 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1937
1938                         if (scrubbing_direction > 0) {
1939                                 /* was forwards, go backwards */
1940                                 _session->request_transport_speed (-0.1);
1941                                 scrubbing_direction = -1;
1942                         } else {
1943                                 /* was backwards, go forwards */
1944                                 _session->request_transport_speed (0.1);
1945                                 scrubbing_direction = 1;
1946                         }
1947
1948                         scrub_reverse_distance = 0;
1949                         scrub_reversals = 0;
1950                 }
1951         }
1952
1953         last_scrub_x = current_x;
1954 }
1955
1956 bool
1957 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1958 {
1959         _last_motion_y = event->motion.y;
1960
1961         if (event->motion.is_hint) {
1962                 gint x, y;
1963
1964                 /* We call this so that MOTION_NOTIFY events continue to be
1965                    delivered to the canvas. We need to do this because we set
1966                    Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1967                    the density of the events, at the expense of a round-trip
1968                    to the server. Given that this will mostly occur on cases
1969                    where DISPLAY = :0.0, and given the cost of what the motion
1970                    event might do, its a good tradeoff.
1971                 */
1972
1973                 _track_canvas->get_pointer (x, y);
1974         }
1975
1976         if (current_stepping_trackview) {
1977                 /* don't keep the persistent stepped trackview if the mouse moves */
1978                 current_stepping_trackview = 0;
1979                 step_timeout.disconnect ();
1980         }
1981         
1982         if (_session && _session->actively_recording()) {
1983                 /* Sorry. no dragging stuff around while we record */
1984                 return true;
1985         }
1986         
1987         update_join_object_range_location (event->motion.y);
1988         
1989         if (_drags->active ()) {
1990                 return _drags->motion_handler (event, from_autoscroll);
1991         }
1992
1993         return false;
1994 }
1995
1996 bool
1997 Editor::can_remove_control_point (ArdourCanvas::Item* item)
1998 {
1999         ControlPoint* control_point;
2000
2001         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2002                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2003                 abort(); /*NOTREACHED*/
2004         }
2005
2006         AutomationLine& line = control_point->line ();
2007         if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2008                 /* we shouldn't remove the first or last gain point in region gain lines */
2009                 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2010                         return false;
2011                 }
2012         }
2013
2014         return true;
2015 }
2016
2017 void
2018 Editor::remove_control_point (ArdourCanvas::Item* item)
2019 {
2020         if (!can_remove_control_point (item)) {
2021                 return;
2022         }
2023
2024         ControlPoint* control_point;
2025
2026         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2027                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2028                 abort(); /*NOTREACHED*/
2029         }
2030
2031         control_point->line().remove_point (*control_point);
2032 }
2033
2034 void
2035 Editor::edit_control_point (ArdourCanvas::Item* item)
2036 {
2037         ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2038
2039         if (p == 0) {
2040                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2041                 abort(); /*NOTREACHED*/
2042         }
2043
2044         ControlPointDialog d (p);
2045         ensure_float (d);
2046
2047         if (d.run () != RESPONSE_ACCEPT) {
2048                 return;
2049         }
2050
2051         p->line().modify_point_y (*p, d.get_y_fraction ());
2052 }
2053
2054 void
2055 Editor::edit_notes (TimeAxisViewItem& tavi)
2056 {
2057         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2058
2059         if (!mrv) {
2060                 return;
2061         }
2062
2063         MidiRegionView::Selection const & s = mrv->selection();
2064
2065         if (s.empty ()) {
2066                 return;
2067         }
2068
2069         EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2070         d->show_all ();
2071         ensure_float (*d);
2072
2073         d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2074 }
2075
2076 void
2077 Editor::note_edit_done (int r, EditNoteDialog* d)
2078 {
2079         d->done (r);
2080         delete d;
2081 }
2082
2083 void
2084 Editor::visible_order_range (int* low, int* high) const
2085 {
2086         *low = TimeAxisView::max_order ();
2087         *high = 0;
2088
2089         for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2090
2091                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2092
2093                 if (!rtv->hidden()) {
2094
2095                         if (*high < rtv->order()) {
2096                                 *high = rtv->order ();
2097                         }
2098
2099                         if (*low > rtv->order()) {
2100                                 *low = rtv->order ();
2101                         }
2102                 }
2103         }
2104 }
2105
2106 void
2107 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2108 {
2109         /* Either add to or set the set the region selection, unless
2110            this is an alignment click (control used)
2111         */
2112
2113         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2114                 TimeAxisView* tv = &rv.get_time_axis_view();
2115                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2116                 double speed = 1.0;
2117                 if (rtv && rtv->is_track()) {
2118                         speed = rtv->track()->speed();
2119                 }
2120
2121                 framepos_t where = get_preferred_edit_position();
2122
2123                 if (where >= 0) {
2124
2125                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2126
2127                                 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2128
2129                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2130
2131                                 align_region (rv.region(), End, (framepos_t) (where * speed));
2132
2133                         } else {
2134
2135                                 align_region (rv.region(), Start, (framepos_t) (where * speed));
2136                         }
2137                 }
2138         }
2139 }
2140
2141 void
2142 Editor::collect_new_region_view (RegionView* rv)
2143 {
2144         latest_regionviews.push_back (rv);
2145 }
2146
2147 void
2148 Editor::collect_and_select_new_region_view (RegionView* rv)
2149 {
2150         selection->add(rv);
2151         latest_regionviews.push_back (rv);
2152 }
2153
2154 void
2155 Editor::cancel_selection ()
2156 {
2157         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2158                 (*i)->hide_selection ();
2159         }
2160
2161         selection->clear ();
2162         clicked_selection = 0;
2163 }
2164
2165 void
2166 Editor::cancel_time_selection ()
2167 {
2168         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2169                 (*i)->hide_selection ();
2170         }
2171         selection->time.clear ();
2172         clicked_selection = 0;
2173 }       
2174
2175 void
2176 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2177 {
2178         RegionView* rv = clicked_regionview;
2179
2180         /* Choose action dependant on which button was pressed */
2181         switch (event->button.button) {
2182         case 1:
2183                 begin_reversible_command (_("start point trim"));
2184
2185                 if (selection->selected (rv)) {
2186                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2187                              i != selection->regions.by_layer().end(); ++i)
2188                         {
2189                                 if (!(*i)->region()->locked()) {
2190                                         (*i)->region()->clear_changes ();
2191                                         (*i)->region()->trim_front (new_bound);
2192                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2193                                 }
2194                         }
2195
2196                 } else {
2197                         if (!rv->region()->locked()) {
2198                                 rv->region()->clear_changes ();
2199                                 rv->region()->trim_front (new_bound);
2200                                 _session->add_command(new StatefulDiffCommand (rv->region()));
2201                         }
2202                 }
2203
2204                 commit_reversible_command();
2205
2206                 break;
2207         case 2:
2208                 begin_reversible_command (_("End point trim"));
2209
2210                 if (selection->selected (rv)) {
2211
2212                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2213                         {
2214                                 if (!(*i)->region()->locked()) {
2215                                         (*i)->region()->clear_changes();
2216                                         (*i)->region()->trim_end (new_bound);
2217                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2218                                 }
2219                         }
2220
2221                 } else {
2222
2223                         if (!rv->region()->locked()) {
2224                                 rv->region()->clear_changes ();
2225                                 rv->region()->trim_end (new_bound);
2226                                 _session->add_command (new StatefulDiffCommand (rv->region()));
2227                         }
2228                 }
2229
2230                 commit_reversible_command();
2231
2232                 break;
2233         default:
2234                 break;
2235         }
2236 }
2237
2238 void
2239 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2240 {
2241         Marker* marker;
2242         bool is_start;
2243
2244         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2245                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2246                 abort(); /*NOTREACHED*/
2247         }
2248
2249         Location* location = find_location_from_marker (marker, is_start);
2250         location->set_hidden (true, this);
2251 }
2252
2253 gint
2254 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2255 {
2256         using namespace Gtkmm2ext;
2257
2258         ArdourPrompter prompter (false);
2259
2260         prompter.set_prompt (_("Name for region:"));
2261         prompter.set_initial_text (clicked_regionview->region()->name());
2262         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2263         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2264         prompter.show_all ();
2265         switch (prompter.run ()) {
2266         case Gtk::RESPONSE_ACCEPT:
2267                 string str;
2268                 prompter.get_result(str);
2269                 if (str.length()) {
2270                         clicked_regionview->region()->set_name (str);
2271                 }
2272                 break;
2273         }
2274         return true;
2275 }
2276
2277
2278 void
2279 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2280 {
2281         /* no brushing without a useful snap setting */
2282
2283         switch (_snap_mode) {
2284         case SnapMagnetic:
2285                 return; /* can't work because it allows region to be placed anywhere */
2286         default:
2287                 break; /* OK */
2288         }
2289
2290         switch (_snap_type) {
2291         case SnapToMark:
2292                 return;
2293
2294         default:
2295                 break;
2296         }
2297
2298         /* don't brush a copy over the original */
2299
2300         if (pos == rv->region()->position()) {
2301                 return;
2302         }
2303
2304         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2305
2306         if (rtv == 0 || !rtv->is_track()) {
2307                 return;
2308         }
2309
2310         boost::shared_ptr<Playlist> playlist = rtv->playlist();
2311         double speed = rtv->track()->speed();
2312
2313         playlist->clear_changes ();
2314         boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2315         playlist->add_region (new_region, (framepos_t) (pos * speed));
2316         _session->add_command (new StatefulDiffCommand (playlist));
2317
2318         // playlist is frozen, so we have to update manually XXX this is disgusting
2319
2320         playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2321 }
2322
2323 gint
2324 Editor::track_height_step_timeout ()
2325 {
2326         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2327                 current_stepping_trackview = 0;
2328                 return false;
2329         }
2330         return true;
2331 }
2332
2333 void
2334 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2335 {
2336         assert (region_view);
2337
2338         if (!region_view->region()->playlist()) {
2339                 return;
2340         }
2341
2342         switch (Config->get_edit_mode()) {
2343                 case Splice:
2344                         _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2345                         break;
2346                 case Ripple:
2347                         _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2348                         break;
2349                 default:
2350                         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2351                         break;
2352
2353         }
2354 }
2355
2356 void
2357 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2358 {
2359         assert (region_view);
2360
2361         if (!region_view->region()->playlist()) {
2362                 return;
2363         }
2364
2365         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2366 }
2367
2368 void
2369 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2370 {
2371         assert (region_view);
2372
2373         if (!region_view->region()->playlist()) {
2374                 return;
2375         }
2376
2377         if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2378                 return;
2379         }
2380
2381         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2382
2383         begin_reversible_command (Operations::drag_region_brush);
2384 }
2385
2386 /** Start a grab where a time range is selected, track(s) are selected, and the
2387  *  user clicks and drags a region with a modifier in order to create a new region containing
2388  *  the section of the clicked region that lies within the time range.
2389  */
2390 void
2391 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2392 {
2393         if (clicked_regionview == 0) {
2394                 return;
2395         }
2396
2397         /* lets try to create new Region for the selection */
2398
2399         vector<boost::shared_ptr<Region> > new_regions;
2400         create_region_from_selection (new_regions);
2401
2402         if (new_regions.empty()) {
2403                 return;
2404         }
2405
2406         /* XXX fix me one day to use all new regions */
2407
2408         boost::shared_ptr<Region> region (new_regions.front());
2409
2410         /* add it to the current stream/playlist.
2411
2412            tricky: the streamview for the track will add a new regionview. we will
2413            catch the signal it sends when it creates the regionview to
2414            set the regionview we want to then drag.
2415         */
2416
2417         latest_regionviews.clear();
2418         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2419
2420         /* A selection grab currently creates two undo/redo operations, one for
2421            creating the new region and another for moving it.
2422         */
2423
2424         begin_reversible_command (Operations::selection_grab);
2425
2426         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2427
2428         playlist->clear_changes ();
2429         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2430         _session->add_command(new StatefulDiffCommand (playlist));
2431
2432         commit_reversible_command ();
2433
2434         c.disconnect ();
2435
2436         if (latest_regionviews.empty()) {
2437                 /* something went wrong */
2438                 return;
2439         }
2440
2441         /* we need to deselect all other regionviews, and select this one
2442            i'm ignoring undo stuff, because the region creation will take care of it
2443         */
2444         selection->set (latest_regionviews);
2445
2446         _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2447 }
2448
2449 void
2450 Editor::escape ()
2451 {
2452         if (_drags->active ()) {
2453                 _drags->abort ();
2454         } else {
2455                 selection->clear ();
2456         }
2457
2458         reset_focus ();
2459 }
2460
2461 void
2462 Editor::set_internal_edit (bool yn)
2463 {
2464         if (_internal_editing == yn) {
2465                 return;
2466         }
2467
2468         _internal_editing = yn;
2469
2470         if (yn) {
2471                 pre_internal_mouse_mode = mouse_mode;
2472                 pre_internal_snap_type = _snap_type;
2473                 pre_internal_snap_mode = _snap_mode;
2474
2475                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2476                         (*i)->enter_internal_edit_mode ();
2477                 }
2478
2479                 set_snap_to (internal_snap_type);
2480                 set_snap_mode (internal_snap_mode);
2481
2482         } else {
2483
2484                 internal_snap_mode = _snap_mode;
2485                 internal_snap_type = _snap_type;
2486
2487                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2488                         (*i)->leave_internal_edit_mode ();
2489                 }
2490
2491                 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2492                         /* we were drawing .. flip back to something sensible */
2493                         set_mouse_mode (pre_internal_mouse_mode);
2494                 }
2495
2496                 set_snap_to (pre_internal_snap_type);
2497                 set_snap_mode (pre_internal_snap_mode);
2498         }
2499
2500         reset_canvas_cursor ();
2501         MouseModeChanged ();
2502 }
2503
2504 /** Update _join_object_range_state which indicate whether we are over the top
2505  *  or bottom half of a route view, used by the `join object/range' tool
2506  *  mode. Coordinates in canvas space.
2507  */
2508 void
2509 Editor::update_join_object_range_location (double y)
2510 {
2511         if (_internal_editing || !get_smart_mode()) {
2512                 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2513                 return;
2514         }
2515
2516         JoinObjectRangeState const old = _join_object_range_state;
2517
2518         if (mouse_mode == MouseObject) {
2519                 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2520         } else if (mouse_mode == MouseRange) {
2521                 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2522         }
2523
2524         if (entered_regionview) {
2525
2526                 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2527                 double const c = item_space.y / entered_regionview->height();
2528                         
2529                 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2530                 
2531                 if (_join_object_range_state != old) {
2532                         set_canvas_cursor (which_track_cursor ());
2533                 }
2534
2535         } else if (entered_track) {
2536
2537                 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2538                 
2539                 if (entered_route_view) {
2540
2541                         double cx = 0;
2542                         double cy = y;
2543
2544                         entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2545
2546                         double track_height = entered_route_view->view()->child_height();
2547                         if (Config->get_show_name_highlight()) {
2548                                 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2549                         }
2550                         double const c = cy / track_height;
2551
2552
2553                         if (c <= 0.5) {
2554                                 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2555                         } else {
2556                                 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2557                         }
2558
2559                 } else {
2560                         /* Other kinds of tracks use object mode */
2561                         _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2562                 }
2563
2564                 if (_join_object_range_state != old) {
2565                         set_canvas_cursor (which_track_cursor ());
2566                 }
2567         }
2568 }
2569
2570 Editing::MouseMode
2571 Editor::effective_mouse_mode () const
2572 {
2573         if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2574                 return MouseObject;
2575         } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2576                 return MouseRange;
2577         }
2578
2579         return mouse_mode;
2580 }
2581
2582 void
2583 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2584 {
2585         NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2586         assert (e);
2587
2588         e->region_view().delete_note (e->note ());
2589 }
2590
2591 /** Obtain the pointer position in canvas coordinates */
2592 void
2593 Editor::get_pointer_position (double& x, double& y) const
2594 {
2595         int px, py;
2596         _track_canvas->get_pointer (px, py);
2597         _track_canvas->window_to_canvas (px, py, x, y);
2598 }