diff --git a/English.lproj/InfoPlist.strings b/English.lproj/InfoPlist.strings index feb22c3..a4d425a 100644 Binary files a/English.lproj/InfoPlist.strings and b/English.lproj/InfoPlist.strings differ diff --git a/Resources/Info.plist b/Resources/Info.plist index 2fda5b1..1a6e1b4 100644 --- a/Resources/Info.plist +++ b/Resources/Info.plist @@ -22,12 +22,13 @@ CFBundleTypeRole Editor LSTypeIsPackage - + NSDocumentClass VLDocument NSExportableAs VLLilypondType + VLMusicXMLType NSPersistentStoreTypeKey XML @@ -50,6 +51,20 @@ NSPersistentStoreTypeKey Binary + + CFBundleTypeExtensions + + xml + + CFBundleTypeName + VLMusicXMLType + CFBundleTypeRole + None + LSTypeIsPackage + + NSPersistentStoreTypeKey + Binary + CFBundleExecutable VocalEasel diff --git a/Resources/VLPDFWindow.nib/keyedobjects.nib b/Resources/VLPDFWindow.nib/keyedobjects.nib index 34d87ee..7b2208f 100644 Binary files a/Resources/VLPDFWindow.nib/keyedobjects.nib and b/Resources/VLPDFWindow.nib/keyedobjects.nib differ diff --git a/Sources/VLAppController.mm b/Sources/VLAppController.mm index 6e301c5..9a787da 100644 --- a/Sources/VLAppController.mm +++ b/Sources/VLAppController.mm @@ -67,14 +67,13 @@ char line[1000]; FILE * output = popen([command UTF8String], "r"); if (fgets(line, 1000, output)) { - fprintf(stderr, "Line %s", line); size_t len = strlen(line); if (len && line[len-1]=='\n') { line[len-1] = 0; return [NSString stringWithUTF8String:line]; } } else - NSLog(@"Failed command: %@ (%d)\n", command, errno); + NSLog(@"Failed command: %@ %s (%d)\n", command, feof(output) ? "EOF" : "Error", errno); pclose(output); return nil; } @@ -181,9 +180,4 @@ } } -- (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender -{ - return NO; -} - @end diff --git a/Sources/VLDocument.mm b/Sources/VLDocument.mm index 89d805c..16010a6 100644 --- a/Sources/VLDocument.mm +++ b/Sources/VLDocument.mm @@ -32,21 +32,29 @@ return self; } -- (void) close -{ - [logWin close]; - [pdfWin close]; - - [super close]; -} - - (void) dealloc { delete song; + [lilypondTemplate release]; + [songTitle release]; + [songLyricist release]; + [songComposer release]; + [songArranger release]; + [super dealloc]; } +- (void)removeWindowController:(NSWindowController *)win +{ + if (win == logWin) + logWin = nil; + else if (win == pdfWin) + pdfWin = nil; + + [super removeWindowController:win]; +} + - (VLLogWindow *)logWin { if (!logWin) { @@ -141,12 +149,14 @@ [self updateChangeCount:NSChangeDone]; } -- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError +- (NSFileWrapper *)fileWrapperOfType:(NSString *)typeName error:(NSError **)outError { if ([typeName isEqual:@"VLNativeType"]) { - return [self XMLDataWithError:outError]; + return [self XMLFileWrapperWithError:outError flat:NO]; + } else if ([typeName isEqual:@"VLMusicXMLType"]) { + return [self XMLFileWrapperWithError:outError flat:YES]; } else if ([typeName isEqual:@"VLLilypondType"]) { - return [self lilypondDataWithError:outError]; + return [self lilypondFileWrapperWithError:outError]; } else { if (outError) *outError = [NSError errorWithDomain:NSCocoaErrorDomain @@ -156,10 +166,10 @@ } } -- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError +- (BOOL)readFromFileWrapper:(NSFileWrapper *)wrapper ofType:(NSString *)typeName error:(NSError **)outError { if ([typeName isEqual:@"VLNativeType"]) { - return [self readFromXMLData:data error:outError]; + return [self readFromXMLFileWrapper:wrapper error:outError]; } else { if (outError) *outError = [NSError errorWithDomain:NSCocoaErrorDomain @@ -169,14 +179,12 @@ } } - -- (IBAction) engrave:(id)sender +- (IBAction) performEngrave:(id)sender { NSTask * lilypondTask = [[NSTask alloc] init]; NSString * path = [[self fileURL] path]; - NSString * root = - [[path lastPathComponent] stringByDeletingPathExtension]; - NSString * tmpDir = @"/var/tmp"; + NSString * base = [[path lastPathComponent] + stringByDeletingPathExtension]; NSBundle * mainBundle = [NSBundle mainBundle]; // @@ -184,21 +192,20 @@ // NSError * err; [self writeToURL: - [NSURL fileURLWithPath: - [[tmpDir stringByAppendingPathComponent:root] - stringByAppendingPathExtension:@"ly"]] + [NSURL fileURLWithPath:[[path stringByAppendingPathComponent:base] + stringByAppendingPathExtension:@"ly"]] ofType:@"VLLilypondType" error:&err]; NSPipe * pipe = [NSPipe pipe]; NSString * tool = [[NSUserDefaults standardUserDefaults] stringForKey:@"VLLilypondPath"]; - NSArray * arguments = [NSArray arrayWithObjects:tool, root, nil]; + NSArray * arguments = [NSArray arrayWithObjects:tool, base, nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(engraveDone:) name:NSTaskDidTerminateNotification object:lilypondTask]; - [lilypondTask setCurrentDirectoryPath:tmpDir]; + [lilypondTask setCurrentDirectoryPath:path]; [lilypondTask setStandardOutput: pipe]; [lilypondTask setStandardError: pipe]; [lilypondTask setArguments: arguments]; @@ -216,24 +223,43 @@ [[NSNotificationCenter defaultCenter] removeObserver: self]; int status = [[notification object] terminationStatus]; if (!status) { - NSFileManager * fileManager = [NSFileManager defaultManager]; - NSString * path = [[self fileURL] path]; - NSString * root = - [[path lastPathComponent] stringByDeletingPathExtension]; - NSString * tmpDir = @"/var/tmp"; - NSString * dstDir = [path stringByDeletingLastPathComponent]; - NSString * pdf = - [root stringByAppendingPathExtension:@"pdf"]; - [fileManager - removeFileAtPath:[dstDir stringByAppendingPathComponent:pdf] - handler:nil]; - [fileManager - movePath:[tmpDir stringByAppendingPathComponent:pdf] - toPath:[dstDir stringByAppendingPathComponent:pdf] - handler:nil]; [[self pdfWin] showWindow: self]; [pdfWin reloadPDF]; - } + } else { + NSBeep(); + } +} + +- (void) engrave:(NSDocument *)doc didSave:(BOOL)didSave contextInfo:(void *)contextInfo +{ + if (didSave) + [self performEngrave:(id)contextInfo]; +} + +- (void)engrave:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(id)sender +{ + if (returnCode == NSAlertDefaultReturn) { + [[alert window] orderOut:self]; + [self saveDocumentWithDelegate:self + didSaveSelector:@selector(engrave:didSave:contextInfo:) + contextInfo:sender]; + } +} + +- (IBAction) engrave:(id)sender +{ + if ([self isDocumentEdited]) { + NSAlert * alert = + [NSAlert alertWithMessageText:@"Do you want to save your changes?" + defaultButton:[self fileURL] ? @"Save" : @"Save..." + alternateButton:@"Cancel" otherButton:nil + informativeTextWithFormat:@"You need to save your document before typesetting."]; + [alert beginSheetModalForWindow:[sheetWin window] + modalDelegate:self + didEndSelector:@selector(engrave:returnCode:contextInfo:) + contextInfo:sender]; + } else + [self performEngrave:sender]; } - (IBAction) showOutput:(id)sender diff --git a/Sources/VLLilypondDocument.h b/Sources/VLLilypondDocument.h index 2afa1e7..57aef3b 100644 --- a/Sources/VLLilypondDocument.h +++ b/Sources/VLLilypondDocument.h @@ -11,6 +11,6 @@ @interface VLDocument (Lilypond) -- (NSData *)lilypondDataWithError:(NSError **)outError; +- (NSFileWrapper *)lilypondFileWrapperWithError:(NSError **)outError; @end diff --git a/Sources/VLLilypondDocument.mm b/Sources/VLLilypondDocument.mm index bb1314e..da10b74 100644 --- a/Sources/VLLilypondDocument.mm +++ b/Sources/VLLilypondDocument.mm @@ -131,4 +131,16 @@ const char * sKeyNames[] = { return [ly dataUsingEncoding:enc]; } +- (NSFileWrapper *)lilypondFileWrapperWithError:(NSError **)outError +{ + NSData * data = [self lilypondDataWithError:outError]; + + if (!data) + return nil; + else + return [[[NSFileWrapper alloc] + initRegularFileWithContents:data] + autorelease]; +} + @end diff --git a/Sources/VLPDFWindow.mm b/Sources/VLPDFWindow.mm index 0402545..52dde7d 100644 --- a/Sources/VLPDFWindow.mm +++ b/Sources/VLPDFWindow.mm @@ -36,13 +36,36 @@ static NSString* sZoomOutToolbarItemIdentifier = @"Zoom Out Toolbar Item Identif - (void)reloadPDF { if (pdfView) { - NSString * inString = [[[self document] fileURL] path]; - NSString * baseString = [inString stringByDeletingPathExtension]; - NSString * outString = [baseString stringByAppendingPathExtension: @"pdf"]; - NSURL * pdfURL = [NSURL fileURLWithPath: outString]; - PDFDocument * pdfDoc = [[[PDFDocument alloc] initWithURL: pdfURL] autorelease]; - [(PDFView *)pdfView setDocument: pdfDoc]; - [pdfView setNeedsDisplay:YES]; + NSString * path = [[[self document] fileURL] path]; + if (!path) + return; + NSFileWrapper * wrapper = + [[[NSFileWrapper alloc] initWithPath:path] autorelease]; + // + // Find newest pdf file + // + NSEnumerator * w = [[wrapper fileWrappers] objectEnumerator]; + NSString * pdfPath = nil; + NSDate * pdfDate = nil; + while (wrapper = [w nextObject]) { + NSString * path = [wrapper filename]; + if (![[path pathExtension] isEqual:@"pdf"]) + continue; + NSDate * date = [[wrapper fileAttributes] + objectForKey:NSFileModificationDate]; + if (!pdfPath || [date compare:pdfDate]==NSOrderedAscending) { + pdfPath = path; + pdfDate = date; + } + } + if (pdfPath) { + NSURL * pdfURL = + [NSURL fileURLWithPath:[path stringByAppendingPathComponent:pdfPath]]; + PDFDocument * pdfDoc = + [[[PDFDocument alloc] initWithURL:pdfURL] autorelease]; + [(PDFView *)pdfView setDocument: pdfDoc]; + [pdfView setNeedsDisplay:YES]; + } } } diff --git a/Sources/VLXMLDocument.h b/Sources/VLXMLDocument.h index 12a59c1..0205458 100644 --- a/Sources/VLXMLDocument.h +++ b/Sources/VLXMLDocument.h @@ -11,7 +11,7 @@ @interface VLDocument (XML) -- (NSData *)XMLDataWithError:(NSError **)outError; -- (BOOL)readFromXMLData:(NSData *)data error:(NSError **)outError; +- (NSFileWrapper *)XMLFileWrapperWithError:(NSError **)outError flat:(BOOL)flat; +- (BOOL)readFromXMLFileWrapper:(NSFileWrapper *)wrapper error:(NSError **)outError; @end diff --git a/Sources/VLXMLDocument.mm b/Sources/VLXMLDocument.mm index 9d5813e..0177b8e 100644 --- a/Sources/VLXMLDocument.mm +++ b/Sources/VLXMLDocument.mm @@ -283,6 +283,28 @@ const char * sSteps = "C DbD EbE F GbG AbA BbB "; return [doc XMLDataWithOptions:NSXMLNodePrettyPrint|NSXMLNodeCompactEmptyElement]; } +- (NSFileWrapper *)XMLFileWrapperWithError:(NSError **)outError flat:(BOOL)flat; +{ + NSData * contents = [self XMLDataWithError:outError]; + + if (!contents) { + return nil; + } else if (flat) { + return [[[NSFileWrapper alloc] + initRegularFileWithContents:contents] + autorelease]; + } else { + NSFileWrapper * wrap = [[[NSFileWrapper alloc] + initDirectoryWithFileWrappers: + [NSDictionary dictionary]] + autorelease]; + [wrap addRegularFileWithContents:contents + preferredFilename:@"Song"]; + + return wrap; + } +} + - (BOOL)readPropsFromAttributes:(NSXMLElement*)attr error:(NSError **)outError { VLProperties & prop = song->fProperties.front(); @@ -432,4 +454,11 @@ int8_t sStepToPitch[] = { return YES; } +- (BOOL)readFromXMLFileWrapper:(NSFileWrapper *)wrapper error:(NSError **)outError +{ + return [self readFromXMLData: [[[wrapper fileWrappers] objectForKey:@"Song"] + regularFileContents] + error:outError]; +} + @end