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