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