when a context click event occurs in the editor, store the event so we can use it...
[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 "pbd/memento_command.h"
31 #include "pbd/basename.h"
32 #include "pbd/stateful_diff_command.h"
33
34 #include "gtkmm2ext/bindings.h"
35 #include "gtkmm2ext/utils.h"
36 #include "gtkmm2ext/tearoff.h"
37
38 #include "ardour_ui.h"
39 #include "actions.h"
40 #include "canvas-note.h"
41 #include "editor.h"
42 #include "time_axis_view.h"
43 #include "audio_time_axis.h"
44 #include "audio_region_view.h"
45 #include "midi_region_view.h"
46 #include "marker.h"
47 #include "streamview.h"
48 #include "region_gain_line.h"
49 #include "automation_time_axis.h"
50 #include "control_point.h"
51 #include "prompter.h"
52 #include "utils.h"
53 #include "selection.h"
54 #include "keyboard.h"
55 #include "editing.h"
56 #include "rgb_macros.h"
57 #include "control_point_dialog.h"
58 #include "editor_drag.h"
59 #include "automation_region_view.h"
60 #include "edit_note_dialog.h"
61 #include "mouse_cursors.h"
62 #include "editor_cursors.h"
63 #include "verbose_cursor.h"
64
65 #include "ardour/types.h"
66 #include "ardour/profile.h"
67 #include "ardour/route.h"
68 #include "ardour/audio_track.h"
69 #include "ardour/playlist.h"
70 #include "ardour/audioplaylist.h"
71 #include "ardour/audioregion.h"
72 #include "ardour/midi_region.h"
73 #include "ardour/dB.h"
74 #include "ardour/utils.h"
75 #include "ardour/region_factory.h"
76 #include "ardour/source_factory.h"
77 #include "ardour/session.h"
78 #include "ardour/operations.h"
79
80 #include <bitset>
81
82 #include "i18n.h"
83
84 using namespace std;
85 using namespace ARDOUR;
86 using namespace PBD;
87 using namespace Gtk;
88 using namespace Editing;
89 using Gtkmm2ext::Keyboard;
90
91 bool
92 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
93 {
94         int x, y;
95         double wx, wy;
96         Gdk::ModifierType mask;
97         Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
98         Glib::RefPtr<const Gdk::Window> pointer_window;
99
100         if (!canvas_window) {
101                 return false;
102         }
103
104         pointer_window = canvas_window->get_pointer (x, y, mask);
105
106         if (pointer_window == track_canvas->get_bin_window()) {
107                 wx = x;
108                 wy = y;
109                 in_track_canvas = true;
110
111         } else {
112                 in_track_canvas = false;
113                         return false;
114         }
115
116         GdkEvent event;
117         event.type = GDK_BUTTON_RELEASE;
118         event.button.x = wx;
119         event.button.y = wy;
120
121         where = event_frame (&event, 0, 0);
122         return true;
123 }
124
125 framepos_t
126 Editor::event_frame (GdkEvent const * event, double* pcx, double* pcy) const
127 {
128         double cx, cy;
129
130         if (pcx == 0) {
131                 pcx = &cx;
132         }
133         if (pcy == 0) {
134                 pcy = &cy;
135         }
136
137         *pcx = 0;
138         *pcy = 0;
139
140         switch (event->type) {
141         case GDK_BUTTON_RELEASE:
142         case GDK_BUTTON_PRESS:
143         case GDK_2BUTTON_PRESS:
144         case GDK_3BUTTON_PRESS:
145                 *pcx = event->button.x;
146                 *pcy = event->button.y;
147                 _trackview_group->w2i(*pcx, *pcy);
148                 break;
149         case GDK_MOTION_NOTIFY:
150                 *pcx = event->motion.x;
151                 *pcy = event->motion.y;
152                 _trackview_group->w2i(*pcx, *pcy);
153                 break;
154         case GDK_ENTER_NOTIFY:
155         case GDK_LEAVE_NOTIFY:
156                 track_canvas->w2c(event->crossing.x, event->crossing.y, *pcx, *pcy);
157                 break;
158         case GDK_KEY_PRESS:
159         case GDK_KEY_RELEASE:
160                 // track_canvas->w2c(event->key.x, event->key.y, *pcx, *pcy);
161                 break;
162         default:
163                 warning << string_compose (_("Editor::event_frame() used on unhandled event type %1"), event->type) << endmsg;
164                 break;
165         }
166
167         /* note that pixel_to_frame() never returns less than zero, so even if the pixel
168            position is negative (as can be the case with motion events in particular),
169            the frame location is always positive.
170         */
171
172         return pixel_to_frame (*pcx);
173 }
174
175 Gdk::Cursor*
176 Editor::which_grabber_cursor ()
177 {
178         Gdk::Cursor* c = _cursors->grabber;
179
180         if (_internal_editing) {
181                 switch (mouse_mode) {
182                 case MouseRange:
183                         c = _cursors->midi_pencil;
184                         break;
185
186                 case MouseObject:
187                         c = _cursors->grabber_note;
188                         break;
189
190                 case MouseTimeFX:
191                         c = _cursors->midi_resize;
192                         break;
193
194                 default:
195                         break;
196                 }
197
198         } else {
199
200                 switch (_edit_point) {
201                 case EditAtMouse:
202                         c = _cursors->grabber_edit_point;
203                         break;
204                 default:
205                         boost::shared_ptr<Movable> m = _movable.lock();
206                         if (m && m->locked()) {
207                                 c = _cursors->speaker;
208                         }
209                         break;
210                 }
211         }
212
213         return c;
214 }
215
216 void
217 Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
218 {
219         boost::shared_ptr<Trimmable> st = _trimmable.lock();
220
221         if (!st || st == t) {
222                 _trimmable = t;
223                 set_canvas_cursor ();
224         }
225 }
226
227 void
228 Editor::set_current_movable (boost::shared_ptr<Movable> m)
229 {
230         boost::shared_ptr<Movable> sm = _movable.lock();
231
232         if (!sm || sm != m) {
233                 _movable = m;
234                 set_canvas_cursor ();
235         }
236 }
237
238 void
239 Editor::set_canvas_cursor ()
240 {
241         if (_internal_editing) {
242
243                 switch (mouse_mode) {
244                 case MouseRange:
245                         current_canvas_cursor = _cursors->midi_pencil;
246                         break;
247
248                 case MouseObject:
249                         current_canvas_cursor = which_grabber_cursor();
250                         break;
251
252                 case MouseTimeFX:
253                         current_canvas_cursor = _cursors->midi_resize;
254                         break;
255
256                 default:
257                         return;
258                 }
259
260         } else {
261
262                 switch (mouse_mode) {
263                 case MouseRange:
264                         current_canvas_cursor = _cursors->selector;
265                         break;
266
267                 case MouseObject:
268                         current_canvas_cursor = which_grabber_cursor();
269                         break;
270
271                 case MouseGain:
272                         current_canvas_cursor = _cursors->cross_hair;
273                         break;
274
275                 case MouseZoom:
276                         if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
277                                 current_canvas_cursor = _cursors->zoom_out;
278                         } else {
279                                 current_canvas_cursor = _cursors->zoom_in;
280                         }
281                         break;
282
283                 case MouseTimeFX:
284                         current_canvas_cursor = _cursors->time_fx; // just use playhead
285                         break;
286
287                 case MouseAudition:
288                         current_canvas_cursor = _cursors->speaker;
289                         break;
290                 }
291         }
292
293         switch (_join_object_range_state) {
294         case JOIN_OBJECT_RANGE_NONE:
295                 break;
296         case JOIN_OBJECT_RANGE_OBJECT:
297                 current_canvas_cursor = which_grabber_cursor ();
298                 break;
299         case JOIN_OBJECT_RANGE_RANGE:
300                 current_canvas_cursor = _cursors->selector;
301                 break;
302         }
303
304         /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
305         if (join_object_range_button.get_active()) {
306                 double x, y;
307                 get_pointer_position (x, y);
308                 ArdourCanvas::Item* i = track_canvas->get_item_at (x, y);
309                 if (i && i->property_parent() && (*i->property_parent()).get_data (X_("timeselection"))) {
310                         pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y + vertical_adjustment.get_value() - canvas_timebars_vsize);
311                         if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
312                                 current_canvas_cursor = _cursors->up_down;
313                         }
314                 }
315         }
316
317         set_canvas_cursor (current_canvas_cursor, true);
318 }
319
320 void
321 Editor::set_mouse_mode (MouseMode m, bool force)
322 {
323         if (_drags->active ()) {
324                 return;
325         }
326
327         if (!force && m == mouse_mode) {
328                 return;
329         }
330
331         Glib::RefPtr<Action> act;
332
333         switch (m) {
334         case MouseRange:
335                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
336                 break;
337
338         case MouseObject:
339                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
340                 break;
341
342         case MouseGain:
343                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-gain"));
344                 break;
345
346         case MouseZoom:
347                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-zoom"));
348                 break;
349
350         case MouseTimeFX:
351                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
352                 break;
353
354         case MouseAudition:
355                 act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
356                 break;
357         }
358
359         assert (act);
360
361         Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
362         assert (tact);
363
364         /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
365         tact->set_active (false);
366         tact->set_active (true);
367
368         MouseModeChanged (); /* EMIT SIGNAL */
369 }
370
371 void
372 Editor::mouse_mode_toggled (MouseMode m)
373 {
374         mouse_mode = m;
375
376         instant_save ();
377
378         if (!internal_editing()) {
379                 if (mouse_mode != MouseRange && _join_object_range_state == JOIN_OBJECT_RANGE_NONE) {
380
381                         /* in all modes except range and joined object/range, hide the range selection,
382                            show the object (region) selection.
383                         */
384
385                         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
386                                 (*i)->hide_selection ();
387                         }
388
389                 } else {
390
391                         /*
392                           in range or object/range mode, show the range selection.
393                         */
394
395                         for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
396                                 (*i)->show_selection (selection->time);
397                         }
398                 }
399         }
400
401         set_canvas_cursor ();
402
403         MouseModeChanged (); /* EMIT SIGNAL */
404 }
405
406 void
407 Editor::step_mouse_mode (bool next)
408 {
409         switch (current_mouse_mode()) {
410         case MouseObject:
411                 if (next) {
412                         if (Profile->get_sae()) {
413                                 set_mouse_mode (MouseZoom);
414                         } else {
415                                 set_mouse_mode (MouseRange);
416                         }
417                 } else {
418                         set_mouse_mode (MouseTimeFX);
419                 }
420                 break;
421
422         case MouseRange:
423                 if (next) set_mouse_mode (MouseZoom);
424                 else set_mouse_mode (MouseObject);
425                 break;
426
427         case MouseZoom:
428                 if (next) {
429                         if (Profile->get_sae()) {
430                                 set_mouse_mode (MouseTimeFX);
431                         } else {
432                                 set_mouse_mode (MouseGain);
433                         }
434                 } else {
435                         if (Profile->get_sae()) {
436                                 set_mouse_mode (MouseObject);
437                         } else {
438                                 set_mouse_mode (MouseRange);
439                         }
440                 }
441                 break;
442
443         case MouseGain:
444                 if (next) set_mouse_mode (MouseTimeFX);
445                 else set_mouse_mode (MouseZoom);
446                 break;
447
448         case MouseTimeFX:
449                 if (next) {
450                         set_mouse_mode (MouseAudition);
451                 } else {
452                         if (Profile->get_sae()) {
453                                 set_mouse_mode (MouseZoom);
454                         } else {
455                                 set_mouse_mode (MouseGain);
456                         }
457                 }
458                 break;
459
460         case MouseAudition:
461                 if (next) set_mouse_mode (MouseObject);
462                 else set_mouse_mode (MouseTimeFX);
463                 break;
464         }
465 }
466
467 void
468 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
469 {
470         /* in object/audition/timefx/gain-automation mode,
471            any button press sets the selection if the object
472            can be selected. this is a bit of hack, because
473            we want to avoid this if the mouse operation is a
474            region alignment.
475
476            note: not dbl-click or triple-click
477
478            Also note that there is no region selection in internal edit mode, otherwise
479            for operations operating on the selection (e.g. cut) it is not obvious whether
480            to cut notes or regions.
481         */
482
483         if (((mouse_mode != MouseObject) &&
484              (_join_object_range_state != JOIN_OBJECT_RANGE_OBJECT) &&
485              (mouse_mode != MouseAudition || item_type != RegionItem) &&
486              (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
487              (mouse_mode != MouseGain) &&
488              (mouse_mode != MouseRange)) ||
489             ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3) ||
490             internal_editing()) {
491
492                 return;
493         }
494
495         if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
496
497                 if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
498
499                         /* almost no selection action on modified button-2 or button-3 events */
500
501                         if (item_type != RegionItem && event->button.button != 2) {
502                                 return;
503                         }
504                 }
505         }
506
507         Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
508         bool press = (event->type == GDK_BUTTON_PRESS);
509
510         switch (item_type) {
511         case RegionItem:
512                 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
513                         set_selected_regionview_from_click (press, op, true);
514                 } else if (event->type == GDK_BUTTON_PRESS) {
515                         selection->clear_tracks ();
516                         set_selected_track_as_side_effect (op, true);
517                 }
518                 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
519                         clicked_selection = select_range_around_region (selection->regions.front());
520                 }
521                 break;
522
523         case RegionViewNameHighlight:
524         case RegionViewName:
525         case LeftFrameHandle:
526         case RightFrameHandle:
527                 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
528                         set_selected_regionview_from_click (press, op, true);
529                 } else if (event->type == GDK_BUTTON_PRESS) {
530                         set_selected_track_as_side_effect (op);
531                 }
532                 break;
533
534
535         case FadeInHandleItem:
536         case FadeInItem:
537         case FadeOutHandleItem:
538         case FadeOutItem:
539                 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
540                         set_selected_regionview_from_click (press, op, true);
541                 } else if (event->type == GDK_BUTTON_PRESS) {
542                         set_selected_track_as_side_effect (op);
543                 }
544                 break;
545
546         case ControlPointItem:
547                 set_selected_track_as_side_effect (op, true);
548                 if (mouse_mode != MouseRange || _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
549                         set_selected_control_point_from_click (op, false);
550                 }
551                 break;
552
553         case StreamItem:
554                 /* for context click, select track */
555                 if (event->button.button == 3) {
556                         selection->clear_tracks ();
557                         set_selected_track_as_side_effect (op, true);
558                 }
559                 break;
560
561         case AutomationTrackItem:
562                 set_selected_track_as_side_effect (op, true);
563                 break;
564
565         default:
566                 break;
567         }
568 }
569
570 bool
571 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
572 {
573         /* single mouse clicks on any of these item types operate
574            independent of mouse mode, mostly because they are
575            not on the main track canvas or because we want
576            them to be modeless.
577         */
578
579         switch (item_type) {
580         case PlayheadCursorItem:
581                 _drags->set (new CursorDrag (this, item, true), event);
582                 return true;
583
584         case MarkerItem:
585                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
586                         hide_marker (item, event);
587                 } else {
588                         _drags->set (new MarkerDrag (this, item), event);
589                 }
590                 return true;
591
592         case TempoMarkerItem:
593         {
594                 TempoMarker* m = reinterpret_cast<TempoMarker*> (item->get_data ("marker"));
595                 assert (m);
596                 if (m->tempo().movable ()) {
597                         _drags->set (
598                                 new TempoMarkerDrag (
599                                         this,
600                                         item,
601                                         Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
602                                         ),
603                                 event
604                                 );
605                         return true;
606                 } else {
607                         return false;
608                 }
609         }
610
611         case MeterMarkerItem:
612         {
613                 MeterMarker* m = reinterpret_cast<MeterMarker*> (item->get_data ("marker"));
614                 assert (m);
615                 if (m->meter().movable ()) {
616                         _drags->set (
617                                 new MeterMarkerDrag (
618                                         this,
619                                         item,
620                                         Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
621                                         ),
622                                 event
623                                 );
624                         return true;
625                 } else {
626                         return false;
627                 }
628         }
629
630         case MarkerBarItem:
631         case TempoBarItem:
632         case MeterBarItem:
633                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
634                         _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
635                 }
636                 return true;
637                 break;
638
639
640         case RangeMarkerBarItem:
641                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
642                         _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
643                 } else {
644                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
645                 }
646                 return true;
647                 break;
648
649         case CdMarkerBarItem:
650                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
651                         _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
652                 } else {
653                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
654                 }
655                 return true;
656                 break;
657
658         case TransportMarkerBarItem:
659                 if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
660                         _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), event);
661                 } else {
662                         _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
663                 }
664                 return true;
665                 break;
666
667         default:
668                 break;
669         }
670
671         if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
672                 /* special case: allow trim of range selections in joined object mode;
673                    in theory eff should equal MouseRange in this case, but it doesn't
674                    because entering the range selection canvas item results in entered_regionview
675                    being set to 0, so update_join_object_range_location acts as if we aren't
676                    over a region.
677                 */
678                 if (item_type == StartSelectionTrimItem) {
679                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
680                 } else if (item_type == EndSelectionTrimItem) {
681                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
682                 }
683         }
684
685         Editing::MouseMode eff = effective_mouse_mode ();
686
687         /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
688         if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
689                 eff = MouseObject;
690         }
691
692         switch (eff) {
693         case MouseRange:
694                 switch (item_type) {
695                 case StartSelectionTrimItem:
696                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
697                         break;
698
699                 case EndSelectionTrimItem:
700                         _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
701                         break;
702
703                 case SelectionItem:
704                         if (Keyboard::modifier_state_contains
705                             (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier))) {
706                                 // contains and not equals because I can't use alt as a modifier alone.
707                                 start_selection_grab (item, event);
708                         } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
709                                 /* grab selection for moving */
710                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
711                         } else {
712                                 double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
713                                 pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
714                                 if (tvp.first) {
715                                         AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
716                                         if (join_object_range_button.get_active() && atv) {
717                                                 /* smart "join" mode: drag automation */
718                                                 _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, _cursors->up_down);
719                                         } else {
720                                                 /* this was debated, but decided the more common action was to
721                                                    make a new selection */
722                                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
723                                         }
724                                 }
725                         }
726                         break;
727
728                 case NoteItem:
729                         if (internal_editing()) {
730                                 /* trim notes if we're in internal edit mode and near the ends of the note */
731                                 ArdourCanvas::CanvasNote* cn = dynamic_cast<ArdourCanvas::CanvasNote*> (item);
732                                 if (cn && cn->big_enough_to_trim() && cn->mouse_near_ends()) {
733                                         _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
734                                 } else {
735                                         _drags->set (new NoteDrag (this, item), event);
736                                 }
737                         }
738                         return true;
739
740                 case StreamItem:
741                         if (internal_editing()) {
742                                 if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
743                                         _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
744                                         return true;
745                                 } 
746                         } else {
747                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
748                                 return true;
749                         }
750                         break;
751
752                 case RegionViewNameHighlight:
753                         if (!clicked_regionview->region()->locked()) {
754                                 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
755                                 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
756                                 return true;
757                         }
758                         break;
759
760                 case LeftFrameHandle:
761                 case RightFrameHandle:
762                         if (!internal_editing() && !clicked_regionview->region()->locked()) {
763                                 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
764                                 _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
765                                 return true;
766                         }
767                         break;
768
769                 default:
770                         if (!internal_editing()) {
771                                 _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
772                         }
773                 }
774                 return true;
775                 break;
776
777         case MouseObject:
778                 switch (item_type) {
779                 case NoteItem:
780                         if (internal_editing()) {
781                                 ArdourCanvas::CanvasNoteEvent* cn = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
782                                 if (cn->mouse_near_ends()) {
783                                         _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
784                                 } else {
785                                         _drags->set (new NoteDrag (this, item), event);
786                                 }
787                                 return true;
788                         }
789                         break;
790
791                 default:
792                         break;
793                 }
794
795                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
796                     event->type == GDK_BUTTON_PRESS) {
797
798                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
799
800                 } else if (event->type == GDK_BUTTON_PRESS) {
801
802                         switch (item_type) {
803                         case FadeInHandleItem:
804                         {
805                                 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
806                                 _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_in);
807                                 return true;
808                         }
809
810                         case FadeOutHandleItem:
811                         {
812                                 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
813                                 _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), s), event, _cursors->fade_out);
814                                 return true;
815                         }
816
817                         case FeatureLineItem:
818                         {
819                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
820                                         remove_transient(item);
821                                         return true;
822                                 }
823
824                                 _drags->set (new FeatureLineDrag (this, item), event);
825                                 return true;
826                                 break;
827                         }
828
829                         case RegionItem:
830                                 if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
831                                         /* click on an automation region view; do nothing here and let the ARV's signal handler
832                                            sort it out.
833                                         */
834                                         break;
835                                 }
836
837                                 if (internal_editing ()) {
838                                         /* no region drags in internal edit mode */
839                                         break;
840                                 }
841
842                                 /* click on a normal region view */
843                                 if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
844                                         add_region_copy_drag (item, event, clicked_regionview);
845                                 } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
846                                         add_region_brush_drag (item, event, clicked_regionview);
847                                 } else {
848                                         add_region_drag (item, event, clicked_regionview);
849                                 }
850
851                                 if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT && !selection->regions.empty()) {
852                                         _drags->add (new SelectionDrag (this, clicked_axisview->get_selection_rect (clicked_selection)->rect, SelectionDrag::SelectionMove));
853                                 }
854
855                                 _drags->start_grab (event);
856                                 break;
857
858                         case RegionViewNameHighlight:
859                         case LeftFrameHandle:
860                         case RightFrameHandle:
861                                 if (!clicked_regionview->region()->locked()) {
862                                         RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
863                                         _drags->set (new TrimDrag (this, item, clicked_regionview, s.by_layer()), event);
864                                         return true;
865                                 }
866                                 break;
867
868                         case RegionViewName:
869                         {
870                                 /* rename happens on edit clicks */
871                                 RegionSelection s = get_equivalent_regions (selection->regions, Properties::edit.property_id);
872                                 _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, s.by_layer()), event);
873                                 return true;
874                                 break;
875                         }
876
877                         case ControlPointItem:
878                                 _drags->set (new ControlPointDrag (this, item), event);
879                                 return true;
880                                 break;
881
882                         case AutomationLineItem:
883                                 _drags->set (new LineDrag (this, item), event);
884                                 return true;
885                                 break;
886
887                         case StreamItem:
888                                 if (internal_editing()) {
889                                         if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
890                                                 _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
891                                         }
892                                         return true;
893                                 } else {
894                                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
895                                 }
896                                 break;
897
898                         case AutomationTrackItem:
899                         {
900                                 TimeAxisView* parent = clicked_axisview->get_parent ();
901                                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
902                                 assert (atv);
903                                 if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
904                                         /* create a MIDI region so that we have somewhere to put automation */
905                                         _drags->set (new RegionCreateDrag (this, item, parent), event);
906                                 } else {
907                                         /* rubberband drag to select automation points */
908                                         _drags->set (new EditorRubberbandSelectDrag (this, item), event);
909                                 }
910                                 break;
911                         }
912
913                         case SelectionItem:
914                         {
915                                 if (join_object_range_button.get_active()) {
916                                         /* we're in "smart" joined mode, and we've clicked on a Selection */
917                                         double const y = event->button.y + vertical_adjustment.get_value() - canvas_timebars_vsize;
918                                         pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
919                                         if (tvp.first) {
920                                                 /* if we're over an automation track, start a drag of its data */
921                                                 AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
922                                                 if (atv) {
923                                                         _drags->set (new AutomationRangeDrag (this, atv->base_item(), selection->time), event, _cursors->up_down);
924                                                 }
925
926                                                 /* if we're over a track and a region, and in the `object' part of a region,
927                                                    put a selection around the region and drag both
928                                                 */
929                                                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
930                                                 if (rtv && _join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
931                                                         boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (rtv->route ());
932                                                         if (t) {
933                                                                 boost::shared_ptr<Playlist> pl = t->playlist ();
934                                                                 if (pl) {
935
936                                                                         boost::shared_ptr<Region> r = pl->top_region_at (event_frame (event));
937                                                                         if (r) {
938                                                                                 RegionView* rv = rtv->view()->find_view (r);
939                                                                                 clicked_selection = select_range_around_region (rv);
940                                                                                 _drags->add (new SelectionDrag (this, item, SelectionDrag::SelectionMove));
941                                                                                 list<RegionView*> rvs;
942                                                                                 rvs.push_back (rv);
943                                                                                 _drags->add (new RegionMoveDrag (this, item, rv, rvs, false, false));
944                                                                                 _drags->start_grab (event);
945                                                                         }
946                                                                 }
947                                                         }
948                                                 }
949                                         }
950                                 }
951                                 break;
952                         }
953
954 #ifdef WITH_CMT
955                         case ImageFrameHandleStartItem:
956                                 imageframe_start_handle_op(item, event) ;
957                                 return(true) ;
958                                 break ;
959                         case ImageFrameHandleEndItem:
960                                 imageframe_end_handle_op(item, event) ;
961                                 return(true) ;
962                                 break ;
963                         case MarkerViewHandleStartItem:
964                                 markerview_item_start_handle_op(item, event) ;
965                                 return(true) ;
966                                 break ;
967                         case MarkerViewHandleEndItem:
968                                 markerview_item_end_handle_op(item, event) ;
969                                 return(true) ;
970                                 break ;
971                         case MarkerViewItem:
972                                 start_markerview_grab(item, event) ;
973                                 break ;
974                         case ImageFrameItem:
975                                 start_imageframe_grab(item, event) ;
976                                 break ;
977 #endif
978
979                         case MarkerBarItem:
980
981                                 break;
982
983                         default:
984                                 break;
985                         }
986                 }
987                 return true;
988                 break;
989
990         case MouseGain:
991                 switch (item_type) {
992                 case RegionItem:
993                         /* start a grab so that if we finish after moving
994                            we can tell what happened.
995                         */
996                         _drags->set (new RegionGainDrag (this, item), event, current_canvas_cursor);
997                         break;
998
999                 case GainLineItem:
1000                         _drags->set (new LineDrag (this, item), event);
1001                         return true;
1002
1003                 case ControlPointItem:
1004                         _drags->set (new ControlPointDrag (this, item), event);
1005                         return true;
1006                         break;
1007
1008                 default:
1009                         break;
1010                 }
1011                 return true;
1012                 break;
1013
1014                 switch (item_type) {
1015                 case ControlPointItem:
1016                         _drags->set (new ControlPointDrag (this, item), event);
1017                         break;
1018
1019                 case AutomationLineItem:
1020                         _drags->set (new LineDrag (this, item), event);
1021                         break;
1022
1023                 case RegionItem:
1024                         // XXX need automation mode to identify which
1025                         // line to use
1026                         // start_line_grab_from_regionview (item, event);
1027                         break;
1028
1029                 default:
1030                         break;
1031                 }
1032                 return true;
1033                 break;
1034
1035         case MouseZoom:
1036                 if (event->type == GDK_BUTTON_PRESS) {
1037                         _drags->set (new MouseZoomDrag (this, item), event);
1038                 }
1039
1040                 return true;
1041                 break;
1042
1043         case MouseTimeFX:
1044                 if (internal_editing() && item_type == NoteItem) {
1045                         /* drag notes if we're in internal edit mode */
1046                         _drags->set (new NoteResizeDrag (this, item), event, current_canvas_cursor);
1047                         return true;
1048                 } else if ((!internal_editing() || dynamic_cast<AudioRegionView*> (clicked_regionview)) && clicked_regionview) {
1049                         /* do time-FX if we're not in internal edit mode, or we are but we clicked on an audio region */
1050                         _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1051                         return true;
1052                 }
1053                 break;
1054
1055         case MouseAudition:
1056                 _drags->set (new ScrubDrag (this, item), event);
1057                 scrub_reversals = 0;
1058                 scrub_reverse_distance = 0;
1059                 last_scrub_x = event->button.x;
1060                 scrubbing_direction = 0;
1061                 set_canvas_cursor (_cursors->transparent);
1062                 return true;
1063                 break;
1064
1065         default:
1066                 break;
1067         }
1068
1069         return false;
1070 }
1071
1072 bool
1073 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1074 {
1075         Editing::MouseMode const eff = effective_mouse_mode ();
1076         switch (eff) {
1077         case MouseObject:
1078                 switch (item_type) {
1079                 case RegionItem:
1080                         if (internal_editing ()) {
1081                                 /* no region drags in internal edit mode */
1082                                 return false;
1083                         }
1084
1085                         if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1086                                 add_region_copy_drag (item, event, clicked_regionview);
1087                         } else {
1088                                 add_region_drag (item, event, clicked_regionview);
1089                         }
1090                         _drags->start_grab (event);
1091                         return true;
1092                         break;
1093                 case ControlPointItem:
1094                         _drags->set (new ControlPointDrag (this, item), event);
1095                         return true;
1096                         break;
1097
1098                 default:
1099                         break;
1100                 }
1101
1102                 switch (item_type) {
1103                 case RegionViewNameHighlight:
1104                         _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1105                         return true;
1106                         break;
1107
1108                 case LeftFrameHandle:
1109                 case RightFrameHandle:
1110                         if (!internal_editing ()) {
1111                                 _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1112                         }
1113                         return true;
1114                         break;
1115
1116                 case RegionViewName:
1117                         _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1118                         return true;
1119                         break;
1120
1121                 default:
1122                         break;
1123                 }
1124
1125                 break;
1126
1127         case MouseRange:
1128                 /* relax till release */
1129                 return true;
1130                 break;
1131
1132
1133         case MouseZoom:
1134                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
1135                         temporal_zoom_to_frame (false, event_frame (event));
1136                 } else {
1137                         temporal_zoom_to_frame (true, event_frame(event));
1138                 }
1139                 return true;
1140                 break;
1141
1142         default:
1143                 break;
1144         }
1145
1146         return false;
1147 }
1148
1149 bool
1150 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1151 {
1152         if (event->type != GDK_BUTTON_PRESS) {
1153                 return false;
1154         }
1155
1156         Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->track_canvas->get_window();
1157
1158         if (canvas_window) {
1159                 Glib::RefPtr<const Gdk::Window> pointer_window;
1160                 int x, y;
1161                 double wx, wy;
1162                 Gdk::ModifierType mask;
1163
1164                 pointer_window = canvas_window->get_pointer (x, y, mask);
1165
1166                 if (pointer_window == track_canvas->get_bin_window()) {
1167                         track_canvas->window_to_world (x, y, wx, wy);
1168                 }
1169         }
1170
1171         pre_press_cursor = current_canvas_cursor;
1172
1173         track_canvas->grab_focus();
1174
1175         if (_session && _session->actively_recording()) {
1176                 return true;
1177         }
1178
1179         button_selection (item, event, item_type);
1180
1181         if (!_drags->active () &&
1182             (Keyboard::is_delete_event (&event->button) ||
1183              Keyboard::is_context_menu_event (&event->button) ||
1184              Keyboard::is_edit_event (&event->button))) {
1185
1186                 /* handled by button release */
1187                 return true;
1188         }
1189
1190         switch (event->button.button) {
1191         case 1:
1192                 return button_press_handler_1 (item, event, item_type);
1193                 break;
1194
1195         case 2:
1196                 return button_press_handler_2 (item, event, item_type);
1197                 break;
1198
1199         case 3:
1200                 break;
1201
1202         default:
1203                 return button_press_dispatch (&event->button);
1204                 break;
1205
1206         }
1207
1208         return false;
1209 }
1210
1211 bool
1212 Editor::button_press_dispatch (GdkEventButton* ev)
1213 {
1214         /* this function is intended only for buttons 4 and above.
1215          */
1216
1217         Gtkmm2ext::MouseButton b (ev->state, ev->button);
1218         return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1219 }
1220
1221 bool
1222 Editor::button_release_dispatch (GdkEventButton* ev)
1223 {
1224         /* this function is intended only for buttons 4 and above.
1225          */
1226
1227         Gtkmm2ext::MouseButton b (ev->state, ev->button);
1228         return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1229 }
1230
1231 bool
1232 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1233 {
1234         framepos_t where = event_frame (event, 0, 0);
1235         AutomationTimeAxisView* atv = 0;
1236
1237         if (pre_press_cursor) {
1238                 set_canvas_cursor (pre_press_cursor);
1239                 pre_press_cursor = 0;
1240         }
1241
1242         /* no action if we're recording */
1243
1244         if (_session && _session->actively_recording()) {
1245                 return true;
1246         }
1247
1248         /* see if we're finishing a drag */
1249
1250         bool were_dragging = false;
1251         if (_drags->active ()) {
1252                 bool const r = _drags->end_grab (event);
1253                 if (r) {
1254                         /* grab dragged, so do nothing else */
1255                         return true;
1256                 }
1257
1258                 were_dragging = true;
1259         }
1260
1261         update_region_layering_order_editor ();
1262
1263         /* edit events get handled here */
1264
1265         if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1266                 switch (item_type) {
1267                 case RegionItem:
1268                         show_region_properties ();
1269                         break;
1270
1271                 case TempoMarkerItem:
1272                         edit_tempo_marker (item);
1273                         break;
1274
1275                 case MeterMarkerItem:
1276                         edit_meter_marker (item);
1277                         break;
1278
1279                 case RegionViewName:
1280                         if (clicked_regionview->name_active()) {
1281                                 return mouse_rename_region (item, event);
1282                         }
1283                         break;
1284
1285                 case ControlPointItem:
1286                         edit_control_point (item);
1287                         break;
1288
1289                 case NoteItem:
1290                         edit_note (item);
1291                         break;
1292
1293                 default:
1294                         break;
1295                 }
1296                 return true;
1297         }
1298
1299         /* context menu events get handled here */
1300
1301         if (Keyboard::is_context_menu_event (&event->button)) {
1302
1303                 context_click_event = *event;
1304
1305                 if (!_drags->active ()) {
1306
1307                         /* no matter which button pops up the context menu, tell the menu
1308                            widget to use button 1 to drive menu selection.
1309                         */
1310
1311                         switch (item_type) {
1312                         case FadeInItem:
1313                         case FadeInHandleItem:
1314                         case FadeOutItem:
1315                         case FadeOutHandleItem:
1316                                 popup_fade_context_menu (1, event->button.time, item, item_type);
1317                                 break;
1318
1319                         case StreamItem:
1320                                 popup_track_context_menu (1, event->button.time, item_type, false);
1321                                 break;
1322
1323                         case RegionItem:
1324                         case RegionViewNameHighlight:
1325                         case LeftFrameHandle:
1326                         case RightFrameHandle:
1327                         case RegionViewName:
1328                                 popup_track_context_menu (1, event->button.time, item_type, false);
1329                                 break;
1330
1331                         case SelectionItem:
1332                                 popup_track_context_menu (1, event->button.time, item_type, true);
1333                                 break;
1334
1335                         case AutomationTrackItem:
1336                                 popup_track_context_menu (1, event->button.time, item_type, false);
1337                                 break;
1338
1339                         case MarkerBarItem:
1340                         case RangeMarkerBarItem:
1341                         case TransportMarkerBarItem:
1342                         case CdMarkerBarItem:
1343                         case TempoBarItem:
1344                         case MeterBarItem:
1345                                 popup_ruler_menu (where, item_type);
1346                                 break;
1347
1348                         case MarkerItem:
1349                                 marker_context_menu (&event->button, item);
1350                                 break;
1351
1352                         case TempoMarkerItem:
1353                                 tempo_or_meter_marker_context_menu (&event->button, item);
1354                                 break;
1355
1356                         case MeterMarkerItem:
1357                                 tempo_or_meter_marker_context_menu (&event->button, item);
1358                                 break;
1359
1360                         case CrossfadeViewItem:
1361                                 popup_track_context_menu (1, event->button.time, item_type, false);
1362                                 break;
1363
1364                         case ControlPointItem:
1365                                 popup_control_point_context_menu (item, event);
1366                                 break;
1367
1368 #ifdef WITH_CMT
1369                         case ImageFrameItem:
1370                                 popup_imageframe_edit_menu(1, event->button.time, item, true) ;
1371                                 break ;
1372                         case ImageFrameTimeAxisItem:
1373                                 popup_imageframe_edit_menu(1, event->button.time, item, false) ;
1374                                 break ;
1375                         case MarkerViewItem:
1376                                 popup_marker_time_axis_edit_menu(1, event->button.time, item, true) ;
1377                                 break ;
1378                         case MarkerTimeAxisItem:
1379                                 popup_marker_time_axis_edit_menu(1, event->button.time, item, false) ;
1380                                 break ;
1381 #endif
1382
1383                         default:
1384                                 break;
1385                         }
1386
1387                         return true;
1388                 }
1389         }
1390
1391         /* delete events get handled here */
1392
1393         Editing::MouseMode const eff = effective_mouse_mode ();
1394
1395         if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1396
1397                 switch (item_type) {
1398                 case TempoMarkerItem:
1399                         remove_tempo_marker (item);
1400                         break;
1401
1402                 case MeterMarkerItem:
1403                         remove_meter_marker (item);
1404                         break;
1405
1406                 case MarkerItem:
1407                         remove_marker (*item, event);
1408                         break;
1409
1410                 case RegionItem:
1411                         if (eff == MouseObject) {
1412                                 remove_clicked_region ();
1413                         }
1414                         break;
1415
1416                 case ControlPointItem:
1417                         remove_control_point (item);
1418                         break;
1419
1420                 case NoteItem:
1421                         remove_midi_note (item, event);
1422                         break;
1423
1424                 default:
1425                         break;
1426                 }
1427                 return true;
1428         }
1429
1430         switch (event->button.button) {
1431         case 1:
1432
1433                 switch (item_type) {
1434                 /* see comments in button_press_handler */
1435                 case PlayheadCursorItem:
1436                 case MarkerItem:
1437                 case GainLineItem:
1438                 case AutomationLineItem:
1439                 case StartSelectionTrimItem:
1440                 case EndSelectionTrimItem:
1441                         return true;
1442
1443                 case MarkerBarItem:
1444                         if (!_dragging_playhead) {
1445                                 snap_to_with_modifier (where, event, 0, true);
1446                                 mouse_add_new_marker (where);
1447                         }
1448                         return true;
1449
1450                 case CdMarkerBarItem:
1451                         if (!_dragging_playhead) {
1452                                 // if we get here then a dragged range wasn't done
1453                                 snap_to_with_modifier (where, event, 0, true);
1454                                 mouse_add_new_marker (where, true);
1455                         }
1456                         return true;
1457
1458                 case TempoBarItem:
1459                         if (!_dragging_playhead) {
1460                                 snap_to_with_modifier (where, event);
1461                                 mouse_add_new_tempo_event (where);
1462                         }
1463                         return true;
1464
1465                 case MeterBarItem:
1466                         if (!_dragging_playhead) {
1467                                 mouse_add_new_meter_event (pixel_to_frame (event->button.x));
1468                         }
1469                         return true;
1470                         break;
1471
1472                 default:
1473                         break;
1474                 }
1475
1476                 switch (eff) {
1477                 case MouseObject:
1478                         switch (item_type) {
1479                         case AutomationTrackItem:
1480                                 atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1481                                 if (atv) {
1482                                         atv->add_automation_event (event, where, event->button.y);
1483                                 }
1484                                 return true;
1485                                 break;
1486
1487                         default:
1488                                 break;
1489                         }
1490                         break;
1491
1492                 case MouseGain:
1493                         switch (item_type) {
1494                         case RegionItem:
1495                         {
1496                                 /* check that we didn't drag before releasing, since
1497                                    its really annoying to create new control
1498                                    points when doing this.
1499                                 */
1500                                 AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1501                                 if (were_dragging && arv) {
1502                                         arv->add_gain_point_event (item, event);
1503                                 }
1504                                 return true;
1505                                 break;
1506                         }
1507
1508                         case AutomationTrackItem:
1509                                 dynamic_cast<AutomationTimeAxisView*>(clicked_axisview)->
1510                                         add_automation_event (event, where, event->button.y);
1511                                 return true;
1512                                 break;
1513                         default:
1514                                 break;
1515                         }
1516                         break;
1517
1518                 case MouseAudition:
1519                         set_canvas_cursor (current_canvas_cursor);
1520                         if (scrubbing_direction == 0) {
1521                                 /* no drag, just a click */
1522                                 switch (item_type) {
1523                                 case RegionItem:
1524                                         play_selected_region ();
1525                                         break;
1526                                 default:
1527                                         break;
1528                                 }
1529                         } else {
1530                                 /* make sure we stop */
1531                                 _session->request_transport_speed (0.0);
1532                         }
1533                         break;
1534
1535                 default:
1536                         break;
1537
1538                 }
1539
1540                 /* do any (de)selection operations that should occur on button release */
1541                 button_selection (item, event, item_type);
1542                 return true;
1543                 break;
1544
1545
1546         case 2:
1547                 switch (eff) {
1548
1549                 case MouseObject:
1550                         switch (item_type) {
1551                         case RegionItem:
1552                                 if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1553                                         raise_region ();
1554                                 } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1555                                         lower_region ();
1556                                 } else {
1557                                         // Button2 click is unused
1558                                 }
1559                                 return true;
1560
1561                                 break;
1562
1563                         default:
1564                                 break;
1565                         }
1566                         break;
1567
1568                 case MouseRange:
1569
1570                         // x_style_paste (where, 1.0);
1571                         return true;
1572                         break;
1573
1574                 default:
1575                         break;
1576                 }
1577
1578                 break;
1579
1580         case 3:
1581                 break;
1582
1583         default:
1584                 break;
1585         }
1586
1587         return false;
1588 }
1589
1590 bool
1591 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1592 {
1593         ControlPoint* cp;
1594         Marker * marker;
1595         double fraction;
1596         bool ret = true;
1597
1598         switch (item_type) {
1599         case ControlPointItem:
1600                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1601                         cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1602                         cp->set_visible (true);
1603
1604                         double at_x, at_y;
1605                         at_x = cp->get_x();
1606                         at_y = cp->get_y ();
1607                         cp->i2w (at_x, at_y);
1608                         at_x += 10.0;
1609                         at_y += 10.0;
1610
1611                         fraction = 1.0 - (cp->get_y() / cp->line().height());
1612
1613                         if (is_drawable() && !_drags->active ()) {
1614                                 set_canvas_cursor (_cursors->fader);
1615                         }
1616
1617                         _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
1618                         _verbose_cursor->show ();
1619                 }
1620                 break;
1621
1622         case GainLineItem:
1623                 if (mouse_mode == MouseGain) {
1624                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1625                         if (line)
1626                                 line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredGainLine.get();
1627                         if (is_drawable()) {
1628                                 set_canvas_cursor (_cursors->fader);
1629                         }
1630                 }
1631                 break;
1632
1633         case AutomationLineItem:
1634                 if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
1635                         {
1636                                 ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1637                                 if (line)
1638                                         line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_EnteredAutomationLine.get();
1639                         }
1640                         if (is_drawable()) {
1641                                 set_canvas_cursor (_cursors->fader);
1642                         }
1643                 }
1644                 break;
1645
1646         case RegionViewNameHighlight:
1647                 if (is_drawable() && mouse_mode == MouseObject && entered_regionview) {
1648                         set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1649                         _over_region_trim_target = true;
1650                 }
1651                 break;
1652
1653         case LeftFrameHandle:
1654         case RightFrameHandle:
1655                 if (is_drawable() && mouse_mode == MouseObject && !internal_editing() && entered_regionview) {
1656                         set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1657                 }
1658                 break;
1659
1660         case StartSelectionTrimItem:
1661 #ifdef WITH_CMT
1662         case ImageFrameHandleStartItem:
1663         case MarkerViewHandleStartItem:
1664 #endif
1665                 if (is_drawable()) {
1666                         set_canvas_cursor (_cursors->left_side_trim);
1667                 }
1668                 break;
1669         case EndSelectionTrimItem:
1670 #ifdef WITH_CMT
1671         case ImageFrameHandleEndItem:
1672         case MarkerViewHandleEndItem:
1673 #endif
1674                 if (is_drawable()) {
1675                         set_canvas_cursor (_cursors->right_side_trim);
1676                 }
1677                 break;
1678
1679         case PlayheadCursorItem:
1680                 if (is_drawable()) {
1681                         switch (_edit_point) {
1682                         case EditAtMouse:
1683                                 set_canvas_cursor (_cursors->grabber_edit_point);
1684                                 break;
1685                         default:
1686                                 set_canvas_cursor (_cursors->grabber);
1687                                 break;
1688                         }
1689                 }
1690                 break;
1691
1692         case RegionViewName:
1693
1694                 /* when the name is not an active item, the entire name highlight is for trimming */
1695
1696                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1697                         if (mouse_mode == MouseObject && is_drawable()) {
1698                                 set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
1699                                 _over_region_trim_target = true;
1700                         }
1701                 }
1702                 break;
1703
1704
1705         case AutomationTrackItem:
1706                 if (is_drawable()) {
1707                         Gdk::Cursor *cursor;
1708                         switch (mouse_mode) {
1709                         case MouseRange:
1710                                 cursor = _cursors->selector;
1711                                 break;
1712                         case MouseZoom:
1713                                 cursor = _cursors->zoom_in;
1714                                 break;
1715                         default:
1716                                 cursor = _cursors->cross_hair;
1717                                 break;
1718                         }
1719
1720                         set_canvas_cursor (cursor);
1721
1722                         AutomationTimeAxisView* atv;
1723                         if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1724                                 clear_entered_track = false;
1725                                 set_entered_track (atv);
1726                         }
1727                 }
1728                 break;
1729
1730         case MarkerBarItem:
1731         case RangeMarkerBarItem:
1732         case TransportMarkerBarItem:
1733         case CdMarkerBarItem:
1734         case MeterBarItem:
1735         case TempoBarItem:
1736                 if (is_drawable()) {
1737                         set_canvas_cursor (_cursors->timebar);
1738                 }
1739                 break;
1740
1741         case MarkerItem:
1742                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1743                         break;
1744                 }
1745                 entered_marker = marker;
1746                 marker->set_color_rgba (ARDOUR_UI::config()->canvasvar_EnteredMarker.get());
1747                 // fall through
1748         case MeterMarkerItem:
1749         case TempoMarkerItem:
1750                 if (is_drawable()) {
1751                         set_canvas_cursor (_cursors->timebar);
1752                 }
1753                 break;
1754
1755         case FadeInHandleItem:
1756                 if (mouse_mode == MouseObject && !internal_editing()) {
1757                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1758                         if (rect) {
1759                                 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1760                         }
1761                         set_canvas_cursor (_cursors->fade_in);
1762                 }
1763                 break;
1764
1765         case FadeOutHandleItem:
1766                 if (mouse_mode == MouseObject && !internal_editing()) {
1767                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1768                         if (rect) {
1769                                 rect->property_fill_color_rgba() = 0xBBBBBBAA;
1770                         }
1771                         set_canvas_cursor (_cursors->fade_out);
1772                 }
1773                 break;
1774         case FeatureLineItem:
1775                 {
1776                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1777                         line->property_fill_color_rgba() = 0xFF0000FF;
1778                 }
1779                 break;
1780         case SelectionItem:
1781                 if (join_object_range_button.get_active()) {
1782                         set_canvas_cursor ();
1783                 }
1784                 break;
1785
1786         default:
1787                 break;
1788         }
1789
1790         /* second pass to handle entered track status in a comprehensible way.
1791          */
1792
1793         switch (item_type) {
1794         case GainLineItem:
1795         case AutomationLineItem:
1796         case ControlPointItem:
1797                 /* these do not affect the current entered track state */
1798                 clear_entered_track = false;
1799                 break;
1800
1801         case AutomationTrackItem:
1802                 /* handled above already */
1803                 break;
1804
1805         default:
1806                 set_entered_track (0);
1807                 break;
1808         }
1809
1810         return ret;
1811 }
1812
1813 bool
1814 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1815 {
1816         AutomationLine* al;
1817         ControlPoint* cp;
1818         Marker *marker;
1819         Location *loc;
1820         RegionView* rv;
1821         bool is_start;
1822         bool ret = true;
1823
1824         switch (item_type) {
1825         case ControlPointItem:
1826                 cp = reinterpret_cast<ControlPoint*>(item->get_data ("control_point"));
1827                 if (cp->line().the_list()->interpolation() != AutomationList::Discrete) {
1828                         if (cp->line().npoints() > 1 && !cp->get_selected()) {
1829                                 cp->set_visible (false);
1830                         }
1831                 }
1832
1833                 if (is_drawable()) {
1834                         set_canvas_cursor (current_canvas_cursor);
1835                 }
1836
1837                 _verbose_cursor->hide ();
1838                 break;
1839
1840         case RegionViewNameHighlight:
1841         case LeftFrameHandle:
1842         case RightFrameHandle:
1843         case StartSelectionTrimItem:
1844         case EndSelectionTrimItem:
1845         case PlayheadCursorItem:
1846
1847 #ifdef WITH_CMT
1848         case ImageFrameHandleStartItem:
1849         case ImageFrameHandleEndItem:
1850         case MarkerViewHandleStartItem:
1851         case MarkerViewHandleEndItem:
1852 #endif
1853
1854                 _over_region_trim_target = false;
1855
1856                 if (is_drawable()) {
1857                         set_canvas_cursor (current_canvas_cursor);
1858                 }
1859                 break;
1860
1861         case GainLineItem:
1862         case AutomationLineItem:
1863                 al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1864                 {
1865                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1866                         if (line)
1867                                 line->property_fill_color_rgba() = al->get_line_color();
1868                 }
1869                 if (is_drawable()) {
1870                         set_canvas_cursor (current_canvas_cursor);
1871                 }
1872                 break;
1873
1874         case RegionViewName:
1875                 /* see enter_handler() for notes */
1876                 _over_region_trim_target = false;
1877
1878                 if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
1879                         if (is_drawable() && mouse_mode == MouseObject) {
1880                                 set_canvas_cursor (current_canvas_cursor);
1881                         }
1882                 }
1883                 break;
1884
1885         case RangeMarkerBarItem:
1886         case TransportMarkerBarItem:
1887         case CdMarkerBarItem:
1888         case MeterBarItem:
1889         case TempoBarItem:
1890         case MarkerBarItem:
1891                 if (is_drawable()) {
1892                         set_canvas_cursor (current_canvas_cursor);
1893                 }
1894                 break;
1895
1896         case MarkerItem:
1897                 if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1898                         break;
1899                 }
1900                 entered_marker = 0;
1901                 if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1902                         location_flags_changed (loc, this);
1903                 }
1904                 // fall through
1905         case MeterMarkerItem:
1906         case TempoMarkerItem:
1907
1908                 if (is_drawable()) {
1909                         set_canvas_cursor (_cursors->timebar);
1910                 }
1911
1912                 break;
1913
1914         case FadeInHandleItem:
1915         case FadeOutHandleItem:
1916                 rv = static_cast<RegionView*>(item->get_data ("regionview"));
1917                 {
1918                         ArdourCanvas::SimpleRect *rect = dynamic_cast<ArdourCanvas::SimpleRect *> (item);
1919                         if (rect) {
1920                                 rect->property_fill_color_rgba() = rv->get_fill_color();
1921                                 rect->property_outline_pixels() = 0;
1922                         }
1923                 }
1924                 set_canvas_cursor (current_canvas_cursor);
1925                 break;
1926
1927         case AutomationTrackItem:
1928                 if (is_drawable()) {
1929                         set_canvas_cursor (current_canvas_cursor);
1930                         clear_entered_track = true;
1931                         Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
1932                 }
1933                 break;
1934         case FeatureLineItem:
1935                 {
1936                         ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1937                         line->property_fill_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();;
1938                 }
1939                 break;
1940
1941         default:
1942                 break;
1943         }
1944
1945         return ret;
1946 }
1947
1948 gint
1949 Editor::left_automation_track ()
1950 {
1951         if (clear_entered_track) {
1952                 set_entered_track (0);
1953                 clear_entered_track = false;
1954         }
1955         return false;
1956 }
1957
1958 void
1959 Editor::scrub (framepos_t frame, double current_x)
1960 {
1961         double delta;
1962
1963         if (scrubbing_direction == 0) {
1964                 /* first move */
1965                 _session->request_locate (frame, false);
1966                 _session->request_transport_speed (0.1);
1967                 scrubbing_direction = 1;
1968
1969         } else {
1970
1971                 if (last_scrub_x > current_x) {
1972
1973                         /* pointer moved to the left */
1974
1975                         if (scrubbing_direction > 0) {
1976
1977                                 /* we reversed direction to go backwards */
1978
1979                                 scrub_reversals++;
1980                                 scrub_reverse_distance += (int) (last_scrub_x - current_x);
1981
1982                         } else {
1983
1984                                 /* still moving to the left (backwards) */
1985
1986                                 scrub_reversals = 0;
1987                                 scrub_reverse_distance = 0;
1988
1989                                 delta = 0.01 * (last_scrub_x - current_x);
1990                                 _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1991                         }
1992
1993                 } else {
1994                         /* pointer moved to the right */
1995
1996                         if (scrubbing_direction < 0) {
1997                                 /* we reversed direction to go forward */
1998
1999                                 scrub_reversals++;
2000                                 scrub_reverse_distance += (int) (current_x - last_scrub_x);
2001
2002                         } else {
2003                                 /* still moving to the right */
2004
2005                                 scrub_reversals = 0;
2006                                 scrub_reverse_distance = 0;
2007
2008                                 delta = 0.01 * (current_x - last_scrub_x);
2009                                 _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
2010                         }
2011                 }
2012
2013                 /* if there have been more than 2 opposite motion moves detected, or one that moves
2014                    back more than 10 pixels, reverse direction
2015                 */
2016
2017                 if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
2018
2019                         if (scrubbing_direction > 0) {
2020                                 /* was forwards, go backwards */
2021                                 _session->request_transport_speed (-0.1);
2022                                 scrubbing_direction = -1;
2023                         } else {
2024                                 /* was backwards, go forwards */
2025                                 _session->request_transport_speed (0.1);
2026                                 scrubbing_direction = 1;
2027                         }
2028
2029                         scrub_reverse_distance = 0;
2030                         scrub_reversals = 0;
2031                 }
2032         }
2033
2034         last_scrub_x = current_x;
2035 }
2036
2037 bool
2038 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
2039 {
2040         _last_motion_y = event->motion.y;
2041
2042         if (event->motion.is_hint) {
2043                 gint x, y;
2044
2045                 /* We call this so that MOTION_NOTIFY events continue to be
2046                    delivered to the canvas. We need to do this because we set
2047                    Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
2048                    the density of the events, at the expense of a round-trip
2049                    to the server. Given that this will mostly occur on cases
2050                    where DISPLAY = :0.0, and given the cost of what the motion
2051                    event might do, its a good tradeoff.
2052                 */
2053
2054                 track_canvas->get_pointer (x, y);
2055         }
2056
2057         if (current_stepping_trackview) {
2058                 /* don't keep the persistent stepped trackview if the mouse moves */
2059                 current_stepping_trackview = 0;
2060                 step_timeout.disconnect ();
2061         }
2062
2063         if (_session && _session->actively_recording()) {
2064                 /* Sorry. no dragging stuff around while we record */
2065                 return true;
2066         }
2067
2068         JoinObjectRangeState const old = _join_object_range_state;
2069         update_join_object_range_location (event->motion.x, event->motion.y);
2070         if (_join_object_range_state != old) {
2071                 set_canvas_cursor ();
2072         }
2073
2074         if (_over_region_trim_target) {
2075                 set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
2076         }
2077
2078         bool handled = false;
2079         if (_drags->active ()) {
2080                 handled = _drags->motion_handler (event, from_autoscroll);
2081         }
2082
2083         if (!handled) {
2084                 return false;
2085         }
2086
2087         track_canvas_motion (event);
2088         return true;
2089 }
2090
2091 bool
2092 Editor::can_remove_control_point (ArdourCanvas::Item* item)
2093 {
2094         ControlPoint* control_point;
2095
2096         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2097                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2098                 /*NOTREACHED*/
2099         }
2100
2101         AutomationLine& line = control_point->line ();
2102         if (dynamic_cast<AudioRegionGainLine*> (&line)) {
2103                 /* we shouldn't remove the first or last gain point in region gain lines */
2104                 if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
2105                         return false;
2106                 }
2107         }
2108
2109         return true;
2110 }
2111
2112 void
2113 Editor::remove_control_point (ArdourCanvas::Item* item)
2114 {
2115         if (!can_remove_control_point (item)) {
2116                 return;
2117         }
2118
2119         ControlPoint* control_point;
2120
2121         if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
2122                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2123                 /*NOTREACHED*/
2124         }
2125
2126         control_point->line().remove_point (*control_point);
2127 }
2128
2129 void
2130 Editor::edit_control_point (ArdourCanvas::Item* item)
2131 {
2132         ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
2133
2134         if (p == 0) {
2135                 fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
2136                 /*NOTREACHED*/
2137         }
2138
2139         ControlPointDialog d (p);
2140         d.set_position (Gtk::WIN_POS_MOUSE);
2141         ensure_float (d);
2142
2143         if (d.run () != RESPONSE_ACCEPT) {
2144                 return;
2145         }
2146
2147         p->line().modify_point_y (*p, d.get_y_fraction ());
2148 }
2149
2150 void
2151 Editor::edit_note (ArdourCanvas::Item* item)
2152 {
2153         ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2154         assert (e);
2155
2156         EditNoteDialog d (&e->region_view(), e);
2157         d.set_position (Gtk::WIN_POS_MOUSE);
2158         ensure_float (d);
2159
2160         d.run ();
2161 }
2162
2163
2164 void
2165 Editor::visible_order_range (int* low, int* high) const
2166 {
2167         *low = TimeAxisView::max_order ();
2168         *high = 0;
2169
2170         for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
2171
2172                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
2173
2174                 if (!rtv->hidden()) {
2175
2176                         if (*high < rtv->order()) {
2177                                 *high = rtv->order ();
2178                         }
2179
2180                         if (*low > rtv->order()) {
2181                                 *low = rtv->order ();
2182                         }
2183                 }
2184         }
2185 }
2186
2187 void
2188 Editor::region_view_item_click (AudioRegionView& rv, GdkEventButton* event)
2189 {
2190         /* Either add to or set the set the region selection, unless
2191            this is an alignment click (control used)
2192         */
2193
2194         if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
2195                 TimeAxisView* tv = &rv.get_time_axis_view();
2196                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
2197                 double speed = 1.0;
2198                 if (rtv && rtv->is_track()) {
2199                         speed = rtv->track()->speed();
2200                 }
2201
2202                 framepos_t where = get_preferred_edit_position();
2203
2204                 if (where >= 0) {
2205
2206                         if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2207
2208                                 align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2209
2210                         } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2211
2212                                 align_region (rv.region(), End, (framepos_t) (where * speed));
2213
2214                         } else {
2215
2216                                 align_region (rv.region(), Start, (framepos_t) (where * speed));
2217                         }
2218                 }
2219         }
2220 }
2221
2222 void
2223 Editor::collect_new_region_view (RegionView* rv)
2224 {
2225         latest_regionviews.push_back (rv);
2226 }
2227
2228 void
2229 Editor::collect_and_select_new_region_view (RegionView* rv)
2230 {
2231         selection->add(rv);
2232         latest_regionviews.push_back (rv);
2233 }
2234
2235 void
2236 Editor::cancel_selection ()
2237 {
2238         for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2239                 (*i)->hide_selection ();
2240         }
2241
2242         selection->clear ();
2243         clicked_selection = 0;
2244 }
2245
2246
2247 void
2248 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2249 {
2250         RegionView* rv = clicked_regionview;
2251
2252         /* Choose action dependant on which button was pressed */
2253         switch (event->button.button) {
2254         case 1:
2255                 begin_reversible_command (_("start point trim"));
2256
2257                 if (selection->selected (rv)) {
2258                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2259                              i != selection->regions.by_layer().end(); ++i)
2260                         {
2261                                 if ( (*i) == NULL){
2262                                     cerr << "region view contains null region" << endl;
2263                                 }
2264
2265                                 if (!(*i)->region()->locked()) {
2266                                         (*i)->region()->clear_changes ();
2267                                         (*i)->region()->trim_front (new_bound);
2268                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2269                                 }
2270                         }
2271
2272                 } else {
2273                         if (!rv->region()->locked()) {
2274                                 rv->region()->clear_changes ();
2275                                 rv->region()->trim_front (new_bound);
2276                                 _session->add_command(new StatefulDiffCommand (rv->region()));
2277                         }
2278                 }
2279
2280                 commit_reversible_command();
2281
2282                 break;
2283         case 2:
2284                 begin_reversible_command (_("End point trim"));
2285
2286                 if (selection->selected (rv)) {
2287
2288                         for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2289                         {
2290                                 if (!(*i)->region()->locked()) {
2291                                         (*i)->region()->clear_changes();
2292                                         (*i)->region()->trim_end (new_bound);
2293                                         _session->add_command(new StatefulDiffCommand ((*i)->region()));
2294                                 }
2295                         }
2296
2297                 } else {
2298
2299                         if (!rv->region()->locked()) {
2300                                 rv->region()->clear_changes ();
2301                                 rv->region()->trim_end (new_bound);
2302                                 _session->add_command (new StatefulDiffCommand (rv->region()));
2303                         }
2304                 }
2305
2306                 commit_reversible_command();
2307
2308                 break;
2309         default:
2310                 break;
2311         }
2312 }
2313
2314 void
2315 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2316 {
2317         Marker* marker;
2318         bool is_start;
2319
2320         if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2321                 fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2322                 /*NOTREACHED*/
2323         }
2324
2325         Location* location = find_location_from_marker (marker, is_start);
2326         location->set_hidden (true, this);
2327 }
2328
2329
2330 void
2331 Editor::reposition_zoom_rect (framepos_t start, framepos_t end)
2332 {
2333         double x1 = frame_to_pixel (start);
2334         double x2 = frame_to_pixel (end);
2335         double y2 = full_canvas_height - 1.0;
2336
2337         zoom_rect->property_x1() = x1;
2338         zoom_rect->property_y1() = 1.0;
2339         zoom_rect->property_x2() = x2;
2340         zoom_rect->property_y2() = y2;
2341 }
2342
2343
2344 gint
2345 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2346 {
2347         using namespace Gtkmm2ext;
2348
2349         ArdourPrompter prompter (false);
2350
2351         prompter.set_prompt (_("Name for region:"));
2352         prompter.set_initial_text (clicked_regionview->region()->name());
2353         prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2354         prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2355         prompter.show_all ();
2356         switch (prompter.run ()) {
2357         case Gtk::RESPONSE_ACCEPT:
2358                 string str;
2359                 prompter.get_result(str);
2360                 if (str.length()) {
2361                         clicked_regionview->region()->set_name (str);
2362                 }
2363                 break;
2364         }
2365         return true;
2366 }
2367
2368
2369 void
2370 Editor::mouse_brush_insert_region (RegionView* rv, framepos_t pos)
2371 {
2372         /* no brushing without a useful snap setting */
2373
2374         switch (_snap_mode) {
2375         case SnapMagnetic:
2376                 return; /* can't work because it allows region to be placed anywhere */
2377         default:
2378                 break; /* OK */
2379         }
2380
2381         switch (_snap_type) {
2382         case SnapToMark:
2383                 return;
2384
2385         default:
2386                 break;
2387         }
2388
2389         /* don't brush a copy over the original */
2390
2391         if (pos == rv->region()->position()) {
2392                 return;
2393         }
2394
2395         RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2396
2397         if (rtv == 0 || !rtv->is_track()) {
2398                 return;
2399         }
2400
2401         boost::shared_ptr<Playlist> playlist = rtv->playlist();
2402         double speed = rtv->track()->speed();
2403
2404         playlist->clear_changes ();
2405         boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2406         playlist->add_region (new_region, (framepos_t) (pos * speed));
2407         _session->add_command (new StatefulDiffCommand (playlist));
2408
2409         // playlist is frozen, so we have to update manually XXX this is disgusting
2410
2411         playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2412 }
2413
2414 gint
2415 Editor::track_height_step_timeout ()
2416 {
2417         if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2418                 current_stepping_trackview = 0;
2419                 return false;
2420         }
2421         return true;
2422 }
2423
2424 void
2425 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2426 {
2427         assert (region_view);
2428
2429         if (!region_view->region()->playlist()) {
2430                 return;
2431         }
2432
2433         _region_motion_group->raise_to_top ();
2434
2435         if (Config->get_edit_mode() == Splice) {
2436                 _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2437         } else {
2438                 RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2439                 _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, false));
2440         }
2441
2442         /* sync the canvas to what we think is its current state */
2443         update_canvas_now();
2444 }
2445
2446 void
2447 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2448 {
2449         assert (region_view);
2450
2451         if (!region_view->region()->playlist()) {
2452                 return;
2453         }
2454
2455         _region_motion_group->raise_to_top ();
2456
2457         RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2458         _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), false, true));
2459 }
2460
2461 void
2462 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2463 {
2464         assert (region_view);
2465
2466         if (!region_view->region()->playlist()) {
2467                 return;
2468         }
2469
2470         if (Config->get_edit_mode() == Splice) {
2471                 return;
2472         }
2473
2474         RegionSelection s = get_equivalent_regions (selection->regions, ARDOUR::Properties::edit.property_id);
2475         _drags->add (new RegionMoveDrag (this, item, region_view, s.by_layer(), true, false));
2476
2477         begin_reversible_command (Operations::drag_region_brush);
2478 }
2479
2480 /** Start a grab where a time range is selected, track(s) are selected, and the
2481  *  user clicks and drags a region with a modifier in order to create a new region containing
2482  *  the section of the clicked region that lies within the time range.
2483  */
2484 void
2485 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2486 {
2487         if (clicked_regionview == 0) {
2488                 return;
2489         }
2490
2491         /* lets try to create new Region for the selection */
2492
2493         vector<boost::shared_ptr<Region> > new_regions;
2494         create_region_from_selection (new_regions);
2495
2496         if (new_regions.empty()) {
2497                 return;
2498         }
2499
2500         /* XXX fix me one day to use all new regions */
2501
2502         boost::shared_ptr<Region> region (new_regions.front());
2503
2504         /* add it to the current stream/playlist.
2505
2506            tricky: the streamview for the track will add a new regionview. we will
2507            catch the signal it sends when it creates the regionview to
2508            set the regionview we want to then drag.
2509         */
2510
2511         latest_regionviews.clear();
2512         sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2513
2514         /* A selection grab currently creates two undo/redo operations, one for
2515            creating the new region and another for moving it.
2516         */
2517
2518         begin_reversible_command (Operations::selection_grab);
2519
2520         boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2521
2522         playlist->clear_changes ();
2523         clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2524         _session->add_command(new StatefulDiffCommand (playlist));
2525
2526         commit_reversible_command ();
2527
2528         c.disconnect ();
2529
2530         if (latest_regionviews.empty()) {
2531                 /* something went wrong */
2532                 return;
2533         }
2534
2535         /* we need to deselect all other regionviews, and select this one
2536            i'm ignoring undo stuff, because the region creation will take care of it
2537         */
2538         selection->set (latest_regionviews);
2539
2540         _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2541 }
2542
2543 void
2544 Editor::escape ()
2545 {
2546         if (_drags->active ()) {
2547                 _drags->abort ();
2548         } else {
2549                 selection->clear ();
2550         }
2551 }
2552
2553 void
2554 Editor::set_internal_edit (bool yn)
2555 {
2556         _internal_editing = yn;
2557
2558         if (yn) {
2559                 mouse_select_button.set_image (::get_icon("midi_tool_pencil"));
2560                 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Draw/Edit MIDI Notes"));
2561                 mouse_mode_toggled (mouse_mode);
2562
2563                 pre_internal_mouse_mode = mouse_mode;
2564
2565                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2566                         (*i)->enter_internal_edit_mode ();
2567                 }
2568
2569         } else {
2570
2571                 mouse_select_button.set_image (::get_icon("tool_range"));
2572                 ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Select/Move Ranges"));
2573                 mouse_mode_toggled (mouse_mode); // sets cursor
2574
2575                 for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2576                         (*i)->leave_internal_edit_mode ();
2577                 }
2578
2579                 if (mouse_mode == MouseRange && pre_internal_mouse_mode != MouseRange) {
2580                         /* we were drawing .. flip back to something sensible */
2581                         set_mouse_mode (pre_internal_mouse_mode);
2582                 }
2583         }
2584 }
2585
2586 /** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
2587  *  used by the `join object/range' tool mode.
2588  */
2589 void
2590 Editor::update_join_object_range_location (double /*x*/, double y)
2591 {
2592         /* XXX: actually, this decides based on whether the mouse is in the top or bottom half of a RouteTimeAxisView;
2593            entered_{track,regionview} is not always setup (e.g. if the mouse is over a TimeSelection), and to get a Region
2594            that we're over requires searching the playlist.
2595         */
2596
2597         if (join_object_range_button.get_active() == false || (mouse_mode != MouseRange && mouse_mode != MouseObject)) {
2598                 _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2599                 return;
2600         }
2601
2602         if (mouse_mode == MouseObject) {
2603                 _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2604         } else if (mouse_mode == MouseRange) {
2605                 _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2606         }
2607
2608         /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
2609         pair<TimeAxisView*, int> tvp = trackview_by_y_position (y + vertical_adjustment.get_value() - canvas_timebars_vsize);
2610
2611         if (tvp.first) {
2612
2613                 RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
2614                 if (rtv) {
2615
2616                         double cx = 0;
2617                         double cy = y;
2618                         rtv->canvas_display()->w2i (cx, cy);
2619
2620                         double const c = cy / rtv->view()->child_height();
2621                         double d;
2622                         double const f = modf (c, &d);
2623
2624                         _join_object_range_state = f < 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2625                 }
2626         }
2627 }
2628
2629 Editing::MouseMode
2630 Editor::effective_mouse_mode () const
2631 {
2632         if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2633                 return MouseObject;
2634         } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2635                 return MouseRange;
2636         }
2637
2638         return mouse_mode;
2639 }
2640
2641 void
2642 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2643 {
2644         ArdourCanvas::CanvasNoteEvent* e = dynamic_cast<ArdourCanvas::CanvasNoteEvent*> (item);
2645         assert (e);
2646
2647         e->region_view().delete_note (e->note ());
2648 }
2649
2650 void
2651 Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
2652 {
2653         assert (rv);
2654
2655         ArdourCanvas::Group* g = rv->get_canvas_group ();
2656         ArdourCanvas::Group* p = g->get_parent_group ();
2657
2658         /* Compute x in region view parent coordinates */
2659         double dy = 0;
2660         p->w2i (x, dy);
2661
2662         double x1, x2, y1, y2;
2663         g->get_bounds (x1, y1, x2, y2);
2664
2665         /* Halfway across the region */
2666         double const h = (x1 + x2) / 2;
2667
2668         Trimmable::CanTrim ct = rv->region()->can_trim ();
2669         if (x <= h) {
2670                 if (ct & Trimmable::FrontTrimEarlier) {
2671                         set_canvas_cursor (_cursors->left_side_trim);
2672                 } else {
2673                         set_canvas_cursor (_cursors->left_side_trim_right_only);
2674                 }
2675         } else {
2676                 if (ct & Trimmable::EndTrimLater) {
2677                         set_canvas_cursor (_cursors->right_side_trim);
2678                 } else {
2679                         set_canvas_cursor (_cursors->right_side_trim_left_only);
2680                 }
2681         }
2682 }
2683
2684 /** Obtain the pointer position in world coordinates */
2685 void
2686 Editor::get_pointer_position (double& x, double& y) const
2687 {
2688         int px, py;
2689         track_canvas->get_pointer (px, py);
2690         track_canvas->window_to_world (px, py, x, y);
2691 }