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