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