move a bunch of MIDI editing into EditingContext
This commit is contained in:
parent
89f780b78c
commit
23a672b45d
|
@ -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 ();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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 ();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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 ()
|
||||
{
|
||||
|
|
|
@ -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 ();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue