93c0abea356bf2be94e2ae58f9525151e8b067f9
[ardour.git] / gtk2_ardour / editor_canvas_events.cc
1 /*
2     Copyright (C) 2000 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 <cstdlib>
21 #include <cmath>
22 #include <algorithm>
23 #include <typeinfo>
24
25 #include "pbd/stacktrace.h"
26
27 #include "ardour/midi_region.h"
28 #include "ardour/region_factory.h"
29 #include "ardour/profile.h"
30
31 #include "canvas/canvas.h"
32 #include "canvas/text.h"
33 #include "canvas/scroll_group.h"
34
35 #include "editor.h"
36 #include "keyboard.h"
37 #include "public_editor.h"
38 #include "ardour_ui.h"
39 #include "audio_region_view.h"
40 #include "audio_streamview.h"
41 #include "audio_time_axis.h"
42 #include "region_gain_line.h"
43 #include "automation_line.h"
44 #include "automation_time_axis.h"
45 #include "automation_line.h"
46 #include "control_point.h"
47 #include "editor_drag.h"
48 #include "midi_time_axis.h"
49 #include "editor_regions.h"
50 #include "verbose_cursor.h"
51
52 #include "i18n.h"
53
54 using namespace std;
55 using namespace ARDOUR;
56 using namespace PBD;
57 using namespace Gtk;
58 using namespace ArdourCanvas;
59
60 using Gtkmm2ext::Keyboard;
61
62 bool
63 Editor::track_canvas_scroll (GdkEventScroll* ev)
64 {
65         if (Keyboard::some_magic_widget_has_focus()) {
66                 return false;
67         }
68         
69         framepos_t xdelta;
70         int direction = ev->direction;
71
72         /* this event arrives without transformation by the canvas, so we have
73          * to transform the coordinates to be able to look things up.
74          */
75
76         Duple event_coords = _track_canvas->window_to_canvas (Duple (ev->x, ev->y));
77
78   retry:
79         switch (direction) {
80         case GDK_SCROLL_UP:
81                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
82                         //for mouse-wheel zoom, force zoom-focus to mouse
83                         Editing::ZoomFocus temp_focus = zoom_focus;
84                         zoom_focus = Editing::ZoomFocusMouse;
85                         temporal_zoom_step (false);
86                         zoom_focus = temp_focus;
87                         return true;
88                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
89                         direction = GDK_SCROLL_LEFT;
90                         goto retry;
91                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
92                         if (!current_stepping_trackview) {
93                                 step_timeout = Glib::signal_timeout().connect (sigc::mem_fun(*this, &Editor::track_height_step_timeout), 500);
94                                 std::pair<TimeAxisView*, int> const p = trackview_by_y_position (event_coords.y, false);
95                                 current_stepping_trackview = p.first;
96                                 if (!current_stepping_trackview) {
97                                         return false;
98                                 }
99                         }
100                         last_track_height_step_timestamp = get_microseconds();
101                         current_stepping_trackview->step_height (false);
102                         return true;
103                 } else {
104                         scroll_up_one_track ();
105                         return true;
106                 }
107                 break;
108
109         case GDK_SCROLL_DOWN:
110                 if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
111                         //for mouse-wheel zoom, force zoom-focus to mouse
112                         Editing::ZoomFocus temp_focus = zoom_focus;
113                         zoom_focus = Editing::ZoomFocusMouse;
114                         temporal_zoom_step (true);
115                         zoom_focus = temp_focus;
116                         return true;
117                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
118                         direction = GDK_SCROLL_RIGHT;
119                         goto retry;
120                 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
121                         if (!current_stepping_trackview) {
122                                 step_timeout = Glib::signal_timeout().connect (sigc::mem_fun(*this, &Editor::track_height_step_timeout), 500);
123                                 std::pair<TimeAxisView*, int> const p = trackview_by_y_position (event_coords.y, false);
124                                 current_stepping_trackview = p.first;
125                                 if (!current_stepping_trackview) {
126                                         return false;
127                                 }
128                         }
129                         last_track_height_step_timestamp = get_microseconds();
130                         current_stepping_trackview->step_height (true);
131                         return true;
132                 } else {
133                         scroll_down_one_track ();
134                         return true;
135                 }
136                 break;
137
138         case GDK_SCROLL_LEFT:
139                 xdelta = (current_page_samples() / 8);
140                 if (leftmost_frame > xdelta) {
141                         reset_x_origin (leftmost_frame - xdelta);
142                 } else {
143                         reset_x_origin (0);
144                 }
145                 break;
146
147         case GDK_SCROLL_RIGHT:
148                 xdelta = (current_page_samples() / 8);
149                 if (max_framepos - xdelta > leftmost_frame) {
150                         reset_x_origin (leftmost_frame + xdelta);
151                 } else {
152                         reset_x_origin (max_framepos - current_page_samples());
153                 }
154                 break;
155
156         default:
157                 /* what? */
158                 break;
159         }
160
161         return false;
162 }
163
164 bool
165 Editor::canvas_scroll_event (GdkEventScroll *event, bool from_canvas)
166 {
167         if (from_canvas) {
168                 boost::optional<ArdourCanvas::Rect> rulers = _time_markers_group->bounding_box();
169                 if (rulers && rulers->contains (Duple (event->x, event->y))) {
170                         return canvas_ruler_event ((GdkEvent*) event, timecode_ruler, TimecodeRulerItem);
171                 }
172         }
173
174         _track_canvas->grab_focus();
175         return track_canvas_scroll (event);
176 }
177
178 bool
179 Editor::track_canvas_button_press_event (GdkEventButton */*event*/)
180 {
181         selection->clear ();
182         _track_canvas->grab_focus();
183         return false;
184 }
185
186 bool
187 Editor::track_canvas_button_release_event (GdkEventButton *event)
188 {
189         if (_drags->active ()) {
190                 _drags->end_grab ((GdkEvent*) event);
191         }
192         return false;
193 }
194
195 bool
196 Editor::track_canvas_motion_notify_event (GdkEventMotion */*event*/)
197 {
198         int x, y;
199         /* keep those motion events coming */
200         _track_canvas->get_pointer (x, y);
201         return false;
202 }
203
204 bool
205 Editor::typed_event (ArdourCanvas::Item* item, GdkEvent *event, ItemType type)
206 {
207         gint ret = FALSE;
208
209         switch (event->type) {
210         case GDK_BUTTON_PRESS:
211         case GDK_2BUTTON_PRESS:
212         case GDK_3BUTTON_PRESS:
213                 ret = button_press_handler (item, event, type);
214                 break;
215         case GDK_BUTTON_RELEASE:
216                 ret = button_release_handler (item, event, type);
217                 break;
218         case GDK_MOTION_NOTIFY:
219                 ret = motion_handler (item, event);
220                 break;
221
222         case GDK_ENTER_NOTIFY:
223                 ret = enter_handler (item, event, type);
224                 break;
225
226         case GDK_LEAVE_NOTIFY:
227                 ret = leave_handler (item, event, type);
228                 break;
229
230         case GDK_KEY_PRESS:
231                 ret = key_press_handler (item, event, type);
232                 break;
233
234         case GDK_KEY_RELEASE:
235                 ret = key_release_handler (item, event, type);
236                 break;
237
238         default:
239                 break;
240         }
241         return ret;
242 }
243
244 bool
245 Editor::canvas_region_view_event (GdkEvent *event, ArdourCanvas::Item* item, RegionView *rv)
246 {
247         bool ret = false;
248
249         if (!rv->sensitive ()) {
250                 return false;
251         }
252
253         switch (event->type) {
254         case GDK_BUTTON_PRESS:
255         case GDK_2BUTTON_PRESS:
256         case GDK_3BUTTON_PRESS:
257                 clicked_regionview = rv;
258                 clicked_control_point = 0;
259                 clicked_axisview = &rv->get_time_axis_view();
260                 clicked_routeview = dynamic_cast<RouteTimeAxisView*>(clicked_axisview);
261                 ret = button_press_handler (item, event, RegionItem);
262                 break;
263
264         case GDK_BUTTON_RELEASE:
265                 ret = button_release_handler (item, event, RegionItem);
266                 break;
267
268         case GDK_MOTION_NOTIFY:
269                 ret = motion_handler (item, event);
270                 break;
271
272         case GDK_ENTER_NOTIFY:
273                 set_entered_regionview (rv);
274                 ret = enter_handler (item, event, RegionItem);
275                 break;
276
277         case GDK_LEAVE_NOTIFY:
278                 if (event->crossing.detail != GDK_NOTIFY_INFERIOR) {
279                         set_entered_regionview (0);
280                         ret = leave_handler (item, event, RegionItem);
281                 }
282                 break;
283
284         default:
285                 break;
286         }
287
288         return ret;
289 }
290
291 bool
292 Editor::canvas_wave_view_event (GdkEvent *event, ArdourCanvas::Item* item, RegionView* rv)
293 {
294         /* we only care about enter events here, required for mouse/cursor
295          * tracking. there is a non-linear (non-child/non-parent) relationship
296          * between various components of a regionview and so when we leave one
297          * of them (e.g. a trim handle) and enter another (e.g. the waveview)
298          * no other items get notified. enter/leave handling does not propagate
299          * in the same way as other events, so we need to catch this because
300          * entering (and leaving) the waveview is equivalent to
301          * entering/leaving the regionview (which is why it is passed in as a
302          * third argument).
303          *
304          * And in fact, we really only care about enter events.
305          */
306
307         bool ret = false;
308
309         if (!rv->sensitive ()) {
310                 return false;
311         }
312
313         switch (event->type) {
314         case GDK_ENTER_NOTIFY:
315                 set_entered_regionview (rv);
316                 ret = enter_handler (item, event, WaveItem);
317                 break;
318
319         default:
320                 break;
321         }
322
323         return ret;
324 }        
325
326
327 bool
328 Editor::canvas_stream_view_event (GdkEvent *event, ArdourCanvas::Item* item, RouteTimeAxisView *tv)
329 {
330         bool ret = FALSE;
331
332         switch (event->type) {
333         case GDK_BUTTON_PRESS:
334         case GDK_2BUTTON_PRESS:
335         case GDK_3BUTTON_PRESS:
336                 clicked_regionview = 0;
337                 clicked_control_point = 0;
338                 clicked_axisview = tv;
339                 clicked_routeview = dynamic_cast<RouteTimeAxisView*>(clicked_axisview);
340                 ret = button_press_handler (item, event, StreamItem);
341                 break;
342
343         case GDK_BUTTON_RELEASE:
344                 ret = button_release_handler (item, event, StreamItem);
345                 break;
346
347         case GDK_MOTION_NOTIFY:
348                 ret = motion_handler (item, event);
349                 break;
350
351         case GDK_ENTER_NOTIFY:
352                 set_entered_track (tv);
353                 ret = enter_handler (item, event, StreamItem);
354                 break;
355
356         case GDK_LEAVE_NOTIFY:
357                 if (event->crossing.detail != GDK_NOTIFY_INFERIOR) {
358                         set_entered_track (0);
359                 }
360                 ret = leave_handler (item, event, StreamItem);
361                 break;
362
363         default:
364                 break;
365         }
366
367         return ret;
368 }
369
370 bool
371 Editor::canvas_automation_track_event (GdkEvent *event, ArdourCanvas::Item* item, AutomationTimeAxisView *atv)
372 {
373         bool ret = false;
374
375         switch (event->type) {
376         case GDK_BUTTON_PRESS:
377         case GDK_2BUTTON_PRESS:
378         case GDK_3BUTTON_PRESS:
379                 clicked_regionview = 0;
380                 clicked_control_point = 0;
381                 clicked_axisview = atv;
382                 clicked_routeview = 0;
383                 ret = button_press_handler (item, event, AutomationTrackItem);
384                 break;
385
386         case GDK_BUTTON_RELEASE:
387                 ret = button_release_handler (item, event, AutomationTrackItem);
388                 break;
389
390         case GDK_MOTION_NOTIFY:
391                 ret = motion_handler (item, event);
392                 break;
393
394         case GDK_ENTER_NOTIFY:
395                 ret = enter_handler (item, event, AutomationTrackItem);
396                 break;
397
398         case GDK_LEAVE_NOTIFY:
399                 ret = leave_handler (item, event, AutomationTrackItem);
400                 break;
401
402         default:
403                 break;
404         }
405
406         return ret;
407 }
408
409 bool
410 Editor::canvas_start_xfade_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv)
411 {
412         if (!rv->sensitive()) {
413                 return false;
414         }
415
416         switch (event->type) {
417         case GDK_BUTTON_PRESS:
418                 clicked_regionview = rv;
419                 clicked_control_point = 0;
420                 clicked_axisview = &rv->get_time_axis_view();
421                 clicked_routeview = dynamic_cast<RouteTimeAxisView*>(clicked_axisview);
422                 if (event->button.button == 3) {
423                         return button_press_handler (item, event, StartCrossFadeItem);
424                 }
425                 break;
426
427         case GDK_BUTTON_RELEASE:
428                 if (event->button.button == 3) {
429                         return button_release_handler (item, event, StartCrossFadeItem);
430                 }
431                 break;
432
433         default:
434                 break;
435
436         }
437
438         /* In Mixbus, the crossfade area is used to trim the region while leaving the fade anchor intact (see preserve_fade_anchor)*/
439         /* however in A3 this feature is unfinished, and it might be better to do it with a modifier-trim instead, anyway */
440         /* if we return RegionItem here then we avoid the issue until it is resolved later */
441         return typed_event (item, event, RegionItem); // StartCrossFadeItem);
442 }
443
444 bool
445 Editor::canvas_end_xfade_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv)
446 {
447         if (!rv->sensitive()) {
448                 return false;
449         }
450
451         switch (event->type) {
452         case GDK_BUTTON_PRESS:
453                 clicked_regionview = rv;
454                 clicked_control_point = 0;
455                 clicked_axisview = &rv->get_time_axis_view();
456                 clicked_routeview = dynamic_cast<RouteTimeAxisView*>(clicked_axisview);
457                 if (event->button.button == 3) {
458                         return button_press_handler (item, event, EndCrossFadeItem);
459                 }
460                 break;
461
462         case GDK_BUTTON_RELEASE:
463                 if (event->button.button == 3) {
464                         return button_release_handler (item, event, EndCrossFadeItem);
465                 }
466                 break;
467
468         default:
469                 break;
470
471         }
472
473         /* In Mixbus, the crossfade area is used to trim the region while leaving the fade anchor intact (see preserve_fade_anchor)*/
474         /* however in A3 this feature is unfinished, and it might be better to do it with a modifier-trim instead, anyway */
475         /* if we return RegionItem here then we avoid the issue until it is resolved later */
476         return typed_event (item, event, RegionItem); // EndCrossFadeItem);
477 }
478
479 bool
480 Editor::canvas_fade_in_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv)
481 {
482         /* we handle only button 3 press/release events */
483
484         if (!rv->sensitive()) {
485                 return false;
486         }
487
488         switch (event->type) {
489         case GDK_BUTTON_PRESS:
490                 clicked_regionview = rv;
491                 clicked_control_point = 0;
492                 clicked_axisview = &rv->get_time_axis_view();
493                 clicked_routeview = dynamic_cast<RouteTimeAxisView*>(clicked_axisview);
494                 if (event->button.button == 3) {
495                         return button_press_handler (item, event, FadeInItem);
496                 }
497                 break;
498
499         case GDK_BUTTON_RELEASE:
500                 if (event->button.button == 3) {
501                         return button_release_handler (item, event, FadeInItem);
502                 }
503                 break;
504
505         default:
506                 break;
507
508         }
509
510         /* proxy for the regionview, except enter/leave events */
511
512         if (event->type == GDK_ENTER_NOTIFY || event->type == GDK_LEAVE_NOTIFY) {
513                 return true;
514         } else {
515                 return canvas_region_view_event (event, rv->get_canvas_group(), rv);
516         }
517 }
518
519 bool
520 Editor::canvas_fade_in_handle_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv, bool trim)
521 {
522         bool ret = false;
523
524         if (!rv->sensitive()) {
525                 return false;
526         }
527
528         switch (event->type) {
529         case GDK_BUTTON_PRESS:
530         case GDK_2BUTTON_PRESS:
531         case GDK_3BUTTON_PRESS:
532                 clicked_regionview = rv;
533                 clicked_control_point = 0;
534                 clicked_axisview = &rv->get_time_axis_view();
535                 clicked_routeview = dynamic_cast<RouteTimeAxisView*>(clicked_axisview);
536                 ret = button_press_handler (item, event, trim ? FadeInTrimHandleItem : FadeInHandleItem);
537                 break;
538
539         case GDK_BUTTON_RELEASE:
540                 ret = button_release_handler (item, event, trim ? FadeInTrimHandleItem : FadeInHandleItem);
541                 maybe_locate_with_edit_preroll ( rv->region()->position() );
542                 break;
543
544         case GDK_MOTION_NOTIFY:
545                 ret = motion_handler (item, event);
546                 break;
547
548         case GDK_ENTER_NOTIFY:
549                 ret = enter_handler (item, event, trim ? FadeInTrimHandleItem : FadeInHandleItem);
550                 break;
551
552         case GDK_LEAVE_NOTIFY:
553                 ret = leave_handler (item, event, trim ? FadeInTrimHandleItem : FadeInHandleItem);
554                 break;
555
556         default:
557                 break;
558         }
559
560         return ret;
561 }
562
563 bool
564 Editor::canvas_fade_out_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv)
565 {
566         /* we handle only button 3 press/release events */
567
568         if (!rv->sensitive()) {
569                 return false;
570         }
571
572         switch (event->type) {
573         case GDK_BUTTON_PRESS:
574                 clicked_regionview = rv;
575                 clicked_control_point = 0;
576                 clicked_axisview = &rv->get_time_axis_view();
577                 clicked_routeview = dynamic_cast<RouteTimeAxisView*>(clicked_axisview);
578                 if (event->button.button == 3) {
579                         return button_press_handler (item, event, FadeOutItem);
580                 }
581                 break;
582
583         case GDK_BUTTON_RELEASE:
584                 if (event->button.button == 3) {
585                         return button_release_handler (item, event, FadeOutItem);
586                 }
587                 break;
588
589         default:
590                 break;
591
592         }
593
594         /* proxy for the regionview, except enter/leave events */
595
596         if (event->type == GDK_ENTER_NOTIFY || event->type == GDK_LEAVE_NOTIFY) {
597                 return true;
598         } else {
599                 return canvas_region_view_event (event, rv->get_canvas_group(), rv);
600         }
601 }
602
603 bool
604 Editor::canvas_fade_out_handle_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv, bool trim)
605 {
606         bool ret = false;
607
608         if (!rv->sensitive()) {
609                 return false;
610         }
611
612         switch (event->type) {
613         case GDK_BUTTON_PRESS:
614         case GDK_2BUTTON_PRESS:
615         case GDK_3BUTTON_PRESS:
616                 clicked_regionview = rv;
617                 clicked_control_point = 0;
618                 clicked_axisview = &rv->get_time_axis_view();
619                 clicked_routeview = dynamic_cast<RouteTimeAxisView*>(clicked_axisview);
620                 ret = button_press_handler (item, event, trim ? FadeOutTrimHandleItem : FadeOutHandleItem);
621                 break;
622
623         case GDK_BUTTON_RELEASE:
624                 ret = button_release_handler (item, event, trim ? FadeOutTrimHandleItem : FadeOutHandleItem);
625                 maybe_locate_with_edit_preroll ( rv->region()->last_frame() - rv->get_fade_out_shape_width() );
626                 break;
627
628         case GDK_MOTION_NOTIFY:
629                 ret = motion_handler (item, event);
630                 break;
631
632         case GDK_ENTER_NOTIFY:
633                 ret = enter_handler (item, event, trim ? FadeOutTrimHandleItem : FadeOutHandleItem);
634                 break;
635
636         case GDK_LEAVE_NOTIFY:
637                 ret = leave_handler (item, event, trim ? FadeOutTrimHandleItem : FadeOutHandleItem);
638                 break;
639
640         default:
641                 break;
642         }
643
644         return ret;
645 }
646
647 struct DescendingRegionLayerSorter {
648     bool operator()(boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
649             return a->layer() > b->layer();
650     }
651 };
652
653 bool
654 Editor::canvas_control_point_event (GdkEvent *event, ArdourCanvas::Item* item, ControlPoint* cp)
655 {
656         switch (event->type) {
657         case GDK_BUTTON_PRESS:
658         case GDK_2BUTTON_PRESS:
659         case GDK_3BUTTON_PRESS:
660                 clicked_control_point = cp;
661                 clicked_axisview = &cp->line().trackview;
662                 clicked_routeview = dynamic_cast<RouteTimeAxisView*>(clicked_axisview);
663                 clicked_regionview = 0;
664                 break;
665
666         case GDK_SCROLL_UP:
667                 break;
668
669         case GDK_SCROLL_DOWN:
670                 break;
671
672         default:
673                 break;
674         }
675
676         return typed_event (item, event, ControlPointItem);
677 }
678
679 bool
680 Editor::canvas_line_event (GdkEvent *event, ArdourCanvas::Item* item, AutomationLine* al)
681 {
682         ItemType type;
683
684         if (dynamic_cast<AudioRegionGainLine*> (al) != 0) {
685                 type = GainLineItem;
686         } else {
687                 type = AutomationLineItem;
688         }
689
690         return typed_event (item, event, type);
691 }
692
693 bool
694 Editor::canvas_selection_rect_event (GdkEvent *event, ArdourCanvas::Item* item, SelectionRect* rect)
695 {
696         bool ret = false;
697
698         switch (event->type) {
699         case GDK_BUTTON_PRESS:
700         case GDK_2BUTTON_PRESS:
701         case GDK_3BUTTON_PRESS:
702                 clicked_selection = rect->id;
703                 ret = button_press_handler (item, event, SelectionItem);
704                 break;
705         case GDK_BUTTON_RELEASE:
706                 ret = button_release_handler (item, event, SelectionItem);
707                 break;
708         case GDK_MOTION_NOTIFY:
709                 ret = motion_handler (item, event);
710                 break;
711                 /* Don't need these at the moment. */
712         case GDK_ENTER_NOTIFY:
713                 ret = enter_handler (item, event, SelectionItem);
714                 break;
715
716         case GDK_LEAVE_NOTIFY:
717                 ret = leave_handler (item, event, SelectionItem);
718                 break;
719
720         default:
721                 break;
722         }
723
724         return ret;
725 }
726
727 bool
728 Editor::canvas_selection_start_trim_event (GdkEvent *event, ArdourCanvas::Item* item, SelectionRect* rect)
729 {
730         bool ret = false;
731
732         switch (event->type) {
733         case GDK_BUTTON_PRESS:
734         case GDK_2BUTTON_PRESS:
735         case GDK_3BUTTON_PRESS:
736                 clicked_selection = rect->id;
737                 ret = button_press_handler (item, event, StartSelectionTrimItem);
738                 break;
739         case GDK_BUTTON_RELEASE:
740                 ret = button_release_handler (item, event, StartSelectionTrimItem);
741                 break;
742         case GDK_MOTION_NOTIFY:
743                 ret = motion_handler (item, event);
744                 break;
745         case GDK_ENTER_NOTIFY:
746                 ret = enter_handler (item, event, StartSelectionTrimItem);
747                 break;
748
749         case GDK_LEAVE_NOTIFY:
750                 ret = leave_handler (item, event, StartSelectionTrimItem);
751                 break;
752
753         default:
754                 break;
755         }
756
757         return ret;
758 }
759
760 bool
761 Editor::canvas_selection_end_trim_event (GdkEvent *event, ArdourCanvas::Item* item, SelectionRect* rect)
762 {
763         bool ret = false;
764
765         switch (event->type) {
766         case GDK_BUTTON_PRESS:
767         case GDK_2BUTTON_PRESS:
768         case GDK_3BUTTON_PRESS:
769                 clicked_selection = rect->id;
770                 ret = button_press_handler (item, event, EndSelectionTrimItem);
771                 break;
772         case GDK_BUTTON_RELEASE:
773                 ret = button_release_handler (item, event, EndSelectionTrimItem);
774                 break;
775         case GDK_MOTION_NOTIFY:
776                 ret = motion_handler (item, event);
777                 break;
778         case GDK_ENTER_NOTIFY:
779                 ret = enter_handler (item, event, EndSelectionTrimItem);
780                 break;
781
782         case GDK_LEAVE_NOTIFY:
783                 ret = leave_handler (item, event, EndSelectionTrimItem);
784                 break;
785
786         default:
787                 break;
788         }
789
790         return ret;
791 }
792
793 bool
794 Editor::canvas_frame_handle_event (GdkEvent* event, ArdourCanvas::Item* item, RegionView* rv)
795 {
796         bool ret = false;
797
798         /* frame handles are not active when in internal edit mode, because actual notes
799            might be in the area occupied by the handle - we want them to be editable as normal.
800         */
801
802         if (internal_editing() || !rv->sensitive()) {
803                 return false;
804         }
805
806         /* NOTE: frame handles pretend to be the colored trim bar from an event handling
807            perspective. XXX change this ??
808         */
809
810         ItemType type;
811
812         if (item->get_data ("isleft")) {
813                 type = LeftFrameHandle;
814         } else {
815                 type = RightFrameHandle;
816         }
817
818         switch (event->type) {
819         case GDK_BUTTON_PRESS:
820         case GDK_2BUTTON_PRESS:
821         case GDK_3BUTTON_PRESS:
822                 clicked_regionview = rv;
823                 clicked_control_point = 0;
824                 clicked_axisview = &clicked_regionview->get_time_axis_view();
825                 clicked_routeview = dynamic_cast<RouteTimeAxisView*>(clicked_axisview);
826                 ret = button_press_handler (item, event, type);
827                 break;
828         case GDK_BUTTON_RELEASE:
829                 ret = button_release_handler (item, event, type);
830                 break;
831         case GDK_MOTION_NOTIFY:
832                 ret = motion_handler (item, event);
833                 break;
834         case GDK_ENTER_NOTIFY:
835                 ret = enter_handler (item, event, type);
836                 break;
837
838         case GDK_LEAVE_NOTIFY:
839                 ret = leave_handler (item, event, type);
840                 break;
841
842         default:
843                 break;
844         }
845
846         return ret;
847 }
848
849
850 bool
851 Editor::canvas_region_view_name_highlight_event (GdkEvent* event, ArdourCanvas::Item* item, RegionView* rv)
852 {
853         bool ret = false;
854
855         if (!rv->sensitive()) {
856                 return false;
857         }
858
859         switch (event->type) {
860         case GDK_BUTTON_PRESS:
861         case GDK_2BUTTON_PRESS:
862         case GDK_3BUTTON_PRESS:
863                 clicked_regionview = rv;
864                 clicked_control_point = 0;
865                 clicked_axisview = &clicked_regionview->get_time_axis_view();
866                 clicked_routeview = dynamic_cast<RouteTimeAxisView*>(clicked_axisview);
867                 ret = button_press_handler (item, event, RegionViewNameHighlight);
868                 break;
869         case GDK_BUTTON_RELEASE:
870                 ret = button_release_handler (item, event, RegionViewNameHighlight);
871                 break;
872         case GDK_MOTION_NOTIFY:
873                 motion_handler (item, event);
874                 ret = true; // force this to avoid progagating the event into the regionview
875                 break;
876         case GDK_ENTER_NOTIFY:
877                 ret = enter_handler (item, event, RegionViewNameHighlight);
878                 break;
879
880         case GDK_LEAVE_NOTIFY:
881                 ret = leave_handler (item, event, RegionViewNameHighlight);
882                 break;
883
884         default:
885                 break;
886         }
887
888         return ret;
889 }
890
891 bool
892 Editor::canvas_region_view_name_event (GdkEvent *event, ArdourCanvas::Item* item, RegionView* rv)
893 {
894         bool ret = false;
895
896         if (!rv->sensitive()) {
897                 return false;
898         }
899
900         switch (event->type) {
901         case GDK_BUTTON_PRESS:
902         case GDK_2BUTTON_PRESS:
903         case GDK_3BUTTON_PRESS:
904                 clicked_regionview = rv;
905                 clicked_control_point = 0;
906                 clicked_axisview = &clicked_regionview->get_time_axis_view();
907                 clicked_routeview = dynamic_cast<RouteTimeAxisView*>(clicked_axisview);
908                 ret = button_press_handler (item, event, RegionViewName);
909                 break;
910         case GDK_BUTTON_RELEASE:
911                 ret = button_release_handler (item, event, RegionViewName);
912                 break;
913         case GDK_MOTION_NOTIFY:
914                 ret = motion_handler (item, event);
915                 break;
916         case GDK_ENTER_NOTIFY:
917                 ret = enter_handler (item, event, RegionViewName);
918                 break;
919
920         case GDK_LEAVE_NOTIFY:
921                 ret = leave_handler (item, event, RegionViewName);
922                 break;
923
924         default:
925                 break;
926         }
927
928         return ret;
929 }
930
931 bool
932 Editor::canvas_feature_line_event (GdkEvent *event, ArdourCanvas::Item* item, RegionView*)
933 {
934         bool ret = false;
935
936         switch (event->type) {
937         case GDK_BUTTON_PRESS:
938         case GDK_2BUTTON_PRESS:
939         case GDK_3BUTTON_PRESS:
940                 clicked_regionview = 0;
941                 clicked_control_point = 0;
942                 clicked_axisview = 0;
943                 clicked_routeview = 0; //dynamic_cast<RouteTimeAxisView*>(clicked_axisview);
944                 ret = button_press_handler (item, event, FeatureLineItem);
945                 break;
946
947         case GDK_BUTTON_RELEASE:
948                 ret = button_release_handler (item, event, FeatureLineItem);
949                 break;
950
951         case GDK_MOTION_NOTIFY:
952                 ret = motion_handler (item, event);
953                 break;
954
955         case GDK_ENTER_NOTIFY:
956                 ret = enter_handler (item, event, FeatureLineItem);
957                 break;
958
959         case GDK_LEAVE_NOTIFY:
960                 ret = leave_handler (item, event, FeatureLineItem);
961                 break;
962
963         default:
964                 break;
965         }
966
967         return ret;
968 }
969
970 bool
971 Editor::canvas_marker_event (GdkEvent *event, ArdourCanvas::Item* item, Marker* /*marker*/)
972 {
973         return typed_event (item, event, MarkerItem);
974 }
975
976 bool
977 Editor::canvas_marker_bar_event (GdkEvent *event, ArdourCanvas::Item* item)
978 {
979         return typed_event (item, event, MarkerBarItem);
980 }
981
982 bool
983 Editor::canvas_range_marker_bar_event (GdkEvent *event, ArdourCanvas::Item* item)
984 {
985         return typed_event (item, event, RangeMarkerBarItem);
986 }
987
988 bool
989 Editor::canvas_transport_marker_bar_event (GdkEvent *event, ArdourCanvas::Item* item)
990 {
991         return typed_event (item, event, TransportMarkerBarItem);
992 }
993
994 bool
995 Editor::canvas_cd_marker_bar_event (GdkEvent *event, ArdourCanvas::Item* item)
996 {
997         return typed_event (item, event, CdMarkerBarItem);
998 }
999
1000 bool
1001 Editor::canvas_videotl_bar_event (GdkEvent *event, ArdourCanvas::Item* item)
1002 {
1003         return typed_event (item, event, VideoBarItem);
1004 }
1005
1006 bool
1007 Editor::canvas_tempo_marker_event (GdkEvent *event, ArdourCanvas::Item* item, TempoMarker* /*marker*/)
1008 {
1009         return typed_event (item, event, TempoMarkerItem);
1010 }
1011
1012 bool
1013 Editor::canvas_meter_marker_event (GdkEvent *event, ArdourCanvas::Item* item, MeterMarker* /*marker*/)
1014 {
1015         return typed_event (item, event, MeterMarkerItem);
1016 }
1017
1018 bool
1019 Editor::canvas_ruler_event (GdkEvent *event, ArdourCanvas::Item* item, ItemType type)
1020 {
1021         framepos_t xdelta;
1022         bool handled = false;
1023
1024         if (event->type == GDK_SCROLL) {
1025                 
1026                 /* scroll events in the rulers are handled a little differently from
1027                    scrolling elsewhere in the canvas.
1028                 */
1029
1030                 switch (event->scroll.direction) {
1031                 case GDK_SCROLL_UP:
1032                         temporal_zoom_step (false);
1033                         handled = true;
1034                         break;
1035                         
1036                 case GDK_SCROLL_DOWN:
1037                         temporal_zoom_step (true);
1038                         handled = true;
1039                         break;
1040                         
1041                 case GDK_SCROLL_LEFT:
1042                         xdelta = (current_page_samples() / 2);
1043                         if (leftmost_frame > xdelta) {
1044                                 reset_x_origin (leftmost_frame - xdelta);
1045                         } else {
1046                                 reset_x_origin (0);
1047                         }
1048                         handled = true;
1049                         break;
1050                         
1051                 case GDK_SCROLL_RIGHT:
1052                         xdelta = (current_page_samples() / 2);
1053                         if (max_framepos - xdelta > leftmost_frame) {
1054                                 reset_x_origin (leftmost_frame + xdelta);
1055                         } else {
1056                                 reset_x_origin (max_framepos - current_page_samples());
1057                         }
1058                         handled = true;
1059                         break;
1060                         
1061                 default:
1062                         /* what? */
1063                         break;
1064                 }
1065                 return handled;
1066         }
1067
1068         return typed_event (item, event, type);
1069 }
1070
1071 bool
1072 Editor::canvas_tempo_bar_event (GdkEvent *event, ArdourCanvas::Item* item)
1073 {
1074         return typed_event (item, event, TempoBarItem);
1075 }
1076
1077 bool
1078 Editor::canvas_meter_bar_event (GdkEvent *event, ArdourCanvas::Item* item)
1079 {
1080         return typed_event (item, event, MeterBarItem);
1081 }
1082
1083 bool
1084 Editor::canvas_playhead_cursor_event (GdkEvent *event, ArdourCanvas::Item* item)
1085 {
1086         return typed_event (item, event, PlayheadCursorItem);
1087 }
1088
1089 bool
1090 Editor::canvas_note_event (GdkEvent *event, ArdourCanvas::Item* item)
1091 {
1092         if (!internal_editing()) {
1093                 return false;
1094         }
1095
1096         return typed_event (item, event, NoteItem);
1097 }
1098
1099 bool
1100 Editor::canvas_drop_zone_event (GdkEvent* event)
1101 {
1102         GdkEventScroll scroll;  
1103         ArdourCanvas::Duple winpos;
1104         
1105         switch (event->type) {
1106         case GDK_BUTTON_RELEASE:
1107                 if (event->button.button == 1) {
1108                         selection->clear_objects ();
1109                         selection->clear_tracks ();
1110                 }
1111                 break;
1112
1113         case GDK_SCROLL:
1114                 /* convert coordinates back into window space so that
1115                    we can just call canvas_scroll_event().
1116                 */
1117                 winpos = _track_canvas->canvas_to_window (Duple (event->scroll.x, event->scroll.y));
1118                 scroll = event->scroll;
1119                 scroll.x = winpos.x;
1120                 scroll.y = winpos.y;
1121                 return canvas_scroll_event (&scroll, true);
1122                 break;
1123
1124         case GDK_ENTER_NOTIFY:
1125                 return typed_event (_canvas_drop_zone, event, DropZoneItem);
1126
1127         case GDK_LEAVE_NOTIFY:
1128                 return typed_event (_canvas_drop_zone, event, DropZoneItem);
1129
1130         default:
1131                 break;
1132         }
1133
1134         return true;
1135 }
1136
1137 bool
1138 Editor::track_canvas_drag_motion (Glib::RefPtr<Gdk::DragContext> const& context, int x, int y, guint time)
1139 {
1140         boost::shared_ptr<Region> region;
1141         boost::shared_ptr<Region> region_copy;
1142         RouteTimeAxisView* rtav;
1143         GdkEvent event;
1144         double px;
1145         double py;
1146
1147         string target = _track_canvas->drag_dest_find_target (context, _track_canvas->drag_dest_get_target_list());
1148
1149         if (target.empty()) {
1150                 return false;
1151         }
1152
1153         event.type = GDK_MOTION_NOTIFY;
1154         event.button.x = x;
1155         event.button.y = y;
1156         /* assume we're dragging with button 1 */
1157         event.motion.state = Gdk::BUTTON1_MASK;
1158
1159         (void) window_event_sample (&event, &px, &py);
1160
1161         std::pair<TimeAxisView*, int> const tv = trackview_by_y_position (py, false);
1162         bool can_drop = false;
1163         
1164         if (tv.first != 0) {
1165
1166                 /* over a time axis view of some kind */
1167
1168                 rtav = dynamic_cast<RouteTimeAxisView*> (tv.first);
1169                 
1170                 if (rtav != 0 && rtav->is_track ()) {
1171                         /* over a track, not a bus */
1172                         can_drop = true;
1173                 }
1174                         
1175
1176         } else {
1177                 /* not over a time axis view, so drop is possible */
1178                 can_drop = true;
1179         }
1180
1181         if (can_drop) {
1182                 region = _regions->get_dragged_region ();
1183                 
1184                 if (region) {
1185                         
1186                         if ((boost::dynamic_pointer_cast<AudioRegion> (region) != 0 &&
1187                              dynamic_cast<AudioTimeAxisView*> (tv.first) != 0) ||
1188                             (boost::dynamic_pointer_cast<MidiRegion> (region) != 0 &&
1189                              dynamic_cast<MidiTimeAxisView*> (tv.first) != 0)) {
1190                                 
1191                                 /* audio to audio 
1192                                    OR 
1193                                    midi to midi
1194                                 */
1195                                 
1196                                 context->drag_status (context->get_suggested_action(), time);
1197                                 return true;
1198                         }
1199                 } else {
1200                         /* DND originating from outside ardour
1201                          *
1202                          * TODO: check if file is audio/midi, allow drops on same track-type only,
1203                          * currently: if audio is dropped on a midi-track, it is only added to the region-list
1204                          */
1205                         if (Profile->get_sae() || ARDOUR_UI::config()->get_only_copy_imported_files()) {
1206                                 context->drag_status(Gdk::ACTION_COPY, time);
1207                         } else {
1208                                 if ((context->get_actions() & (Gdk::ACTION_COPY | Gdk::ACTION_LINK | Gdk::ACTION_MOVE)) == Gdk::ACTION_COPY) {
1209                                         context->drag_status(Gdk::ACTION_COPY, time);
1210                                 } else {
1211                                         context->drag_status(Gdk::ACTION_LINK, time);
1212                                 }
1213                         }
1214                         return true;
1215                 }
1216         }
1217
1218         /* no drop here */
1219         context->drag_status (Gdk::DragAction (0), time);
1220         return false;
1221 }
1222
1223 void
1224 Editor::drop_regions (const Glib::RefPtr<Gdk::DragContext>& /*context*/,
1225                       int x, int y,
1226                       const SelectionData& /*data*/,
1227                       guint /*info*/, guint /*time*/)
1228 {
1229         boost::shared_ptr<Region> region;
1230         boost::shared_ptr<Region> region_copy;
1231         RouteTimeAxisView* rtav;
1232         GdkEvent event;
1233         double px;
1234         double py;
1235
1236         event.type = GDK_MOTION_NOTIFY;
1237         event.button.x = x;
1238         event.button.y = y;
1239         /* assume we're dragging with button 1 */
1240         event.motion.state = Gdk::BUTTON1_MASK;
1241
1242         framepos_t const pos = window_event_sample (&event, &px, &py);
1243
1244         std::pair<TimeAxisView*, int> const tv = trackview_by_y_position (py, false);
1245
1246         if (tv.first != 0) {
1247
1248                 rtav = dynamic_cast<RouteTimeAxisView*> (tv.first);
1249                 
1250                 if (rtav != 0 && rtav->is_track ()) {
1251
1252                         boost::shared_ptr<Region> region = _regions->get_dragged_region ();
1253                         
1254                         if (region) {
1255
1256                                 region_copy = RegionFactory::create (region, true);
1257         
1258
1259                                 if ((boost::dynamic_pointer_cast<AudioRegion> (region_copy) != 0 &&
1260                                     dynamic_cast<AudioTimeAxisView*> (tv.first) != 0) ||
1261                                     (boost::dynamic_pointer_cast<MidiRegion> (region_copy) != 0 &&
1262                                      dynamic_cast<MidiTimeAxisView*> (tv.first) != 0)) {
1263
1264                                         /* audio to audio 
1265                                            OR 
1266                                            midi to midi
1267                                         */
1268
1269
1270                                         _drags->set (new RegionInsertDrag (this, region_copy, rtav, pos), &event);
1271                                         _drags->end_grab (0);
1272                                 }
1273                         }
1274                 }
1275         }
1276 }
1277
1278 bool
1279 Editor::key_press_handler (ArdourCanvas::Item*, GdkEvent*, ItemType)
1280 {
1281         return false;
1282 }
1283
1284 bool
1285 Editor::key_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType type)
1286 {
1287
1288         bool handled = false;
1289
1290         switch (type) {
1291         case TempoMarkerItem:
1292                 switch (event->key.keyval) {
1293                 case GDK_Delete:
1294                         remove_tempo_marker (item);
1295                         handled = true;
1296                         break;
1297                 default:
1298                         break;
1299                 }
1300                 break;
1301
1302         case MeterMarkerItem:
1303                 switch (event->key.keyval) {
1304                 case GDK_Delete:
1305                         remove_meter_marker (item);
1306                         handled = true;
1307                         break;
1308                 default:
1309                         break;
1310                 }
1311                 break;
1312
1313         default:
1314                 break;
1315         }
1316
1317         return handled;
1318 }
1319