move a bunch of MIDI editing into EditingContext

This commit is contained in:
Paul Davis 2024-01-30 12:40:30 -07:00
parent 89f780b78c
commit 23a672b45d
10 changed files with 462 additions and 358 deletions

View File

@ -21,19 +21,28 @@
#include "pbd/error.h"
#include "pbd/stacktrace.h"
#include "ardour/legatize.h"
#include "ardour/midi_region.h"
#include "ardour/midi_source.h"
#include "ardour/rc_configuration.h"
#include "ardour/transpose.h"
#include "ardour/quantize.h"
#include "gtkmm2ext/bindings.h"
#include "actions.h"
#include "ardour_ui.h"
#include "edit_note_dialog.h"
#include "editing_context.h"
#include "editor_drag.h"
#include "keyboard.h"
#include "midi_region_view.h"
#include "note_base.h"
#include "quantize_dialog.h"
#include "selection.h"
#include "selection_memento.h"
#include "transform_dialog.h"
#include "transpose_dialog.h"
#include "verbose_cursor.h"
#include "pbd/i18n.h"
@ -48,6 +57,7 @@ using namespace Temporal;
using std::string;
sigc::signal<void> EditingContext::DropDownKeys;
Gtkmm2ext::Bindings* EditingContext::button_bindings = nullptr;
static const gchar *_grid_type_strings[] = {
N_("No Grid"),
@ -104,6 +114,17 @@ EditingContext::EditingContext ()
, _visible_canvas_height (0)
, quantize_dialog (nullptr)
{
if (!button_bindings) {
button_bindings = new Bindings ("editor-mouse");
XMLNode* node = button_settings();
if (node) {
for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) {
button_bindings->load_operation (**i);
}
}
}
grid_type_strings = I18N (_grid_type_strings);
}
@ -260,7 +281,7 @@ EditingContext::register_midi_actions (Bindings* midi_bindings)
}
void
EditingContext::midi_action (void (MidiRegionView::*method)())
EditingContext::midi_action (void (MidiView::*method)())
{
MidiRegionSelection ms = get_selection().midi_regions();
@ -1638,3 +1659,260 @@ EditingContext::typed_event (ArdourCanvas::Item* item, GdkEvent *event, ItemType
return ret;
}
void
EditingContext::popup_note_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
{
using namespace Menu_Helpers;
NoteBase* note = reinterpret_cast<NoteBase*>(item->get_data("notebase"));
if (!note) {
return;
}
/* We need to get the selection here and pass it to the operations, since
popping up the menu will cause a region leave event which clears
entered_regionview. */
MidiView& mrv = note->region_view();
const RegionSelection rs = region_selection ();
const uint32_t sel_size = mrv.selection_size ();
MenuList& items = _note_context_menu.items();
items.clear();
if (sel_size > 0) {
items.push_back (MenuElem(_("Delete"), sigc::mem_fun(mrv, &MidiView::delete_selection)));
}
items.push_back(MenuElem(_("Edit..."), sigc::bind(sigc::mem_fun(*this, &EditingContext::edit_notes), &mrv)));
items.push_back(MenuElem(_("Transpose..."), sigc::bind(sigc::mem_fun(*this, &EditingContext::transpose_regions), rs)));
items.push_back(MenuElem(_("Legatize"), sigc::bind(sigc::mem_fun(*this, &EditingContext::legatize_regions), rs, false)));
if (sel_size < 2) {
items.back().set_sensitive (false);
}
items.push_back(MenuElem(_("Quantize..."), sigc::bind(sigc::mem_fun(*this, &EditingContext::quantize_regions), rs)));
items.push_back(MenuElem(_("Remove Overlap"), sigc::bind(sigc::mem_fun(*this, &EditingContext::legatize_regions), rs, true)));
if (sel_size < 2) {
items.back().set_sensitive (false);
}
items.push_back(MenuElem(_("Transform..."), sigc::bind(sigc::mem_fun(*this, &EditingContext::transform_regions), rs)));
_note_context_menu.popup (event->button.button, event->button.time);
}
XMLNode*
EditingContext::button_settings () const
{
XMLNode* settings = ARDOUR_UI::instance()->editor_settings();
XMLNode* node = find_named_node (*settings, X_("Buttons"));
if (!node) {
node = new XMLNode (X_("Buttons"));
}
return node;
}
std::vector<MidiView*>
EditingContext::filter_to_unique_midi_region_views (RegionSelection const & rs) const
{
typedef std::pair<std::shared_ptr<MidiSource>,timepos_t> MapEntry;
std::set<MapEntry> single_region_set;
std::vector<MidiView*> views;
/* build a list of regions that are unique with respect to their source
* and start position. Note: this is non-exhaustive... if someone has a
* non-forked copy of a MIDI region and then suitably modifies it, this
* will still put both regions into the list of things to be acted
* upon.
*
* Solution: user should not select both regions, or should fork one of them.
*/
for (auto const & rv : rs) {
MidiView* mrv = dynamic_cast<MidiView*> (rv);
if (!mrv) {
continue;
}
MapEntry entry = make_pair (mrv->midi_region()->midi_source(), mrv->midi_region()->start());
if (single_region_set.insert (entry).second) {
views.push_back (mrv);
}
}
return views;
}
void
EditingContext::quantize_region ()
{
if (_session) {
quantize_regions(region_selection ());
}
}
void
EditingContext::quantize_regions (const RegionSelection& rs)
{
if (rs.n_midi_regions() == 0) {
return;
}
Quantize* quant = get_quantize_op ();
if (!quant) {
return;
}
if (!quant->empty()) {
apply_midi_note_edit_op (*quant, rs);
}
delete quant;
}
void
EditingContext::legatize_region (bool shrink_only)
{
if (_session) {
legatize_regions(region_selection (), shrink_only);
}
}
void
EditingContext::legatize_regions (const RegionSelection& rs, bool shrink_only)
{
if (rs.n_midi_regions() == 0) {
return;
}
Legatize legatize(shrink_only);
apply_midi_note_edit_op (legatize, rs);
}
void
EditingContext::transform_region ()
{
if (_session) {
transform_regions(region_selection ());
}
}
void
EditingContext::transform_regions (const RegionSelection& rs)
{
if (rs.n_midi_regions() == 0) {
return;
}
TransformDialog td;
td.present();
const int r = td.run();
td.hide();
if (r == Gtk::RESPONSE_OK) {
Transform transform(td.get());
apply_midi_note_edit_op(transform, rs);
}
}
void
EditingContext::transpose_region ()
{
if (_session) {
transpose_regions(region_selection ());
}
}
void
EditingContext::transpose_regions (const RegionSelection& rs)
{
if (rs.n_midi_regions() == 0) {
return;
}
TransposeDialog d;
int const r = d.run ();
if (r == RESPONSE_ACCEPT) {
Transpose transpose(d.semitones ());
apply_midi_note_edit_op (transpose, rs);
}
}
void
EditingContext::edit_notes (MidiView* mrv)
{
MidiView::Selection const & s = mrv->selection();
if (s.empty ()) {
return;
}
EditNoteDialog* d = new EditNoteDialog (mrv, s);
d->show_all ();
d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &EditingContext::note_edit_done), d));
}
void
EditingContext::note_edit_done (int r, EditNoteDialog* d)
{
d->done (r);
delete d;
}
PBD::Command*
EditingContext::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiView& mrv)
{
Evoral::Sequence<Temporal::Beats>::Notes selected;
mrv.selection_as_notelist (selected, true);
if (selected.empty()) {
return 0;
}
std::vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
v.push_back (selected);
timepos_t pos = mrv.midi_region()->source_position();
return op (mrv.midi_region()->model(), pos.beats(), v);
}
void
EditingContext::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
{
if (rs.empty()) {
return;
}
bool in_command = false;
std::vector<MidiView*> views = filter_to_unique_midi_region_views (rs);
for (auto & mv : views) {
Command* cmd = apply_midi_note_edit_op_to_region (op, *mv);
if (cmd) {
if (!in_command) {
begin_reversible_command (op.name ());
in_command = true;
}
(*cmd)();
_session->add_command (cmd);
}
}
if (in_command) {
commit_reversible_command ();
_session->set_dirty ();
}
}

