Alter snap modifier so that it turns the grid on when it's off as well as vice-versa.
[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                                 snap_to_with_modifier (where, event, 0, true);
1185                                 mouse_add_new_marker (where);
1186                         }
1187                         return true;
1188
1189                 case CdMarkerBarItem:
1190                         if (!_dragging_playhead) {
1191                                 // if we get here then a dragged range wasn't done
1192                                 snap_to_with_modifier (where, event, 0, true);
1193                                 mouse_add_new_marker (where, true);
1194                         }
1195                         return true;
1196
1197                 case TempoBarItem:
1198                         if (!_dragging_playhead) {
1199                                 snap_to_with_modifier (where, event);
1200                                 mouse_add_new_tempo_event (where);
1201                         }
1202                         return true;
1203                         
1204                 case MeterBarItem:
1205                         if (!_dragging_playhead) {
1206                                 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1207                         } 
1208                         return true;
1209                         break;
1210
1211                 default:
1212                         break;
1213                 }
1214                 
1215                 switch (mouse_mode) {
1216                 case MouseObject:
1217                         switch (item_type) {
1218                         case AutomationTrackItem:
1219                                 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1220                                 if (atv) {
1221                                         atv->add_automation_event (item, event, where, event->button.y);
1222                                 } 
1223                                 return true;
1224                                 
1225                                 break;
1226                                 
1227                         default:
1228                                 break;
1229                         }
1230                         break;
1231
1232                 case MouseGain:
1233                         // Gain only makes sense for audio regions
1234
1235                         if (!dynamic_cast<AudioRegionView*>(clicked_regionview)) {
1236                                 break;
1237                         }
1238
1239                         switch (item_type) {
1240                         case RegionItem:
1241                                 /* check that we didn't drag before releasing, since
1242                                    its really annoying to create new control
1243                                    points when doing this.
1244                                 */
1245                                 if (were_dragging) {
1246                                         dynamic_cast<AudioRegionView*>(clicked_regionview)->add_gain_point_event (item, event);
1247                                 }
1248                                 return true;
1249                                 break;
1250                                 
1251                         case AutomationTrackItem:
1252                                 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1253                                         add_automation_event (item, event, where, event->button.y);
1254                                 return true;
1255                                 break;
1256                         default:
1257                                 break;
1258                         }
1259                         break;
1260                         
1261                 case MouseAudition:
1262                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1263                         if (scrubbing_direction == 0) {
1264                                 /* no drag, just a click */
1265                                 switch (item_type) {
1266                                 case RegionItem:
1267                                         play_selected_region ();
1268                                         break;
1269                                 default:
1270                                         break;
1271                                 }
1272                         } else {
1273                                 /* make sure we stop */
1274                                 session->request_transport_speed (0.0);
1275                         }
1276                         break;
1277                         
1278                 default:
1279                         break;
1280
1281                 }
1282
1283                 return true;
1284                 break;
1285
1286
1287         case 2:
1288                 switch (mouse_mode) {
1289                         
1290                 case MouseObject:
1291                         switch (item_type) {
1292                         case RegionItem:
1293                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1294                                         raise_region ();
1295                                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1296                                         lower_region ();
1297                                 } else {
1298                                         // Button2 click is unused
1299                                 }
1300                                 return true;
1301                                 
1302                                 break;
1303                                 
1304                         default:
1305                                 break;
1306                         }
1307                         break;
1308                         
1309                 case MouseRange:
1310                         
1311                         // x_style_paste (where, 1.0);
1312                         return true;
1313                         break;
1314                         
1315                 default:
1316                         break;
1317                 }
1318
1319                 break;
1320         
1321         case 3:
1322                 break;
1323                 
1324         default:
1325                 break;
1326         }
1327         return false;
1328 }
1329
1330 bool
1331 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type)
1332 {
1333         ControlPoint* cp;
1334         Marker * marker;
1335         double fraction;
1336         
1337         if (last_item_entered != item) {
1338                 last_item_entered = item;
1339                 last_item_entered_n = 0;
1340         }
1341
1342         switch (item_type) {
1343         case ControlPointItem:
1344                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1345                         cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1346                         cp->set_visible (true);
1347
1348                         double at_x, at_y;
1349                         at_x = cp->get_x();
1350                         at_y = cp->get_y ();
1351                         cp->item()->i2w (at_x, at_y);
1352                         at_x += 10.0;
1353                         at_y += 10.0;
1354
1355                         fraction = 1.0 - (cp->get_y() / cp->line().height());
1356
1357                         if (is_drawable() && dynamic_cast<ScrubDrag*> (_drag) == 0) {
1358                                 track_canvas->get_window()->set_cursor (*fader_cursor);
1359                         }
1360
1361                         last_item_entered_n++;
1362                         set_verbose_canvas_cursor (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1363                         if (last_item_entered_n < 10) {
1364                                 show_verbose_canvas_cursor ();
1365                         }
1366                 }
1367                 break;
1368
1369         case GainLineItem:
1370                 if (mouse_mode == MouseGain) {
1371                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1372                         if (line)
1373                                 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1374                         if (is_drawable()) {
1375                                 track_canvas->get_window()->set_cursor (*fader_cursor);
1376                         }
1377                 }
1378                 break;
1379                         
1380         case AutomationLineItem:
1381                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1382                         {
1383                                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1384                                 if (line)
1385                                         line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1386                         }
1387                         if (is_drawable()) {
1388                                 track_canvas->get_window()->set_cursor (*fader_cursor);
1389                         }
1390                 }
1391                 break;
1392                 
1393         case RegionViewNameHighlight:
1394                 if (is_drawable() && mouse_mode == MouseObject) {
1395                         track_canvas->get_window()->set_cursor (*trimmer_cursor);
1396                 }
1397                 break;
1398
1399         case StartSelectionTrimItem:
1400         case EndSelectionTrimItem:
1401
1402 #ifdef WITH_CMT
1403         case ImageFrameHandleStartItem:
1404         case ImageFrameHandleEndItem:
1405         case MarkerViewHandleStartItem:
1406         case MarkerViewHandleEndItem:
1407 #endif
1408
1409                 if (is_drawable()) {
1410                         track_canvas->get_window()->set_cursor (*trimmer_cursor);
1411                 }
1412                 break;
1413
1414         case PlayheadCursorItem:
1415                 if (is_drawable()) {
1416                         switch (_edit_point) {
1417                         case EditAtMouse:
1418                                 track_canvas->get_window()->set_cursor (*grabber_edit_point_cursor);
1419                                 break;
1420                         default:
1421                                 track_canvas->get_window()->set_cursor (*grabber_cursor);
1422                                 break;
1423                         }
1424                 }
1425                 break;
1426
1427         case RegionViewName:
1428                 
1429                 /* when the name is not an active item, the entire name highlight is for trimming */
1430
1431                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1432                         if (mouse_mode == MouseObject && is_drawable()) {
1433                                 track_canvas->get_window()->set_cursor (*trimmer_cursor);
1434                         }
1435                 } 
1436                 break;
1437
1438
1439         case AutomationTrackItem:
1440                 if (is_drawable()) {
1441                         Gdk::Cursor *cursor;
1442                         switch (mouse_mode) {
1443                         case MouseRange:
1444                                 cursor = selector_cursor;
1445                                 break;
1446                         case MouseZoom:
1447                                 cursor = zoom_cursor;
1448                                 break;
1449                         default:
1450                                 cursor = cross_hair_cursor;
1451                                 break;
1452                         }
1453
1454                         track_canvas->get_window()->set_cursor (*cursor);
1455
1456                         AutomationTimeAxisView* atv;
1457                         if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1458                                 clear_entered_track = false;
1459                                 set_entered_track (atv);
1460                         }
1461                 }
1462                 break;
1463
1464         case MarkerBarItem:
1465         case RangeMarkerBarItem:
1466         case TransportMarkerBarItem:
1467         case CdMarkerBarItem:
1468         case MeterBarItem:
1469         case TempoBarItem:
1470                 if (is_drawable()) {
1471                         track_canvas->get_window()->set_cursor (*timebar_cursor);
1472                 }
1473                 break;
1474
1475         case MarkerItem:
1476                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1477                         break;
1478                 }
1479                 entered_marker = marker;
1480                 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1481                 // fall through
1482         case MeterMarkerItem:
1483         case TempoMarkerItem:
1484                 if (is_drawable()) {
1485                         track_canvas->get_window()->set_cursor (*timebar_cursor);
1486                 }
1487                 break;
1488         case FadeInHandleItem:
1489         case FadeOutHandleItem:
1490                 if (mouse_mode == MouseObject) {
1491                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1492                         if (rect) {
1493                                 rect->property_fill_color_rgba() = 0;
1494                                 rect->property_outline_pixels() = 1;
1495                         }
1496                 }
1497                 break;
1498
1499         default:
1500                 break;
1501         }
1502
1503         /* second pass to handle entered track status in a comprehensible way.
1504          */
1505
1506         switch (item_type) {
1507         case GainLineItem:
1508         case AutomationLineItem:
1509         case ControlPointItem:
1510                 /* these do not affect the current entered track state */
1511                 clear_entered_track = false;
1512                 break;
1513
1514         case AutomationTrackItem:
1515                 /* handled above already */
1516                 break;
1517
1518         default:
1519                 set_entered_track (0);
1520                 break;
1521         }
1522
1523         return false;
1524 }
1525
1526 bool
1527 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent* /*event*/, ItemType item_type)
1528 {
1529         AutomationLine* al;
1530         ControlPoint* cp;
1531         Marker *marker;
1532         Location *loc;
1533         RegionView* rv;
1534         bool is_start;
1535
1536         switch (item_type) {
1537         case ControlPointItem:
1538                 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1539                 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1540                         if (cp->line().npoints() > 1 && !cp->selected()) {
1541                                 cp->set_visible (false);
1542                         }
1543                 }
1544                 
1545                 if (is_drawable()) {
1546                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1547                 }
1548
1549                 hide_verbose_canvas_cursor ();
1550                 break;
1551                 
1552         case RegionViewNameHighlight:
1553         case StartSelectionTrimItem:
1554         case EndSelectionTrimItem:
1555         case PlayheadCursorItem:
1556
1557 #ifdef WITH_CMT
1558         case ImageFrameHandleStartItem:
1559         case ImageFrameHandleEndItem:
1560         case MarkerViewHandleStartItem:
1561         case MarkerViewHandleEndItem:
1562 #endif
1563
1564                 if (is_drawable()) {
1565                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1566                 }
1567                 break;
1568
1569         case GainLineItem:
1570         case AutomationLineItem:
1571                 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1572                 {
1573                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1574                         if (line)
1575                                 line->property_fill_color_rgba() = al->get_line_color();
1576                 }
1577                 if (is_drawable()) {
1578                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1579                 }
1580                 break;
1581
1582         case RegionViewName:
1583                 /* see enter_handler() for notes */
1584                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1585                         if (is_drawable() && mouse_mode == MouseObject) {
1586                                 track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1587                         }
1588                 }
1589                 break;
1590
1591         case RangeMarkerBarItem:
1592         case TransportMarkerBarItem:
1593         case CdMarkerBarItem:
1594         case MeterBarItem:
1595         case TempoBarItem:
1596         case MarkerBarItem:
1597                 if (is_drawable()) {
1598                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1599                 }
1600                 break;
1601                 
1602         case MarkerItem:
1603                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1604                         break;
1605                 }
1606                 entered_marker = 0;
1607                 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1608                         location_flags_changed (loc, this);
1609                 }
1610                 // fall through
1611         case MeterMarkerItem:
1612         case TempoMarkerItem:
1613                 
1614                 if (is_drawable()) {
1615                         track_canvas->get_window()->set_cursor (*timebar_cursor);
1616                 }
1617
1618                 break;
1619
1620         case FadeInHandleItem:
1621         case FadeOutHandleItem:
1622                 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1623                 {
1624                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1625                         if (rect) {
1626                                 rect->property_fill_color_rgba() = rv->get_fill_color();
1627                                 rect->property_outline_pixels() = 0;
1628                         }
1629                 }
1630                 break;
1631
1632         case AutomationTrackItem:
1633                 if (is_drawable()) {
1634                         track_canvas->get_window()->set_cursor (*current_canvas_cursor);
1635                         clear_entered_track = true;
1636                         Glib::signal_idle().connect (mem_fun(*this, &Editor::left_automation_track));
1637                 }
1638                 break;
1639                 
1640         default:
1641                 break;
1642         }
1643
1644         return false;
1645 }
1646
1647 gint
1648 Editor::left_automation_track ()
1649 {
1650         if (clear_entered_track) {
1651                 set_entered_track (0);
1652                 clear_entered_track = false;
1653         }
1654         return false;
1655 }
1656
1657 void
1658 Editor::scrub ()
1659 {
1660         double delta;
1661         
1662         if (scrubbing_direction == 0) {
1663                 /* first move */
1664                 session->request_locate (_drag->current_pointer_frame(), false);
1665                 session->request_transport_speed (0.1);
1666                 scrubbing_direction = 1;
1667                 
1668         } else {
1669                 
1670                 if (last_scrub_x > _drag->current_pointer_x()) {
1671                         
1672                         /* pointer moved to the left */
1673                         
1674                         if (scrubbing_direction > 0) {
1675                                 
1676                                 /* we reversed direction to go backwards */
1677                                 
1678                                 scrub_reversals++;
1679                                 scrub_reverse_distance += (int) (last_scrub_x - _drag->current_pointer_x());
1680                                 
1681                         } else {
1682                                 
1683                                 /* still moving to the left (backwards) */
1684                                 
1685                                 scrub_reversals = 0;
1686                                 scrub_reverse_distance = 0;
1687                                 
1688                                 delta = 0.01 * (last_scrub_x - _drag->current_pointer_x());
1689                                 session->request_transport_speed (session->transport_speed() - delta);
1690                         }
1691                         
1692                 } else {
1693                         /* pointer moved to the right */
1694                         
1695                         if (scrubbing_direction < 0) {
1696                                 /* we reversed direction to go forward */
1697                                 
1698                                 scrub_reversals++;
1699                                 scrub_reverse_distance += (int) (_drag->current_pointer_x() - last_scrub_x);
1700                                 
1701                         } else {
1702                                 /* still moving to the right */
1703                                 
1704                                 scrub_reversals = 0;
1705                                 scrub_reverse_distance = 0;
1706                                 
1707                                 delta = 0.01 * (_drag->current_pointer_x() - last_scrub_x);
1708                                 session->request_transport_speed (session->transport_speed() + delta);
1709                         }
1710                 }
1711                 
1712                 /* if there have been more than 2 opposite motion moves detected, or one that moves
1713                    back more than 10 pixels, reverse direction
1714                 */
1715                 
1716                 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1717                         
1718                         if (scrubbing_direction > 0) {
1719                                 /* was forwards, go backwards */
1720                                 session->request_transport_speed (-0.1);
1721                                 scrubbing_direction = -1;
1722                         } else {
1723                                 /* was backwards, go forwards */
1724                                 session->request_transport_speed (0.1);
1725                                 scrubbing_direction = 1;
1726                         }
1727                         
1728                         scrub_reverse_distance = 0;
1729                         scrub_reversals = 0;
1730                 }
1731         }
1732         
1733         last_scrub_x = _drag->current_pointer_x();
1734 }
1735
1736 bool
1737 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1738 {
1739         if (event->motion.is_hint) {
1740                 gint x, y;
1741                 
1742                 /* We call this so that MOTION_NOTIFY events continue to be
1743                    delivered to the canvas. We need to do this because we set
1744                    Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1745                    the density of the events, at the expense of a round-trip
1746                    to the server. Given that this will mostly occur on cases
1747                    where DISPLAY = :0.0, and given the cost of what the motion
1748                    event might do, its a good tradeoff.  
1749                 */
1750
1751                 track_canvas->get_pointer (x, y);
1752         } 
1753
1754         if (current_stepping_trackview) {
1755                 /* don't keep the persistent stepped trackview if the mouse moves */
1756                 current_stepping_trackview = 0;
1757                 step_timeout.disconnect ();
1758         }
1759
1760         if (session && session->actively_recording()) {
1761                 /* Sorry. no dragging stuff around while we record */
1762                 return true;
1763         }
1764
1765         bool handled = false;
1766         if (_drag) {
1767                 handled = _drag->motion_handler (event, from_autoscroll);
1768         }
1769
1770         if (!handled) {
1771                 return false;
1772         }
1773
1774         track_canvas_motion (event);
1775         return true;
1776 }
1777
1778 void
1779 Editor::remove_gain_control_point (ArdourCanvas::Item*item, GdkEvent* /*event*/)
1780 {
1781         ControlPoint* control_point;
1782
1783         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1784                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1785                 /*NOTREACHED*/
1786         }
1787
1788         // We shouldn't remove the first or last gain point
1789         if (control_point->line().is_last_point(*control_point) ||
1790                 control_point->line().is_first_point(*control_point)) { 
1791                 return;
1792         }
1793
1794         control_point->line().remove_point (*control_point);
1795 }
1796
1797 void
1798 Editor::remove_control_point (ArdourCanvas::Item* item, GdkEvent* /*event*/)
1799 {
1800         ControlPoint* control_point;
1801
1802         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1803                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1804                 /*NOTREACHED*/
1805         }
1806
1807         control_point->line().remove_point (*control_point);
1808 }
1809
1810 void
1811 Editor::edit_control_point (ArdourCanvas::Item* item)
1812 {
1813         ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1814
1815         if (p == 0) {
1816                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1817                 /*NOTREACHED*/
1818         }
1819
1820         ControlPointDialog d (p);
1821         d.set_position (Gtk::WIN_POS_MOUSE);
1822         ensure_float (d);
1823
1824         if (d.run () != RESPONSE_ACCEPT) {
1825                 return;
1826         }
1827
1828         p->line().modify_point_y (*p, d.get_y_fraction ());
1829 }
1830
1831
1832 void
1833 Editor::visible_order_range (int* low, int* high) const
1834 {
1835         *low = TimeAxisView::max_order ();
1836         *high = 0;
1837         
1838         for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1839
1840                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1841                 
1842                 if (!rtv->hidden()) {
1843                         
1844                         if (*high < rtv->order()) {
1845                                 *high = rtv->order ();
1846                         }
1847                         
1848                         if (*low > rtv->order()) {
1849                                 *low = rtv->order ();
1850                         }
1851                 }
1852         }
1853 }
1854
1855 void
1856 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
1857 {
1858         /* Either add to or set the set the region selection, unless
1859            this is an alignment click (control used)
1860         */
1861         
1862         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1863                 TimeAxisView* tv = &rv.get_time_axis_view();
1864                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1865                 double speed = 1.0;
1866                 if (rtv && rtv->is_track()) {
1867                         speed = rtv->get_diskstream()->speed();
1868                 }
1869
1870                 nframes64_t where = get_preferred_edit_position();
1871
1872                 if (where >= 0) {
1873
1874                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
1875                                 
1876                                 align_region (rv.region(), SyncPoint, (nframes64_t) (where * speed));
1877                                 
1878                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
1879                                 
1880                                 align_region (rv.region(), End, (nframes64_t) (where * speed));
1881                                 
1882                         } else {
1883                                 
1884                                 align_region (rv.region(), Start, (nframes64_t) (where * speed));
1885                         }
1886                 }
1887         }
1888 }
1889
1890 void
1891 Editor::show_verbose_time_cursor (nframes64_t frame, double offset, double xpos, double ypos) 
1892 {
1893         char buf[128];
1894         SMPTE::Time smpte;
1895         BBT_Time bbt;
1896         int hours, mins;
1897         nframes64_t frame_rate;
1898         float secs;
1899
1900         if (session == 0) {
1901                 return;
1902         }
1903
1904         AudioClock::Mode m;
1905
1906         if (Profile->get_sae() || Profile->get_small_screen()) {
1907                 m = ARDOUR_UI::instance()->primary_clock.mode();
1908         } else {
1909                 m = ARDOUR_UI::instance()->secondary_clock.mode();
1910         }
1911
1912         switch (m) {
1913         case AudioClock::BBT:
1914                 session->bbt_time (frame, bbt);
1915                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, bbt.bars, bbt.beats, bbt.ticks);
1916                 break;
1917                 
1918         case AudioClock::SMPTE:
1919                 session->smpte_time (frame, smpte);
1920                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
1921                 break;
1922
1923         case AudioClock::MinSec:
1924                 /* XXX this is copied from show_verbose_duration_cursor() */
1925                 frame_rate = session->frame_rate();
1926                 hours = frame / (frame_rate * 3600);
1927                 frame = frame % (frame_rate * 3600);
1928                 mins = frame / (frame_rate * 60);
1929                 frame = frame % (frame_rate * 60);
1930                 secs = (float) frame / (float) frame_rate;
1931                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
1932                 break;
1933
1934         default:
1935                 snprintf (buf, sizeof(buf), "%" PRIi64, frame);
1936                 break;
1937         }
1938
1939         if (xpos >= 0 && ypos >=0) {
1940                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
1941         }
1942         else {
1943                 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);
1944         }
1945         show_verbose_canvas_cursor ();
1946 }
1947
1948 void
1949 Editor::show_verbose_duration_cursor (nframes64_t start, nframes64_t end, double offset, double xpos, double ypos) 
1950 {
1951         char buf[128];
1952         SMPTE::Time smpte;
1953         BBT_Time sbbt;
1954         BBT_Time ebbt;
1955         int hours, mins;
1956         nframes64_t distance, frame_rate;
1957         float secs;
1958         Meter meter_at_start(session->tempo_map().meter_at(start));
1959
1960         if (session == 0) {
1961                 return;
1962         }
1963
1964         AudioClock::Mode m;
1965
1966         if (Profile->get_sae() || Profile->get_small_screen()) {
1967                 m = ARDOUR_UI::instance()->primary_clock.mode ();
1968         } else {
1969                 m = ARDOUR_UI::instance()->secondary_clock.mode ();
1970         }
1971
1972         switch (m) {
1973         case AudioClock::BBT:
1974                 session->bbt_time (start, sbbt);
1975                 session->bbt_time (end, ebbt);
1976
1977                 /* subtract */
1978                 /* XXX this computation won't work well if the
1979                 user makes a selection that spans any meter changes.
1980                 */
1981
1982                 ebbt.bars -= sbbt.bars;
1983                 if (ebbt.beats >= sbbt.beats) {
1984                         ebbt.beats -= sbbt.beats;
1985                 } else {
1986                         ebbt.bars--;
1987                         ebbt.beats =  int(meter_at_start.beats_per_bar()) + ebbt.beats - sbbt.beats;
1988                 }
1989                 if (ebbt.ticks >= sbbt.ticks) {
1990                         ebbt.ticks -= sbbt.ticks;
1991                 } else {
1992                         ebbt.beats--;
1993                         ebbt.ticks = int(Meter::ticks_per_beat) + ebbt.ticks - sbbt.ticks;
1994                 }
1995                 
1996                 snprintf (buf, sizeof (buf), "%02" PRIu32 "|%02" PRIu32 "|%02" PRIu32, ebbt.bars, ebbt.beats, ebbt.ticks);
1997                 break;
1998                 
1999         case AudioClock::SMPTE:
2000                 session->smpte_duration (end - start, smpte);
2001                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
2002                 break;
2003
2004         case AudioClock::MinSec:
2005                 /* XXX this stuff should be elsewhere.. */
2006                 distance = end - start;
2007                 frame_rate = session->frame_rate();
2008                 hours = distance / (frame_rate * 3600);
2009                 distance = distance % (frame_rate * 3600);
2010                 mins = distance / (frame_rate * 60);
2011                 distance = distance % (frame_rate * 60);
2012                 secs = (float) distance / (float) frame_rate;
2013                 snprintf (buf, sizeof (buf), "%02" PRId32 ":%02" PRId32 ":%.4f", hours, mins, secs);
2014                 break;
2015
2016         default:
2017                 snprintf (buf, sizeof(buf), "%" PRIi64, end - start);
2018                 break;
2019         }
2020
2021         if (xpos >= 0 && ypos >=0) {
2022                 set_verbose_canvas_cursor (buf, xpos + offset, ypos + offset);
2023         }
2024         else {
2025                 set_verbose_canvas_cursor (buf, _drag->current_pointer_x() + offset, _drag->current_pointer_y() + offset);
2026         }
2027
2028         show_verbose_canvas_cursor ();
2029 }
2030
2031 void
2032 Editor::collect_new_region_view (RegionView* rv)
2033 {
2034         latest_regionviews.push_back (rv);
2035 }
2036
2037 void
2038 Editor::collect_and_select_new_region_view (RegionView* rv)
2039 {
2040         selection->add(rv);
2041         latest_regionviews.push_back (rv);
2042 }
2043
2044 void
2045 Editor::cancel_selection ()
2046 {
2047         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2048                 (*i)->hide_selection ();
2049         }
2050
2051         selection->clear ();
2052         clicked_selection = 0;
2053 }       
2054
2055
2056 void
2057 Editor::single_contents_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool swap_direction, bool obey_snap)
2058 {
2059         boost::shared_ptr<Region> region (rv.region());
2060
2061         if (region->locked()) {
2062                 return;
2063         }
2064
2065         nframes64_t new_bound;
2066
2067         double speed = 1.0;
2068         TimeAxisView* tvp = clicked_axisview;
2069         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2070
2071         if (tv && tv->is_track()) {
2072                 speed = tv->get_diskstream()->speed();
2073         }
2074         
2075         if (left_direction) {
2076                 if (swap_direction) {
2077                         new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2078                 } else {
2079                         new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2080                 }
2081         } else {
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         }
2088
2089         if (obey_snap) {
2090                 snap_to (new_bound);
2091         }
2092         region->trim_start ((nframes64_t) (new_bound * speed), this);   
2093         rv.region_changed (StartChanged);
2094 }
2095
2096 void
2097 Editor::single_start_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2098 {
2099         boost::shared_ptr<Region> region (rv.region()); 
2100
2101         if (region->locked()) {
2102                 return;
2103         }
2104
2105         nframes64_t new_bound;
2106
2107         double speed = 1.0;
2108         TimeAxisView* tvp = clicked_axisview;
2109         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2110
2111         if (tv && tv->is_track()) {
2112                 speed = tv->get_diskstream()->speed();
2113         }
2114         
2115         if (left_direction) {
2116                 new_bound = (nframes64_t) (region->position()/speed) - frame_delta;
2117         } else {
2118                 new_bound = (nframes64_t) (region->position()/speed) + frame_delta;
2119         }
2120
2121         if (obey_snap) {
2122                 snap_to (new_bound, (left_direction ? 0 : 1));  
2123         }
2124         
2125         nframes64_t pre_trim_first_frame = region->first_frame();
2126
2127         region->trim_front ((nframes64_t) (new_bound * speed), this);
2128   
2129         if (no_overlap) {
2130                 //Get the next region on the left of this region and shrink/expand it.
2131                 boost::shared_ptr<Playlist> playlist (region->playlist());
2132                 boost::shared_ptr<Region> region_left = playlist->find_next_region (pre_trim_first_frame, End, 0);
2133                 
2134                 bool regions_touching = false;
2135
2136                 if (region_left != 0 && (pre_trim_first_frame == region_left->last_frame() + 1)){
2137                     regions_touching = true;
2138                 }
2139
2140                 //Only trim region on the left if the first frame has gone beyond the left region's last frame.
2141                 if (region_left != 0 && 
2142                         (region_left->last_frame() > region->first_frame() || regions_touching)) 
2143                 {
2144                         region_left->trim_end(region->first_frame(), this);
2145                 }
2146         }
2147
2148         
2149
2150         rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2151 }
2152
2153 void
2154 Editor::single_end_trim (RegionView& rv, nframes64_t frame_delta, bool left_direction, bool obey_snap, bool no_overlap)
2155 {
2156         boost::shared_ptr<Region> region (rv.region());
2157
2158         if (region->locked()) {
2159                 return;
2160         }
2161
2162         nframes64_t new_bound;
2163
2164         double speed = 1.0;
2165         TimeAxisView* tvp = clicked_axisview;
2166         RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*>(tvp);
2167
2168         if (tv && tv->is_track()) {
2169                 speed = tv->get_diskstream()->speed();
2170         }
2171
2172         if (left_direction) {
2173                 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) - frame_delta;
2174         } else {
2175                 new_bound = (nframes64_t) ((region->last_frame() + 1)/speed) + frame_delta;
2176         }
2177
2178         if (obey_snap) {
2179                 snap_to (new_bound);
2180         }
2181
2182         nframes64_t pre_trim_last_frame = region->last_frame();
2183
2184         region->trim_end ((nframes64_t) (new_bound * speed), this);
2185
2186         if (no_overlap) {
2187                 //Get the next region on the right of this region and shrink/expand it.
2188                 boost::shared_ptr<Playlist> playlist (region->playlist());
2189                 boost::shared_ptr<Region> region_right = playlist->find_next_region (pre_trim_last_frame, Start, 1);
2190
2191                 bool regions_touching = false;
2192
2193                 if (region_right != 0 && (pre_trim_last_frame == region_right->first_frame() - 1)){
2194                     regions_touching = true;
2195                 }
2196
2197                 //Only trim region on the right if the last frame has gone beyond the right region's first frame.
2198                 if (region_right != 0 &&
2199                         (region_right->first_frame() < region->last_frame() || regions_touching)) 
2200                 {
2201                         region_right->trim_front(region->last_frame() + 1, this);
2202                 }
2203                 
2204                 rv.region_changed (Change (LengthChanged|PositionChanged|StartChanged));
2205         }
2206         else {
2207                 rv.region_changed (LengthChanged);
2208         }
2209 }
2210
2211
2212 void
2213 Editor::point_trim (GdkEvent* event)
2214 {
2215         RegionView* rv = clicked_regionview;
2216
2217         nframes64_t new_bound = _drag->current_pointer_frame();
2218
2219         snap_to_with_modifier (new_bound, event);
2220
2221         /* Choose action dependant on which button was pressed */
2222         switch (event->button.button) {
2223         case 1:
2224                 begin_reversible_command (_("Start point trim"));
2225
2226                 if (selection->selected (rv)) {
2227                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2228                              i != selection->regions.by_layer().end(); ++i)
2229                         {
2230                                 if ( (*i) == NULL){
2231                                     cerr << "region view contains null region" << endl;
2232                                 }
2233
2234                                 if (!(*i)->region()->locked()) {
2235                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2236                                         XMLNode &before = pl->get_state();
2237
2238                                         (*i)->region()->trim_front (new_bound, this);
2239
2240                                         XMLNode &after = pl->get_state();
2241                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2242                                 }
2243                         }
2244
2245                 } else {
2246                         if (!rv->region()->locked()) {
2247                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2248                                 XMLNode &before = pl->get_state();
2249                                 rv->region()->trim_front (new_bound, this);
2250                                 XMLNode &after = pl->get_state();
2251                                 session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2252                         }
2253                 }
2254
2255                 commit_reversible_command();
2256         
2257                 break;
2258         case 2:
2259                 begin_reversible_command (_("End point trim"));
2260
2261                 if (selection->selected (rv)) {
2262                         
2263                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2264                         {
2265                                 if (!(*i)->region()->locked()) {
2266                                         boost::shared_ptr<Playlist> pl = (*i)->region()->playlist();
2267                                         XMLNode &before = pl->get_state();
2268                                         (*i)->region()->trim_end (new_bound, this);
2269                                         XMLNode &after = pl->get_state();
2270                                         session->add_command(new MementoCommand<Playlist>(*pl.get(), &before, &after));
2271                                 }
2272                         }
2273
2274                 } else {
2275
2276                         if (!rv->region()->locked()) {
2277                                 boost::shared_ptr<Playlist> pl = rv->region()->playlist();
2278                                 XMLNode &before = pl->get_state();
2279                                 rv->region()->trim_end (new_bound, this);
2280                                 XMLNode &after = pl->get_state();
2281                                 session->add_command (new MementoCommand<Playlist>(*pl.get(), &before, &after));
2282                         }
2283                 }
2284
2285                 commit_reversible_command();
2286         
2287                 break;
2288         default:
2289                 break;
2290         }
2291 }
2292
2293 void
2294 Editor::thaw_region_after_trim (RegionView& rv)
2295 {
2296         boost::shared_ptr<Region> region (rv.region());
2297
2298         if (region->locked()) {
2299                 return;
2300         }
2301
2302         region->thaw (_("trimmed region"));
2303
2304         AudioRegionView* arv = dynamic_cast<AudioRegionView*>(&rv);
2305         
2306         if (arv) {
2307                 arv->unhide_envelope ();
2308         }
2309 }
2310
2311 void
2312 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2313 {
2314         Marker* marker;
2315         bool is_start;
2316
2317         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2318                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2319                 /*NOTREACHED*/
2320         }
2321
2322         Location* location = find_location_from_marker (marker, is_start);      
2323         location->set_hidden (true, this);
2324 }
2325
2326
2327 void
2328 Editor::reposition_zoom_rect (nframes64_t start, nframes64_t end)
2329 {
2330         double x1 = frame_to_pixel (start);
2331         double x2 = frame_to_pixel (end);
2332         double y2 = full_canvas_height - 1.0;
2333
2334         zoom_rect->property_x1() = x1;
2335         zoom_rect->property_y1() = 1.0;
2336         zoom_rect->property_x2() = x2;
2337         zoom_rect->property_y2() = y2;
2338 }
2339
2340
2341 gint
2342 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2343 {
2344         using namespace Gtkmm2ext;
2345
2346         ArdourPrompter prompter (false);
2347
2348         prompter.set_prompt (_("Name for region:"));
2349         prompter.set_initial_text (clicked_regionview->region()->name());
2350         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2351         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2352         prompter.show_all ();
2353         switch (prompter.run ()) {
2354         case Gtk::RESPONSE_ACCEPT:
2355                 string str;
2356                 prompter.get_result(str);
2357                 if (str.length()) {
2358                         clicked_regionview->region()->set_name (str);
2359                 }
2360                 break;
2361         }
2362         return true;
2363 }
2364
2365
2366 void
2367 Editor::mouse_brush_insert_region (RegionView* rv, nframes64_t pos)
2368 {
2369         /* no brushing without a useful snap setting */
2370
2371         switch (snap_mode) {
2372         case SnapMagnetic:
2373                 return; /* can't work because it allows region to be placed anywhere */
2374         default:
2375                 break; /* OK */
2376         }
2377
2378         switch (snap_type) {
2379         case SnapToMark:
2380                 return;
2381
2382         default:
2383                 break;
2384         }
2385
2386         /* don't brush a copy over the original */
2387         
2388         if (pos == rv->region()->position()) {
2389                 return;
2390         }
2391
2392         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2393
2394         if (rtv == 0 || !rtv->is_track()) {
2395                 return;
2396         }
2397
2398         boost::shared_ptr<Playlist> playlist = rtv->playlist();
2399         double speed = rtv->get_diskstream()->speed();
2400         
2401         XMLNode &before = playlist->get_state();
2402         playlist->add_region (RegionFactory::create (rv->region()), (nframes64_t) (pos * speed));
2403         XMLNode &after = playlist->get_state();
2404         session->add_command(new MementoCommand<Playlist>(*playlist.get(), &before, &after));
2405         
2406         // playlist is frozen, so we have to update manually
2407         
2408         playlist->Modified(); /* EMIT SIGNAL */
2409 }
2410
2411 gint
2412 Editor::track_height_step_timeout ()
2413 {
2414         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2415                 current_stepping_trackview = 0;
2416                 return false;
2417         }
2418         return true;
2419 }
2420
2421 void
2422 Editor::start_region_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2423 {
2424         assert (region_view);
2425
2426         _region_motion_group->raise_to_top ();
2427         
2428         assert (_drag == 0);
2429         
2430         if (Config->get_edit_mode() == Splice) {
2431                 _drag = new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer());
2432         } else {
2433                 RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2434                 _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false);
2435         }
2436         
2437         _drag->start_grab (event);
2438
2439         begin_reversible_command (_("move region(s)"));
2440
2441         /* sync the canvas to what we think is its current state */
2442         update_canvas_now();
2443 }
2444
2445 void
2446 Editor::start_region_copy_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2447 {
2448         assert (region_view);
2449         assert (_drag == 0);
2450         
2451         _region_motion_group->raise_to_top ();
2452
2453         RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2454         _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true);
2455         _drag->start_grab(event);
2456 }
2457
2458 void
2459 Editor::start_region_brush_grab (ArdourCanvas::Item* item, GdkEvent* event, RegionView* region_view)
2460 {
2461         assert (region_view);
2462         assert (_drag == 0);
2463         
2464         if (Config->get_edit_mode() == Splice) {
2465                 return;
2466         }
2467
2468         RegionSelection s = get_equivalent_regions (selection->regions, RouteGroup::Edit);
2469         _drag = new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false);
2470         _drag->start_grab (event);
2471         
2472         begin_reversible_command (_("Drag region brush"));
2473 }
2474
2475 /** Start a grab where a time range is selected, track(s) are selected, and the
2476  *  user clicks and drags a region with a modifier in order to create a new region containing
2477  *  the section of the clicked region that lies within the time range.
2478  */
2479 void
2480 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2481 {
2482         if (clicked_regionview == 0) {
2483                 return;
2484         }
2485
2486         /* lets try to create new Region for the selection */
2487
2488         vector<boost::shared_ptr<Region> > new_regions;
2489         create_region_from_selection (new_regions);
2490
2491         if (new_regions.empty()) {
2492                 return;
2493         }
2494
2495         /* XXX fix me one day to use all new regions */
2496         
2497         boost::shared_ptr<Region> region (new_regions.front());
2498
2499         /* add it to the current stream/playlist.
2500
2501            tricky: the streamview for the track will add a new regionview. we will
2502            catch the signal it sends when it creates the regionview to
2503            set the regionview we want to then drag.
2504         */
2505         
2506         latest_regionviews.clear();
2507         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (mem_fun(*this, &Editor::collect_new_region_view));
2508         
2509         /* A selection grab currently creates two undo/redo operations, one for 
2510            creating the new region and another for moving it.
2511         */
2512
2513         begin_reversible_command (_("selection grab"));
2514
2515         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2516
2517         XMLNode *before = &(playlist->get_state());
2518         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2519         XMLNode *after = &(playlist->get_state());
2520         session->add_command(new MementoCommand<Playlist>(*playlist, before, after));
2521
2522         commit_reversible_command ();
2523         
2524         c.disconnect ();
2525         
2526         if (latest_regionviews.empty()) {
2527                 /* something went wrong */
2528                 return;
2529         }
2530
2531         /* we need to deselect all other regionviews, and select this one
2532            i'm ignoring undo stuff, because the region creation will take care of it 
2533         */
2534         selection->set (latest_regionviews);
2535         
2536         assert (_drag == 0);
2537         _drag = new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false);
2538         _drag->start_grab (event);
2539 }
2540
2541 void
2542 Editor::break_drag ()
2543 {
2544         if (_drag) {
2545                 _drag->break_drag ();
2546         }
2547 }
2548
2549 void
2550 Editor::set_internal_edit (bool yn)
2551 {
2552         _internal_editing = yn;
2553
2554         if (yn) {
2555                 mouse_select_button.set_image (*(manage (new Image (::get_icon("midi_tool_select")))));
2556                 mouse_move_button.set_image (*(manage (new Image (::get_icon("midi_tool_pencil")))));
2557         } else {
2558                 mouse_select_button.set_image (*(manage (new Image (::get_xpm("tool_range.xpm")))));
2559                 mouse_move_button.set_image (*(manage (new Image (::get_icon("tool_object")))));
2560         }
2561
2562         set_canvas_cursor ();
2563 }