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