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

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