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