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