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