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