diff --git a/OpenUtau/Controls/PianoRoll.axaml.cs b/OpenUtau/Controls/PianoRoll.axaml.cs
index 00cfaaefe..8aa00b3b4 100644
--- a/OpenUtau/Controls/PianoRoll.axaml.cs
+++ b/OpenUtau/Controls/PianoRoll.axaml.cs
@@ -798,6 +798,9 @@ private void NotesCanvasLeftPointerPressed(Control control, PointerPoint point,
ViewModel.NotesViewModel.DeselectNotes();
editState = new NoteSplitEditState(
control, ViewModel, this, noteHitInfo.note);
+ } else if (args.KeyModifiers == KeyModifiers.Alt) {
+ editState = new NoteMoveEditState(control, ViewModel, this, noteHitInfo.note, true);
+ Cursor = ViewConstants.cursorSizeAll;
} else {
editState = new NoteMoveEditState(control, ViewModel, this, noteHitInfo.note);
Cursor = ViewConstants.cursorSizeAll;
diff --git a/OpenUtau/Strings/Strings.axaml b/OpenUtau/Strings/Strings.axaml
index fe9830797..6673f60cb 100644
--- a/OpenUtau/Strings/Strings.axaml
+++ b/OpenUtau/Strings/Strings.axaml
@@ -27,6 +27,7 @@
Add note
Delete note(s)
Edit note(s)
+ Duplicate note(s)
Edit part fade in
Edit part fade out
Edit lyric(s)
diff --git a/OpenUtau/Views/NoteEditStates.cs b/OpenUtau/Views/NoteEditStates.cs
index 98da786cc..70626ff18 100644
--- a/OpenUtau/Views/NoteEditStates.cs
+++ b/OpenUtau/Views/NoteEditStates.cs
@@ -12,6 +12,7 @@
using OpenUtau.Core.Format;
using OpenUtau.Core.Ustx;
using OpenUtau.Core.Util;
+using ReactiveUI;
namespace OpenUtau.App.Views {
class KeyboardPlayState {
@@ -141,17 +142,24 @@ public override void Update(IPointer pointer, Point point) {
}
class NoteMoveEditState : NoteEditState {
- public readonly UNote note;
+ public UNote note;
private double xOffset;
+ private bool duplicated = true;
protected override bool ShowValueTip => false;
- protected override string? commandNameKey => "command.note.move";
+ protected override string? commandNameKey => commandNameKeyLocal;
+ private string commandNameKeyLocal = "command.note.move";
public NoteMoveEditState(
Control control,
PianoRollViewModel vm,
IValueTip valueTip,
- UNote note) : base(control, vm, valueTip) {
+ UNote note,
+ bool duplicate = false) : base(control, vm, valueTip) {
this.note = note;
+ if (duplicate) {
+ this.duplicated = false;
+ commandNameKeyLocal = "command.note.duplicate";
+ }
var notesVm = vm.NotesViewModel;
if (!notesVm.Selection.Contains(note)) {
notesVm.SelectNote(note);
@@ -177,10 +185,10 @@ public override void Update(IPointer pointer, Point point) {
int deltaTone = notesVm.PointToTone(point) - note.tone;
int minDeltaTone;
int maxDeltaTone;
- var selectedNotes = notesVm.Selection.ToList();
- if (selectedNotes.Count > 0) {
- minDeltaTone = -selectedNotes.Select(p => p.tone).Min();
- maxDeltaTone = ViewConstants.MaxTone - 1 - selectedNotes.Select(p => p.tone).Max();
+ var notes = notesVm.Selection.ToList();
+ if (notes.Count > 0) {
+ minDeltaTone = -notes.Select(p => p.tone).Min();
+ maxDeltaTone = ViewConstants.MaxTone - 1 - notes.Select(p => p.tone).Max();
} else {
minDeltaTone = -note.tone;
maxDeltaTone = ViewConstants.MaxTone - 1 - note.tone;
@@ -195,9 +203,9 @@ public override void Update(IPointer pointer, Point point) {
int deltaTick = newPos - note.position;
int minDeltaTick;
int maxDeltaTick;
- if (selectedNotes.Count > 0) {
- minDeltaTick = -selectedNotes.Select(n => n.position).Min();
- maxDeltaTick = part.Duration - selectedNotes.Select(n => n.End).Max();
+ if (notes.Count > 0) {
+ minDeltaTick = -notes.Select(n => n.position).Min();
+ maxDeltaTick = part.Duration - notes.Select(n => n.End).Max();
} else {
minDeltaTick = -note.position;
maxDeltaTick = part.Duration - note.End;
@@ -207,12 +215,21 @@ public override void Update(IPointer pointer, Point point) {
if (deltaTone == 0 && deltaTick == 0) {
return;
}
- if (selectedNotes.Count == 0) {
- DocManager.Inst.ExecuteCmd(new MoveNoteCommand(
- part, note, deltaTick, deltaTone));
+
+ if (!duplicated) {
+ notes.Remove(note);
+ note = note.Clone();
+ notes = notes.Select(note => note.Clone()).ToList();
+ notes.Add(note);
+ DocManager.Inst.ExecuteCmd(new AddNoteCommand(part, notes));
+ notesVm.Selection.Select(notes);
+ MessageBus.Current.SendMessage(new NotesSelectionEvent(notesVm.Selection));
+ duplicated = true;
+ }
+ if (notes.Count == 0) {
+ DocManager.Inst.ExecuteCmd(new MoveNoteCommand(part, note, deltaTick, deltaTone));
} else {
- DocManager.Inst.ExecuteCmd(new MoveNoteCommand(
- part, selectedNotes, deltaTick, deltaTone));
+ DocManager.Inst.ExecuteCmd(new MoveNoteCommand(part, notes, deltaTick, deltaTone));
}
}
}