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