Provide dialogs to edit pan values numerically, at least for
[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/audioregion.h"
66 #include "ardour/operations.h"
67 #include "ardour/playlist.h"
68 #include "ardour/profile.h"
69 #include "ardour/region_factory.h"
70 #include "ardour/route.h"
71 #include "ardour/session.h"
72 #include "ardour/types.h"
73
74 #include <bitset>
75
76 #include "i18n.h"
77
78 using namespace std;
79 using namespace ARDOUR;
80 using namespace PBD;
81 using namespace Gtk;
82 using namespace Editing;
83 using Gtkmm2ext::Keyboard;
84
85 bool
86 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
87 {
88         /* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
89            pays attentions to subwindows. this means that menu windows are ignored, and 
90            if the pointer is in a menu, the return window from the call will be the
91            the regular subwindow *under* the menu.
92
93            this matters quite a lot if the pointer is moving around in a menu that overlaps
94            the track canvas because we will believe that we are within the track canvas
95            when we are not. therefore, we track enter/leave events for the track canvas
96            and allow that to override the result of gdk_window_get_pointer().
97         */
98
99         if (!within_track_canvas) {
100                 return false;
101         }
102
103         int x, y;
104         double wx, wy;
105         Gdk::ModifierType mask;
106         Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
107         Glib::RefPtr<const Gdk::Window> pointer_window;
108
109         if (!canvas_window) {
110                 return false;
111         }
112
113         pointer_window = canvas_window->get_pointer (x, y, mask);
114
115         if (pointer_window == track_canvas->get_bin_window()) {
116                 wx = x;
117                 wy = y;
118                 in_track_canvas = true;
119
120         } else {
121                 in_track_canvas = false;
122                         return false;
123         }
124
125         GdkEvent event;
126         event.type = GDK_BUTTON_RELEASE;
127         event.button.x = wx;
128         event.button.y = wy;
129
130         where = event_frame (&event, 0, 0);
131         return true;
132 }
133
134 framepos_t
135 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
136 {
137         double cx, cy;
138
139         if (pcx == 0) {
140                 pcx = &cx;
141         }
142         if (pcy == 0) {
143                 pcy = &cy;
144         }
145
146         *pcx = 0;
147         *pcy = 0;
148
149         switch (event->type) {
150         case GDK_BUTTON_RELEASE:
151         case GDK_BUTTON_PRESS:
152         case GDK_2BUTTON_PRESS:
153         case GDK_3BUTTON_PRESS:
154                 *pcx = event->button.x;
155                 *pcy = event->button.y;
156                 _trackview_group->w2i(*pcx, *pcy);
157                 break;
158         case GDK_MOTION_NOTIFY:
159                 *pcx = event->motion.x;
160                 *pcy = event->motion.y;
161                 _trackview_group->w2i(*pcx, *pcy);
162                 break;
163         case GDK_ENTER_NOTIFY:
164         case GDK_LEAVE_NOTIFY:
165                 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
166                 break;
167         case GDK_KEY_PRESS:
168         case GDK_KEY_RELEASE:
169                 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
170                 break;
171         default:
172                 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
173                 break;
174         }
175
176         /* note that pixel_to_frame() never returns less than zero, so even if the pixel
177            position is negative (as can be the case with motion events in particular),
178            the frame location is always positive.
179         */
180
181         return pixel_to_frame (*pcx);
182 }
183
184 Gdk::Cursor*
185 Editor::which_grabber_cursor ()
186 {
187         Gdk::Cursor* c = _cursors->grabber;
188
189         if (_internal_editing) {
190                 switch (mouse_mode) {
191                 case MouseDraw:
192                         c = _cursors->midi_pencil;
193                         break;
194
195                 case MouseObject:
196                         c = _cursors->grabber_note;
197                         break;
198
199                 case MouseTimeFX:
200                         c = _cursors->midi_resize;
201                         break;
202
203                 default:
204                         break;
205                 }
206
207         } else {
208
209                 switch (_edit_point) {
210                 case EditAtMouse:
211                         c = _cursors->grabber_edit_point;
212                         break;
213                 default:
214                         boost::shared_ptr<Movable> m = _movable.lock();
215                         if (m && m->locked()) {
216                                 c = _cursors->speaker;
217                         }
218                         break;
219                 }
220         }
221
222         return c;
223 }
224
225 void
226 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
227 {
228         boost::shared_ptr<Trimmable> st = _trimmable.lock();
229
230         if (!st || st == t) {
231                 _trimmable = t;
232                 set_canvas_cursor ();
233         }
234 }
235
236 void
237 Editor::set_current_movable (boost::shared_ptr<Movable> m)
238 {
239         boost::shared_ptr<Movable> sm = _movable.lock();
240
241         if (!sm || sm != m) {
242                 _movable = m;
243                 set_canvas_cursor ();
244         }
245 }
246
247 void
248 Editor::set_canvas_cursor ()
249 {
250         switch (mouse_mode) {
251         case MouseRange:
252                 current_canvas_cursor = _cursors->selector;
253                 break;
254
255         case MouseObject:
256                 current_canvas_cursor = which_grabber_cursor();
257                 break;
258
259         case MouseDraw:
260                 current_canvas_cursor = _cursors->midi_pencil;
261                 break;
262
263         case MouseGain:
264                 current_canvas_cursor = _cursors->cross_hair;
265                 break;
266
267         case MouseZoom:
268                 if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
269                         current_canvas_cursor = _cursors->zoom_out;
270                 } else {
271                         current_canvas_cursor = _cursors->zoom_in;
272                 }
273                 break;
274
275         case MouseTimeFX:
276                 current_canvas_cursor = _cursors->time_fx; // just use playhead
277                 break;
278
279         case MouseAudition:
280                 current_canvas_cursor = _cursors->speaker;
281                 break;
282         }
283
284         switch (_join_object_range_state) {
285         case JOIN_OBJECT_RANGE_NONE:
286                 break;
287         case JOIN_OBJECT_RANGE_OBJECT:
288                 current_canvas_cursor = which_grabber_cursor ();
289                 break;
290         case JOIN_OBJECT_RANGE_RANGE:
291                 current_canvas_cursor = _cursors->selector;
292                 break;
293         }
294
295         /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
296         if (smart_mode_action->get_active()) {
297                 double x, y;
298                 get_pointer_position (x, y);
299                 ArdourCanvas::Item* i = track_canvas->get_item_at (x, y);
300                 if (i && i->property_parent() && (*i->property_parent()).get_data (X_("timeselection"))) {
301                         pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y + vertical_adjustment.get_value() - canvas_timebars_vsize);
302                         if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
303                                 current_canvas_cursor = _cursors->up_down;
304                         }
305                 }
306         }
307
308         set_canvas_cursor (current_canvas_cursor, true);
309 }
310
311 void
312 Editor::set_mouse_mode (MouseMode m, bool force)
313 {
314         if (_drags->active ()) {
315                 return;
316         }
317
318         if (!force && m == mouse_mode) {
319                 return;
320         }
321
322         Glib::RefPtr<Action> act;
323
324         switch (m) {
325         case MouseRange:
326                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
327                 break;
328
329         case MouseObject:
330                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
331                 break;
332
333         case MouseDraw:
334                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
335                 break;
336
337         case MouseGain:
338                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
339                 break;
340
341         case MouseZoom:
342                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
343                 break;
344
345         case MouseTimeFX:
346                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
347                 break;
348
349         case MouseAudition:
350                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
351                 break;
352         }
353
354         assert (act);
355
356         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
357         assert (tact);
358
359         /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
360         tact->set_active (false);
361         tact->set_active (true);
362
363         MouseModeChanged (); /* EMIT SIGNAL */
364 }
365
366 void
367 Editor::mouse_mode_toggled (MouseMode m)
368 {
369         Glib::RefPtr<Action> act;
370         Glib::RefPtr<ToggleAction> tact;
371
372         switch (m) {
373         case MouseRange:
374                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
375                 break;
376
377         case MouseObject:
378                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
379                 break;
380
381         case MouseDraw:
382                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
383                 break;
384
385         case MouseGain:
386                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
387                 break;
388
389         case MouseZoom:
390                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
391                 break;
392
393         case MouseTimeFX:
394                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
395                 break;
396
397         case MouseAudition:
398                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
399                 break;
400         }
401
402         assert (act);
403
404         tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
405         assert (tact);
406
407         if (!tact->get_active()) {
408                 /* this was just the notification that the old mode has been
409                  * left. we'll get called again with the new mode active in a
410                  * jiffy.
411                  */
412                 return;
413         }
414
415         switch (m) {
416         case MouseDraw:
417                 act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
418                 tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
419                 tact->set_active (true);
420                 break;
421         default:
422                 break;
423         }
424
425         mouse_mode = m;
426
427         instant_save ();
428
429         if (!internal_editing()) {
430                 if (mouse_mode != MouseRange && mouse_mode != MouseGain && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
431
432                         /* in all modes except range, gain and joined object/range, hide the range selection,
433                            show the object (region) selection.
434                         */
435
436                         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
437                                 (*i)->hide_selection ();
438                         }
439
440                 } else {
441
442                         /*
443                           in range or object/range mode, show the range selection.
444                         */
445
446                         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
447                                 (*i)->show_selection (selection->time);
448                         }
449                 }
450         }
451
452         set_canvas_cursor ();
453         set_gain_envelope_visibility ();
454
455         MouseModeChanged (); /* EMIT SIGNAL */
456 }
457
458 void
459 Editor::step_mouse_mode (bool next)
460 {
461         switch (current_mouse_mode()) {
462         case MouseObject:
463                 if (next) {
464                         if (Profile->get_sae()) {
465                                 set_mouse_mode (MouseZoom);
466                         } else {
467                                 set_mouse_mode (MouseRange);
468                         }
469                 } else {
470                         set_mouse_mode (MouseTimeFX);
471                 }
472                 break;
473
474         case MouseRange:
475                 if (next) set_mouse_mode (MouseDraw);
476                 else set_mouse_mode (MouseObject);
477                 break;
478
479         case MouseDraw:
480                 if (next) set_mouse_mode (MouseZoom);
481                 else set_mouse_mode (MouseRange);
482                 break;
483
484         case MouseZoom:
485                 if (next) {
486                         if (Profile->get_sae()) {
487                                 set_mouse_mode (MouseTimeFX);
488                         } else {
489                                 set_mouse_mode (MouseGain);
490                         }
491                 } else {
492                         if (Profile->get_sae()) {
493                                 set_mouse_mode (MouseObject);
494                         } else {
495                                 set_mouse_mode (MouseDraw);
496                         }
497                 }
498                 break;
499
500         case MouseGain:
501                 if (next) set_mouse_mode (MouseTimeFX);
502                 else set_mouse_mode (MouseZoom);
503                 break;
504
505         case MouseTimeFX:
506                 if (next) {
507                         set_mouse_mode (MouseAudition);
508                 } else {
509                         if (Profile->get_sae()) {
510                                 set_mouse_mode (MouseZoom);
511                         } else {
512                                 set_mouse_mode (MouseGain);
513                         }
514                 }
515                 break;
516
517         case MouseAudition:
518                 if (next) set_mouse_mode (MouseObject);
519                 else set_mouse_mode (MouseTimeFX);
520                 break;
521         }
522 }
523
524 bool
525 Editor::toggle_internal_editing_from_double_click (GdkEvent* event)
526 {
527         if (_drags->active()) {
528                 _drags->end_grab (event);
529         } 
530
531         ActionManager::do_action ("MouseMode", "toggle-internal-edit");
532
533         /* prevent reversion of edit cursor on button release */
534         
535         pre_press_cursor = 0;
536
537         return true;
538 }
539
540 void
541 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
542 {
543         /* in object/audition/timefx/gain-automation mode,
544            any button press sets the selection if the object
545            can be selected. this is a bit of hack, because
546            we want to avoid this if the mouse operation is a
547            region alignment.
548
549            note: not dbl-click or triple-click
550
551            Also note that there is no region selection in internal edit mode, otherwise
552            for operations operating on the selection (e.g. cut) it is not obvious whether
553            to cut notes or regions.
554         */
555
556         if (((mouse_mode != MouseObject) &&
557              (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
558              (mouse_mode != MouseAudition || item_type != RegionItem) &&
559              (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
560              (mouse_mode != MouseGain) &&
561              (mouse_mode != MouseRange) &&
562              (mouse_mode != MouseDraw)) ||
563             ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
564             (internal_editing() && mouse_mode != MouseTimeFX)) {
565
566                 return;
567         }
568
569         if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
570
571                 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
572
573                         /* almost no selection action on modified button-2 or button-3 events */
574
575                         if (item_type != RegionItem && event->button.button != 2) {
576                                 return;
577                         }
578                 }
579         }
580
581         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
582         bool press = (event->type == GDK_BUTTON_PRESS);
583
584         switch (item_type) {
585         case RegionItem:
586                 if (!doing_range_stuff()) {
587                         set_selected_regionview_from_click (press, op);
588                 }
589                 
590                 if (press) {
591                         if (doing_range_stuff()) {
592                                 /* don't change the selection unless the
593                                    clicked track is not currently selected. if
594                                    so, "collapse" the selection to just this
595                                    track
596                                 */
597                                 if (!selection->selected (clicked_axisview)) {
598                                         set_selected_track_as_side_effect (Selection::Set);
599                                 }
600                         }
601                 }
602                 break;
603
604         case RegionViewNameHighlight:
605         case RegionViewName:
606         case LeftFrameHandle:
607         case RightFrameHandle:
608                 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
609                         set_selected_regionview_from_click (press, op);
610                 } else if (event->type == GDK_BUTTON_PRESS) {
611                         set_selected_track_as_side_effect (op);
612                 }
613                 break;
614
615         case FadeInHandleItem:
616         case FadeInItem:
617         case FadeOutHandleItem:
618         case FadeOutItem:
619         case StartCrossFadeItem:
620         case EndCrossFadeItem:
621                 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
622                         set_selected_regionview_from_click (press, op);
623                 } else if (event->type == GDK_BUTTON_PRESS) {
624                         set_selected_track_as_side_effect (op);
625                 }
626                 break;
627
628         case ControlPointItem:
629                 set_selected_track_as_side_effect (op);
630                 if (doing_object_stuff() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
631                         set_selected_control_point_from_click (press, op);
632                 }
633                 break;
634
635         case StreamItem:
636                 /* for context click, select track */
637                 if (event->button.button == 3) {
638                         selection->clear_tracks ();
639                         set_selected_track_as_side_effect (op);
640                 }
641                 break;
642
643         case AutomationTrackItem:
644                 set_selected_track_as_side_effect (op);
645                 break;
646
647         default:
648                 break;
649         }
650 }
651
652 bool
653 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
654 {
655         /* single mouse clicks on any of these item types operate
656            independent of mouse mode, mostly because they are
657            not on the main track canvas or because we want
658            them to be modeless.
659         */
660
661         switch (item_type) {
662         case PlayheadCursorItem:
663                 _drags->set (new CursorDrag (this, item, true), event);
664                 return true;
665
666         case MarkerItem:
667                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
668                         hide_marker (item, event);
669                 } else {
670                         _drags->set (new MarkerDrag (this, item), event);
671                 }
672                 return true;
673
674         case TempoMarkerItem:
675         {
676                 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
677                 assert (m);
678                 if (m->tempo().movable ()) {
679                         _drags->set (
680                                 new TempoMarkerDrag (
681                                         this,
682                                         item,
683                                         Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
684                                         ),
685                                 event
686                                 );
687                         return true;
688                 } else {
689                         return false;
690                 }
691         }
692
693         case MeterMarkerItem:
694         {
695                 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
696                 assert (m);
697                 if (m->meter().movable ()) {
698                         _drags->set (
699                                 new MeterMarkerDrag (
700                                         this,
701                                         item,
702                                         Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
703                                         ),
704                                 event
705                                 );
706                         return true;
707                 } else {
708                         return false;
709                 }
710         }
711
712         case MarkerBarItem:
713         case TempoBarItem:
714         case MeterBarItem:
715                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
716                         _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
717                 }
718                 return true;
719                 break;
720
721
722         case RangeMarkerBarItem:
723                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
724                         _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
725                 } else {
726                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
727                 }
728                 return true;
729                 break;
730
731         case CdMarkerBarItem:
732                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
733                         _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
734                 } else {
735                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
736                 }
737                 return true;
738                 break;
739
740         case TransportMarkerBarItem:
741                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
742                         _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
743                 } else {
744                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
745                 }
746                 return true;
747                 break;
748
749         default:
750                 break;
751         }
752
753         if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
754                 /* special case: allow trim of range selections in joined object mode;
755                    in theory eff should equal MouseRange in this case, but it doesn't
756                    because entering the range selection canvas item results in entered_regionview
757                    being set to 0, so update_join_object_range_location acts as if we aren't
758                    over a region.
759                 */
760                 if (item_type == StartSelectionTrimItem) {
761                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
762                 } else if (item_type == EndSelectionTrimItem) {
763                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
764                 }
765         }
766
767         Editing::MouseMode eff = effective_mouse_mode ();
768
769         /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
770         if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
771                 eff = MouseObject;
772         }
773
774         switch (eff) {
775         case MouseRange:
776                 switch (item_type) {
777                 case StartSelectionTrimItem:
778                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
779                         break;
780
781                 case EndSelectionTrimItem:
782                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
783                         break;
784
785                 case SelectionItem:
786                         if (Keyboard::modifier_state_contains
787                             (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
788                                 // contains and not equals because I can't use alt as a modifier alone.
789                                 start_selection_grab (item, event);
790                         } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
791                                 /* grab selection for moving */
792                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
793                         } else {
794                                 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
795                                 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
796                                 if (tvp.first) {
797                                         AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
798                                         if (smart_mode_action->get_active() && atv) {
799                                                 /* smart "join" mode: drag automation */
800                                                 _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
801                                         } else {
802                                                 /* this was debated, but decided the more common action was to
803                                                    make a new selection */
804                                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
805                                         }
806                                 }
807                         }
808                         break;
809
810                 case StreamItem:
811                         if (internal_editing()) {
812                                 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
813                                         _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
814                                         return true;
815                                 } 
816                         } else {
817                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
818                                 return true;
819                         }
820                         break;
821
822                 case RegionViewNameHighlight:
823                         if (!clicked_regionview->region()->locked()) {
824                                 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
825                                 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
826                                 return true;
827                         }
828                         break;
829
830                 case LeftFrameHandle:
831                 case RightFrameHandle:
832                         if (!internal_editing() && doing_object_stuff() && !clicked_regionview->region()->locked()) {
833                                 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
834                                 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
835                                 return true;
836                         }
837                         break;
838
839                 default:
840                         if (!internal_editing()) {
841                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
842                         }
843                 }
844                 return true;
845                 break;
846
847         case MouseDraw:
848                 switch (item_type) {
849                 case NoteItem:
850                         if (internal_editing()) {
851                                 /* trim notes if we're in internal edit mode and near the ends of the note */
852                                 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
853                                 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
854                                         _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
855                                 } else {
856                                         _drags->set (new NoteDrag (this, item), event);
857                                 }
858                                 return true;
859                         }
860                         break;
861
862                 default:
863                         break;
864                 }
865                 break;
866
867         case MouseObject:
868                 switch (item_type) {
869                 case NoteItem:
870                         if (internal_editing()) {
871                                 ArdourCanvas::CanvasNoteEvent* cn = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
872                                 if (cn->mouse_near_ends()) {
873                                         _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
874                                 } else {
875                                         _drags->set (new NoteDrag (this, item), event);
876                                 }
877                                 return true;
878                         }
879                         break;
880
881                 default:
882                         break;
883                 }
884
885                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
886                     event->type == GDK_BUTTON_PRESS) {
887
888                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
889
890                 } else if (event->type == GDK_BUTTON_PRESS) {
891
892                         switch (item_type) {
893                         case FadeInHandleItem:
894                         {
895                                 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
896                                 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
897                                 return true;
898                         }
899
900                         case FadeOutHandleItem:
901                         {
902                                 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
903                                 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
904                                 return true;
905                         }
906
907                         case StartCrossFadeItem:
908                                 _drags->set (new CrossfadeEdgeDrag (this, reinterpret_cast<AudioRegionView*>(item->get_data("regionview")), item, true), event, 0);
909                                 break;
910
911                         case EndCrossFadeItem:
912                                 _drags->set (new CrossfadeEdgeDrag (this, reinterpret_cast<AudioRegionView*>(item->get_data("regionview")), item, false), event, 0);
913                                 break;
914
915                         case FeatureLineItem:
916                         {
917                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
918                                         remove_transient(item);
919                                         return true;
920                                 }
921
922                                 _drags->set (new FeatureLineDrag (this, item), event);
923                                 return true;
924                                 break;
925                         }
926
927                         case RegionItem:
928                                 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
929                                         /* click on an automation region view; do nothing here and let the ARV's signal handler
930                                            sort it out.
931                                         */
932                                         break;
933                                 }
934
935                                 if (internal_editing ()) {
936                                         if (event->type == GDK_2BUTTON_PRESS && event->button.button == 1) {
937                                                 Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("toggle-internal-edit"));
938                                                 act->activate ();
939                                         }
940                                         break;
941                                 }
942
943                                 /* click on a normal region view */
944                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
945                                         add_region_copy_drag (item, event, clicked_regionview);
946                                 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
947                                         add_region_brush_drag (item, event, clicked_regionview);
948                                 } else {
949                                         add_region_drag (item, event, clicked_regionview);
950                                 }
951
952
953                                 if (!internal_editing() && (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE && !selection->regions.empty())) {
954                                         _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
955                                 }
956
957                                 _drags->start_grab (event);
958                                 break;
959
960                         case RegionViewNameHighlight:
961                         case LeftFrameHandle:
962                         case RightFrameHandle:
963                                 if (!clicked_regionview->region()->locked()) {
964                                         RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
965                                         _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
966                                         return true;
967                                 }
968                                 break;
969
970                         case RegionViewName:
971                         {
972                                 /* rename happens on edit clicks */
973                                 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
974                                 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
975                                 return true;
976                                 break;
977                         }
978
979                         case ControlPointItem:
980                                 _drags->set (new ControlPointDrag (this, item), event);
981                                 return true;
982                                 break;
983
984                         case AutomationLineItem:
985                                 _drags->set (new LineDrag (this, item), event);
986                                 return true;
987                                 break;
988
989                         case StreamItem:
990                                 if (internal_editing()) {
991                                         if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
992                                                 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
993                                         }
994                                         return true;
995                                 } else {
996                                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
997                                 }
998                                 break;
999
1000                         case AutomationTrackItem:
1001                         {
1002                                 TimeAxisView* parent = clicked_axisview->get_parent ();
1003                                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
1004                                 assert (atv);
1005                                 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
1006                                         /* create a MIDI region so that we have somewhere to put automation */
1007                                         _drags->set (new RegionCreateDrag (this, item, parent), event);
1008                                 } else {
1009                                         /* rubberband drag to select automation points */
1010                                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
1011                                 }
1012                                 break;
1013                         }
1014
1015                         case SelectionItem:
1016                         {
1017                                 if (smart_mode_action->get_active()) {
1018                                         /* we're in "smart" joined mode, and we've clicked on a Selection */
1019                                         double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
1020                                         pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
1021                                         if (tvp.first) {
1022                                                 /* if we're over an automation track, start a drag of its data */
1023                                                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
1024                                                 if (atv) {
1025                                                         _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
1026                                                 }
1027
1028                                                 /* if we're over a track and a region, and in the `object' part of a region,
1029                                                    put a selection around the region and drag both
1030                                                 */
1031                                                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
1032                                                 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
1033                                                         boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
1034                                                         if (t) {
1035                                                                 boost::shared_ptr<Playlist> pl = t->playlist ();
1036                                                                 if (pl) {
1037
1038                                                                         boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
1039                                                                         if (r) {
1040                                                                                 RegionView* rv = rtv->view()->find_view (r);
1041                                                                                 clicked_selection = select_range (rv->region()->position(), 
1042                                                                                                                   rv->region()->last_frame()+1);
1043                                                                                 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
1044                                                                                 list<RegionView*> rvs;
1045                                                                                 rvs.push_back (rv);
1046                                                                                 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
1047                                                                                 _drags->start_grab (event);
1048                                                                         }
1049                                                                 }
1050                                                         }
1051                                                 }
1052                                         }
1053                                 }
1054                                 break;
1055                         }
1056
1057 #ifdef WITH_CMT
1058                         case ImageFrameHandleStartItem:
1059                                 imageframe_start_handle_op(item, event) ;
1060                                 return(true) ;
1061                                 break ;
1062                         case ImageFrameHandleEndItem:
1063                                 imageframe_end_handle_op(item, event) ;
1064                                 return(true) ;
1065                                 break ;
1066                         case MarkerViewHandleStartItem:
1067                                 markerview_item_start_handle_op(item, event) ;
1068                                 return(true) ;
1069                                 break ;
1070                         case MarkerViewHandleEndItem:
1071                                 markerview_item_end_handle_op(item, event) ;
1072                                 return(true) ;
1073                                 break ;
1074                         case MarkerViewItem:
1075                                 start_markerview_grab(item, event) ;
1076                                 break ;
1077                         case ImageFrameItem:
1078                                 start_imageframe_grab(item, event) ;
1079                                 break ;
1080 #endif
1081
1082                         case MarkerBarItem:
1083
1084                                 break;
1085
1086                         default:
1087                                 break;
1088                         }
1089                 }
1090                 return true;
1091                 break;
1092
1093         case MouseGain:
1094                 switch (item_type) {
1095                 case GainLineItem:
1096                         _drags->set (new LineDrag (this, item), event);
1097                         return true;
1098
1099                 case ControlPointItem:
1100                         _drags->set (new ControlPointDrag (this, item), event);
1101                         return true;
1102                         break;
1103
1104                 case SelectionItem:
1105                 {
1106                         AudioRegionView* arv = dynamic_cast<AudioRegionView *> (clicked_regionview);
1107                         if (arv) {
1108                                 _drags->set (new AutomationRangeDrag (this, arv, selection->time), event, _cursors->up_down);
1109                                 _drags->start_grab (event);
1110                         }
1111                         return true;
1112                         break;
1113                 }
1114
1115                 case AutomationLineItem:
1116                         _drags->set (new LineDrag (this, 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 (clicked_regionview) {
1139                         /* do time-FX  */
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) && !dynamic_cast<AutomationRegionView*> (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                         leave_internal_edit_mode = true;
1304                         break;
1305
1306                 default:
1307                         break;
1308                 }
1309                 
1310                 if (leave_internal_edit_mode) {
1311                         ActionManager::do_action ("MouseMode", "toggle-internal-edit");
1312                 }
1313         }
1314
1315         button_selection (item, event, item_type);
1316
1317         if (!_drags->active () &&
1318             (Keyboard::is_delete_event (&event->button) ||
1319              Keyboard::is_context_menu_event (&event->button) ||
1320              Keyboard::is_edit_event (&event->button))) {
1321
1322                 /* handled by button release */
1323                 return true;
1324         }
1325
1326         switch (event->button.button) {
1327         case 1:
1328                 return button_press_handler_1 (item, event, item_type);
1329                 break;
1330
1331         case 2:
1332                 return button_press_handler_2 (item, event, item_type);
1333                 break;
1334
1335         case 3:
1336                 break;
1337
1338         default:
1339                 return button_press_dispatch (&event->button);
1340                 break;
1341
1342         }
1343
1344         return false;
1345 }
1346
1347 bool
1348 Editor::button_press_dispatch (GdkEventButton* ev)
1349 {
1350         /* this function is intended only for buttons 4 and above.
1351          */
1352
1353         Gtkmm2ext::MouseButton b (ev->state, ev->button);
1354         return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1355 }
1356
1357 bool
1358 Editor::button_release_dispatch (GdkEventButton* ev)
1359 {
1360         /* this function is intended only for buttons 4 and above.
1361          */
1362
1363         Gtkmm2ext::MouseButton b (ev->state, ev->button);
1364         return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1365 }
1366
1367 bool
1368 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1369 {
1370         framepos_t where = event_frame (event, 0, 0);
1371         AutomationTimeAxisView* atv = 0;
1372
1373         if (pre_press_cursor) {
1374                 set_canvas_cursor (pre_press_cursor);
1375                 pre_press_cursor = 0;
1376         }
1377
1378         /* no action if we're recording */
1379
1380         if (_session && _session->actively_recording()) {
1381                 return true;
1382         }
1383
1384         /* see if we're finishing a drag */
1385
1386         bool were_dragging = false;
1387         if (_drags->active ()) {
1388                 bool const r = _drags->end_grab (event);
1389                 if (r) {
1390                         /* grab dragged, so do nothing else */
1391                         return true;
1392                 }
1393
1394                 were_dragging = true;
1395         }
1396
1397         update_region_layering_order_editor ();
1398
1399         /* edit events get handled here */
1400
1401         if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1402                 switch (item_type) {
1403                 case RegionItem:
1404                         show_region_properties ();
1405                         break;
1406
1407                 case TempoMarkerItem:
1408                         edit_tempo_marker (item);
1409                         break;
1410
1411                 case MeterMarkerItem:
1412                         edit_meter_marker (item);
1413                         break;
1414
1415                 case RegionViewName:
1416                         if (clicked_regionview->name_active()) {
1417                                 return mouse_rename_region (item, event);
1418                         }
1419                         break;
1420
1421                 case ControlPointItem:
1422                         edit_control_point (item);
1423                         break;
1424
1425                 case NoteItem:
1426                         edit_note (item);
1427                         break;
1428
1429                 default:
1430                         break;
1431                 }
1432                 return true;
1433         }
1434
1435         /* context menu events get handled here */
1436
1437         if (Keyboard::is_context_menu_event (&event->button)) {
1438
1439                 context_click_event = *event;
1440
1441                 if (!_drags->active ()) {
1442
1443                         /* no matter which button pops up the context menu, tell the menu
1444                            widget to use button 1 to drive menu selection.
1445                         */
1446
1447                         switch (item_type) {
1448                         case FadeInItem:
1449                         case FadeInHandleItem:
1450                         case FadeOutItem:
1451                         case FadeOutHandleItem:
1452                                 popup_fade_context_menu (1, event->button.time, item, item_type);
1453                                 break;
1454
1455                         case StartCrossFadeItem:
1456                                 popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1457                                 break;
1458
1459                         case EndCrossFadeItem:
1460                                 popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1461                                 break;
1462
1463                         case StreamItem:
1464                                 popup_track_context_menu (1, event->button.time, item_type, false);
1465                                 break;
1466
1467                         case RegionItem:
1468                         case RegionViewNameHighlight:
1469                         case LeftFrameHandle:
1470                         case RightFrameHandle:
1471                         case RegionViewName:
1472                                 popup_track_context_menu (1, event->button.time, item_type, false);
1473                                 break;
1474
1475                         case SelectionItem:
1476                                 popup_track_context_menu (1, event->button.time, item_type, true);
1477                                 break;
1478                                 
1479                         case AutomationTrackItem:
1480                                 popup_track_context_menu (1, event->button.time, item_type, false);
1481                                 break;
1482
1483                         case MarkerBarItem:
1484                         case RangeMarkerBarItem:
1485                         case TransportMarkerBarItem:
1486                         case CdMarkerBarItem:
1487                         case TempoBarItem:
1488                         case MeterBarItem:
1489                                 popup_ruler_menu (where, item_type);
1490                                 break;
1491
1492                         case MarkerItem:
1493                                 marker_context_menu (&event->button, item);
1494                                 break;
1495
1496                         case TempoMarkerItem:
1497                                 tempo_or_meter_marker_context_menu (&event->button, item);
1498                                 break;
1499
1500                         case MeterMarkerItem:
1501                                 tempo_or_meter_marker_context_menu (&event->button, item);
1502                                 break;
1503
1504                         case CrossfadeViewItem:
1505                                 popup_track_context_menu (1, event->button.time, item_type, false);
1506                                 break;
1507
1508                         case ControlPointItem:
1509                                 popup_control_point_context_menu (item, event);
1510                                 break;
1511
1512 #ifdef WITH_CMT
1513                         case ImageFrameItem:
1514                                 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1515                                 break ;
1516                         case ImageFrameTimeAxisItem:
1517                                 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1518                                 break ;
1519                         case MarkerViewItem:
1520                                 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1521                                 break ;
1522                         case MarkerTimeAxisItem:
1523                                 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1524                                 break ;
1525 #endif
1526
1527                         default:
1528                                 break;
1529                         }
1530
1531                         return true;
1532                 }
1533         }
1534
1535         /* delete events get handled here */
1536
1537         Editing::MouseMode const eff = effective_mouse_mode ();
1538
1539         if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1540
1541                 switch (item_type) {
1542                 case TempoMarkerItem:
1543                         remove_tempo_marker (item);
1544                         break;
1545
1546                 case MeterMarkerItem:
1547                         remove_meter_marker (item);
1548                         break;
1549
1550                 case MarkerItem:
1551                         remove_marker (*item, event);
1552                         break;
1553
1554                 case RegionItem:
1555                         if (eff == MouseObject) {
1556                                 remove_clicked_region ();
1557                         }
1558                         break;
1559
1560                 case ControlPointItem:
1561                         remove_control_point (item);
1562                         break;
1563
1564                 case NoteItem:
1565                         remove_midi_note (item, event);
1566                         break;
1567
1568                 default:
1569                         break;
1570                 }
1571                 return true;
1572         }
1573
1574         switch (event->button.button) {
1575         case 1:
1576
1577                 switch (item_type) {
1578                 /* see comments in button_press_handler */
1579                 case PlayheadCursorItem:
1580                 case MarkerItem:
1581                 case GainLineItem:
1582                 case AutomationLineItem:
1583                 case StartSelectionTrimItem:
1584                 case EndSelectionTrimItem:
1585                         return true;
1586
1587                 case MarkerBarItem:
1588                         if (!_dragging_playhead) {
1589                                 snap_to_with_modifier (where, event, 0, true);
1590                                 mouse_add_new_marker (where);
1591                         }
1592                         return true;
1593
1594                 case CdMarkerBarItem:
1595                         if (!_dragging_playhead) {
1596                                 // if we get here then a dragged range wasn't done
1597                                 snap_to_with_modifier (where, event, 0, true);
1598                                 mouse_add_new_marker (where, true);
1599                         }
1600                         return true;
1601
1602                 case TempoBarItem:
1603                         if (!_dragging_playhead) {
1604                                 snap_to_with_modifier (where, event);
1605                                 mouse_add_new_tempo_event (where);
1606                         }
1607                         return true;
1608
1609                 case MeterBarItem:
1610                         if (!_dragging_playhead) {
1611                                 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1612                         }
1613                         return true;
1614                         break;
1615
1616                 default:
1617                         break;
1618                 }
1619
1620                 switch (eff) {
1621                 case MouseObject:
1622                         switch (item_type) {
1623                         case AutomationTrackItem:
1624                                 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1625                                 if (atv) {
1626                                         atv->add_automation_event (event, where, event->button.y);
1627                                 }
1628                                 return true;
1629                                 break;
1630                         default:
1631                                 break;
1632                         }
1633                         break;
1634
1635                 case MouseGain:
1636                         switch (item_type) {
1637                         case RegionItem:
1638                         {
1639                                 /* check that we didn't drag before releasing, since
1640                                    its really annoying to create new control
1641                                    points when doing this.
1642                                 */
1643                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1644                                 if (!were_dragging && arv) {
1645                                         arv->add_gain_point_event (item, event);
1646                                 }
1647                                 return true;
1648                                 break;
1649                         }
1650
1651                         case AutomationTrackItem:
1652                                 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1653                                         add_automation_event (event, where, event->button.y);
1654                                 return true;
1655                                 break;
1656                         default:
1657                                 break;
1658                         }
1659                         break;
1660
1661                 case MouseAudition:
1662                         set_canvas_cursor (current_canvas_cursor);
1663                         if (scrubbing_direction == 0) {
1664                                 /* no drag, just a click */
1665                                 switch (item_type) {
1666                                 case RegionItem:
1667                                         play_selected_region ();
1668                                         break;
1669                                 default:
1670                                         break;
1671                                 }
1672                         } else {
1673                                 /* make sure we stop */
1674                                 _session->request_transport_speed (0.0);
1675                         }
1676                         break;
1677
1678                 default:
1679                         break;
1680
1681                 }
1682
1683                 /* do any (de)selection operations that should occur on button release */
1684                 button_selection (item, event, item_type);
1685                 return true;
1686                 break;
1687
1688
1689         case 2:
1690                 switch (eff) {
1691
1692                 case MouseObject:
1693                         switch (item_type) {
1694                         case RegionItem:
1695                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1696                                         raise_region ();
1697                                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1698                                         lower_region ();
1699                                 } else {
1700                                         // Button2 click is unused
1701                                 }
1702                                 return true;
1703
1704                                 break;
1705
1706                         default:
1707                                 break;
1708                         }
1709                         break;
1710
1711                 case MouseDraw:
1712                         return true;
1713                         
1714                 case MouseRange:
1715                         // x_style_paste (where, 1.0);
1716                         return true;
1717                         break;
1718
1719                 default:
1720                         break;
1721                 }
1722
1723                 break;
1724
1725         case 3:
1726                 break;
1727
1728         default:
1729                 break;
1730         }
1731
1732         return false;
1733 }
1734
1735 bool
1736 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1737 {
1738         ControlPoint* cp;
1739         Marker * marker;
1740         double fraction;
1741         bool ret = true;
1742
1743         switch (item_type) {
1744         case ControlPointItem:
1745                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1746                         cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1747                         cp->set_visible (true);
1748
1749                         double at_x, at_y;
1750                         at_x = cp->get_x();
1751                         at_y = cp->get_y ();
1752                         cp->i2w (at_x, at_y);
1753                         at_x += 10.0;
1754                         at_y += 10.0;
1755
1756                         fraction = 1.0 - (cp->get_y() / cp->line().height());
1757
1758                         if (is_drawable() && !_drags->active ()) {
1759                                 set_canvas_cursor (_cursors->fader);
1760                         }
1761
1762                         _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1763                         _verbose_cursor->show ();
1764                 }
1765                 break;
1766
1767         case GainLineItem:
1768                 if (mouse_mode == MouseGain) {
1769                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1770                         if (line)
1771                                 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1772                         if (is_drawable()) {
1773                                 set_canvas_cursor (_cursors->fader);
1774                         }
1775                 }
1776                 break;
1777
1778         case AutomationLineItem:
1779                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1780                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1781                         if (line) {
1782                                 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1783                         }
1784                         if (is_drawable()) {
1785                                 set_canvas_cursor (_cursors->fader);
1786                         }
1787                 }
1788                 break;
1789
1790         case RegionViewNameHighlight:
1791                 if (is_drawable() && doing_object_stuff() && entered_regionview) {
1792                         set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1793                         _over_region_trim_target = true;
1794                 }
1795                 break;
1796
1797         case LeftFrameHandle:
1798         case RightFrameHandle:
1799                 if (is_drawable() && doing_object_stuff() && !internal_editing() && entered_regionview) {
1800                         set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1801                 }
1802                 break;
1803
1804         case StartSelectionTrimItem:
1805 #ifdef WITH_CMT
1806         case ImageFrameHandleStartItem:
1807         case MarkerViewHandleStartItem:
1808 #endif
1809                 if (is_drawable()) {
1810                         set_canvas_cursor (_cursors->left_side_trim);
1811                 }
1812                 break;
1813         case EndSelectionTrimItem:
1814 #ifdef WITH_CMT
1815         case ImageFrameHandleEndItem:
1816         case MarkerViewHandleEndItem:
1817 #endif
1818                 if (is_drawable()) {
1819                         set_canvas_cursor (_cursors->right_side_trim);
1820                 }
1821                 break;
1822
1823         case PlayheadCursorItem:
1824                 if (is_drawable()) {
1825                         switch (_edit_point) {
1826                         case EditAtMouse:
1827                                 set_canvas_cursor (_cursors->grabber_edit_point);
1828                                 break;
1829                         default:
1830                                 set_canvas_cursor (_cursors->grabber);
1831                                 break;
1832                         }
1833                 }
1834                 break;
1835
1836         case RegionViewName:
1837
1838                 /* when the name is not an active item, the entire name highlight is for trimming */
1839
1840                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1841                         if (mouse_mode == MouseObject && is_drawable()) {
1842                                 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1843                                 _over_region_trim_target = true;
1844                         }
1845                 }
1846                 break;
1847
1848
1849         case AutomationTrackItem:
1850                 if (is_drawable()) {
1851                         Gdk::Cursor *cursor;
1852                         switch (mouse_mode) {
1853                         case MouseRange:
1854                                 cursor = _cursors->selector;
1855                                 break;
1856                         case MouseZoom:
1857                                 cursor = _cursors->zoom_in;
1858                                 break;
1859                         default:
1860                                 cursor = _cursors->cross_hair;
1861                                 break;
1862                         }
1863
1864                         set_canvas_cursor (cursor);
1865
1866                         AutomationTimeAxisView* atv;
1867                         if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1868                                 clear_entered_track = false;
1869                                 set_entered_track (atv);
1870                         }
1871                 }
1872                 break;
1873
1874         case MarkerBarItem:
1875         case RangeMarkerBarItem:
1876         case TransportMarkerBarItem:
1877         case CdMarkerBarItem:
1878         case MeterBarItem:
1879         case TempoBarItem:
1880                 if (is_drawable()) {
1881                         set_canvas_cursor (_cursors->timebar);
1882                 }
1883                 break;
1884
1885         case MarkerItem:
1886                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1887                         break;
1888                 }
1889                 entered_marker = marker;
1890                 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1891                 // fall through
1892         case MeterMarkerItem:
1893         case TempoMarkerItem:
1894                 if (is_drawable()) {
1895                         set_canvas_cursor (_cursors->timebar);
1896                 }
1897                 break;
1898
1899         case FadeInHandleItem:
1900                 if (mouse_mode == MouseObject && !internal_editing()) {
1901                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1902                         if (rect) {
1903                                 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1904                         }
1905                         set_canvas_cursor (_cursors->fade_in);
1906                 }
1907                 break;
1908
1909         case FadeOutHandleItem:
1910                 if (mouse_mode == MouseObject && !internal_editing()) {
1911                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1912                         if (rect) {
1913                                 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1914                         }
1915                         set_canvas_cursor (_cursors->fade_out);
1916                 }
1917                 break;
1918         case FeatureLineItem:
1919                 {
1920                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1921                         line->property_fill_color_rgba() = 0xFF0000FF;
1922                 }
1923                 break;
1924         case SelectionItem:
1925                 if (smart_mode_action->get_active()) {
1926                         set_canvas_cursor ();
1927                 }
1928                 break;
1929
1930         default:
1931                 break;
1932         }
1933
1934         /* second pass to handle entered track status in a comprehensible way.
1935          */
1936
1937         switch (item_type) {
1938         case GainLineItem:
1939         case AutomationLineItem:
1940         case ControlPointItem:
1941                 /* these do not affect the current entered track state */
1942                 clear_entered_track = false;
1943                 break;
1944
1945         case AutomationTrackItem:
1946                 /* handled above already */
1947                 break;
1948
1949         default:
1950                 set_entered_track (0);
1951                 break;
1952         }
1953
1954         return ret;
1955 }
1956
1957 bool
1958 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1959 {
1960         AutomationLine* al;
1961         ControlPoint* cp;
1962         Marker *marker;
1963         Location *loc;
1964         RegionView* rv;
1965         bool is_start;
1966         bool ret = true;
1967
1968         switch (item_type) {
1969         case ControlPointItem:
1970                 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1971                 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1972                         if (cp->line().npoints() > 1 && !cp->get_selected()) {
1973                                 cp->set_visible (false);
1974                         }
1975                 }
1976
1977                 if (is_drawable()) {
1978                         set_canvas_cursor (current_canvas_cursor);
1979                 }
1980
1981                 _verbose_cursor->hide ();
1982                 break;
1983
1984         case RegionViewNameHighlight:
1985         case LeftFrameHandle:
1986         case RightFrameHandle:
1987         case StartSelectionTrimItem:
1988         case EndSelectionTrimItem:
1989         case PlayheadCursorItem:
1990
1991 #ifdef WITH_CMT
1992         case ImageFrameHandleStartItem:
1993         case ImageFrameHandleEndItem:
1994         case MarkerViewHandleStartItem:
1995         case MarkerViewHandleEndItem:
1996 #endif
1997
1998                 _over_region_trim_target = false;
1999
2000                 if (is_drawable()) {
2001                         set_canvas_cursor (current_canvas_cursor);
2002                 }
2003                 break;
2004
2005         case GainLineItem:
2006         case AutomationLineItem:
2007                 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
2008                 {
2009                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2010                         if (line)
2011                                 line->property_fill_color_rgba() = al->get_line_color();
2012                 }
2013                 if (is_drawable()) {
2014                         set_canvas_cursor (current_canvas_cursor);
2015                 }
2016                 break;
2017
2018         case RegionViewName:
2019                 /* see enter_handler() for notes */
2020                 _over_region_trim_target = false;
2021
2022                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
2023                         if (is_drawable() && mouse_mode == MouseObject) {
2024                                 set_canvas_cursor (current_canvas_cursor);
2025                         }
2026                 }
2027                 break;
2028
2029         case RangeMarkerBarItem:
2030         case TransportMarkerBarItem:
2031         case CdMarkerBarItem:
2032         case MeterBarItem:
2033         case TempoBarItem:
2034         case MarkerBarItem:
2035                 if (is_drawable()) {
2036                         set_canvas_cursor (current_canvas_cursor);
2037                 }
2038                 break;
2039
2040         case MarkerItem:
2041                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2042                         break;
2043                 }
2044                 entered_marker = 0;
2045                 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
2046                         location_flags_changed (loc, this);
2047                 }
2048                 // fall through
2049         case MeterMarkerItem:
2050         case TempoMarkerItem:
2051
2052                 if (is_drawable()) {
2053                         set_canvas_cursor (current_canvas_cursor);
2054                 }
2055
2056                 break;
2057
2058         case FadeInHandleItem:
2059         case FadeOutHandleItem:
2060                 rv = static_cast<RegionView*>(item->get_data ("regionview"));
2061                 {
2062                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
2063                         if (rect) {
2064                                 rect->property_fill_color_rgba() = rv->get_fill_color();
2065                                 rect->property_outline_pixels() = 0;
2066                         }
2067                 }
2068                 set_canvas_cursor (current_canvas_cursor);
2069                 break;
2070
2071         case AutomationTrackItem:
2072                 if (is_drawable()) {
2073                         set_canvas_cursor (current_canvas_cursor);
2074                         clear_entered_track = true;
2075                         Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
2076                 }
2077                 break;
2078         case FeatureLineItem:
2079                 {
2080                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
2081                         line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
2082                 }
2083                 break;
2084
2085         default:
2086                 break;
2087         }
2088
2089         return ret;
2090 }
2091
2092 gint
2093 Editor::left_automation_track ()
2094 {
2095         if (clear_entered_track) {
2096                 set_entered_track (0);
2097                 clear_entered_track = false;
2098         }
2099         return false;
2100 }
2101
2102 void
2103 Editor::scrub (framepos_t frame, double current_x)
2104 {
2105         double delta;
2106
2107         if (scrubbing_direction == 0) {
2108                 /* first move */
2109                 _session->request_locate (frame, false);
2110                 _session->request_transport_speed (0.1);
2111                 scrubbing_direction = 1;
2112
2113         } else {
2114
2115                 if (last_scrub_x > current_x) {
2116
2117                         /* pointer moved to the left */
2118
2119                         if (scrubbing_direction > 0) {
2120
2121                                 /* we reversed direction to go backwards */
2122
2123                                 scrub_reversals++;
2124                                 scrub_reverse_distance += (int) (last_scrub_x - current_x);
2125
2126                         } else {
2127
2128                                 /* still moving to the left (backwards) */
2129
2130                                 scrub_reversals = 0;
2131                                 scrub_reverse_distance = 0;
2132
2133                                 delta = 0.01 * (last_scrub_x - current_x);
2134                                 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
2135                         }
2136
2137                 } else {
2138                         /* pointer moved to the right */
2139
2140                         if (scrubbing_direction < 0) {
2141                                 /* we reversed direction to go forward */
2142
2143                                 scrub_reversals++;
2144                                 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2145
2146                         } else {
2147                                 /* still moving to the right */
2148
2149                                 scrub_reversals = 0;
2150                                 scrub_reverse_distance = 0;
2151
2152                                 delta = 0.01 * (current_x - last_scrub_x);
2153                                 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2154                         }
2155                 }
2156
2157                 /* if there have been more than 2 opposite motion moves detected, or one that moves
2158                    back more than 10 pixels, reverse direction
2159                 */
2160
2161                 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2162
2163                         if (scrubbing_direction > 0) {
2164                                 /* was forwards, go backwards */
2165                                 _session->request_transport_speed (-0.1);
2166                                 scrubbing_direction = -1;
2167                         } else {
2168                                 /* was backwards, go forwards */
2169                                 _session->request_transport_speed (0.1);
2170                                 scrubbing_direction = 1;
2171                         }
2172
2173                         scrub_reverse_distance = 0;
2174                         scrub_reversals = 0;
2175                 }
2176         }
2177
2178         last_scrub_x = current_x;
2179 }
2180
2181 bool
2182 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2183 {
2184         _last_motion_y = event->motion.y;
2185
2186         if (event->motion.is_hint) {
2187                 gint x, y;
2188
2189                 /* We call this so that MOTION_NOTIFY events continue to be
2190                    delivered to the canvas. We need to do this because we set
2191                    Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2192                    the density of the events, at the expense of a round-trip
2193                    to the server. Given that this will mostly occur on cases
2194                    where DISPLAY = :0.0, and given the cost of what the motion
2195                    event might do, its a good tradeoff.
2196                 */
2197
2198                 track_canvas->get_pointer (x, y);
2199         }
2200
2201         if (current_stepping_trackview) {
2202                 /* don't keep the persistent stepped trackview if the mouse moves */
2203                 current_stepping_trackview = 0;
2204                 step_timeout.disconnect ();
2205         }
2206
2207         if (_session && _session->actively_recording()) {
2208                 /* Sorry. no dragging stuff around while we record */
2209                 return true;
2210         }
2211
2212         JoinObjectRangeState const old = _join_object_range_state;
2213         update_join_object_range_location (event->motion.x, event->motion.y);
2214         if (_join_object_range_state != old) {
2215                 set_canvas_cursor ();
2216         }
2217
2218         if (_over_region_trim_target) {
2219                 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2220         }
2221
2222         bool handled = false;
2223         if (_drags->active ()) {
2224                 handled = _drags->motion_handler (event, from_autoscroll);
2225         }
2226
2227         if (!handled) {
2228                 return false;
2229         }
2230
2231         track_canvas_motion (event);
2232         return true;
2233 }
2234
2235 bool
2236 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2237 {
2238         ControlPoint* control_point;
2239
2240         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2241                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2242                 /*NOTREACHED*/
2243         }
2244
2245         AutomationLine& line = control_point->line ();
2246         if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2247                 /* we shouldn't remove the first or last gain point in region gain lines */
2248                 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2249                         return false;
2250                 }
2251         }
2252
2253         return true;
2254 }
2255
2256 void
2257 Editor::remove_control_point (ArdourCanvas::Item* item)
2258 {
2259         if (!can_remove_control_point (item)) {
2260                 return;
2261         }
2262
2263         ControlPoint* control_point;
2264
2265         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2266                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2267                 /*NOTREACHED*/
2268         }
2269
2270         control_point->line().remove_point (*control_point);
2271 }
2272
2273 void
2274 Editor::edit_control_point (ArdourCanvas::Item* item)
2275 {
2276         ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2277
2278         if (p == 0) {
2279                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2280                 /*NOTREACHED*/
2281         }
2282
2283         ControlPointDialog d (p);
2284         d.set_position (Gtk::WIN_POS_MOUSE);
2285         ensure_float (d);
2286
2287         if (d.run () != RESPONSE_ACCEPT) {
2288                 return;
2289         }
2290
2291         p->line().modify_point_y (*p, d.get_y_fraction ());
2292 }
2293
2294 void
2295 Editor::edit_note (ArdourCanvas::Item* item)
2296 {
2297         ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2298         assert (e);
2299
2300         EditNoteDialog d (&e->region_view(), e);
2301         d.set_position (Gtk::WIN_POS_MOUSE);
2302         ensure_float (d);
2303
2304         d.run ();
2305 }
2306
2307
2308 void
2309 Editor::visible_order_range (int* low, int* high) const
2310 {
2311         *low = TimeAxisView::max_order ();
2312         *high = 0;
2313
2314         for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2315
2316                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2317
2318                 if (!rtv->hidden()) {
2319
2320                         if (*high < rtv->order()) {
2321                                 *high = rtv->order ();
2322                         }
2323
2324                         if (*low > rtv->order()) {
2325                                 *low = rtv->order ();
2326                         }
2327                 }
2328         }
2329 }
2330
2331 void
2332 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2333 {
2334         /* Either add to or set the set the region selection, unless
2335            this is an alignment click (control used)
2336         */
2337
2338         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2339                 TimeAxisView* tv = &rv.get_time_axis_view();
2340                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2341                 double speed = 1.0;
2342                 if (rtv && rtv->is_track()) {
2343                         speed = rtv->track()->speed();
2344                 }
2345
2346                 framepos_t where = get_preferred_edit_position();
2347
2348                 if (where >= 0) {
2349
2350                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2351
2352                                 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2353
2354                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2355
2356                                 align_region (rv.region(), End, (framepos_t) (where * speed));
2357
2358                         } else {
2359
2360                                 align_region (rv.region(), Start, (framepos_t) (where * speed));
2361                         }
2362                 }
2363         }
2364 }
2365
2366 void
2367 Editor::collect_new_region_view (RegionView* rv)
2368 {
2369         latest_regionviews.push_back (rv);
2370 }
2371
2372 void
2373 Editor::collect_and_select_new_region_view (RegionView* rv)
2374 {
2375         selection->add(rv);
2376         latest_regionviews.push_back (rv);
2377 }
2378
2379 void
2380 Editor::cancel_selection ()
2381 {
2382         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2383                 (*i)->hide_selection ();
2384         }
2385
2386         selection->clear ();
2387         clicked_selection = 0;
2388 }
2389
2390
2391 void
2392 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2393 {
2394         RegionView* rv = clicked_regionview;
2395
2396         /* Choose action dependant on which button was pressed */
2397         switch (event->button.button) {
2398         case 1:
2399                 begin_reversible_command (_("start point trim"));
2400
2401                 if (selection->selected (rv)) {
2402                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2403                              i != selection->regions.by_layer().end(); ++i)
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         if (_internal_editing == yn) {
2697                 return;
2698         }
2699
2700         _internal_editing = yn;
2701         
2702         if (yn) {
2703                 pre_internal_mouse_mode = mouse_mode;
2704                 pre_internal_snap_type = _snap_type;
2705                 pre_internal_snap_mode = _snap_mode;
2706
2707                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2708                         (*i)->enter_internal_edit_mode ();
2709                 }
2710
2711                 set_snap_to (internal_snap_type);
2712                 set_snap_mode (internal_snap_mode);
2713
2714         } else {
2715
2716                 internal_snap_mode = _snap_mode;
2717                 internal_snap_type = _snap_type;
2718
2719                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2720                         (*i)->leave_internal_edit_mode ();
2721                 }
2722
2723                 if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
2724                         /* we were drawing .. flip back to something sensible */
2725                         set_mouse_mode (pre_internal_mouse_mode);
2726                 }
2727
2728                 set_snap_to (pre_internal_snap_type);
2729                 set_snap_mode (pre_internal_snap_mode);
2730         }
2731         
2732         set_canvas_cursor ();
2733 }
2734
2735 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2736  *  used by the `join object/range' tool mode.
2737  */
2738 void
2739 Editor::update_join_object_range_location (double /*x*/, double y)
2740 {
2741         /* XXX: actually, this decides based on whether the mouse is in the top
2742            or bottom half of a the waveform part RouteTimeAxisView;
2743
2744            Note that entered_{track,regionview} is not always setup (e.g. if
2745            the mouse is over a TimeSelection), and to get a Region
2746            that we're over requires searching the playlist.
2747         */
2748
2749         if (!smart_mode_action->get_active() || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2750                 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2751                 return;
2752         }
2753
2754         if (mouse_mode == MouseObject) {
2755                 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2756         } else if (mouse_mode == MouseRange) {
2757                 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2758         }
2759
2760         /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2761         pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2762
2763         if (tvp.first) {
2764
2765                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2766                 if (rtv) {
2767
2768                         double cx = 0;
2769                         double cy = y;
2770                         rtv->canvas_display()->w2i (cx, cy);
2771
2772                         double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
2773                         double d;
2774                         double const f = modf (c, &d);
2775
2776                         _join_object_range_state = f < 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2777                 }
2778         }
2779 }
2780
2781 Editing::MouseMode
2782 Editor::effective_mouse_mode () const
2783 {
2784         if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2785                 return MouseObject;
2786         } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2787                 return MouseRange;
2788         }
2789
2790         return mouse_mode;
2791 }
2792
2793 void
2794 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2795 {
2796         ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2797         assert (e);
2798
2799         e->region_view().delete_note (e->note ());
2800 }
2801
2802 void
2803 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2804 {
2805         assert (rv);
2806
2807         ArdourCanvas::Group* g = rv->get_canvas_group ();
2808         ArdourCanvas::Group* p = g->get_parent_group ();
2809
2810         /* Compute x in region view parent coordinates */
2811         double dy = 0;
2812         p->w2i (x, dy);
2813
2814         double x1, x2, y1, y2;
2815         g->get_bounds (x1, y1, x2, y2);
2816
2817         /* Halfway across the region */
2818         double const h = (x1 + x2) / 2;
2819
2820         Trimmable::CanTrim ct = rv->region()->can_trim ();
2821         if (x <= h) {
2822                 if (ct & Trimmable::FrontTrimEarlier) {
2823                         set_canvas_cursor (_cursors->left_side_trim);
2824                 } else {
2825                         set_canvas_cursor (_cursors->left_side_trim_right_only);
2826                 }
2827         } else {
2828                 if (ct & Trimmable::EndTrimLater) {
2829                         set_canvas_cursor (_cursors->right_side_trim);
2830                 } else {
2831                         set_canvas_cursor (_cursors->right_side_trim_left_only);
2832                 }
2833         }
2834 }
2835
2836 /** Obtain the pointer position in world coordinates */
2837 void
2838 Editor::get_pointer_position (double& x, double& y) const
2839 {
2840         int px, py;
2841         track_canvas->get_pointer (px, py);
2842         track_canvas->window_to_world (px, py, x, y);
2843 }