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