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