VocalEasel/Sources/VLDocument.mm

461 lines
10 KiB
Plaintext
Raw Normal View History

2006-09-11 02:49:56 +00:00
//
// VLDocument.mm
2006-09-11 02:49:56 +00:00
// Vocalese
//
// Created by Matthias Neeracher on 12/17/05.
// Copyright __MyCompanyName__ 2005 . All rights reserved.
//
#import "VLDocument.h"
2006-10-22 07:16:29 +00:00
#import "VLXMLDocument.h"
#import "VLLilypondDocument.h"
2006-11-04 08:15:34 +00:00
#import "VLMMADocument.h"
2006-11-12 11:37:36 +00:00
#import "VLMIDIDocument.h"
#import "VLPDFWindow.h"
#import "VLLogWindow.h"
#import "VLSheetWindow.h"
2006-11-13 04:26:09 +00:00
#import "VLSoundOut.h"
2006-09-11 02:49:56 +00:00
2006-12-04 07:04:24 +00:00
@interface VLSongWrapper : NSObject {
VLSong * wrappedSong;
}
+ (VLSongWrapper *)wrapperWithSong:(VLSong *)song;
- (VLSong *)song;
@end
@implementation VLSongWrapper
- (id)initWithSong:(VLSong *)song
{
if (self = [super init])
wrappedSong = new VLSong(*song);
return self;
}
- (void) dealloc
{
delete wrappedSong;
[super dealloc];
}
+ (VLSongWrapper *)wrapperWithSong:(VLSong *)song
{
return [[[VLSongWrapper alloc] initWithSong:song] autorelease];
}
- (VLSong *)song
{
return wrappedSong;
}
@end
@implementation VLDocument
2006-10-03 17:52:54 +00:00
- (id)init
2006-10-03 17:52:54 +00:00
{
self = [super init];
if (self) {
song = new VLSong;
lilypondTemplate = @"default";
songTitle = @"";
songLyricist = @"";
songComposer = @"";
songArranger = @"";
2006-11-04 08:15:34 +00:00
songGroove = @"Swing";
songTempo = [[NSNumber numberWithInt:120] retain];
sheetWin = nil;
pdfWin = nil;
logWin = nil;
2006-11-10 08:09:18 +00:00
tmpPath = nil;
vcsWrapper = nil;
repeatVolta = 2;
2007-01-21 11:34:40 +00:00
brandNew = true;
2006-12-04 07:04:24 +00:00
[self setHasUndoManager:YES];
undo =
[[VLKeyValueUndo alloc] initWithOwner:self
keysAndNames: [NSDictionary dictionaryWithObjectsAndKeys:
@"", @"songTitle",
@"", @"songLyricist",
@"", @"songComposer",
@"", @"songArranger",
@"", @"songGroove",
@"", @"songTempo",
nil]];
}
return self;
2006-10-03 17:52:54 +00:00
}
- (void) dealloc
2006-10-03 17:52:54 +00:00
{
delete song;
2006-10-03 17:52:54 +00:00
[lilypondTemplate release];
[songTitle release];
[songLyricist release];
[songComposer release];
[songArranger release];
[vcsWrapper release];
2006-12-04 07:04:24 +00:00
[undo release];
2006-11-10 08:09:18 +00:00
if (tmpPath) {
[[NSFileManager defaultManager] removeFileAtPath:tmpPath handler:nil];
[tmpPath release];
}
[super dealloc];
2006-10-03 17:52:54 +00:00
}
- (void)removeWindowController:(NSWindowController *)win
{
if (win == logWin)
logWin = nil;
else if (win == pdfWin)
pdfWin = nil;
2007-01-21 11:34:40 +00:00
else if (win == sheetWin)
sheetWin = nil;
[super removeWindowController:win];
}
- (VLLogWindow *)logWin
{
if (!logWin) {
logWin = [[VLLogWindow alloc] initWithWindowNibName: @"VLLogWindow"];
[self addWindowController: logWin];
[logWin release];
}
return logWin;
}
- (VLPDFWindow *)pdfWin
{
if (!pdfWin) {
pdfWin = [[VLPDFWindow alloc] initWithWindowNibName: @"VLPDFWindow"];
[self addWindowController: pdfWin];
[pdfWin release];
}
return pdfWin;
}
- (void)makeWindowControllers
2006-09-11 02:49:56 +00:00
{
sheetWin = [[VLSheetWindow alloc] initWithWindowNibName: @"VLDocument"];
[self addWindowController: sheetWin];
[sheetWin setShouldCloseDocument:YES];
[sheetWin release];
2006-09-11 02:49:56 +00:00
}
- (void)showWindows
2006-09-11 02:49:56 +00:00
{
[sheetWin showWindow: self];
if ([pdfWin isWindowLoaded])
[pdfWin showWindow: self];
if ([logWin isWindowLoaded])
[logWin showWindow: self];
2006-09-11 02:49:56 +00:00
}
- (VLSong *) song
{
return song;
}
- (NSNumber *) songKey
{
const VLProperties & prop = song->fProperties.front();
return [NSNumber numberWithInt: (prop.fKey << 8) | (prop.fMode & 0xFF)];
}
- (void) setKey:(int)key transpose:(BOOL)transpose
{
2006-12-04 07:04:24 +00:00
[self willChangeSong];
2006-09-11 02:49:56 +00:00
VLProperties & prop = song->fProperties.front();
2006-12-04 07:04:24 +00:00
if (transpose)
2006-10-14 10:10:19 +00:00
song->Transpose((7*((key>>8)-prop.fKey) % 12));
2006-09-11 02:49:56 +00:00
prop.fKey = key >> 8;
prop.fMode= key & 0xFF;
2006-10-14 10:10:19 +00:00
[self updateChangeCount:NSChangeDone];
2006-09-11 02:49:56 +00:00
}
- (NSNumber *) songTime
{
const VLProperties & prop = song->fProperties.front();
return [NSNumber numberWithInt: (prop.fTime.fNum << 8) | prop.fTime.fDenom];
}
- (void) setTimeNum:(int)num denom:(int)denom
{
2006-12-04 07:04:24 +00:00
[self willChangeSong];
2006-09-11 02:49:56 +00:00
VLProperties & prop = song->fProperties.front();
prop.fTime = VLFraction(num, denom);
2006-10-14 10:10:19 +00:00
[self updateChangeCount:NSChangeDone];
2006-09-11 02:49:56 +00:00
}
- (NSNumber *) songDivisions
{
const VLProperties & prop = song->fProperties.front();
return [NSNumber numberWithInt: prop.fDivisions];
}
- (void) setDivisions:(int)divisions
{
2006-12-04 07:04:24 +00:00
[self willChangeSong];
2006-09-11 02:49:56 +00:00
VLProperties & prop = song->fProperties.front();
prop.fDivisions = divisions;
2006-10-14 10:10:19 +00:00
[self updateChangeCount:NSChangeDone];
2006-09-11 02:49:56 +00:00
}
- (int) repeatVolta
{
return repeatVolta;
}
2007-01-21 11:34:40 +00:00
- (bool) brandNew
{
return brandNew && ![self isDocumentEdited];
}
- (void) setRepeatVolta:(int)volta
{
repeatVolta = volta;
}
2006-11-10 08:09:18 +00:00
- (NSString *) tmpPath
{
if (!tmpPath) {
tmpPath = [[NSString alloc] initWithFormat:@"/var/tmp/VocalEasel.%08x",
self];
[[NSFileManager defaultManager] createDirectoryAtPath:tmpPath attributes:nil];
}
return tmpPath;
}
- (NSString *) workPath
{
if ([self fileURL]) // Prefer our wrapper directory
return [[self fileURL] path];
else
return [self tmpPath];
}
- (NSString *) baseName
{
return [[[self workPath] lastPathComponent] stringByDeletingPathExtension];
}
- (NSURL *) fileURLWithExtension:(NSString*)extension
{
return [NSURL fileURLWithPath:
[[[self workPath] stringByAppendingPathComponent:[self baseName]]
stringByAppendingPathExtension:extension]];
}
- (BOOL)saveToURL:(NSURL *)absoluteURL ofType:(NSString *)typeName forSaveOperation:(NSSaveOperationType)saveOperation error:(NSError **)outError
{
NSFileWrapper * preservedVCSWrapper = nil;
switch (saveOperation) {
case NSSaveToOperation:
case NSAutosaveOperation:
preservedVCSWrapper = vcsWrapper;
[preservedVCSWrapper retain];
// Fall through
case NSSaveAsOperation:
[vcsWrapper release];
vcsWrapper = nil;
// Fall through
case NSSaveOperation:
break;
}
BOOL res = [super saveToURL:absoluteURL ofType:typeName
forSaveOperation:saveOperation error:outError];
if (!vcsWrapper)
vcsWrapper = preservedVCSWrapper;
return res;
}
- (NSFileWrapper *)fileWrapperOfType:(NSString *)typeName error:(NSError **)outError
2006-10-22 07:16:29 +00:00
{
2006-10-23 07:42:53 +00:00
if ([typeName isEqual:@"VLNativeType"]) {
return [self XMLFileWrapperWithError:outError flat:NO];
} else if ([typeName isEqual:@"VLMusicXMLType"]) {
return [self XMLFileWrapperWithError:outError flat:YES];
2006-10-23 07:42:53 +00:00
} else if ([typeName isEqual:@"VLLilypondType"]) {
return [self lilypondFileWrapperWithError:outError];
2006-11-04 08:15:34 +00:00
} else if ([typeName isEqual:@"VLMMAType"]) {
return [self mmaFileWrapperWithError:outError];
2006-11-12 11:37:36 +00:00
} else if ([typeName isEqual:@"VLMIDIType"]) {
return [self midiFileWrapperWithError:outError];
2006-10-23 07:42:53 +00:00
} else {
if (outError)
*outError = [NSError errorWithDomain:NSCocoaErrorDomain
code:NSPersistentStoreInvalidTypeError
userInfo:nil];
2006-10-22 07:16:29 +00:00
return nil;
2006-10-23 07:42:53 +00:00
}
2006-10-22 07:16:29 +00:00
}
- (BOOL)readFromFileWrapper:(NSFileWrapper *)wrapper ofType:(NSString *)typeName error:(NSError **)outError
2006-10-22 07:16:29 +00:00
{
2007-01-21 11:34:40 +00:00
brandNew = false;
//
// On opening a document, close all unchanged empty documents
//
NSEnumerator * docs = [[[NSDocumentController sharedDocumentController]
documents] objectEnumerator];
while (VLDocument * doc = [docs nextObject])
if ([doc brandNew])
[[doc windowControllers]
makeObjectsPerformSelector:@selector(close)];
2006-10-23 07:42:53 +00:00
if ([typeName isEqual:@"VLNativeType"]) {
return [self readFromXMLFileWrapper:wrapper error:outError];
2006-10-23 07:42:53 +00:00
} else {
if (outError)
*outError = [NSError errorWithDomain:NSCocoaErrorDomain
code:NSPersistentStoreInvalidTypeError
userInfo:nil];
2006-10-22 07:16:29 +00:00
return NO;
2006-10-23 07:42:53 +00:00
}
2006-10-22 07:16:29 +00:00
}
- (void) changedFileWrapper
{
if (NSURL * url = [self fileURL])
if (NSDate * modDate =
[[[NSFileManager defaultManager] fileAttributesAtPath:[url path]
traverseLink:YES]
objectForKey:NSFileModificationDate])
[self setFileModificationDate:modDate];
}
2006-11-12 11:37:36 +00:00
- (NSTask *) taskWithLaunchPath:(NSString *)launch arguments:(NSArray *)args;
{
NSTask * task = [[NSTask alloc] init];
NSString * path = [self workPath];
NSPipe * pipe = [NSPipe pipe];
[task setCurrentDirectoryPath: path];
[task setStandardOutput: pipe];
[task setStandardError: pipe];
[task setArguments: args];
[task setLaunchPath: launch];
[[self logWin] showWindow: self];
[NSThread detachNewThreadSelector:@selector(logFromFileHandle:) toTarget:logWin
withObject:[pipe fileHandleForReading]];
return task;
}
2006-11-10 08:09:18 +00:00
- (IBAction) engrave:(id)sender
{
2006-11-10 08:09:18 +00:00
NSString * base = [self baseName];
NSBundle * mainBundle = [NSBundle mainBundle];
//
// Convert to Lilypond format
//
NSError * err;
2006-11-10 08:09:18 +00:00
[self writeToURL:[self fileURLWithExtension:@"ly"]
ofType:@"VLLilypondType" error:&err];
2006-11-12 11:37:36 +00:00
NSString * launch =
[mainBundle pathForResource:@"lilyWrapper" ofType:@""
inDirectory:@"bin"];
NSString * tool =
[[NSUserDefaults standardUserDefaults]
stringForKey:@"VLLilypondPath"];
2006-11-12 11:37:36 +00:00
NSArray * args = [NSArray arrayWithObjects:tool, base, nil];
NSTask * task = [self taskWithLaunchPath:launch arguments:args];
[[NSNotificationCenter defaultCenter]
addObserver:self selector:@selector(engraveDone:)
2006-11-12 11:37:36 +00:00
name:NSTaskDidTerminateNotification object:task];
2006-11-12 11:37:36 +00:00
[task launch];
}
- (void)engraveDone:(NSNotification *)notification {
[[NSNotificationCenter defaultCenter] removeObserver: self];
int status = [[notification object] terminationStatus];
if (!status) {
[[self pdfWin] showWindow: self];
[pdfWin reloadPDF];
} else {
NSBeep();
}
[self changedFileWrapper];
}
2006-11-13 04:26:09 +00:00
- (IBAction) play:(id)sender
{
NSError * err;
[self writeToURL:[self fileURLWithExtension:@"mid"]
ofType:@"VLMIDIType" error:&err];
[self changedFileWrapper];
2006-11-13 04:26:09 +00:00
VLSoundOut::Instance()->PlayFile(
CFDataRef([NSData dataWithContentsOfURL:
[self fileURLWithExtension:@"mid"]]));
}
2007-01-02 07:01:55 +00:00
- (IBAction) stop:(id)sender
{
VLSoundOut::Instance()->Stop();
}
- (IBAction) showOutput:(id)sender
{
[[self pdfWin] showWindow:sender];
}
- (IBAction) showLog:(id)sender
{
[[self logWin] showWindow:sender];
}
2006-12-04 07:04:24 +00:00
- (void) willChangeSong
{
[self willChangeValueForKey:@"song"];
[[self undoManager] registerUndoWithTarget:self
selector:@selector(restoreSong:)
object:[VLSongWrapper wrapperWithSong:song]];
}
- (void) didChangeSong
{
[self didChangeValueForKey:@"song"];
[self updateChangeCount:NSChangeDone];
}
- (void) restoreSong:(VLSongWrapper *)savedSong
{
[self willChangeSong];
[self willChangeValueForKey:@"songKey"];
[self willChangeValueForKey:@"songTime"];
[self willChangeValueForKey:@"songDivisions"];
song->swap(*[savedSong song]);
[self didChangeValueForKey:@"songKey"];
[self didChangeValueForKey:@"songTime"];
[self didChangeValueForKey:@"songDivisions"];
[self didChangeSong];
}
2006-09-11 02:49:56 +00:00
@end