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