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

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