View File

@ -60,7 +60,9 @@ class XMLNode;
class CursorContext;
class DragManager;
class EditorCursor;
class EditNoteDialog;
class MidiRegionView;
class MidiView;
class MouseCursors;
class VerboseCursor;
class TrackViewList;
@ -308,9 +310,14 @@ public:
/* MIDI actions, proxied to selected MidiRegionView(s) */
ARDOUR::Quantize* get_quantize_op ();
virtual void apply_midi_note_edit_op (ARDOUR::MidiOperator& op, const RegionSelection& rs) = 0;
void midi_action (void (MidiRegionView::*method)());
virtual std::vector<MidiRegionView*> filter_to_unique_midi_region_views (RegionSelection const & ms) const = 0;
void apply_midi_note_edit_op (ARDOUR::MidiOperator& op, const RegionSelection& rs);
void midi_action (void (MidiView::*method)());
std::vector<MidiView*> filter_to_unique_midi_region_views (RegionSelection const & ms) const;
void quantize_region ();
void transform_region ();
void legatize_region (bool shrink_only);
void transpose_region ();
void register_midi_actions (Gtkmm2ext::Bindings*);
@ -470,6 +477,23 @@ public:
virtual bool key_press_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) = 0;
virtual bool key_release_handler (ArdourCanvas::Item*, GdkEvent*, ItemType) = 0;
void popup_note_context_menu (ArdourCanvas::Item*, GdkEvent*);
Gtk::Menu _note_context_menu;
static Gtkmm2ext::Bindings* button_bindings;
XMLNode* button_settings () const;
virtual RegionSelection region_selection() = 0;
void edit_notes (MidiView*);
void note_edit_done (int, EditNoteDialog*);
PBD::Command* apply_midi_note_edit_op_to_region (ARDOUR::MidiOperator& op, MidiView& mrv);
void quantize_regions (const RegionSelection& rs);
void legatize_regions (const RegionSelection& rs, bool shrink_only);
void transform_regions (const RegionSelection& rs);
void transpose_regions (const RegionSelection& rs);
};

