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)); } } }