source: releases/Pester/1.1b4/Source/NJRFSObjectSelector.m

Last change on this file was 53, checked in by Nicholas Riley, 21 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.