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