mirror of
https://github.com/microtherion/VocalEasel.git
synced 2024-12-22 11:14:00 +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);
|
||||
VLProperties defaultProperties = {fourFour, 0, 1, 3};
|
||||
|
||||
|
@ -1681,3 +1684,129 @@ void VLSong::iterator::AdjustStatus()
|
|||
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
|
||||
//
|
||||
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 {
|
||||
|
@ -257,7 +262,7 @@ struct VLRepeat {
|
|||
};
|
||||
|
||||
struct VLSong {
|
||||
VLSong();
|
||||
VLSong(bool initialize = true);
|
||||
void swap(VLSong & other);
|
||||
|
||||
std::vector<VLProperties> fProperties;
|
||||
|
@ -327,6 +332,16 @@ struct VLSong {
|
|||
std::string GetWord(size_t stanza, size_t measure, VLFraction at);
|
||||
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 CountStanzas() const;
|
||||
void LilypondNotes(std::string & notes) const;
|
||||
|
|
|
@ -10,6 +10,11 @@
|
|||
#import "VLSheetViewSelection.h"
|
||||
#import "VLDocument.h"
|
||||
|
||||
//
|
||||
// We're too lazy to properly serialize our private pasteboard format.
|
||||
//
|
||||
static VLSong sPasteboard;
|
||||
|
||||
@implementation VLSheetView (Selection)
|
||||
|
||||
- (void)editSelection
|
||||
|
@ -74,18 +79,41 @@
|
|||
|
||||
- (IBAction)cut:(id)sender
|
||||
{
|
||||
[self copy:sender];
|
||||
[self delete: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
|
||||
{
|
||||
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
|
||||
{
|
||||
[[self document] willChangeSong];
|
||||
[self song]->DeleteMeasures(fSelStart, fSelEnd);
|
||||
[[self document] didChangeSong];
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (IBAction)editRepeat:(id)sender
|
||||
|
|
Loading…
Reference in New Issue
Block a user