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