source: trunk/Cocoa/Pester/Source/NJRFSObjectSelector.m @ 53

Last change on this file since 53 was 53, checked in by Nicholas Riley, 19 years ago

Updated for Pester 1.1a5 (very limited release).

Pester 1.1a4 was never released.

File size: 9.6 KB
Line 
1#import "NJRFSObjectSelector.h"
2#import "NSImage-NJRExtensions.h"
3#import "NSString-NJRExtensions.h"
4#include <Carbon/Carbon.h>
5
6static NSImage *PopupTriangleImage = nil;
7static NSSize PopupTriangleSize;
8
9@implementation NJRFSObjectSelector
10
11- (void)_initSelector;
12{
13    if (PopupTriangleImage == nil) {
14        PopupTriangleImage = [[NSImage imageNamed: @"Popup triangle"] retain];
15        PopupTriangleSize = [PopupTriangleImage size];
16    }
17    canChooseFiles = YES; canChooseDirectories = NO;
18    [self setAlias: nil];
19    [[self cell] setHighlightsBy: NSChangeBackgroundCell];
20    [[self cell] setGradientType: NSGradientNone];
21    [self registerForDraggedTypes:
22        [NSArray arrayWithObjects: NSFilenamesPboardType, NSURLPboardType, nil]];
23}
24
25- (void)dealloc;
26{
27    [selectedAlias release];
28    [fileTypes release];
29    [super dealloc];
30}
31
32- (id)initWithCoder:(NSCoder *)coder;
33{
34    if ( (self = [super initWithCoder: coder]) != nil) {
35        [self _initSelector];
36    }
37    return self;
38}
39
40- (id)initWithFrame:(NSRect)frame;
41{
42    if ( (self = [super initWithFrame: frame]) != nil) {
43        [self _initSelector];
44    }
45    return self;
46}
47
48- (void)drawRect:(NSRect)rect;
49{
50    NSRect boundsRect = [self bounds];
51    [super drawRect: rect];
52    if (dragAccepted) {
53        [[NSColor selectedControlColor] set];
54        [NSBezierPath setDefaultLineWidth: 2];
55        [NSBezierPath strokeRect: NSInsetRect(boundsRect, 2, 2)];
56    } else if (selectedAlias != nil && [self isEnabled]) {
57        // equivalent to popup triangle location for large bezel in Carbon
58        [PopupTriangleImage compositeToPoint: NSMakePoint(NSMaxX(boundsRect) - PopupTriangleSize.width - 5, NSMaxY(boundsRect) - 5) operation: NSCompositeSourceOver];
59    }
60}
61
62- (BOOL)acceptsPath:(NSString *)path;
63{
64    NSFileManager *fm = [NSFileManager defaultManager];
65    BOOL isDir;
66
67    if (![fm fileExistsAtPath: path isDirectory: &isDir]) return NO;
68
69    if (isDir) return canChooseDirectories;
70    if (canChooseFiles) {
71        NSEnumerator *e = [fileTypes objectEnumerator];
72        NSString *extension = [path pathExtension];
73        NSString *hfsType = NSHFSTypeOfFile(path);
74        NSString *fileType;
75       
76        while ( (fileType = [e nextObject]) != nil) {
77            if ([fileType isEqualToString: extension] || [fileType isEqualToString: hfsType])
78                return YES;
79        }
80    }
81    return NO;
82}
83
84- (void)setImage:(NSImage *)image;
85{
86    [super setImage: [image bestFitImageForSize: [[self cell] cellSize]]];
87}
88
89- (IBAction)select:(id)sender;
90{
91    NSOpenPanel *openPanel = [NSOpenPanel openPanel];
92    NSString *path = [selectedAlias fullPath];
93    [openPanel setAllowsMultipleSelection: NO];
94    [openPanel setCanChooseDirectories: canChooseDirectories];
95    [openPanel setCanChooseFiles: canChooseFiles];
96    [openPanel beginSheetForDirectory: [path stringByDeletingLastPathComponent]
97                                 file: [path lastPathComponent]
98                                types: fileTypes
99                       modalForWindow: [self window]
100                        modalDelegate: self
101                       didEndSelector: @selector(openPanelDidEnd:returnCode:contextInfo:)
102                          contextInfo: nil];
103}
104
105- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
106{
107    [sheet close];
108
109    if (returnCode == NSOKButton) {
110        NSArray *files = [sheet filenames];
111        NSAssert1([files count] == 1, @"%d items returned, only one expected", [files count]);
112        [self setPath: [files objectAtIndex: 0]];
113        [[self target] tryToPerform: [self action] with: self];
114    }
115}
116
117- (void)revealInFinder:(id<NSMenuItem>)sender;
118{
119    NSString *path = [sender representedObject];
120    if (path == nil) return;
121    [[NSWorkspace sharedWorkspace] selectFile: path inFileViewerRootedAtPath: @""];
122}
123
124- (void)setAlias:(BDAlias *)alias;
125{
126    if (selectedAlias != alias) {
127        [selectedAlias release];
128        selectedAlias = [alias retain];
129    }
130
131    if (alias != nil) { // alias is set
132        NSString *path = [alias fullPath];
133        NSString *revealPath = nil;
134        NSMenu *menu = [[NSMenu alloc] initWithTitle: @""];
135        NSFileManager *fmgr = [NSFileManager defaultManager];
136        NSMenuItem *item;
137        if (path != nil) { // can resolve alias
138            [self setImage: [[NSWorkspace sharedWorkspace] iconForFile: path]];
139            {   // set image first so titleRectForBounds: returns the correct value
140                NSMutableString *title = [[fmgr displayNameAtPath: path] mutableCopy];
141                NSDictionary *fontAttributes = [[self attributedTitle] fontAttributesInRange: NSMakeRange(0, 0)];
142                [title truncateToWidth: [[self cell] titleRectForBounds: [self bounds]].size.width - PopupTriangleSize.width by: NSLineBreakByTruncatingMiddle withAttributes: fontAttributes];
143                [self setTitle: title];
144                [title release];
145            }
146            do {
147                item = [menu addItemWithTitle: [fmgr displayNameAtPath: path]
148                                       action: @selector(revealInFinder:)
149                                keyEquivalent: @""];
150                [item setTarget: self];
151                [item setRepresentedObject: revealPath];
152                [item setImage:
153                    [[[NSWorkspace sharedWorkspace] iconForFile: path] bestFitImageForSize: NSMakeSize(16, 16)]];
154                revealPath = path;
155                path = [path stringByDeletingLastPathComponent];
156                NSAssert1(![path isEqualToString: revealPath], @"Stuck on path |%@|", [alias fullPath]);
157            } while (![revealPath isEqualToString: @"/"] && ![path isEqualToString: @"/Volumes"]);
158            [[self cell] setMenu: menu];
159        } else {
160            [self setImage: nil];
161            [self setTitle: @"(not available)"];
162            [[self cell] setMenu: nil];
163        }
164    } else {
165        [self setImage: nil];
166        [self setTitle: @"(none selected)"];
167        [[self cell] setMenu: nil];
168    }
169    [self setEnabled: isEnabled];
170}
171
172- (BOOL)isEnabled;
173{
174    return isEnabled;
175}
176
177- (void)setEnabled:(BOOL)enabled;
178{
179    isEnabled = enabled;
180    [super setEnabled: enabled ? selectedAlias != nil : NO];
181}
182
183- (void)rightMouseDown:(NSEvent *)theEvent;
184{
185    [self mouseDown: theEvent];
186}
187
188- (void)otherMouseDown:(NSEvent *)theEvent;
189{
190    [self mouseDown: theEvent];
191}
192
193extern MenuRef _NSGetCarbonMenu(NSMenu *menu);
194
195- (void)mouseDown:(NSEvent *)theEvent;
196{
197    NSMenu *menu = [[self cell] menu];
198    MenuRef mRef = _NSGetCarbonMenu(menu);
199
200    if (mRef == NULL) {
201        NSMenu *appMenu = [[[NSApp mainMenu] itemWithTitle: @""] submenu];
202        if (appMenu != nil) {
203            NSMenuItem *item = [appMenu addItemWithTitle: @"" action: NULL keyEquivalent: @""];
204            [appMenu setSubmenu: menu forItem: item];
205            [appMenu removeItem: item];
206        }
207        mRef = _NSGetCarbonMenu(menu);
208    }
209
210    ChangeMenuAttributes(mRef, kMenuAttrExcludesMarkColumn, 0);
211    theEvent = [NSEvent mouseEventWithType: [theEvent type]
212                                  location: [self convertPoint: NSMakePoint(-1, 1) toView: nil]
213                             modifierFlags: [theEvent modifierFlags]
214                                 timestamp: [theEvent timestamp]
215                              windowNumber: [theEvent windowNumber]
216                                   context: [theEvent context]
217                               eventNumber: [theEvent eventNumber]
218                                clickCount: [theEvent clickCount]
219                                  pressure: [theEvent pressure]];
220
221    // this undocumented API does not work any better; contextual menu items are still added:
222    // [menu _popUpMenuWithEvent: theEvent forView: self];
223    [NSMenu popUpContextMenu: menu withEvent: theEvent forView: self];
224}
225
226- (BDAlias *)alias;
227{
228    return selectedAlias;
229}
230
231- (void)setPath:(NSString *)path;
232{
233    [self setAlias: [BDAlias aliasWithPath: path]];
234}
235
236- (BOOL)canChooseDirectories;
237{
238    return canChooseDirectories;
239}
240
241- (BOOL)canChooseFiles;
242{
243    return canChooseFiles;
244}
245
246- (void)setCanChooseDirectories:(BOOL)flag;
247{
248    canChooseDirectories = flag;
249}
250
251- (void)setCanChooseFiles:(BOOL)flag;
252{
253    canChooseFiles = flag;
254}
255
256- (NSArray *)fileTypes;
257{
258    return fileTypes;
259}
260
261- (void)setFileTypes:(NSArray *)types;
262{
263    if (fileTypes == types) return;
264   
265    [fileTypes release];
266    fileTypes = [types retain];
267}
268
269@end
270
271
272@implementation NJRFSObjectSelector (NSDraggingDestination)
273
274- (BOOL)acceptsDragFrom:(id <NSDraggingInfo>)sender;
275{
276    NSURL *url = [NSURL URLFromPasteboard: [sender draggingPasteboard]];
277
278    if (url == nil || ![url isFileURL]) return NO;
279    return [self acceptsPath: [url path]];
280}
281
282- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender;
283{
284    if ([self acceptsDragFrom: sender] && [sender draggingSourceOperationMask] &
285        (NSDragOperationCopy | NSDragOperationLink)) {
286        dragAccepted = YES;
287        [self setNeedsDisplay: YES];
288        return NSDragOperationLink;
289    }
290    return NSDragOperationNone;
291}
292
293- (void)draggingExited:(id <NSDraggingInfo>)sender;
294{
295    dragAccepted = NO;
296    [self setNeedsDisplay: YES];
297}
298
299- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender;
300{
301    dragAccepted = NO;
302    [self setNeedsDisplay: YES];
303    return [self acceptsDragFrom: sender];
304}
305
306- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender;
307{
308    if ([sender draggingSource] != self) {
309        NSURL *url = [NSURL URLFromPasteboard: [sender draggingPasteboard]];
310        if (url == nil) return NO;
311        [self setPath: [url path]];
312        [[self target] tryToPerform: [self action] with: self];
313    }
314    return YES;
315}
316
317@end
Note: See TracBrowser for help on using the repository browser.