reinstate (de)selection operations that should happen on mouse button release
[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         cerr << "button release hits bottom\n";
1553
1554         return false;
1555 }
1556
1557 bool
1558 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1559 {
1560         ControlPoint* cp;
1561         Marker * marker;
1562         double fraction;
1563         bool ret = true;
1564
1565         last_item_entered = item;
1566
1567         switch (item_type) {
1568         case ControlPointItem:
1569                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1570                         cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1571                         cp->set_visible (true);
1572
1573                         double at_x, at_y;
1574                         at_x = cp->get_x();
1575                         at_y = cp->get_y ();
1576                         cp->i2w (at_x, at_y);
1577                         at_x += 10.0;
1578                         at_y += 10.0;
1579
1580                         fraction = 1.0 - (cp->get_y() / cp->line().height());
1581
1582                         if (is_drawable() && !_drags->active ()) {
1583                                 set_canvas_cursor (_cursors->fader);
1584                         }
1585
1586                         set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1587                         show_verbose_canvas_cursor ();
1588                 }
1589                 break;
1590
1591         case GainLineItem:
1592                 if (mouse_mode == MouseGain) {
1593                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1594                         if (line)
1595                                 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1596                         if (is_drawable()) {
1597                                 set_canvas_cursor (_cursors->fader);
1598                         }
1599                 }
1600                 break;
1601
1602         case AutomationLineItem:
1603                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1604                         {
1605                                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1606                                 if (line)
1607                                         line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1608                         }
1609                         if (is_drawable()) {
1610                                 set_canvas_cursor (_cursors->fader);
1611                         }
1612                 }
1613                 break;
1614
1615         case RegionViewNameHighlight:
1616                 if (is_drawable() && mouse_mode == MouseObject && entered_regionview) {
1617                         set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1618                         _over_region_trim_target = true;
1619                 }
1620                 break;
1621
1622         case LeftFrameHandle:
1623         case RightFrameHandle:
1624                 if (is_drawable() && mouse_mode == MouseObject && !internal_editing() && entered_regionview) {
1625                         set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1626                 }
1627                 break;
1628
1629         case StartSelectionTrimItem:
1630         case EndSelectionTrimItem:
1631
1632 #ifdef WITH_CMT
1633         case ImageFrameHandleStartItem:
1634         case ImageFrameHandleEndItem:
1635         case MarkerViewHandleStartItem:
1636         case MarkerViewHandleEndItem:
1637 #endif
1638
1639                 if (is_drawable()) {
1640                         set_canvas_cursor (_cursors->trimmer);
1641                 }
1642                 break;
1643
1644         case PlayheadCursorItem:
1645                 if (is_drawable()) {
1646                         switch (_edit_point) {
1647                         case EditAtMouse:
1648                                 set_canvas_cursor (_cursors->grabber_edit_point);
1649                                 break;
1650                         default:
1651                                 set_canvas_cursor (_cursors->grabber);
1652                                 break;
1653                         }
1654                 }
1655                 break;
1656
1657         case RegionViewName:
1658
1659                 /* when the name is not an active item, the entire name highlight is for trimming */
1660
1661                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1662                         if (mouse_mode == MouseObject && is_drawable()) {
1663                                 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1664                                 _over_region_trim_target = true;
1665                         }
1666                 }
1667                 break;
1668
1669
1670         case AutomationTrackItem:
1671                 if (is_drawable()) {
1672                         Gdk::Cursor *cursor;
1673                         switch (mouse_mode) {
1674                         case MouseRange:
1675                                 cursor = _cursors->selector;
1676                                 break;
1677                         case MouseZoom:
1678                                 cursor = _cursors->zoom_in;
1679                                 break;
1680                         default:
1681                                 cursor = _cursors->cross_hair;
1682                                 break;
1683                         }
1684
1685                         set_canvas_cursor (cursor);
1686
1687                         AutomationTimeAxisView* atv;
1688                         if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1689                                 clear_entered_track = false;
1690                                 set_entered_track (atv);
1691                         }
1692                 }
1693                 break;
1694
1695         case MarkerBarItem:
1696         case RangeMarkerBarItem:
1697         case TransportMarkerBarItem:
1698         case CdMarkerBarItem:
1699         case MeterBarItem:
1700         case TempoBarItem:
1701                 if (is_drawable()) {
1702                         set_canvas_cursor (_cursors->timebar);
1703                 }
1704                 break;
1705
1706         case MarkerItem:
1707                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1708                         break;
1709                 }
1710                 entered_marker = marker;
1711                 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1712                 // fall through
1713         case MeterMarkerItem:
1714         case TempoMarkerItem:
1715                 if (is_drawable()) {
1716                         set_canvas_cursor (_cursors->timebar);
1717                 }
1718                 break;
1719
1720         case FadeInHandleItem:
1721                 if (mouse_mode == MouseObject && !internal_editing()) {
1722                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1723                         if (rect) {
1724                                 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1725                         }
1726                         set_canvas_cursor (_cursors->fade_in);
1727                 }
1728                 break;
1729
1730         case FadeOutHandleItem:
1731                 if (mouse_mode == MouseObject && !internal_editing()) {
1732                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1733                         if (rect) {
1734                                 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1735                         }
1736                         set_canvas_cursor (_cursors->fade_out);
1737                 }
1738                 break;
1739         case FeatureLineItem:
1740                 {
1741                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1742                         line->property_fill_color_rgba() = 0xFF0000FF;
1743                 }
1744                 break;
1745         case SelectionItem:
1746                 if (join_object_range_button.get_active()) {
1747                         set_canvas_cursor ();
1748                 }
1749                 break;
1750                 
1751         default:
1752                 break;
1753         }
1754
1755         /* second pass to handle entered track status in a comprehensible way.
1756          */
1757
1758         switch (item_type) {
1759         case GainLineItem:
1760         case AutomationLineItem:
1761         case ControlPointItem:
1762                 /* these do not affect the current entered track state */
1763                 clear_entered_track = false;
1764                 break;
1765
1766         case AutomationTrackItem:
1767                 /* handled above already */
1768                 break;
1769
1770         default:
1771                 set_entered_track (0);
1772                 break;
1773         }
1774
1775         return ret;
1776 }
1777
1778 bool
1779 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1780 {
1781         AutomationLine* al;
1782         ControlPoint* cp;
1783         Marker *marker;
1784         Location *loc;
1785         RegionView* rv;
1786         bool is_start;
1787         bool ret = true;
1788
1789         switch (item_type) {
1790         case ControlPointItem:
1791                 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1792                 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1793                         if (cp->line().npoints() > 1 && !cp->get_selected()) {
1794                                 cp->set_visible (false);
1795                         }
1796                 }
1797
1798                 if (is_drawable()) {
1799                         set_canvas_cursor (current_canvas_cursor);
1800                 }
1801
1802                 hide_verbose_canvas_cursor ();
1803                 break;
1804
1805         case RegionViewNameHighlight:
1806         case LeftFrameHandle:
1807         case RightFrameHandle:
1808         case StartSelectionTrimItem:
1809         case EndSelectionTrimItem:
1810         case PlayheadCursorItem:
1811
1812 #ifdef WITH_CMT
1813         case ImageFrameHandleStartItem:
1814         case ImageFrameHandleEndItem:
1815         case MarkerViewHandleStartItem:
1816         case MarkerViewHandleEndItem:
1817 #endif
1818
1819                 _over_region_trim_target = false;
1820                 
1821                 if (is_drawable()) {
1822                         set_canvas_cursor (current_canvas_cursor);
1823                 }
1824                 break;
1825
1826         case GainLineItem:
1827         case AutomationLineItem:
1828                 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1829                 {
1830                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1831                         if (line)
1832                                 line->property_fill_color_rgba() = al->get_line_color();
1833                 }
1834                 if (is_drawable()) {
1835                         set_canvas_cursor (current_canvas_cursor);
1836                 }
1837                 break;
1838
1839         case RegionViewName:
1840                 /* see enter_handler() for notes */
1841                 _over_region_trim_target = false;
1842                 
1843                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1844                         if (is_drawable() && mouse_mode == MouseObject) {
1845                                 set_canvas_cursor (current_canvas_cursor);
1846                         }
1847                 }
1848                 break;
1849
1850         case RangeMarkerBarItem:
1851         case TransportMarkerBarItem:
1852         case CdMarkerBarItem:
1853         case MeterBarItem:
1854         case TempoBarItem:
1855         case MarkerBarItem:
1856                 if (is_drawable()) {
1857                         set_canvas_cursor (current_canvas_cursor);
1858                 }
1859                 break;
1860
1861         case MarkerItem:
1862                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1863                         break;
1864                 }
1865                 entered_marker = 0;
1866                 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1867                         location_flags_changed (loc, this);
1868                 }
1869                 // fall through
1870         case MeterMarkerItem:
1871         case TempoMarkerItem:
1872
1873                 if (is_drawable()) {
1874                         set_canvas_cursor (_cursors->timebar);
1875                 }
1876
1877                 break;
1878
1879         case FadeInHandleItem:
1880         case FadeOutHandleItem:
1881                 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1882                 {
1883                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1884                         if (rect) {
1885                                 rect->property_fill_color_rgba() = rv->get_fill_color();
1886                                 rect->property_outline_pixels() = 0;
1887                         }
1888                 }
1889                 set_canvas_cursor (current_canvas_cursor);
1890                 break;
1891
1892         case AutomationTrackItem:
1893                 if (is_drawable()) {
1894                         set_canvas_cursor (current_canvas_cursor);
1895                         clear_entered_track = true;
1896                         Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1897                 }
1898                 break;
1899         case FeatureLineItem:
1900                 {
1901                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1902                         line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
1903                 }
1904                 break;
1905
1906         default:
1907                 break;
1908         }
1909
1910         return ret;
1911 }
1912
1913 gint
1914 Editor::left_automation_track ()
1915 {
1916         if (clear_entered_track) {
1917                 set_entered_track (0);
1918                 clear_entered_track = false;
1919         }
1920         return false;
1921 }
1922
1923 void
1924 Editor::scrub (framepos_t frame, double current_x)
1925 {
1926         double delta;
1927
1928         if (scrubbing_direction == 0) {
1929                 /* first move */
1930                 _session->request_locate (frame, false);
1931                 _session->request_transport_speed (0.1);
1932                 scrubbing_direction = 1;
1933
1934         } else {
1935
1936                 if (last_scrub_x > current_x) {
1937
1938                         /* pointer moved to the left */
1939
1940                         if (scrubbing_direction > 0) {
1941
1942                                 /* we reversed direction to go backwards */
1943
1944                                 scrub_reversals++;
1945                                 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1946
1947                         } else {
1948
1949                                 /* still moving to the left (backwards) */
1950
1951                                 scrub_reversals = 0;
1952                                 scrub_reverse_distance = 0;
1953
1954                                 delta = 0.01 * (last_scrub_x - current_x);
1955                                 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1956                         }
1957
1958                 } else {
1959                         /* pointer moved to the right */
1960
1961                         if (scrubbing_direction < 0) {
1962                                 /* we reversed direction to go forward */
1963
1964                                 scrub_reversals++;
1965                                 scrub_reverse_distance += (int) (current_x - last_scrub_x);
1966
1967                         } else {
1968                                 /* still moving to the right */
1969
1970                                 scrub_reversals = 0;
1971                                 scrub_reverse_distance = 0;
1972
1973                                 delta = 0.01 * (current_x - last_scrub_x);
1974                                 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1975                         }
1976                 }
1977
1978                 /* if there have been more than 2 opposite motion moves detected, or one that moves
1979                    back more than 10 pixels, reverse direction
1980                 */
1981
1982                 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1983
1984                         if (scrubbing_direction > 0) {
1985                                 /* was forwards, go backwards */
1986                                 _session->request_transport_speed (-0.1);
1987                                 scrubbing_direction = -1;
1988                         } else {
1989                                 /* was backwards, go forwards */
1990                                 _session->request_transport_speed (0.1);
1991                                 scrubbing_direction = 1;
1992                         }
1993
1994                         scrub_reverse_distance = 0;
1995                         scrub_reversals = 0;
1996                 }
1997         }
1998
1999         last_scrub_x = current_x;
2000 }
2001
2002 bool
2003 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2004 {
2005         _last_motion_y = event->motion.y;
2006         
2007         if (event->motion.is_hint) {
2008                 gint x, y;
2009
2010                 /* We call this so that MOTION_NOTIFY events continue to be
2011                    delivered to the canvas. We need to do this because we set
2012                    Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2013                    the density of the events, at the expense of a round-trip
2014                    to the server. Given that this will mostly occur on cases
2015                    where DISPLAY = :0.0, and given the cost of what the motion
2016                    event might do, its a good tradeoff.
2017                 */
2018
2019                 track_canvas->get_pointer (x, y);
2020         }
2021
2022         if (current_stepping_trackview) {
2023                 /* don't keep the persistent stepped trackview if the mouse moves */
2024                 current_stepping_trackview = 0;
2025                 step_timeout.disconnect ();
2026         }
2027
2028         if (_session && _session->actively_recording()) {
2029                 /* Sorry. no dragging stuff around while we record */
2030                 return true;
2031         }
2032
2033         JoinObjectRangeState const old = _join_object_range_state;
2034         update_join_object_range_location (event->motion.x, event->motion.y);
2035         if (_join_object_range_state != old) {
2036                 set_canvas_cursor ();
2037         }
2038
2039         if (_over_region_trim_target) {
2040                 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2041         }
2042
2043         bool handled = false;
2044         if (_drags->active ()) {
2045                 handled = _drags->motion_handler (event, from_autoscroll);
2046         }
2047         
2048         if (!handled) {
2049                 return false;
2050         }
2051
2052         track_canvas_motion (event);
2053         return true;
2054 }
2055
2056 void
2057 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
2058 {
2059         ControlPoint* control_point;
2060
2061         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2062                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2063                 /*NOTREACHED*/
2064         }
2065
2066         // We shouldn't remove the first or last gain point
2067         if (control_point->line().is_last_point(*control_point) ||
2068                 control_point->line().is_first_point(*control_point)) {
2069                 return;
2070         }
2071
2072         control_point->line().remove_point (*control_point);
2073 }
2074
2075 void
2076 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2077 {
2078         ControlPoint* control_point;
2079
2080         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2081                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2082                 /*NOTREACHED*/
2083         }
2084
2085         control_point->line().remove_point (*control_point);
2086 }
2087
2088 void
2089 Editor::edit_control_point (ArdourCanvas::Item* item)
2090 {
2091         ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2092
2093         if (p == 0) {
2094                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2095                 /*NOTREACHED*/
2096         }
2097
2098         ControlPointDialog d (p);
2099         d.set_position (Gtk::WIN_POS_MOUSE);
2100         ensure_float (d);
2101
2102         if (d.run () != RESPONSE_ACCEPT) {
2103                 return;
2104         }
2105
2106         p->line().modify_point_y (*p, d.get_y_fraction ());
2107 }
2108
2109 void
2110 Editor::edit_note (ArdourCanvas::Item* item)
2111 {
2112         ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2113         assert (e);
2114
2115         EditNoteDialog d (&e->region_view(), e);
2116         d.set_position (Gtk::WIN_POS_MOUSE);
2117         ensure_float (d);
2118
2119         d.run ();
2120 }
2121         
2122
2123 void
2124 Editor::visible_order_range (int* low, int* high) const
2125 {
2126         *low = TimeAxisView::max_order ();
2127         *high = 0;
2128
2129         for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2130
2131                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2132
2133                 if (!rtv->hidden()) {
2134
2135                         if (*high < rtv->order()) {
2136                                 *high = rtv->order ();
2137                         }
2138
2139                         if (*low > rtv->order()) {
2140                                 *low = rtv->order ();
2141                         }
2142                 }
2143         }
2144 }
2145
2146 void
2147 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2148 {
2149         /* Either add to or set the set the region selection, unless
2150            this is an alignment click (control used)
2151         */
2152
2153         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2154                 TimeAxisView* tv = &rv.get_time_axis_view();
2155                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2156                 double speed = 1.0;
2157                 if (rtv && rtv->is_track()) {
2158                         speed = rtv->track()->speed();
2159                 }
2160
2161                 framepos_t where = get_preferred_edit_position();
2162
2163                 if (where >= 0) {
2164
2165                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2166
2167                                 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2168
2169                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2170
2171                                 align_region (rv.region(), End, (framepos_t) (where * speed));
2172
2173                         } else {
2174
2175                                 align_region (rv.region(), Start, (framepos_t) (where * speed));
2176                         }
2177                 }
2178         }
2179 }
2180
2181 void
2182 Editor::show_verbose_time_cursor (framepos_t frame, double offset, double xpos, double ypos)
2183 {
2184         char buf[128];
2185         Timecode::Time timecode;
2186         Timecode::BBT_Time bbt;
2187         int hours, mins;
2188         framepos_t frame_rate;
2189         float secs;
2190
2191         if (_session == 0) {
2192                 return;
2193         }
2194
2195         AudioClock::Mode m;
2196
2197         if (Profile->get_sae() || Profile->get_small_screen()) {
2198                 m = ARDOUR_UI::instance()->primary_clock.mode();
2199         } else {
2200                 m = ARDOUR_UI::instance()->secondary_clock.mode();
2201         }
2202
2203         switch (m) {
2204         case AudioClock::BBT:
2205                 _session->bbt_time (frame, bbt);
2206                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
2207                 break;
2208
2209         case AudioClock::Timecode:
2210                 _session->timecode_time (frame, timecode);
2211                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2212                 break;
2213
2214         case AudioClock::MinSec:
2215                 /* XXX this is copied from show_verbose_duration_cursor() */
2216                 frame_rate = _session->frame_rate();
2217                 hours = frame / (frame_rate * 3600);
2218                 frame = frame % (frame_rate * 3600);
2219                 mins = frame / (frame_rate * 60);
2220                 frame = frame % (frame_rate * 60);
2221                 secs = (float) frame / (float) frame_rate;
2222                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2223                 break;
2224
2225         default:
2226                 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
2227                 break;
2228         }
2229
2230         if (xpos >= 0 && ypos >=0) {
2231                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2232         } else {
2233                 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset - horizontal_position(), _drags->current_pointer_y() + offset - vertical_adjustment.get_value() + canvas_timebars_vsize);
2234         }
2235         show_verbose_canvas_cursor ();
2236 }
2237
2238 void
2239 Editor::show_verbose_duration_cursor (framepos_t start, framepos_t end, double offset, double xpos, double ypos)
2240 {
2241         char buf[128];
2242         Timecode::Time timecode;
2243         Timecode::BBT_Time sbbt;
2244         Timecode::BBT_Time ebbt;
2245         int hours, mins;
2246         framepos_t distance, frame_rate;
2247         float secs;
2248         Meter meter_at_start(_session->tempo_map().meter_at(start));
2249
2250         if (_session == 0) {
2251                 return;
2252         }
2253
2254         AudioClock::Mode m;
2255
2256         if (Profile->get_sae() || Profile->get_small_screen()) {
2257                 m = ARDOUR_UI::instance()->primary_clock.mode ();
2258         } else {
2259                 m = ARDOUR_UI::instance()->secondary_clock.mode ();
2260         }
2261
2262         switch (m) {
2263         case AudioClock::BBT:
2264                 _session->bbt_time (start, sbbt);
2265                 _session->bbt_time (end, ebbt);
2266
2267                 /* subtract */
2268                 /* XXX this computation won't work well if the
2269                 user makes a selection that spans any meter changes.
2270                 */
2271
2272                 ebbt.bars -= sbbt.bars;
2273                 if (ebbt.beats >= sbbt.beats) {
2274                         ebbt.beats -= sbbt.beats;
2275                 } else {
2276                         ebbt.bars--;
2277                         ebbt.beats =  int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
2278                 }
2279                 if (ebbt.ticks >= sbbt.ticks) {
2280                         ebbt.ticks -= sbbt.ticks;
2281                 } else {
2282                         ebbt.beats--;
2283                         ebbt.ticks = int(Timecode::BBT_Time::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
2284                 }
2285
2286                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
2287                 break;
2288
2289         case AudioClock::Timecode:
2290                 _session->timecode_duration (end - start, timecode);
2291                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
2292                 break;
2293
2294         case AudioClock::MinSec:
2295                 /* XXX this stuff should be elsewhere.. */
2296                 distance = end - start;
2297                 frame_rate = _session->frame_rate();
2298                 hours = distance / (frame_rate * 3600);
2299                 distance = distance % (frame_rate * 3600);
2300                 mins = distance / (frame_rate * 60);
2301                 distance = distance % (frame_rate * 60);
2302                 secs = (float) distance / (float) frame_rate;
2303                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%07.4f", hours, mins, secs);
2304                 break;
2305
2306         default:
2307                 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2308                 break;
2309         }
2310
2311         if (xpos >= 0 && ypos >=0) {
2312                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2313         }
2314         else {
2315                 set_verbose_canvas_cursor (buf, _drags->current_pointer_x() + offset, _drags->current_pointer_y() + offset);
2316         }
2317
2318         show_verbose_canvas_cursor ();
2319 }
2320
2321 void
2322 Editor::collect_new_region_view (RegionView* rv)
2323 {
2324         latest_regionviews.push_back (rv);
2325 }
2326
2327 void
2328 Editor::collect_and_select_new_region_view (RegionView* rv)
2329 {
2330         selection->add(rv);
2331         latest_regionviews.push_back (rv);
2332 }
2333
2334 void
2335 Editor::cancel_selection ()
2336 {
2337         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2338                 (*i)->hide_selection ();
2339         }
2340
2341         selection->clear ();
2342         clicked_selection = 0;
2343 }
2344
2345
2346 void
2347 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2348 {
2349         RegionView* rv = clicked_regionview;
2350
2351         /* Choose action dependant on which button was pressed */
2352         switch (event->button.button) {
2353         case 1:
2354                 begin_reversible_command (_("start point trim"));
2355
2356                 if (selection->selected (rv)) {
2357                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2358                              i != selection->regions.by_layer().end(); ++i)
2359                         {
2360                                 if ( (*i) == NULL){
2361                                     cerr << "region view contains null region" << endl;
2362                                 }
2363
2364                                 if (!(*i)->region()->locked()) {
2365                                         (*i)->region()->clear_changes ();
2366                                         (*i)->region()->trim_front (new_bound, this);
2367                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2368                                 }
2369                         }
2370
2371                 } else {
2372                         if (!rv->region()->locked()) {
2373                                 rv->region()->clear_changes ();
2374                                 rv->region()->trim_front (new_bound, this);
2375                                 _session->add_command(new StatefulDiffCommand (rv->region()));
2376                         }
2377                 }
2378
2379                 commit_reversible_command();
2380
2381                 break;
2382         case 2:
2383                 begin_reversible_command (_("End point trim"));
2384
2385                 if (selection->selected (rv)) {
2386
2387                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2388                         {
2389                                 if (!(*i)->region()->locked()) {
2390                                         (*i)->region()->clear_changes();
2391                                         (*i)->region()->trim_end (new_bound, this);
2392                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2393                                 }
2394                         }
2395
2396                 } else {
2397
2398                         if (!rv->region()->locked()) {
2399                                 rv->region()->clear_changes ();
2400                                 rv->region()->trim_end (new_bound, this);
2401                                 _session->add_command (new StatefulDiffCommand (rv->region()));
2402                         }
2403                 }
2404
2405                 commit_reversible_command();
2406
2407                 break;
2408         default:
2409                 break;
2410         }
2411 }
2412
2413 void
2414 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2415 {
2416         Marker* marker;
2417         bool is_start;
2418
2419         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2420                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2421                 /*NOTREACHED*/
2422         }
2423
2424         Location* location = find_location_from_marker (marker, is_start);
2425         location->set_hidden (true, this);
2426 }
2427
2428
2429 void
2430 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2431 {
2432         double x1 = frame_to_pixel (start);
2433         double x2 = frame_to_pixel (end);
2434         double y2 = full_canvas_height - 1.0;
2435
2436         zoom_rect->property_x1() = x1;
2437         zoom_rect->property_y1() = 1.0;
2438         zoom_rect->property_x2() = x2;
2439         zoom_rect->property_y2() = y2;
2440 }
2441
2442
2443 gint
2444 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2445 {
2446         using namespace Gtkmm2ext;
2447
2448         ArdourPrompter prompter (false);
2449
2450         prompter.set_prompt (_("Name for region:"));
2451         prompter.set_initial_text (clicked_regionview->region()->name());
2452         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2453         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2454         prompter.show_all ();
2455         switch (prompter.run ()) {
2456         case Gtk::RESPONSE_ACCEPT:
2457                 string str;
2458                 prompter.get_result(str);
2459                 if (str.length()) {
2460                         clicked_regionview->region()->set_name (str);
2461                 }
2462                 break;
2463         }
2464         return true;
2465 }
2466
2467
2468 void
2469 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2470 {
2471         /* no brushing without a useful snap setting */
2472
2473         switch (_snap_mode) {
2474         case SnapMagnetic:
2475                 return; /* can't work because it allows region to be placed anywhere */
2476         default:
2477                 break; /* OK */
2478         }
2479
2480         switch (_snap_type) {
2481         case SnapToMark:
2482                 return;
2483
2484         default:
2485                 break;
2486         }
2487
2488         /* don't brush a copy over the original */
2489
2490         if (pos == rv->region()->position()) {
2491                 return;
2492         }
2493
2494         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2495
2496         if (rtv == 0 || !rtv->is_track()) {
2497                 return;
2498         }
2499
2500         boost::shared_ptr<Playlist> playlist = rtv->playlist();
2501         double speed = rtv->track()->speed();
2502
2503         playlist->clear_changes ();
2504         boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2505         playlist->add_region (new_region, (framepos_t) (pos * speed));
2506         _session->add_command (new StatefulDiffCommand (playlist));
2507
2508         // playlist is frozen, so we have to update manually XXX this is disgusting
2509
2510         playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2511 }
2512
2513 gint
2514 Editor::track_height_step_timeout ()
2515 {
2516         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2517                 current_stepping_trackview = 0;
2518                 return false;
2519         }
2520         return true;
2521 }
2522
2523 void
2524 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2525 {
2526         assert (region_view);
2527
2528         if (!region_view->region()->playlist()) {
2529                 return;
2530         }
2531
2532         _region_motion_group->raise_to_top ();
2533
2534         if (Config->get_edit_mode() == Splice) {
2535                 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2536         } else {
2537                 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2538                 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2539         }
2540
2541         /* sync the canvas to what we think is its current state */
2542         update_canvas_now();
2543 }
2544
2545 void
2546 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2547 {
2548         assert (region_view);
2549
2550         if (!region_view->region()->playlist()) {
2551                 return;
2552         }
2553
2554         _region_motion_group->raise_to_top ();
2555
2556         RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2557         _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2558 }
2559
2560 void
2561 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2562 {
2563         assert (region_view);
2564
2565         if (!region_view->region()->playlist()) {
2566                 return;
2567         }
2568         
2569         if (Config->get_edit_mode() == Splice) {
2570                 return;
2571         }
2572
2573         RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2574         _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2575
2576         begin_reversible_command (Operations::drag_region_brush);
2577 }
2578
2579 /** Start a grab where a time range is selected, track(s) are selected, and the
2580  *  user clicks and drags a region with a modifier in order to create a new region containing
2581  *  the section of the clicked region that lies within the time range.
2582  */
2583 void
2584 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2585 {
2586         if (clicked_regionview == 0) {
2587                 return;
2588         }
2589
2590         /* lets try to create new Region for the selection */
2591
2592         vector<boost::shared_ptr<Region> > new_regions;
2593         create_region_from_selection (new_regions);
2594
2595         if (new_regions.empty()) {
2596                 return;
2597         }
2598
2599         /* XXX fix me one day to use all new regions */
2600
2601         boost::shared_ptr<Region> region (new_regions.front());
2602
2603         /* add it to the current stream/playlist.
2604
2605            tricky: the streamview for the track will add a new regionview. we will
2606            catch the signal it sends when it creates the regionview to
2607            set the regionview we want to then drag.
2608         */
2609
2610         latest_regionviews.clear();
2611         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2612
2613         /* A selection grab currently creates two undo/redo operations, one for
2614            creating the new region and another for moving it.
2615         */
2616
2617         begin_reversible_command (Operations::selection_grab);
2618
2619         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2620
2621         playlist->clear_changes ();
2622         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2623         _session->add_command(new StatefulDiffCommand (playlist));
2624
2625         commit_reversible_command ();
2626
2627         c.disconnect ();
2628
2629         if (latest_regionviews.empty()) {
2630                 /* something went wrong */
2631                 return;
2632         }
2633
2634         /* we need to deselect all other regionviews, and select this one
2635            i'm ignoring undo stuff, because the region creation will take care of it
2636         */
2637         selection->set (latest_regionviews);
2638
2639         _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2640 }
2641
2642 void
2643 Editor::escape ()
2644 {
2645         if (_drags->active ()) {
2646                 _drags->abort ();
2647         } else {
2648                 selection->clear ();
2649         }
2650 }
2651
2652 void
2653 Editor::set_internal_edit (bool yn)
2654 {
2655         _internal_editing = yn;
2656
2657         if (yn) {
2658                 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2659                 mouse_select_button.get_image ()->show ();
2660                 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
2661                 mouse_mode_toggled (mouse_mode);
2662
2663                 pre_internal_mouse_mode = mouse_mode;
2664
2665                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2666                         (*i)->enter_internal_edit_mode ();
2667                 }
2668
2669         } else {
2670
2671                 mouse_select_button.set_image (*(manage (new Image (::get_icon("tool_range")))));
2672                 mouse_select_button.get_image ()->show ();
2673                 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Select/Move Ranges"));
2674                 mouse_mode_toggled (mouse_mode); // sets cursor
2675
2676                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2677                         (*i)->leave_internal_edit_mode ();
2678                 }
2679
2680                 if (mouse_mode == MouseRange && pre_internal_mouse_mode != MouseRange) {
2681                         /* we were drawing .. flip back to something sensible */
2682                         set_mouse_mode (pre_internal_mouse_mode);
2683                 }
2684         }
2685 }
2686
2687 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2688  *  used by the `join object/range' tool mode.
2689  */
2690 void
2691 Editor::update_join_object_range_location (double x, double y)
2692 {
2693         /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2694            entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2695            that we're over requires searching the playlist.
2696         */
2697            
2698         if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2699                 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2700                 return;
2701         }
2702         
2703         if (mouse_mode == MouseObject) {
2704                 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2705         } else if (mouse_mode == MouseRange) {
2706                 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2707         }
2708
2709         /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2710         pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2711         
2712         if (tvp.first) {
2713
2714                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2715                 if (rtv) {
2716
2717                         double cx = 0;
2718                         double cy = y;
2719                         rtv->canvas_display()->w2i (cx, cy);
2720
2721                         double const c = cy / rtv->view()->child_height();
2722                         double d;
2723                         double const f = modf (c, &d);
2724
2725                         _join_object_range_state = f < 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2726                 }
2727         }
2728 }
2729
2730 Editing::MouseMode
2731 Editor::effective_mouse_mode () const
2732 {
2733         if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2734                 return MouseObject;
2735         } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2736                 return MouseRange;
2737         }
2738
2739         return mouse_mode;
2740 }
2741
2742 void
2743 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2744 {
2745         ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2746         assert (e);
2747
2748         e->region_view().delete_note (e->note ());
2749 }
2750
2751 void
2752 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2753 {
2754         ArdourCanvas::Group* g = rv->get_canvas_group ();
2755         ArdourCanvas::Group* p = g->get_parent_group ();
2756
2757         /* Compute x in region view parent coordinates */
2758         double dy = 0;
2759         p->w2i (x, dy);
2760
2761         double x1, x2, y1, y2;
2762         g->get_bounds (x1, y1, x2, y2);
2763
2764         /* Halfway across the region */
2765         double const h = (x1 + x2) / 2;
2766
2767         Trimmable::CanTrim ct = rv->region()->can_trim ();
2768         if (x <= h) {
2769                 if (ct & Trimmable::FrontTrimEarlier) {
2770                         set_canvas_cursor (_cursors->left_side_trim);
2771                 } else {
2772                         set_canvas_cursor (_cursors->left_side_trim_right_only);
2773                 }
2774         } else {
2775                 if (ct & Trimmable::EndTrimLater) {
2776                         set_canvas_cursor (_cursors->right_side_trim);
2777                 } else {
2778                         set_canvas_cursor (_cursors->right_side_trim_left_only);
2779                 }
2780         }
2781 }