View File

@ -327,7 +327,6 @@ Editor::Editor ()
, _err_screen_engine (0)
, cut_buffer_start (0)
, cut_buffer_length (0)
, button_bindings (0)
, last_paste_pos (timepos_t::max (Temporal::AudioTime)) /* XXX NUTEMPO how to choose time domain */
, paste_count (0)
, sfbrowser (0)
@ -820,17 +819,6 @@ Editor::Editor ()
_show_marker_lines = false;
/* Button bindings */
button_bindings = new Bindings ("editor-mouse");
XMLNode* node = button_settings();
if (node) {
for (XMLNodeList::const_iterator i = node->children().begin(); i != node->children().end(); ++i) {
button_bindings->load_operation (**i);
}
}
constructed = true;
/* grab current parameter state */
@ -884,19 +872,6 @@ Editor::~Editor()
}
}
XMLNode*
Editor::button_settings () const
{
XMLNode* settings = ARDOUR_UI::instance()->editor_settings();
XMLNode* node = find_named_node (*settings, X_("Buttons"));
if (!node) {
node = new XMLNode (X_("Buttons"));
}
return node;
}
bool
Editor::get_smart_mode () const
{
@ -6060,60 +6035,6 @@ Editor::popup_control_point_context_menu (ArdourCanvas::Item* item, GdkEvent* ev
_control_point_context_menu.popup (event->button.button, event->button.time);
}
void
Editor::popup_note_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
{
using namespace Menu_Helpers;
NoteBase* note = reinterpret_cast<NoteBase*>(item->get_data("notebase"));
if (!note) {
return;
}
/* We need to get the selection here and pass it to the operations, since
popping up the menu will cause a region leave event which clears
entered_regionview. */
MidiView& mrv = note->region_view();
const RegionSelection rs = get_regions_from_selection_and_entered ();
const uint32_t sel_size = mrv.selection_size ();
MenuList& items = _note_context_menu.items();
items.clear();
if (sel_size > 0) {
items.push_back(MenuElem(_("Delete"),
sigc::mem_fun(mrv, &MidiView::delete_selection)));
}
items.push_back(MenuElem(_("Edit..."),
sigc::bind(sigc::mem_fun(*this, &Editor::edit_notes), &mrv)));
items.push_back(MenuElem(_("Transpose..."),
sigc::bind(sigc::mem_fun(*this, &Editor::transpose_regions), rs)));
items.push_back(MenuElem(_("Legatize"),
sigc::bind(sigc::mem_fun(*this, &Editor::legatize_regions), rs, false)));
if (sel_size < 2) {
items.back().set_sensitive (false);
}
items.push_back(MenuElem(_("Quantize..."),
sigc::bind(sigc::mem_fun(*this, &Editor::quantize_regions), rs)));
items.push_back(MenuElem(_("Remove Overlap"),
sigc::bind(sigc::mem_fun(*this, &Editor::legatize_regions), rs, true)));
if (sel_size < 2) {
items.back().set_sensitive (false);
}
items.push_back(MenuElem(_("Transform..."),
sigc::bind(sigc::mem_fun(*this, &Editor::transform_regions), rs)));
_note_context_menu.popup (event->button.button, event->button.time);
}
void
Editor::zoom_vertical_modifier_released()
{
@ -6296,5 +6217,5 @@ Editor::snap_to_internal (timepos_t& start, Temporal::RoundMode direction, SnapP
ArdourCanvas::Duple
Editor::upper_left() const
{
get_trackview_group ()->canvas_origin ().y;
return get_trackview_group ()->canvas_origin ();
}

View File

@ -538,6 +538,8 @@ protected:
void suspend_route_redisplay ();
void resume_route_redisplay ();
RegionSelection region_selection();
private:
void color_handler ();
@ -774,9 +776,6 @@ private:
void popup_control_point_context_menu (ArdourCanvas::Item*, GdkEvent*);
Gtk::Menu _control_point_context_menu;
void popup_note_context_menu (ArdourCanvas::Item*, GdkEvent*);
Gtk::Menu _note_context_menu;
void initial_display ();
void add_stripables (ARDOUR::StripableList&);
void add_routes (ARDOUR::RouteList&);
@ -1166,9 +1165,6 @@ private:
bool key_press_handler (ArdourCanvas::Item*, GdkEvent*, ItemType);
bool key_release_handler (ArdourCanvas::Item*, GdkEvent*, ItemType);
Gtkmm2ext::Bindings* button_bindings;
XMLNode* button_settings () const;
/* KEYMAP HANDLING */
void register_actions ();
@ -1247,18 +1243,9 @@ private:
void normalize_region ();
void adjust_region_gain (bool up);
void reset_region_gain ();
void apply_midi_note_edit_op (ARDOUR::MidiOperator& op, const RegionSelection& rs);
void set_tempo_curve_range (double& max, double& min) const;
void quantize_region ();
void quantize_regions (const RegionSelection& rs);
void legatize_region (bool shrink_only);
void legatize_regions (const RegionSelection& rs, bool shrink_only);
void deinterlace_midi_regions (const RegionSelection& rs);
void deinterlace_selected_midi_regions ();
void transform_region ();
void transform_regions (const RegionSelection& rs);
void transpose_region ();
void transpose_regions (const RegionSelection& rs);
void set_tempo_curve_range (double& max, double& min) const;
void insert_patch_change (bool from_context);
void fork_selected_regions ();
void fork_regions_from_unselected ();
@ -1699,7 +1686,6 @@ private:
void edit_meter_marker (MeterMarker&);
void edit_bbt_marker (BBTMarker&);
void edit_control_point (ArdourCanvas::Item*);
void edit_notes (MidiView*);
void edit_region (RegionView*);
void edit_current_meter ();
@ -2116,8 +2102,6 @@ private:
void apply_filter (ARDOUR::Filter&, std::string cmd, ProgressReporter* progress = 0);
PBD::Command* apply_midi_note_edit_op_to_region (ARDOUR::MidiOperator& op, MidiRegionView& mrv);
/* plugin setup */
int plugin_setup (std::shared_ptr<ARDOUR::Route>, std::shared_ptr<ARDOUR::PluginInsert>, ARDOUR::Route::PluginSetupOptions);
@ -2304,7 +2288,6 @@ private:
bool _show_touched_automation;
int time_fx (ARDOUR::RegionList&, Temporal::ratio_t ratio, bool pitching, bool fixed_end);
void note_edit_done (int, EditNoteDialog*);
void toggle_sound_midi_notes ();
/** Flag for a bit of a hack wrt control point selection; see set_selected_control_point_from_click */
@ -2328,8 +2311,6 @@ private:
MainMenuDisabler* _main_menu_disabler;
std::vector<MidiRegionView*> filter_to_unique_midi_region_views (RegionSelection const & ms) const;
/* private helper functions to help with registering region actions */
Glib::RefPtr<Gtk::Action> register_region_action (Glib::RefPtr<Gtk::ActionGroup> group, Editing::RegionActionTarget, char const* name, char const* label, sigc::slot<void> slot);

View File

@ -2356,28 +2356,6 @@ Editor::edit_control_point (ArdourCanvas::Item* item)
}
}
void
Editor::edit_notes (MidiView* mrv)
{
MidiView::Selection const & s = mrv->selection();
if (s.empty ()) {
return;
}
EditNoteDialog* d = new EditNoteDialog (mrv, s);
d->show_all ();
d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
}
void
Editor::note_edit_done (int r, EditNoteDialog* d)
{
d->done (r);
delete d;
}
void
Editor::edit_region (RegionView* rv)
{

View File

@ -6037,54 +6037,6 @@ Editor::strip_region_silence ()
}
}
PBD::Command*
Editor::apply_midi_note_edit_op_to_region (MidiOperator& op, MidiRegionView& mrv)
{
Evoral::Sequence<Temporal::Beats>::Notes selected;
mrv.selection_as_notelist (selected, true);
if (selected.empty()) {
return 0;
}
vector<Evoral::Sequence<Temporal::Beats>::Notes> v;
v.push_back (selected);
timepos_t pos = mrv.midi_region()->source_position();
return op (mrv.midi_region()->model(), pos.beats(), v);
}
void
Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
{
if (rs.empty()) {
return;
}
bool in_command = false;
vector<MidiRegionView*> views = filter_to_unique_midi_region_views (rs);
for (vector<MidiRegionView*>::iterator mrv = views.begin(); mrv != views.end(); ++mrv) {
Command* cmd = apply_midi_note_edit_op_to_region (op, **mrv);
if (cmd) {
if (!in_command) {
begin_reversible_command (op.name ());
in_command = true;
}
(*cmd)();
_session->add_command (cmd);
}
}
if (in_command) {
commit_reversible_command ();
_session->set_dirty ();
}
}
#include "ardour/midi_source.h" // MidiSource::name()
void
@ -6214,140 +6166,6 @@ Editor::fork_selected_regions ()
}
}
void
Editor::quantize_region ()
{
if (_session) {
quantize_regions(get_regions_from_selection_and_entered ());
}
}
void
Editor::quantize_regions (const RegionSelection& rs)
{
if (rs.n_midi_regions() == 0) {
return;
}
Quantize* quant = get_quantize_op ();
if (!quant) {
return;
}
if (!quant->empty()) {
apply_midi_note_edit_op (*quant, rs);
}
delete quant;
}
void
Editor::legatize_region (bool shrink_only)
{
if (_session) {
legatize_regions(get_regions_from_selection_and_entered (), shrink_only);
}
}
void
Editor::deinterlace_midi_regions (const RegionSelection& rs)
{
begin_reversible_command (_("de-interlace midi"));
RegionSelection rcopy = rs;
if (_session) {
for (RegionSelection::iterator i = rcopy.begin (); i != rcopy.end(); i++) {
MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
if (mrv) {
XMLNode& before (mrv->region()->playlist()->get_state());
/* pass the regions to deinterlace_midi_region*/
_session->deinterlace_midi_region(mrv->midi_region());
XMLNode& after (mrv->region()->playlist()->get_state());
_session->add_command (new MementoCommand<Playlist>(*(mrv->region()->playlist()), &before, &after));
}
}
}
/* Remove the original region(s) safely, without rippling, as part of this command */
remove_regions(rs, false /*can_ripple*/, true /*as_part_of_other_command*/);
commit_reversible_command ();
}
void
Editor::deinterlace_selected_midi_regions ()
{
if (_session) {
RegionSelection rs = get_regions_from_selection_and_entered ();
deinterlace_midi_regions(rs);
}
}
void
Editor::legatize_regions (const RegionSelection& rs, bool shrink_only)
{
if (rs.n_midi_regions() == 0) {
return;
}
Legatize legatize(shrink_only);
apply_midi_note_edit_op (legatize, rs);
}
void
Editor::transform_region ()
{
if (_session) {
transform_regions(get_regions_from_selection_and_entered ());
}
}
void
Editor::transform_regions (const RegionSelection& rs)
{
if (rs.n_midi_regions() == 0) {
return;
}
TransformDialog td;
td.present();
const int r = td.run();
td.hide();
if (r == Gtk::RESPONSE_OK) {
Transform transform(td.get());
apply_midi_note_edit_op(transform, rs);
}
}
void
Editor::transpose_region ()
{
if (_session) {
transpose_regions(get_regions_from_selection_and_entered ());
}
}
void
Editor::transpose_regions (const RegionSelection& rs)
{
if (rs.n_midi_regions() == 0) {
return;
}
TransposeDialog d;
int const r = d.run ();
if (r == RESPONSE_ACCEPT) {
Transpose transpose(d.semitones ());
apply_midi_note_edit_op (transpose, rs);
}
}
void
Editor::insert_patch_change (bool from_context)
@ -6470,6 +6288,43 @@ Editor::apply_filter (Filter& filter, string command, ProgressReporter* progress
}
}
void
Editor::deinterlace_midi_regions (const RegionSelection& rs)
{
begin_reversible_command (_("de-interlace midi"));
RegionSelection rcopy = rs;
if (_session) {
for (RegionSelection::iterator i = rcopy.begin (); i != rcopy.end(); i++) {
MidiRegionView* const mrv = dynamic_cast<MidiRegionView*> (*i);
if (mrv) {
XMLNode& before (mrv->region()->playlist()->get_state());
/* pass the regions to deinterlace_midi_region*/
_session->deinterlace_midi_region(mrv->midi_region());
XMLNode& after (mrv->region()->playlist()->get_state());
_session->add_command (new MementoCommand<Playlist>(*(mrv->region()->playlist()), &before, &after));
}
}
}
/* Remove the original region(s) safely, without rippling, as part of this command */
remove_regions(rs, false /*can_ripple*/, true /*as_part_of_other_command*/);
commit_reversible_command ();
}
void
Editor::deinterlace_selected_midi_regions ()
{
if (_session) {
RegionSelection rs = region_selection ();
deinterlace_midi_regions(rs);
}
}
void
Editor::external_edit_region ()
{
@ -9219,41 +9074,6 @@ Editor::launch_playlist_selector ()
}
}
vector<MidiRegionView*>
Editor::filter_to_unique_midi_region_views (RegionSelection const & ms) const
{
typedef std::pair<std::shared_ptr<MidiSource>,timepos_t> MapEntry;
std::set<MapEntry> single_region_set;
vector<MidiRegionView*> views;
/* build a list of regions that are unique with respect to their source
* and start position. Note: this is non-exhaustive... if someone has a
* non-forked copy of a MIDI region and then suitably modifies it, this
* will still put both regions into the list of things to be acted
* upon.
*
* Solution: user should not select both regions, or should fork one of them.
*/
for (MidiRegionSelection::const_iterator i = ms.begin(); i != ms.end(); ++i) {
MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*i);
if (!mrv) {
continue;
}
MapEntry entry = make_pair (mrv->midi_region()->midi_source(), mrv->region()->start());
if (single_region_set.insert (entry).second) {
views.push_back (mrv);
}
}
return views;
}
void
Editor::add_region_marker ()
{

View File

@ -2476,3 +2476,9 @@ Editor::move_selected_tracks (bool up)
ensure_time_axis_view_is_visible (*scroll_to, false);
}
}
RegionSelection
Editor::region_selection()
{
return get_regions_from_selection_and_entered ();
}

