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