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