fix mistaken "do not roll" conclusion in TransportFSM::compute_should_roll()
[ardour.git] / gtk2_ardour / midi_region_view.h
index a99881bc216d63207fc12319e3c61e5bac5fe848..6d5a6c250658f5017f5d52c67072dffe7ce95a0a 100644 (file)
@@ -1,20 +1,26 @@
 /*
-    Copyright (C) 2001-2011 Paul Davis
-
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
+ * Copyright (C) 2006-2015 David Robillard <d@drobilla.net>
+ * Copyright (C) 2008-2012 Hans Baier <hansfbaier@googlemail.com>
+ * Copyright (C) 2008-2017 Paul Davis <paul@linuxaudiosystems.com>
+ * Copyright (C) 2009-2012 Carl Hetherington <carl@carlh.net>
+ * Copyright (C) 2015-2016 Tim Mayberry <mojofunk@gmail.com>
+ * Copyright (C) 2015-2017 Nick Mainsbridge <mainsbridge@gmail.com>
+ * Copyright (C) 2015-2017 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
 
 #ifndef __gtk_ardour_midi_region_view_h__
 #define __gtk_ardour_midi_region_view_h__
@@ -65,8 +71,8 @@ class CursorContext;
 class MidiRegionView : public RegionView
 {
 public:
-       typedef Evoral::Note<Evoral::Beats> NoteType;
-       typedef Evoral::Sequence<Evoral::Beats>::Notes Notes;
+       typedef Evoral::Note<Temporal::Beats> NoteType;
+       typedef Evoral::Sequence<Temporal::Beats>::Notes Notes;
 
        MidiRegionView (ArdourCanvas::Container*              parent,
                        RouteTimeAxisView&                    tv,
@@ -99,8 +105,8 @@ public:
        { return midi_view()->midi_view(); }
 
        void step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
-                           Evoral::Beats pos, Evoral::Beats len);
-       void step_sustain (Evoral::Beats beats);
+                           Temporal::Beats pos, Temporal::Beats len);
+       void step_sustain (Temporal::Beats beats);
        void set_height (double);
        void apply_note_range(uint8_t lowest, uint8_t highest, bool force=false);
 
@@ -109,31 +115,32 @@ public:
        uint32_t get_fill_color() const;
        void color_handler ();
 
-       void show_step_edit_cursor (Evoral::Beats pos);
-       void move_step_edit_cursor (Evoral::Beats pos);
+       void show_step_edit_cursor (Temporal::Beats pos);
+       void move_step_edit_cursor (Temporal::Beats pos);
        void hide_step_edit_cursor ();
-       void set_step_edit_cursor_width (Evoral::Beats beats);
+       void set_step_edit_cursor_width (Temporal::Beats beats);
 
        void redisplay_model();
 
        GhostRegion* add_ghost (TimeAxisView&);
 
        NoteBase* add_note(const boost::shared_ptr<NoteType> note, bool visible);
-       void resolve_note(uint8_t note_num, Evoral::Beats end_time);
+       void resolve_note(uint8_t note_num, Temporal::Beats end_time);
 
        void cut_copy_clear (Editing::CutCopyOp);
-       bool paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx, const int32_t sub_num);
-       void paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer&);
+       bool paste (samplepos_t pos, const ::Selection& selection, PasteContext& ctx, const int32_t sub_num);
+       void paste_internal (samplepos_t pos, unsigned paste_count, float times, const MidiCutBuffer&);
 
        void add_canvas_patch_change (ARDOUR::MidiModel::PatchChangePtr patch, const std::string& displaytext, bool);
+       void remove_canvas_patch_change (PatchChange* pc);
 
        /** Look up the given time and channel in the 'automation' and set keys accordingly.
         * @param time the time of the patch change event
         * @param channel the MIDI channel of the event
-        * @key a reference to an instance of MIDI::Name::PatchPrimaryKey whose fields will
+        * @param key a reference to an instance of MIDI::Name::PatchPrimaryKey whose fields will
         *        will be set according to the result of the lookup
         */
