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