1 | // |
---|
2 | // NJRQTMediaPopUpButton.m |
---|
3 | // Pester |
---|
4 | // |
---|
5 | // Created by Nicholas Riley on Sat Oct 26 2002. |
---|
6 | // Copyright (c) 2002 Nicholas Riley. All rights reserved. |
---|
7 | // |
---|
8 | |
---|
9 | #import "NJRQTMediaPopUpButton.h" |
---|
10 | #import "SoundFileManager.h" |
---|
11 | #import "NSMovie-NJRExtensions.h" |
---|
12 | #import "NSImage-NJRExtensions.h" |
---|
13 | |
---|
14 | static const int NJRQTMediaPopUpButtonMaxRecentItems = 10; |
---|
15 | |
---|
16 | NSString * const NJRQTMediaPopUpButtonMovieChangedNotification = @"NJRQTMediaPopUpButtonMovieChangedNotification"; |
---|
17 | |
---|
18 | @interface NJRQTMediaPopUpButton (Private) |
---|
19 | - (void)_setPath:(NSString *)path; |
---|
20 | - (NSMenuItem *)_itemForAlias:(BDAlias *)alias; |
---|
21 | - (BOOL)_validateWithPreview:(BOOL)doPreview; |
---|
22 | @end |
---|
23 | |
---|
24 | @implementation NJRQTMediaPopUpButton |
---|
25 | |
---|
26 | // XXX handle refreshing sound list on resume |
---|
27 | // XXX don't add icons on Puma, they look like ass |
---|
28 | // XXX launch preview on a separate thread (if movies take too long to load, they inhibit the interface responsiveness) |
---|
29 | // XXX when dropping invalid JPEG file on button, it dies |
---|
30 | |
---|
31 | // Recent media layout: |
---|
32 | // Most recent media are at TOP of menu (smaller item numbers, starting at [self indexOfItem: otherItem] + 1) |
---|
33 | // Most recent media are at END of array (larger indices) |
---|
34 | |
---|
35 | #pragma mark recently selected media tracking |
---|
36 | |
---|
37 | - (NSString *)_defaultKey; |
---|
38 | { |
---|
39 | NSAssert([self tag] != 0, @"CanÕt track recently selected media for popup with tag 0: please set a tag"); |
---|
40 | return [NSString stringWithFormat: @"NJRQTMediaPopUpButtonMaxRecentItems tag %d", [self tag]]; |
---|
41 | } |
---|
42 | |
---|
43 | - (void)_writeRecentMedia; |
---|
44 | { |
---|
45 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; |
---|
46 | [defaults setObject: recentMediaAliasData forKey: [self _defaultKey]]; |
---|
47 | [defaults synchronize]; |
---|
48 | } |
---|
49 | |
---|
50 | - (NSMenuItem *)_addRecentMediaAtPath:(NSString *)path withAlias:(BDAlias *)alias; |
---|
51 | { |
---|
52 | NSString *title = [[NSFileManager defaultManager] displayNameAtPath: path]; |
---|
53 | NSMenu *menu = [self menu]; |
---|
54 | NSMenuItem *item = [menu insertItemWithTitle: title action: @selector(_aliasSelected:) keyEquivalent: @"" atIndex: [menu indexOfItem: otherItem] + 1]; |
---|
55 | [item setTarget: self]; |
---|
56 | [item setRepresentedObject: alias]; |
---|
57 | [item setImage: [[[NSWorkspace sharedWorkspace] iconForFile: path] bestFitImageForSize: NSMakeSize(16, 16)]]; |
---|
58 | [recentMediaAliasData addObject: [alias aliasData]]; |
---|
59 | if ([recentMediaAliasData count] > NJRQTMediaPopUpButtonMaxRecentItems) { |
---|
60 | [menu removeItemAtIndex: [menu numberOfItems] - 1]; |
---|
61 | [recentMediaAliasData removeObjectAtIndex: 0]; |
---|
62 | } |
---|
63 | return item; |
---|
64 | } |
---|
65 | |
---|
66 | - (void)_addRecentMediaFromAliasesData:(NSArray *)aliasesData; |
---|
67 | { |
---|
68 | NSEnumerator *e = [aliasesData objectEnumerator]; |
---|
69 | NSData *aliasData; |
---|
70 | BDAlias *alias; |
---|
71 | while ( (aliasData = [e nextObject]) != nil) { |
---|
72 | if ( (alias = [[BDAlias alloc] initWithData: aliasData]) != nil) { |
---|
73 | [self _addRecentMediaAtPath: [alias fullPath] withAlias: alias]; |
---|
74 | [alias release]; |
---|
75 | } |
---|
76 | } |
---|
77 | } |
---|
78 | |
---|
79 | - (void)_validateRecentMedia; |
---|
80 | { |
---|
81 | NSEnumerator *e = [recentMediaAliasData reverseObjectEnumerator]; |
---|
82 | NSData *aliasData; |
---|
83 | NSMenuItem *item; |
---|
84 | BDAlias *itemAlias; |
---|
85 | int otherIndex = [self indexOfItem: otherItem]; |
---|
86 | int aliasDataCount = [recentMediaAliasData count]; |
---|
87 | int lastItemIndex = [self numberOfItems] - 1; |
---|
88 | int recentItemCount = lastItemIndex - otherIndex; |
---|
89 | int recentItemIndex = otherIndex; |
---|
90 | NSAssert2(recentItemCount == aliasDataCount, @"Counted %d recent menu items, %d of alias data", recentItemCount, aliasDataCount); |
---|
91 | while ( (aliasData = [e nextObject]) != nil) { // go BACKWARD through array while going DOWN menu |
---|
92 | recentItemIndex++; |
---|
93 | item = [self itemAtIndex: recentItemIndex]; |
---|
94 | itemAlias = [item representedObject]; |
---|
95 | if ([itemAlias aliasDataIsEqual: aliasData]) |
---|
96 | NSLog(@"item %d %@: %@", recentItemIndex, [item title], [itemAlias fullPath]); |
---|
97 | else |
---|
98 | NSLog(@"ITEM %d %@: %@ != aliasData %@", recentItemIndex, [item title], [itemAlias fullPath], [[BDAlias aliasWithData: aliasData] fullPath]); |
---|
99 | } |
---|
100 | } |
---|
101 | |
---|
102 | #pragma mark initialize-release |
---|
103 | |
---|
104 | - (void)awakeFromNib; |
---|
105 | { |
---|
106 | NSMenu *menu; |
---|
107 | NSMenuItem *item; |
---|
108 | SoundFileManager *sfm = [SoundFileManager sharedSoundFileManager]; |
---|
109 | int soundCount = [sfm count]; |
---|
110 | |
---|
111 | [self removeAllItems]; |
---|
112 | menu = [self menu]; |
---|
113 | item = [menu addItemWithTitle: @"Alert sound" action: @selector(_beepSelected:) keyEquivalent: @""]; |
---|
114 | [item setTarget: self]; |
---|
115 | [menu addItem: [NSMenuItem separatorItem]]; |
---|
116 | if (soundCount == 0) { |
---|
117 | item = [menu addItemWithTitle: @"CanÕt locate alert sounds" action: nil keyEquivalent: @""]; |
---|
118 | [item setEnabled: NO]; |
---|
119 | } else { |
---|
120 | SoundFile *sf; |
---|
121 | int i; |
---|
122 | [sfm sortByName]; |
---|
123 | for (i = 0 ; i < soundCount ; i++) { |
---|
124 | sf = [sfm soundFileAtIndex: i]; |
---|
125 | item = [menu addItemWithTitle: [sf name] action: @selector(_soundFileSelected:) keyEquivalent: @""]; |
---|
126 | [item setTarget: self]; |
---|
127 | [item setRepresentedObject: sf]; |
---|
128 | [item setImage: [[[NSWorkspace sharedWorkspace] iconForFile: [sf path]] bestFitImageForSize: NSMakeSize(16, 16)]]; |
---|
129 | } |
---|
130 | } |
---|
131 | [menu addItem: [NSMenuItem separatorItem]]; |
---|
132 | item = [menu addItemWithTitle: @"OtherÉ" action: @selector(select:) keyEquivalent: @""]; |
---|
133 | [item setTarget: self]; |
---|
134 | otherItem = [item retain]; |
---|
135 | |
---|
136 | [self _validateWithPreview: NO]; |
---|
137 | |
---|
138 | recentMediaAliasData = [[NSMutableArray alloc] initWithCapacity: NJRQTMediaPopUpButtonMaxRecentItems + 1]; |
---|
139 | [self _addRecentMediaFromAliasesData: [[NSUserDefaults standardUserDefaults] arrayForKey: [self _defaultKey]]]; |
---|
140 | // [self _validateRecentMedia]; |
---|
141 | |
---|
142 | [self registerForDraggedTypes: |
---|
143 | [NSArray arrayWithObjects: NSFilenamesPboardType, NSURLPboardType, nil]]; |
---|
144 | } |
---|
145 | |
---|
146 | - (void)dealloc; |
---|
147 | { |
---|
148 | [recentMediaAliasData release]; recentMediaAliasData = nil; |
---|
149 | [otherItem release]; |
---|
150 | [selectedAlias release]; [previousAlias release]; |
---|
151 | [super dealloc]; |
---|
152 | } |
---|
153 | |
---|
154 | #pragma mark accessing |
---|
155 | |
---|
156 | - (BDAlias *)selectedAlias; |
---|
157 | { |
---|
158 | return selectedAlias; |
---|
159 | } |
---|
160 | |
---|
161 | - (void)_setAlias:(BDAlias *)alias; |
---|
162 | { |
---|
163 | BDAlias *oldAlias = [selectedAlias retain]; |
---|
164 | [previousAlias release]; |
---|
165 | previousAlias = oldAlias; |
---|
166 | if (selectedAlias != alias) { |
---|
167 | [selectedAlias release]; |
---|
168 | selectedAlias = [alias retain]; |
---|
169 | } |
---|
170 | } |
---|
171 | |
---|
172 | - (void)_setPath:(NSString *)path; |
---|
173 | { |
---|
174 | [self _setAlias: [BDAlias aliasWithPath: path]]; |
---|
175 | } |
---|
176 | |
---|
177 | - (NSMenuItem *)_itemForAlias:(BDAlias *)alias; |
---|
178 | { |
---|
179 | NSString *path; |
---|
180 | SoundFile *sf; |
---|
181 | if (alias == nil) { |
---|
182 | return [self itemAtIndex: 0]; |
---|
183 | } |
---|
184 | |
---|
185 | // [self _validateRecentMedia]; |
---|
186 | path = [alias fullPath]; |
---|
187 | sf = [[SoundFileManager sharedSoundFileManager] soundFileFromPath: path]; |
---|
188 | // NSLog(@"_itemForAlias: %@", path); |
---|
189 | |
---|
190 | // selected a system sound? |
---|
191 | if (sf != nil) { |
---|
192 | // NSLog(@"_itemForAlias: selected system sound"); |
---|
193 | return [self itemAtIndex: [self indexOfItemWithRepresentedObject: sf]]; |
---|
194 | } else { |
---|
195 | NSEnumerator *e = [recentMediaAliasData reverseObjectEnumerator]; |
---|
196 | NSData *aliasData; |
---|
197 | NSMenuItem *item; |
---|
198 | int recentIndex = 1; |
---|
199 | |
---|
200 | while ( (aliasData = [e nextObject]) != nil) { |
---|
201 | // selected a recently selected, non-system sound? |
---|
202 | if ([alias aliasDataIsEqual: aliasData]) { |
---|
203 | int otherIndex = [self indexOfItem: otherItem]; |
---|
204 | int menuIndex = recentIndex + otherIndex; |
---|
205 | if (menuIndex == otherIndex + 1) return [self itemAtIndex: menuIndex]; // already at top |
---|
206 | // remove item, add (at top) later |
---|
207 | // NSLog(@"_itemForAlias removing item: idx %d + otherItemIdx %d + 1 = %d [%@]", recentIndex, otherIndex, menuIndex, [self itemAtIndex: menuIndex]); |
---|
208 | [self removeItemAtIndex: menuIndex]; |
---|
209 | [recentMediaAliasData removeObjectAtIndex: [recentMediaAliasData count] - recentIndex]; |
---|
210 | break; |
---|
211 | } |
---|
212 | recentIndex++; |
---|
213 | } |
---|
214 | |
---|
215 | // create the item |
---|
216 | item = [self _addRecentMediaAtPath: path withAlias: alias]; |
---|
217 | [self _writeRecentMedia]; |
---|
218 | return item; |
---|
219 | } |
---|
220 | } |
---|
221 | |
---|
222 | - (BOOL)canRepeat; |
---|
223 | { |
---|
224 | return movieCanRepeat; |
---|
225 | } |
---|
226 | |
---|
227 | #pragma mark selected media validation |
---|
228 | |
---|
229 | - (void)_invalidateSelection; |
---|
230 | { |
---|
231 | [self _setAlias: previousAlias]; |
---|
232 | [self selectItem: [self _itemForAlias: [self selectedAlias]]]; |
---|
233 | [[NSNotificationCenter defaultCenter] postNotificationName: NJRQTMediaPopUpButtonMovieChangedNotification object: self]; |
---|
234 | } |
---|
235 | |
---|
236 | - (BOOL)_validateWithPreview:(BOOL)doPreview; |
---|
237 | { |
---|
238 | [preview stop: self]; |
---|
239 | if (selectedAlias == nil) { |
---|
240 | [preview setMovie: nil]; |
---|
241 | movieCanRepeat = YES; |
---|
242 | if (doPreview) NSBeep(); |
---|
243 | } else { |
---|
244 | NSMovie *movie = [[NSMovie alloc] initWithURL: [NSURL fileURLWithPath: [selectedAlias fullPath]] byReference: YES]; |
---|
245 | movieCanRepeat = ![movie isStatic]; |
---|
246 | if ([movie hasAudio]) |
---|
247 | [preview setMovie: movie]; |
---|
248 | else { |
---|
249 | [preview setMovie: nil]; |
---|
250 | if (movie == nil) { |
---|
251 | NSBeginAlertSheet(@"Format not recognized", @"OK", nil, nil, [self window], nil, nil, nil, nil, @"The item you selected isnÕt a sound or movie recognized by QuickTime. Please select a different item."); |
---|
252 | [self _invalidateSelection]; |
---|
253 | return NO; |
---|
254 | } |
---|
255 | if (![movie hasAudio] && ![movie hasVideo]) { |
---|
256 | NSBeginAlertSheet(@"No video or audio", @"OK", nil, nil, [self window], nil, nil, nil, nil, @"Ò%@Ó contains neither audio nor video content playable by QuickTime. Please select a different item.", [[NSFileManager defaultManager] displayNameAtPath: [selectedAlias fullPath]]); |
---|
257 | [self _invalidateSelection]; |
---|
258 | [movie release]; |
---|
259 | return NO; |
---|
260 | } |
---|
261 | } |
---|
262 | [movie release]; |
---|
263 | [preview start: self]; |
---|
264 | } |
---|
265 | [[NSNotificationCenter defaultCenter] postNotificationName: NJRQTMediaPopUpButtonMovieChangedNotification object: self]; |
---|
266 | return YES; |
---|
267 | } |
---|
268 | |
---|
269 | #pragma mark actions |
---|
270 | |
---|
271 | - (IBAction)stopSoundPreview:(id)sender; |
---|
272 | { |
---|
273 | [preview stop: self]; |
---|
274 | } |
---|
275 | |
---|
276 | - (void)_beepSelected:(NSMenuItem *)sender; |
---|
277 | { |
---|
278 | [self _setAlias: nil]; |
---|
279 | [self _validateWithPreview: YES]; |
---|
280 | } |
---|
281 | |
---|
282 | - (void)_soundFileSelected:(NSMenuItem *)sender; |
---|
283 | { |
---|
284 | [self _setPath: [(SoundFile *)[sender representedObject] path]]; |
---|
285 | if (![self _validateWithPreview: YES]) { |
---|
286 | [[self menu] removeItem: sender]; |
---|
287 | } |
---|
288 | } |
---|
289 | |
---|
290 | - (void)_aliasSelected:(NSMenuItem *)sender; |
---|
291 | { |
---|
292 | BDAlias *alias = [sender representedObject]; |
---|
293 | int index = [self indexOfItem: sender], otherIndex = [self indexOfItem: otherItem]; |
---|
294 | [self _setAlias: alias]; |
---|
295 | if (![self _validateWithPreview: YES]) { |
---|
296 | [[self menu] removeItem: sender]; |
---|
297 | } else if (index > otherIndex + 1) { // move "other" item to top of list |
---|
298 | int recentIndex = [recentMediaAliasData count] - index + otherIndex; |
---|
299 | NSMenuItem *item = [[self itemAtIndex: index] retain]; |
---|
300 | NSData *data = [[recentMediaAliasData objectAtIndex: recentIndex] retain]; |
---|
301 | [self _validateRecentMedia]; |
---|
302 | [self removeItemAtIndex: index]; |
---|
303 | [[self menu] insertItem: item atIndex: otherIndex + 1]; |
---|
304 | [self selectItem: item]; |
---|
305 | [item release]; |
---|
306 | NSAssert(recentIndex >= 0, @"Recent media index invalid"); |
---|
307 | // NSLog(@"_aliasSelected removing item %d - %d + %d = %d of recentMediaAliasData", [recentMediaAliasData count], index, otherIndex, recentIndex); |
---|
308 | [recentMediaAliasData removeObjectAtIndex: recentIndex]; |
---|
309 | [recentMediaAliasData addObject: data]; |
---|
310 | [self _validateRecentMedia]; |
---|
311 | [data release]; |
---|
312 | } // else NSLog(@"_aliasSelected ...already at top"); |
---|
313 | } |
---|
314 | |
---|
315 | - (IBAction)select:(id)sender; |
---|
316 | { |
---|
317 | NSOpenPanel *openPanel = [NSOpenPanel openPanel]; |
---|
318 | NSString *path = [selectedAlias fullPath]; |
---|
319 | [openPanel setAllowsMultipleSelection: NO]; |
---|
320 | [openPanel setCanChooseDirectories: NO]; |
---|
321 | [openPanel setCanChooseFiles: YES]; |
---|
322 | [openPanel beginSheetForDirectory: [path stringByDeletingLastPathComponent] |
---|
323 | file: [path lastPathComponent] |
---|
324 | types: nil // XXX fix for QuickTime! |
---|
325 | modalForWindow: [self window] |
---|
326 | modalDelegate: self |
---|
327 | didEndSelector: @selector(openPanelDidEnd:returnCode:contextInfo:) |
---|
328 | contextInfo: nil]; |
---|
329 | } |
---|
330 | |
---|
331 | - (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo; |
---|
332 | { |
---|
333 | [sheet close]; |
---|
334 | |
---|
335 | if (returnCode == NSOKButton) { |
---|
336 | NSArray *files = [sheet filenames]; |
---|
337 | NSAssert1([files count] == 1, @"%d items returned, only one expected", [files count]); |
---|
338 | [self _setPath: [files objectAtIndex: 0]]; |
---|
339 | if ([self _validateWithPreview: YES]) { |
---|
340 | [self selectItem: [self _itemForAlias: selectedAlias]]; |
---|
341 | } |
---|
342 | } else { |
---|
343 | // "Other..." item is still selected, revert to previously selected item |
---|
344 | // XXX issue with cancelling, top item in recent menu is sometimes duplicated!? |
---|
345 | [self selectItem: [self _itemForAlias: selectedAlias]]; |
---|
346 | } |
---|
347 | // [self _validateRecentMedia]; |
---|
348 | } |
---|
349 | |
---|
350 | #pragma mark drag feedback |
---|
351 | |
---|
352 | - (void)drawRect:(NSRect)rect; |
---|
353 | { |
---|
354 | if (dragAccepted) { |
---|
355 | NSWindow *window = [self window]; |
---|
356 | NSRect boundsRect = [self bounds]; |
---|
357 | BOOL isFirstResponder = ([window firstResponder] == self); |
---|
358 | // focus ring and drag feedback interfere with one another |
---|
359 | if (isFirstResponder) [window makeFirstResponder: window]; |
---|
360 | [super drawRect: rect]; |
---|
361 | [[NSColor selectedControlColor] set]; |
---|
362 | NSFrameRectWithWidthUsingOperation(NSInsetRect(boundsRect, 2, 2), 3, NSCompositeSourceIn); |
---|
363 | if (isFirstResponder) [window makeFirstResponder: self]; |
---|
364 | } else { |
---|
365 | [super drawRect: rect]; |
---|
366 | } |
---|
367 | } |
---|
368 | |
---|
369 | @end |
---|
370 | |
---|
371 | @implementation NJRQTMediaPopUpButton (NSDraggingDestination) |
---|
372 | |
---|
373 | - (BOOL)acceptsDragFrom:(id <NSDraggingInfo>)sender; |
---|
374 | { |
---|
375 | NSURL *url = [NSURL URLFromPasteboard: [sender draggingPasteboard]]; |
---|
376 | NSFileManager *fm = [NSFileManager defaultManager]; |
---|
377 | BOOL isDir; |
---|
378 | |
---|
379 | if (url == nil || ![url isFileURL]) return NO; |
---|
380 | |
---|
381 | if (![fm fileExistsAtPath: [url path] isDirectory: &isDir]) return NO; |
---|
382 | |
---|
383 | if (isDir) return NO; |
---|
384 | |
---|
385 | return YES; |
---|
386 | } |
---|
387 | |
---|
388 | - (NSString *)_descriptionForDraggingInfo:(id <NSDraggingInfo>)sender; |
---|
389 | { |
---|
390 | NSDragOperation mask = [sender draggingSourceOperationMask]; |
---|
391 | NSMutableString *s = [NSMutableString stringWithFormat: @"Drag seq %d source: %@", |
---|
392 | [sender draggingSequenceNumber], [sender draggingSource]]; |
---|
393 | NSPasteboard *draggingPasteboard = [sender draggingPasteboard]; |
---|
394 | NSArray *types = [draggingPasteboard types]; |
---|
395 | NSEnumerator *e = [types objectEnumerator]; |
---|
396 | NSString *type; |
---|
397 | [s appendString: @"\nDrag operations:"]; |
---|
398 | if (mask & NSDragOperationCopy) [s appendString: @" copy"]; |
---|
399 | if (mask & NSDragOperationLink) [s appendString: @" link"]; |
---|
400 | if (mask & NSDragOperationGeneric) [s appendString: @" generic"]; |
---|
401 | if (mask & NSDragOperationPrivate) [s appendString: @" private"]; |
---|
402 | if (mask & NSDragOperationMove) [s appendString: @" move"]; |
---|
403 | if (mask & NSDragOperationDelete) [s appendString: @" delete"]; |
---|
404 | if (mask & NSDragOperationEvery) [s appendString: @" every"]; |
---|
405 | if (mask & NSDragOperationNone) [s appendString: @" none"]; |
---|
406 | [s appendFormat: @"\nImage: %@ at %@", [sender draggedImage], |
---|
407 | NSStringFromPoint([sender draggedImageLocation])]; |
---|
408 | [s appendFormat: @"\nDestination: %@ at %@", [sender draggingDestinationWindow], |
---|
409 | NSStringFromPoint([sender draggingLocation])]; |
---|
410 | [s appendFormat: @"\nPasteboard: %@ types:", draggingPasteboard]; |
---|
411 | while ( (type = [e nextObject]) != nil) { |
---|
412 | if ([type hasPrefix: @"CorePasteboardFlavorType 0x"]) { |
---|
413 | const char *osTypeHex = [[type substringFromIndex: [type rangeOfString: @"0x" options: NSBackwardsSearch].location] lossyCString]; |
---|
414 | OSType osType; |
---|
415 | sscanf(osTypeHex, "%lx", &osType); |
---|
416 | [s appendFormat: @" '%4s'", &osType]; |
---|
417 | } else { |
---|
418 | [s appendFormat: @" \"%@\"", type]; |
---|
419 | } |
---|
420 | } |
---|
421 | return s; |
---|
422 | } |
---|
423 | |
---|
424 | - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender; |
---|
425 | { |
---|
426 | if ([self acceptsDragFrom: sender] && [sender draggingSourceOperationMask] & |
---|
427 | (NSDragOperationCopy | NSDragOperationLink)) { |
---|
428 | dragAccepted = YES; |
---|
429 | [self setNeedsDisplay: YES]; |
---|
430 | // NSLog(@"draggingEntered accept:\n%@", [self _descriptionForDraggingInfo: sender]); |
---|
431 | return NSDragOperationLink; |
---|
432 | } |
---|
433 | return NSDragOperationNone; |
---|
434 | } |
---|
435 | |
---|
436 | - (void)draggingExited:(id <NSDraggingInfo>)sender; |
---|
437 | { |
---|
438 | dragAccepted = NO; |
---|
439 | [self setNeedsDisplay: YES]; |
---|
440 | } |
---|
441 | |
---|
442 | - (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender; |
---|
443 | { |
---|
444 | dragAccepted = NO; |
---|
445 | [self setNeedsDisplay: YES]; |
---|
446 | return [self acceptsDragFrom: sender]; |
---|
447 | } |
---|
448 | |
---|
449 | - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender; |
---|
450 | { |
---|
451 | if ([sender draggingSource] != self) { |
---|
452 | NSURL *url = [NSURL URLFromPasteboard: [sender draggingPasteboard]]; |
---|
453 | if (url == nil) return NO; |
---|
454 | [self _setPath: [url path]]; |
---|
455 | if ([self _validateWithPreview: YES]) { |
---|
456 | [self selectItem: [self _itemForAlias: selectedAlias]]; |
---|
457 | } |
---|
458 | } |
---|
459 | return YES; |
---|
460 | } |
---|
461 | |
---|
462 | @end |
---|