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