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