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