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