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

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

Popup triangle.tiff: Needed display component of NJRFSObjectSelector.
This one appears to use transparency unlike the one I prepared for
Process Exhibits, so it should be preferred.

NSMovie-NJRExtensions: Added -isStatic to identify whether the movie
contains dynamic components. If the movie is static, it most likely
contains only an image and shouldn't be 'played' as such, otherwise
the duration will be so short that the image won't be useful.

NJRFSObjectSelector: Fixed -drawRect: to draw the drag feedback
rectangle inside the bounds of the control, not inside whatever dirty
rectangle is passed to the method (often they are the same, but not
always). Only draw the popup arrow if the control is enabled.
Properly draw the popup arrow with transparency. Display the 'make
alias' cursor as additional drag feedback.

PSMovieAlertController: Only repeat movie and auto-close after movie
finished if it contains time-based media, otherwise just display the
movie (an image) until the window is closed or alarms are cancelled.

NSImage-NJRExtensions: Include code to actually scale icons from
F-Script Anywhere, otherwise the menu ends up with 32x32 (or
potentially larger) icons if smaller variations are not provided.
There's some more code in FSA that chooses which representation to
select; this code may still not be properly handling representations,
but it works better now. A good test case is the icon for Tex-Edit
Plus documents.

NJRQTMediaPopUpButton: Added notification for movie change (needed to
update interface). Changed -_validatePreview to
-_validateWithPreview: - preview is now optional. This will be needed
when I add archiving support for the selected item; we'll need to
validate it before updating the interface, but we don't want sounds to
play. Added some #pragma mark lines to separate methods by
functionality. Call -validateWithPreview: NO in awakeFromNib (again,
this logic will become more sophisticated later). Commented some
debugging logic since I'm pretty happy with the code. Added
-canRepeat accessor and setters, notification support in the
validation method. Added drag feedback.

PSAlarmSetController: Notification support, -setSoundRepetitionCount:
to avoid flashing with repetition text field control as NSStepper is
adjusted

English.lproj/MainMenu.nib: Switched action method for the NSStepper
stuff discussed above.

AM /Users/nicholas/Documents/Development/Cocoa/Pester/Source/Popup triangle.tiff
M /Users/nicholas/Documents/Development/Cocoa/Pester/Source/NSMovie-NJRExtensions.m
M /Users/nicholas/Documents/Development/Cocoa/Pester/Source/NJRFSObjectSelector.m
M /Users/nicholas/Documents/Development/Cocoa/Pester/Source/PSMovieAlertController.m
M /Users/nicholas/Documents/Development/Cocoa/Pester/Source/NSImage-NJRExtensions.m
M /Users/nicholas/Documents/Development/Cocoa/Pester/Source/NJRQTMediaPopUpButton.h
M /Users/nicholas/Documents/Development/Cocoa/Pester/Source/NJRQTMediaPopUpButton.m
M /Users/nicholas/Documents/Development/Cocoa/Pester/Source/.DS_Store
M /Users/nicholas/Documents/Development/Cocoa/Pester/Source/English.lproj/MainMenu.nib/objects.nib
M /Users/nicholas/Documents/Development/Cocoa/Pester/Source/English.lproj/MainMenu.nib/info.nib
M /Users/nicholas/Documents/Development/Cocoa/Pester/Source/English.lproj/MainMenu.nib/classes.nib
M /Users/nicholas/Documents/Development/Cocoa/Pester/Source/Pester.pbproj/nicholas.pbxuser
M /Users/nicholas/Documents/Development/Cocoa/Pester/Source/Pester.pbproj/project.pbxproj
M /Users/nicholas/Documents/Development/Cocoa/Pester/Source/PSAlarmSetController.h
M /Users/nicholas/Documents/Development/Cocoa/Pester/Source/PSAlarmSetController.m
M /Users/nicholas/Documents/Development/Cocoa/Pester/Source/NSMovie-NJRExtensions.h

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