-       void get_patch_key_at (Evoral::Beats time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const;
+       void get_patch_key_at (Temporal::Beats time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const;
 
        /** Convert a given PatchChange into a PatchPrimaryKey
         */
@@ -144,16 +151,18 @@ public:
         * @param new_patch new patch
         */
        void change_patch_change (PatchChange& old_patch, const MIDI::Name::PatchPrimaryKey& new_patch);
-       void change_patch_change (ARDOUR::MidiModel::PatchChangePtr, Evoral::PatchChange<Evoral::Beats> const &);
+       void change_patch_change (ARDOUR::MidiModel::PatchChangePtr, Evoral::PatchChange<Temporal::Beats> const &);
 
-       void add_patch_change (framecnt_t, Evoral::PatchChange<Evoral::Beats> const &);
-       void move_patch_change (PatchChange &, Evoral::Beats);
+       void add_patch_change (samplecnt_t, Evoral::PatchChange<Temporal::Beats> const &);
+       void move_patch_change (PatchChange &, Temporal::Beats);
        void delete_patch_change (PatchChange *);
        void edit_patch_change (PatchChange *);
 
        void delete_sysex (SysEx*);
 
        /** Change a patch to the next or previous bank/program.
+        *
+        * @param patch The patch-change instance (canvas item)
         * @param bank If true, step bank, otherwise, step program.
         * @param delta Amount to adjust number.
         */
@@ -175,11 +184,11 @@ public:
 
        void start_note_diff_command (std::string name = "midi edit");
        void note_diff_add_change (NoteBase* ev, ARDOUR::MidiModel::NoteDiffCommand::Property, uint8_t val);
-       void note_diff_add_change (NoteBase* ev, ARDOUR::MidiModel::NoteDiffCommand::Property, Evoral::Beats val);
+       void note_diff_add_change (NoteBase* ev, ARDOUR::MidiModel::NoteDiffCommand::Property, Temporal::Beats val);
        void note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity = false);
        void note_diff_remove_note (NoteBase* ev);
 
-       void apply_diff (bool as_subcommand = false);
+       void apply_diff (bool as_subcommand = false, bool was_copy = false);
        void abort_command();
 
        void   note_entered(NoteBase* ev);
@@ -196,18 +205,24 @@ public:
        void   delete_note (boost::shared_ptr<NoteType>);
        size_t selection_size() { return _selection.size(); }
        void   select_all_notes ();
-       void   select_range(framepos_t start, framepos_t end);
+       void   select_range(samplepos_t start, samplepos_t end);
        void   invert_selection ();
 
+       Temporal::Beats earliest_in_selection ();
        void move_selection(double dx, double dy, double cumulative_dy);
-       void note_dropped (NoteBase* ev, ARDOUR::frameoffset_t, int8_t d_note);
+       void note_dropped (NoteBase* ev, double d_qn, int8_t d_note, bool copy);
+       NoteBase* copy_selection (NoteBase* primary);
+       void move_copies(double dx_qn, double dy, double cumulative_dy);
 
        void select_notes (std::list<Evoral::event_id_t>);
        void select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend);
        void toggle_matching_notes (uint8_t notenum, uint16_t channel_mask);
 
-       /** Return true iff the note is within the extent of the region.
+       /** Test if a note is within this region's range
+        *
+        * @param note the note to test
         * @param visible will be set to true if the note is within the visible note range, false otherwise.
+        * @return true iff the note is within the (time) extent of the region.
         */
        bool note_in_region_range(const boost::shared_ptr<NoteType> note, bool& visible) const;
 
@@ -255,49 +270,51 @@ public:
         */
        double snap_to_pixel(double x, bool ensure_snap = false);
 
-       /** Snap a region relative pixel coordinate to frame units.
+       /** Snap a region relative pixel coordinate to sample units.
         * @param x a pixel coordinate relative to region start
         * @param ensure_snap ignore SnapOff and magnetic snap.
         * Required for inverting snap logic with modifier keys and snap delta calculation.
-        * @return the snapped framepos_t coordinate relative to region start
+        * @return the snapped samplepos_t coordinate relative to region start
         */
-       framepos_t snap_pixel_to_sample(double x, bool ensure_snap = false);
+       samplepos_t snap_pixel_to_sample(double x, bool ensure_snap = false);
 
