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