UKFilePathView
Language: Objective-C, Author: Uli Kusterer
License: MIT/X11
An NSView that displays a file path. This looks kind of like how Sherlock displays the location of a file. Basically you get each folder's display name with its icon in front of it, and little grey triangles between them, and you get the icon of each file or folder and its display name.
If the path is too long, this takes items out of the middle and displays an ellipsis character (...) instead.
You can also right-click or control-click on this to get a contextual menu that contains "Reveal in Finder" and "Show Real Names" menu choices. Real names displays the actual path, starting with "/", and using the actual file names instead of their display names. As a shortcut, double-clicking this view is the same as "Reveal in Finder".
Finally, this also lets you hook up buttons to the view to let you choose an existing, or new file or folder name.
UKFilePathView source preview
// // UKFilePathView.m // Shovel // // Created by Uli Kusterer on Thu Mar 25 2004. // Copyright (c) 2004 M. Uli Kusterer. All rights reserved. // #import "UKFilePathView.h" #import "UKGraphics.h" @implementation UKFilePathView -(id) initWithFrame: (NSRect)frame { self = [super initWithFrame:frame]; if( self ) { filePath = nil; // Means "none". noDisplayNames = NO; // Show display names by default. canChooseFiles = YES; canChooseDirectories = NO; treatsFilePackagesAsDirectories = NO; placeholderString = [NSLocalizedString(@"<None>", @"default UKFilePathView placeholder") retain]; } return self; } -(void) dealloc { [filePath release]; [placeholderString release]; [types release]; [super dealloc]; } -(void) drawRect:(NSRect)rect { NSPoint pos = { 0, 0 }; NSDictionary* attribs = [NSDictionary dictionaryWithObjectsAndKeys: [NSFont systemFontOfSize: [NSFont systemFontSize]], NSFontAttributeName, [NSColor controlTextColor], NSForegroundColorAttributeName, nil]; NSDictionary* disAttribs = [NSDictionary dictionaryWithObjectsAndKeys: [NSFont systemFontOfSize: [NSFont systemFontSize]], NSFontAttributeName, [NSColor disabledControlTextColor], NSForegroundColorAttributeName, nil]; NSMutableArray* components = [NSMutableArray array]; NSMutableArray* icons = [NSMutableArray array]; NSMutableArray* paths = [NSMutableArray array]; NSSize theSize = [@"foo" sizeWithAttributes: attribs]; NSEnumerator* enny = nil; NSString* currName; NSImage* emptyImg = [[[NSImage alloc] initWithSize: NSMakeSize(1,1)] autorelease]; // Draw border and make sure text is vertically centered: UKDrawDropHighlightedEditableWhiteBezel( drawDropHighlight, action != 0, [self bounds], [self bounds] ); pos.y += truncf(([self bounds].size.height -theSize.height) /2); // If no path specified, show "none": if( filePath == nil ) { [placeholderString drawAtPoint: NSMakePoint(pos.x +4, pos.y +2) withAttributes: disAttribs]; return; } // Build the display path and list our icons: enny = [[filePath pathComponents] objectEnumerator]; NSMutableString* currPath = [NSMutableString string]; while( (currName = [enny nextObject]) ) { int pln = [currPath length]; if( pln == 0 || [currPath characterAtIndex: pln -1] != '/' ) [currPath appendString: @"/"]; if( ![currName isEqualToString: @"/"] ) [currPath appendString: currName]; [icons addObject: [[NSWorkspace sharedWorkspace] iconForFile: currPath]]; [paths addObject: [[currPath copy] autorelease]]; if( !noDisplayNames ) // We're showing display names? { [components addObject: [[NSFileManager defaultManager] displayNameAtPath: currPath]]; if( [currPath isEqualToString: @"/Volumes"] ) { [components removeObjectsInRange: NSMakeRange(0,2)]; [icons removeObjectsInRange: NSMakeRange(0,2)]; [paths removeObjectsInRange: NSMakeRange(0,2)]; } } else [components addObject: currName]; } enny = [components objectEnumerator]; int componentsToGo = [components count]; // Calculate width of displayed path: theSize.width = 0; theSize.height = 0; while( (currName = [enny nextObject]) ) { theSize.width += [currName sizeWithAttributes: attribs].width +UK_PATH_NAME_TOTAL_HMARGIN +UK_PATH_ICON_IMG_WIDTH +UK_PATH_ICON_NAME_HDISTANCE; if( --componentsToGo > 0 ) theSize.width += UK_PATH_ARROW_IMG_WIDTH; } // If it's wider than our box, start taking components out of the middle: if( [components count] > 2 && (theSize.width > [self bounds].size.width) ) { int middleEntry = ([components count] /2) -1; if( (middleEntry * 2) < [components count] ) middleEntry++; // Replace the middle-most entry with an ellipsis ("..."): theSize.width -= [[components objectAtIndex: middleEntry] sizeWithAttributes: attribs].width +UK_PATH_NAME_TOTAL_HMARGIN +UK_PATH_ICON_IMG_WIDTH +UK_PATH_ICON_NAME_HDISTANCE; [components replaceObjectAtIndex: middleEntry withObject: UK_PATH_ELLIPSIS]; theSize.width += [UK_PATH_ELLIPSIS sizeWithAttributes: attribs].width +UK_PATH_NAME_TOTAL_HMARGIN; while( [components count] > 3 && (theSize.width > [self bounds].size.width) ) { [components removeObjectAtIndex: middleEntry]; // Remove "...". [icons removeObjectAtIndex: middleEntry]; // Remove empty icon for ellipsis. [paths removeObjectAtIndex: middleEntry]; // Remove empty path for ellipsis. middleEntry = ([components count] /2); theSize.width -= [[components objectAtIndex: middleEntry] sizeWithAttributes: attribs].width +UK_PATH_NAME_TOTAL_HMARGIN +UK_PATH_ARROW_IMG_WIDTH +UK_PATH_ICON_IMG_WIDTH +UK_PATH_ICON_NAME_HDISTANCE; [components replaceObjectAtIndex: middleEntry withObject: UK_PATH_ELLIPSIS]; [icons replaceObjectAtIndex: middleEntry withObject: emptyImg]; [paths replaceObjectAtIndex: middleEntry withObject: @""]; } } if( [components count] == 3 && theSize.width > [self bounds].size.width ) // Still wider? { // Remove final two components so we only show file icon and name: [components removeObjectAtIndex: 1]; [icons removeObjectAtIndex: 1]; [paths removeObjectAtIndex: 1]; [components removeObjectAtIndex: 0]; [icons removeObjectAtIndex: 0]; [paths removeObjectAtIndex: 0]; } // Draw components that are left: NSImage* theIcon = nil; NSEnumerator* iconEnny = [icons objectEnumerator]; NSString* thePath = nil; NSEnumerator* pathEnny = [paths objectEnumerator]; enny = [components objectEnumerator]; componentsToGo = [components count]; while( (currName = [enny nextObject]) ) { theIcon = [iconEnny nextObject]; thePath = [pathEnny nextObject]; theSize = [currName sizeWithAttributes: attribs]; theSize.width += UK_PATH_NAME_TOTAL_HMARGIN; theSize.height += UK_PATH_NAME_TOTAL_VMARGIN; BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath: thePath]; //NSLog(@"%d %@ (%@)", (int)exists, currName, thePath ); if( ![UK_PATH_ELLIPSIS isEqualToString: currName] ) { [theIcon setSize: NSMakeSize(UK_PATH_ICON_IMG_WIDTH,UK_PATH_ICON_IMG_WIDTH)]; [theIcon dissolveToPoint: NSMakePoint(pos.x +UK_PATH_NAME_LEFT_MARGIN, pos.y +UK_PATH_NAME_BOTTOM_MARGIN) fraction: (exists ? 1 : 0.3)]; pos.x += UK_PATH_ICON_IMG_WIDTH +UK_PATH_ICON_NAME_HDISTANCE; } [currName drawAtPoint: NSMakePoint(pos.x +UK_PATH_NAME_LEFT_MARGIN, pos.y +UK_PATH_NAME_BOTTOM_MARGIN) withAttributes: (exists ? attribs : disAttribs)]; pos.x += theSize.width; if( --componentsToGo > 0 ) { [[NSImage imageNamed: @"PathArrow.tiff"] dissolveToPoint: pos fraction: 1]; pos.x += UK_PATH_ARROW_IMG_WIDTH; } } } -(NSString *) filePath { return filePath; } -(void) setFilePath: (NSString *)newFilePath { if( filePath != newFilePath ) { [filePath release]; filePath = [newFilePath retain]; [self setNeedsDisplay: YES]; } } -(void) reshowDisplayNames: (id)sender { noDisplayNames = NO; [self setNeedsDisplay: YES]; } -(void) showRealNames: (id)sender { if( !noDisplayNames ) { noDisplayNames = YES; [self setNeedsDisplay: YES]; [NSTimer scheduledTimerWithTimeInterval: 5 // 5 secs should be enough. target: self selector:@selector(reshowDisplayNames:) userInfo:nil repeats: NO]; } } -(void) toggleShowRealNames: (id)sender { noDisplayNames = !noDisplayNames; [self setNeedsDisplay: YES]; } -(void) revealInFinder: (id)sender { [[NSWorkspace sharedWorkspace] selectFile: filePath inFileViewerRootedAtPath: @""]; } -(BOOL) validateMenuItem: (id<NSMenuItem>)item { if( [item action] == @selector(revealInFinder:) || [item action] == @selector(showRealNames:) ) return filePath != nil; else if( [item action] == @selector(toggleShowRealNames:) ) { [item setState: noDisplayNames]; return filePath != nil; } else return NO; } // Upon a click, we shortly toggle from display to file names and back: -(void) mouseDown: (NSEvent*)evt { if( filePath && [evt clickCount] == 2 ) [[NSWorkspace sharedWorkspace] selectFile: filePath inFileViewerRootedAtPath: @""]; } +(NSMenu*) defaultMenu { NSMenu* theMenu = [[[NSMenu alloc] initWithTitle: @"Contextual Menu"] autorelease]; [theMenu addItemWithTitle: NSLocalizedString(@"Reveal in Finder",@"UKFilePathView contextual menu item") action: @selector(revealInFinder:) keyEquivalent: @""]; [theMenu addItemWithTitle: NSLocalizedString(@"Show Real File Names",@"UKFilePathView contextual menu item") action: @selector(toggleShowRealNames:) keyEquivalent: @""]; return theMenu; } -(IBAction) pickFile: (id)sender { NSOpenPanel* op = [NSOpenPanel openPanel]; [op setAllowsMultipleSelection: NO]; [op setCanChooseFiles: canChooseFiles]; [op setCanChooseDirectories: canChooseDirectories]; [op setTreatsFilePackagesAsDirectories: treatsFilePackagesAsDirectories]; [op beginSheetForDirectory: [filePath stringByDeletingLastPathComponent] file: filePath types: types modalForWindow: [self window] modalDelegate: self didEndSelector: @selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo: self]; } -(IBAction) pickNewFile: (id)sender { NSSavePanel* op = [NSSavePanel savePanel]; [op setTreatsFilePackagesAsDirectories: treatsFilePackagesAsDirectories]; [op beginSheetForDirectory: [filePath stringByDeletingLastPathComponent] file: filePath modalForWindow: [self window] modalDelegate: self didEndSelector: @selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo: self]; } -(void) openPanelDidEnd: (NSOpenPanel*)sheet returnCode: (int)returnCode contextInfo: (void*)contextInfo { if( returnCode == NSOKButton ) { [self setFilePath: [sheet filename]]; if( [target respondsToSelector: action] ) [target performSelector: action withObject: self]; } } -(IBAction) pickNoFile: (id)sender { [self setFilePath: nil]; if( [target respondsToSelector: action] ) [target performSelector: action withObject: self]; } // ---------------------------------------------------------- // - types: // ---------------------------------------------------------- - (NSArray *) types { return types; } // ---------------------------------------------------------- // - setTypes: // ---------------------------------------------------------- - (void) setTypes: (NSArray *) theTypes { if (types != theTypes) { [types release]; types = [theTypes retain]; } } // ---------------------------------------------------------- // - canChooseFiles: // ---------------------------------------------------------- - (BOOL) canChooseFiles { return canChooseFiles; } // ---------------------------------------------------------- // - setCanChooseFiles: // ---------------------------------------------------------- - (void) setCanChooseFiles: (BOOL) flag { canChooseFiles = flag; } // ---------------------------------------------------------- // - canChooseDirectories: // ---------------------------------------------------------- - (BOOL) canChooseDirectories { return canChooseDirectories; } // ---------------------------------------------------------- // - setCanChooseDirectories: // ---------------------------------------------------------- - (void) setCanChooseDirectories: (BOOL) flag { canChooseDirectories = flag; } // ---------------------------------------------------------- // - treatsFilePackagesAsDirectories: // ---------------------------------------------------------- - (BOOL) treatsFilePackagesAsDirectories { return treatsFilePackagesAsDirectories; } // ---------------------------------------------------------- // - setTreatsFilePackagesAsDirectories: // ---------------------------------------------------------- - (void) setTreatsFilePackagesAsDirectories: (BOOL) flag { treatsFilePackagesAsDirectories = flag; } -(NSString *) stringValue { return [self filePath]; } -(void) setStringValue: (NSString*)s { [self setFilePath: s]; } -(void) concludeDragOperation: (id <NSDraggingInfo>)sender { drawDropHighlight = NO; [self setNeedsDisplay: YES]; } -(NSDragOperation) draggingEntered: (id <NSDraggingInfo>)sender { drawDropHighlight = YES; [self setNeedsDisplay: YES]; return NSDragOperationLink; } -(BOOL) prepareForDragOperation:(id <NSDraggingInfo>)sender { return YES; } -(BOOL) performDragOperation: (id <NSDraggingInfo>)sender { NSPasteboard* pb = [sender draggingPasteboard]; NSString* type = [pb availableTypeFromArray: [NSArray arrayWithObjects: NSFilenamesPboardType, NSFilesPromisePboardType, nil]]; NSArray* arr = [pb propertyListForType: type]; NSString* thePath = [arr objectAtIndex: 0]; NSString* fileExtension = [thePath pathExtension]; BOOL isDir = NO; [[NSFileManager defaultManager] fileExistsAtPath: thePath isDirectory: &isDir]; if( [types containsObject: [fileExtension lowercaseString]] && ((!isDir && canChooseFiles) || (isDir && canChooseDirectories)) ) { [self setFilePath: thePath]; [target performSelector: action withObject: self]; } else NSBeep(); return YES; } -(void) draggingExited:(id <NSDraggingInfo>)sender { drawDropHighlight = NO; [self setNeedsDisplay: YES]; } // ---------------------------------------------------------- // - action: // ---------------------------------------------------------- - (SEL) action { return action; } // ---------------------------------------------------------- // - setAction: // ---------------------------------------------------------- - (void) setAction: (SEL) theAction { action = theAction; if( action != 0 ) [self registerForDraggedTypes: [NSArray arrayWithObjects: NSFilenamesPboardType, NSFilesPromisePboardType, nil]]; else [self unregisterDraggedTypes]; } // ---------------------------------------------------------- // - target: // ---------------------------------------------------------- - (id) target { return target; } // ---------------------------------------------------------- // - setTarget: // ---------------------------------------------------------- - (void) setTarget: (id) theTarget { target = theTarget; } -(void) setPlaceholderString: (NSString*)string { if( string != placeholderString ) { [placeholderString release]; placeholderString = [string retain]; } } -(NSString*) placeholderString { return placeholderString; } @end
UKFilePathView header preview
// // UKFilePathView.h // Shovel // // Created by Uli Kusterer on Thu Mar 25 2004. // Copyright (c) 2004 M. Uli Kusterer. All rights reserved. // #import <AppKit/AppKit.h> /* An NSView that displays a file path. This looks kind of like how Sherlock displays the location of a file. Basically you get each folder's display name with its icon in front of it, and little grey triangles between them, and you get the icon of the file or folder at the end and its display name. If the path is too long, this takes items out of the middle and displays an ellipsis character (...) instead. You can also right-click or control-click on this to get a contextual menu that contains "Reveal in Finder" and "Show Real Names" menu choices. Real names displays the actual path, starting with "/", and using the actual file names instead of their display names. As a shortcut, double-clicking this view is the same as "Reveal in Finder". This needs: - PathArrow.tiff (for the little triangle) - UKGraphics.h/.m (for drawing the bezel around the view) */ @interface UKFilePathView : NSView { NSString* filePath; // The path to be displayed. BOOL noDisplayNames; // Show actual names, not display names. BOOL canChooseFiles; // Handed on directly to the open panel. BOOL canChooseDirectories; // Handed on directly to the open panel. BOOL treatsFilePackagesAsDirectories; // Handed on directly to the open/save panels. NSArray* types; // Handed on directly to the open panel. SEL action; id target; BOOL drawDropHighlight; NSString* placeholderString; // A placeholder to show when path is NIL. Defaults to "none". } -(NSString *) filePath; -(void) setFilePath: (NSString *)newFilePath; -(id) target; -(void) setTarget: (id) theTarget; -(SEL) action; -(void) setAction: (SEL) theAction; -(void) revealInFinder: (id)sender; -(void) showRealNames: (id)sender; -(void) toggleShowRealNames: (id)sender; -(NSString *) stringValue; // same as filePath. -(void) setStringValue: (NSString*)s; // same as setFilePath. -(void) setPlaceholderString: (NSString*)string; -(NSString*) placeholderString; // UI for changing value: -(IBAction) pickFile: (id)sender; // NSOpenPanel. Chooses existing files. -(IBAction) pickNewFile: (id)sender; // NSSavePanel. Lets the user specify name and location for new files. -(IBAction) pickNoFile: (id)sender; // Sets the file path to NIL. // Getters/setters for the NSOpenPanel properties: -(BOOL) canChooseFiles; -(void) setCanChooseFiles: (BOOL)flag; -(BOOL) canChooseDirectories; -(void) setCanChooseDirectories: (BOOL)flag; -(NSArray*) types; -(void) setTypes: (NSArray*)theTypes; // Getters/setters for NSOpenPanel/NSSavePanel properties: -(BOOL) treatsFilePackagesAsDirectories; -(void) setTreatsFilePackagesAsDirectories: (BOOL)flag; @end // What to display instead of items that had to be left out because there wasn't room: #define UK_PATH_ELLIPSIS @"..." // Some constants we use for this view's metrics: #define UK_PATH_NAME_LEFT_MARGIN 4 #define UK_PATH_NAME_RIGHT_MARGIN 4 #define UK_PATH_NAME_TOP_MARGIN 2 #define UK_PATH_NAME_BOTTOM_MARGIN 2 #define UK_PATH_NAME_TOTAL_VMARGIN (UK_PATH_NAME_TOP_MARGIN +UK_PATH_NAME_BOTTOM_MARGIN) #define UK_PATH_NAME_TOTAL_HMARGIN (UK_PATH_NAME_LEFT_MARGIN +UK_PATH_NAME_RIGHT_MARGIN) #define UK_PATH_ARROW_IMG_WIDTH 16 #define UK_PATH_ICON_IMG_WIDTH 16 #define UK_PATH_ICON_NAME_HDISTANCE 2Download Archive
Compatible with:
- Mac OS X 10.3
- Mac OS X 10.4 PPC
- Mac OS X 10.4 Intel
- Mac OS X 10.5 PPC
- Mac OS X 10.5 Intel
