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

Last change on this file since 43 was 43, checked in by Nicholas Riley, 20 years ago

Pester 1.1a1.

English.lproj/InfoPlist.strings: Updated for 1.1a1.

English.lproj/MainMenu.nib: Placeholder for day names in popup menu, fixed up by code (this means you can still edit it from IB though). Added command-shift-T to both in/at cells (required, code removes one or the other as appropriate). Fixed up sizes of fields. Default to today (this will need fixing when we localized the word "today", but it's fine for now...).

English.lproj/Notifier.nib: Remove date formatter because we set a string directly now instead (could set formatter from code, but we don't).

NJRDateFormatter: many workarounds for Cocoa bugs: missing AM/PM, incorrect results with space before AM/PM, etc. Added class methods to do format manipulation and return localized formats which work for output (though not always for input; this class has an internal workaround for the AM/PM problem).

NJRFSObjectSelector: properly handle enabled attribute, store internally and report externally as appropriate. Previously, the button would become enabled if you dropped something on it even if it was supposed to be disabled.

NJRQTMediaPopUpButton: stop sound preview when button disabled.

NJRVoicePopUpButton: stop voice preview when button disabled.

PSAlarm: new method -dateString returns long date string. Maintain local copy of long date, short date and time formats, and locale, using NJRDateFormatter.

PSAlarmNotifierController: update to use -[PSAlarm dateString], -[PSAlarm timeString] for localization instead of using broken formatter.

PSAlarmSetController: update documentation for some more Cocoa bugs I need to file. Set time of day and date formatters with localized date formats from NJRDateFormatter (retain/release issue here?) Localize weekday popup for predefined dates. Localize static date display with NJRDateFormatter. Note a solution (thanks to Douglas Davidson) for figuring out which control is editing. Added command-shift-T key equivalent to toggle in/at. Properly work around bugs witih soundRepetitionCount flashing, except where it's impossible to do anything else.

Read Me.rtfd: Updated for 1.1a1.

VERSION: Updated for 1.1a1.

File size: 9.0 KB
Line 
1#import "NJRFSObjectSelector.h"
2#import "NSImage-NJRExtensions.h"
3#include <Carbon/Carbon.h>
4
5@implementation NJRFSObjectSelector
6
7- (void)_initSelector;
8{
9    canChooseFiles = YES; canChooseDirectories = NO;
10    [self setAlias: nil];
11    [[self cell] setHighlightsBy: NSChangeBackgroundCell];
12    [[self cell] setGradientType: NSGradientNone];
13    [self registerForDraggedTypes:
14        [NSArray arrayWithObjects: NSFilenamesPboardType, NSURLPboardType, nil]];
15}
16
17- (void)dealloc;
18{
19    [selectedAlias release];
20    [fileTypes release];
21    [super dealloc];
22}
23
24- (id)initWithCoder:(NSCoder *)coder;
25{
26    if ( (self = [super initWithCoder: coder]) != nil) {
27        [self _initSelector];
28    }
29    return self;
30}
31
32- (id)initWithFrame:(NSRect)frame;
33{
34    if ( (self = [super initWithFrame: frame]) != nil) {
35        [self _initSelector];
36    }
37    return self;
38}
39
40- (void)drawRect:(NSRect)rect;
41{
42    NSRect boundsRect = [self bounds];
43    [super drawRect: rect];
44    if (dragAccepted) {
45        [[NSColor selectedControlColor] set];
46        [NSBezierPath setDefaultLineWidth: 2];
47        [NSBezierPath strokeRect: NSInsetRect(boundsRect, 2, 2)];
48    } else if (selectedAlias != nil && [self isEnabled]) {
49        static NSImage *popupTriangle = nil;
50        static NSSize imageSize;
51        if (popupTriangle == nil) {
52            popupTriangle = [[NSImage imageNamed: @"Popup triangle"] retain];
53            imageSize = [popupTriangle size];
54        }
55        // equivalent to popup triangle location for large bezel in Carbon
56        [popupTriangle compositeToPoint: NSMakePoint(NSMaxX(boundsRect) - imageSize.width - 5, NSMaxY(boundsRect) - 5) operation: NSCompositeSourceOver];
57    }
58}
59
60- (BOOL)acceptsPath:(NSString *)path;
61{
62    NSFileManager *fm = [NSFileManager defaultManager];
63    BOOL isDir;
64
65    if (![fm fileExistsAtPath: path isDirectory: &isDir]) return NO;
66
67    if (isDir) return canChooseDirectories;
68    if (canChooseFiles) {
69        NSEnumerator *e = [fileTypes objectEnumerator];
70        NSString *extension = [path pathExtension];
71        NSString *hfsType = NSHFSTypeOfFile(path);
72        NSString *fileType;
73       
74        while ( (fileType = [e nextObject]) != nil) {
75            if ([fileType isEqualToString: extension] || [fileType isEqualToString: hfsType])
76                return YES;
77        }
78    }
79    return NO;
80}
81
82- (void)setImage:(NSImage *)image;
83{
84    [super setImage: [image bestFitImageForSize: [[self cell] cellSize]]];
85}
86
87- (IBAction)select:(id)sender;
88{
89    NSOpenPanel *openPanel = [NSOpenPanel openPanel];
90    NSString *path = [selectedAlias fullPath];
91    [openPanel setAllowsMultipleSelection: NO];
92    [openPanel setCanChooseDirectories: canChooseDirectories];
93    [openPanel setCanChooseFiles: canChooseFiles];
94    [openPanel beginSheetForDirectory: [path stringByDeletingLastPathComponent]
95                                 file: [path lastPathComponent]
96                                types: fileTypes
97                       modalForWindow: [self window]
98                        modalDelegate: self
99                       didEndSelector: @selector(openPanelDidEnd:returnCode:contextInfo:)
100                          contextInfo: nil];
101}
102
103- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
104{
105    [sheet close];
106
107    if (returnCode == NSOKButton) {
108        NSArray *files = [sheet filenames];
109        NSAssert1([files count] == 1, @"%d items returned, only one expected", [files count]);
110        [self setPath: [files objectAtIndex: 0]];
111        [[self target] tryToPerform: [self action] with: self];
112    }
113}
114
115- (void)revealInFinder:(id<NSMenuItem>)sender;
116{
117    NSString *path = [sender representedObject];
118    if (path == nil) return;
119    [[NSWorkspace sharedWorkspace] selectFile: path inFileViewerRootedAtPath: @""];
120}
121
122- (void)setAlias:(BDAlias *)alias;
123{
124    if (selectedAlias != alias) {
125        [selectedAlias release];
126        selectedAlias = [alias retain];
127    }
128
129    if (alias != nil) { // alias is set
130        NSString *path = [alias fullPath];
131        NSString *revealPath = nil;
132        NSString *targetName = [path lastPathComponent];
133        NSMenu *menu = [[NSMenu alloc] initWithTitle: @""];
134        NSFileManager *fmgr = [NSFileManager defaultManager];
135        NSMenuItem *item;
136        if (path != nil) { // can resolve alias
137            [self setImage: [[NSWorkspace sharedWorkspace] iconForFile: path]];
138            [self setTitle: targetName];
139            do {
140                item = [menu addItemWithTitle: [fmgr displayNameAtPath: path]
141                                       action: @selector(revealInFinder:)
142                                keyEquivalent: @""];
143                [item setTarget: self];
144                [item setRepresentedObject: revealPath];
145                [item setImage:
146                    [[[NSWorkspace sharedWorkspace] iconForFile: path] bestFitImageForSize: NSMakeSize(16, 16)]];
147                revealPath = path;
148                path = [path stringByDeletingLastPathComponent];
149                NSAssert1(![path isEqualToString: revealPath], @"Stuck on path |%@|", [alias fullPath]);
150            } while (![revealPath isEqualToString: @"/"] && ![path isEqualToString: @"/Volumes"]);
151            [[self cell] setMenu: menu];
152        } else {
153            [self setImage: nil];
154            [self setTitle: @"(not available)"];
155            [[self cell] setMenu: nil];
156        }
157    } else {
158        [self setImage: nil];
159        [self setTitle: @"(none selected)"];
160        [[self cell] setMenu: nil];
161    }
162    [self setEnabled: isEnabled];
163}
164
165- (BOOL)isEnabled;
166{
167    return isEnabled;
168}
169
170- (void)setEnabled:(BOOL)enabled;
171{
172    isEnabled = enabled;
173    [super setEnabled: enabled ? selectedAlias != nil : NO];
174}
175
176- (void)rightMouseDown:(NSEvent *)theEvent;
177{
178    [self mouseDown: theEvent];
179}
180
181- (void)otherMouseDown:(NSEvent *)theEvent;
182{
183    [self mouseDown: theEvent];
184}
185
186extern MenuRef _NSGetCarbonMenu(NSMenu *menu);
187
188- (void)mouseDown:(NSEvent *)theEvent;
189{
190    NSMenu *menu = [[self cell] menu];
191    MenuRef mRef = _NSGetCarbonMenu(menu);
192
193    if (mRef == NULL) {
194        NSMenu *appMenu = [[[NSApp mainMenu] itemWithTitle: @""] submenu];
195        if (appMenu != nil) {
196            NSMenuItem *item = [appMenu addItemWithTitle: @"" action: NULL keyEquivalent: @""];
197            [appMenu setSubmenu: menu forItem: item];
198            [appMenu removeItem: item];
199        }
200        mRef = _NSGetCarbonMenu(menu);
201    }
202
203    ChangeMenuAttributes(mRef, kMenuAttrExcludesMarkColumn, 0);
204    theEvent = [NSEvent mouseEventWithType: [theEvent type]
205                                  location: [self convertPoint: NSMakePoint(-1, 1) toView: nil]
206                             modifierFlags: [theEvent modifierFlags]
207                                 timestamp: [theEvent timestamp]
208                              windowNumber: [theEvent windowNumber]
209                                   context: [theEvent context]
210                               eventNumber: [theEvent eventNumber]
211                                clickCount: [theEvent clickCount]
212                                  pressure: [theEvent pressure]];
213
214    // this undocumented API does not work any better; contextual menu items are still added:
215    // [menu _popUpMenuWithEvent: theEvent forView: self];
216    [NSMenu popUpContextMenu: menu withEvent: theEvent forView: self];
217}
218
219- (BDAlias *)alias;
220{
221    return selectedAlias;
222}
223
224- (void)setPath:(NSString *)path;
225{
226    [self setAlias: [BDAlias aliasWithPath: path]];
227}
228
229- (BOOL)canChooseDirectories;
230{
231    return canChooseDirectories;
232}
233
234- (BOOL)canChooseFiles;
235{
236    return canChooseFiles;
237}
238
239- (void)setCanChooseDirectories:(BOOL)flag;
240{
241    canChooseDirectories = flag;
242}
243
244- (void)setCanChooseFiles:(BOOL)flag;
245{
246    canChooseFiles = flag;
247}
248
249- (NSArray *)fileTypes;
250{
251    return fileTypes;
252}
253
254- (void)setFileTypes:(NSArray *)types;
255{
256    if (fileTypes == types) return;
257   
258    [fileTypes release];
259    fileTypes = [types retain];
260}
261
262@end
263
264
265@implementation NJRFSObjectSelector (NSDraggingDestination)
266
267- (BOOL)acceptsDragFrom:(id <NSDraggingInfo>)sender;
268{
269    NSURL *url = [NSURL URLFromPasteboard: [sender draggingPasteboard]];
270
271    if (url == nil || ![url isFileURL]) return NO;
272    return [self acceptsPath: [url path]];
273}
274
275- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender;
276{
277    if ([self acceptsDragFrom: sender] && [sender draggingSourceOperationMask] &
278        (NSDragOperationCopy | NSDragOperationLink)) {
279        dragAccepted = YES;
280        [self setNeedsDisplay: YES];
281        return NSDragOperationLink;
282    }
283    return NSDragOperationNone;
284}
285
286- (void)draggingExited:(id <NSDraggingInfo>)sender;
287{
288    dragAccepted = NO;
289    [self setNeedsDisplay: YES];
290}
291
292- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender;
293{
294    dragAccepted = NO;
295    [self setNeedsDisplay: YES];
296    return [self acceptsDragFrom: sender];
297}
298
299- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender;
300{
301    if ([sender draggingSource] != self) {
302        NSURL *url = [NSURL URLFromPasteboard: [sender draggingPasteboard]];
303        if (url == nil) return NO;
304        [self setPath: [url path]];
305        [[self target] tryToPerform: [self action] with: self];
306    }
307    return YES;
308}
309
310@end
Note: See TracBrowser for help on using the repository browser.