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