View File

@ -26,9 +26,12 @@
#include "canvas/rectangle.h"
#include "editor_cursors.h"
#include "editor_drag.h"
#include "keyboard.h"
#include "midi_cue_background.h"
#include "midi_cue_editor.h"
#include "midi_cue_view.h"
#include "note_base.h"
#include "ui_config.h"
#include "verbose_cursor.h"
@ -36,12 +39,14 @@
using namespace ARDOUR;
using namespace ArdourCanvas;
using namespace Gtkmm2ext;
using namespace Temporal;
MidiCueEditor::MidiCueEditor()
: vertical_adjustment (0.0, 0.0, 10.0, 400.0)
, horizontal_adjustment (0.0, 0.0, 1e16)
, view (nullptr)
, mouse_mode (Editing::MouseDraw)
{
build_canvas ();
@ -250,14 +255,51 @@ MidiCueEditor::set_region (std::shared_ptr<ARDOUR::MidiTrack> t, std::shared_ptr
}
bool
MidiCueEditor::button_press_handler (ArdourCanvas::Item*, GdkEvent*, ItemType)
MidiCueEditor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
{
switch (event->button.button) {
case 1:
return button_press_handler_1 (item, event, item_type);
break;
case 2:
return button_press_handler_2 (item, event, item_type);
break;
case 3:
break;
default:
return button_press_dispatch (&event->button);
break;
}
return true;
}
bool
MidiCueEditor::button_press_handler_1 (ArdourCanvas::Item*, GdkEvent*, ItemType)
MidiCueEditor::button_press_handler_1 (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
{
NoteBase* note = nullptr;
if (mouse_mode == Editing::MouseContent) {
switch (item_type) {
case NoteItem:
/* Existing note: allow trimming/motion */
if ((note = reinterpret_cast<NoteBase*> (item->get_data ("notebase")))) {
if (note->big_enough_to_trim() && note->mouse_near_ends()) {
_drags->set (new NoteResizeDrag (*this, item), event, get_canvas_cursor());
} else {
_drags->set (new NoteDrag (*this, item), event);
}
}
return true;
default:
break;
}
}
return true;
}
@ -268,21 +310,39 @@ MidiCueEditor::button_press_handler_2 (ArdourCanvas::Item*, GdkEvent*, ItemType)
}
bool
MidiCueEditor::button_release_handler (ArdourCanvas::Item*, GdkEvent*, ItemType)
MidiCueEditor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type)
{
if (Keyboard::is_context_menu_event (&event->button)) {
switch (item_type) {
case NoteItem:
if (internal_editing()) {
popup_note_context_menu (item, event);
}
break;
default:
break;
}
}
return true;
}
bool
MidiCueEditor::button_press_dispatch (GdkEventButton*)
MidiCueEditor::button_press_dispatch (GdkEventButton* ev)
{
return true;
/* this function is intended only for buttons 4 and above. */
Gtkmm2ext::MouseButton b (ev->state, ev->button);
return button_bindings->activate (b, Gtkmm2ext::Bindings::Press);
}
bool
MidiCueEditor::button_release_dispatch (GdkEventButton*)
MidiCueEditor::button_release_dispatch (GdkEventButton* ev)
{
return true;
/* this function is intended only for buttons 4 and above. */
Gtkmm2ext::MouseButton b (ev->state, ev->button);
return button_bindings->activate (b, Gtkmm2ext::Bindings::Release);
}
bool
@ -315,3 +375,36 @@ MidiCueEditor::key_release_handler (ArdourCanvas::Item*, GdkEvent*, ItemType)
return true;
}
void
MidiCueEditor::set_mouse_mode (Editing::MouseMode m, bool force)
{
if (m != Editing::MouseDraw && m != Editing::MouseContent) {
return;
}
mouse_mode = m;
}
void
MidiCueEditor::step_mouse_mode (bool next)
{
}
Editing::MouseMode
MidiCueEditor::current_mouse_mode () const
{
return mouse_mode;
}
bool
MidiCueEditor::internal_editing() const
{
return true;
}
RegionSelection
MidiCueEditor::region_selection()
{
RegionSelection rs;
return rs;
}

