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