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