maybe fix wine/windows vst support build
[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 StreamItem:
823                         if (internal_editing()) {
824                                 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
825                                         _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
826                                         return true;
827                                 } 
828                         } else {
829                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
830                                 return true;
831                         }
832                         break;
833
834                 case RegionViewNameHighlight:
835                         if (!clicked_regionview->region()->locked()) {
836                                 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
837                                 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
838                                 return true;
839                         }
840                         break;
841
842                 case LeftFrameHandle:
843                 case RightFrameHandle:
844                         if (!internal_editing() && doing_object_stuff() && !clicked_regionview->region()->locked()) {
845                                 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
846                                 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
847                                 return true;
848                         }
849                         break;
850
851                 default:
852                         if (!internal_editing()) {
853                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
854                         }
855                 }
856                 return true;
857                 break;
858
859         case MouseDraw:
860                 switch (item_type) {
861                 case NoteItem:
862                         if (internal_editing()) {
863                                 /* trim notes if we're in internal edit mode and near the ends of the note */
864                                 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
865                                 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
866                                         _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
867                                 } else {
868                                         _drags->set (new NoteDrag (this, item), event);
869                                 }
870                                 return true;
871                         }
872                         break;
873
874                 default:
875                         break;
876                 }
877
878         case MouseObject:
879                 switch (item_type) {
880                 case NoteItem:
881                         if (internal_editing()) {
882                                 ArdourCanvas::CanvasNoteEvent* cn = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
883                                 if (cn->mouse_near_ends()) {
884                                         _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
885                                 } else {
886                                         _drags->set (new NoteDrag (this, item), event);
887                                 }
888                                 return true;
889                         }
890                         break;
891
892                 default:
893                         break;
894                 }
895
896                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
897                     event->type == GDK_BUTTON_PRESS) {
898
899                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
900
901                 } else if (event->type == GDK_BUTTON_PRESS) {
902
903                         switch (item_type) {
904                         case FadeInHandleItem:
905                         {
906                                 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
907                                 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
908                                 return true;
909                         }
910
911                         case FadeOutHandleItem:
912                         {
913                                 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
914                                 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
915                                 return true;
916                         }
917
918                         case FeatureLineItem:
919                         {
920                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
921                                         remove_transient(item);
922                                         return true;
923                                 }
924
925                                 _drags->set (new FeatureLineDrag (this, item), event);
926                                 return true;
927                                 break;
928                         }
929
930                         case RegionItem:
931                                 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
932                                         /* click on an automation region view; do nothing here and let the ARV's signal handler
933                                            sort it out.
934                                         */
935                                         break;
936                                 }
937
938                                 if (internal_editing ()) {
939                                         if (event->type == GDK_2BUTTON_PRESS && event->button.button == 1) {
940                                                 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
941                                                 act->activate ();
942                                         }
943                                         break;
944                                 }
945
946                                 /* click on a normal region view */
947                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
948                                         add_region_copy_drag (item, event, clicked_regionview);
949                                 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
950                                         add_region_brush_drag (item, event, clicked_regionview);
951                                 } else {
952                                         add_region_drag (item, event, clicked_regionview);
953                                 }
954
955
956                                 if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
957                                         _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
958                                 }
959
960                                 _drags->start_grab (event);
961                                 break;
962
963                         case RegionViewNameHighlight:
964                         case LeftFrameHandle:
965                         case RightFrameHandle:
966                                 if (!clicked_regionview->region()->locked()) {
967                                         RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
968                                         _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
969                                         return true;
970                                 }
971                                 break;
972
973                         case RegionViewName:
974                         {
975                                 /* rename happens on edit clicks */
976                                 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
977                                 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
978                                 return true;
979                                 break;
980                         }
981
982                         case ControlPointItem:
983                                 _drags->set (new ControlPointDrag (this, item), event);
984                                 return true;
985                                 break;
986
987                         case AutomationLineItem:
988                                 _drags->set (new LineDrag (this, item), event);
989                                 return true;
990                                 break;
991
992                         case StreamItem:
993                                 if (internal_editing()) {
994                                         if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
995                                                 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
996                                         }
997                                         return true;
998                                 } else {
999                                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1000                                 }
1001                                 break;
1002
1003                         case AutomationTrackItem:
1004                         {
1005                                 TimeAxisView* parent = clicked_axisview->get_parent ();
1006                                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1007                                 assert (atv);
1008                                 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1009                                         /* create a MIDI region so that we have somewhere to put automation */
1010                                         _drags->set (new RegionCreateDrag (this, item, parent), event);
1011                                 } else {
1012                                         /* rubberband drag to select automation points */
1013                                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1014                                 }
1015                                 break;
1016                         }
1017
1018                         case SelectionItem:
1019                         {
1020                                 if (smart_mode_action->get_active()) {
1021                                         /* we're in "smart" joined mode, and we've clicked on a Selection */
1022                                         double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
1023                                         pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1024                                         if (tvp.first) {
1025                                                 /* if we're over an automation track, start a drag of its data */
1026                                                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1027                                                 if (atv) {
1028                                                         _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, _cursors->up_down);
1029                                                 }
1030
1031                                                 /* if we're over a track and a region, and in the `object' part of a region,
1032                                                    put a selection around the region and drag both
1033                                                 */
1034                                                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1035                                                 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1036                                                         boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1037                                                         if (t) {
1038                                                                 boost::shared_ptr<Playlist> pl = t->playlist ();
1039                                                                 if (pl) {
1040
1041                                                                         boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
1042                                                                         if (r) {
1043                                                                                 RegionView* rv = rtv->view()->find_view (r);
1044                                                                                 clicked_selection = select_range (rv->region()->position(), 
1045                                                                                                                   rv->region()->last_frame()+1);
1046                                                                                 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1047                                                                                 list<RegionView*> rvs;
1048                                                                                 rvs.push_back (rv);
1049                                                                                 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1050                                                                                 _drags->start_grab (event);
1051                                                                         }
1052                                                                 }
1053                                                         }
1054                                                 }
1055                                         }
1056                                 }
1057                                 break;
1058                         }
1059
1060 #ifdef WITH_CMT
1061                         case ImageFrameHandleStartItem:
1062                                 imageframe_start_handle_op(item, event) ;
1063                                 return(true) ;
1064                                 break ;
1065                         case ImageFrameHandleEndItem:
1066                                 imageframe_end_handle_op(item, event) ;
1067                                 return(true) ;
1068                                 break ;
1069                         case MarkerViewHandleStartItem:
1070                                 markerview_item_start_handle_op(item, event) ;
1071                                 return(true) ;
1072                                 break ;
1073                         case MarkerViewHandleEndItem:
1074                                 markerview_item_end_handle_op(item, event) ;
1075                                 return(true) ;
1076                                 break ;
1077                         case MarkerViewItem:
1078                                 start_markerview_grab(item, event) ;
1079                                 break ;
1080                         case ImageFrameItem:
1081                                 start_imageframe_grab(item, event) ;
1082                                 break ;
1083 #endif
1084
1085                         case MarkerBarItem:
1086
1087                                 break;
1088
1089                         default:
1090                                 break;
1091                         }
1092                 }
1093                 return true;
1094                 break;
1095
1096         case MouseGain:
1097                 switch (item_type) {
1098                 case RegionItem:
1099                         /* start a grab so that if we finish after moving
1100                            we can tell what happened.
1101                         */
1102                         _drags->set (new RegionGainDrag (this, item), event, current_canvas_cursor);
1103                         break;
1104
1105                 case GainLineItem:
1106                         _drags->set (new LineDrag (this, item), event);
1107                         return true;
1108
1109                 case ControlPointItem:
1110                         _drags->set (new ControlPointDrag (this, item), event);
1111                         return true;
1112                         break;
1113
1114                 default:
1115                         break;
1116                 }
1117                 return true;
1118                 break;
1119
1120                 switch (item_type) {
1121                 case ControlPointItem:
1122                         _drags->set (new ControlPointDrag (this, item), event);
1123                         break;
1124
1125                 case AutomationLineItem:
1126                         _drags->set (new LineDrag (this, item), event);
1127                         break;
1128
1129                 case RegionItem:
1130                         // XXX need automation mode to identify which
1131                         // line to use
1132                         // start_line_grab_from_regionview (item, event);
1133                         break;
1134
1135                 default:
1136                         break;
1137                 }
1138                 return true;
1139                 break;
1140
1141         case MouseZoom:
1142                 if (event->type == GDK_BUTTON_PRESS) {
1143                         _drags->set (new MouseZoomDrag (this, item), event);
1144                 }
1145
1146                 return true;
1147                 break;
1148
1149         case MouseTimeFX:
1150                 if (internal_editing() && item_type == NoteItem) {
1151                         /* drag notes if we're in internal edit mode */
1152                         _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1153                         return true;
1154                 } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
1155                         /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
1156                         _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1157                         return true;
1158                 }
1159                 break;
1160
1161         case MouseAudition:
1162                 _drags->set (new ScrubDrag (this, item), event);
1163                 scrub_reversals = 0;
1164                 scrub_reverse_distance = 0;
1165                 last_scrub_x = event->button.x;
1166                 scrubbing_direction = 0;
1167                 set_canvas_cursor (_cursors->transparent);
1168                 return true;
1169                 break;
1170
1171         default:
1172                 break;
1173         }
1174
1175         return false;
1176 }
1177
1178 bool
1179 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1180 {
1181         Editing::MouseMode const eff = effective_mouse_mode ();
1182         switch (eff) {
1183         case MouseObject:
1184                 switch (item_type) {
1185                 case RegionItem:
1186                         if (internal_editing ()) {
1187                                 /* no region drags in internal edit mode */
1188                                 return false;
1189                         }
1190
1191                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1192                                 add_region_copy_drag (item, event, clicked_regionview);
1193                         } else {
1194                                 add_region_drag (item, event, clicked_regionview);
1195                         }
1196                         _drags->start_grab (event);
1197                         return true;
1198                         break;
1199                 case ControlPointItem:
1200                         _drags->set (new ControlPointDrag (this, item), event);
1201                         return true;
1202                         break;
1203
1204                 default:
1205                         break;
1206                 }
1207
1208                 switch (item_type) {
1209                 case RegionViewNameHighlight:
1210                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1211                         return true;
1212                         break;
1213
1214                 case LeftFrameHandle:
1215                 case RightFrameHandle:
1216                         if (!internal_editing ()) {
1217                                 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1218                         }
1219                         return true;
1220                         break;
1221
1222                 case RegionViewName:
1223                         _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1224                         return true;
1225                         break;
1226
1227                 default:
1228                         break;
1229                 }
1230
1231                 break;
1232
1233         case MouseDraw:
1234                 return false;
1235
1236         case MouseRange:
1237                 /* relax till release */
1238                 return true;
1239                 break;
1240
1241
1242         case MouseZoom:
1243                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1244                         temporal_zoom_to_frame (false, event_frame (event));
1245                 } else {
1246                         temporal_zoom_to_frame (true, event_frame(event));
1247                 }
1248                 return true;
1249                 break;
1250
1251         default:
1252                 break;
1253         }
1254
1255         return false;
1256 }
1257    
1258 bool
1259 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1260 {
1261         if (event->type != GDK_BUTTON_PRESS) {
1262                 return false;
1263         }
1264
1265         Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1266
1267         if (canvas_window) {
1268                 Glib::RefPtr<const Gdk::Window> pointer_window;
1269                 int x, y;
1270                 double wx, wy;
1271                 Gdk::ModifierType mask;
1272
1273                 pointer_window = canvas_window->get_pointer (x, y, mask);
1274
1275                 if (pointer_window == track_canvas->get_bin_window()) {
1276                         track_canvas->window_to_world (x, y, wx, wy);
1277                 }
1278         }
1279
1280         pre_press_cursor = current_canvas_cursor;
1281         
1282         track_canvas->grab_focus();
1283
1284         if (_session && _session->actively_recording()) {
1285                 return true;
1286         }
1287
1288
1289
1290         if (internal_editing()) {
1291                 bool leave_internal_edit_mode = false;
1292
1293                 switch (item_type) {
1294                 case NoteItem:
1295                         break;
1296
1297                 case RegionItem:
1298                         if (!dynamic_cast<MidiRegionView*> (clicked_regionview)) {
1299                                 leave_internal_edit_mode = true;
1300                         }
1301                         break;
1302
1303                 case PlayheadCursorItem:
1304                 case MarkerItem:
1305                 case TempoMarkerItem:
1306                 case MeterMarkerItem:
1307                 case MarkerBarItem:
1308                 case TempoBarItem:
1309                 case MeterBarItem:
1310                 case RangeMarkerBarItem:
1311                 case CdMarkerBarItem:
1312                 case TransportMarkerBarItem:
1313                         /* button press on these events never does anything to
1314                            change the editing mode.
1315                         */
1316                         break;
1317                         
1318                 case StreamItem:
1319                         leave_internal_edit_mode = true;
1320                         break;
1321
1322                 default:
1323                         break;
1324                 }
1325                 
1326                 if (leave_internal_edit_mode) {
1327                         ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1328                 }
1329         }
1330
1331         button_selection (item, event, item_type);
1332
1333         if (!_drags->active () &&
1334             (Keyboard::is_delete_event (&event->button) ||
1335              Keyboard::is_context_menu_event (&event->button) ||
1336              Keyboard::is_edit_event (&event->button))) {
1337
1338                 /* handled by button release */
1339                 return true;
1340         }
1341
1342         switch (event->button.button) {
1343         case 1:
1344                 return button_press_handler_1 (item, event, item_type);
1345                 break;
1346
1347         case 2:
1348                 return button_press_handler_2 (item, event, item_type);
1349                 break;
1350
1351         case 3:
1352                 break;
1353
1354         default:
1355                 return button_press_dispatch (&event->button);
1356                 break;
1357
1358         }
1359
1360         return false;
1361 }
1362
1363 bool
1364 Editor::button_press_dispatch (GdkEventButton* ev)
1365 {
1366         /* this function is intended only for buttons 4 and above.
1367          */
1368
1369         Gtkmm2ext::MouseButton b (ev->state, ev->button);
1370         return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1371 }
1372
1373 bool
1374 Editor::button_release_dispatch (GdkEventButton* ev)
1375 {
1376         /* this function is intended only for buttons 4 and above.
1377          */
1378
1379         Gtkmm2ext::MouseButton b (ev->state, ev->button);
1380         return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1381 }
1382
1383 bool
1384 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1385 {
1386         framepos_t where = event_frame (event, 0, 0);
1387         AutomationTimeAxisView* atv = 0;
1388
1389         if (pre_press_cursor) {
1390                 set_canvas_cursor (pre_press_cursor);
1391                 pre_press_cursor = 0;
1392         }
1393
1394         /* no action if we're recording */
1395
1396         if (_session && _session->actively_recording()) {
1397                 return true;
1398         }
1399
1400         /* see if we're finishing a drag */
1401
1402         bool were_dragging = false;
1403         if (_drags->active ()) {
1404                 bool const r = _drags->end_grab (event);
1405                 if (r) {
1406                         /* grab dragged, so do nothing else */
1407                         return true;
1408                 }
1409
1410                 were_dragging = true;
1411         }
1412
1413         update_region_layering_order_editor ();
1414
1415         /* edit events get handled here */
1416
1417         if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1418                 switch (item_type) {
1419                 case RegionItem:
1420                         show_region_properties ();
1421                         break;
1422
1423                 case TempoMarkerItem:
1424                         edit_tempo_marker (item);
1425                         break;
1426
1427                 case MeterMarkerItem:
1428                         edit_meter_marker (item);
1429                         break;
1430
1431                 case RegionViewName:
1432                         if (clicked_regionview->name_active()) {
1433                                 return mouse_rename_region (item, event);
1434                         }
1435                         break;
1436
1437                 case ControlPointItem:
1438                         edit_control_point (item);
1439                         break;
1440
1441                 case NoteItem:
1442                         edit_note (item);
1443                         break;
1444
1445                 default:
1446                         break;
1447                 }
1448                 return true;
1449         }
1450
1451         /* context menu events get handled here */
1452
1453         if (Keyboard::is_context_menu_event (&event->button)) {
1454
1455                 context_click_event = *event;
1456
1457                 if (!_drags->active ()) {
1458
1459                         /* no matter which button pops up the context menu, tell the menu
1460                            widget to use button 1 to drive menu selection.
1461                         */
1462
1463                         switch (item_type) {
1464                         case FadeInItem:
1465                         case FadeInHandleItem:
1466                         case FadeOutItem:
1467                         case FadeOutHandleItem:
1468                                 popup_fade_context_menu (1, event->button.time, item, item_type);
1469                                 break;
1470
1471                         case StreamItem:
1472                                 popup_track_context_menu (1, event->button.time, item_type, false);
1473                                 break;
1474
1475                         case RegionItem:
1476                         case RegionViewNameHighlight:
1477                         case LeftFrameHandle:
1478                         case RightFrameHandle:
1479                         case RegionViewName:
1480                                 popup_track_context_menu (1, event->button.time, item_type, false);
1481                                 break;
1482
1483                         case SelectionItem:
1484                                 popup_track_context_menu (1, event->button.time, item_type, true);
1485                                 break;
1486
1487                         case AutomationTrackItem:
1488                                 popup_track_context_menu (1, event->button.time, item_type, false);
1489                                 break;
1490
1491                         case MarkerBarItem:
1492                         case RangeMarkerBarItem:
1493                         case TransportMarkerBarItem:
1494                         case CdMarkerBarItem:
1495                         case TempoBarItem:
1496                         case MeterBarItem:
1497                                 popup_ruler_menu (where, item_type);
1498                                 break;
1499
1500                         case MarkerItem:
1501                                 marker_context_menu (&event->button, item);
1502                                 break;
1503
1504                         case TempoMarkerItem:
1505                                 tempo_or_meter_marker_context_menu (&event->button, item);
1506                                 break;
1507
1508                         case MeterMarkerItem:
1509                                 tempo_or_meter_marker_context_menu (&event->button, item);
1510                                 break;
1511
1512                         case CrossfadeViewItem:
1513                                 popup_track_context_menu (1, event->button.time, item_type, false);
1514                                 break;
1515
1516                         case ControlPointItem:
1517                                 popup_control_point_context_menu (item, event);
1518                                 break;
1519
1520 #ifdef WITH_CMT
1521                         case ImageFrameItem:
1522                                 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1523                                 break ;
1524                         case ImageFrameTimeAxisItem:
1525                                 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1526                                 break ;
1527                         case MarkerViewItem:
1528                                 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1529                                 break ;
1530                         case MarkerTimeAxisItem:
1531                                 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1532                                 break ;
1533 #endif
1534
1535                         default:
1536                                 break;
1537                         }
1538
1539                         return true;
1540                 }
1541         }
1542
1543         /* delete events get handled here */
1544
1545         Editing::MouseMode const eff = effective_mouse_mode ();
1546
1547         if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1548
1549                 switch (item_type) {
1550                 case TempoMarkerItem:
1551                         remove_tempo_marker (item);
1552                         break;
1553
1554                 case MeterMarkerItem:
1555                         remove_meter_marker (item);
1556                         break;
1557
1558                 case MarkerItem:
1559                         remove_marker (*item, event);
1560                         break;
1561
1562                 case RegionItem:
1563                         if (eff == MouseObject) {
1564                                 remove_clicked_region ();
1565                         }
1566                         break;
1567
1568                 case ControlPointItem:
1569                         remove_control_point (item);
1570                         break;
1571
1572                 case NoteItem:
1573                         remove_midi_note (item, event);
1574                         break;
1575
1576                 default:
1577                         break;
1578                 }
1579                 return true;
1580         }
1581
1582         switch (event->button.button) {
1583         case 1:
1584
1585                 switch (item_type) {
1586                 /* see comments in button_press_handler */
1587                 case PlayheadCursorItem:
1588                 case MarkerItem:
1589                 case GainLineItem:
1590                 case AutomationLineItem:
1591                 case StartSelectionTrimItem:
1592                 case EndSelectionTrimItem:
1593                         return true;
1594
1595                 case MarkerBarItem:
1596                         if (!_dragging_playhead) {
1597                                 snap_to_with_modifier (where, event, 0, true);
1598                                 mouse_add_new_marker (where);
1599                         }
1600                         return true;
1601
1602                 case CdMarkerBarItem:
1603                         if (!_dragging_playhead) {
1604                                 // if we get here then a dragged range wasn't done
1605                                 snap_to_with_modifier (where, event, 0, true);
1606                                 mouse_add_new_marker (where, true);
1607                         }
1608                         return true;
1609
1610                 case TempoBarItem:
1611                         if (!_dragging_playhead) {
1612                                 snap_to_with_modifier (where, event);
1613                                 mouse_add_new_tempo_event (where);
1614                         }
1615                         return true;
1616
1617                 case MeterBarItem:
1618                         if (!_dragging_playhead) {
1619                                 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1620                         }
1621                         return true;
1622                         break;
1623
1624                 default:
1625                         break;
1626                 }
1627
1628                 switch (eff) {
1629                 case MouseObject:
1630                         switch (item_type) {
1631                         case AutomationTrackItem:
1632                                 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1633                                 if (atv) {
1634                                         atv->add_automation_event (event, where, event->button.y);
1635                                 }
1636                                 return true;
1637                                 break;
1638                         default:
1639                                 break;
1640                         }
1641                         break;
1642
1643                 case MouseGain:
1644                         switch (item_type) {
1645                         case RegionItem:
1646                         {
1647                                 /* check that we didn't drag before releasing, since
1648                                    its really annoying to create new control
1649                                    points when doing this.
1650                                 */
1651                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1652                                 if (were_dragging && arv) {
1653                                         arv->add_gain_point_event (item, event);
1654                                 }
1655                                 return true;
1656                                 break;
1657                         }
1658
1659                         case AutomationTrackItem:
1660                                 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1661                                         add_automation_event (event, where, event->button.y);
1662                                 return true;
1663                                 break;
1664                         default:
1665                                 break;
1666                         }
1667                         break;
1668
1669                 case MouseAudition:
1670                         set_canvas_cursor (current_canvas_cursor);
1671                         if (scrubbing_direction == 0) {
1672                                 /* no drag, just a click */
1673                                 switch (item_type) {
1674                                 case RegionItem:
1675                                         play_selected_region ();
1676                                         break;
1677                                 default:
1678                                         break;
1679                                 }
1680                         } else {
1681                                 /* make sure we stop */
1682                                 _session->request_transport_speed (0.0);
1683                         }
1684                         break;
1685
1686                 default:
1687                         break;
1688
1689                 }
1690
1691                 /* do any (de)selection operations that should occur on button release */
1692                 button_selection (item, event, item_type);
1693                 return true;
1694                 break;
1695
1696
1697         case 2:
1698                 switch (eff) {
1699
1700                 case MouseObject:
1701                         switch (item_type) {
1702                         case RegionItem:
1703                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1704                                         raise_region ();
1705                                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1706                                         lower_region ();
1707                                 } else {
1708                                         // Button2 click is unused
1709                                 }
1710                                 return true;
1711
1712                                 break;
1713
1714                         default:
1715                                 break;
1716                         }
1717                         break;
1718
1719                 case MouseDraw:
1720                         return true;
1721                         
1722                 case MouseRange:
1723                         // x_style_paste (where, 1.0);
1724                         return true;
1725                         break;
1726
1727                 default:
1728                         break;
1729                 }
1730
1731                 break;
1732
1733         case 3:
1734                 break;
1735
1736         default:
1737                 break;
1738         }
1739
1740         return false;
1741 }
1742
1743 bool
1744 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1745 {
1746         ControlPoint* cp;
1747         Marker * marker;
1748         double fraction;
1749         bool ret = true;
1750
1751         switch (item_type) {
1752         case ControlPointItem:
1753                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1754                         cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1755                         cp->set_visible (true);
1756
1757                         double at_x, at_y;
1758                         at_x = cp->get_x();
1759                         at_y = cp->get_y ();
1760                         cp->i2w (at_x, at_y);
1761                         at_x += 10.0;
1762                         at_y += 10.0;
1763
1764                         fraction = 1.0 - (cp->get_y() / cp->line().height());
1765
1766                         if (is_drawable() && !_drags->active ()) {
1767                                 set_canvas_cursor (_cursors->fader);
1768                         }
1769
1770                         _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1771                         _verbose_cursor->show ();
1772                 }
1773                 break;
1774
1775         case GainLineItem:
1776                 if (mouse_mode == MouseGain) {
1777                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1778                         if (line)
1779                                 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1780                         if (is_drawable()) {
1781                                 set_canvas_cursor (_cursors->fader);
1782                         }
1783                 }
1784                 break;
1785
1786         case AutomationLineItem:
1787                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1788                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1789                         if (line) {
1790                                 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1791                         }
1792                         if (is_drawable()) {
1793                                 set_canvas_cursor (_cursors->fader);
1794                         }
1795                 }
1796                 break;
1797
1798         case RegionViewNameHighlight:
1799                 if (is_drawable() && doing_object_stuff() && entered_regionview) {
1800                         set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1801                         _over_region_trim_target = true;
1802                 }
1803                 break;
1804
1805         case LeftFrameHandle:
1806         case RightFrameHandle:
1807                 if (is_drawable() && doing_object_stuff() && !internal_editing() && entered_regionview) {
1808                         set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1809                 }
1810                 break;
1811
1812         case StartSelectionTrimItem:
1813 #ifdef WITH_CMT
1814         case ImageFrameHandleStartItem:
1815         case MarkerViewHandleStartItem:
1816 #endif
1817                 if (is_drawable()) {
1818                         set_canvas_cursor (_cursors->left_side_trim);
1819                 }
1820                 break;
1821         case EndSelectionTrimItem:
1822 #ifdef WITH_CMT
1823         case ImageFrameHandleEndItem:
1824         case MarkerViewHandleEndItem:
1825 #endif
1826                 if (is_drawable()) {
1827                         set_canvas_cursor (_cursors->right_side_trim);
1828                 }
1829                 break;
1830
1831         case PlayheadCursorItem:
1832                 if (is_drawable()) {
1833                         switch (_edit_point) {
1834                         case EditAtMouse:
1835                                 set_canvas_cursor (_cursors->grabber_edit_point);
1836                                 break;
1837                         default:
1838                                 set_canvas_cursor (_cursors->grabber);
1839                                 break;
1840                         }
1841                 }
1842                 break;
1843
1844         case RegionViewName:
1845
1846                 /* when the name is not an active item, the entire name highlight is for trimming */
1847
1848                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1849                         if (mouse_mode == MouseObject && is_drawable()) {
1850                                 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1851                                 _over_region_trim_target = true;
1852                         }
1853                 }
1854                 break;
1855
1856
1857         case AutomationTrackItem:
1858                 if (is_drawable()) {
1859                         Gdk::Cursor *cursor;
1860                         switch (mouse_mode) {
1861                         case MouseRange:
1862                                 cursor = _cursors->selector;
1863                                 break;
1864                         case MouseZoom:
1865                                 cursor = _cursors->zoom_in;
1866                                 break;
1867                         default:
1868                                 cursor = _cursors->cross_hair;
1869                                 break;
1870                         }
1871
1872                         set_canvas_cursor (cursor);
1873
1874                         AutomationTimeAxisView* atv;
1875                         if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1876                                 clear_entered_track = false;
1877                                 set_entered_track (atv);
1878                         }
1879                 }
1880                 break;
1881
1882         case MarkerBarItem:
1883         case RangeMarkerBarItem:
1884         case TransportMarkerBarItem:
1885         case CdMarkerBarItem:
1886         case MeterBarItem:
1887         case TempoBarItem:
1888                 if (is_drawable()) {
1889                         set_canvas_cursor (_cursors->timebar);
1890                 }
1891                 break;
1892
1893         case MarkerItem:
1894                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1895                         break;
1896                 }
1897                 entered_marker = marker;
1898                 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1899                 // fall through
1900         case MeterMarkerItem:
1901         case TempoMarkerItem:
1902                 if (is_drawable()) {
1903                         set_canvas_cursor (_cursors->timebar);
1904                 }
1905                 break;
1906
1907         case FadeInHandleItem:
1908                 if (mouse_mode == MouseObject && !internal_editing()) {
1909                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1910                         if (rect) {
1911                                 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1912                         }
1913                         set_canvas_cursor (_cursors->fade_in);
1914                 }
1915                 break;
1916
1917         case FadeOutHandleItem:
1918                 if (mouse_mode == MouseObject && !internal_editing()) {
1919                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1920                         if (rect) {
1921                                 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1922                         }
1923                         set_canvas_cursor (_cursors->fade_out);
1924                 }
1925                 break;
1926         case FeatureLineItem:
1927                 {
1928                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1929                         line->property_fill_color_rgba() = 0xFF0000FF;
1930                 }
1931                 break;
1932         case SelectionItem:
1933                 if (smart_mode_action->get_active()) {
1934                         set_canvas_cursor ();
1935                 }
1936                 break;
1937
1938         default:
1939                 break;
1940         }
1941
1942         /* second pass to handle entered track status in a comprehensible way.
1943          */
1944
1945         switch (item_type) {
1946         case GainLineItem:
1947         case AutomationLineItem:
1948         case ControlPointItem:
1949                 /* these do not affect the current entered track state */
1950                 clear_entered_track = false;
1951                 break;
1952
1953         case AutomationTrackItem:
1954                 /* handled above already */
1955                 break;
1956
1957         default:
1958                 set_entered_track (0);
1959                 break;
1960         }
1961
1962         return ret;
1963 }
1964
1965 bool
1966 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1967 {
1968         AutomationLine* al;
1969         ControlPoint* cp;
1970         Marker *marker;
1971         Location *loc;
1972         RegionView* rv;
1973         bool is_start;
1974         bool ret = true;
1975
1976         switch (item_type) {
1977         case ControlPointItem:
1978                 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1979                 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1980                         if (cp->line().npoints() > 1 && !cp->get_selected()) {
1981                                 cp->set_visible (false);
1982                         }
1983                 }
1984
1985                 if (is_drawable()) {
1986                         set_canvas_cursor (current_canvas_cursor);
1987                 }
1988
1989                 _verbose_cursor->hide ();
1990                 break;
1991
1992         case RegionViewNameHighlight:
1993         case LeftFrameHandle:
1994         case RightFrameHandle:
1995         case StartSelectionTrimItem:
1996         case EndSelectionTrimItem:
1997         case PlayheadCursorItem:
1998
1999 #ifdef WITH_CMT
2000         case ImageFrameHandleStartItem:
2001         case ImageFrameHandleEndItem:
2002         case MarkerViewHandleStartItem:
2003         case MarkerViewHandleEndItem:
2004 #endif
2005
2006                 _over_region_trim_target = false;
2007
2008                 if (is_drawable()) {
2009                         set_canvas_cursor (current_canvas_cursor);
2010                 }
2011                 break;
2012
2013         case GainLineItem:
2014         case AutomationLineItem:
2015                 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2016                 {
2017                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2018                         if (line)
2019                                 line->property_fill_color_rgba() = al->get_line_color();
2020                 }
2021                 if (is_drawable()) {
2022                         set_canvas_cursor (current_canvas_cursor);
2023                 }
2024                 break;
2025
2026         case RegionViewName:
2027                 /* see enter_handler() for notes */
2028                 _over_region_trim_target = false;
2029
2030                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2031                         if (is_drawable() && mouse_mode == MouseObject) {
2032                                 set_canvas_cursor (current_canvas_cursor);
2033                         }
2034                 }
2035                 break;
2036
2037         case RangeMarkerBarItem:
2038         case TransportMarkerBarItem:
2039         case CdMarkerBarItem:
2040         case MeterBarItem:
2041         case TempoBarItem:
2042         case MarkerBarItem:
2043                 if (is_drawable()) {
2044                         set_canvas_cursor (current_canvas_cursor);
2045                 }
2046                 break;
2047
2048         case MarkerItem:
2049                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2050                         break;
2051                 }
2052                 entered_marker = 0;
2053                 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2054                         location_flags_changed (loc, this);
2055                 }
2056                 // fall through
2057         case MeterMarkerItem:
2058         case TempoMarkerItem:
2059
2060                 if (is_drawable()) {
2061                         set_canvas_cursor (current_canvas_cursor);
2062                 }
2063
2064                 break;
2065
2066         case FadeInHandleItem:
2067         case FadeOutHandleItem:
2068                 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2069                 {
2070                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
2071                         if (rect) {
2072                                 rect->property_fill_color_rgba() = rv->get_fill_color();
2073                                 rect->property_outline_pixels() = 0;
2074                         }
2075                 }
2076                 set_canvas_cursor (current_canvas_cursor);
2077                 break;
2078
2079         case AutomationTrackItem:
2080                 if (is_drawable()) {
2081                         set_canvas_cursor (current_canvas_cursor);
2082                         clear_entered_track = true;
2083                         Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2084                 }
2085                 break;
2086         case FeatureLineItem:
2087                 {
2088                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2089                         line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
2090                 }
2091                 break;
2092
2093         default:
2094                 break;
2095         }
2096
2097         return ret;
2098 }
2099
2100 gint
2101 Editor::left_automation_track ()
2102 {
2103         if (clear_entered_track) {
2104                 set_entered_track (0);
2105                 clear_entered_track = false;
2106         }
2107         return false;
2108 }
2109
2110 void
2111 Editor::scrub (framepos_t frame, double current_x)
2112 {
2113         double delta;
2114
2115         if (scrubbing_direction == 0) {
2116                 /* first move */
2117                 _session->request_locate (frame, false);
2118                 _session->request_transport_speed (0.1);
2119                 scrubbing_direction = 1;
2120
2121         } else {
2122
2123                 if (last_scrub_x > current_x) {
2124
2125                         /* pointer moved to the left */
2126
2127                         if (scrubbing_direction > 0) {
2128
2129                                 /* we reversed direction to go backwards */
2130
2131                                 scrub_reversals++;
2132                                 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2133
2134                         } else {
2135
2136                                 /* still moving to the left (backwards) */
2137
2138                                 scrub_reversals = 0;
2139                                 scrub_reverse_distance = 0;
2140
2141                                 delta = 0.01 * (last_scrub_x - current_x);
2142                                 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2143                         }
2144
2145                 } else {
2146                         /* pointer moved to the right */
2147
2148                         if (scrubbing_direction < 0) {
2149                                 /* we reversed direction to go forward */
2150
2151                                 scrub_reversals++;
2152                                 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2153
2154                         } else {
2155                                 /* still moving to the right */
2156
2157                                 scrub_reversals = 0;
2158                                 scrub_reverse_distance = 0;
2159
2160                                 delta = 0.01 * (current_x - last_scrub_x);
2161                                 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2162                         }
2163                 }
2164
2165                 /* if there have been more than 2 opposite motion moves detected, or one that moves
2166                    back more than 10 pixels, reverse direction
2167                 */
2168
2169                 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2170
2171                         if (scrubbing_direction > 0) {
2172                                 /* was forwards, go backwards */
2173                                 _session->request_transport_speed (-0.1);
2174                                 scrubbing_direction = -1;
2175                         } else {
2176                                 /* was backwards, go forwards */
2177                                 _session->request_transport_speed (0.1);
2178                                 scrubbing_direction = 1;
2179                         }
2180
2181                         scrub_reverse_distance = 0;
2182                         scrub_reversals = 0;
2183                 }
2184         }
2185
2186         last_scrub_x = current_x;
2187 }
2188
2189 bool
2190 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2191 {
2192         _last_motion_y = event->motion.y;
2193
2194         if (event->motion.is_hint) {
2195                 gint x, y;
2196
2197                 /* We call this so that MOTION_NOTIFY events continue to be
2198                    delivered to the canvas. We need to do this because we set
2199                    Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2200                    the density of the events, at the expense of a round-trip
2201                    to the server. Given that this will mostly occur on cases
2202                    where DISPLAY = :0.0, and given the cost of what the motion
2203                    event might do, its a good tradeoff.
2204                 */
2205
2206                 track_canvas->get_pointer (x, y);
2207         }
2208
2209         if (current_stepping_trackview) {
2210                 /* don't keep the persistent stepped trackview if the mouse moves */
2211                 current_stepping_trackview = 0;
2212                 step_timeout.disconnect ();
2213         }
2214
2215         if (_session && _session->actively_recording()) {
2216                 /* Sorry. no dragging stuff around while we record */
2217                 return true;
2218         }
2219
2220         JoinObjectRangeState const old = _join_object_range_state;
2221         update_join_object_range_location (event->motion.x, event->motion.y);
2222         if (_join_object_range_state != old) {
2223                 set_canvas_cursor ();
2224         }
2225
2226         if (_over_region_trim_target) {
2227                 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2228         }
2229
2230         bool handled = false;
2231         if (_drags->active ()) {
2232                 handled = _drags->motion_handler (event, from_autoscroll);
2233         }
2234
2235         if (!handled) {
2236                 return false;
2237         }
2238
2239         track_canvas_motion (event);
2240         return true;
2241 }
2242
2243 bool
2244 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2245 {
2246         ControlPoint* control_point;
2247
2248         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2249                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2250                 /*NOTREACHED*/
2251         }
2252
2253         AutomationLine& line = control_point->line ();
2254         if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2255                 /* we shouldn't remove the first or last gain point in region gain lines */
2256                 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2257                         return false;
2258                 }
2259         }
2260
2261         return true;
2262 }
2263
2264 void
2265 Editor::remove_control_point (ArdourCanvas::Item* item)
2266 {
2267         if (!can_remove_control_point (item)) {
2268                 return;
2269         }
2270
2271         ControlPoint* control_point;
2272
2273         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2274                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2275                 /*NOTREACHED*/
2276         }
2277
2278         control_point->line().remove_point (*control_point);
2279 }
2280
2281 void
2282 Editor::edit_control_point (ArdourCanvas::Item* item)
2283 {
2284         ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2285
2286         if (p == 0) {
2287                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2288                 /*NOTREACHED*/
2289         }
2290
2291         ControlPointDialog d (p);
2292         d.set_position (Gtk::WIN_POS_MOUSE);
2293         ensure_float (d);
2294
2295         if (d.run () != RESPONSE_ACCEPT) {
2296                 return;
2297         }
2298
2299         p->line().modify_point_y (*p, d.get_y_fraction ());
2300 }
2301
2302 void
2303 Editor::edit_note (ArdourCanvas::Item* item)
2304 {
2305         ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2306         assert (e);
2307
2308         EditNoteDialog d (&e->region_view(), e);
2309         d.set_position (Gtk::WIN_POS_MOUSE);
2310         ensure_float (d);
2311
2312         d.run ();
2313 }
2314
2315
2316 void
2317 Editor::visible_order_range (int* low, int* high) const
2318 {
2319         *low = TimeAxisView::max_order ();
2320         *high = 0;
2321
2322         for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2323
2324                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2325
2326                 if (!rtv->hidden()) {
2327
2328                         if (*high < rtv->order()) {
2329                                 *high = rtv->order ();
2330                         }
2331
2332                         if (*low > rtv->order()) {
2333                                 *low = rtv->order ();
2334                         }
2335                 }
2336         }
2337 }
2338
2339 void
2340 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2341 {
2342         /* Either add to or set the set the region selection, unless
2343            this is an alignment click (control used)
2344         */
2345
2346         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2347                 TimeAxisView* tv = &rv.get_time_axis_view();
2348                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2349                 double speed = 1.0;
2350                 if (rtv && rtv->is_track()) {
2351                         speed = rtv->track()->speed();
2352                 }
2353
2354                 framepos_t where = get_preferred_edit_position();
2355
2356                 if (where >= 0) {
2357
2358                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2359
2360                                 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2361
2362                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2363
2364                                 align_region (rv.region(), End, (framepos_t) (where * speed));
2365
2366                         } else {
2367
2368                                 align_region (rv.region(), Start, (framepos_t) (where * speed));
2369                         }
2370                 }
2371         }
2372 }
2373
2374 void
2375 Editor::collect_new_region_view (RegionView* rv)
2376 {
2377         latest_regionviews.push_back (rv);
2378 }
2379
2380 void
2381 Editor::collect_and_select_new_region_view (RegionView* rv)
2382 {
2383         selection->add(rv);
2384         latest_regionviews.push_back (rv);
2385 }
2386
2387 void
2388 Editor::cancel_selection ()
2389 {
2390         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2391                 (*i)->hide_selection ();
2392         }
2393
2394         selection->clear ();
2395         clicked_selection = 0;
2396 }
2397
2398
2399 void
2400 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2401 {
2402         RegionView* rv = clicked_regionview;
2403
2404         /* Choose action dependant on which button was pressed */
2405         switch (event->button.button) {
2406         case 1:
2407                 begin_reversible_command (_("start point trim"));
2408
2409                 if (selection->selected (rv)) {
2410                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2411                              i != selection->regions.by_layer().end(); ++i)
2412                         {
2413                                 if ( (*i) == NULL){
2414                                     cerr << "region view contains null region" << endl;
2415                                 }
2416
2417                                 if (!(*i)->region()->locked()) {
2418                                         (*i)->region()->clear_changes ();
2419                                         (*i)->region()->trim_front (new_bound);
2420                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2421                                 }
2422                         }
2423
2424                 } else {
2425                         if (!rv->region()->locked()) {
2426                                 rv->region()->clear_changes ();
2427                                 rv->region()->trim_front (new_bound);
2428                                 _session->add_command(new StatefulDiffCommand (rv->region()));
2429                         }
2430                 }
2431
2432                 commit_reversible_command();
2433
2434                 break;
2435         case 2:
2436                 begin_reversible_command (_("End point trim"));
2437
2438                 if (selection->selected (rv)) {
2439
2440                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2441                         {
2442                                 if (!(*i)->region()->locked()) {
2443                                         (*i)->region()->clear_changes();
2444                                         (*i)->region()->trim_end (new_bound);
2445                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2446                                 }
2447                         }
2448
2449                 } else {
2450
2451                         if (!rv->region()->locked()) {
2452                                 rv->region()->clear_changes ();
2453                                 rv->region()->trim_end (new_bound);
2454                                 _session->add_command (new StatefulDiffCommand (rv->region()));
2455                         }
2456                 }
2457
2458                 commit_reversible_command();
2459
2460                 break;
2461         default:
2462                 break;
2463         }
2464 }
2465
2466 void
2467 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2468 {
2469         Marker* marker;
2470         bool is_start;
2471
2472         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2473                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2474                 /*NOTREACHED*/
2475         }
2476
2477         Location* location = find_location_from_marker (marker, is_start);
2478         location->set_hidden (true, this);
2479 }
2480
2481
2482 void
2483 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2484 {
2485         double x1 = frame_to_pixel (start);
2486         double x2 = frame_to_pixel (end);
2487         double y2 = full_canvas_height - 1.0;
2488
2489         zoom_rect->property_x1() = x1;
2490         zoom_rect->property_y1() = 1.0;
2491         zoom_rect->property_x2() = x2;
2492         zoom_rect->property_y2() = y2;
2493 }
2494
2495
2496 gint
2497 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2498 {
2499         using namespace Gtkmm2ext;
2500
2501         ArdourPrompter prompter (false);
2502
2503         prompter.set_prompt (_("Name for region:"));
2504         prompter.set_initial_text (clicked_regionview->region()->name());
2505         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2506         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2507         prompter.show_all ();
2508         switch (prompter.run ()) {
2509         case Gtk::RESPONSE_ACCEPT:
2510                 string str;
2511                 prompter.get_result(str);
2512                 if (str.length()) {
2513                         clicked_regionview->region()->set_name (str);
2514                 }
2515                 break;
2516         }
2517         return true;
2518 }
2519
2520
2521 void
2522 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2523 {
2524         /* no brushing without a useful snap setting */
2525
2526         switch (_snap_mode) {
2527         case SnapMagnetic:
2528                 return; /* can't work because it allows region to be placed anywhere */
2529         default:
2530                 break; /* OK */
2531         }
2532
2533         switch (_snap_type) {
2534         case SnapToMark:
2535                 return;
2536
2537         default:
2538                 break;
2539         }
2540
2541         /* don't brush a copy over the original */
2542
2543         if (pos == rv->region()->position()) {
2544                 return;
2545         }
2546
2547         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2548
2549         if (rtv == 0 || !rtv->is_track()) {
2550                 return;
2551         }
2552
2553         boost::shared_ptr<Playlist> playlist = rtv->playlist();
2554         double speed = rtv->track()->speed();
2555
2556         playlist->clear_changes ();
2557         boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2558         playlist->add_region (new_region, (framepos_t) (pos * speed));
2559         _session->add_command (new StatefulDiffCommand (playlist));
2560
2561         // playlist is frozen, so we have to update manually XXX this is disgusting
2562
2563         playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2564 }
2565
2566 gint
2567 Editor::track_height_step_timeout ()
2568 {
2569         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2570                 current_stepping_trackview = 0;
2571                 return false;
2572         }
2573         return true;
2574 }
2575
2576 void
2577 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2578 {
2579         assert (region_view);
2580
2581         if (!region_view->region()->playlist()) {
2582                 return;
2583         }
2584
2585         _region_motion_group->raise_to_top ();
2586
2587         if (Config->get_edit_mode() == Splice) {
2588                 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2589         } else {
2590                 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2591                 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2592         }
2593
2594         /* sync the canvas to what we think is its current state */
2595         update_canvas_now();
2596 }
2597
2598 void
2599 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2600 {
2601         assert (region_view);
2602
2603         if (!region_view->region()->playlist()) {
2604                 return;
2605         }
2606
2607         _region_motion_group->raise_to_top ();
2608
2609         RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2610         _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2611 }
2612
2613 void
2614 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2615 {
2616         assert (region_view);
2617
2618         if (!region_view->region()->playlist()) {
2619                 return;
2620         }
2621
2622         if (Config->get_edit_mode() == Splice) {
2623                 return;
2624         }
2625
2626         RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2627         _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2628
2629         begin_reversible_command (Operations::drag_region_brush);
2630 }
2631
2632 /** Start a grab where a time range is selected, track(s) are selected, and the
2633  *  user clicks and drags a region with a modifier in order to create a new region containing
2634  *  the section of the clicked region that lies within the time range.
2635  */
2636 void
2637 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2638 {
2639         if (clicked_regionview == 0) {
2640                 return;
2641         }
2642
2643         /* lets try to create new Region for the selection */
2644
2645         vector<boost::shared_ptr<Region> > new_regions;
2646         create_region_from_selection (new_regions);
2647
2648         if (new_regions.empty()) {
2649                 return;
2650         }
2651
2652         /* XXX fix me one day to use all new regions */
2653
2654         boost::shared_ptr<Region> region (new_regions.front());
2655
2656         /* add it to the current stream/playlist.
2657
2658            tricky: the streamview for the track will add a new regionview. we will
2659            catch the signal it sends when it creates the regionview to
2660            set the regionview we want to then drag.
2661         */
2662
2663         latest_regionviews.clear();
2664         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2665
2666         /* A selection grab currently creates two undo/redo operations, one for
2667            creating the new region and another for moving it.
2668         */
2669
2670         begin_reversible_command (Operations::selection_grab);
2671
2672         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2673
2674         playlist->clear_changes ();
2675         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2676         _session->add_command(new StatefulDiffCommand (playlist));
2677
2678         commit_reversible_command ();
2679
2680         c.disconnect ();
2681
2682         if (latest_regionviews.empty()) {
2683                 /* something went wrong */
2684                 return;
2685         }
2686
2687         /* we need to deselect all other regionviews, and select this one
2688            i'm ignoring undo stuff, because the region creation will take care of it
2689         */
2690         selection->set (latest_regionviews);
2691
2692         _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2693 }
2694
2695 void
2696 Editor::escape ()
2697 {
2698         if (_drags->active ()) {
2699                 _drags->abort ();
2700         } else {
2701                 selection->clear ();
2702         }
2703 }
2704
2705 void
2706 Editor::set_internal_edit (bool yn)
2707 {
2708         if (_internal_editing == yn) {
2709                 return;
2710         }
2711
2712         _internal_editing = yn;
2713         
2714         if (yn) {
2715                 pre_internal_mouse_mode = mouse_mode;
2716                 pre_internal_snap_type = _snap_type;
2717                 pre_internal_snap_mode = _snap_mode;
2718
2719                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2720                         (*i)->enter_internal_edit_mode ();
2721                 }
2722
2723                 set_snap_to (internal_snap_type);
2724                 set_snap_mode (internal_snap_mode);
2725
2726         } else {
2727
2728                 internal_snap_mode = _snap_mode;
2729                 internal_snap_type = _snap_type;
2730
2731                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2732                         (*i)->leave_internal_edit_mode ();
2733                 }
2734
2735                 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2736                         /* we were drawing .. flip back to something sensible */
2737                         set_mouse_mode (pre_internal_mouse_mode);
2738                 }
2739
2740                 set_snap_to (pre_internal_snap_type);
2741                 set_snap_mode (pre_internal_snap_mode);
2742         }
2743         
2744         set_canvas_cursor ();
2745 }
2746
2747 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2748  *  used by the `join object/range' tool mode.
2749  */
2750 void
2751 Editor::update_join_object_range_location (double /*x*/, double y)
2752 {
2753         /* XXX: actually, this decides based on whether the mouse is in the top
2754            or bottom half of a the waveform part RouteTimeAxisView;
2755
2756            Note that entered_{track,regionview} is not always setup (e.g. if
2757            the mouse is over a TimeSelection), and to get a Region
2758            that we're over requires searching the playlist.
2759         */
2760
2761         if (!smart_mode_action->get_active() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2762                 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2763                 return;
2764         }
2765
2766         if (mouse_mode == MouseObject) {
2767                 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2768         } else if (mouse_mode == MouseRange) {
2769                 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2770         }
2771
2772         /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2773         pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2774
2775         if (tvp.first) {
2776
2777                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2778                 if (rtv) {
2779
2780                         double cx = 0;
2781                         double cy = y;
2782                         rtv->canvas_display()->w2i (cx, cy);
2783
2784                         double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2785                         double d;
2786                         double const f = modf (c, &d);
2787
2788                         _join_object_range_state = f < 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2789                 }
2790         }
2791 }
2792
2793 Editing::MouseMode
2794 Editor::effective_mouse_mode () const
2795 {
2796         if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2797                 return MouseObject;
2798         } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2799                 return MouseRange;
2800         }
2801
2802         return mouse_mode;
2803 }
2804
2805 void
2806 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2807 {
2808         ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2809         assert (e);
2810
2811         e->region_view().delete_note (e->note ());
2812 }
2813
2814 void
2815 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2816 {
2817         assert (rv);
2818
2819         ArdourCanvas::Group* g = rv->get_canvas_group ();
2820         ArdourCanvas::Group* p = g->get_parent_group ();
2821
2822         /* Compute x in region view parent coordinates */
2823         double dy = 0;
2824         p->w2i (x, dy);
2825
2826         double x1, x2, y1, y2;
2827         g->get_bounds (x1, y1, x2, y2);
2828
2829         /* Halfway across the region */
2830         double const h = (x1 + x2) / 2;
2831
2832         Trimmable::CanTrim ct = rv->region()->can_trim ();
2833         if (x <= h) {
2834                 if (ct & Trimmable::FrontTrimEarlier) {
2835                         set_canvas_cursor (_cursors->left_side_trim);
2836                 } else {
2837                         set_canvas_cursor (_cursors->left_side_trim_right_only);
2838                 }
2839         } else {
2840                 if (ct & Trimmable::EndTrimLater) {
2841                         set_canvas_cursor (_cursors->right_side_trim);
2842                 } else {
2843                         set_canvas_cursor (_cursors->right_side_trim_left_only);
2844                 }
2845         }
2846 }
2847
2848 /** Obtain the pointer position in world coordinates */
2849 void
2850 Editor::get_pointer_position (double& x, double& y) const
2851 {
2852         int px, py;
2853         track_canvas->get_pointer (px, py);
2854         track_canvas->window_to_world (px, py, x, y);
2855 }