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