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