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