View File

@ -69,6 +69,11 @@ class MidiCueEditor : public CueEditor
void reset_zoom (samplecnt_t);
void set_mouse_mode (Editing::MouseMode, bool force = false);
void step_mouse_mode (bool next);
Editing::MouseMode current_mouse_mode () const;
bool internal_editing() const;
protected:
Temporal::timepos_t snap_to_grid (Temporal::timepos_t const & start,
Temporal::RoundMode direction,
@ -125,6 +130,10 @@ class MidiCueEditor : public CueEditor
void build_canvas ();
void canvas_allocate (Gtk::Allocation);
Editing::MouseMode mouse_mode;
RegionSelection region_selection();
};

View File

@ -175,10 +175,6 @@ public:
virtual void reverse_region () = 0;
virtual void normalize_region () = 0;
virtual void quantize_region () = 0;
virtual void legatize_region (bool shrink_only) = 0;
virtual void transform_region () = 0;
virtual void transpose_region () = 0;
virtual void pitch_shift_region () = 0;
virtual void transition_to_rolling (bool fwd) = 0;
@ -322,8 +318,6 @@ public:
virtual void add_to_idle_resize (TimeAxisView*, int32_t) = 0;
virtual Temporal::timecnt_t get_paste_offset (Temporal::timepos_t const & pos, unsigned paste_count, Temporal::timecnt_t const & duration) = 0;
virtual void edit_notes (MidiView*) = 0;
virtual void queue_visual_videotimeline_update () = 0;
virtual void set_close_video_sensitive (bool) = 0;
virtual void toggle_ruler_video (bool) = 0;