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