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

Last change on this file since 578 was 558, checked in by Nicholas Riley, 15 years ago

Fix leak in NJRFSObjectSelector.

File size: 9.6 KB
Line 
1#import "NJRFSObjectSelector.h"
2#import "NSMenuItem-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- (IBAction)select:(id)sender;
85{
86 NSOpenPanel *openPanel = [NSOpenPanel openPanel];
87 NSString *path = [selectedAlias fullPath];
88 [openPanel setAllowsMultipleSelection: NO];
89 [openPanel setCanChooseDirectories: canChooseDirectories];
90 [openPanel setCanChooseFiles: canChooseFiles];
91 [openPanel beginSheetForDirectory: [path stringByDeletingLastPathComponent]
92 file: [path lastPathComponent]
93 types: fileTypes
94 modalForWindow: [self window]
95 modalDelegate: self
96 didEndSelector: @selector(openPanelDidEnd:returnCode:contextInfo:)
97 contextInfo: nil];
98}
99
100- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
101{
102 [sheet close];
103
104 if (returnCode == NSOKButton) {
105 NSArray *files = [sheet filenames];
106 NSAssert1([files count] == 1, @"%d items returned, only one expected", [files count]);
107 [self setPath: [files objectAtIndex: 0]];
108 if ([self target] != nil && [[self target] respondsToSelector:[self action]])
109 [[self target] performSelector: [self action] withObject: self];
110 }
111}
112
113- (void)revealInFinder:(id<NSMenuItem>)sender;
114{
115 NSString *path = [sender representedObject];
116 if (path == nil) return;
117 [[NSWorkspace sharedWorkspace] selectFile: path inFileViewerRootedAtPath: @""];
118}
119
120- (void)setAlias:(BDAlias *)alias;
121{
122 if (selectedAlias != alias) {
123 [selectedAlias release];
124 selectedAlias = [alias retain];
125 }
126
127 if (alias != nil) { // alias is set
128 NSString *path = [alias fullPath];
129 NSString *revealPath = nil;
130 NSMenu *menu = [[NSMenu alloc] initWithTitle: @""];
131 NSFileManager *fmgr = [NSFileManager defaultManager];
132 NSMenuItem *item;
133 if (path != nil) { // can resolve alias
134 [self setImage: [[NSWorkspace sharedWorkspace] iconForFile: path]];
135 { // set image first so titleRectForBounds: returns the correct value
136 NSMutableString *title = [[fmgr displayNameAtPath: path] mutableCopy];
137 NSDictionary *fontAttributes = [[self attributedTitle] fontAttributesInRange: NSMakeRange(0, 0)];
138 [title truncateToWidth: [[self cell] titleRectForBounds: [self bounds]].size.width - PopupTriangleSize.width by: NSLineBreakByTruncatingMiddle withAttributes: fontAttributes];
139 [self setTitle: title];
140 [title release];
141 }
142 do {
143 NSAssert1(![path isEqualToString: revealPath], @"Stuck on path |%@|", [alias fullPath]);
144 item = [menu addItemWithTitle: [fmgr displayNameAtPath: path]
145 action: @selector(revealInFinder:)
146 keyEquivalent: @""];
147 [item setTarget: self];
148 [item setRepresentedObject: revealPath];
149 [item setImageFromPath: path];
150 revealPath = path;
151 path = [path stringByDeletingLastPathComponent];
152 } while (![revealPath isEqualToString: @"/"] && ![path isEqualToString: @"/Volumes"]);
153 [[self cell] setMenu: menu];
154 } else {
155 [self setImage: nil];
156 [self setTitle: @"(not available)"];
157 [[self cell] setMenu: nil];
158 }
159 [menu release];
160 } else {
161 [self setImage: nil];
162 [self setTitle: @"(none selected)"];
163 [[self cell] setMenu: nil];
164 }
165 [self setEnabled: isEnabled];
166}
167
168- (BOOL)isEnabled;
169{
170 return isEnabled;
171}
172
173- (void)setEnabled:(BOOL)enabled;
174{
175 isEnabled = enabled;
176 [super setEnabled: enabled ? selectedAlias != nil : NO];
177}
178
179- (void)rightMouseDown:(NSEvent *)theEvent;
180{
181 [self mouseDown: theEvent];
182}
183
184- (void)otherMouseDown:(NSEvent *)theEvent;
185{
186 [self mouseDown: theEvent];
187}
188
189extern MenuRef _NSGetCarbonMenu(NSMenu *menu);
190
191- (void)mouseDown:(NSEvent *)theEvent;
192{
193 if (![self isEnabled]) return;
194
195 NSMenu *menu = [[self cell] menu];
196 MenuRef mRef = _NSGetCarbonMenu(menu);
197
198 if (mRef == NULL) {
199 NSMenu *appMenu = [[[NSApp mainMenu] itemWithTitle: @""] submenu];
200 if (appMenu != nil) {
201 NSMenuItem *item = [appMenu addItemWithTitle: @"" action: NULL keyEquivalent: @""];
202 [appMenu setSubmenu: menu forItem: item];
203 [appMenu removeItem: item];
204 }
205 mRef = _NSGetCarbonMenu(menu);
206 }
207
208 ChangeMenuAttributes(mRef, kMenuAttrExcludesMarkColumn, 0);
209 theEvent = [NSEvent mouseEventWithType: [theEvent type]
210 location: [self convertPoint: NSMakePoint(-1, 1) toView: nil]
211 modifierFlags: [theEvent modifierFlags]
212 timestamp: [theEvent timestamp]
213 windowNumber: [theEvent windowNumber]
214 context: [theEvent context]
215 eventNumber: [theEvent eventNumber]
216 clickCount: [theEvent clickCount]
217 pressure: [theEvent pressure]];
218
219 // XXX otherwise Cocoa thoughtfully doesn't give me the font I want
220 NSFont *font = [[self cell] font];
221 [NSMenu popUpContextMenu: menu withEvent: theEvent forView: self withFont:
222 [NSFont fontWithName: [font fontName] size: [font pointSize] - 0.001]];
223}
224
225- (BDAlias *)alias;
226{
227 return selectedAlias;
228}
229
230- (void)setPath:(NSString *)path;
231{
232 [self setAlias: [BDAlias aliasWithPath: path]];
233}
234
235- (BOOL)canChooseDirectories;
236{
237 return canChooseDirectories;
238}
239
240- (BOOL)canChooseFiles;
241{
242 return canChooseFiles;
243}
244
245- (void)setCanChooseDirectories:(BOOL)flag;
246{
247 canChooseDirectories = flag;
248}
249
250- (void)setCanChooseFiles:(BOOL)flag;
251{
252 canChooseFiles = flag;
253}
254
255- (NSArray *)fileTypes;
256{
257 return fileTypes;
258}
259
260- (void)setFileTypes:(NSArray *)types;
261{
262 if (fileTypes == types) return;
263
264 [fileTypes release];
265 fileTypes = [types retain];
266}
267
268@end
269
270
271@implementation NJRFSObjectSelector (NSDraggingDestination)
272
273- (BOOL)acceptsDragFrom:(id <NSDraggingInfo>)sender;
274{
275 NSURL *url = [NSURL URLFromPasteboard: [sender draggingPasteboard]];
276
277 if (url == nil || ![url isFileURL]) return NO;
278 return [self acceptsPath: [url path]];
279}
280
281- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender;
282{
283 if ([self acceptsDragFrom: sender] && [sender draggingSourceOperationMask] &
284 (NSDragOperationCopy | NSDragOperationLink)) {
285 dragAccepted = YES;
286 [self setNeedsDisplay: YES];
287 return NSDragOperationLink;
288 }
289 return NSDragOperationNone;
290}
291
292- (void)draggingExited:(id <NSDraggingInfo>)sender;
293{
294 dragAccepted = NO;
295 [self setNeedsDisplay: YES];
296}
297
298- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender;
299{
300 dragAccepted = NO;
301 [self setNeedsDisplay: YES];
302 return [self acceptsDragFrom: sender];
303}
304
305- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender;
306{
307 if ([sender draggingSource] != self) {
308 NSURL *url = [NSURL URLFromPasteboard: [sender draggingPasteboard]];
309 if (url == nil) return NO;
310 [self setPath: [url path]];
311 if ([self target] != nil && [[self target] respondsToSelector:[self action]])
312 [[self target] performSelector: [self action] withObject: self];
313 }
314 return YES;
315}
316
317@end
Note: See TracBrowser for help on using the repository browser.