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