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