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