ardour
editor_mouse.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2000-2001 Paul Davis
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or
7  (at your option) any later version.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program; if not, write to the Free Software
16  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 
18 */
19 
20 #include <cassert>
21 #include <cstdlib>
22 #include <stdint.h>
23 #include <cmath>
24 #include <set>
25 #include <string>
26 #include <algorithm>
27 #include <bitset>
28 
29 #include "pbd/error.h"
30 #include "pbd/enumwriter.h"
31 #include "pbd/memento_command.h"
32 #include "pbd/basename.h"
34 
35 #include "gtkmm2ext/bindings.h"
36 #include "gtkmm2ext/utils.h"
37 #include "gtkmm2ext/tearoff.h"
38 
39 #include "canvas/canvas.h"
40 
41 #include "ardour/audioregion.h"
42 #include "ardour/operations.h"
43 #include "ardour/playlist.h"
44 #include "ardour/profile.h"
45 #include "ardour/region_factory.h"
46 #include "ardour/route.h"
47 #include "ardour/session.h"
48 #include "ardour/types.h"
49 
50 #include "ardour_ui.h"
51 #include "actions.h"
52 #include "editor.h"
53 #include "time_axis_view.h"
54 #include "audio_time_axis.h"
55 #include "audio_region_view.h"
56 #include "midi_region_view.h"
57 #include "marker.h"
58 #include "streamview.h"
59 #include "region_gain_line.h"
60 #include "automation_time_axis.h"
61 #include "control_point.h"
62 #include "prompter.h"
63 #include "selection.h"
64 #include "keyboard.h"
65 #include "editing.h"
66 #include "rgb_macros.h"
67 #include "control_point_dialog.h"
68 #include "editor_drag.h"
69 #include "automation_region_view.h"
70 #include "edit_note_dialog.h"
71 #include "mouse_cursors.h"
72 #include "editor_cursors.h"
73 #include "verbose_cursor.h"
74 #include "note.h"
75 
76 #include "i18n.h"
77 
78 using namespace std;
79 using namespace ARDOUR;
80 using namespace PBD;
81 using namespace Gtk;
82 using namespace Editing;
84 
85 bool
86 Editor::mouse_frame (framepos_t& where, bool& in_track_canvas) const
87 {
88  /* gdk_window_get_pointer() has X11's XQueryPointer semantics in that it only
89  pays attentions to subwindows. this means that menu windows are ignored, and
90  if the pointer is in a menu, the return window from the call will be the
91  the regular subwindow *under* the menu.
92 
93  this matters quite a lot if the pointer is moving around in a menu that overlaps
94  the track canvas because we will believe that we are within the track canvas
95  when we are not. therefore, we track enter/leave events for the track canvas
96  and allow that to override the result of gdk_window_get_pointer().
97  */
98 
99  if (!within_track_canvas) {
100  return false;
101  }
102 
103  int x, y;
104  Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas->get_window();
105 
106  if (!canvas_window) {
107  return false;
108  }
109 
110  Glib::RefPtr<const Gdk::Window> pointer_window = Gdk::Display::get_default()->get_window_at_pointer (x, y);
111 
112  if (!pointer_window) {
113  return false;
114  }
115 
116  if (pointer_window != canvas_window) {
117  in_track_canvas = false;
118  return false;
119  }
120 
121  in_track_canvas = true;
122 
123  GdkEvent event;
124  event.type = GDK_BUTTON_RELEASE;
125  event.button.x = x;
126  event.button.y = y;
127 
128  where = window_event_sample (&event, 0, 0);
129 
130  return true;
131 }
132 
134 Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
135 {
136  ArdourCanvas::Duple d;
137 
138  if (!gdk_event_get_coords (event, &d.x, &d.y)) {
139  return 0;
140  }
141 
142  /* event coordinates are in window units, so convert to canvas
143  */
144 
145  d = _track_canvas->window_to_canvas (d);
146 
147  if (pcx) {
148  *pcx = d.x;
149  }
150 
151  if (pcy) {
152  *pcy = d.y;
153  }
154 
155  return pixel_to_sample (d.x);
156 }
157 
159 Editor::canvas_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
160 {
161  double x;
162  double y;
163 
164  /* event coordinates are already in canvas units */
165 
166  if (!gdk_event_get_coords (event, &x, &y)) {
167  cerr << "!NO c COORDS for event type " << event->type << endl;
168  return 0;
169  }
170 
171  if (pcx) {
172  *pcx = x;
173  }
174 
175  if (pcy) {
176  *pcy = y;
177  }
178 
179  /* note that pixel_to_sample_from_event() never returns less than zero, so even if the pixel
180  position is negative (as can be the case with motion events in particular),
181  the frame location is always positive.
182  */
183 
184  return pixel_to_sample_from_event (x);
185 }
186 
187 void
189 {
190  boost::shared_ptr<Trimmable> st = _trimmable.lock();
191 
192  if (!st || st == t) {
193  _trimmable = t;
194  }
195 }
196 
197 void
199 {
200  boost::shared_ptr<Movable> sm = _movable.lock();
201 
202  if (!sm || sm != m) {
203  _movable = m;
204  }
205 }
206 
207 void
209 {
210  MouseMode m = mouse_mode;
211 
212  Glib::RefPtr<Action> act = ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
213  assert (act);
214  Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic (act);
215  assert (tact);
216 
217  set_mouse_mode(m, true); //call this so the button styles can get updated
218 }
219 
220 static Glib::RefPtr<Action>
222 {
223  switch (m) {
224  case MouseRange:
225  return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-range"));
226  case MouseObject:
227  return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-object"));
228  case MouseCut:
229  return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-cut"));
230  case MouseDraw:
231  return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-draw"));
232  case MouseTimeFX:
233  return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-timefx"));
234  case MouseContent:
235  return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-content"));
236  case MouseAudition:
237  return ActionManager::get_action (X_("MouseMode"), X_("set-mouse-mode-audition"));
238  }
239  return Glib::RefPtr<Action>();
240 }
241 
242 void
244 {
245  if (_drags->active ()) {
246  return;
247  }
248 
249  if (!force && m == mouse_mode) {
250  return;
251  }
252 
253  if (ARDOUR::Profile->get_mixbus()) {
254  if ( m == MouseCut) m = MouseObject;
255  }
256 
257  Glib::RefPtr<Action> act = get_mouse_mode_action(m);
258  Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
259 
260  /* go there and back to ensure that the toggled handler is called to set up mouse_mode */
261  tact->set_active (false);
262  tact->set_active (true);
263 
264  //NOTE: this will result in a call to mouse_mode_toggled which does the heavy lifting
265 }
266 
267 void
269 {
270  if (ARDOUR::Profile->get_mixbus()) {
271  if ( m == MouseCut) m = MouseObject;
272  }
273 
274  Glib::RefPtr<Action> act = get_mouse_mode_action(m);
275  Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act);
276 
277  if (!tact->get_active()) {
278  /* this was just the notification that the old mode has been
279  * left. we'll get called again with the new mode active in a
280  * jiffy.
281  */
282  return;
283  }
284 
285  if (_session && mouse_mode == MouseAudition) {
286  /* stop transport and reset default speed to avoid oddness with
287  auditioning */
288  _session->request_transport_speed (0.0, true);
289  }
290 
291  const bool was_internal = internal_editing();
292 
293  mouse_mode = m;
294 
295  /* Switch snap type/mode if we're moving to/from an internal tool. Note
296  this must toggle the actions and not call set_snap_*() directly,
297  otherwise things get out of sync and the combo box stops working. */
298  if (!was_internal && internal_editing()) {
299  snap_type_action(internal_snap_type)->set_active(true);
300  snap_mode_action(internal_snap_mode)->set_active(true);
301  } else if (was_internal && !internal_editing()) {
302  snap_type_action(pre_internal_snap_type)->set_active(true);
303  snap_mode_action(pre_internal_snap_mode)->set_active(true);
304  }
305 
306  instant_save ();
307 
308  /* this should generate a new enter event which will
309  trigger the appropiate cursor.
310  */
311 
312  if (_track_canvas) {
313  _track_canvas->re_enter ();
314  }
315 
316  set_gain_envelope_visibility ();
317 
318  update_time_selection_display ();
319 
320  update_all_enter_cursors ();
321 
322  MouseModeChanged (); /* EMIT SIGNAL */
323 }
324 
325 bool
327 {
328  return mouse_mode == Editing::MouseContent || mouse_mode == Editing::MouseDraw;
329 }
330 
331 void
333 {
334  switch (mouse_mode) {
335  case MouseRange:
336  selection->clear_objects ();
337  selection->ClearMidiNoteSelection (); /* EMIT SIGNAL */
338  break;
339  case MouseObject:
340  selection->clear_time ();
341  selection->clear_tracks ();
342  selection->ClearMidiNoteSelection (); /* EMIT SIGNAL */
343  break;
344  case MouseDraw:
345  /* Clear regions, but not time or tracks, since that
346  would destroy the range selection rectangle, which we need to stick
347  around for AutomationRangeDrag. */
348  selection->clear_regions ();
349  selection->clear_playlists ();
350  break;
351  case MouseContent:
352  /* This handles internal edit.
353  Clear everything except points and notes.
354  */
355  selection->clear_regions();
356  selection->clear_lines();
357  selection->clear_playlists ();
358 
359  selection->clear_time ();
360  selection->clear_tracks ();
361  break;
362 
363  case MouseTimeFX:
364  /* We probably want to keep region selection */
365  selection->clear_points ();
366  selection->clear_lines();
367  selection->clear_playlists ();
368 
369  selection->clear_time ();
370  selection->clear_tracks ();
371  break;
372 
373  case MouseAudition:
374  /*Don't lose lines or points if no action in this mode */
375  selection->clear_regions ();
376  selection->clear_playlists ();
377  selection->clear_time ();
378  selection->clear_tracks ();
379  break;
380 
381  default:
382  /*Clear everything */
383  selection->clear_objects();
384  selection->clear_time ();
385  selection->clear_tracks ();
386  break;
387  }
388 }
389 
390 void
392 {
393  const int n_mouse_modes = (int)MouseContent + 1;
394  int current = (int)current_mouse_mode();
395  if (next) {
396  set_mouse_mode((MouseMode)((current + 1) % n_mouse_modes));
397  } else {
398  set_mouse_mode((MouseMode)((current + n_mouse_modes - 1) % n_mouse_modes));
399  }
400 }
401 
402 void
403 Editor::button_selection (ArdourCanvas::Item* /*item*/, GdkEvent* event, ItemType item_type)
404 {
405  /* in object/audition/timefx/gain-automation mode,
406  any button press sets the selection if the object
407  can be selected. this is a bit of hack, because
408  we want to avoid this if the mouse operation is a
409  region alignment.
410 
411  note: not dbl-click or triple-click
412 
413  Also note that there is no region selection in internal edit mode, otherwise
414  for operations operating on the selection (e.g. cut) it is not obvious whether
415  to cut notes or regions.
416  */
417 
418  MouseMode eff_mouse_mode = effective_mouse_mode ();
419 
420  if (eff_mouse_mode == MouseCut) {
421  /* never change selection in cut mode */
422  return;
423  }
424 
425  if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
426  /* context clicks are always about object properties, even if
427  we're in range mode within smart mode.
428  */
429  eff_mouse_mode = MouseObject;
430  }
431 
432  /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
433  if (get_smart_mode()) {
434  switch (item_type) {
435  case FadeInHandleItem:
437  case FadeOutHandleItem:
439  eff_mouse_mode = MouseObject;
440  break;
441  default:
442  break;
443  }
444  }
445 
446  if (((mouse_mode != MouseObject) &&
447  (mouse_mode != MouseAudition || item_type != RegionItem) &&
448  (mouse_mode != MouseTimeFX || item_type != RegionItem) &&
449  (mouse_mode != MouseDraw)) ||
450  ((event->type != GDK_BUTTON_PRESS && event->type != GDK_BUTTON_RELEASE) || event->button.button > 3)) {
451  return;
452  }
453 
454  if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
455 
456  if ((event->button.state & Keyboard::RelevantModifierKeyMask) && event->button.button != 1) {
457 
458  /* almost no selection action on modified button-2 or button-3 events */
459 
460  if (item_type != RegionItem && event->button.button != 2) {
461  return;
462  }
463  }
464  }
465 
466  Selection::Operation op = ArdourKeyboard::selection_type (event->button.state);
467  bool press = (event->type == GDK_BUTTON_PRESS);
468 
469  if (press) {
470  _mouse_changed_selection = false;
471  }
472 
473  switch (item_type) {
474  case RegionItem:
475  if (press) {
476  if (eff_mouse_mode != MouseRange) {
477  _mouse_changed_selection = set_selected_regionview_from_click (press, op);
478  } else {
479  /* don't change the selection unless the
480  clicked track is not currently selected. if
481  so, "collapse" the selection to just this
482  track
483  */
484  if (!selection->selected (clicked_axisview)) {
485  set_selected_track_as_side_effect (Selection::Set);
486  }
487  }
488  } else {
489  if (eff_mouse_mode != MouseRange) {
490  _mouse_changed_selection |= set_selected_regionview_from_click (press, op);
491  }
492  }
493  break;
494 
496  case RegionViewName:
497  case LeftFrameHandle:
498  case RightFrameHandle:
499  case FadeInHandleItem:
501  case FadeInItem:
502  case FadeOutHandleItem:
504  case FadeOutItem:
505  case StartCrossFadeItem:
506  case EndCrossFadeItem:
507  if (get_smart_mode() || eff_mouse_mode != MouseRange) {
508  _mouse_changed_selection |= set_selected_regionview_from_click (press, op);
509  } else if (event->type == GDK_BUTTON_PRESS) {
510  set_selected_track_as_side_effect (op);
511  }
512  break;
513 
514  case ControlPointItem:
515  set_selected_track_as_side_effect (op);
516  if (eff_mouse_mode != MouseRange) {
517  _mouse_changed_selection |= set_selected_control_point_from_click (press, op);
518  }
519  break;
520 
521  case StreamItem:
522  /* for context click, select track */
523  if (event->button.button == 3) {
524  selection->clear_tracks ();
525  set_selected_track_as_side_effect (op);
526 
527  /* We won't get a release.*/
528  begin_reversible_selection_op (X_("Button 3 Menu Select"));
529  commit_reversible_selection_op ();
530  }
531  break;
532 
533  case AutomationTrackItem:
534  set_selected_track_as_side_effect (op);
535  break;
536 
537  default:
538  break;
539  }
540 
541  if ((!press) && _mouse_changed_selection) {
542  begin_reversible_selection_op (X_("Button Selection"));
543  commit_reversible_selection_op ();
544  _mouse_changed_selection = false;
545  }
546 }
547 
548 bool
549 Editor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
550 {
551  /* single mouse clicks on any of these item types operate
552  independent of mouse mode, mostly because they are
553  not on the main track canvas or because we want
554  them to be modeless.
555  */
556 
557  NoteBase* note = NULL;
558 
559  switch (item_type) {
560  case PlayheadCursorItem:
561  _drags->set (new CursorDrag (this, *playhead_cursor, true), event);
562  return true;
563 
564  case MarkerItem:
565  if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
566  hide_marker (item, event);
567  } else {
568  _drags->set (new MarkerDrag (this, item), event);
569  }
570  return true;
571 
572  case TempoMarkerItem:
573  {
574  _drags->set (
575  new TempoMarkerDrag (
576  this,
577  item,
578  Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
579  ),
580  event
581  );
582  return true;
583  }
584 
585  case MeterMarkerItem:
586  {
587  _drags->set (
588  new MeterMarkerDrag (
589  this,
590  item,
591  Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)
592  ),
593  event
594  );
595  return true;
596  }
597 
598  case VideoBarItem:
599  _drags->set (new VideoTimeLineDrag (this, item), event);
600  return true;
601  break;
602 
603  case MarkerBarItem:
604  case TempoBarItem:
605  case MeterBarItem:
606  case TimecodeRulerItem:
607  case SamplesRulerItem:
608  case MinsecRulerItem:
609  case BBTRulerItem:
610  if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
611  _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
612  }
613  return true;
614  break;
615 
616 
617  case RangeMarkerBarItem:
618  if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
619  _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateSkipMarker), event);
620  } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
621  _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateRangeMarker), event);
622  } else {
623  _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
624  }
625  return true;
626  break;
627 
628  case CdMarkerBarItem:
629  if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
630  _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
631  } else {
632  _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateCDMarker), event);
633  }
634  return true;
635  break;
636 
638  if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
639  _drags->set (new CursorDrag (this, *playhead_cursor, false), event);
640  } else {
641  _drags->set (new RangeMarkerBarDrag (this, item, RangeMarkerBarDrag::CreateTransportMarker), event);
642  }
643  return true;
644  break;
645 
646  default:
647  break;
648  }
649 
650  if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
651  /* special case: allow trim of range selections in joined object mode;
652  in theory eff should equal MouseRange in this case, but it doesn't
653  because entering the range selection canvas item results in entered_regionview
654  being set to 0, so update_join_object_range_location acts as if we aren't
655  over a region.
656  */
657  if (item_type == StartSelectionTrimItem) {
658  _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
659  } else if (item_type == EndSelectionTrimItem) {
660  _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
661  }
662  }
663 
664  Editing::MouseMode eff = effective_mouse_mode ();
665 
666  /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
667  if (get_smart_mode()) {
668  switch (item_type) {
669  case FadeInHandleItem:
671  case FadeOutHandleItem:
673  eff = MouseObject;
674  break;
675  default:
676  break;
677  }
678  }
679 
680  switch (eff) {
681  case MouseRange:
682  switch (item_type) {
684  _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionStartTrim), event);
685  break;
686 
688  _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionEndTrim), event);
689  break;
690 
691  case SelectionItem:
692  if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
693  start_selection_grab (item, event);
694  return true;
695  } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::SecondaryModifier)) {
696  /* grab selection for moving */
697  _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionMove), event);
698  } else {
699  /* this was debated, but decided the more common action was to
700  make a new selection */
701  _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
702  }
703  break;
704 
705  case StreamItem:
706  if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
707  _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
708  } else {
709  _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
710  }
711  return true;
712  break;
713 
715  if (!clicked_regionview->region()->locked()) {
716  _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
717  return true;
718  }
719  break;
720 
721  default:
722  if (Keyboard::modifier_state_equals (event->button.state, Keyboard::RangeSelectModifier)) {
723  _drags->set (new SelectionDrag (this, item, SelectionDrag::SelectionExtend), event);
724  } else {
725  _drags->set (new SelectionDrag (this, item, SelectionDrag::CreateSelection), event);
726  }
727  }
728  return true;
729  break;
730 
731  case MouseCut:
732  switch (item_type) {
733  case RegionItem:
734  case FadeInHandleItem:
735  case FadeOutHandleItem:
736  case LeftFrameHandle:
737  case RightFrameHandle:
738  case FeatureLineItem:
740  case RegionViewName:
741  case StreamItem:
742  case AutomationTrackItem:
743  _drags->set (new RegionCutDrag (this, item, canvas_event_sample (event)), event, get_canvas_cursor());
744  return true;
745  break;
746  default:
747  break;
748  }
749  break;
750 
751  case MouseContent:
752  switch (item_type) {
753  case NoteItem:
754  /* Existing note: allow trimming/motion */
755  if ((note = reinterpret_cast<NoteBase*> (item->get_data ("notebase")))) {
756  if (note->big_enough_to_trim() && note->mouse_near_ends()) {
757  _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
758  } else {
759  _drags->set (new NoteDrag (this, item), event);
760  }
761  }
762  return true;
763 
764  case ControlPointItem:
765  _drags->set (new ControlPointDrag (this, item), event);
766  return true;
767  break;
768 
769  case StreamItem:
770  //in the past, we created a new midi region here, but perhaps that is best left to the Draw mode
771  break;
772 
773  case AutomationTrackItem:
774  /* rubberband drag to select automation points */
775  _drags->set (new EditorRubberbandSelectDrag (this, item), event);
776  return true;
777  break;
778 
779  case RegionItem:
780  if (dynamic_cast<AutomationRegionView*>(clicked_regionview)) {
781  /* rubberband drag to select automation points */
782  _drags->set (new EditorRubberbandSelectDrag (this, item), event);
783  return true;
784  }
785  break;
786 
787  default:
788  break;
789  }
790  break;
791 
792  case MouseObject:
793  if (Keyboard::modifier_state_contains (event->button.state, Keyboard::ModifierMask(Keyboard::PrimaryModifier|Keyboard::SecondaryModifier)) &&
794  event->type == GDK_BUTTON_PRESS) {
795 
796  _drags->set (new EditorRubberbandSelectDrag (this, item), event);
797 
798  } else if (event->type == GDK_BUTTON_PRESS) {
799 
800  switch (item_type) {
801  case FadeInHandleItem:
802  {
803  _drags->set (new FadeInDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_in);
804  return true;
805  }
806 
807  case FadeOutHandleItem:
808  {
809  _drags->set (new FadeOutDrag (this, item, reinterpret_cast<RegionView*> (item->get_data("regionview")), selection->regions), event, _cursors->fade_out);
810  return true;
811  }
812 
813  case StartCrossFadeItem:
814  case EndCrossFadeItem:
815  /* we might allow user to grab inside the fade to trim a region with preserve_fade_anchor. for not this is not fully implemented */
816 // if (!clicked_regionview->region()->locked()) {
817 // _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
818 // return true;
819 // }
820  break;
821 
822  case FeatureLineItem:
823  {
824  if (Keyboard::modifier_state_contains (event->button.state, Keyboard::TertiaryModifier)) {
825  remove_transient(item);
826  return true;
827  }
828 
829  _drags->set (new FeatureLineDrag (this, item), event);
830  return true;
831  break;
832  }
833 
834  case RegionItem:
835  if (dynamic_cast<AutomationRegionView*> (clicked_regionview)) {
836  /* click on an automation region view; do nothing here and let the ARV's signal handler
837  sort it out.
838  */
839  break;
840  }
841 
842  /* click on a normal region view */
843  if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
844  add_region_copy_drag (item, event, clicked_regionview);
845  } else if (Keyboard::the_keyboard().key_is_down (GDK_b)) {
846  add_region_brush_drag (item, event, clicked_regionview);
847  } else {
848  add_region_drag (item, event, clicked_regionview);
849  }
850 
851 
852  _drags->start_grab (event);
853  return true;
854  break;
855 
857  case LeftFrameHandle:
858  case RightFrameHandle:
859  if (!clicked_regionview->region()->locked()) {
860  _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
861  return true;
862  }
863  break;
864 
867  if (!clicked_regionview->region()->locked()) {
868  _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
869  return true;
870  }
871  break;
872 
873  case RegionViewName:
874  {
875  /* rename happens on edit clicks */
876  if (clicked_regionview->get_name_highlight()) {
877  _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
878  return true;
879  }
880  break;
881  }
882 
883  case ControlPointItem:
884  _drags->set (new ControlPointDrag (this, item), event);
885  return true;
886  break;
887 
888  case AutomationLineItem:
889  _drags->set (new LineDrag (this, item), event);
890  return true;
891  break;
892 
893  case StreamItem:
894  _drags->set (new EditorRubberbandSelectDrag (this, item), event);
895  return true;
896 
897  case AutomationTrackItem:
898  {
899  TimeAxisView* parent = clicked_axisview->get_parent ();
900  AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (clicked_axisview);
901  assert (atv);
902  if (parent && dynamic_cast<MidiTimeAxisView*> (parent) && atv->show_regions ()) {
903 
904  RouteTimeAxisView* p = dynamic_cast<RouteTimeAxisView*> (parent);
905  assert (p);
907  if (pl->n_regions() == 0) {
908  /* Parent has no regions; create one so that we have somewhere to put automation */
909  _drags->set (new RegionCreateDrag (this, item, parent), event);
910  } else {
911  /* See if there's a region before the click that we can extend, and extend it if so */
912  framepos_t const t = canvas_event_sample (event);
913  boost::shared_ptr<Region> prev = pl->find_next_region (t, End, -1);
914  if (!prev) {
915  _drags->set (new RegionCreateDrag (this, item, parent), event);
916  } else {
917  prev->set_length (t - prev->position ());
918  }
919  }
920  } else {
921  /* rubberband drag to select automation points */
922  _drags->set (new EditorRubberbandSelectDrag (this, item), event);
923  }
924  break;
925  }
926 
927  case SelectionItem:
928  {
929  break;
930  }
931 
932  case MarkerBarItem:
933 
934  break;
935 
936  default:
937  break;
938  }
939  }
940  return true;
941  break;
942 
943  case MouseDraw:
944  switch (item_type) {
945  case GainLineItem:
946  _drags->set (new LineDrag (this, item), event);
947  return true;
948 
949  case ControlPointItem:
950  _drags->set (new ControlPointDrag (this, item), event);
951  return true;
952  break;
953 
954  case SelectionItem:
955  {
956  if (dynamic_cast<AudioRegionView*>(clicked_regionview) ||
957  dynamic_cast<AutomationRegionView*>(clicked_regionview)) {
958  _drags->set (new AutomationRangeDrag (this, clicked_regionview, selection->time),
959  event, _cursors->up_down);
960  } else {
961  double const y = event->button.y;
962  pair<TimeAxisView*, int> tvp = trackview_by_y_position (y, false);
963  if (tvp.first) {
964  AutomationTimeAxisView* atv = dynamic_cast<AutomationTimeAxisView*> (tvp.first);
965  if ( atv) {
966  /* smart "join" mode: drag automation */
967  _drags->set (new AutomationRangeDrag (this, atv, selection->time), event, _cursors->up_down);
968  }
969  }
970  }
971  return true;
972  break;
973  }
974 
975  case AutomationLineItem:
976  _drags->set (new LineDrag (this, item), event);
977  break;
978 
979  case NoteItem:
980  if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
981  if (note->big_enough_to_trim() && note->mouse_near_ends()) {
982  /* Note is big and pointer is near the end, trim */
983  _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
984  } else {
985  /* Drag note */
986  _drags->set (new NoteDrag (this, item), event);
987  }
988  return true;
989  }
990  return true;
991 
992  case StreamItem:
993  if (dynamic_cast<MidiTimeAxisView*> (clicked_axisview)) {
994  _drags->set (new RegionCreateDrag (this, item, clicked_axisview), event);
995  }
996  return true;
997 
998  default:
999  break;
1000  }
1001  return true;
1002  break;
1003 
1004  case MouseTimeFX:
1005  if (item_type == NoteItem) {
1006  /* resize-drag notes */
1007  if ((note = reinterpret_cast<NoteBase*>(item->get_data ("notebase")))) {
1008  if (note->big_enough_to_trim()) {
1009  _drags->set (new NoteResizeDrag (this, item), event, get_canvas_cursor());
1010  }
1011  }
1012  return true;
1013  } else if (clicked_regionview) {
1014  /* do time-FX */
1015  _drags->set (new TimeFXDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1016  return true;
1017  }
1018  break;
1019 
1020  case MouseAudition:
1021  _drags->set (new ScrubDrag (this, item), event, _cursors->transparent);
1022  scrub_reversals = 0;
1023  scrub_reverse_distance = 0;
1024  last_scrub_x = event->button.x;
1025  scrubbing_direction = 0;
1026  return true;
1027  break;
1028 
1029  default:
1030  break;
1031  }
1032 
1033  return false;
1034 }
1035 
1036 bool
1037 Editor::button_press_handler_2 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1038 {
1039  Editing::MouseMode const eff = effective_mouse_mode ();
1040  switch (eff) {
1041  case MouseObject:
1042  switch (item_type) {
1043  case RegionItem:
1044  if (Keyboard::modifier_state_contains (event->button.state, Keyboard::CopyModifier)) {
1045  add_region_copy_drag (item, event, clicked_regionview);
1046  } else {
1047  add_region_drag (item, event, clicked_regionview);
1048  }
1049  _drags->start_grab (event);
1050  return true;
1051  break;
1052  case ControlPointItem:
1053  _drags->set (new ControlPointDrag (this, item), event);
1054  return true;
1055  break;
1056 
1057  default:
1058  break;
1059  }
1060 
1061  switch (item_type) {
1063  _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1064  return true;
1065  break;
1066 
1067  case LeftFrameHandle:
1068  case RightFrameHandle:
1069  _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
1070  return true;
1071  break;
1072 
1073  case RegionViewName:
1074  _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
1075  return true;
1076  break;
1077 
1078  default:
1079  break;
1080  }
1081 
1082  break;
1083 
1084  case MouseDraw:
1085  return false;
1086 
1087  case MouseRange:
1088  /* relax till release */
1089  return true;
1090  break;
1091 
1092  default:
1093  break;
1094  }
1095 
1096  return false;
1097 }
1098 
1099 bool
1100 Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1101 {
1102  if (event->type == GDK_2BUTTON_PRESS) {
1103  _drags->mark_double_click ();
1104  gdk_pointer_ungrab (GDK_CURRENT_TIME);
1105  return true;
1106  }
1107 
1108  if (event->type != GDK_BUTTON_PRESS) {
1109  return false;
1110  }
1111 
1112  _track_canvas->grab_focus();
1113 
1114  if (_session && _session->actively_recording()) {
1115  return true;
1116  }
1117 
1118  button_selection (item, event, item_type);
1119 
1120  if (!_drags->active () &&
1121  (Keyboard::is_delete_event (&event->button) ||
1122  Keyboard::is_context_menu_event (&event->button) ||
1123  Keyboard::is_edit_event (&event->button))) {
1124 
1125  /* handled by button release */
1126  return true;
1127  }
1128 
1129  //not rolling, range mode click + join_play_range : locate the PH here
1130  if ( !_drags->active () && !_session->transport_rolling() && ( effective_mouse_mode() == MouseRange ) && ARDOUR_UI::config()->get_follow_edits() ) {
1131  framepos_t where = canvas_event_sample (event);
1132  snap_to(where);
1133  _session->request_locate (where, false);
1134  }
1135 
1136  switch (event->button.button) {
1137  case 1:
1138  return button_press_handler_1 (item, event, item_type);
1139  break;
1140 
1141  case 2:
1142  return button_press_handler_2 (item, event, item_type);
1143  break;
1144 
1145  case 3:
1146  break;
1147 
1148  default:
1149  return button_press_dispatch (&event->button);
1150  break;
1151 
1152  }
1153 
1154  return false;
1155 }
1156 
1157 bool
1158 Editor::button_press_dispatch (GdkEventButton* ev)
1159 {
1160  /* this function is intended only for buttons 4 and above.
1161  */
1162 
1163  Gtkmm2ext::MouseButton b (ev->state, ev->button);
1164  return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
1165 }
1166 
1167 bool
1169 {
1170  /* this function is intended only for buttons 4 and above.
1171  */
1172 
1173  Gtkmm2ext::MouseButton b (ev->state, ev->button);
1174  return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
1175 }
1176 
1177 bool
1178 Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1179 {
1180  framepos_t where = canvas_event_sample (event);
1181  AutomationTimeAxisView* atv = 0;
1182 
1183  _press_cursor_ctx.reset();
1184 
1185  /* no action if we're recording */
1186 
1187  if (_session && _session->actively_recording()) {
1188  return true;
1189  }
1190 
1191  bool were_dragging = false;
1192 
1193  if (!Keyboard::is_context_menu_event (&event->button)) {
1194 
1195  /* see if we're finishing a drag */
1196 
1197  if (_drags->active ()) {
1198  bool const r = _drags->end_grab (event);
1199  if (r) {
1200  /* grab dragged, so do nothing else */
1201  return true;
1202  }
1203 
1204  were_dragging = true;
1205  }
1206 
1207  update_region_layering_order_editor ();
1208  }
1209 
1210  /* edit events get handled here */
1211 
1212  if (!_drags->active () && Keyboard::is_edit_event (&event->button)) {
1213  switch (item_type) {
1214  case RegionItem:
1215  show_region_properties ();
1216  break;
1217 
1218  case TempoMarkerItem: {
1219  Marker* marker;
1220  TempoMarker* tempo_marker;
1221 
1222  if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1223  fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1224  abort(); /*NOTREACHED*/
1225  }
1226 
1227  if ((tempo_marker = dynamic_cast<TempoMarker*> (marker)) == 0) {
1228  fatal << _("programming error: marker for tempo is not a tempo marker!") << endmsg;
1229  abort(); /*NOTREACHED*/
1230  }
1231 
1232  edit_tempo_marker (*tempo_marker);
1233  break;
1234  }
1235 
1236  case MeterMarkerItem: {
1237  Marker* marker;
1238  MeterMarker* meter_marker;
1239 
1240  if ((marker = reinterpret_cast<Marker *> (item->get_data ("marker"))) == 0) {
1241  fatal << _("programming error: tempo marker canvas item has no marker object pointer!") << endmsg;
1242  abort(); /*NOTREACHED*/
1243  }
1244 
1245  if ((meter_marker = dynamic_cast<MeterMarker*> (marker)) == 0) {
1246  fatal << _("programming error: marker for meter is not a meter marker!") << endmsg;
1247  abort(); /*NOTREACHED*/
1248  }
1249  edit_meter_marker (*meter_marker);
1250  break;
1251  }
1252 
1253  case RegionViewName:
1254  if (clicked_regionview->name_active()) {
1255  return mouse_rename_region (item, event);
1256  }
1257  break;
1258 
1259  case ControlPointItem:
1260  edit_control_point (item);
1261  break;
1262 
1263  default:
1264  break;
1265  }
1266  return true;
1267  }
1268 
1269  /* context menu events get handled here */
1270  if (Keyboard::is_context_menu_event (&event->button)) {
1271 
1272  context_click_event = *event;
1273 
1274  if (!_drags->active ()) {
1275 
1276  /* no matter which button pops up the context menu, tell the menu
1277  widget to use button 1 to drive menu selection.
1278  */
1279 
1280  switch (item_type) {
1281  case FadeInItem:
1282  case FadeInHandleItem:
1283  case FadeInTrimHandleItem:
1284  case StartCrossFadeItem:
1285  popup_xfade_in_context_menu (1, event->button.time, item, item_type);
1286  break;
1287 
1288  case FadeOutItem:
1289  case FadeOutHandleItem:
1290  case FadeOutTrimHandleItem:
1291  case EndCrossFadeItem:
1292  popup_xfade_out_context_menu (1, event->button.time, item, item_type);
1293  break;
1294 
1295  case LeftFrameHandle:
1296  case RightFrameHandle:
1297  break;
1298 
1299  case StreamItem:
1300  popup_track_context_menu (1, event->button.time, item_type, false);
1301  break;
1302 
1303  case RegionItem:
1305  case RegionViewName:
1306  popup_track_context_menu (1, event->button.time, item_type, false);
1307  break;
1308 
1309  case SelectionItem:
1310  popup_track_context_menu (1, event->button.time, item_type, true);
1311  break;
1312 
1313  case AutomationTrackItem:
1314  popup_track_context_menu (1, event->button.time, item_type, false);
1315  break;
1316 
1317  case MarkerBarItem:
1318  case RangeMarkerBarItem:
1320  case CdMarkerBarItem:
1321  case TempoBarItem:
1322  case MeterBarItem:
1323  case VideoBarItem:
1324  case TimecodeRulerItem:
1325  case SamplesRulerItem:
1326  case MinsecRulerItem:
1327  case BBTRulerItem:
1328  popup_ruler_menu (where, item_type);
1329  break;
1330 
1331  case MarkerItem:
1332  marker_context_menu (&event->button, item);
1333  break;
1334 
1335  case TempoMarkerItem:
1336  tempo_or_meter_marker_context_menu (&event->button, item);
1337  break;
1338 
1339  case MeterMarkerItem:
1340  tempo_or_meter_marker_context_menu (&event->button, item);
1341  break;
1342 
1343  case CrossfadeViewItem:
1344  popup_track_context_menu (1, event->button.time, item_type, false);
1345  break;
1346 
1347  case ControlPointItem:
1348  popup_control_point_context_menu (item, event);
1349  break;
1350 
1351  case NoteItem:
1352  if (internal_editing()) {
1353  popup_note_context_menu (item, event);
1354  }
1355  break;
1356 
1357  default:
1358  break;
1359  }
1360 
1361  return true;
1362  }
1363  }
1364 
1365  /* delete events get handled here */
1366 
1367  Editing::MouseMode const eff = effective_mouse_mode ();
1368 
1369  if (!_drags->active () && Keyboard::is_delete_event (&event->button)) {
1370 
1371  switch (item_type) {
1372  case TempoMarkerItem:
1373  remove_tempo_marker (item);
1374  break;
1375 
1376  case MeterMarkerItem:
1377  remove_meter_marker (item);
1378  break;
1379 
1380  case MarkerItem:
1381  remove_marker (*item, event);
1382  break;
1383 
1384  case RegionItem:
1385  if (eff == MouseObject) {
1386  remove_clicked_region ();
1387  }
1388  break;
1389 
1390  case ControlPointItem:
1391  remove_control_point (item);
1392  break;
1393 
1394  case NoteItem:
1395  remove_midi_note (item, event);
1396  break;
1397 
1398  default:
1399  break;
1400  }
1401  return true;
1402  }
1403 
1404  switch (event->button.button) {
1405  case 1:
1406 
1407  switch (item_type) {
1408  /* see comments in button_press_handler */
1409  case PlayheadCursorItem:
1410  case MarkerItem:
1411  case GainLineItem:
1412  case AutomationLineItem:
1414  case EndSelectionTrimItem:
1415  return true;
1416 
1417  case MarkerBarItem:
1418  if (!_dragging_playhead) {
1419  snap_to_with_modifier (where, event, RoundNearest, true);
1420  mouse_add_new_marker (where);
1421  }
1422  return true;
1423 
1424  case CdMarkerBarItem:
1425  if (!_dragging_playhead) {
1426  // if we get here then a dragged range wasn't done
1427  snap_to_with_modifier (where, event, RoundNearest, true);
1428  mouse_add_new_marker (where, true);
1429  }
1430  return true;
1431 
1432  case TempoBarItem:
1433  if (!_dragging_playhead) {
1434  snap_to_with_modifier (where, event);
1435  mouse_add_new_tempo_event (where);
1436  }
1437  return true;
1438 
1439  case MeterBarItem:
1440  if (!_dragging_playhead) {
1441  mouse_add_new_meter_event (pixel_to_sample (event->button.x));
1442  }
1443  return true;
1444  break;
1445 
1446  case TimecodeRulerItem:
1447  case SamplesRulerItem:
1448  case MinsecRulerItem:
1449  case BBTRulerItem:
1450  return true;
1451  break;
1452 
1453  default:
1454  break;
1455  }
1456 
1457  switch (eff) {
1458  case MouseDraw:
1459  switch (item_type) {
1460  case RegionItem:
1461  {
1462  /* check that we didn't drag before releasing, since
1463  its really annoying to create new control
1464  points when doing this.
1465  */
1466  AudioRegionView* arv = dynamic_cast<AudioRegionView*> (clicked_regionview);
1467  if (!were_dragging && arv) {
1468  bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1469  arv->add_gain_point_event (item, event, with_guard_points);
1470  }
1471  return true;
1472  break;
1473  }
1474 
1475  case AutomationTrackItem: {
1476  bool with_guard_points = Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier);
1477  atv = dynamic_cast<AutomationTimeAxisView*>(clicked_axisview);
1478  if (atv) {
1479  atv->add_automation_event (event, where, event->button.y, with_guard_points);
1480  }
1481  return true;
1482  break;
1483  }
1484  default:
1485  break;
1486  }
1487  break;
1488 
1489  case MouseAudition:
1490  if (scrubbing_direction == 0) {
1491  /* no drag, just a click */
1492  switch (item_type) {
1493  case RegionItem:
1494  play_selected_region ();
1495  break;
1496  default:
1497  break;
1498  }
1499  } else if (_session) {
1500  /* make sure we stop */
1501  _session->request_transport_speed (0.0);
1502  }
1503  break;
1504 
1505  default:
1506  break;
1507 
1508  }
1509 
1510  /* do any (de)selection operations that should occur on button release */
1511  button_selection (item, event, item_type);
1512 
1513  return true;
1514  break;
1515 
1516 
1517  case 2:
1518  switch (eff) {
1519 
1520  case MouseObject:
1521  switch (item_type) {
1522  case RegionItem:
1523  if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
1524  raise_region ();
1525  } else if (Keyboard::modifier_state_equals (event->button.state, Keyboard::ModifierMask (Keyboard::TertiaryModifier|Keyboard::SecondaryModifier))) {
1526  lower_region ();
1527  } else {
1528  // Button2 click is unused
1529  }
1530  return true;
1531 
1532  break;
1533 
1534  default:
1535  break;
1536  }
1537  break;
1538 
1539  case MouseDraw:
1540  return true;
1541 
1542  case MouseRange:
1543  // x_style_paste (where, 1.0);
1544  return true;
1545  break;
1546 
1547  default:
1548  break;
1549  }
1550 
1551  break;
1552 
1553  case 3:
1554  break;
1555 
1556  default:
1557  break;
1558  }
1559 
1560  return false;
1561 }
1562 
1563 bool
1564 Editor::enter_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
1565 {
1566  ControlPoint* cp;
1567  Marker * marker;
1568  double fraction;
1569  bool ret = true;
1570 
1571  /* by the time we reach here, entered_regionview and entered trackview
1572  * will have already been set as appropriate. Things are done this
1573  * way because this method isn't passed a pointer to a variable type of
1574  * thing that is entered (which may or may not be canvas item).
1575  * (e.g. the actual entered regionview)
1576  */
1577 
1578  choose_canvas_cursor_on_entry (item_type);
1579 
1580  switch (item_type) {
1581  case ControlPointItem:
1582  if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1583  cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
1584  cp->show ();
1585 
1586  fraction = 1.0 - (cp->get_y() / cp->line().height());
1587 
1588  _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
1589  _verbose_cursor->show ();
1590  }
1591  break;
1592 
1593  case GainLineItem:
1594  if (mouse_mode == MouseDraw) {
1595  ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1596  if (line) {
1597  line->set_outline_color (ARDOUR_UI::config()->color ("entered gain line"));
1598  }
1599  }
1600  break;
1601 
1602  case AutomationLineItem:
1603  if (mouse_mode == MouseDraw || mouse_mode == MouseObject) {
1604  ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1605  if (line) {
1606  line->set_outline_color (ARDOUR_UI::config()->color ("entered automation line"));
1607  }
1608  }
1609  break;
1610 
1611  case AutomationTrackItem:
1613  if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
1614  clear_entered_track = false;
1615  set_entered_track (atv);
1616  }
1617  break;
1618 
1619  case MarkerItem:
1620  if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1621  break;
1622  }
1623  entered_marker = marker;
1624  marker->set_color_rgba (ARDOUR_UI::config()->color ("entered marker"));
1625  // fall through
1626  case MeterMarkerItem:
1627  case TempoMarkerItem:
1628  break;
1629 
1630  case FadeInHandleItem:
1631  case FadeInTrimHandleItem:
1632  if (mouse_mode == MouseObject) {
1633  ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1634  if (rect) {
1635  RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1636  rect->set_fill_color (rv->get_fill_color());
1637  }
1638  }
1639  break;
1640 
1641  case FadeOutHandleItem:
1642  case FadeOutTrimHandleItem:
1643  if (mouse_mode == MouseObject) {
1644  ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1645  if (rect) {
1646  RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
1647  rect->set_fill_color (rv->get_fill_color ());
1648  }
1649  }
1650  break;
1651 
1652  case FeatureLineItem:
1653  {
1654  ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1655  line->set_outline_color (0xFF0000FF);
1656  }
1657  break;
1658 
1659  case SelectionItem:
1660  break;
1661 
1662  default:
1663  break;
1664  }
1665 
1666  /* third pass to handle entered track status in a comprehensible way.
1667  */
1668 
1669  switch (item_type) {
1670  case GainLineItem:
1671  case AutomationLineItem:
1672  case ControlPointItem:
1673  /* these do not affect the current entered track state */
1674  clear_entered_track = false;
1675  break;
1676 
1677  case AutomationTrackItem:
1678  /* handled above already */
1679  break;
1680 
1681  default:
1682 
1683  break;
1684  }
1685 
1686  return ret;
1687 }
1688 
1689 bool
1690 Editor::leave_handler (ArdourCanvas::Item* item, GdkEvent*, ItemType item_type)
1691 {
1692  AutomationLine* al;
1693  Marker *marker;
1694  Location *loc;
1695  bool is_start;
1696  bool ret = true;
1697 
1698  if (!_enter_stack.empty()) {
1699  _enter_stack.pop_back();
1700  }
1701 
1702  switch (item_type) {
1703  case ControlPointItem:
1704  _verbose_cursor->hide ();
1705  break;
1706 
1707  case GainLineItem:
1708  case AutomationLineItem:
1709  al = reinterpret_cast<AutomationLine*> (item->get_data ("line"));
1710  {
1711  ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1712  if (line) {
1713  line->set_outline_color (al->get_line_color());
1714  }
1715  }
1716  break;
1717 
1718  case MarkerItem:
1719  if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
1720  break;
1721  }
1722  entered_marker = 0;
1723  if ((loc = find_location_from_marker (marker, is_start)) != 0) {
1724  location_flags_changed (loc);
1725  }
1726  // fall through
1727  case MeterMarkerItem:
1728  case TempoMarkerItem:
1729  break;
1730 
1731  case FadeInTrimHandleItem:
1732  case FadeOutTrimHandleItem:
1733  case FadeInHandleItem:
1734  case FadeOutHandleItem:
1735  {
1736  ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
1737  if (rect) {
1738  rect->set_fill_color (ARDOUR_UI::config()->color ("inactive fade handle"));
1739  }
1740  }
1741  break;
1742 
1743  case AutomationTrackItem:
1744  break;
1745 
1746  case FeatureLineItem:
1747  {
1748  ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
1749  line->set_outline_color (ARDOUR_UI::config()->color ("zero line"));
1750  }
1751  break;
1752 
1753  default:
1754  break;
1755  }
1756 
1757  return ret;
1758 }
1759 
1760 void
1761 Editor::scrub (framepos_t frame, double current_x)
1762 {
1763  double delta;
1764 
1765  if (scrubbing_direction == 0) {
1766  /* first move */
1767  _session->request_locate (frame, false);
1768  _session->request_transport_speed (0.1);
1769  scrubbing_direction = 1;
1770 
1771  } else {
1772 
1773  if (last_scrub_x > current_x) {
1774 
1775  /* pointer moved to the left */
1776 
1777  if (scrubbing_direction > 0) {
1778 
1779  /* we reversed direction to go backwards */
1780 
1781  scrub_reversals++;
1782  scrub_reverse_distance += (int) (last_scrub_x - current_x);
1783 
1784  } else {
1785 
1786  /* still moving to the left (backwards) */
1787 
1788  scrub_reversals = 0;
1789  scrub_reverse_distance = 0;
1790 
1791  delta = 0.01 * (last_scrub_x - current_x);
1792  _session->request_transport_speed_nonzero (_session->transport_speed() - delta);
1793  }
1794 
1795  } else {
1796  /* pointer moved to the right */
1797 
1798  if (scrubbing_direction < 0) {
1799  /* we reversed direction to go forward */
1800 
1801  scrub_reversals++;
1802  scrub_reverse_distance += (int) (current_x - last_scrub_x);
1803 
1804  } else {
1805  /* still moving to the right */
1806 
1807  scrub_reversals = 0;
1808  scrub_reverse_distance = 0;
1809 
1810  delta = 0.01 * (current_x - last_scrub_x);
1811  _session->request_transport_speed_nonzero (_session->transport_speed() + delta);
1812  }
1813  }
1814 
1815  /* if there have been more than 2 opposite motion moves detected, or one that moves
1816  back more than 10 pixels, reverse direction
1817  */
1818 
1819  if (scrub_reversals >= 2 || scrub_reverse_distance > 10) {
1820 
1821  if (scrubbing_direction > 0) {
1822  /* was forwards, go backwards */
1823  _session->request_transport_speed (-0.1);
1824  scrubbing_direction = -1;
1825  } else {
1826  /* was backwards, go forwards */
1827  _session->request_transport_speed (0.1);
1828  scrubbing_direction = 1;
1829  }
1830 
1831  scrub_reverse_distance = 0;
1832  scrub_reversals = 0;
1833  }
1834  }
1835 
1836  last_scrub_x = current_x;
1837 }
1838 
1839 bool
1840 Editor::motion_handler (ArdourCanvas::Item* /*item*/, GdkEvent* event, bool from_autoscroll)
1841 {
1842  _last_motion_y = event->motion.y;
1843 
1844  if (event->motion.is_hint) {
1845  gint x, y;
1846 
1847  /* We call this so that MOTION_NOTIFY events continue to be
1848  delivered to the canvas. We need to do this because we set
1849  Gdk::POINTER_MOTION_HINT_MASK on the canvas. This reduces
1850  the density of the events, at the expense of a round-trip
1851  to the server. Given that this will mostly occur on cases
1852  where DISPLAY = :0.0, and given the cost of what the motion
1853  event might do, its a good tradeoff.
1854  */
1855 
1856  _track_canvas->get_pointer (x, y);
1857  }
1858 
1859  if (current_stepping_trackview) {
1860  /* don't keep the persistent stepped trackview if the mouse moves */
1861  current_stepping_trackview = 0;
1862  step_timeout.disconnect ();
1863  }
1864 
1865  if (_session && _session->actively_recording()) {
1866  /* Sorry. no dragging stuff around while we record */
1867  return true;
1868  }
1869 
1870  update_join_object_range_location (event->motion.y);
1871 
1872  if (_drags->active ()) {
1873  return _drags->motion_handler (event, from_autoscroll);
1874  }
1875 
1876  return false;
1877 }
1878 
1879 bool
1880 Editor::can_remove_control_point (ArdourCanvas::Item* item)
1881 {
1882  ControlPoint* control_point;
1883 
1884  if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1885  fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1886  abort(); /*NOTREACHED*/
1887  }
1888 
1889  AutomationLine& line = control_point->line ();
1890  if (dynamic_cast<AudioRegionGainLine*> (&line)) {
1891  /* we shouldn't remove the first or last gain point in region gain lines */
1892  if (line.is_last_point(*control_point) || line.is_first_point(*control_point)) {
1893  return false;
1894  }
1895  }
1896 
1897  return true;
1898 }
1899 
1900 void
1901 Editor::remove_control_point (ArdourCanvas::Item* item)
1902 {
1903  if (!can_remove_control_point (item)) {
1904  return;
1905  }
1906 
1907  ControlPoint* control_point;
1908 
1909  if ((control_point = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"))) == 0) {
1910  fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1911  abort(); /*NOTREACHED*/
1912  }
1913 
1914  control_point->line().remove_point (*control_point);
1915 }
1916 
1917 void
1918 Editor::edit_control_point (ArdourCanvas::Item* item)
1919 {
1920  ControlPoint* p = reinterpret_cast<ControlPoint *> (item->get_data ("control_point"));
1921 
1922  if (p == 0) {
1923  fatal << _("programming error: control point canvas item has no control point object pointer!") << endmsg;
1924  abort(); /*NOTREACHED*/
1925  }
1926 
1927  ControlPointDialog d (p);
1928 
1929  if (d.run () != RESPONSE_ACCEPT) {
1930  return;
1931  }
1932 
1933  p->line().modify_point_y (*p, d.get_y_fraction ());
1934 }
1935 
1936 void
1938 {
1939  MidiRegionView::Selection const & s = mrv->selection();
1940 
1941  if (s.empty ()) {
1942  return;
1943  }
1944 
1945  EditNoteDialog* d = new EditNoteDialog (mrv, s);
1946  d->show_all ();
1947 
1948  d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
1949 }
1950 
1951 void
1953 {
1954  d->done (r);
1955  delete d;
1956 }
1957 
1958 void
1959 Editor::visible_order_range (int* low, int* high) const
1960 {
1961  *low = TimeAxisView::max_order ();
1962  *high = 0;
1963 
1964  for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
1965 
1966  RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*i);
1967 
1968  if (!rtv->hidden()) {
1969 
1970  if (*high < rtv->order()) {
1971  *high = rtv->order ();
1972  }
1973 
1974  if (*low > rtv->order()) {
1975  *low = rtv->order ();
1976  }
1977  }
1978  }
1979 }
1980 
1981 void
1983 {
1984  /* Either add to or set the set the region selection, unless
1985  this is an alignment click (control used)
1986  */
1987 
1988  if (Keyboard::modifier_state_contains (event->state, Keyboard::PrimaryModifier)) {
1989  TimeAxisView* tv = &rv.get_time_axis_view();
1990  RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(tv);
1991  double speed = 1.0;
1992  if (rtv && rtv->is_track()) {
1993  speed = rtv->track()->speed();
1994  }
1995 
1996  framepos_t where = get_preferred_edit_position();
1997 
1998  if (where >= 0) {
1999 
2000  if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::SecondaryModifier))) {
2001 
2002  align_region (rv.region(), SyncPoint, (framepos_t) (where * speed));
2003 
2004  } else if (Keyboard::modifier_state_equals (event->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
2005 
2006  align_region (rv.region(), End, (framepos_t) (where * speed));
2007 
2008  } else {
2009 
2010  align_region (rv.region(), Start, (framepos_t) (where * speed));
2011  }
2012  }
2013  }
2014 }
2015 
2016 void
2018 {
2019  latest_regionviews.push_back (rv);
2020 }
2021 
2022 void
2024 {
2025  selection->add(rv);
2026  latest_regionviews.push_back (rv);
2027 }
2028 
2029 void
2031 {
2032  for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2033  (*i)->hide_selection ();
2034  }
2035 
2036  selection->clear ();
2037  clicked_selection = 0;
2038 }
2039 
2040 void
2042 {
2043  for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
2044  (*i)->hide_selection ();
2045  }
2046  selection->time.clear ();
2047  clicked_selection = 0;
2048 }
2049 
2050 void
2051 Editor::point_trim (GdkEvent* event, framepos_t new_bound)
2052 {
2053  RegionView* rv = clicked_regionview;
2054 
2055  /* Choose action dependant on which button was pressed */
2056  switch (event->button.button) {
2057  case 1:
2058  begin_reversible_command (_("start point trim"));
2059 
2060  if (selection->selected (rv)) {
2061  for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin();
2062  i != selection->regions.by_layer().end(); ++i)
2063  {
2064  if (!(*i)->region()->locked()) {
2065  (*i)->region()->clear_changes ();
2066  (*i)->region()->trim_front (new_bound);
2067  _session->add_command(new StatefulDiffCommand ((*i)->region()));
2068  }
2069  }
2070 
2071  } else {
2072  if (!rv->region()->locked()) {
2073  rv->region()->clear_changes ();
2074  rv->region()->trim_front (new_bound);
2075  _session->add_command(new StatefulDiffCommand (rv->region()));
2076  }
2077  }
2078 
2079  commit_reversible_command();
2080 
2081  break;
2082  case 2:
2083  begin_reversible_command (_("End point trim"));
2084 
2085  if (selection->selected (rv)) {
2086 
2087  for (list<RegionView*>::const_iterator i = selection->regions.by_layer().begin(); i != selection->regions.by_layer().end(); ++i)
2088  {
2089  if (!(*i)->region()->locked()) {
2090  (*i)->region()->clear_changes();
2091  (*i)->region()->trim_end (new_bound);
2092  _session->add_command(new StatefulDiffCommand ((*i)->region()));
2093  }
2094  }
2095 
2096  } else {
2097 
2098  if (!rv->region()->locked()) {
2099  rv->region()->clear_changes ();
2100  rv->region()->trim_end (new_bound);
2101  _session->add_command (new StatefulDiffCommand (rv->region()));
2102  }
2103  }
2104 
2105  commit_reversible_command();
2106 
2107  break;
2108  default:
2109  break;
2110  }
2111 }
2112 
2113 void
2114 Editor::hide_marker (ArdourCanvas::Item* item, GdkEvent* /*event*/)
2115 {
2116  Marker* marker;
2117  bool is_start;
2118 
2119  if ((marker = static_cast<Marker *> (item->get_data ("marker"))) == 0) {
2120  fatal << _("programming error: marker canvas item has no marker object pointer!") << endmsg;
2121  abort(); /*NOTREACHED*/
2122  }
2123 
2124  Location* location = find_location_from_marker (marker, is_start);
2125  location->set_hidden (true, this);
2126 }
2127 
2128 gint
2129 Editor::mouse_rename_region (ArdourCanvas::Item* /*item*/, GdkEvent* /*event*/)
2130 {
2131  using namespace Gtkmm2ext;
2132 
2133  ArdourPrompter prompter (false);
2134 
2135  prompter.set_prompt (_("Name for region:"));
2136  prompter.set_initial_text (clicked_regionview->region()->name());
2137  prompter.add_button (_("Rename"), Gtk::RESPONSE_ACCEPT);
2138  prompter.set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
2139  prompter.show_all ();
2140  switch (prompter.run ()) {
2141  case Gtk::RESPONSE_ACCEPT:
2142  string str;
2143  prompter.get_result(str);
2144  if (str.length()) {
2145  clicked_regionview->region()->set_name (str);
2146  }
2147  break;
2148  }
2149  return true;
2150 }
2151 
2152 
2153 void
2155 {
2156  /* no brushing without a useful snap setting */
2157 
2158  switch (_snap_mode) {
2159  case SnapMagnetic:
2160  return; /* can't work because it allows region to be placed anywhere */
2161  default:
2162  break; /* OK */
2163  }
2164 
2165  switch (_snap_type) {
2166  case SnapToMark:
2167  return;
2168 
2169  default:
2170  break;
2171  }
2172 
2173  /* don't brush a copy over the original */
2174 
2175  if (pos == rv->region()->position()) {
2176  return;
2177  }
2178 
2179  RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*>(&rv->get_time_axis_view());
2180 
2181  if (rtv == 0 || !rtv->is_track()) {
2182  return;
2183  }
2184 
2185  boost::shared_ptr<Playlist> playlist = rtv->playlist();
2186  double speed = rtv->track()->speed();
2187 
2188  playlist->clear_changes ();
2189  boost::shared_ptr<Region> new_region (RegionFactory::create (rv->region(), true));
2190  playlist->add_region (new_region, (framepos_t) (pos * speed));
2191  _session->add_command (new StatefulDiffCommand (playlist));
2192 
2193  // playlist is frozen, so we have to update manually XXX this is disgusting
2194 
2195  playlist->RegionAdded (new_region); /* EMIT SIGNAL */
2196 }
2197 
2198 gint
2200 {
2201  if (get_microseconds() - last_track_height_step_timestamp < 250000) {
2202  current_stepping_trackview = 0;
2203  return false;
2204  }
2205  return true;
2206 }
2207 
2208 void
2209 Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2210 {
2211  assert (region_view);
2212 
2213  if (!region_view->region()->playlist()) {
2214  return;
2215  }
2216 
2217  switch (Config->get_edit_mode()) {
2218  case Splice:
2219  _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
2220  break;
2221  case Ripple:
2222  _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer()));
2223  break;
2224  default:
2225  _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
2226  break;
2227 
2228  }
2229 }
2230 
2231 void
2232 Editor::add_region_copy_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2233 {
2234  assert (region_view);
2235 
2236  if (!region_view->region()->playlist()) {
2237  return;
2238  }
2239 
2240  _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
2241 }
2242 
2243 void
2244 Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region_view)
2245 {
2246  assert (region_view);
2247 
2248  if (!region_view->region()->playlist()) {
2249  return;
2250  }
2251 
2252  if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) {
2253  return;
2254  }
2255 
2256  _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), true, false));
2257 
2258  begin_reversible_command (Operations::drag_region_brush);
2259 }
2260 
2265 void
2266 Editor::start_selection_grab (ArdourCanvas::Item* /*item*/, GdkEvent* event)
2267 {
2268  if (clicked_regionview == 0) {
2269  return;
2270  }
2271 
2272  /* lets try to create new Region for the selection */
2273 
2274  vector<boost::shared_ptr<Region> > new_regions;
2275  create_region_from_selection (new_regions);
2276 
2277  if (new_regions.empty()) {
2278  return;
2279  }
2280 
2281  /* XXX fix me one day to use all new regions */
2282 
2283  boost::shared_ptr<Region> region (new_regions.front());
2284 
2285  /* add it to the current stream/playlist.
2286 
2287  tricky: the streamview for the track will add a new regionview. we will
2288  catch the signal it sends when it creates the regionview to
2289  set the regionview we want to then drag.
2290  */
2291 
2292  latest_regionviews.clear();
2293  sigc::connection c = clicked_routeview->view()->RegionViewAdded.connect (sigc::mem_fun(*this, &Editor::collect_new_region_view));
2294 
2295  /* A selection grab currently creates two undo/redo operations, one for
2296  creating the new region and another for moving it.
2297  */
2298 
2299  begin_reversible_command (Operations::selection_grab);
2300 
2301  boost::shared_ptr<Playlist> playlist = clicked_axisview->playlist();
2302 
2303  playlist->clear_changes ();
2304  clicked_routeview->playlist()->add_region (region, selection->time[clicked_selection].start);
2305  _session->add_command(new StatefulDiffCommand (playlist));
2306 
2307  c.disconnect ();
2308 
2309  if (latest_regionviews.empty()) {
2310  /* something went wrong */
2311  return;
2312  }
2313 
2314  /* we need to deselect all other regionviews, and select this one
2315  i'm ignoring undo stuff, because the region creation will take care of it
2316  */
2317 
2318  selection->set (latest_regionviews);
2319 
2320  commit_reversible_command ();
2321 
2322  _drags->set (new RegionMoveDrag (this, latest_regionviews.front()->get_canvas_group(), latest_regionviews.front(), latest_regionviews, false, false), event);
2323 }
2324 
2325 void
2327 {
2328  if (_drags->active ()) {
2329  _drags->abort ();
2330  } else {
2331  selection->clear ();
2332  }
2333 
2334  reset_focus ();
2335 }
2336 
2341 void
2343 {
2344  if (!get_smart_mode()) {
2345  _join_object_range_state = JOIN_OBJECT_RANGE_NONE;
2346  return;
2347  }
2348 
2349  JoinObjectRangeState const old = _join_object_range_state;
2350 
2351  if (mouse_mode == MouseObject) {
2352  _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2353  } else if (mouse_mode == MouseRange) {
2354  _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2355  }
2356 
2357  if (entered_regionview) {
2358 
2359  ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
2360  double const c = item_space.y / entered_regionview->height();
2361 
2362  _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
2363 
2364  Editor::EnterContext* ctx = get_enter_context(RegionItem);
2365  if (_join_object_range_state != old && ctx) {
2366  ctx->cursor_ctx->change(which_track_cursor());
2367  }
2368 
2369  } else if (entered_track) {
2370 
2371  RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
2372 
2373  if (entered_route_view) {
2374 
2375  double cx = 0;
2376  double cy = y;
2377 
2378  entered_route_view->canvas_display()->canvas_to_item (cx, cy);
2379 
2380  double track_height = entered_route_view->view()->child_height();
2381  if (ARDOUR_UI::config()->get_show_name_highlight()) {
2382  track_height -= TimeAxisViewItem::NAME_HIGHLIGHT_SIZE;
2383  }
2384  double const c = cy / track_height;
2385 
2386 
2387  if (c <= 0.5) {
2388  _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
2389  } else {
2390  _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2391  }
2392 
2393  } else {
2394  /* Other kinds of tracks use object mode */
2395  _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
2396  }
2397 
2398  Editor::EnterContext* ctx = get_enter_context(StreamItem);
2399  if (_join_object_range_state != old && ctx) {
2400  ctx->cursor_ctx->change(which_track_cursor());
2401  }
2402  }
2403 }
2404 
2407 {
2408  if (_join_object_range_state == JOIN_OBJECT_RANGE_OBJECT) {
2409  return MouseObject;
2410  } else if (_join_object_range_state == JOIN_OBJECT_RANGE_RANGE) {
2411  return MouseRange;
2412  }
2413 
2414  return mouse_mode;
2415 }
2416 
2417 void
2418 Editor::remove_midi_note (ArdourCanvas::Item* item, GdkEvent *)
2419 {
2420  NoteBase* e = reinterpret_cast<NoteBase*> (item->get_data ("notebase"));
2421  assert (e);
2422 
2423  e->region_view().delete_note (e->note ());
2424 }
2425 
2427 void
2428 Editor::get_pointer_position (double& x, double& y) const
2429 {
2430  int px, py;
2431  _track_canvas->get_pointer (px, py);
2432  _track_canvas->window_to_canvas (px, py, x, y);
2433 }
void change(Gdk::Cursor *cursor)
uint32_t get_line_color() const
boost::shared_ptr< ARDOUR::Playlist > playlist() const
LIBPBD_API Transmitter fatal
void modify_point_y(ControlPoint &, double)
TimeAxisView & get_time_axis_view() const
void trim_front(framepos_t new_position)
Definition: region.cc:746
void scrub(framepos_t, double)
bool button_press_handler_2(ArdourCanvas::Item *, GdkEvent *, ItemType)
MouseMode
Definition: editing.h:91
TimeAxisView * get_parent()
void mouse_mode_toggled(Editing::MouseMode m)
ItemType
Definition: editor_items.h:23
void add_region_drag(ArdourCanvas::Item *, GdkEvent *, RegionView *)
void set_current_movable(boost::shared_ptr< ARDOUR::Movable >)
void start_selection_grab(ArdourCanvas::Item *, GdkEvent *)
gint track_height_step_timeout()
void remove_control_point(ArdourCanvas::Item *)
void update_time_selection_display()
LIBARDOUR_API GQuark selection_grab
Definition: operations.cc:31
Definition: ardour_ui.h:130
void button_selection(ArdourCanvas::Item *item, GdkEvent *event, ItemType item_type)
framepos_t canvas_event_sample(GdkEvent const *, double *px=0, double *py=0) const
AutomationLine & line() const
Definition: control_point.h:81
framepos_t window_event_sample(GdkEvent const *, double *px=0, double *py=0) const
void step_mouse_mode(bool next)
LIBGTKMM2EXT_API Glib::RefPtr< Gtk::Action > get_action(const char *group, const char *name)
Definition: actions.cc:406
void update_join_object_range_location(double)
void hide_marker(ArdourCanvas::Item *, GdkEvent *)
Definition: marker.h:41
Definition: Beats.hpp:239
void set_current_trimmable(boost::shared_ptr< ARDOUR::Trimmable >)
Editing::MouseMode effective_mouse_mode() const
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
StreamView * view() const
void edit_notes(MidiRegionView *)
Round to nearest.
Definition: types.h:224
bool mouse_frame(framepos_t &, bool &in_track_canvas) const
Definition: editor_mouse.cc:86
void set_initial_text(std::string txt)
Definition: prompter.h:50
bool motion_handler(ArdourCanvas::Item *, GdkEvent *, bool from_autoscroll=false)
void region_view_item_click(AudioRegionView &, GdkEventButton *)
gint mouse_rename_region(ArdourCanvas::Item *, GdkEvent *)
LIBARDOUR_API GQuark drag_region_brush
Definition: operations.cc:29
Selection selection() const
JoinObjectRangeState
Definition: editor.h:593
LIBARDOUR_API microseconds_t get_microseconds()
Definition: globals.cc:728
#define _(Text)
Definition: i18n.h:11
void trim_end(framepos_t new_position)
Definition: region.cc:836
bool leave_handler(ArdourCanvas::Item *, GdkEvent *, ItemType)
static int max_order()
MidiRegionView & region_view() const
Definition: note_base.h:104
LIBGTKMM2EXT_API uint64_t Keyboard
Definition: debug.cc:23
void add_region(boost::shared_ptr< Region >, framepos_t position, float times=1, bool auto_partition=false)
Definition: playlist.cc:668
#define X_(Text)
Definition: i18n.h:13
static Glib::RefPtr< Action > get_mouse_mode_action(MouseMode m)
virtual void remove_point(ControlPoint &)
LIBARDOUR_API RCConfiguration * Config
Definition: globals.cc:119
guint32 height() const
boost::shared_ptr< Region > find_next_region(framepos_t frame, RegionPoint point, int dir)
Definition: playlist.cc:1905
void point_trim(GdkEvent *, framepos_t)
boost::shared_ptr< ARDOUR::Region > region() const
Definition: region_view.h:66
void set_prompt(std::string prompt)
Definition: prompter.h:46
void remove_midi_note(ArdourCanvas::Item *, GdkEvent *)
bool enter_handler(ArdourCanvas::Item *, GdkEvent *, ItemType)
void add_gain_point_event(ArdourCanvas::Item *item, GdkEvent *event, bool with_guard_points)
void set_color_rgba(uint32_t rgba)
Definition: marker.cc:474
ArdourCanvas::Container * canvas_display()
void delete_note(boost::shared_ptr< NoteType >)
void escape()
virtual std::string get_verbose_cursor_string(double) const
Definition: amp.h:29
void visible_order_range(int *, int *) const
bool button_release_handler(ArdourCanvas::Item *, GdkEvent *, ItemType)
boost::shared_ptr< ARDOUR::Track > track() const
Definition: route_ui.cc:1738
bool internal_editing() const
boost::shared_ptr< Playlist > playlist()
Definition: track.cc:590
bool button_press_handler(ArdourCanvas::Item *, GdkEvent *, ItemType)
int64_t framepos_t
Definition: types.h:66
bool hidden() const
void mouse_brush_insert_region(RegionView *, framepos_t pos)
uint32_t get_fill_color() const
Definition: region_view.cc:532
bool can_remove_control_point(ArdourCanvas::Item *)
LIBARDOUR_API RuntimeProfile * Profile
Definition: globals.cc:120
void get_result(std::string &str, bool strip=true)
Definition: prompter.cc:104
bool is_track() const
Definition: route_ui.cc:1732
bool button_press_dispatch(GdkEventButton *)
framepos_t position() const
Definition: region.h:112
bool button_press_handler_1(ArdourCanvas::Item *, GdkEvent *, ItemType)
double get_y_fraction() const
bool is_last_point(ControlPoint &)
const boost::shared_ptr< NoteType > note() const
Definition: note_base.h:103
uint32_t n_regions() const
Definition: playlist.cc:2237
void cancel_time_selection()
std::set< NoteBase * > Selection
bool button_release_dispatch(GdkEventButton *)
PBD::Signal1< void, boost::weak_ptr< Region > > RegionAdded
Definition: playlist.h:191
bool mouse_near_ends() const
Definition: note_base.cc:293
bool locked() const
Definition: region.h:164
Definition: editor.h:134
static Selection::Operation selection_type(guint state)
Definition: keyboard.cc:178
double child_height() const
Definition: streamview.cc:613
bool is_first_point(ControlPoint &)
boost::shared_ptr< CursorContext > cursor_ctx
Definition: editor.h:489
void set_length(framecnt_t)
Definition: region.cc:430
void add_region_brush_drag(ArdourCanvas::Item *, GdkEvent *, RegionView *)
static UIConfiguration * config()
Definition: ardour_ui.h:188
Definition: debug.h:30
void add_automation_event(GdkEvent *, framepos_t, double, bool with_guard_points)
void get_pointer_position(double &, double &) const
void mouse_mode_object_range_toggled()
double get_y() const
Definition: control_point.h:60
void add_region_copy_drag(ArdourCanvas::Item *, GdkEvent *, RegionView *)
void set_mouse_mode(Editing::MouseMode, bool force=true)
void collect_new_region_view(RegionView *)
void cancel_selection()
int64_t framepos_t
void clear_changes()
Definition: stateful.cc:184
void set_hidden(bool yn, void *src)
Definition: location.cc:441
int order() const
void collect_and_select_new_region_view(RegionView *)
virtual bool big_enough_to_trim() const
Definition: note_base.cc:300
boost::shared_ptr< ARDOUR::Playlist > playlist() const
Definition: region.h:251
static double NAME_HIGHLIGHT_SIZE
void note_edit_done(int, EditNoteDialog *)
void edit_control_point(ArdourCanvas::Item *)
double speed() const
Definition: track.cc:759
LIBARDOUR_API PBD::PropertyDescriptor< bool > color
Definition: route_group.cc:50