Merge branch 'waveview_hacks' of https://github.com/nmains/ardour into cairocanvas
[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                                 double const y = event->button.y;
780                                 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
781                                 if (tvp.first) {
782                                         AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
783                                         if ( get_smart_mode() && atv) {
784                                                 /* smart "join" mode: drag automation */
785                                                 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
786                                         } else {
787                                                 /* this was debated, but decided the more common action was to
788                                                    make a new selection */
789                                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
790                                         }
791                                 }
792                         }
793                         break;
794
795                 case StreamItem:
796                         if (internal_editing()) {
797                                 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
798                                         _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
799                                         return true;
800                                 } 
801                         } else {
802                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
803                                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
804                                 } else {
805                                         _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
806                                 }
807                                 return true;
808                         }
809                         break;
810
811                 case RegionViewNameHighlight:
812                         if (!clicked_regionview->region()->locked()) {
813                                 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
814                                 return true;
815                         }
816                         break;
817
818                 default:
819                         if (!internal_editing()) {
820                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
821                                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
822                                 } else {
823                                         _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
824                                 }
825                         }
826                 }
827                 return true;
828                 break;
829
830         case MouseDraw:
831                 switch (item_type) {
832                 case NoteItem:
833                         /* Existing note: allow trimming/motion */
834                         if (internal_editing()) {
835                                 /* trim notes if we're in internal edit mode and near the ends of the note */
836                                 NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
837                                 assert (cn);
838                                 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
839                                         _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
840                                 } else {
841                                         _drags->set (new NoteDrag (this, item), event);
842                                 }
843                                 return true;
844                         } 
845                         break;
846                 case StreamItem:
847                         if (internal_editing()) {
848                                 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
849                                         _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
850                                 }
851                                 return true;
852                         }
853                         break;
854
855                 default:
856                         break;
857                 }
858                 break;
859
860         case MouseCut:
861                 switch (item_type) {
862                 case RegionItem:
863                 case FadeInHandleItem:
864                 case FadeOutHandleItem:
865                 case LeftFrameHandle:
866                 case RightFrameHandle:
867                 case FeatureLineItem:
868                 case RegionViewNameHighlight:
869                 case RegionViewName:
870                 case StreamItem:
871                 case AutomationTrackItem:
872                         _drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, current_canvas_cursor);
873                         return true;
874                         break;
875                 default:
876                         break;
877                 }
878                 break;
879
880         case MouseObject:
881                 switch (item_type) {
882                 case NoteItem:
883                         /* Existing note: allow trimming/motion */
884                         if (internal_editing()) {
885                                 NoteBase* cn = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
886                                 assert (cn);
887                                 if (cn->big_enough_to_trim() && cn->mouse_near_ends()) {
888                                         _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
889                                 } else {
890                                         _drags->set (new NoteDrag (this, item), event);
891                                 }
892                                 return true;
893                         }
894                         break;
895
896                 default:
897                         break;
898                 }
899
900                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
901                     event->type == GDK_BUTTON_PRESS) {
902
903                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
904
905                 } else if (event->type == GDK_BUTTON_PRESS) {
906
907                         switch (item_type) {
908                         case FadeInHandleItem:
909                         {
910                                 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
911                                 return true;
912                         }
913
914                         case FadeOutHandleItem:
915                         {
916                                 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
917                                 return true;
918                         }
919
920                         case StartCrossFadeItem:
921                         case EndCrossFadeItem:
922                                 /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor.  for not this is not fully implemented */ 
923 //                              if (!clicked_regionview->region()->locked()) {
924 //                                      _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
925 //                                      return true;
926 //                              }
927                                 break;
928
929                         case FeatureLineItem:
930                         {
931                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
932                                         remove_transient(item);
933                                         return true;
934                                 }
935
936                                 _drags->set (new FeatureLineDrag (this, item), event);
937                                 return true;
938                                 break;
939                         }
940
941                         case RegionItem:
942                                 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
943                                         /* click on an automation region view; do nothing here and let the ARV's signal handler
944                                            sort it out.
945                                         */
946                                         break;
947                                 }
948
949                                 if (internal_editing ()) {
950                                         break;
951                                 }
952
953                                 /* click on a normal region view */
954                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
955                                         add_region_copy_drag (item, event, clicked_regionview);
956                                 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
957                                         add_region_brush_drag (item, event, clicked_regionview);
958                                 } else {
959                                         add_region_drag (item, event, clicked_regionview);
960                                 }
961
962
963 //                              if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
964 //                                      _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
965 //                              }
966
967                                 _drags->start_grab (event);
968                                 return true;
969                                 break;
970
971                         case RegionViewNameHighlight:
972                         case LeftFrameHandle:
973                         case RightFrameHandle:
974                                 if (!clicked_regionview->region()->locked()) {
975                                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
976                                         return true;
977                                 }
978                                 break;
979
980                         case FadeInTrimHandleItem:
981                         case FadeOutTrimHandleItem:
982                                 if (!clicked_regionview->region()->locked()) {
983                                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
984                                         return true;
985                                 }
986                                 break;
987
988                         case RegionViewName:
989                         {
990                                 /* rename happens on edit clicks */
991                                 if (clicked_regionview->get_name_highlight()) {
992                                         _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
993                                         return true;
994                                 }
995                                 break;
996                         }
997
998                         case ControlPointItem:
999                                 _drags->set (new ControlPointDrag (this, item), event);
1000                                 return true;
1001                                 break;
1002
1003                         case AutomationLineItem:
1004                                 _drags->set (new LineDrag (this, item), event);
1005                                 return true;
1006                                 break;
1007
1008                         case StreamItem:
1009                                 if (internal_editing()) {
1010                                         if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
1011                                                 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
1012                                         }
1013                                         return true;
1014                                 } else {
1015                                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1016                                 }
1017                                 break;
1018
1019                         case AutomationTrackItem:
1020                         {
1021                                 TimeAxisView* parent = clicked_axisview->get_parent ();
1022                                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1023                                 assert (atv);
1024                                 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1025
1026                                         RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
1027                                         assert (p);
1028                                         boost::shared_ptr<Playlist> pl = p->track()->playlist ();
1029                                         if (pl->n_regions() == 0) {
1030                                                 /* Parent has no regions; create one so that we have somewhere to put automation */
1031                                                 _drags->set (new RegionCreateDrag (this, item, parent), event);
1032                                         } else {
1033                                                 /* See if there's a region before the click that we can extend, and extend it if so */
1034                                                 framepos_t const t = canvas_event_sample (event);
1035                                                 boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
1036                                                 if (!prev) {
1037                                                         _drags->set (new RegionCreateDrag (this, item, parent), event);
1038                                                 } else {
1039                                                         prev->set_length (t - prev->position ());
1040                                                 }
1041                                         }
1042                                 } else {
1043                                         /* rubberband drag to select automation points */
1044                                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1045                                 }
1046                                 break;
1047                         }
1048
1049                         case SelectionItem:
1050                         {
1051                                 if ( get_smart_mode() ) {
1052                                         /* we're in "smart" joined mode, and we've clicked on a Selection */
1053                                         double const y = event->button.y;
1054                                         pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1055                                         if (tvp.first) {
1056                                                 /* if we're over an automation track, start a drag of its data */
1057                                                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1058                                                 if (atv) {
1059                                                         _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1060                                                 }
1061
1062                                                 /* if we're over a track and a region, and in the `object' part of a region,
1063                                                    put a selection around the region and drag both
1064                                                 */
1065 /*                                              RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1066                                                 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1067                                                         boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1068                                                         if (t) {
1069                                                                 boost::shared_ptr<Playlist> pl = t->playlist ();
1070                                                                 if (pl) {
1071
1072                                                                         boost::shared_ptr<Region> r = pl->top_region_at (canvas_event_sample (event));
1073                                                                         if (r) {
1074                                                                                 RegionView* rv = rtv->view()->find_view (r);
1075                                                                                 clicked_selection = select_range (rv->region()->position(), 
1076                                                                                                                   rv->region()->last_frame()+1);
1077                                                                                 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1078                                                                                 list<RegionView*> rvs;
1079                                                                                 rvs.push_back (rv);
1080                                                                                 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1081                                                                                 _drags->start_grab (event);
1082                                                                                 return true;
1083                                                                         }
1084                                                                 }
1085                                                         }
1086                                                 }
1087 */
1088                                         }
1089                                 }
1090                                 break;
1091                         }
1092
1093                         case MarkerBarItem:
1094
1095                                 break;
1096
1097                         default:
1098                                 break;
1099                         }
1100                 }
1101                 return true;
1102                 break;
1103
1104         case MouseGain:
1105                 switch (item_type) {
1106                 case GainLineItem:
1107                         _drags->set (new LineDrag (this, item), event);
1108                         return true;
1109
1110                 case ControlPointItem:
1111                         _drags->set (new ControlPointDrag (this, item), event);
1112                         return true;
1113                         break;
1114
1115                 case SelectionItem:
1116                 {
1117                         AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1118                         if (arv) {
1119                                 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1120                                 _drags->start_grab (event);
1121                         }
1122                         return true;
1123                         break;
1124                 }
1125
1126                 case AutomationLineItem:
1127                         _drags->set (new LineDrag (this, item), event);
1128                         break;
1129                         
1130                 default:
1131                         break;
1132                 }
1133                 return true;
1134                 break;
1135
1136         case MouseZoom:
1137                 if (event->type == GDK_BUTTON_PRESS) {
1138                         _drags->set (new MouseZoomDrag (this, item), event);
1139                 }
1140
1141                 return true;
1142                 break;
1143
1144         case MouseTimeFX:
1145                 if (internal_editing() && item_type == NoteItem ) {
1146                         /* drag notes if we're in internal edit mode */
1147                         NoteBase* cn = reinterpret_cast<NoteBase*>(item->get_data ("notebase"));
1148                         assert (cn);
1149                         if (cn->big_enough_to_trim()) {
1150                                 _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1151                         }
1152                         return true;
1153                 } else if (clicked_regionview) {
1154                         /* do time-FX  */
1155                         _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1156                         return true;
1157                 }
1158                 break;
1159
1160         case MouseAudition:
1161                 _drags->set (new ScrubDrag (this, item), event);
1162                 scrub_reversals = 0;
1163                 scrub_reverse_distance = 0;
1164                 last_scrub_x = event->button.x;
1165                 scrubbing_direction = 0;
1166                 push_canvas_cursor (_cursors->transparent);
1167                 return true;
1168                 break;
1169
1170         default:
1171                 break;
1172         }
1173
1174         return false;
1175 }
1176
1177 bool
1178 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1179 {
1180         Editing::MouseMode const eff = effective_mouse_mode ();
1181         switch (eff) {
1182         case MouseObject:
1183                 switch (item_type) {
1184                 case RegionItem:
1185                         if (internal_editing ()) {
1186                                 /* no region drags in internal edit mode */
1187                                 return false;
1188                         }
1189
1190                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1191                                 add_region_copy_drag (item, event, clicked_regionview);
1192                         } else {
1193                                 add_region_drag (item, event, clicked_regionview);
1194                         }
1195                         _drags->start_grab (event);
1196                         return true;
1197                         break;
1198                 case ControlPointItem:
1199                         _drags->set (new ControlPointDrag (this, item), event);
1200                         return true;
1201                         break;
1202
1203                 default:
1204                         break;
1205                 }
1206
1207                 switch (item_type) {
1208                 case RegionViewNameHighlight:
1209                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1210                         return true;
1211                         break;
1212
1213                 case LeftFrameHandle:
1214                 case RightFrameHandle:
1215                         if (!internal_editing ()) {
1216                                 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1217                         }
1218                         return true;
1219                         break;
1220
1221                 case RegionViewName:
1222                         _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1223                         return true;
1224                         break;
1225
1226                 default:
1227                         break;
1228                 }
1229
1230                 break;
1231
1232         case MouseDraw:
1233                 return false;
1234
1235         case MouseRange:
1236                 /* relax till release */
1237                 return true;
1238                 break;
1239
1240
1241         case MouseZoom:
1242                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1243                         temporal_zoom_to_frame (false, canvas_event_sample (event));
1244                 } else {
1245                         temporal_zoom_to_frame (true, canvas_event_sample(event));
1246                 }
1247                 return true;
1248                 break;
1249
1250         default:
1251                 break;
1252         }
1253
1254         return false;
1255 }
1256    
1257 bool
1258 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1259 {
1260         if (event->type == GDK_2BUTTON_PRESS) {
1261                 _drags->mark_double_click ();
1262                 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1263                 return true;
1264         }
1265
1266         if (event->type != GDK_BUTTON_PRESS) {
1267                 return false;
1268         }
1269
1270         pre_press_cursor = current_canvas_cursor;
1271
1272         _track_canvas->grab_focus();
1273
1274         if (_session && _session->actively_recording()) {
1275                 return true;
1276         }
1277
1278         if (internal_editing()) {
1279                 bool leave_internal_edit_mode = false;
1280
1281                 switch (item_type) {
1282                 case NoteItem:
1283                         break;
1284
1285                 case RegionItem:
1286                         if (!dynamic_cast<MidiRegionView*> (clicked_regionview) && !dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
1287                                 leave_internal_edit_mode = true;
1288                         }
1289                         break;
1290
1291                 case PlayheadCursorItem:
1292                 case MarkerItem:
1293                 case TempoMarkerItem:
1294                 case MeterMarkerItem:
1295                 case MarkerBarItem:
1296                 case TempoBarItem:
1297                 case MeterBarItem:
1298                 case RangeMarkerBarItem:
1299                 case CdMarkerBarItem:
1300                 case TransportMarkerBarItem:
1301                 case StreamItem:
1302                 case TimecodeRulerItem:
1303                 case SamplesRulerItem:
1304                 case MinsecRulerItem:
1305                 case BBTRulerItem:
1306                         /* button press on these items never does anything to
1307                            change the editing mode.
1308                         */
1309                         break;
1310
1311                 default:
1312                         break;
1313                 }
1314                 
1315                 if (leave_internal_edit_mode) {
1316                         ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1317                 }
1318         }
1319
1320         button_selection (item, event, item_type);
1321
1322         if (!_drags->active () &&
1323             (Keyboard::is_delete_event (&event->button) ||
1324              Keyboard::is_context_menu_event (&event->button) ||
1325              Keyboard::is_edit_event (&event->button))) {
1326
1327                 /* handled by button release */
1328                 return true;
1329         }
1330
1331         //not rolling, range mode click + join_play_range :  locate the PH here
1332         if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && Config->get_follow_edits() ) {
1333                 framepos_t where = canvas_event_sample (event);
1334                 snap_to(where);
1335                 _session->request_locate (where, false);
1336         }
1337
1338         switch (event->button.button) {
1339         case 1:
1340                 return button_press_handler_1 (item, event, item_type);
1341                 break;
1342
1343         case 2:
1344                 return button_press_handler_2 (item, event, item_type);
1345                 break;
1346
1347         case 3:
1348                 break;
1349
1350         default:
1351                 return button_press_dispatch (&event->button);
1352                 break;
1353
1354         }
1355
1356         return false;
1357 }
1358
1359 bool
1360 Editor::button_press_dispatch (GdkEventButton* ev)
1361 {
1362         /* this function is intended only for buttons 4 and above.
1363          */
1364
1365         Gtkmm2ext::MouseButton b (ev->state, ev->button);
1366         return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1367 }
1368
1369 bool
1370 Editor::button_release_dispatch (GdkEventButton* ev)
1371 {
1372         /* this function is intended only for buttons 4 and above.
1373          */
1374
1375         Gtkmm2ext::MouseButton b (ev->state, ev->button);
1376         return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1377 }
1378
1379 bool
1380 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1381 {
1382         framepos_t where = canvas_event_sample (event);
1383         AutomationTimeAxisView* atv = 0;
1384
1385         if (pre_press_cursor) {
1386                 set_canvas_cursor (pre_press_cursor);
1387                 pre_press_cursor = 0;
1388         }
1389
1390         /* no action if we're recording */
1391
1392         if (_session && _session->actively_recording()) {
1393                 return true;
1394         }
1395
1396         /* see if we're finishing a drag */
1397
1398         bool were_dragging = false;
1399         if (_drags->active ()) {
1400                 bool const r = _drags->end_grab (event);
1401                 if (r) {
1402                         /* grab dragged, so do nothing else */
1403                         return true;
1404                 }
1405
1406                 were_dragging = true;
1407         }
1408
1409         update_region_layering_order_editor ();
1410
1411         /* edit events get handled here */
1412
1413         if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1414                 switch (item_type) {
1415                 case RegionItem:
1416                         show_region_properties ();
1417                         break;
1418
1419                 case TempoMarkerItem: {
1420                         Marker* marker;
1421                         TempoMarker* tempo_marker;
1422                         
1423                         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1424                                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1425                                 /*NOTREACHED*/
1426                         }
1427                         
1428                         if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1429                                 fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1430                                 /*NOTREACHED*/
1431                         }
1432                         
1433                         edit_tempo_marker (*tempo_marker);
1434                         break;
1435                 }
1436
1437                 case MeterMarkerItem: {
1438                         Marker* marker;
1439                         MeterMarker* meter_marker;
1440                         
1441                         if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1442                                 fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1443                                 /*NOTREACHED*/
1444                         }
1445                         
1446                         if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1447                                 fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1448                                 /*NOTREACHED*/
1449                         }
1450                         edit_meter_marker (*meter_marker);
1451                         break;
1452                 }
1453
1454                 case RegionViewName:
1455                         if (clicked_regionview->name_active()) {
1456                                 return mouse_rename_region (item, event);
1457                         }
1458                         break;
1459
1460                 case ControlPointItem:
1461                         edit_control_point (item);
1462                         break;
1463
1464                 default:
1465                         break;
1466                 }
1467                 return true;
1468         }
1469
1470         /* context menu events get handled here */
1471         if (Keyboard::is_context_menu_event (&event->button)) {
1472
1473                 context_click_event = *event;
1474
1475                 if (!_drags->active ()) {
1476
1477                         /* no matter which button pops up the context menu, tell the menu
1478                            widget to use button 1 to drive menu selection.
1479                         */
1480
1481                         switch (item_type) {
1482                         case FadeInItem:
1483                         case FadeInHandleItem:
1484                         case FadeInTrimHandleItem:
1485                         case StartCrossFadeItem:
1486                         case LeftFrameHandle:
1487                                 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1488                                 break;
1489
1490                         case FadeOutItem:
1491                         case FadeOutHandleItem:
1492                         case FadeOutTrimHandleItem:
1493                         case EndCrossFadeItem:
1494                         case RightFrameHandle:
1495                                 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1496                                 break;
1497
1498                         case StreamItem:
1499                                 popup_track_context_menu (1, event->button.time, item_type, false);
1500                                 break;
1501
1502                         case RegionItem:
1503                         case RegionViewNameHighlight:
1504                         case RegionViewName:
1505                                 popup_track_context_menu (1, event->button.time, item_type, false);
1506                                 break;
1507
1508                         case SelectionItem:
1509                                 popup_track_context_menu (1, event->button.time, item_type, true);
1510                                 break;
1511                                 
1512                         case AutomationTrackItem:
1513                                 popup_track_context_menu (1, event->button.time, item_type, false);
1514                                 break;
1515
1516                         case MarkerBarItem:
1517                         case RangeMarkerBarItem:
1518                         case TransportMarkerBarItem:
1519                         case CdMarkerBarItem:
1520                         case TempoBarItem:
1521                         case MeterBarItem:
1522                         case VideoBarItem:
1523                         case TimecodeRulerItem:
1524                         case SamplesRulerItem:
1525                         case MinsecRulerItem:
1526                         case BBTRulerItem:
1527                                 popup_ruler_menu (where, item_type);
1528                                 break;
1529
1530                         case MarkerItem:
1531                                 marker_context_menu (&event->button, item);
1532                                 break;
1533
1534                         case TempoMarkerItem:
1535                                 tempo_or_meter_marker_context_menu (&event->button, item);
1536                                 break;
1537
1538                         case MeterMarkerItem:
1539                                 tempo_or_meter_marker_context_menu (&event->button, item);
1540                                 break;
1541
1542                         case CrossfadeViewItem:
1543                                 popup_track_context_menu (1, event->button.time, item_type, false);
1544                                 break;
1545
1546                         case ControlPointItem:
1547                                 popup_control_point_context_menu (item, event);
1548                                 break;
1549
1550                         default:
1551                                 break;
1552                         }
1553
1554                         return true;
1555                 }
1556         }
1557
1558         /* delete events get handled here */
1559
1560         Editing::MouseMode const eff = effective_mouse_mode ();
1561
1562         if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1563
1564                 switch (item_type) {
1565                 case TempoMarkerItem:
1566                         remove_tempo_marker (item);
1567                         break;
1568
1569                 case MeterMarkerItem:
1570                         remove_meter_marker (item);
1571                         break;
1572
1573                 case MarkerItem:
1574                         remove_marker (*item, event);
1575                         break;
1576
1577                 case RegionItem:
1578                         if (eff == MouseObject) {
1579                                 remove_clicked_region ();
1580                         }
1581                         break;
1582
1583                 case ControlPointItem:
1584                         remove_control_point (item);
1585                         break;
1586
1587                 case NoteItem:
1588                         remove_midi_note (item, event);
1589                         break;
1590
1591                 default:
1592                         break;
1593                 }
1594                 return true;
1595         }
1596
1597         switch (event->button.button) {
1598         case 1:
1599
1600                 switch (item_type) {
1601                 /* see comments in button_press_handler */
1602                 case PlayheadCursorItem:
1603                 case MarkerItem:
1604                 case GainLineItem:
1605                 case AutomationLineItem:
1606                 case StartSelectionTrimItem:
1607                 case EndSelectionTrimItem:
1608                         return true;
1609
1610                 case MarkerBarItem:
1611                         if (!_dragging_playhead) {
1612                                 snap_to_with_modifier (where, event, 0, true);
1613                                 mouse_add_new_marker (where);
1614                         }
1615                         return true;
1616
1617                 case CdMarkerBarItem:
1618                         if (!_dragging_playhead) {
1619                                 // if we get here then a dragged range wasn't done
1620                                 snap_to_with_modifier (where, event, 0, true);
1621                                 mouse_add_new_marker (where, true);
1622                         }
1623                         return true;
1624
1625                 case TempoBarItem:
1626                         if (!_dragging_playhead) {
1627                                 snap_to_with_modifier (where, event);
1628                                 mouse_add_new_tempo_event (where);
1629                         }
1630                         return true;
1631
1632                 case MeterBarItem:
1633                         if (!_dragging_playhead) {
1634                                 mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1635                         }
1636                         return true;
1637                         break;
1638
1639                 case TimecodeRulerItem:
1640                 case SamplesRulerItem:
1641                 case MinsecRulerItem:
1642                 case BBTRulerItem:
1643                         return true;
1644                         break;
1645
1646                 default:
1647                         break;
1648                 }
1649
1650                 switch (eff) {
1651                 case MouseObject:
1652                         switch (item_type) {
1653                         case AutomationTrackItem:
1654                                 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1655                                 if (atv) {
1656                                         bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1657                                         atv->add_automation_event (event, where, event->button.y, with_guard_points);
1658                                 }
1659                                 return true;
1660                                 break;
1661                         default:
1662                                 break;
1663                         }
1664                         break;
1665
1666                 case MouseGain:
1667                         switch (item_type) {
1668                         case RegionItem:
1669                         {
1670                                 /* check that we didn't drag before releasing, since
1671                                    its really annoying to create new control
1672                                    points when doing this.
1673                                 */
1674                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1675                                 if (!were_dragging && arv) {
1676                                         bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1677                                         arv->add_gain_point_event (item, event, with_guard_points);
1678                                 }
1679                                 return true;
1680                                 break;
1681                         }
1682
1683                         case AutomationTrackItem: {
1684                                 bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1685                                 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1686                                         add_automation_event (event, where, event->button.y, with_guard_points);
1687                                 return true;
1688                                 break;
1689                         }
1690                         default:
1691                                 break;
1692                         }
1693                         break;
1694
1695                 case MouseAudition:
1696                         pop_canvas_cursor ();
1697                         if (scrubbing_direction == 0) {
1698                                 /* no drag, just a click */
1699                                 switch (item_type) {
1700                                 case RegionItem:
1701                                         play_selected_region ();
1702                                         break;
1703                                 default:
1704                                         break;
1705                                 }
1706                         } else {
1707                                 /* make sure we stop */
1708                                 _session->request_transport_speed (0.0);
1709                         }
1710                         break;
1711
1712                 default:
1713                         break;
1714
1715                 }
1716
1717                 /* do any (de)selection operations that should occur on button release */
1718                 button_selection (item, event, item_type);
1719                 return true;
1720                 break;
1721
1722
1723         case 2:
1724                 switch (eff) {
1725
1726                 case MouseObject:
1727                         switch (item_type) {
1728                         case RegionItem:
1729                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1730                                         raise_region ();
1731                                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1732                                         lower_region ();
1733                                 } else {
1734                                         // Button2 click is unused
1735                                 }
1736                                 return true;
1737
1738                                 break;
1739
1740                         default:
1741                                 break;
1742                         }
1743                         break;
1744
1745                 case MouseDraw:
1746                         return true;
1747                         
1748                 case MouseRange:
1749                         // x_style_paste (where, 1.0);
1750                         return true;
1751                         break;
1752
1753                 default:
1754                         break;
1755                 }
1756
1757                 break;
1758
1759         case 3:
1760                 break;
1761
1762         default:
1763                 break;
1764         }
1765
1766         return false;
1767 }
1768
1769 bool
1770 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1771 {
1772         ControlPoint* cp;
1773         Marker * marker;
1774         double fraction;
1775         bool ret = true;
1776
1777         /* by the time we reach here, entered_regionview and entered trackview
1778          * will have already been set as appropriate. Things are done this 
1779          * way because this method isn't passed a pointer to a variable type of
1780          * thing that is entered (which may or may not be canvas item).
1781          * (e.g. the actual entered regionview)
1782          */
1783
1784         choose_canvas_cursor_on_entry (&event->crossing, item_type);
1785
1786         switch (item_type) {
1787         case ControlPointItem:
1788                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1789                         cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1790                         cp->show ();
1791
1792                         fraction = 1.0 - (cp->get_y() / cp->line().height());
1793
1794                         _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1795                         _verbose_cursor->show ();
1796                 }
1797                 break;
1798
1799         case GainLineItem:
1800                 if (mouse_mode == MouseGain) {
1801                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1802                         if (line) {
1803                                 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
1804                         }
1805                 }
1806                 break;
1807
1808         case AutomationLineItem:
1809                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1810                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1811                         if (line) {
1812                                 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
1813                         }
1814                 }
1815                 break;
1816
1817         case AutomationTrackItem:
1818                 AutomationTimeAxisView* atv;
1819                 if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1820                         clear_entered_track = false;
1821                         set_entered_track (atv);
1822                 }
1823                 break;
1824
1825         case MarkerItem:
1826                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1827                         break;
1828                 }
1829                 entered_marker = marker;
1830                 marker->set_color_rgba (ARDOUR_UI::config()->get_canvasvar_EnteredMarker());
1831                 // fall through
1832         case MeterMarkerItem:
1833         case TempoMarkerItem:
1834                 break;
1835
1836         case FadeInHandleItem:
1837         case FadeInTrimHandleItem:
1838                 if (mouse_mode == MouseObject && !internal_editing()) {
1839                         ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1840                         if (rect) {
1841                                 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1842                                 rect->set_fill_color (rv->get_fill_color());
1843                         }
1844                 }
1845                 break;
1846
1847         case FadeOutHandleItem:
1848         case FadeOutTrimHandleItem:
1849                 if (mouse_mode == MouseObject && !internal_editing()) {
1850                         ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1851                         if (rect) {
1852                                 RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1853                                 rect->set_fill_color (rv->get_fill_color ());
1854                         }
1855                 }
1856                 break;
1857
1858         case FeatureLineItem:
1859         {
1860                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1861                 line->set_outline_color (0xFF0000FF);
1862         }
1863         break;
1864
1865         case SelectionItem:
1866                 break;
1867
1868         default:
1869                 break;
1870         }
1871
1872         /* third pass to handle entered track status in a comprehensible way.
1873          */
1874
1875         switch (item_type) {
1876         case GainLineItem:
1877         case AutomationLineItem:
1878         case ControlPointItem:
1879                 /* these do not affect the current entered track state */
1880                 clear_entered_track = false;
1881                 break;
1882
1883         case AutomationTrackItem:
1884                 /* handled above already */
1885                 break;
1886
1887         default:
1888
1889                 break;
1890         }
1891
1892         return ret;
1893 }
1894
1895 bool
1896 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1897 {
1898         AutomationLine* al;
1899         Marker *marker;
1900         Location *loc;
1901         bool is_start;
1902         bool ret = true;
1903
1904         switch (item_type) {
1905         case ControlPointItem:
1906                 _verbose_cursor->hide (); 
1907                 break;
1908
1909         case GainLineItem:
1910         case AutomationLineItem:
1911                 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1912                 {
1913                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1914                         if (line) {
1915                                 line->set_outline_color (al->get_line_color());
1916                         }
1917                 }
1918                 break;
1919
1920         case MarkerItem:
1921                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1922                         break;
1923                 }
1924                 entered_marker = 0;
1925                 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1926                         location_flags_changed (loc, this);
1927                 }
1928                 // fall through
1929         case MeterMarkerItem:
1930         case TempoMarkerItem:
1931                 break;
1932
1933         case FadeInTrimHandleItem:
1934         case FadeOutTrimHandleItem:
1935         case FadeInHandleItem:
1936         case FadeOutHandleItem:
1937         {
1938                 ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1939                 if (rect) {
1940                         rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
1941                 }
1942         }
1943         break;
1944
1945         case AutomationTrackItem:
1946                 break;
1947
1948         case FeatureLineItem:
1949         {
1950                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1951                 line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
1952         }
1953         break;
1954
1955         default:
1956                 break;
1957         }
1958
1959         return ret;
1960 }
1961
1962 void
1963 Editor::scrub (framepos_t frame, double current_x)
1964 {
1965         double delta;
1966
1967         if (scrubbing_direction == 0) {
1968                 /* first move */
1969                 _session->request_locate (frame, false);
1970                 _session->request_transport_speed (0.1);
1971                 scrubbing_direction = 1;
1972
1973         } else {
1974
1975                 if (last_scrub_x > current_x) {
1976
1977                         /* pointer moved to the left */
1978
1979                         if (scrubbing_direction > 0) {
1980
1981                                 /* we reversed direction to go backwards */
1982
1983                                 scrub_reversals++;
1984                                 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1985
1986                         } else {
1987
1988                                 /* still moving to the left (backwards) */
1989
1990                                 scrub_reversals = 0;
1991                                 scrub_reverse_distance = 0;
1992
1993                                 delta = 0.01 * (last_scrub_x - current_x);
1994                                 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1995                         }
1996
1997                 } else {
1998                         /* pointer moved to the right */
1999
2000                         if (scrubbing_direction < 0) {
2001                                 /* we reversed direction to go forward */
2002
2003                                 scrub_reversals++;
2004                                 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2005
2006                         } else {
2007                                 /* still moving to the right */
2008
2009                                 scrub_reversals = 0;
2010                                 scrub_reverse_distance = 0;
2011
2012                                 delta = 0.01 * (current_x - last_scrub_x);
2013                                 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2014                         }
2015                 }
2016
2017                 /* if there have been more than 2 opposite motion moves detected, or one that moves
2018                    back more than 10 pixels, reverse direction
2019                 */
2020
2021                 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2022
2023                         if (scrubbing_direction > 0) {
2024                                 /* was forwards, go backwards */
2025                                 _session->request_transport_speed (-0.1);
2026                                 scrubbing_direction = -1;
2027                         } else {
2028                                 /* was backwards, go forwards */
2029                                 _session->request_transport_speed (0.1);
2030                                 scrubbing_direction = 1;
2031                         }
2032
2033                         scrub_reverse_distance = 0;
2034                         scrub_reversals = 0;
2035                 }
2036         }
2037
2038         last_scrub_x = current_x;
2039 }
2040
2041 bool
2042 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2043 {
2044         _last_motion_y = event->motion.y;
2045
2046         if (event->motion.is_hint) {
2047                 gint x, y;
2048
2049                 /* We call this so that MOTION_NOTIFY events continue to be
2050                    delivered to the canvas. We need to do this because we set
2051                    Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2052                    the density of the events, at the expense of a round-trip
2053                    to the server. Given that this will mostly occur on cases
2054                    where DISPLAY = :0.0, and given the cost of what the motion
2055                    event might do, its a good tradeoff.
2056                 */
2057
2058                 _track_canvas->get_pointer (x, y);
2059         }
2060
2061         if (current_stepping_trackview) {
2062                 /* don't keep the persistent stepped trackview if the mouse moves */
2063                 current_stepping_trackview = 0;
2064                 step_timeout.disconnect ();
2065         }
2066         
2067         if (_session && _session->actively_recording()) {
2068                 /* Sorry. no dragging stuff around while we record */
2069                 return true;
2070         }
2071         
2072         update_join_object_range_location (event->motion.y);
2073         
2074         if (_drags->active ()) {
2075                 return _drags->motion_handler (event, from_autoscroll);
2076         }
2077
2078         return false;
2079 }
2080
2081 bool
2082 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2083 {
2084         ControlPoint* control_point;
2085
2086         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2087                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2088                 /*NOTREACHED*/
2089         }
2090
2091         AutomationLine& line = control_point->line ();
2092         if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2093                 /* we shouldn't remove the first or last gain point in region gain lines */
2094                 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2095                         return false;
2096                 }
2097         }
2098
2099         return true;
2100 }
2101
2102 void
2103 Editor::remove_control_point (ArdourCanvas::Item* item)
2104 {
2105         if (!can_remove_control_point (item)) {
2106                 return;
2107         }
2108
2109         ControlPoint* control_point;
2110
2111         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2112                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2113                 /*NOTREACHED*/
2114         }
2115
2116         control_point->line().remove_point (*control_point);
2117 }
2118
2119 void
2120 Editor::edit_control_point (ArdourCanvas::Item* item)
2121 {
2122         ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2123
2124         if (p == 0) {
2125                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2126                 /*NOTREACHED*/
2127         }
2128
2129         ControlPointDialog d (p);
2130         ensure_float (d);
2131
2132         if (d.run () != RESPONSE_ACCEPT) {
2133                 return;
2134         }
2135
2136         p->line().modify_point_y (*p, d.get_y_fraction ());
2137 }
2138
2139 void
2140 Editor::edit_notes (TimeAxisViewItem& tavi)
2141 {
2142         MidiRegionView* mrv = dynamic_cast<MidiRegionView*>(&tavi);
2143
2144         if (!mrv) {
2145                 return;
2146         }
2147
2148         MidiRegionView::Selection const & s = mrv->selection();
2149
2150         if (s.empty ()) {
2151                 return;
2152         }
2153
2154         EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
2155         d->show_all ();
2156         ensure_float (*d);
2157
2158         d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
2159 }
2160
2161 void
2162 Editor::note_edit_done (int r, EditNoteDialog* d)
2163 {
2164         d->done (r);
2165         delete d;
2166 }
2167
2168 void
2169 Editor::visible_order_range (int* low, int* high) const
2170 {
2171         *low = TimeAxisView::max_order ();
2172         *high = 0;
2173
2174         for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2175
2176                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2177
2178                 if (!rtv->hidden()) {
2179
2180                         if (*high < rtv->order()) {
2181                                 *high = rtv->order ();
2182                         }
2183
2184                         if (*low > rtv->order()) {
2185                                 *low = rtv->order ();
2186                         }
2187                 }
2188         }
2189 }
2190
2191 void
2192 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2193 {
2194         /* Either add to or set the set the region selection, unless
2195            this is an alignment click (control used)
2196         */
2197
2198         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2199                 TimeAxisView* tv = &rv.get_time_axis_view();
2200                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2201                 double speed = 1.0;
2202                 if (rtv && rtv->is_track()) {
2203                         speed = rtv->track()->speed();
2204                 }
2205
2206                 framepos_t where = get_preferred_edit_position();
2207
2208                 if (where >= 0) {
2209
2210                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2211
2212                                 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2213
2214                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2215
2216                                 align_region (rv.region(), End, (framepos_t) (where * speed));
2217
2218                         } else {
2219
2220                                 align_region (rv.region(), Start, (framepos_t) (where * speed));
2221                         }
2222                 }
2223         }
2224 }
2225
2226 void
2227 Editor::collect_new_region_view (RegionView* rv)
2228 {
2229         latest_regionviews.push_back (rv);
2230 }
2231
2232 void
2233 Editor::collect_and_select_new_region_view (RegionView* rv)
2234 {
2235         selection->add(rv);
2236         latest_regionviews.push_back (rv);
2237 }
2238
2239 void
2240 Editor::cancel_selection ()
2241 {
2242         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2243                 (*i)->hide_selection ();
2244         }
2245
2246         selection->clear ();
2247         clicked_selection = 0;
2248 }
2249
2250 void
2251 Editor::cancel_time_selection ()
2252 {
2253         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2254                 (*i)->hide_selection ();
2255         }
2256         selection->time.clear ();
2257         clicked_selection = 0;
2258 }       
2259
2260 void
2261 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2262 {
2263         RegionView* rv = clicked_regionview;
2264
2265         /* Choose action dependant on which button was pressed */
2266         switch (event->button.button) {
2267         case 1:
2268                 begin_reversible_command (_("start point trim"));
2269
2270                 if (selection->selected (rv)) {
2271                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2272                              i != selection->regions.by_layer().end(); ++i)
2273                         {
2274                                 if (!(*i)->region()->locked()) {
2275                                         (*i)->region()->clear_changes ();
2276                                         (*i)->region()->trim_front (new_bound);
2277                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2278                                 }
2279                         }
2280
2281                 } else {
2282                         if (!rv->region()->locked()) {
2283                                 rv->region()->clear_changes ();
2284                                 rv->region()->trim_front (new_bound);
2285                                 _session->add_command(new StatefulDiffCommand (rv->region()));
2286                         }
2287                 }
2288
2289                 commit_reversible_command();
2290
2291                 break;
2292         case 2:
2293                 begin_reversible_command (_("End point trim"));
2294
2295                 if (selection->selected (rv)) {
2296
2297                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2298                         {
2299                                 if (!(*i)->region()->locked()) {
2300                                         (*i)->region()->clear_changes();
2301                                         (*i)->region()->trim_end (new_bound);
2302                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2303                                 }
2304                         }
2305
2306                 } else {
2307
2308                         if (!rv->region()->locked()) {
2309                                 rv->region()->clear_changes ();
2310                                 rv->region()->trim_end (new_bound);
2311                                 _session->add_command (new StatefulDiffCommand (rv->region()));
2312                         }
2313                 }
2314
2315                 commit_reversible_command();
2316
2317                 break;
2318         default:
2319                 break;
2320         }
2321 }
2322
2323 void
2324 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2325 {
2326         Marker* marker;
2327         bool is_start;
2328
2329         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2330                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2331                 /*NOTREACHED*/
2332         }
2333
2334         Location* location = find_location_from_marker (marker, is_start);
2335         location->set_hidden (true, this);
2336 }
2337
2338
2339 void
2340 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2341 {
2342         double x1 = sample_to_pixel (start);
2343         double x2 = sample_to_pixel (end);
2344         double y2 = _full_canvas_height - 1.0;
2345
2346         zoom_rect->set (ArdourCanvas::Rect (x1, 1.0, x2, y2));
2347 }
2348
2349
2350 gint
2351 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2352 {
2353         using namespace Gtkmm2ext;
2354
2355         ArdourPrompter prompter (false);
2356
2357         prompter.set_prompt (_("Name for region:"));
2358         prompter.set_initial_text (clicked_regionview->region()->name());
2359         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2360         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2361         prompter.show_all ();
2362         switch (prompter.run ()) {
2363         case Gtk::RESPONSE_ACCEPT:
2364                 string str;
2365                 prompter.get_result(str);
2366                 if (str.length()) {
2367                         clicked_regionview->region()->set_name (str);
2368                 }
2369                 break;
2370         }
2371         return true;
2372 }
2373
2374
2375 void
2376 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2377 {
2378         /* no brushing without a useful snap setting */
2379
2380         switch (_snap_mode) {
2381         case SnapMagnetic:
2382                 return; /* can't work because it allows region to be placed anywhere */
2383         default:
2384                 break; /* OK */
2385         }
2386
2387         switch (_snap_type) {
2388         case SnapToMark:
2389                 return;
2390
2391         default:
2392                 break;
2393         }
2394
2395         /* don't brush a copy over the original */
2396
2397         if (pos == rv->region()->position()) {
2398                 return;
2399         }
2400
2401         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2402
2403         if (rtv == 0 || !rtv->is_track()) {
2404                 return;
2405         }
2406
2407         boost::shared_ptr<Playlist> playlist = rtv->playlist();
2408         double speed = rtv->track()->speed();
2409
2410         playlist->clear_changes ();
2411         boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2412         playlist->add_region (new_region, (framepos_t) (pos * speed));
2413         _session->add_command (new StatefulDiffCommand (playlist));
2414
2415         // playlist is frozen, so we have to update manually XXX this is disgusting
2416
2417         playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2418 }
2419
2420 gint
2421 Editor::track_height_step_timeout ()
2422 {
2423         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2424                 current_stepping_trackview = 0;
2425                 return false;
2426         }
2427         return true;
2428 }
2429
2430 void
2431 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2432 {
2433         assert (region_view);
2434
2435         if (!region_view->region()->playlist()) {
2436                 return;
2437         }
2438
2439         switch (Config->get_edit_mode()) {
2440                 case Splice:
2441                         _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2442                         break;
2443                 case Ripple:
2444                         _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2445                         break;
2446                 default:
2447                         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2448                         break;
2449
2450         }
2451 }
2452
2453 void
2454 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2455 {
2456         assert (region_view);
2457
2458         if (!region_view->region()->playlist()) {
2459                 return;
2460         }
2461
2462         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2463 }
2464
2465 void
2466 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2467 {
2468         assert (region_view);
2469
2470         if (!region_view->region()->playlist()) {
2471                 return;
2472         }
2473
2474         if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2475                 return;
2476         }
2477
2478         _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2479
2480         begin_reversible_command (Operations::drag_region_brush);
2481 }
2482
2483 /** Start a grab where a time range is selected, track(s) are selected, and the
2484  *  user clicks and drags a region with a modifier in order to create a new region containing
2485  *  the section of the clicked region that lies within the time range.
2486  */
2487 void
2488 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2489 {
2490         if (clicked_regionview == 0) {
2491                 return;
2492         }
2493
2494         /* lets try to create new Region for the selection */
2495
2496         vector<boost::shared_ptr<Region> > new_regions;
2497         create_region_from_selection (new_regions);
2498
2499         if (new_regions.empty()) {
2500                 return;
2501         }
2502
2503         /* XXX fix me one day to use all new regions */
2504
2505         boost::shared_ptr<Region> region (new_regions.front());
2506
2507         /* add it to the current stream/playlist.
2508
2509            tricky: the streamview for the track will add a new regionview. we will
2510            catch the signal it sends when it creates the regionview to
2511            set the regionview we want to then drag.
2512         */
2513
2514         latest_regionviews.clear();
2515         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2516
2517         /* A selection grab currently creates two undo/redo operations, one for
2518            creating the new region and another for moving it.
2519         */
2520
2521         begin_reversible_command (Operations::selection_grab);
2522
2523         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2524
2525         playlist->clear_changes ();
2526         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2527         _session->add_command(new StatefulDiffCommand (playlist));
2528
2529         commit_reversible_command ();
2530
2531         c.disconnect ();
2532
2533         if (latest_regionviews.empty()) {
2534                 /* something went wrong */
2535                 return;
2536         }
2537
2538         /* we need to deselect all other regionviews, and select this one
2539            i'm ignoring undo stuff, because the region creation will take care of it
2540         */
2541         selection->set (latest_regionviews);
2542
2543         _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2544 }
2545
2546 void
2547 Editor::escape ()
2548 {
2549         if (_drags->active ()) {
2550                 _drags->abort ();
2551         } else {
2552                 selection->clear ();
2553         }
2554 }
2555
2556 void
2557 Editor::set_internal_edit (bool yn)
2558 {
2559         if (_internal_editing == yn) {
2560                 return;
2561         }
2562
2563         _internal_editing = yn;
2564
2565         if (yn) {
2566                 pre_internal_mouse_mode = mouse_mode;
2567                 pre_internal_snap_type = _snap_type;
2568                 pre_internal_snap_mode = _snap_mode;
2569
2570                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2571                         (*i)->enter_internal_edit_mode ();
2572                 }
2573
2574                 set_snap_to (internal_snap_type);
2575                 set_snap_mode (internal_snap_mode);
2576
2577         } else {
2578
2579                 internal_snap_mode = _snap_mode;
2580                 internal_snap_type = _snap_type;
2581
2582                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2583                         (*i)->leave_internal_edit_mode ();
2584                 }
2585
2586                 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2587                         /* we were drawing .. flip back to something sensible */
2588                         set_mouse_mode (pre_internal_mouse_mode);
2589                 }
2590
2591                 set_snap_to (pre_internal_snap_type);
2592                 set_snap_mode (pre_internal_snap_mode);
2593         }
2594
2595         reset_canvas_cursor ();
2596 }
2597
2598 /** Update _join_object_range_state which indicate whether we are over the top
2599  *  or bottom half of a route view, used by the `join object/range' tool
2600  *  mode. Coordinates in canvas space.
2601  */
2602 void
2603 Editor::update_join_object_range_location (double y)
2604 {
2605         if (_internal_editing || !get_smart_mode()) {
2606                 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2607                 return;
2608         }
2609
2610         JoinObjectRangeState const old = _join_object_range_state;
2611
2612         if (mouse_mode == MouseObject) {
2613                 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2614         } else if (mouse_mode == MouseRange) {
2615                 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2616         }
2617
2618         if (entered_regionview) {
2619
2620                 ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2621                 double const c = item_space.y / entered_regionview->height();
2622                         
2623                 _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2624                 
2625                 if (_join_object_range_state != old) {
2626                         set_canvas_cursor (which_track_cursor ());
2627                 }
2628
2629         } else if (entered_track) {
2630
2631                 RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2632                 
2633                 if (entered_route_view) {
2634
2635                         double cx = 0;
2636                         double cy = y;
2637
2638                         entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2639
2640                         double track_height = entered_route_view->view()->child_height();
2641                         if (Config->get_show_name_highlight()) {
2642                                 track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2643                         }
2644                         double const c = cy / track_height;
2645
2646
2647                         if (c <= 0.5) {
2648                                 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2649                         } else {
2650                                 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2651                         }
2652
2653                 } else {
2654                         /* Other kinds of tracks use object mode */
2655                         _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2656                 }
2657
2658                 if (_join_object_range_state != old) {
2659                         set_canvas_cursor (which_track_cursor ());
2660                 }
2661         }
2662 }
2663
2664 Editing::MouseMode
2665 Editor::effective_mouse_mode () const
2666 {
2667         if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2668                 return MouseObject;
2669         } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2670                 return MouseRange;
2671         }
2672
2673         return mouse_mode;
2674 }
2675
2676 void
2677 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2678 {
2679         NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2680         assert (e);
2681
2682         e->region_view().delete_note (e->note ());
2683 }
2684
2685 /** Obtain the pointer position in canvas coordinates */
2686 void
2687 Editor::get_pointer_position (double& x, double& y) const
2688 {
2689         int px, py;
2690         _track_canvas->get_pointer (px, py);
2691         _track_canvas->window_to_canvas (px, py, x, y);
2692 }