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