Revision: 26842 http://sourceforge.net/p/bibdesk/svn/26842 Author: hofman Date: 2021-09-11 09:08:16 +0000 (Sat, 11 Sep 2021) Log Message: ----------- Don't change the intrinsic height of the group text cell when displaying colors, as this determines the size of the icon
Modified Paths: -------------- trunk/bibdesk/BDSKGroupTextFieldCell.m trunk/bibdesk/BDSKLinkedFile.m Modified: trunk/bibdesk/BDSKGroupTextFieldCell.m =================================================================== --- trunk/bibdesk/BDSKGroupTextFieldCell.m 2021-09-11 06:30:29 UTC (rev 26841) +++ trunk/bibdesk/BDSKGroupTextFieldCell.m 2021-09-11 09:08:16 UTC (rev 26842) @@ -47,7 +47,7 @@ - (NSSize)cellSizeForBounds:(NSRect)rect { NSSize size = [super cellSizeForBounds:rect]; if ([self displayAsColor]) - size.width = size.height = 4.0; + size.width = size.height + 2.0; return size; } Modified: trunk/bibdesk/BDSKLinkedFile.m =================================================================== --- trunk/bibdesk/BDSKLinkedFile.m 2021-09-11 06:30:29 UTC (rev 26841) +++ trunk/bibdesk/BDSKLinkedFile.m 2021-09-11 09:08:16 UTC (rev 26842) @@ -94,7 +94,6 @@ @interface BDSKLinkedAliasFile : BDSKLinkedFile { id alias; // can be a BDSKAlias or bookmark NSData - id fileRef; // can be a BDSKFileRef or a file reference NSURL NSString *relativePath; NSURL *fileURL; BOOL isInitial; @@ -102,13 +101,25 @@ BOOL hasSkimNotesNeedsUpdate; id delegate; } +- (void)updateFileURL; +- (NSData *)copyAliasDataRelativeToPath:(NSString *)newBasePath; +@end +#pragma mark - + +@interface BDSKAliasLinkedFile : BDSKLinkedAliasFile { + const FSRef *fileRef; +} - (void)updateFileRef; -- (void)updateFileURL; +@end -- (NSData *)copyAliasDataRelativeToPath:(NSString *)newBasePath; +#pragma mark - + +@interface BDSKBookmarkLinkedFile : BDSKLinkedAliasFile { + NSURL *fileRefURL; +} +- (void)updateFileRefURL; - (NSData *)copyBookmarkDataRelativeToPath:(NSString *)newBasePath; - @end #pragma mark - @@ -139,6 +150,10 @@ saveRelativePathOnly = [[NSUserDefaults standardUserDefaults] boolForKey:BDSKSaveLinkedFilesAsRelativePathOnlyKey]; saveArchivedData = [[NSUserDefaults standardUserDefaults] boolForKey:BDSKSaveLinkedFilesAsArchivedDataKey]; wantsBookmark = saveArchivedData == NO && [[NSUserDefaults standardUserDefaults] boolForKey:BDSKSaveLinkedFilesAsBookmarkDataKey]; + + NSString *fileClassName = wantsBookmark ? @"BDSKBookmarkLinkedFile" : @"BDSKAliasLinkedFile"; + [NSKeyedUnarchiver setClass:NSClassFromString(fileClassName) forClassName:@"BDSKLinkedAliasFile"]; + [NSUnarchiver decodeClassName:@"BDSKLinkedAliasFile" asClassName:fileClassName]; } + (id)allocWithZone:(NSZone *)aZone { @@ -246,7 +261,7 @@ - (id)initWithURL:(NSURL *)aURL delegate:(id<BDSKLinkedFileDelegate>)aDelegate { if([aURL isFileURL]) - return (id)[[BDSKLinkedAliasFile alloc] initWithURL:aURL delegate:aDelegate]; + return (id)[(wantsBookmark ? [BDSKBookmarkLinkedFile alloc] : [BDSKAliasLinkedFile alloc]) initWithURL:aURL delegate:aDelegate]; else if (aURL) return (id)[[BDSKLinkedURL alloc] initWithURL:aURL delegate:aDelegate]; else @@ -254,7 +269,7 @@ } - (id)initWithBase64String:(NSString *)base64String delegate:(id<BDSKLinkedFileDelegate>)aDelegate { - return (id)[[BDSKLinkedAliasFile alloc] initWithBase64String:base64String delegate:aDelegate]; + return (id)[(wantsBookmark ? [BDSKBookmarkLinkedFile alloc] : [BDSKAliasLinkedFile alloc]) initWithBase64String:base64String delegate:aDelegate]; } - (id)initWithURLString:(NSString *)aString { @@ -287,7 +302,6 @@ } else { self = [super init]; if (self) { - fileRef = nil; // this is updated lazily, as we don't know the base path at this point alias = [anAlias retain]; relativePath = [relPath copy]; delegate = aDelegate; @@ -301,24 +315,8 @@ } - (id)initWithURL:(NSURL *)aURL delegate:(id<BDSKLinkedFileDelegate>)aDelegate { - BDSKASSERT([aURL isFileURL]); - - NSString *aPath = [aURL path]; - - BDSKASSERT(nil != aPath); - - NSString *basePath = [aDelegate basePathForLinkedFile:self]; - NSString *relPath = basePath ? [aPath relativePathFromPath:basePath] : nil; - id anAlias; - if (wantsBookmark) - anAlias = BDSKCreateBookmarkDataFromURL(aURL); - else - anAlias = [BDSKAlias newWithPath:aPath basePath:basePath]; - self = [self initWithAlias:anAlias relativePath:relPath delegate:aDelegate]; - [anAlias release]; - if (self && basePath) - [self updateFileRef]; - return self; + BDSKASSERT_NOT_REACHED("Attempt to initialize BDSKLinkedAliasFile directly with a URL"); + return nil; } - (id)initWithBase64String:(NSString *)base64String delegate:(id<BDSKLinkedFileDelegate>)aDelegate { @@ -385,6 +383,11 @@ } - (id)initWithCoder:(NSCoder *)coder { + if ([self isMemberOfClass:[BDSKLinkedAliasFile class]]) { + BDSKASSERT_NOT_REACHED("Attempt to decode a BDSKLinkedAliasFile instance"); + [self release]; + self = wantsBookmark ? [BDSKBookmarkLinkedFile alloc] : [BDSKAliasLinkedFile alloc]; + } id anAlias = nil; NSString *relPath = nil; if ([coder allowsKeyedCoding]) { @@ -404,33 +407,23 @@ - (void)encodeWithCoder:(NSCoder *)coder { // make sure the fileRef is valid [self updateFileURL]; - NSData *data = nil; - NSString *basePath = [delegate basePathForLinkedFile:self]; if ([coder allowsKeyedCoding]) { - if (wantsBookmark) { - data = [self copyBookmarkDataRelativeToPath:basePath]; - if (data) - [coder encodeObject:alias forKey:BOOKMARK_KEY]; - else if ([alias isKindOfClass:[BDSKAlias class]] && (data = [alias copyData])) - [coder encodeObject:data forKey:ALIASDATA_KEY]; - } else { - data = [self copyAliasDataRelativeToPath:basePath]; - if (data) - [coder encodeObject:data forKey:ALIASDATA_KEY]; - else if ([alias isKindOfClass:[NSData class]]) - [coder encodeObject:alias forKey:BOOKMARK_KEY]; - } + // the alias or bookmark data is set by the subclasses [coder encodeObject:relativePath forKey:RELATIVEPATH_KEY]; } else { - data = [self copyAliasDataRelativeToPath:basePath]; + NSData *data = [self copyAliasDataRelativeToPath:[delegate basePathForLinkedFile:self]]; [coder encodeObject:data]; [coder encodeObject:relativePath]; + [data release]; } - [data release]; } +// always encode subclasses as BDSKLinkedAliasFile +- (Class)classForKeyedArchiver { return [BDSKLinkedAliasFile class]; } +- (Class)classForArchiver { return [BDSKLinkedAliasFile class]; } +- (Class)classForPortCoder { return [BDSKLinkedAliasFile class]; } + - (void)dealloc { - BDSKDESTROY(fileRef); BDSKDESTROY(alias); BDSKDESTROY(relativePath); BDSKDESTROY(fileURL); @@ -440,21 +433,11 @@ - (id)copyWithZone:(NSZone *)aZone { // make sure the fileRef is valid [self updateFileURL]; - id anAlias = nil; - if (wantsBookmark) { - anAlias = [self copyBookmarkDataRelativeToPath:[delegate basePathForLinkedFile:self]]; - if (anAlias == nil && [alias isKindOfClass:[BDSKAlias class]]) { - NSData *data = [alias copyData]; - anAlias = [BDSKAlias newWithData:data]; - [data release]; - } - } else { - NSData *data = [self copyAliasDataRelativeToPath:[delegate basePathForLinkedFile:self]]; - anAlias = [BDSKAlias newWithData:data]; - [data release]; - if (anAlias == nil && [alias isKindOfClass:[NSData class]]) - anAlias = [alias copy]; - } + NSData *data = [self copyAliasDataRelativeToPath:[delegate basePathForLinkedFile:self]]; + id anAlias = [BDSKAlias newWithData:data]; + [data release]; + if (anAlias == nil && [alias isKindOfClass:[NSData class]]) + anAlias = [alias copy]; self = [[[self class] allocWithZone:aZone] initWithAlias:anAlias relativePath:relativePath delegate:delegate]; [anAlias release]; return self; @@ -499,25 +482,179 @@ } } -- (NSURL *)newPathURL { - if (fileRef == nil) - return nil; - else if (wantsBookmark) - return (NSURL *)CFURLCreateFilePathURL(kCFAllocatorDefault, (CFURLRef)fileRef, NULL); +// this is really implemented by the subclasses +- (void)updateFileURL { + if (fileURL == nil && relativePath) { + NSString *basePath = [delegate basePathForLinkedFile:self]; + if (basePath) + fileURL = [[NSURL alloc] initFileURLWithPath:[relativePath isAbsolutePath] ? relativePath : [[basePath stringByAppendingPathComponent:relativePath] stringByStandardizingPath]]; + } +} + +- (NSURL *)URL { + [self updateFileURL]; + return fileURL; +} + +- (NSURL *)displayURL { + NSURL *displayURL = [self URL]; + if (displayURL == nil && relativePath) { + NSString *basePath = [delegate basePathForLinkedFile:self]; + displayURL = basePath ? [NSURL URLWithString:[relativePath stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLPathAllowedCharacterSet]] relativeToURL:[NSURL fileURLWithPath:basePath isDirectory:YES]] : [NSURL fileURLWithPath:relativePath isDirectory:NO]; + } + return displayURL; +} + +- (BOOL)hasSkimNotes { + if (hasSkimNotesNeedsUpdate) { + hasSkimNotes = [[fileURL SkimNotes] count] > 0; + hasSkimNotesNeedsUpdate = NO; + } + return hasSkimNotes; +} + +- (NSData *)copyAliasDataRelativeToPath:(NSString *)basePath { + BDSKAlias *anAlias = NULL; + NSData *data = nil; + + if (fileURL) { + anAlias = [BDSKAlias newWithPath:[fileURL path] basePath:basePath]; + } else if (relativePath && basePath) { + NSString *path = [relativePath isAbsolutePath] ? relativePath : [[basePath stringByAppendingPathComponent:relativePath] stringByStandardizingPath]; + anAlias = [BDSKAlias newWithPath:path basePath:basePath]; + } + if (anAlias != NULL) { + data = [anAlias copyData]; + [anAlias release]; + } else if ([alias isKindOfClass:[BDSKAlias class]]) { + data = [alias copyData]; + } + + return data; +} + +- (NSData *)copyDataRelativeToPath:(NSString *)newBasePath isBookmark:(BOOL *)isBookmark { + NSData *data = [self copyAliasDataRelativeToPath:newBasePath]; + if (data) { + *isBookmark = NO; + } else if ([alias isKindOfClass:[NSData class]]) { + data = [alias retain]; + *isBookmark = YES; + } + return data; +} + +- (NSString *)stringRelativeToPath:(NSString *)newBasePath { + BOOL noAlias = saveRelativePathOnly && newBasePath != nil; + if (newBasePath == nil) + newBasePath = [delegate basePathForLinkedFile:self]; + NSDictionary *dictionary = nil; + // this will make sure the fileURL is valid + NSString *path = [self path]; + path = path && newBasePath ? [path relativePathFromPath:newBasePath] : relativePath; + if (noAlias == NO || path == nil) { + BOOL isBookmark = NO; + NSData *data = [self copyDataRelativeToPath:newBasePath isBookmark:&isBookmark]; + if (data) { + dictionary = [NSDictionary dictionaryWithObjectsAndKeys:data, (isBookmark ? BOOKMARK_KEY : ALIASDATA_KEY), path, RELATIVEPATH_KEY, nil]; + [data release]; + } + } + if (dictionary == nil) + dictionary = [NSDictionary dictionaryWithObjectsAndKeys:path, RELATIVEPATH_KEY, nil]; + if (saveArchivedData) + return [[NSKeyedArchiver archivedDataWithRootObject:dictionary] base64String]; else - return BDSKCreateURLFromFSRef([fileRef fsRef]); + return [[NSPropertyListSerialization dataWithPropertyList:dictionary format:NSPropertyListBinaryFormat_v1_0 options:0 error:NULL] base64String]; } +// This is called when the file is added (new or initially), +// the document URL changes, or with aPath != nil after an autofile +// implemented by the subclasses +- (void)updateWithPath:(NSString *)aPath {} + +- (void)updateHasSkimNotes { + hasSkimNotesNeedsUpdate = YES; + if (isInitial == NO) + [delegate performSelector:@selector(linkedFileURLChanged:) withObject:self afterDelay:0.0]; +} + +@end + +#pragma mark - + +@implementation BDSKAliasLinkedFile + +- (id)initWithURL:(NSURL *)aURL delegate:(id<BDSKLinkedFileDelegate>)aDelegate { + BDSKASSERT([aURL isFileURL]); + + NSString *aPath = [aURL path]; + + BDSKASSERT(nil != aPath); + + NSString *basePath = [aDelegate basePathForLinkedFile:self]; + NSString *relPath = basePath ? [aPath relativePathFromPath:basePath] : nil; + id anAlias = [BDSKAlias newWithPath:aPath basePath:basePath]; + self = [self initWithAlias:anAlias relativePath:relPath delegate:aDelegate]; + [anAlias release]; + if (self && basePath) + [self updateFileRef]; + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + if ([coder allowsKeyedCoding]) { + NSData *data = [self copyAliasDataRelativeToPath:[delegate basePathForLinkedFile:self]]; + if (data) + [coder encodeObject:data forKey:ALIASDATA_KEY]; + else if ([alias isKindOfClass:[NSData class]]) + [coder encodeObject:alias forKey:BOOKMARK_KEY]; + [data release]; + } +} + +- (void)dealloc { + BDSKZONEDESTROY(fileRef); + [super dealloc]; +} + +- (id)copyWithZone:(NSZone *)aZone { + // make sure the fileRef is valid + [self updateFileURL]; + NSData *data = [self copyAliasDataRelativeToPath:[delegate basePathForLinkedFile:self]]; + id anAlias = [BDSKAlias newWithData:data]; + [data release]; + if (anAlias == nil && [alias isKindOfClass:[NSData class]]) + anAlias = [alias copy]; + self = [[[self class] allocWithZone:aZone] initWithAlias:anAlias relativePath:relativePath delegate:delegate]; + [anAlias release]; + return self; +} + +- (void)setFileRef:(FSRef *)aRef { + BDSKZONEDESTROY(fileRef); + if (aRef != NULL) { + FSRef *newRef = (FSRef *)NSZoneMalloc([self zone], sizeof(FSRef)); + if (newRef) { + bcopy(aRef, newRef, sizeof(FSRef)); + fileRef = newRef; + } else { + [self release]; + self = nil; + } + } +} + - (void)updateAliasWithBaseRef:(FSRef *)baseRef { BDSKASSERT(fileRef != nil); BDSKASSERT(baseRef != NULL); - BDSKASSERT(wantsBookmark == NO); // update the alias if ([alias isKindOfClass:[BDSKAlias class]]) { - [alias updateWithFSRef:[fileRef fsRef] baseRef:baseRef]; + [alias updateWithFSRef:fileRef baseRef:baseRef]; } else { - BDSKAlias *anAlias = [BDSKAlias newWithFSRef:[fileRef fsRef] baseRef:baseRef]; + BDSKAlias *anAlias = [BDSKAlias newWithFSRef:fileRef baseRef:baseRef]; if (anAlias) { BDSKDESTROY(alias); alias = anAlias; @@ -525,130 +662,55 @@ } } -- (void)updateBookmark { - BDSKASSERT(fileURL != nil); - BDSKASSERT(wantsBookmark); - - // update the bookmark - NSData *data = BDSKCreateBookmarkDataFromURL(fileURL ?: [fileRef filePathURL]); - if (data) { - [alias release]; - alias = data; - } -} - - (void)updateFileRef { NSString *basePath = [delegate basePathForLinkedFile:self]; BOOL shouldUpdate = NO; - if (wantsBookmark) { - // using bookmark and NSURL - if (fileRef == nil) { - NSURL *refURL = nil; - - if (fileURL) { - refURL = (NSURL *)CFURLCreateFileReferenceURL(kCFAllocatorDefault, (CFURLRef)fileURL, NULL); - shouldUpdate = basePath != nil && refURL != nil; - } - - if (refURL == nil && basePath && relativePath) { - NSURL *aURL = [NSURL fileURLWithPath:[relativePath isAbsolutePath] ? relativePath : [basePath stringByAppendingPathComponent:relativePath] isDirectory:NO]; - refURL = (NSURL *)CFURLCreateFileReferenceURL(kCFAllocatorDefault, (CFURLRef)aURL, NULL); - shouldUpdate = refURL != nil; - } - - if (refURL == nil) { - if ([alias isKindOfClass:[NSData class]]) { - NSURL *aURL = (NSURL *)CFURLCreateByResolvingBookmarkData(kCFAllocatorDefault, (CFDataRef)alias, kCFURLBookmarkResolutionWithoutUIMask | kCFURLBookmarkResolutionWithoutMountingMask, NULL, NULL, (Boolean *)&shouldUpdate, NULL); - if (aURL) { - refURL = (NSURL *)CFURLCreateFileReferenceURL(kCFAllocatorDefault, (CFURLRef)aURL, NULL); - [aURL release]; - } - shouldUpdate = shouldUpdate && basePath != nil && refURL != nil; - } else if ([alias isKindOfClass:[BDSKAlias class]]) { - FSRef aRef, baseRef; - BOOL ignored, hasBaseRef = BDSKPathToFSRef(basePath, &baseRef); - if ([alias getFSRef:&aRef baseRef:hasBaseRef ? &baseRef : NULL shouldUpdate:&ignored]){ - NSURL *aURL = BDSKCreateURLFromFSRef(&aRef); - if (aURL) { - refURL = (NSURL *)CFURLCreateFileReferenceURL(kCFAllocatorDefault, (CFURLRef)aURL, NULL); - [aURL release]; - } - } - shouldUpdate = hasBaseRef && refURL != nil; - } - } - - if (refURL != nil) { - [fileRef release]; - fileRef = refURL; - } - } else if (relativePath == nil) { - shouldUpdate = basePath != nil; + FSRef baseRef; + BOOL hasBaseRef = BDSKPathToFSRef(basePath, &baseRef); + + if (fileRef == nil) { + FSRef aRef; + BOOL hasRef = NO; + + if (fileURL) { + hasRef = BDSKPathToFSRef([fileURL path], &aRef); + shouldUpdate = hasBaseRef && hasRef; } - if ((shouldUpdate || fileURL == nil) && fileRef != nil) { - NSURL *aURL = [self newPathURL]; - if (aURL != nil) { - if (fileURL == nil) - fileURL = [aURL retain]; - if (shouldUpdate) { - [self updateBookmark]; - [self setRelativePath:[aURL path] fromPath:basePath]; - } - [aURL release]; - } + if (hasRef == NO && hasBaseRef && relativePath) { + NSString *path = [relativePath isAbsolutePath] ? relativePath : [basePath stringByAppendingPathComponent:relativePath]; + shouldUpdate = hasRef = BDSKPathToFSRef(path, &aRef); } - } else { - // using AliasHandle and FSRef - FSRef baseRef; - BOOL hasBaseRef = BDSKPathToFSRef(basePath, &baseRef); - - if (fileRef == nil) { - FSRef aRef; - BOOL hasRef = NO; - - if (fileURL) { - hasRef = BDSKPathToFSRef([fileURL path], &aRef); + if (hasRef == NO) { + if ([alias isKindOfClass:[BDSKAlias class]]) { + hasRef = [alias getFSRef:&aRef baseRef:hasBaseRef ? &baseRef : NULL shouldUpdate:&shouldUpdate]; + shouldUpdate = (shouldUpdate || relativePath == nil) && hasBaseRef && hasRef; + } else if ([alias isKindOfClass:[NSData class]]) { + NSURL *aURL = [NSURL URLByResolvingBookmarkData:alias options:NSURLBookmarkResolutionWithoutUI relativeToURL:nil bookmarkDataIsStale:NULL error:NULL]; + hasRef = BDSKPathToFSRef([aURL path], &aRef); shouldUpdate = hasBaseRef && hasRef; } - - if (hasRef == NO && hasBaseRef && relativePath) { - NSString *path = [relativePath isAbsolutePath] ? relativePath : [basePath stringByAppendingPathComponent:relativePath]; - shouldUpdate = hasRef = BDSKPathToFSRef(path, &aRef); - } - - if (hasRef == NO) { - if ([alias isKindOfClass:[BDSKAlias class]]) { - hasRef = [alias getFSRef:&aRef baseRef:hasBaseRef ? &baseRef : NULL shouldUpdate:&shouldUpdate]; - shouldUpdate = (shouldUpdate || relativePath == nil) && hasBaseRef && hasRef; - } else if ([alias isKindOfClass:[NSData class]]) { - NSURL *aURL = [NSURL URLByResolvingBookmarkData:alias options:NSURLBookmarkResolutionWithoutUI relativeToURL:nil bookmarkDataIsStale:NULL error:NULL]; - hasRef = BDSKPathToFSRef([aURL path], &aRef); - shouldUpdate = hasBaseRef && hasRef; - } - } - - if (hasRef) { - [fileRef release]; - fileRef = [[BDSKFileRef alloc] initWithFSRef:&aRef]; - } - } else if (relativePath == nil) { - shouldUpdate = hasBaseRef; } - if ((shouldUpdate || fileURL == nil) && fileRef != nil) { - NSURL *aURL = [self newPathURL]; - if (aURL != nil) { - if (fileURL == nil) - fileURL = [aURL retain]; - if (shouldUpdate) { - [self updateAliasWithBaseRef:&baseRef]; - [self setRelativePath:[aURL path] fromPath:basePath]; - } - [aURL release]; + if (hasRef) { + [self setFileRef:&aRef]; + } + } else if (relativePath == nil) { + shouldUpdate = hasBaseRef; + } + + if ((shouldUpdate || fileURL == nil) && fileRef != nil) { + NSURL *aURL = BDSKCreateURLFromFSRef(fileRef); + if (aURL != nil) { + if (fileURL == nil) + fileURL = [aURL retain]; + if (shouldUpdate) { + [self updateAliasWithBaseRef:&baseRef]; + [self setRelativePath:[aURL path] fromPath:basePath]; } + [aURL release]; } } } @@ -658,102 +720,301 @@ NSURL *aURL = nil; if (fileRef != nil) { - aURL = [self newPathURL]; + aURL = BDSKCreateURLFromFSRef(fileRef); if (aURL == nil) // fileRef was invalid, try to update it - BDSKDESTROY(fileRef); + BDSKZONEDESTROY(fileRef); } if (aURL == nil) { // fileRef was nil or invalid [self updateFileRef]; - aURL = [self newPathURL]; + aURL = BDSKCreateURLFromFSRef(fileRef); } if ([aURL isEqual:fileURL] == NO && (aURL != nil || hadFileURL)) { - if (wantsBookmark) { - // using bookmark and NSURL - NSURL *refURL = (NSURL *)CFURLCreateFileReferenceURL(kCFAllocatorDefault, (CFURLRef)fileURL, NULL); - if (refURL) { - // the file was replaced, reference the replacement rather than the moved file - // this is what Dropbox does with file updates - [fileRef release]; - fileRef = refURL; - [aURL release]; - aURL = [self newPathURL]; - NSString *basePath = [delegate basePathForLinkedFile:self]; - if (basePath) { - [self updateBookmark]; + FSRef aRef; + if (BDSKPathToFSRef([fileURL path], &aRef)) { + // the file was replaced, reference the replacement rather than the moved file + // this is what Dropbox does with file updates + [self setFileRef:&aRef]; + [aURL release]; + aURL =BDSKCreateURLFromFSRef(fileRef); + NSString *basePath = [delegate basePathForLinkedFile:self]; + FSRef baseRef; + if (BDSKPathToFSRef(basePath, &baseRef)) { + [self updateAliasWithBaseRef:&baseRef]; + [self setRelativePath:[aURL path] fromPath:basePath]; + } + } + [self setFileURL:aURL]; + } + [aURL release]; + isInitial = NO; +} + +- (NSData *)copyAliasDataRelativeToPath:(NSString *)basePath { + BDSKAlias *anAlias = NULL; + NSData *data = nil; + + if (fileRef) { + FSRef baseRef; + BOOL hasBaseRef = BDSKPathToFSRef(basePath, &baseRef); + anAlias = [BDSKAlias newWithFSRef:fileRef baseRef:hasBaseRef ? &baseRef : NULL]; + } else if (relativePath && basePath) { + NSString *path = [relativePath isAbsolutePath] ? relativePath : [[basePath stringByAppendingPathComponent:relativePath] stringByStandardizingPath]; + anAlias = [BDSKAlias newWithPath:path basePath:basePath]; + } + if (anAlias != NULL) { + data = [anAlias copyData]; + [anAlias release]; + } else if ([alias isKindOfClass:[BDSKAlias class]]) { + data = [alias copyData]; + } + + return data; +} + +- (void)setAliasWithPath:(NSString *)aPath basePath:(NSString *)basePath { + id anAlias = [BDSKAlias newWithPath:aPath basePath:basePath]; + if (anAlias != nil) { + id saveAlias = alias; + alias = anAlias; + [self updateFileRef]; + if (fileRef == nil) { + [anAlias release]; + alias = saveAlias; + [self updateFileRef]; + } else { + [saveAlias release]; + } + } +} + +// This is called when the file is added (new or initially), +// the document URL changes, or with aPath != nil after an autofile +- (void)updateWithPath:(NSString *)aPath { + NSString *basePath = [delegate basePathForLinkedFile:self]; + + if (fileRef == nil) { + // this does the updating if possible + [self updateFileRef]; + } else { + NSURL *aURL = BDSKCreateURLFromFSRef(fileRef); + if (aURL != nil) { + // if the path has changed, updating will be done below + if (basePath && (aPath == nil || [[aURL path] isEqualToString:aPath])) { + FSRef baseRef; + if (BDSKPathToFSRef(basePath, &baseRef)) { + [self updateAliasWithBaseRef:&baseRef]; [self setRelativePath:[aURL path] fromPath:basePath]; } } + [aURL release]; } else { - // using AliasHandle and FSRef + // the fileRef was invalid, reset it and update + BDSKZONEDESTROY(fileRef); + [self updateFileRef]; + if (fileRef == nil && aPath) { + // this can happen after an auto file to a volume, as the file is actually not moved but copied + [self setAliasWithPath:aPath basePath:basePath]; + if (basePath) + [self setRelativePath:aPath fromPath:basePath]; + } + } + } + if (aPath) { + NSString *path = [self path]; + if ([path isEqualToString:aPath] == NO) { + BOOL needsUpdate = YES; FSRef aRef; - if (BDSKPathToFSRef([fileURL path], &aRef)) { - // the file was replaced, reference the replacement rather than the moved file - // this is what Dropbox does with file updates - [fileRef release]; - fileRef = [[BDSKFileRef alloc] initWithFSRef:&aRef]; + if (BDSKPathToFSRef(aPath, &aRef)) { + NSURL *aURL = BDSKCreateURLFromFSRef(&aRef); + if ([path isEqualToString:[aURL path]]) { + needsUpdate = NO; + } else { + [self setFileRef:&aRef]; + [self setFileURL:aURL]; + } [aURL release]; - aURL = [self newPathURL]; - NSString *basePath = [delegate basePathForLinkedFile:self]; + } + if (needsUpdate) { FSRef baseRef; if (BDSKPathToFSRef(basePath, &baseRef)) { [self updateAliasWithBaseRef:&baseRef]; - [self setRelativePath:[aURL path] fromPath:basePath]; + [self setRelativePath:aPath fromPath:basePath]; + } else { + [self setAliasWithPath:aPath basePath:basePath]; } } } - [self setFileURL:aURL]; } - [aURL release]; - isInitial = NO; } -- (NSURL *)URL { +@end + +#pragma mark - + +@implementation BDSKBookmarkLinkedFile + +- (id)initWithURL:(NSURL *)aURL delegate:(id<BDSKLinkedFileDelegate>)aDelegate { + BDSKASSERT([aURL isFileURL]); + + NSString *aPath = [aURL path]; + + BDSKASSERT(nil != aPath); + + NSString *basePath = [aDelegate basePathForLinkedFile:self]; + NSString *relPath = basePath ? [aPath relativePathFromPath:basePath] : nil; + id anAlias = BDSKCreateBookmarkDataFromURL(aURL); + self = [self initWithAlias:anAlias relativePath:relPath delegate:aDelegate]; + [anAlias release]; + if (self && basePath) + [self updateFileRefURL]; + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + if ([coder allowsKeyedCoding]) { + NSData *data = [self copyBookmarkDataRelativeToPath:[delegate basePathForLinkedFile:self]]; + if (data) + [coder encodeObject:alias forKey:BOOKMARK_KEY]; + else if ([alias isKindOfClass:[BDSKAlias class]] && (data = [alias copyData])) + [coder encodeObject:data forKey:ALIASDATA_KEY]; + [data release]; + } +} + +- (void)dealloc { + BDSKDESTROY(fileRefURL); + [super dealloc]; +} + +- (id)copyWithZone:(NSZone *)aZone { + // make sure the fileRef is valid [self updateFileURL]; - return fileURL; + id anAlias = [self copyBookmarkDataRelativeToPath:[delegate basePathForLinkedFile:self]]; + if (anAlias == nil && [alias isKindOfClass:[BDSKAlias class]]) { + NSData *data = [alias copyData]; + anAlias = [BDSKAlias newWithData:data]; + [data release]; + } + self = [[[self class] allocWithZone:aZone] initWithAlias:anAlias relativePath:relativePath delegate:delegate]; + [anAlias release]; + return self; } -- (NSURL *)displayURL { - NSURL *displayURL = [self URL]; - if (displayURL == nil && relativePath) { - NSString *basePath = [delegate basePathForLinkedFile:self]; - displayURL = basePath ? [NSURL URLWithString:[relativePath stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLPathAllowedCharacterSet]] relativeToURL:[NSURL fileURLWithPath:basePath isDirectory:YES]] : [NSURL fileURLWithPath:relativePath isDirectory:NO]; +- (void)updateBookmark { + BDSKASSERT(fileURL != nil); + + // update the bookmark + NSData *data = BDSKCreateBookmarkDataFromURL(fileURL ?: [fileRefURL filePathURL]); + if (data) { + [alias release]; + alias = data; } - return displayURL; } -- (BOOL)hasSkimNotes { - if (hasSkimNotesNeedsUpdate) { - hasSkimNotes = [[fileURL SkimNotes] count] > 0; - hasSkimNotesNeedsUpdate = NO; +- (void)updateFileRefURL { + NSString *basePath = [delegate basePathForLinkedFile:self]; + BOOL shouldUpdate = NO; + + // using bookmark and NSURL + if (fileRefURL == nil) { + NSURL *refURL = nil; + + if (fileURL) { + refURL = (NSURL *)CFURLCreateFileReferenceURL(kCFAllocatorDefault, (CFURLRef)fileURL, NULL); + shouldUpdate = basePath != nil && refURL != nil; + } + + if (refURL == nil && basePath && relativePath) { + NSURL *aURL = [NSURL fileURLWithPath:[relativePath isAbsolutePath] ? relativePath : [basePath stringByAppendingPathComponent:relativePath] isDirectory:NO]; + refURL = (NSURL *)CFURLCreateFileReferenceURL(kCFAllocatorDefault, (CFURLRef)aURL, NULL); + shouldUpdate = refURL != nil; + } + + if (refURL == nil) { + if ([alias isKindOfClass:[NSData class]]) { + NSURL *aURL = (NSURL *)CFURLCreateByResolvingBookmarkData(kCFAllocatorDefault, (CFDataRef)alias, kCFURLBookmarkResolutionWithoutUIMask | kCFURLBookmarkResolutionWithoutMountingMask, NULL, NULL, (Boolean *)&shouldUpdate, NULL); + if (aURL) { + refURL = (NSURL *)CFURLCreateFileReferenceURL(kCFAllocatorDefault, (CFURLRef)aURL, NULL); + [aURL release]; + } + shouldUpdate = shouldUpdate && basePath != nil && refURL != nil; + } else if ([alias isKindOfClass:[BDSKAlias class]]) { + FSRef aRef, baseRef; + BOOL ignored, hasBaseRef = BDSKPathToFSRef(basePath, &baseRef); + if ([alias getFSRef:&aRef baseRef:hasBaseRef ? &baseRef : NULL shouldUpdate:&ignored]){ + NSURL *aURL = BDSKCreateURLFromFSRef(&aRef); + if (aURL) { + refURL = (NSURL *)CFURLCreateFileReferenceURL(kCFAllocatorDefault, (CFURLRef)aURL, NULL); + [aURL release]; + } + } + shouldUpdate = hasBaseRef && refURL != nil; + } + } + + if (refURL != nil) { + [fileRefURL release]; + fileRefURL = refURL; + } + } else if (relativePath == nil) { + shouldUpdate = basePath != nil; } - return hasSkimNotes; + + if ((shouldUpdate || fileURL == nil) && fileRefURL != nil) { + NSURL *aURL = (NSURL *)CFURLCreateFilePathURL(kCFAllocatorDefault, (CFURLRef)fileRefURL, NULL); + if (aURL != nil) { + if (fileURL == nil) + fileURL = [aURL retain]; + if (shouldUpdate) { + [self updateBookmark]; + [self setRelativePath:[aURL path] fromPath:basePath]; + } + [aURL release]; + } + } } -- (NSData *)copyAliasDataRelativeToPath:(NSString *)basePath { - BDSKAlias *anAlias = NULL; - NSData *data = nil; +- (void)updateFileURL { + BOOL hadFileURL = fileURL != nil; + NSURL *aURL = nil; - if (wantsBookmark && fileURL) { - anAlias = [BDSKAlias newWithPath:[fileURL path] basePath:basePath]; - } else if (wantsBookmark == NO && fileRef) { - FSRef baseRef; - BOOL hasBaseRef = BDSKPathToFSRef(basePath, &baseRef); - anAlias = [BDSKAlias newWithFSRef:[fileRef fsRef] baseRef:hasBaseRef ? &baseRef : NULL]; - } else if (relativePath && basePath) { - NSString *path = [relativePath isAbsolutePath] ? relativePath : [[basePath stringByAppendingPathComponent:relativePath] stringByStandardizingPath]; - anAlias = [BDSKAlias newWithPath:path basePath:basePath]; + if (fileRefURL != nil) { + aURL = (NSURL *)CFURLCreateFilePathURL(kCFAllocatorDefault, (CFURLRef)fileRefURL, NULL); + if (aURL == nil) + // fileRefURL was invalid, try to update it + BDSKDESTROY(fileRefURL); } - if (anAlias != NULL) { - data = [anAlias copyData]; - [anAlias release]; - } else if ([alias isKindOfClass:[BDSKAlias class]]) { - data = [alias copyData]; + if (aURL == nil) { + // fileRefURL was nil or invalid + [self updateFileRefURL]; + if (fileRefURL != nil) + aURL = (NSURL *)CFURLCreateFilePathURL(kCFAllocatorDefault, (CFURLRef)fileRefURL, NULL); } - return data; + if ([aURL isEqual:fileURL] == NO && (aURL != nil || hadFileURL)) { + // using bookmark and NSURL + NSURL *refURL = (NSURL *)CFURLCreateFileReferenceURL(kCFAllocatorDefault, (CFURLRef)fileURL, NULL); + if (refURL) { + // the file was replaced, reference the replacement rather than the moved file + // this is what Dropbox does with file updates + [fileRefURL release]; + fileRefURL = refURL; + [aURL release]; + aURL = (NSURL *)CFURLCreateFilePathURL(kCFAllocatorDefault, (CFURLRef)fileRefURL, NULL); + NSString *basePath = [delegate basePathForLinkedFile:self]; + if (basePath) { + [self updateBookmark]; + [self setRelativePath:[aURL path] fromPath:basePath]; + } + } + [self setFileURL:aURL]; + } + [aURL release]; + isInitial = NO; } - (NSData *)copyBookmarkDataRelativeToPath:(NSString *)basePath { @@ -771,52 +1032,27 @@ return data; } -- (NSString *)stringRelativeToPath:(NSString *)newBasePath { - BOOL noAlias = saveRelativePathOnly && newBasePath != nil; - if (newBasePath == nil) - newBasePath = [delegate basePathForLinkedFile:self]; - // this will make sure the fileRef is valid - NSString *path = [self path]; - NSData *data = nil; - NSString *dataKey = ALIASDATA_KEY; - path = path && newBasePath ? [path relativePathFromPath:newBasePath] : relativePath; - if (noAlias == NO || path == nil) { - if (wantsBookmark) { - data = [self copyBookmarkDataRelativeToPath:newBasePath]; - if (data) - dataKey = BOOKMARK_KEY; - else if ([alias isKindOfClass:[BDSKAlias class]]) - data = [alias copyData]; - } else { - data = [self copyAliasDataRelativeToPath:newBasePath]; - if (data == nil && [alias isKindOfClass:[NSData class]]) { - data = [alias retain]; - dataKey = BOOKMARK_KEY; - } - } +- (NSData *)copyDataRelativeToPath:(NSString *)newBasePath isBookmark:(BOOL *)isBookmark { + NSData *data = [self copyBookmarkDataRelativeToPath:newBasePath]; + if (data) { + *isBookmark = YES; + } else if ([alias isKindOfClass:[BDSKAlias class]]) { + data = [alias copyData]; + *isBookmark = NO; } - NSDictionary *dictionary = data ? [NSDictionary dictionaryWithObjectsAndKeys:data, dataKey, path, RELATIVEPATH_KEY, nil] : [NSDictionary dictionaryWithObjectsAndKeys:path, RELATIVEPATH_KEY, nil]; - [data release]; - if (saveArchivedData) - return [[NSKeyedArchiver archivedDataWithRootObject:dictionary] base64String]; - else - return [[NSPropertyListSerialization dataWithPropertyList:dictionary format:NSPropertyListBinaryFormat_v1_0 options:0 error:NULL] base64String]; + return data; } -- (void)setAliasOrBookmarkWithPath:(NSString *)aPath basePath:(NSString *)basePath { - id anAlias = nil; - if (wantsBookmark) - anAlias = BDSKCreateBookmarkDataFromURL([NSURL fileURLWithPath:aPath]); - else - anAlias = [BDSKAlias newWithPath:aPath basePath:basePath]; +- (void)setBookmarkWithPath:(NSString *)aPath { + id anAlias = BDSKCreateBookmarkDataFromURL([NSURL fileURLWithPath:aPath]); if (anAlias != nil) { id saveAlias = alias; alias = anAlias; - [self updateFileRef]; - if (fileRef == nil) { + [self updateFileRefURL]; + if (fileRefURL == nil) { [anAlias release]; alias = saveAlias; - [self updateFileRef]; + [self updateFileRefURL]; } else { [saveAlias release]; } @@ -828,33 +1064,25 @@ - (void)updateWithPath:(NSString *)aPath { NSString *basePath = [delegate basePathForLinkedFile:self]; - if (fileRef == nil) { + if (fileRefURL == nil) { // this does the updating if possible - [self updateFileRef]; + [self updateFileRefURL]; } else { - NSURL *aURL = [self newPathURL]; + NSURL *aURL = (NSURL *)CFURLCreateFilePathURL(kCFAllocatorDefault, (CFURLRef)fileRefURL, NULL); if (aURL != nil) { // if the path has changed, updating will be done below if (basePath && (aPath == nil || [[aURL path] isEqualToString:aPath])) { - if (wantsBookmark) { - [self updateBookmark]; - [self setRelativePath:[aURL path] fromPath:basePath]; - } else { - FSRef baseRef; - if (BDSKPathToFSRef(basePath, &baseRef)) { - [self updateAliasWithBaseRef:&baseRef]; - [self setRelativePath:[aURL path] fromPath:basePath]; - } - } + [self updateBookmark]; + [self setRelativePath:[aURL path] fromPath:basePath]; } [aURL release]; } else { - // the fileRef was invalid, reset it and update - BDSKDESTROY(fileRef); - [self updateFileRef]; - if (fileRef == nil && aPath) { + // the fileRefURL was invalid, reset it and update + BDSKDESTROY(fileRefURL); + [self updateFileRefURL]; + if (fileRefURL == nil && aPath) { // this can happen after an auto file to a volume, as the file is actually not moved but copied - [self setAliasOrBookmarkWithPath:aPath basePath:basePath]; + [self setBookmarkWithPath:aPath]; if (basePath) [self setRelativePath:aPath fromPath:basePath]; } @@ -864,63 +1092,31 @@ NSString *path = [self path]; if ([path isEqualToString:aPath] == NO) { BOOL needsUpdate = YES; - if (wantsBookmark) { - // using bookmark and NSURL - NSURL *refURL = (NSURL *)CFURLCreateFileReferenceURL(kCFAllocatorDefault, (CFURLRef)[NSURL fileURLWithPath:aPath], NULL); - if (refURL) { - NSURL *aURL = (NSURL *)CFURLCreateFilePathURL(kCFAllocatorDefault, (CFURLRef)refURL, NULL); - if ([path isEqualToString:[aURL path]]) { - [refURL release]; - needsUpdate = NO; - } else { - [fileRef release]; - fileRef = refURL; - [self setFileURL:aURL]; - } - [aURL release]; + NSURL *refURL = (NSURL *)CFURLCreateFileReferenceURL(kCFAllocatorDefault, (CFURLRef)[NSURL fileURLWithPath:aPath], NULL); + if (refURL) { + NSURL *aURL = (NSURL *)CFURLCreateFilePathURL(kCFAllocatorDefault, (CFURLRef)refURL, NULL); + if ([path isEqualToString:[aURL path]]) { + [refURL release]; + needsUpdate = NO; + } else { + [fileRefURL release]; + fileRefURL = refURL; + [self setFileURL:aURL]; } - if (needsUpdate) { - if (basePath) { - [self updateBookmark]; - [self setRelativePath:aPath fromPath:basePath]; - } else { - [self setAliasOrBookmarkWithPath:aPath basePath:basePath]; - } + [aURL release]; + } + if (needsUpdate) { + if (basePath) { + [self updateBookmark]; + [self setRelativePath:aPath fromPath:basePath]; + } else { + [self setBookmarkWithPath:aPath]; } - } else { - // using AliasHandle and FSRef - FSRef aRef; - if (BDSKPathToFSRef(aPath, &aRef)) { - NSURL *aURL = BDSKCreateURLFromFSRef(&aRef); - if ([path isEqualToString:[aURL path]]) { - needsUpdate = NO; - } else { - [fileRef release]; - fileRef = [[BDSKFileRef alloc] initWithFSRef:&aRef]; - [self setFileURL:aURL]; - } - [aURL release]; - } - if (needsUpdate) { - FSRef baseRef; - if (BDSKPathToFSRef(basePath, &baseRef)) { - [self updateAliasWithBaseRef:&baseRef]; - [self setRelativePath:aPath fromPath:basePath]; - } else { - [self setAliasOrBookmarkWithPath:aPath basePath:basePath]; - } - } } } } } -- (void)updateHasSkimNotes { - hasSkimNotesNeedsUpdate = YES; - if (isInitial == NO) - [delegate performSelector:@selector(linkedFileURLChanged:) withObject:self afterDelay:0.0]; -} - @end #pragma mark - This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. _______________________________________________ Bibdesk-commit mailing list Bibdesk-commit@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/bibdesk-commit