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