mirror of
https://github.com/microtherion/VocalEasel.git
synced 2024-12-22 19:23:59 +00:00
Implement cut/copy/paste/delete
This commit is contained in:
parent
f2efc9408c
commit
11dab83982
BIN
English.lproj/MainMenu.nib/keyedobjects.nib
generated
BIN
English.lproj/MainMenu.nib/keyedobjects.nib
generated
Binary file not shown.
|
@ -792,8 +792,11 @@ void VLMeasure::MMAChords(std::string & chords, const VLProperties & prop) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VLSong::VLSong()
|
VLSong::VLSong(bool initialize)
|
||||||
{
|
{
|
||||||
|
if (!initialize)
|
||||||
|
return;
|
||||||
|
|
||||||
const VLFraction fourFour(4,4);
|
const VLFraction fourFour(4,4);
|
||||||
VLProperties defaultProperties = {fourFour, 0, 1, 3};
|
VLProperties defaultProperties = {fourFour, 0, 1, 3};
|
||||||
|
|
||||||
|
@ -1681,3 +1684,129 @@ void VLSong::iterator::AdjustStatus()
|
||||||
fStatus.push_back(Repeat(fMeasure, times));
|
fStatus.push_back(Repeat(fMeasure, times));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VLSong VLSong::CopyMeasures(size_t beginMeasure, size_t endMeasure)
|
||||||
|
{
|
||||||
|
VLSong subSong(false);
|
||||||
|
|
||||||
|
int8_t firstProp = fMeasures[beginMeasure].fPropIdx;
|
||||||
|
int8_t lastProp = fMeasures[endMeasure-1].fPropIdx;
|
||||||
|
|
||||||
|
subSong.fProperties.insert(subSong.fProperties.end(),
|
||||||
|
fProperties.begin()+firstProp,
|
||||||
|
fProperties.begin()+lastProp+1);
|
||||||
|
subSong.fMeasures.insert(subSong.fMeasures.end(),
|
||||||
|
fMeasures.begin()+beginMeasure,
|
||||||
|
fMeasures.begin()+endMeasure);
|
||||||
|
|
||||||
|
if (firstProp)
|
||||||
|
for (size_t i=0; i<subSong.fMeasures.size(); ++i)
|
||||||
|
subSong.fMeasures[i].fPropIdx -= firstProp;
|
||||||
|
|
||||||
|
for (size_t r=0; r<fRepeats.size(); ++r)
|
||||||
|
if (fRepeats[r].fEndings[0].fBegin >= beginMeasure
|
||||||
|
&& fRepeats[r].fEndings[0].fEnd <= endMeasure
|
||||||
|
) {
|
||||||
|
VLRepeat repeat = fRepeats[r];
|
||||||
|
for (size_t e=0; e<repeat.fEndings.size(); ++e) {
|
||||||
|
repeat.fEndings[e].fBegin -= beginMeasure;
|
||||||
|
repeat.fEndings[e].fEnd -= endMeasure;
|
||||||
|
}
|
||||||
|
subSong.fRepeats.push_back(repeat);
|
||||||
|
}
|
||||||
|
|
||||||
|
return subSong;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VLSong::PasteMeasures(size_t beginMeasure, const VLSong & measures, int mode)
|
||||||
|
{
|
||||||
|
size_t numMeas = measures.CountMeasures();
|
||||||
|
size_t nextMeasure = mode==kInsert ? beginMeasure : beginMeasure+numMeas;
|
||||||
|
//
|
||||||
|
// Ignore properties for now. We don't use multiple properties yet.
|
||||||
|
//
|
||||||
|
if (mode == kInsert) {
|
||||||
|
fMeasures.insert(fMeasures.begin()+beginMeasure,
|
||||||
|
measures.fMeasures.begin(), measures.fMeasures.end());
|
||||||
|
for (size_t r=0; r<fRepeats.size(); ++r) {
|
||||||
|
VLRepeat & repeat = fRepeats[r];
|
||||||
|
for (size_t e=0; e<repeat.fEndings.size(); ++e) {
|
||||||
|
if (repeat.fEndings[e].fBegin >= beginMeasure)
|
||||||
|
repeat.fEndings[e].fBegin += numMeas;
|
||||||
|
if (repeat.fEndings[e].fEnd >= beginMeasure)
|
||||||
|
repeat.fEndings[e].fEnd += numMeas;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (size_t r=0; r<measures.fRepeats.size(); ++r) {
|
||||||
|
VLRepeat repeat = measures.fRepeats[r];
|
||||||
|
for (size_t e=0; e<repeat.fEndings.size(); ++e) {
|
||||||
|
repeat.fEndings[e].fBegin += beginMeasure;
|
||||||
|
repeat.fEndings[e].fEnd += beginMeasure;
|
||||||
|
}
|
||||||
|
fRepeats.push_back(repeat);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (CountMeasures() < nextMeasure) {
|
||||||
|
VLMeasure rest;
|
||||||
|
rest.fPropIdx = fMeasures.back().fPropIdx;
|
||||||
|
VLFraction dur = fProperties[rest.fPropIdx].fTime;
|
||||||
|
rest.fMelody.push_back(VLLyricsNote(VLRest(dur)));
|
||||||
|
VLChord rchord;
|
||||||
|
rchord.fDuration= dur;
|
||||||
|
rest.fChords.push_back(rchord);
|
||||||
|
|
||||||
|
fMeasures.insert(fMeasures.end(), nextMeasure-CountMeasures(), rest);
|
||||||
|
}
|
||||||
|
for (size_t m=0; m<numMeas; ++m) {
|
||||||
|
const VLMeasure & srcMeas = measures.fMeasures[m];
|
||||||
|
VLMeasure & dstMeas = fMeasures[beginMeasure+m];
|
||||||
|
if (mode & kOverwriteChords)
|
||||||
|
dstMeas.fChords = srcMeas.fChords;
|
||||||
|
if (mode & kOverwriteMelody)
|
||||||
|
dstMeas.fMelody = srcMeas.fMelody;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VLSong::DeleteMeasures(size_t beginMeasure, size_t endMeasure)
|
||||||
|
{
|
||||||
|
int8_t firstProp = fMeasures[beginMeasure].fPropIdx;
|
||||||
|
int8_t lastProp = fMeasures[endMeasure-1].fPropIdx+1;
|
||||||
|
|
||||||
|
if (beginMeasure && fMeasures[beginMeasure-1].fPropIdx == firstProp)
|
||||||
|
++firstProp;
|
||||||
|
if (endMeasure < CountMeasures() && fMeasures[endMeasure].fPropIdx == lastProp)
|
||||||
|
--lastProp;
|
||||||
|
if (lastProp > firstProp) {
|
||||||
|
fProperties.erase(fProperties.begin()+firstProp,
|
||||||
|
fProperties.begin()+lastProp);
|
||||||
|
for (size_t m=endMeasure; m<CountMeasures(); ++m)
|
||||||
|
fMeasures[m].fPropIdx -= lastProp-firstProp;
|
||||||
|
}
|
||||||
|
fMeasures.erase(fMeasures.begin()+beginMeasure,
|
||||||
|
fMeasures.begin()+endMeasure);
|
||||||
|
|
||||||
|
size_t delta = endMeasure-beginMeasure;
|
||||||
|
for (size_t r=0; r<fRepeats.size(); ) {
|
||||||
|
VLRepeat & repeat = fRepeats[r];
|
||||||
|
if (repeat.fEndings[0].fBegin >= beginMeasure
|
||||||
|
&& repeat.fEndings[0].fEnd <= endMeasure
|
||||||
|
) {
|
||||||
|
fRepeats.erase(fRepeats.begin()+r);
|
||||||
|
} else {
|
||||||
|
for (size_t e=0; e<repeat.fEndings.size(); ) {
|
||||||
|
if (repeat.fEndings[e].fBegin > beginMeasure)
|
||||||
|
repeat.fEndings[e].fBegin =
|
||||||
|
std::max(beginMeasure, repeat.fEndings[e].fBegin-delta);
|
||||||
|
if (repeat.fEndings[e].fEnd > beginMeasure)
|
||||||
|
repeat.fEndings[e].fEnd =
|
||||||
|
std::max(beginMeasure, repeat.fEndings[e].fEnd-delta);
|
||||||
|
if (e && repeat.fEndings[e].fBegin==repeat.fEndings[e].fEnd)
|
||||||
|
repeat.fEndings.erase(repeat.fEndings.begin()+e);
|
||||||
|
else
|
||||||
|
++e;
|
||||||
|
}
|
||||||
|
++r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -219,6 +219,11 @@ struct VLProperties {
|
||||||
// Determine visual representation of note head
|
// Determine visual representation of note head
|
||||||
//
|
//
|
||||||
void VisualNote(VLFraction at, VLFraction actualDur, bool prevTriplet, VLFraction *visualDur, bool * triplet) const;
|
void VisualNote(VLFraction at, VLFraction actualDur, bool prevTriplet, VLFraction *visualDur, bool * triplet) const;
|
||||||
|
|
||||||
|
bool operator==(const VLProperties & other)
|
||||||
|
{ return fTime == other.fTime && fKey == other.fKey && fMode == other.fMode
|
||||||
|
&& fDivisions == other.fDivisions;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VLLyricsNote : VLNote {
|
struct VLLyricsNote : VLNote {
|
||||||
|
@ -257,7 +262,7 @@ struct VLRepeat {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VLSong {
|
struct VLSong {
|
||||||
VLSong();
|
VLSong(bool initialize = true);
|
||||||
void swap(VLSong & other);
|
void swap(VLSong & other);
|
||||||
|
|
||||||
std::vector<VLProperties> fProperties;
|
std::vector<VLProperties> fProperties;
|
||||||
|
@ -327,6 +332,16 @@ struct VLSong {
|
||||||
std::string GetWord(size_t stanza, size_t measure, VLFraction at);
|
std::string GetWord(size_t stanza, size_t measure, VLFraction at);
|
||||||
void SetWord(size_t stanza, size_t measure, VLFraction at, std::string word);
|
void SetWord(size_t stanza, size_t measure, VLFraction at, std::string word);
|
||||||
|
|
||||||
|
enum {
|
||||||
|
kInsert,
|
||||||
|
kOverwriteChords = 1,
|
||||||
|
kOverwriteMelody = 2
|
||||||
|
};
|
||||||
|
VLSong CopyMeasures(size_t beginMeasure, size_t endMeasure);
|
||||||
|
void PasteMeasures(size_t beginMeasure, const VLSong & measures,
|
||||||
|
int mode = kInsert);
|
||||||
|
void DeleteMeasures(size_t beginMeasure, size_t endMeasure);
|
||||||
|
|
||||||
size_t CountMeasures() const { return fMeasures.size(); }
|
size_t CountMeasures() const { return fMeasures.size(); }
|
||||||
size_t CountStanzas() const;
|
size_t CountStanzas() const;
|
||||||
void LilypondNotes(std::string & notes) const;
|
void LilypondNotes(std::string & notes) const;
|
||||||
|
|
|
@ -10,6 +10,11 @@
|
||||||
#import "VLSheetViewSelection.h"
|
#import "VLSheetViewSelection.h"
|
||||||
#import "VLDocument.h"
|
#import "VLDocument.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// We're too lazy to properly serialize our private pasteboard format.
|
||||||
|
//
|
||||||
|
static VLSong sPasteboard;
|
||||||
|
|
||||||
@implementation VLSheetView (Selection)
|
@implementation VLSheetView (Selection)
|
||||||
|
|
||||||
- (void)editSelection
|
- (void)editSelection
|
||||||
|
@ -74,18 +79,41 @@
|
||||||
|
|
||||||
- (IBAction)cut:(id)sender
|
- (IBAction)cut:(id)sender
|
||||||
{
|
{
|
||||||
|
[self copy:sender];
|
||||||
|
[self delete:sender];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)copy:(id)sender
|
- (IBAction)copy:(id)sender
|
||||||
{
|
{
|
||||||
|
NSPasteboard * pb = [NSPasteboard generalPasteboard];
|
||||||
|
NSString * pbType = [[NSBundle mainBundle] bundleIdentifier];
|
||||||
|
|
||||||
|
[pb declareTypes:[NSArray arrayWithObject:pbType] owner:nil];
|
||||||
|
if ([pb setString:@"whatever" forType:pbType])
|
||||||
|
sPasteboard = [self song]->CopyMeasures(fSelStart, fSelEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)paste:(id)sender
|
- (IBAction)paste:(id)sender
|
||||||
{
|
{
|
||||||
|
NSPasteboard * pb = [NSPasteboard generalPasteboard];
|
||||||
|
NSString * pbType = [[NSBundle mainBundle] bundleIdentifier];
|
||||||
|
|
||||||
|
if ([pb availableTypeFromArray:[NSArray arrayWithObject:pbType]]) {
|
||||||
|
[[self document] willChangeSong];
|
||||||
|
if (![sender tag]) // Delete on paste, but not on overwrite
|
||||||
|
[self song]->DeleteMeasures(fSelStart, fSelEnd);
|
||||||
|
[self song]->PasteMeasures(fSelStart, sPasteboard, [sender tag]);
|
||||||
|
[[self document] didChangeSong];
|
||||||
|
[self setNeedsDisplay:YES];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)delete:(id)sender
|
- (IBAction)delete:(id)sender
|
||||||
{
|
{
|
||||||
|
[[self document] willChangeSong];
|
||||||
|
[self song]->DeleteMeasures(fSelStart, fSelEnd);
|
||||||
|
[[self document] didChangeSong];
|
||||||
|
[self setNeedsDisplay:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)editRepeat:(id)sender
|
- (IBAction)editRepeat:(id)sender
|
||||||
|
|
Loading…
Reference in New Issue
Block a user