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