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