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

Last change on this file since 553 was 553, checked in by Nicholas Riley, 10 years ago

No longer need special code to scale icons (it had issues, anyway).

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    } else {
160        [self setImage: nil];
161        [self setTitle: @"(none selected)"];
162        [[self cell] setMenu: nil];
163    }
164    [self setEnabled: isEnabled];
165}
166
167- (BOOL)isEnabled;
168{
169    return isEnabled;
170}
171
172- (void)setEnabled:(BOOL)enabled;
173{
174    isEnabled = enabled;
175    [super setEnabled: enabled ? selectedAlias != nil : NO];
176}
177
178- (void)rightMouseDown:(NSEvent *)theEvent;
179{
180    [self mouseDown: theEvent];
181}
182
183- (void)otherMouseDown:(NSEvent *)theEvent;
184{
185    [self mouseDown: theEvent];
186}
187
188extern MenuRef _NSGetCarbonMenu(NSMenu *menu);
189
190- (void)mouseDown:(NSEvent *)theEvent;
191{
192    if (![self isEnabled]) return;
193   
194    NSMenu *menu = [[self cell] menu];
195    MenuRef mRef = _NSGetCarbonMenu(menu);
196
197    if (mRef == NULL) {
198        NSMenu *appMenu = [[[NSApp mainMenu] itemWithTitle: @""] submenu];
199        if (appMenu != nil) {
200            NSMenuItem *item = [appMenu addItemWithTitle: @"" action: NULL keyEquivalent: @""];
201            [appMenu setSubmenu: menu forItem: item];
202            [appMenu removeItem: item];
203        }
204        mRef = _NSGetCarbonMenu(menu);
205    }
206
207    ChangeMenuAttributes(mRef, kMenuAttrExcludesMarkColumn, 0);
208    theEvent = [NSEvent mouseEventWithType: [theEvent type]
209                                  location: [self convertPoint: NSMakePoint(-1, 1) toView: nil]
210                             modifierFlags: [theEvent modifierFlags]
211                                 timestamp: [theEvent timestamp]
212                              windowNumber: [theEvent windowNumber]
213                                   context: [theEvent context]
214                               eventNumber: [theEvent eventNumber]
215                                clickCount: [theEvent clickCount]
216                                  pressure: [theEvent pressure]];
217
218    // XXX otherwise Cocoa thoughtfully doesn't give me the font I want
219    NSFont *font = [[self cell] font];
220    [NSMenu popUpContextMenu: menu withEvent: theEvent forView: self withFont:
221     [NSFont fontWithName: [font fontName] size: [font pointSize] - 0.001]];
222}
223
224- (BDAlias *)alias;
225{
226    return selectedAlias;
227}
228
229- (void)setPath:(NSString *)path;
230{
231    [self setAlias: [BDAlias aliasWithPath: path]];
232}
233
234- (BOOL)canChooseDirectories;
235{
236    return canChooseDirectories;
237}
238
239- (BOOL)canChooseFiles;
240{
241    return canChooseFiles;
242}
243
244- (void)setCanChooseDirectories:(BOOL)flag;
245{
246    canChooseDirectories = flag;
247}
248
249- (void)setCanChooseFiles:(BOOL)flag;
250{
251    canChooseFiles = flag;
252}
253
254- (NSArray *)fileTypes;
255{
256    return fileTypes;
257}
258
259- (void)setFileTypes:(NSArray *)types;
260{
261    if (fileTypes == types) return;
262   
263    [fileTypes release];
264    fileTypes = [types retain];
265}
266
267@end
268
269
270@implementation NJRFSObjectSelector (NSDraggingDestination)
271
272- (BOOL)acceptsDragFrom:(id <NSDraggingInfo>)sender;
273{
274    NSURL *url = [NSURL URLFromPasteboard: [sender draggingPasteboard]];
275
276    if (url == nil || ![url isFileURL]) return NO;
277    return [self acceptsPath: [url path]];
278}
279
280- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender;
281{
282    if ([self acceptsDragFrom: sender] && [sender draggingSourceOperationMask] &
283        (NSDragOperationCopy | NSDragOperationLink)) {
284        dragAccepted = YES;
285        [self setNeedsDisplay: YES];
286        return NSDragOperationLink;
287    }
288    return NSDragOperationNone;
289}
290
291- (void)draggingExited:(id <NSDraggingInfo>)sender;
292{
293    dragAccepted = NO;
294    [self setNeedsDisplay: YES];
295}
296
297- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender;
298{
299    dragAccepted = NO;
300    [self setNeedsDisplay: YES];
301    return [self acceptsDragFrom: sender];
302}
303
304- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender;
305{
306    if ([sender draggingSource] != self) {
307        NSURL *url = [NSURL URLFromPasteboard: [sender draggingPasteboard]];
308        if (url == nil) return NO;
309        [self setPath: [url path]];
310                if ([self target] != nil && [[self target] respondsToSelector:[self action]])
311                        [[self target] performSelector: [self action] withObject: self];
312    }
313    return YES;
314}
315
316@end
Note: See TracBrowser for help on using the repository browser.