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