Fix out-of-whack notebook tabs wrt their contents in certain port matrices, broken...
[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 RubberbandSelectDrag (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 RubberbandSelectDrag (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 RubberbandSelectDrag (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 (item, 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 (item, 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         case EndSelectionTrimItem:
1659
1660 #ifdef WITH_CMT
1661         case ImageFrameHandleStartItem:
1662         case ImageFrameHandleEndItem:
1663         case MarkerViewHandleStartItem:
1664         case MarkerViewHandleEndItem:
1665 #endif
1666
1667                 if (is_drawable()) {
1668                         set_canvas_cursor (_cursors->trimmer);
1669                 }
1670                 break;
1671
1672         case PlayheadCursorItem:
1673                 if (is_drawable()) {
1674                         switch (_edit_point) {
1675                         case EditAtMouse:
1676                                 set_canvas_cursor (_cursors->grabber_edit_point);
1677                                 break;
1678                         default:
1679                                 set_canvas_cursor (_cursors->grabber);
1680                                 break;
1681                         }
1682                 }
1683                 break;
1684
1685         case RegionViewName:
1686
1687                 /* when the name is not an active item, the entire name highlight is for trimming */
1688
1689                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1690                         if (mouse_mode == MouseObject && is_drawable()) {
1691                                 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1692                                 _over_region_trim_target = true;
1693                         }
1694                 }
1695                 break;
1696
1697
1698         case AutomationTrackItem:
1699                 if (is_drawable()) {
1700                         Gdk::Cursor *cursor;
1701                         switch (mouse_mode) {
1702                         case MouseRange:
1703                                 cursor = _cursors->selector;
1704                                 break;
1705                         case MouseZoom:
1706                                 cursor = _cursors->zoom_in;
1707                                 break;
1708                         default:
1709                                 cursor = _cursors->cross_hair;
1710                                 break;
1711                         }
1712
1713                         set_canvas_cursor (cursor);
1714
1715                         AutomationTimeAxisView* atv;
1716                         if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1717                                 clear_entered_track = false;
1718                                 set_entered_track (atv);
1719                         }
1720                 }
1721                 break;
1722
1723         case MarkerBarItem:
1724         case RangeMarkerBarItem:
1725         case TransportMarkerBarItem:
1726         case CdMarkerBarItem:
1727         case MeterBarItem:
1728         case TempoBarItem:
1729                 if (is_drawable()) {
1730                         set_canvas_cursor (_cursors->timebar);
1731                 }
1732                 break;
1733
1734         case MarkerItem:
1735                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1736                         break;
1737                 }
1738                 entered_marker = marker;
1739                 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1740                 // fall through
1741         case MeterMarkerItem:
1742         case TempoMarkerItem:
1743                 if (is_drawable()) {
1744                         set_canvas_cursor (_cursors->timebar);
1745                 }
1746                 break;
1747
1748         case FadeInHandleItem:
1749                 if (mouse_mode == MouseObject && !internal_editing()) {
1750                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1751                         if (rect) {
1752                                 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1753                         }
1754                         set_canvas_cursor (_cursors->fade_in);
1755                 }
1756                 break;
1757
1758         case FadeOutHandleItem:
1759                 if (mouse_mode == MouseObject && !internal_editing()) {
1760                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1761                         if (rect) {
1762                                 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1763                         }
1764                         set_canvas_cursor (_cursors->fade_out);
1765                 }
1766                 break;
1767         case FeatureLineItem:
1768                 {
1769                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1770                         line->property_fill_color_rgba() = 0xFF0000FF;
1771                 }
1772                 break;
1773         case SelectionItem:
1774                 if (join_object_range_button.get_active()) {
1775                         set_canvas_cursor ();
1776                 }
1777                 break;
1778
1779         default:
1780                 break;
1781         }
1782
1783         /* second pass to handle entered track status in a comprehensible way.
1784          */
1785
1786         switch (item_type) {
1787         case GainLineItem:
1788         case AutomationLineItem:
1789         case ControlPointItem:
1790                 /* these do not affect the current entered track state */
1791                 clear_entered_track = false;
1792                 break;
1793
1794         case AutomationTrackItem:
1795                 /* handled above already */
1796                 break;
1797
1798         default:
1799                 set_entered_track (0);
1800                 break;
1801         }
1802
1803         return ret;
1804 }
1805
1806 bool
1807 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1808 {
1809         AutomationLine* al;
1810         ControlPoint* cp;
1811         Marker *marker;
1812         Location *loc;
1813         RegionView* rv;
1814         bool is_start;
1815         bool ret = true;
1816
1817         switch (item_type) {
1818         case ControlPointItem:
1819                 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1820                 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1821                         if (cp->line().npoints() > 1 && !cp->get_selected()) {
1822                                 cp->set_visible (false);
1823                         }
1824                 }
1825
1826                 if (is_drawable()) {
1827                         set_canvas_cursor (current_canvas_cursor);
1828                 }
1829
1830                 _verbose_cursor->hide ();
1831                 break;
1832
1833         case RegionViewNameHighlight:
1834         case LeftFrameHandle:
1835         case RightFrameHandle:
1836         case StartSelectionTrimItem:
1837         case EndSelectionTrimItem:
1838         case PlayheadCursorItem:
1839
1840 #ifdef WITH_CMT
1841         case ImageFrameHandleStartItem:
1842         case ImageFrameHandleEndItem:
1843         case MarkerViewHandleStartItem:
1844         case MarkerViewHandleEndItem:
1845 #endif
1846
1847                 _over_region_trim_target = false;
1848
1849                 if (is_drawable()) {
1850                         set_canvas_cursor (current_canvas_cursor);
1851                 }
1852                 break;
1853
1854         case GainLineItem:
1855         case AutomationLineItem:
1856                 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1857                 {
1858                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1859                         if (line)
1860                                 line->property_fill_color_rgba() = al->get_line_color();
1861                 }
1862                 if (is_drawable()) {
1863                         set_canvas_cursor (current_canvas_cursor);
1864                 }
1865                 break;
1866
1867         case RegionViewName:
1868                 /* see enter_handler() for notes */
1869                 _over_region_trim_target = false;
1870
1871                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1872                         if (is_drawable() && mouse_mode == MouseObject) {
1873                                 set_canvas_cursor (current_canvas_cursor);
1874                         }
1875                 }
1876                 break;
1877
1878         case RangeMarkerBarItem:
1879         case TransportMarkerBarItem:
1880         case CdMarkerBarItem:
1881         case MeterBarItem:
1882         case TempoBarItem:
1883         case MarkerBarItem:
1884                 if (is_drawable()) {
1885                         set_canvas_cursor (current_canvas_cursor);
1886                 }
1887                 break;
1888
1889         case MarkerItem:
1890                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1891                         break;
1892                 }
1893                 entered_marker = 0;
1894                 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1895                         location_flags_changed (loc, this);
1896                 }
1897                 // fall through
1898         case MeterMarkerItem:
1899         case TempoMarkerItem:
1900
1901                 if (is_drawable()) {
1902                         set_canvas_cursor (_cursors->timebar);
1903                 }
1904
1905                 break;
1906
1907         case FadeInHandleItem:
1908         case FadeOutHandleItem:
1909                 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1910                 {
1911                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1912                         if (rect) {
1913                                 rect->property_fill_color_rgba() = rv->get_fill_color();
1914                                 rect->property_outline_pixels() = 0;
1915                         }
1916                 }
1917                 set_canvas_cursor (current_canvas_cursor);
1918                 break;
1919
1920         case AutomationTrackItem:
1921                 if (is_drawable()) {
1922                         set_canvas_cursor (current_canvas_cursor);
1923                         clear_entered_track = true;
1924                         Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1925                 }
1926                 break;
1927         case FeatureLineItem:
1928                 {
1929                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1930                         line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
1931                 }
1932                 break;
1933
1934         default:
1935                 break;
1936         }
1937
1938         return ret;
1939 }
1940
1941 gint
1942 Editor::left_automation_track ()
1943 {
1944         if (clear_entered_track) {
1945                 set_entered_track (0);
1946                 clear_entered_track = false;
1947         }
1948         return false;
1949 }
1950
1951 void
1952 Editor::scrub (framepos_t frame, double current_x)
1953 {
1954         double delta;
1955
1956         if (scrubbing_direction == 0) {
1957                 /* first move */
1958                 _session->request_locate (frame, false);
1959                 _session->request_transport_speed (0.1);
1960                 scrubbing_direction = 1;
1961
1962         } else {
1963
1964                 if (last_scrub_x > current_x) {
1965
1966                         /* pointer moved to the left */
1967
1968                         if (scrubbing_direction > 0) {
1969
1970                                 /* we reversed direction to go backwards */
1971
1972                                 scrub_reversals++;
1973                                 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1974
1975                         } else {
1976
1977                                 /* still moving to the left (backwards) */
1978
1979                                 scrub_reversals = 0;
1980                                 scrub_reverse_distance = 0;
1981
1982                                 delta = 0.01 * (last_scrub_x - current_x);
1983                                 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1984                         }
1985
1986                 } else {
1987                         /* pointer moved to the right */
1988
1989                         if (scrubbing_direction < 0) {
1990                                 /* we reversed direction to go forward */
1991
1992                                 scrub_reversals++;
1993                                 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1994
1995                         } else {
1996                                 /* still moving to the right */
1997
1998                                 scrub_reversals = 0;
1999                                 scrub_reverse_distance = 0;
2000
2001                                 delta = 0.01 * (current_x - last_scrub_x);
2002                                 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2003                         }
2004                 }
2005
2006                 /* if there have been more than 2 opposite motion moves detected, or one that moves
2007                    back more than 10 pixels, reverse direction
2008                 */
2009
2010                 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2011
2012                         if (scrubbing_direction > 0) {
2013                                 /* was forwards, go backwards */
2014                                 _session->request_transport_speed (-0.1);
2015                                 scrubbing_direction = -1;
2016                         } else {
2017                                 /* was backwards, go forwards */
2018                                 _session->request_transport_speed (0.1);
2019                                 scrubbing_direction = 1;
2020                         }
2021
2022                         scrub_reverse_distance = 0;
2023                         scrub_reversals = 0;
2024                 }
2025         }
2026
2027         last_scrub_x = current_x;
2028 }
2029
2030 bool
2031 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2032 {
2033         _last_motion_y = event->motion.y;
2034
2035         if (event->motion.is_hint) {
2036                 gint x, y;
2037
2038                 /* We call this so that MOTION_NOTIFY events continue to be
2039                    delivered to the canvas. We need to do this because we set
2040                    Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2041                    the density of the events, at the expense of a round-trip
2042                    to the server. Given that this will mostly occur on cases
2043                    where DISPLAY = :0.0, and given the cost of what the motion
2044                    event might do, its a good tradeoff.
2045                 */
2046
2047                 track_canvas->get_pointer (x, y);
2048         }
2049
2050         if (current_stepping_trackview) {
2051                 /* don't keep the persistent stepped trackview if the mouse moves */
2052                 current_stepping_trackview = 0;
2053                 step_timeout.disconnect ();
2054         }
2055
2056         if (_session && _session->actively_recording()) {
2057                 /* Sorry. no dragging stuff around while we record */
2058                 return true;
2059         }
2060
2061         JoinObjectRangeState const old = _join_object_range_state;
2062         update_join_object_range_location (event->motion.x, event->motion.y);
2063         if (_join_object_range_state != old) {
2064                 set_canvas_cursor ();
2065         }
2066
2067         if (_over_region_trim_target) {
2068                 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2069         }
2070
2071         bool handled = false;
2072         if (_drags->active ()) {
2073                 handled = _drags->motion_handler (event, from_autoscroll);
2074         }
2075
2076         if (!handled) {
2077                 return false;
2078         }
2079
2080         track_canvas_motion (event);
2081         return true;
2082 }
2083
2084 bool
2085 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2086 {
2087         ControlPoint* control_point;
2088
2089         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2090                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2091                 /*NOTREACHED*/
2092         }
2093
2094         AutomationLine& line = control_point->line ();
2095         if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2096                 /* we shouldn't remove the first or last gain point in region gain lines */
2097                 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2098                         return false;
2099                 }
2100         }
2101
2102         return true;
2103 }
2104
2105 void
2106 Editor::remove_control_point (ArdourCanvas::Item* item)
2107 {
2108         if (!can_remove_control_point (item)) {
2109                 return;
2110         }
2111
2112         ControlPoint* control_point;
2113
2114         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2115                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2116                 /*NOTREACHED*/
2117         }
2118
2119         control_point->line().remove_point (*control_point);
2120 }
2121
2122 void
2123 Editor::edit_control_point (ArdourCanvas::Item* item)
2124 {
2125         ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2126
2127         if (p == 0) {
2128                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2129                 /*NOTREACHED*/
2130         }
2131
2132         ControlPointDialog d (p);
2133         d.set_position (Gtk::WIN_POS_MOUSE);
2134         ensure_float (d);
2135
2136         if (d.run () != RESPONSE_ACCEPT) {
2137                 return;
2138         }
2139
2140         p->line().modify_point_y (*p, d.get_y_fraction ());
2141 }
2142
2143 void
2144 Editor::edit_note (ArdourCanvas::Item* item)
2145 {
2146         ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2147         assert (e);
2148
2149         EditNoteDialog d (&e->region_view(), e);
2150         d.set_position (Gtk::WIN_POS_MOUSE);
2151         ensure_float (d);
2152
2153         d.run ();
2154 }
2155
2156
2157 void
2158 Editor::visible_order_range (int* low, int* high) const
2159 {
2160         *low = TimeAxisView::max_order ();
2161         *high = 0;
2162
2163         for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2164
2165                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2166
2167                 if (!rtv->hidden()) {
2168
2169                         if (*high < rtv->order()) {
2170                                 *high = rtv->order ();
2171                         }
2172
2173                         if (*low > rtv->order()) {
2174                                 *low = rtv->order ();
2175                         }
2176                 }
2177         }
2178 }
2179
2180 void
2181 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2182 {
2183         /* Either add to or set the set the region selection, unless
2184            this is an alignment click (control used)
2185         */
2186
2187         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2188                 TimeAxisView* tv = &rv.get_time_axis_view();
2189                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2190                 double speed = 1.0;
2191                 if (rtv && rtv->is_track()) {
2192                         speed = rtv->track()->speed();
2193                 }
2194
2195                 framepos_t where = get_preferred_edit_position();
2196
2197                 if (where >= 0) {
2198
2199                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2200
2201                                 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2202
2203                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2204
2205                                 align_region (rv.region(), End, (framepos_t) (where * speed));
2206
2207                         } else {
2208
2209                                 align_region (rv.region(), Start, (framepos_t) (where * speed));
2210                         }
2211                 }
2212         }
2213 }
2214
2215 void
2216 Editor::collect_new_region_view (RegionView* rv)
2217 {
2218         latest_regionviews.push_back (rv);
2219 }
2220
2221 void
2222 Editor::collect_and_select_new_region_view (RegionView* rv)
2223 {
2224         selection->add(rv);
2225         latest_regionviews.push_back (rv);
2226 }
2227
2228 void
2229 Editor::cancel_selection ()
2230 {
2231         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2232                 (*i)->hide_selection ();
2233         }
2234
2235         selection->clear ();
2236         clicked_selection = 0;
2237 }
2238
2239
2240 void
2241 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2242 {
2243         RegionView* rv = clicked_regionview;
2244
2245         /* Choose action dependant on which button was pressed */
2246         switch (event->button.button) {
2247         case 1:
2248                 begin_reversible_command (_("start point trim"));
2249
2250                 if (selection->selected (rv)) {
2251                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2252                              i != selection->regions.by_layer().end(); ++i)
2253                         {
2254                                 if ( (*i) == NULL){
2255                                     cerr << "region view contains null region" << endl;
2256                                 }
2257
2258                                 if (!(*i)->region()->locked()) {
2259                                         (*i)->region()->clear_changes ();
2260                                         (*i)->region()->trim_front (new_bound);
2261                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2262                                 }
2263                         }
2264
2265                 } else {
2266                         if (!rv->region()->locked()) {
2267                                 rv->region()->clear_changes ();
2268                                 rv->region()->trim_front (new_bound);
2269                                 _session->add_command(new StatefulDiffCommand (rv->region()));
2270                         }
2271                 }
2272
2273                 commit_reversible_command();
2274
2275                 break;
2276         case 2:
2277                 begin_reversible_command (_("End point trim"));
2278
2279                 if (selection->selected (rv)) {
2280
2281                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2282                         {
2283                                 if (!(*i)->region()->locked()) {
2284                                         (*i)->region()->clear_changes();
2285                                         (*i)->region()->trim_end (new_bound);
2286                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2287                                 }
2288                         }
2289
2290                 } else {
2291
2292                         if (!rv->region()->locked()) {
2293                                 rv->region()->clear_changes ();
2294                                 rv->region()->trim_end (new_bound);
2295                                 _session->add_command (new StatefulDiffCommand (rv->region()));
2296                         }
2297                 }
2298
2299                 commit_reversible_command();
2300
2301                 break;
2302         default:
2303                 break;
2304         }
2305 }
2306
2307 void
2308 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2309 {
2310         Marker* marker;
2311         bool is_start;
2312
2313         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2314                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2315                 /*NOTREACHED*/
2316         }
2317
2318         Location* location = find_location_from_marker (marker, is_start);
2319         location->set_hidden (true, this);
2320 }
2321
2322
2323 void
2324 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2325 {
2326         double x1 = frame_to_pixel (start);
2327         double x2 = frame_to_pixel (end);
2328         double y2 = full_canvas_height - 1.0;
2329
2330         zoom_rect->property_x1() = x1;
2331         zoom_rect->property_y1() = 1.0;
2332         zoom_rect->property_x2() = x2;
2333         zoom_rect->property_y2() = y2;
2334 }
2335
2336
2337 gint
2338 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2339 {
2340         using namespace Gtkmm2ext;
2341
2342         ArdourPrompter prompter (false);
2343
2344         prompter.set_prompt (_("Name for region:"));
2345         prompter.set_initial_text (clicked_regionview->region()->name());
2346         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2347         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2348         prompter.show_all ();
2349         switch (prompter.run ()) {
2350         case Gtk::RESPONSE_ACCEPT:
2351                 string str;
2352                 prompter.get_result(str);
2353                 if (str.length()) {
2354                         clicked_regionview->region()->set_name (str);
2355                 }
2356                 break;
2357         }
2358         return true;
2359 }
2360
2361
2362 void
2363 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2364 {
2365         /* no brushing without a useful snap setting */
2366
2367         switch (_snap_mode) {
2368         case SnapMagnetic:
2369                 return; /* can't work because it allows region to be placed anywhere */
2370         default:
2371                 break; /* OK */
2372         }
2373
2374         switch (_snap_type) {
2375         case SnapToMark:
2376                 return;
2377
2378         default:
2379                 break;
2380         }
2381
2382         /* don't brush a copy over the original */
2383
2384         if (pos == rv->region()->position()) {
2385                 return;
2386         }
2387
2388         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2389
2390         if (rtv == 0 || !rtv->is_track()) {
2391                 return;
2392         }
2393
2394         boost::shared_ptr<Playlist> playlist = rtv->playlist();
2395         double speed = rtv->track()->speed();
2396
2397         playlist->clear_changes ();
2398         boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2399         playlist->add_region (new_region, (framepos_t) (pos * speed));
2400         _session->add_command (new StatefulDiffCommand (playlist));
2401
2402         // playlist is frozen, so we have to update manually XXX this is disgusting
2403
2404         playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2405 }
2406
2407 gint
2408 Editor::track_height_step_timeout ()
2409 {
2410         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2411                 current_stepping_trackview = 0;
2412                 return false;
2413         }
2414         return true;
2415 }
2416
2417 void
2418 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2419 {
2420         assert (region_view);
2421
2422         if (!region_view->region()->playlist()) {
2423                 return;
2424         }
2425
2426         _region_motion_group->raise_to_top ();
2427
2428         if (Config->get_edit_mode() == Splice) {
2429                 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2430         } else {
2431                 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2432                 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2433         }
2434
2435         /* sync the canvas to what we think is its current state */
2436         update_canvas_now();
2437 }
2438
2439 void
2440 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2441 {
2442         assert (region_view);
2443
2444         if (!region_view->region()->playlist()) {
2445                 return;
2446         }
2447
2448         _region_motion_group->raise_to_top ();
2449
2450         RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2451         _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2452 }
2453
2454 void
2455 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2456 {
2457         assert (region_view);
2458
2459         if (!region_view->region()->playlist()) {
2460                 return;
2461         }
2462
2463         if (Config->get_edit_mode() == Splice) {
2464                 return;
2465         }
2466
2467         RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2468         _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2469
2470         begin_reversible_command (Operations::drag_region_brush);
2471 }
2472
2473 /** Start a grab where a time range is selected, track(s) are selected, and the
2474  *  user clicks and drags a region with a modifier in order to create a new region containing
2475  *  the section of the clicked region that lies within the time range.
2476  */
2477 void
2478 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2479 {
2480         if (clicked_regionview == 0) {
2481                 return;
2482         }
2483
2484         /* lets try to create new Region for the selection */
2485
2486         vector<boost::shared_ptr<Region> > new_regions;
2487         create_region_from_selection (new_regions);
2488
2489         if (new_regions.empty()) {
2490                 return;
2491         }
2492
2493         /* XXX fix me one day to use all new regions */
2494
2495         boost::shared_ptr<Region> region (new_regions.front());
2496
2497         /* add it to the current stream/playlist.
2498
2499            tricky: the streamview for the track will add a new regionview. we will
2500            catch the signal it sends when it creates the regionview to
2501            set the regionview we want to then drag.
2502         */
2503
2504         latest_regionviews.clear();
2505         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2506
2507         /* A selection grab currently creates two undo/redo operations, one for
2508            creating the new region and another for moving it.
2509         */
2510
2511         begin_reversible_command (Operations::selection_grab);
2512
2513         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2514
2515         playlist->clear_changes ();
2516         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2517         _session->add_command(new StatefulDiffCommand (playlist));
2518
2519         commit_reversible_command ();
2520
2521         c.disconnect ();
2522
2523         if (latest_regionviews.empty()) {
2524                 /* something went wrong */
2525                 return;
2526         }
2527
2528         /* we need to deselect all other regionviews, and select this one
2529            i'm ignoring undo stuff, because the region creation will take care of it
2530         */
2531         selection->set (latest_regionviews);
2532
2533         _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2534 }
2535
2536 void
2537 Editor::escape ()
2538 {
2539         if (_drags->active ()) {
2540                 _drags->abort ();
2541         } else {
2542                 selection->clear ();
2543         }
2544 }
2545
2546 void
2547 Editor::set_internal_edit (bool yn)
2548 {
2549         _internal_editing = yn;
2550
2551         if (yn) {
2552                 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2553                 mouse_select_button.get_image ()->show ();
2554                 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
2555                 mouse_mode_toggled (mouse_mode);
2556
2557                 pre_internal_mouse_mode = mouse_mode;
2558
2559                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2560                         (*i)->enter_internal_edit_mode ();
2561                 }
2562
2563         } else {
2564
2565                 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2566                 mouse_select_button.get_image ()->show ();
2567                 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Select/Move Ranges"));
2568                 mouse_mode_toggled (mouse_mode); // sets cursor
2569
2570                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2571                         (*i)->leave_internal_edit_mode ();
2572                 }
2573
2574                 if (mouse_mode == MouseRange && pre_internal_mouse_mode != MouseRange) {
2575                         /* we were drawing .. flip back to something sensible */
2576                         set_mouse_mode (pre_internal_mouse_mode);
2577                 }
2578         }
2579 }
2580
2581 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2582  *  used by the `join object/range' tool mode.
2583  */
2584 void
2585 Editor::update_join_object_range_location (double /*x*/, double y)
2586 {
2587         /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2588            entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2589            that we're over requires searching the playlist.
2590         */
2591
2592         if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2593                 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2594                 return;
2595         }
2596
2597         if (mouse_mode == MouseObject) {
2598                 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2599         } else if (mouse_mode == MouseRange) {
2600                 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2601         }
2602
2603         /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2604         pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2605
2606         if (tvp.first) {
2607
2608                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2609                 if (rtv) {
2610
2611                         double cx = 0;
2612                         double cy = y;
2613                         rtv->canvas_display()->w2i (cx, cy);
2614
2615                         double const c = cy / rtv->view()->child_height();
2616                         double d;
2617                         double const f = modf (c, &d);
2618
2619                         _join_object_range_state = f < 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2620                 }
2621         }
2622 }
2623
2624 Editing::MouseMode
2625 Editor::effective_mouse_mode () const
2626 {
2627         if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2628                 return MouseObject;
2629         } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2630                 return MouseRange;
2631         }
2632
2633         return mouse_mode;
2634 }
2635
2636 void
2637 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2638 {
2639         ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2640         assert (e);
2641
2642         e->region_view().delete_note (e->note ());
2643 }
2644
2645 void
2646 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2647 {
2648         assert (rv);
2649
2650         ArdourCanvas::Group* g = rv->get_canvas_group ();
2651         ArdourCanvas::Group* p = g->get_parent_group ();
2652
2653         /* Compute x in region view parent coordinates */
2654         double dy = 0;
2655         p->w2i (x, dy);
2656
2657         double x1, x2, y1, y2;
2658         g->get_bounds (x1, y1, x2, y2);
2659
2660         /* Halfway across the region */
2661         double const h = (x1 + x2) / 2;
2662
2663         Trimmable::CanTrim ct = rv->region()->can_trim ();
2664         if (x <= h) {
2665                 if (ct & Trimmable::FrontTrimEarlier) {
2666                         set_canvas_cursor (_cursors->left_side_trim);
2667                 } else {
2668                         set_canvas_cursor (_cursors->left_side_trim_right_only);
2669                 }
2670         } else {
2671                 if (ct & Trimmable::EndTrimLater) {
2672                         set_canvas_cursor (_cursors->right_side_trim);
2673                 } else {
2674                         set_canvas_cursor (_cursors->right_side_trim_left_only);
2675                 }
2676         }
2677 }
2678
2679 /** Obtain the pointer position in world coordinates */
2680 void
2681 Editor::get_pointer_position (double& x, double& y) const
2682 {
2683         int px, py;
2684         track_canvas->get_pointer (px, py);
2685         track_canvas->window_to_world (px, py, x, y);
2686 }