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