/** * \file LinkBackProxy.mm * This file is part of LyX, the document processor. * Licence details can be found in the file COPYING. * * \author Stefan Schimanski * * Full author contact details are available in file CREDITS. */ #include #include "support/linkback/LinkBackProxy.h" #include "support/linkback/LinkBack.h" /////////////////////////////////////////////////////////////////////// static NSAutoreleasePool * pool = nil; @interface LyXLinkBackClient : NSObject { NSMutableSet * keys; } + (void)load; - (LyXLinkBackClient *)init; - (BOOL)edit:(NSString *)fileName; @end @implementation LyXLinkBackClient + (void)load { pool = [[NSAutoreleasePool alloc] init]; } - (LyXLinkBackClient *)init { self = [super init]; if (self != nil) keys = [[NSMutableSet alloc] init]; return self; } - (void)dealloc { // close all links NSArray * allKeys = [keys allObjects]; unsigned i; for (i = 0; i < [allKeys count]; ++i) { LinkBack * link = [LinkBack activeLinkBackForItemKey:[allKeys objectAtIndex:i]]; [link closeLink]; } [keys release]; [super dealloc]; } - (BOOL)edit:(NSString *)fileName { if ([LinkBack activeLinkBackForItemKey:fileName]) return YES; @try { // get put data, i.e. PDF + LinkBack + 4 bytes PDF-length NSData * data = [NSData dataWithContentsOfFile:fileName]; if (data == nil) { NSLog(@"Cannot read file %@", fileName); return NO; } // Get linkback data which comes behind the pdf data. // The pdf data length are the last 4 bytes. UInt32 pdfLen = 0; pdfLen = *(UInt32 const *)(((UInt8 const *)[data bytes]) + [data length] - 4); pdfLen = NSSwapBigLongToHost(pdfLen); // make it big endian if (pdfLen >= [data length] - 4) { NSLog(@"Invalid file %@ for LinkBack", fileName); return NO; } NSData * linkBackData = [data subdataWithRange:NSMakeRange(pdfLen, [data length] - pdfLen - 4)]; if (linkBackData == nil) { NSLog(@"Invalid file %@ for LinkBack", fileName); return NO; } NSMutableDictionary * linkBackDataDict = [NSUnarchiver unarchiveObjectWithData:linkBackData]; if (linkBackDataDict == nil) { NSLog(@"LinkBack data in %@ corrupted"); return NO; } // create the link to the LinkBack server LinkBack * link = [LinkBack editLinkBackData:linkBackDataDict sourceName:fileName delegate:self itemKey:fileName]; if (link == nil) { NSLog(@"Failed to create LinkBack link for %@", fileName); return NO; } [keys addObject:fileName]; } @catch (NSException * exception) { NSLog(@"LinkBack exception: %@", exception); return NO; } return YES; } - (void)linkBackDidClose:(LinkBack*)link { NSString * fileName = [link itemKey]; if (fileName) { [keys removeObject:fileName]; NSLog(@"LinkBack link for %@ closed", fileName); } } - (void)linkBackServerDidSendEdit:(LinkBack*)link { @try { // get pasteboard and check that linkback and pdf data is available NSPasteboard * pboard = [link pasteboard]; NSArray * linkBackType = [NSArray arrayWithObjects: LinkBackPboardType, nil]; NSArray * pdfType = [NSArray arrayWithObjects: NSPDFPboardType, nil]; if ([pboard availableTypeFromArray:linkBackType] == nil || [pboard availableTypeFromArray:pdfType] == nil) { NSLog(@"No PDF or LinkBack data in pasteboard"); return; } // get new linkback data id linkBackDataDict = [pboard propertyListForType:LinkBackPboardType]; if (linkBackDataDict == nil) { NSLog(@"Cannot get LinkBack data from pasteboard"); return; } NSData * linkBackData = [NSArchiver archivedDataWithRootObject:linkBackDataDict]; if (linkBackData == nil) { NSLog(@"Cannot archive LinkBack data"); return; } // get pdf NSData * pdfData = [pboard dataForType:NSPDFPboardType]; if (pdfData == nil) { NSLog(@"Cannot get PDF data from pasteboard"); return; } // update the file NSString * fileName = [link itemKey]; NSFileHandle * file = [NSFileHandle fileHandleForUpdatingAtPath:fileName]; if (file == nil) { NSLog(@"LinkBack file %@ disappeared.", fileName); return; } [file truncateFileAtOffset:0]; [file writeData:pdfData]; [file writeData:linkBackData]; UInt32 pdfLen = NSSwapHostLongToBig([pdfData length]); // big endian NSData * lenData = [NSData dataWithBytes:&pdfLen length:4]; [file writeData:lenData]; [file closeFile]; } @catch (NSException * exception) { NSLog(@"LinkBack exception in linkBackServerDidSendEdit: %@", exception); } } @end /////////////////////////////////////////////////////////////////////// static LyXLinkBackClient * linkBackClient = nil; void checkAutoReleasePool() { if (pool == nil) pool = [[NSAutoreleasePool alloc] init]; } int isLinkBackDataInPasteboard() { checkAutoReleasePool() ; { NSArray * linkBackType = [NSArray arrayWithObjects: LinkBackPboardType, nil]; NSString * ret = [[NSPasteboard generalPasteboard] availableTypeFromArray:linkBackType]; return ret != nil; } } void getLinkBackData(void const * * buf, unsigned * len) { checkAutoReleasePool() ; { // get linkback data from pasteboard NSPasteboard * pboard = [NSPasteboard generalPasteboard]; id linkBackData = [pboard propertyListForType:LinkBackPboardType]; NSData * nsdata = [NSArchiver archivedDataWithRootObject:linkBackData]; if (nsdata == nil) { *buf = 0; *len = 0; return; } *buf = [nsdata bytes]; *len = [nsdata length]; } } int editLinkBackFile(char const * docName) { // setup Obj-C and our client if (linkBackClient == nil) linkBackClient = [[LyXLinkBackClient alloc] init]; checkAutoReleasePool() ; // FIXME: really UTF8 here? NSString * nsDocName = [NSString stringWithUTF8String:docName]; return [linkBackClient edit:nsDocName] == YES; } void closeAllLinkBackLinks() { [linkBackClient release]; linkBackClient = nil; [pool release]; pool = nil; }