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