This is File.m in view mode; [Download] [Up]
#pragma .h #import <Foundation/NSString.h> #pragma .h #import <IconKit/iconkit.h> #import <Foundation/NSDictionary.h> #import <Foundation/NSPathUtilities.h> #import <Foundation/NSProcessInfo.h> #import <AppKit/NSWorkspace.h> #import "File.h" #import "FileList.h" #pragma .h typedef struct _FileFlags { #pragma .h unsigned int imageLoaded:1; #pragma .h unsigned int acceptingDragImageLoaded:1; #pragma .h unsigned int parentLoaded:1; #pragma .h unsigned int childrenLoaded:1; #pragma .h } FileFlags; @implementation File: IKFolder static NSImage *draggedImage = nil; static NSMutableDictionary *fileMap = nil; + (void)initialize { if (self == [File class]) { fileMap = [[NSMutableDictionary alloc] init]; } return; } + multipleSelectionClass { return [FileList class]; } + (NSArray *) pasteTypes { static NSMutableArray *pasteTypes = nil; if(pasteTypes == nil) { pasteTypes = [[NSMutableArray alloc] init]; [pasteTypes addObject:NSFilenamesPboardType]; [pasteTypes addObject:IKidPboardType()]; } return pasteTypes; } + fileForPath: (NSString *) thePath { // <<HACK>> This is really bad since files which we fetched once will never go away ! // But right now we have bigger fish to fry...so ignor this ugly piece of code. id file = [fileMap objectForKey: thePath]; if( !file ) { file = [[File alloc] initPath: thePath]; [fileMap setObject:file forKey:thePath]; } else { // Just retain it once more so taht we can release it again [file retain]; } return [file autorelease]; } - initPath: (NSString *) thePath { // <<HACK>> Method name should be changed ! initWithPath or initWithFile etc... NSString * fileType; NSString * title = [thePath lastPathComponent]; NSDictionary * dict; if( !title ) return nil; // <<HACK>> This does not work properly with WindowsNT !!! Too hardcoded ! else if( [title isEqual:@""] || [title isEqual:@"/"] ) title = [[NSProcessInfo processInfo] hostName]; // Now we can be sure that the title is what we expect it to be... self = [super init:title]; if( !self ) return nil; path = [thePath retain]; // This could be wrapped in "isDictionray" on the MiscKit.. dict = [[NSFileManager defaultManager] fileAttributesAtPath:path traverseLink:NO]; flags.dragAccepting = [[dict fileType] isEqual:NSFileTypeDirectory]; flags.leaf = !flags.dragAccepting; flags.hidden = NO; fileFlags.imageLoaded = NO; fileFlags.acceptingDragImageLoaded = NO; fileFlags.parentLoaded = NO; fileFlags.childrenLoaded = !flags.dragAccepting; // Set some other minor detaiils. // <<HACK>> This is not really very cleaer..esp the file wrapper part. isApplication = NO; isFileWrapper = NO; fileType = [path pathExtension]; if( [fileType isEqual:@"app"] ) { isApplication = YES; isFileWrapper = YES; flags.dragAccepting = YES; } else if( [fileType isEqual:@"rftd"] || [fileType isEqual:@"nib"] || [fileType isEqual:@"eomodeld"] ) { isFileWrapper = YES; flags.dragAccepting = NO; } return self; } - copyWithZone: (NSZone *) zone; { File *copy = [super copyWithZone: zone]; copy->path = [path copyWithZone:zone]; copy->fileFlags = fileFlags; return copy; } - (void)dealloc { [path release]; [super dealloc]; } - image { if (!fileFlags.imageLoaded) image = draggedImage ? [draggedImage retain] : [[[NSWorkspace sharedWorkspace] iconForFile:path] retain]; fileFlags.imageLoaded = 1; return image; } - acceptingDragImage { if( isFileWrapper ) return [self image]; else return [super acceptingDragImage]; } - (NSString *) path { return path; } - parents { NSString *directory; if (!fileFlags.parentLoaded) { fileFlags.parentLoaded = 1; directory = [path stringByDeletingLastPathComponent]; if([directory isEqual:@""]) { [[File fileForPath:@"/"] addChild: self]; } else { [[File fileForPath:directory] addChild: self]; } } return parents; } - (void)addParent: parent { fileFlags.parentLoaded = 1; [super addParent: parent]; } - (void)removeChild:child withAnnouncement:(BOOL)yesNo { NSEnumerator *enumerator = [fileMap keyEnumerator]; NSString *key, *p = [child path]; fileFlags.childrenLoaded = 0; while(key = [enumerator nextObject]) { if([key hasPrefix:p]) { [fileMap removeObjectForKey:key]; } } [fileMap removeObjectForKey:p]; [super removeChild:child withAnnouncement:yesNo]; } - children { int i, count; if (!fileFlags.childrenLoaded) { NSArray *files = [[NSFileManager defaultManager] directoryContentsAtPath:path]; fileFlags.childrenLoaded = 1; [users setSendAnnouncements: NO]; for(i = 0, count = [files count]; i < count; i++) { NSString *s = [files objectAtIndex:i]; [self addChild: [File fileForPath:[path stringByAppendingPathComponent:s]]]; } [users setSendAnnouncements: YES]; } return children; } - (void)copyToPasteboard: (NSPasteboard *) pboard { [super copyToPasteboard: pboard]; [pboard addTypes:[NSArray arrayWithObject:NSFilenamesPboardType] owner:nil]; [pboard setPropertyList:[NSArray arrayWithObject:path] forType:NSFilenamesPboardType]; } + readFromPasteboard: (NSPasteboard *) pboard { id file = nil; if( (file = [super readFromPasteboard: pboard]) == nil && [pboard availableTypeFromArray:[NSArray arrayWithObject:NSFilenamesPboardType]] != NULL) { id a = [pboard propertyListForType:NSFilenamesPboardType]; if([a isKindOfClass:[NSString class]]) { file = [File fileForPath:a]; } else if( [a isKindOfClass:[NSArray class]] ) { if([a count] > 1) file = [FileList readFromPasteboard:pboard]; else file = [File fileForPath:[a objectAtIndex:0]]; } } return file; } - (unsigned int) draggingEntered: (id <NSDraggingInfo>) sender { int n = [sender draggingSequenceNumber]; if (draggingSession != n) { // We are an application _and_ a fileWrapper, then we should only support "move" operations // if we really support the filepath of the dragged objects. // IF the "command" key is pressed we will actept all dragged files. if( isApplication && isFileWrapper ) { dragging = [[[self class] readFromPasteboard : [sender draggingPasteboard]] retain]; draggingSession = n; if( !dragging ) { operationMask = NSDragOperationNone; } else { // if command key // if( ![self _applicationSupportsFile:[draggedFile path]] ) // return NO; operationMask = NSDragOperationGeneric; // else check agaisnt supported filetypes... } } // otherwise we will leave the work to our superclass // <<NOTE>> This code is really evil since it heavily lelies on the implementation of the super method. // We might not have been able to do this if the super code was not known .. else [super draggingEntered:sender]; } return [self draggingOperation: sender]; } - (unsigned int) draggingOperation: (id <NSDraggingInfo>) sender { unsigned int choices = operationMask & [sender draggingSourceOperationMask] & ~NSDragOperationPrivate; while (choices & (choices - 1)) choices &= choices - 1; return choices; } /* shelf listener methods */ + (void)shelf:sender dragWillEnter: (id <NSDraggingInfo>) source { draggedImage = [[source draggedImage] copy]; } + (void)shelf:sender dragWillExit: (id <NSDraggingInfo>) source { [draggedImage release]; draggedImage = nil; } + (void)shelf:sender dragWillComplete: (id <NSDraggingInfo>) source { [draggedImage release]; draggedImage = nil; } - (BOOL)performDragOperation:(id <NSDraggingInfo>) source { id draggedFile; id pasteboard = [source draggingPasteboard]; int i; id infoDict; id pathArray; draggedFile = [[self class] readFromPasteboard:pasteboard]; // If we are an application the we propably should open that file. // If the command key is pressed we will open if no matter if( isApplication && isFileWrapper ) { if( [draggedFile isKindOfClass:[File class]] ) { NSLog( @"Should perform drop... if we are an app...open the file.. %@", [draggedFile path] ); infoDict = [NSMutableDictionary new]; [infoDict setObject:[self path] forKey:@"App"]; [infoDict setObject:[NSArray arrayWithObject:[draggedFile path]] forKey:@"Files"]; if( [[NSUserDefaults standardUserDefaults] boolForKey:@"UseWorkspaceThreads"] ) [NSThread detachNewThreadSelector:@selector(_detachedAppBasedFileOpen:) toTarget:self withObject:infoDict]; else [self performSelector:@selector(_detachedAppBasedFileOpen:) withObject:infoDict afterDelay:0.5]; } else if( [draggedFile isKindOfClass:[FileList class]] ) { // count, objectAtIndex: etc. infoDict = [NSMutableDictionary new]; [infoDict setObject:[self path] forKey:@"App"]; pathArray = [NSMutableArray new]; for( i=0; i<[draggedFile count]; i++ ) [pathArray addObject:[[draggedFile objectAtIndex:i] path]]; [infoDict setObject:pathArray forKey:@"Files"]; if( [[NSUserDefaults standardUserDefaults] boolForKey:@"UseWorkspaceThreads"] ) [NSThread detachNewThreadSelector:@selector(_detachedAppBasedFileOpen:) toTarget:self withObject:infoDict]; else [self performSelector:@selector(_detachedAppBasedFileOpen:) withObject:infoDict afterDelay:0.5]; } } else if( flags.dragAccepting ) { // Ok...seems like we are a folder and propably should trigger a copy or move operation based on the // drag modifier that we have... or disable the drag if it violates the file system rules (e.g permission etc) } return YES; } - (BOOL)_applicationSupportsFile:(id)path { // We currently accept every file..no restriction what so ever... hmm.. thats definitly ugly. // this will change at least for OpenStep applications where the necessary info should be in the Info.dict ! // <<HACK>> It definitly is a hack that we don't have a subclass ExecutableFile : File or that like which would // handle this properly without all the ifs etc. return YES; } - (void)_detachedAppBasedFileOpen:(id)infoDict { // Subppol needed if threaded... id threadPool = [NSAutoreleasePool new]; id pathEnum = [[infoDict objectForKey:@"Files"] objectEnumerator]; id nextFile; id appPath = [infoDict objectForKey:@"App"]; NSLog( @"Opening with info... %@", infoDict ); while( nextFile = [pathEnum nextObject] ) [[NSWorkspace sharedWorkspace] openFile:nextFile withApplication:appPath andDeactivate:YES]; [threadPool release]; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.