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