Make scroll work on MIDI regions when internal.
[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                         switch (item_type) {
1565                         case AutomationTrackItem:
1566                                 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1567                                 if (atv) {
1568                                         bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1569                                         atv->add_automation_event (event, where, event->button.y, with_guard_points);
1570                                 }
1571                                 return true;
1572                                 break;
1573                         default:
1574                                 break;
1575                         }
1576                         break;
1577
1578                 case MouseGain:
1579                         switch (item_type) {
1580                         case RegionItem:
1581                         {
1582                                 /* check that we didn't drag before releasing, since
1583                                    its really annoying to create new control
1584                                    points when doing this.
1585                                 */
1586                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1587                                 if (!were_dragging && arv) {
1588                                         bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1589                                         arv->add_gain_point_event (item, event, with_guard_points);
1590                                 }
1591                                 return true;
1592                                 break;
1593                         }
1594
1595                         case AutomationTrackItem: {
1596                                 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1597                                 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1598                                         add_automation_event (event, where, event->button.y, with_guard_points);
1599                                 return true;
1600                                 break;
1601                         }
1602                         default:
1603                                 break;
1604                         }
1605                         break;
1606
1607                 case MouseAudition:
1608                         pop_canvas_cursor ();
1609                         if (scrubbing_direction == 0) {
1610                                 /* no drag, just a click */
1611                                 switch (item_type) {
1612                                 case RegionItem:
1613                                         play_selected_region ();
1614                                         break;
1615                                 default:
1616                                         break;
1617                                 }
1618                         } else {
1619                                 /* make sure we stop */
1620                                 _session->request_transport_speed (0.0);
1621                         }
1622                         break;
1623
1624                 default:
1625                         break;
1626
1627                 }
1628
1629                 /* do any (de)selection operations that should occur on button release */
1630                 button_selection (item, event, item_type);
1631                 return true;
1632                 break;
1633
1634
1635         case 2:
1636                 switch (eff) {
1637
1638                 case MouseObject:
1639                         switch (item_type) {
1640                         case RegionItem:
1641                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1642                                         raise_region ();
1643                                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1644                                         lower_region ();
1645                                 } else {
1646                                         // Button2 click is unused
1647                                 }
1648                                 return true;
1649
1650                                 break;
1651
1652                         default:
1653                                 break;
1654                         }
1655                         break;
1656
1657                 case MouseDraw:
1658                         return true;
1659                         
1660                 case MouseRange:
1661                         // x_style_paste (where, 1.0);
1662                         return true;
1663                         break;
1664
1665                 default:
1666                         break;
1667                 }
1668
1669                 break;
1670
1671         case 3:
1672                 break;
1673
1674         default:
1675                 break;
1676         }
1677
1678         return false;
1679 }
1680
1681 bool
1682 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1683 {
1684         ControlPoint* cp;
1685         Marker * marker;
1686         double fraction;
1687         bool ret = true;
1688
1689         /* by the time we reach here, entered_regionview and entered trackview
1690          * will have already been set as appropriate. Things are done this 
1691          * way because this method isn't passed a pointer to a variable type of
1692          * thing that is entered (which may or may not be canvas item).
1693          * (e.g. the actual entered regionview)
1694          */
1695
1696         choose_canvas_cursor_on_entry (&event->crossing, item_type);
1697
1698         switch (item_type) {
1699         case ControlPointItem:
1700                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1701                         cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1702                         cp->show ();
1703
1704                         fraction = 1.0 - (cp->get_y() / cp->line().height());
1705
1706                         _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1707                         _verbose_cursor->show ();
1708                 }
1709                 break;
1710
1711         case GainLineItem:
1712                 if (mouse_mode == MouseGain) {
1713                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1714                         if (line) {
1715                                 line->set_outline_color (ARDOUR_UI::config()->get_EnteredGainLine());
1716                         }
1717                 }
1718                 break;
1719
1720         case AutomationLineItem:
1721                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1722                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1723                         if (line) {
1724                                 line->set_outline_color (ARDOUR_UI::config()->get_EnteredAutomationLine());
1725                         }
1726                 }
1727                 break;
1728
1729         case AutomationTrackItem:
1730                 AutomationTimeAxisView* atv;
1731                 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1732                         clear_entered_track = false;
1733                         set_entered_track (atv);
1734                 }
1735                 break;
1736
1737         case MarkerItem:
1738                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1739                         break;
1740                 }
1741                 entered_marker = marker;
1742                 marker->set_color_rgba (ARDOUR_UI::config()->get_EnteredMarker());
1743                 // fall through
1744         case MeterMarkerItem:
1745         case TempoMarkerItem:
1746                 break;
1747
1748         case FadeInHandleItem:
1749         case FadeInTrimHandleItem:
1750                 if (mouse_mode == MouseObject && !internal_editing()) {
1751                         ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1752                         if (rect) {
1753                                 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1754                                 rect->set_fill_color (rv->get_fill_color());
1755                         }
1756                 }
1757                 break;
1758
1759         case FadeOutHandleItem:
1760         case FadeOutTrimHandleItem:
1761                 if (mouse_mode == MouseObject && !internal_editing()) {
1762                         ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1763                         if (rect) {
1764                                 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1765                                 rect->set_fill_color (rv->get_fill_color ());
1766                         }
1767                 }
1768                 break;
1769
1770         case FeatureLineItem:
1771         {
1772                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1773                 line->set_outline_color (0xFF0000FF);
1774         }
1775         break;
1776
1777         case SelectionItem:
1778                 break;
1779
1780         default:
1781                 break;
1782         }
1783
1784         /* third pass to handle entered track status in a comprehensible way.
1785          */
1786
1787         switch (item_type) {
1788         case GainLineItem:
1789         case AutomationLineItem:
1790         case ControlPointItem:
1791                 /* these do not affect the current entered track state */
1792                 clear_entered_track = false;
1793                 break;
1794
1795         case AutomationTrackItem:
1796                 /* handled above already */
1797                 break;
1798
1799         default:
1800
1801                 break;
1802         }
1803
1804         return ret;
1805 }
1806
1807 bool
1808 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1809 {
1810         AutomationLine* al;
1811         Marker *marker;
1812         Location *loc;
1813         bool is_start;
1814         bool ret = true;
1815
1816         reset_canvas_cursor ();
1817
1818         switch (item_type) {
1819         case ControlPointItem:
1820                 _verbose_cursor->hide (); 
1821                 break;
1822
1823         case GainLineItem:
1824         case AutomationLineItem:
1825                 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1826                 {
1827                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1828                         if (line) {
1829                                 line->set_outline_color (al->get_line_color());
1830                         }
1831                 }
1832                 break;
1833
1834         case MarkerItem:
1835                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1836                         break;
1837                 }
1838                 entered_marker = 0;
1839                 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1840                         location_flags_changed (loc);
1841                 }
1842                 // fall through
1843         case MeterMarkerItem:
1844         case TempoMarkerItem:
1845                 break;
1846
1847         case FadeInTrimHandleItem:
1848         case FadeOutTrimHandleItem:
1849         case FadeInHandleItem:
1850         case FadeOutHandleItem:
1851         {
1852                 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1853                 if (rect) {
1854                         rect->set_fill_color (ARDOUR_UI::config()->get_InactiveFadeHandle());
1855                 }
1856         }
1857         break;
1858
1859         case AutomationTrackItem:
1860                 break;
1861
1862         case FeatureLineItem:
1863         {
1864                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1865                 line->set_outline_color (ARDOUR_UI::config()->get_ZeroLine());
1866         }
1867         break;
1868
1869         default:
1870                 break;
1871         }
1872
1873         return ret;
1874 }
1875
1876 void
1877 Editor::scrub (framepos_t frame, double current_x)
1878 {
1879         double delta;
1880
1881         if (scrubbing_direction == 0) {
1882                 /* first move */
1883                 _session->request_locate (frame, false);
1884                 _session->request_transport_speed (0.1);
1885                 scrubbing_direction = 1;
1886
1887         } else {
1888
1889                 if (last_scrub_x > current_x) {
1890
1891                         /* pointer moved to the left */
1892
1893                         if (scrubbing_direction > 0) {
1894
1895                                 /* we reversed direction to go backwards */
1896
1897                                 scrub_reversals++;
1898                                 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1899
1900                         } else {
1901
1902                                 /* still moving to the left (backwards) */
1903
1904                                 scrub_reversals = 0;
1905                                 scrub_reverse_distance = 0;
1906
1907                                 delta = 0.01 * (last_scrub_x - current_x);
1908                                 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1909                         }
1910
1911                 } else {
1912                         /* pointer moved to the right */
1913
1914                         if (scrubbing_direction < 0) {
1915                                 /* we reversed direction to go forward */
1916
1917                                 scrub_reversals++;
1918                                 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1919
1920                         } else {
1921                                 /* still moving to the right */
1922
1923                                 scrub_reversals = 0;
1924                                 scrub_reverse_distance = 0;
1925
1926                                 delta = 0.01 * (current_x - last_scrub_x);
1927                                 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1928                         }
1929                 }
1930
1931                 /* if there have been more than 2 opposite motion moves detected, or one that moves
1932                    back more than 10 pixels, reverse direction
1933                 */
1934
1935                 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1936
1937                         if (scrubbing_direction > 0) {
1938                                 /* was forwards, go backwards */
1939                                 _session->request_transport_speed (-0.1);
1940                                 scrubbing_direction = -1;
1941                         } else {
1942                                 /* was backwards, go forwards */
1943                                 _session->request_transport_speed (0.1);
1944                                 scrubbing_direction = 1;
1945                         }
1946
1947                         scrub_reverse_distance = 0;
1948                         scrub_reversals = 0;
1949                 }
1950         }
1951
1952         last_scrub_x = current_x;
1953 }
1954
1955 bool
1956 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1957 {
1958         _last_motion_y = event->motion.y;
1959
1960         if (event->motion.is_hint) {
1961                 gint x, y;
1962
1963                 /* We call this so that MOTION_NOTIFY events continue to be
1964                    delivered to the canvas. We need to do this because we set
1965                    Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1966                    the density of the events, at the expense of a round-trip
1967                    to the server. Given that this will mostly occur on cases
1968                    where DISPLAY = :0.0, and given the cost of what the motion
1969                    event might do, its a good tradeoff.
1970                 */
1971
1972                 _track_canvas->get_pointer (x, y);
1973         }
1974
1975         if (current_stepping_trackview) {
1976                 /* don't keep the persistent stepped trackview if the mouse moves */
1977                 current_stepping_trackview = 0;
1978                 step_timeout.disconnect ();
1979         }
1980         
1981         if (_session && _session->actively_recording()) {
1982                 /* Sorry. no dragging stuff around while we record */
1983                 return true;
1984         }
1985         
1986         update_join_object_range_location (event->motion.y);
1987         
1988         if (_drags->active ()) {
1989                 return _drags->motion_handler (event, from_autoscroll);
1990         }
1991
1992         return false;
1993 }
1994
1995 bool
1996 Editor::can_remove_control_point (ArdourCanvas::Item* item)
1997 {
1998         ControlPoint* control_point;
1999
2000         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2001                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2002                 abort(); /*NOTREACHED*/
2003         }
2004
2005         AutomationLine& line = control_point->line ();
2006         if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2007                 /* we shouldn't remove the first or last gain point in region gain lines */
2008                 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2009                         return false;
2010                 }
2011         }
2012
2013         return true;
2014 }
2015
2016 void
2017 Editor::remove_control_point (ArdourCanvas::Item* item)
2018 {
2019         if (!can_remove_control_point (item)) {
2020                 return;
2021         }
2022
2023         ControlPoint* control_point;
2024
2025         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2026                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2027                 abort(); /*NOTREACHED*/
2028         }
2029
2030         control_point->line().remove_point (*control_point);
2031 }
2032
2033 void
2034 Editor::edit_control_point (ArdourCanvas::Item* item)
2035 {
2036         ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2037
2038         if (p == 0) {
2039                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2040                 abort(); /*NOTREACHED*/
2041         }
2042
2043         ControlPointDialog d (p);
2044         ensure_float (d);
2045
2046         if (d.run () != RESPONSE_ACCEPT) {
2047                 return;
2048         }
2049
2050         p->line().modify_point_y (*p, d.get_y_fraction ());
2051 }
2052
2053 void
2054 Editor::edit_notes (TimeAxisViewItem& tavi)
2055 {
2056         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2057
2058         if (!mrv) {
2059                 return;
2060         }
2061
2062         MidiRegionView::Selection const & s = mrv->selection();
2063
2064         if (s.empty ()) {
2065                 return;
2066         }
2067
2068         EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2069         d->show_all ();
2070         ensure_float (*d);
2071
2072         d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2073 }
2074
2075 void
2076 Editor::note_edit_done (int r, EditNoteDialog* d)
2077 {
2078         d->done (r);
2079         delete d;
2080 }
2081
2082 void
2083 Editor::visible_order_range (int* low, int* high) const
2084 {
2085         *low = TimeAxisView::max_order ();
2086         *high = 0;
2087
2088         for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2089
2090                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2091
2092                 if (!rtv->hidden()) {
2093
2094                         if (*high < rtv->order()) {
2095                                 *high = rtv->order ();
2096                         }
2097
2098                         if (*low > rtv->order()) {
2099                                 *low = rtv->order ();
2100                         }
2101                 }
2102         }
2103 }
2104
2105 void
2106 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2107 {
2108         /* Either add to or set the set the region selection, unless
2109            this is an alignment click (control used)
2110         */
2111
2112         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2113                 TimeAxisView* tv = &rv.get_time_axis_view();
2114                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2115                 double speed = 1.0;
2116                 if (rtv && rtv->is_track()) {
2117                         speed = rtv->track()->speed();
2118                 }
2119
2120                 framepos_t where = get_preferred_edit_position();
2121
2122                 if (where >= 0) {
2123
2124                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2125
2126                                 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2127
2128                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2129
2130                                 align_region (rv.region(), End, (framepos_t) (where * speed));
2131
2132                         } else {
2133
2134                                 align_region (rv.region(), Start, (framepos_t) (where * speed));
2135                         }
2136                 }
2137         }
2138 }
2139
2140 void
2141 Editor::collect_new_region_view (RegionView* rv)
2142 {
2143         latest_regionviews.push_back (rv);
2144 }
2145
2146 void
2147 Editor::collect_and_select_new_region_view (RegionView* rv)
2148 {
2149         selection->add(rv);
2150         latest_regionviews.push_back (rv);
2151 }
2152
2153 void
2154 Editor::cancel_selection ()
2155 {
2156         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2157                 (*i)->hide_selection ();
2158         }
2159
2160         selection->clear ();
2161         clicked_selection = 0;
2162 }
2163
2164 void
2165 Editor::cancel_time_selection ()
2166 {
2167         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2168                 (*i)->hide_selection ();
2169         }
2170         selection->time.clear ();
2171         clicked_selection = 0;
2172 }       
2173
2174 void
2175 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2176 {
2177         RegionView* rv = clicked_regionview;
2178
2179         /* Choose action dependant on which button was pressed */
2180         switch (event->button.button) {
2181         case 1:
2182                 begin_reversible_command (_("start point trim"));
2183
2184                 if (selection->selected (rv)) {
2185                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2186                              i != selection->regions.by_layer().end(); ++i)
2187                         {
2188                                 if (!(*i)->region()->locked()) {
2189                                         (*i)->region()->clear_changes ();
2190                                         (*i)->region()->trim_front (new_bound);
2191                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2192                                 }
2193                         }
2194
2195                 } else {
2196                         if (!rv->region()->locked()) {
2197                                 rv->region()->clear_changes ();
2198                                 rv->region()->trim_front (new_bound);
2199                                 _session->add_command(new StatefulDiffCommand (rv->region()));
2200                         }
2201                 }
2202
2203                 commit_reversible_command();
2204
2205                 break;
2206         case 2:
2207                 begin_reversible_command (_("End point trim"));
2208
2209                 if (selection->selected (rv)) {
2210
2211                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2212                         {
2213                                 if (!(*i)->region()->locked()) {
2214                                         (*i)->region()->clear_changes();
2215                                         (*i)->region()->trim_end (new_bound);
2216                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2217                                 }
2218                         }
2219
2220                 } else {
2221
2222                         if (!rv->region()->locked()) {
2223                                 rv->region()->clear_changes ();
2224                                 rv->region()->trim_end (new_bound);
2225                                 _session->add_command (new StatefulDiffCommand (rv->region()));
2226                         }
2227                 }
2228
2229                 commit_reversible_command();
2230
2231                 break;
2232         default:
2233                 break;
2234         }
2235 }
2236
2237 void
2238 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2239 {
2240         Marker* marker;
2241         bool is_start;
2242
2243         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2244                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2245                 abort(); /*NOTREACHED*/
2246         }
2247
2248         Location* location = find_location_from_marker (marker, is_start);
2249         location->set_hidden (true, this);
2250 }
2251
2252 gint
2253 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2254 {
2255         using namespace Gtkmm2ext;
2256
2257         ArdourPrompter prompter (false);
2258
2259         prompter.set_prompt (_("Name for region:"));
2260         prompter.set_initial_text (clicked_regionview->region()->name());
2261         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2262         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2263         prompter.show_all ();
2264         switch (prompter.run ()) {
2265         case Gtk::RESPONSE_ACCEPT:
2266                 string str;
2267                 prompter.get_result(str);
2268                 if (str.length()) {
2269                         clicked_regionview->region()->set_name (str);
2270                 }
2271                 break;
2272         }
2273         return true;
2274 }
2275
2276
2277 void
2278 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2279 {
2280         /* no brushing without a useful snap setting */
2281
2282         switch (_snap_mode) {
2283         case SnapMagnetic:
2284                 return; /* can't work because it allows region to be placed anywhere */
2285         default:
2286                 break; /* OK */
2287         }
2288
2289         switch (_snap_type) {
2290         case SnapToMark:
2291                 return;
2292
2293         default:
2294                 break;
2295         }
2296
2297         /* don't brush a copy over the original */
2298
2299         if (pos == rv->region()->position()) {
2300                 return;
2301         }
2302
2303         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2304
2305         if (rtv == 0 || !rtv->is_track()) {
2306                 return;
2307         }
2308
2309         boost::shared_ptr<Playlist> playlist = rtv->playlist();
2310         double speed = rtv->track()->speed();
2311
2312         playlist->clear_changes ();
2313         boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2314         playlist->add_region (new_region, (framepos_t) (pos * speed));
2315         _session->add_command (new StatefulDiffCommand (playlist));
2316
2317         // playlist is frozen, so we have to update manually XXX this is disgusting
2318
2319         playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2320 }
2321
2322 gint
2323 Editor::track_height_step_timeout ()
2324 {
2325         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2326                 current_stepping_trackview = 0;
2327                 return false;
2328         }
2329         return true;
2330 }
2331
2332 void
2333 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2334 {
2335         assert (region_view);
2336
2337         if (!region_view->region()->playlist()) {
2338                 return;
2339         }
2340
2341         switch (Config->get_edit_mode()) {
2342                 case Splice:
2343                         _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2344                         break;
2345                 case Ripple:
2346                         _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2347                         break;
2348                 default:
2349                         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2350                         break;
2351
2352         }
2353 }
2354
2355 void
2356 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2357 {
2358         assert (region_view);
2359
2360         if (!region_view->region()->playlist()) {
2361                 return;
2362         }
2363
2364         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2365 }
2366
2367 void
2368 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2369 {
2370         assert (region_view);
2371
2372         if (!region_view->region()->playlist()) {
2373                 return;
2374         }
2375
2376         if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2377                 return;
2378         }
2379
2380         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2381
2382         begin_reversible_command (Operations::drag_region_brush);
2383 }
2384
2385 /** Start a grab where a time range is selected, track(s) are selected, and the
2386  *  user clicks and drags a region with a modifier in order to create a new region containing
2387  *  the section of the clicked region that lies within the time range.
2388  */
2389 void
2390 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2391 {
2392         if (clicked_regionview == 0) {
2393                 return;
2394         }
2395
2396         /* lets try to create new Region for the selection */
2397
2398         vector<boost::shared_ptr<Region> > new_regions;
2399         create_region_from_selection (new_regions);
2400
2401         if (new_regions.empty()) {
2402                 return;
2403         }
2404
2405         /* XXX fix me one day to use all new regions */
2406
2407         boost::shared_ptr<Region> region (new_regions.front());
2408
2409         /* add it to the current stream/playlist.
2410
2411            tricky: the streamview for the track will add a new regionview. we will
2412            catch the signal it sends when it creates the regionview to
2413            set the regionview we want to then drag.
2414         */
2415
2416         latest_regionviews.clear();
2417         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2418
2419         /* A selection grab currently creates two undo/redo operations, one for
2420            creating the new region and another for moving it.
2421         */
2422
2423         begin_reversible_command (Operations::selection_grab);
2424
2425         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2426
2427         playlist->clear_changes ();
2428         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2429         _session->add_command(new StatefulDiffCommand (playlist));
2430
2431         commit_reversible_command ();
2432
2433         c.disconnect ();
2434
2435         if (latest_regionviews.empty()) {
2436                 /* something went wrong */
2437                 return;
2438         }
2439
2440         /* we need to deselect all other regionviews, and select this one
2441            i'm ignoring undo stuff, because the region creation will take care of it
2442         */
2443         selection->set (latest_regionviews);
2444
2445         _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2446 }
2447
2448 void
2449 Editor::escape ()
2450 {
2451         if (_drags->active ()) {
2452                 _drags->abort ();
2453         } else {
2454                 selection->clear ();
2455         }
2456
2457         reset_focus ();
2458 }
2459
2460 void
2461 Editor::set_internal_edit (bool yn)
2462 {
2463         if (_internal_editing == yn) {
2464                 return;
2465         }
2466
2467         _internal_editing = yn;
2468
2469         if (yn) {
2470                 pre_internal_mouse_mode = mouse_mode;
2471                 pre_internal_snap_type = _snap_type;
2472                 pre_internal_snap_mode = _snap_mode;
2473
2474                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2475                         (*i)->enter_internal_edit_mode ();
2476                 }
2477
2478                 set_snap_to (internal_snap_type);
2479                 set_snap_mode (internal_snap_mode);
2480
2481         } else {
2482
2483                 internal_snap_mode = _snap_mode;
2484                 internal_snap_type = _snap_type;
2485
2486                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2487                         (*i)->leave_internal_edit_mode ();
2488                 }
2489
2490                 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2491                         /* we were drawing .. flip back to something sensible */
2492                         set_mouse_mode (pre_internal_mouse_mode);
2493                 }
2494
2495                 set_snap_to (pre_internal_snap_type);
2496                 set_snap_mode (pre_internal_snap_mode);
2497         }
2498
2499         reset_canvas_cursor ();
2500         MouseModeChanged ();
2501 }
2502
2503 /** Update _join_object_range_state which indicate whether we are over the top
2504  *  or bottom half of a route view, used by the `join object/range' tool
2505  *  mode. Coordinates in canvas space.
2506  */
2507 void
2508 Editor::update_join_object_range_location (double y)
2509 {
2510         if (_internal_editing || !get_smart_mode()) {
2511                 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2512                 return;
2513         }
2514
2515         JoinObjectRangeState const old = _join_object_range_state;
2516
2517         if (mouse_mode == MouseObject) {
2518                 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2519         } else if (mouse_mode == MouseRange) {
2520                 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2521         }
2522
2523         if (entered_regionview) {
2524
2525                 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2526                 double const c = item_space.y / entered_regionview->height();
2527                         
2528                 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2529                 
2530                 if (_join_object_range_state != old) {
2531                         set_canvas_cursor (which_track_cursor ());
2532                 }
2533
2534         } else if (entered_track) {
2535
2536                 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2537                 
2538                 if (entered_route_view) {
2539
2540                         double cx = 0;
2541                         double cy = y;
2542
2543                         entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2544
2545                         double track_height = entered_route_view->view()->child_height();
2546                         if (Config->get_show_name_highlight()) {
2547                                 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2548                         }
2549                         double const c = cy / track_height;
2550
2551
2552                         if (c <= 0.5) {
2553                                 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2554                         } else {
2555                                 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2556                         }
2557
2558                 } else {
2559                         /* Other kinds of tracks use object mode */
2560                         _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2561                 }
2562
2563                 if (_join_object_range_state != old) {
2564                         set_canvas_cursor (which_track_cursor ());
2565                 }
2566         }
2567 }
2568
2569 Editing::MouseMode
2570 Editor::effective_mouse_mode () const
2571 {
2572         if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2573                 return MouseObject;
2574         } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2575                 return MouseRange;
2576         }
2577
2578         return mouse_mode;
2579 }
2580
2581 void
2582 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2583 {
2584         NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2585         assert (e);
2586
2587         e->region_view().delete_note (e->note ());
2588 }
2589
2590 /** Obtain the pointer position in canvas coordinates */
2591 void
2592 Editor::get_pointer_position (double& x, double& y) const
2593 {
2594         int px, py;
2595         _track_canvas->get_pointer (px, py);
2596         _track_canvas->window_to_canvas (px, py, x, y);
2597 }