-       /** Convert a timestamp in beats into frames (both relative to region position) */
-       framepos_t region_beats_to_region_frames(Evoral::Beats beats) const;
-       /** Convert a timestamp in beats into absolute frames */
-       framepos_t region_beats_to_absolute_frames(Evoral::Beats beats) const {
-               return _region->position() + region_beats_to_region_frames (beats);
+       /** Convert a timestamp in beats into samples (both relative to region position) */
+       samplepos_t region_beats_to_region_samples(Temporal::Beats beats) const;
+       /** Convert a timestamp in beats into absolute samples */
+       samplepos_t region_beats_to_absolute_samples(Temporal::Beats beats) const {
+               return _region->position() + region_beats_to_region_samples (beats);
        }
-       /** Convert a timestamp in frames to beats (both relative to region position) */
-       Evoral::Beats region_frames_to_region_beats(framepos_t) const;
-       double region_frames_to_region_beats_double(framepos_t) const;
-
-       /** Convert a timestamp in beats measured from source start into absolute frames */
-       framepos_t source_beats_to_absolute_frames(Evoral::Beats beats) const;
-       /** Convert a timestamp in beats measured from source start into region-relative frames */
-       framepos_t source_beats_to_region_frames(Evoral::Beats beats) const {
-               return source_beats_to_absolute_frames (beats) - _region->position();
+       /** Convert a timestamp in samples to beats (both relative to region position) */
+       Temporal::Beats region_samples_to_region_beats(samplepos_t) const;
+       double region_samples_to_region_beats_double(samplepos_t) const;
+
+       /** Convert a timestamp in beats measured from source start into absolute samples */
+       samplepos_t source_beats_to_absolute_samples(Temporal::Beats beats) const;
+       /** Convert a timestamp in beats measured from source start into region-relative samples */
+       samplepos_t source_beats_to_region_samples(Temporal::Beats beats) const {
+               return source_beats_to_absolute_samples (beats) - _region->position();
        }
-       /** Convert a timestamp in absolute frames to beats measured from source start*/
-       Evoral::Beats absolute_frames_to_source_beats(framepos_t) const;
+       /** Convert a timestamp in absolute samples to beats measured from source start*/
+       Temporal::Beats absolute_samples_to_source_beats(samplepos_t) const;
 
-       ARDOUR::BeatsFramesConverter const & region_relative_time_converter () const {
+       ARDOUR::BeatsSamplesConverter const & region_relative_time_converter () const {
                return _region_relative_time_converter;
        }
 
-       ARDOUR::BeatsFramesConverter const & source_relative_time_converter () const {
+       ARDOUR::BeatsSamplesConverter const & source_relative_time_converter () const {
                return _source_relative_time_converter;
        }
 
-       ARDOUR::DoubleBeatsFramesConverter const & region_relative_time_converter_double () const {
+       ARDOUR::DoubleBeatsSamplesConverter const & region_relative_time_converter_double () const {
                return _region_relative_time_converter_double;
        }
 
+       double session_relative_qn (double qn) const;
+
        void goto_previous_note (bool add_to_selection);
        void goto_next_note (bool add_to_selection);
-       void change_note_lengths (bool, bool, Evoral::Beats beats, bool start, bool end);
-        void change_velocities (bool up, bool fine, bool allow_smush, bool all_together);
+       void change_note_lengths (bool, bool, Temporal::Beats beats, bool start, bool end);
+       void change_velocities (bool up, bool fine, bool allow_smush, bool all_together);
        void transpose (bool up, bool fine, bool allow_smush);
        void nudge_notes (bool forward, bool fine);
        void channel_edit ();
@@ -321,13 +338,13 @@ public:
        void trim_front_ending ();
 
        /** Add a note to the model, and the view, at a canvas (click) coordinate.
-        * \param t time in frames relative to the position of the region
+        * \param t time in samples relative to the position of the region
         * \param y vertical position in pixels
         * \param length duration of the note in beats
         * \param state the keyboard modifier mask for the canvas event (click).
         * \param shift_snap true alters snap behavior to round down always (false if the gui has already done that).
         */
-       void create_note_at (framepos_t t, double y, Evoral::Beats length, uint32_t state, bool shift_snap);
+       void create_note_at (samplepos_t t, double y, Temporal::Beats length, uint32_t state, bool shift_snap);
 
        /** An external request to clear the note selection, remove MRV from editor
         * selection.
@@ -358,6 +375,7 @@ private:
        friend class NoteDrag;
        friend class NoteCreateDrag;
        friend class HitCreateDrag;
+       friend class MidiGhostRegion;
 
        friend class EditNoteDialog;
 
@@ -380,7 +398,7 @@ private:
        bool note_canvas_event(GdkEvent* ev);
 
        void midi_channel_mode_changed ();
-        PBD::ScopedConnection _channel_mode_changed_connection;
+       PBD::ScopedConnection _channel_mode_changed_connection;
        void instrument_settings_changed ();
        PBD::ScopedConnection _instrument_changed_connection;
 
@@ -392,7 +410,7 @@ private:
        void trim_note(NoteBase* ev, ARDOUR::MidiModel::TimeType start_delta,
                       ARDOUR::MidiModel::TimeType end_delta);
 
-       void update_drag_selection (framepos_t start, framepos_t end, double y0, double y1, bool extend);
+       void update_drag_selection (samplepos_t start, samplepos_t end, double y0, double y1, bool extend);
        void update_vertical_drag_selection (double last_y, double y, bool extend);
 
        void add_to_selection (NoteBase*);
@@ -408,16 +426,18 @@ private:
        uint8_t  _current_range_min;
        uint8_t  _current_range_max;
 
-       typedef std::list<NoteBase*>                          Events;
-       typedef std::vector< boost::shared_ptr<PatchChange> > PatchChanges;
-       typedef std::vector< boost::shared_ptr<SysEx> >       SysExes;
+       typedef boost::unordered_map<boost::shared_ptr<NoteType>, NoteBase*>                             Events;
+       typedef boost::unordered_map<ARDOUR::MidiModel::PatchChangePtr, boost::shared_ptr<PatchChange> > PatchChanges;
+       typedef boost::unordered_map<ARDOUR::MidiModel::constSysExPtr, boost::shared_ptr<SysEx> >        SysExes;
+       typedef std::vector<NoteBase*> CopyDragEvents;
 
-       ARDOUR::BeatsFramesConverter _region_relative_time_converter;
-       ARDOUR::BeatsFramesConverter _source_relative_time_converter;
-       ARDOUR::DoubleBeatsFramesConverter _region_relative_time_converter_double;
+       ARDOUR::BeatsSamplesConverter _region_relative_time_converter;
+       ARDOUR::BeatsSamplesConverter _source_relative_time_converter;
+       ARDOUR::DoubleBeatsSamplesConverter _region_relative_time_converter_double;
 
        boost::shared_ptr<ARDOUR::MidiModel> _model;
        Events                               _events;
+       CopyDragEvents                       _copy_drag_events;
        PatchChanges                         _patch_changes;
        SysExes                              _sys_exes;
        Note**                               _active_notes;
@@ -427,8 +447,8 @@ private:
        double                               _last_ghost_x;
        double                               _last_ghost_y;
        ArdourCanvas::Rectangle*             _step_edit_cursor;
-       Evoral::Beats                        _step_edit_cursor_width;
-       Evoral::Beats                        _step_edit_cursor_position;
+       Temporal::Beats                      _step_edit_cursor_width;
+       Temporal::Beats                      _step_edit_cursor_position;
        NoteBase*                            _channel_selection_scoped_note;
 
        MouseState _mouse_state;
@@ -437,9 +457,6 @@ private:
        /** Currently selected NoteBase objects */
        Selection _selection;
 
-       bool _sort_needed;
-       void time_sort_events ();
-
        MidiCutBuffer* selection_as_cut_buffer () const;
 
        /** New notes (created in the current command) which should be selected
@@ -462,6 +479,9 @@ private:
        NoteBase* find_canvas_note (Evoral::event_id_t id);
        Events::iterator _optimization_iterator;
 
+       boost::shared_ptr<PatchChange> find_canvas_patch_change (ARDOUR::MidiModel::PatchChangePtr p);
+       boost::shared_ptr<SysEx> find_canvas_sys_ex (ARDOUR::MidiModel::SysExPtr s);
+
        void update_note (NoteBase*, bool update_ghost_regions = true);
        void update_sustained (Note *, bool update_ghost_regions = true);
        void update_hit (Hit *, bool update_ghost_regions = true);
@@ -486,7 +506,7 @@ private:
 
        void drop_down_keys ();
        void maybe_select_by_position (GdkEventButton* ev, double x, double y);
-       void get_events (Events& e, Evoral::Sequence<Evoral::Beats>::NoteOperator op, uint8_t val, int chan_mask = 0);
+       void get_events (Events& e, Evoral::Sequence<Temporal::Beats>::NoteOperator op, uint8_t val, int chan_mask = 0);
 
        void display_patch_changes_on_channel (uint8_t, bool);
 
@@ -494,7 +514,7 @@ private:
        void data_recorded (boost::weak_ptr<ARDOUR::MidiSource>);
 
        /** Get grid type as beats, or default to 1 if not snapped to beats. */
-       Evoral::Beats get_grid_beats(framepos_t pos) const;
+       Temporal::Beats get_grid_beats(samplepos_t pos) const;
 
        void remove_ghost_note ();
        void mouse_mode_changed ();
@@ -502,7 +522,7 @@ private:
        void leave_internal ();
        void hide_verbose_cursor ();
 
-       framecnt_t _last_display_zoom;
+       samplecnt_t _last_display_zoom;
 
        double    _last_event_x;
        double    _last_event_y;
@@ -512,17 +532,17 @@ private:
 
        bool _mouse_changed_selection;
 
-       ArdourCanvas::Color _patch_change_outline;
-       ArdourCanvas::Color _patch_change_fill;
+       Gtkmm2ext::Color _patch_change_outline;
+       Gtkmm2ext::Color _patch_change_fill;
 
-       Evoral::Beats snap_frame_to_grid_underneath (framepos_t p, int32_t divisions, bool shift_snap) const;
+       Temporal::Beats snap_sample_to_grid_underneath (samplepos_t p, int32_t divisions, bool shift_snap) const;
 
        PBD::ScopedConnection _mouse_mode_connection;
 
        boost::shared_ptr<CursorContext> _press_cursor_ctx;
 
-        ARDOUR::ChannelMode get_channel_mode() const;
-        uint16_t get_selected_channels () const;
+       ARDOUR::ChannelMode get_channel_mode() const;
+       uint16_t get_selected_channels () const;
 
        inline double contents_height() const { return (_height - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE - 2); }
        inline double contents_note_range () const { return (double)(_current_range_max - _current_range_min + 1); }