source: trunk/Cocoa/Pester/Source/NJRQTMediaPopUpButton.m@ 604

Last change on this file since 604 was 602, checked in by Nicholas Riley, 15 years ago

Rename variables which shadow stdlib functions (and maxSize).

File size: 21.4 KB
RevLine 
[34]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"
[600]10#import "NJRFSObjectSelector.h"
[133]11#import "NJRSoundManager.h"
[541]12#import "QTMovie-NJRExtensions.h"
[355]13#import "NSMenuItem-NJRExtensions.h"
[34]14
[355]15#include <limits.h>
16
[39]17static const int NJRQTMediaPopUpButtonMaxRecentItems = 10;
18
[41]19NSString * const NJRQTMediaPopUpButtonMovieChangedNotification = @"NJRQTMediaPopUpButtonMovieChangedNotification";
20
[34]21@interface NJRQTMediaPopUpButton (Private)
[39]22- (void)_setPath:(NSString *)path;
[40]23- (NSMenuItem *)_itemForAlias:(BDAlias *)alias;
[41]24- (BOOL)_validateWithPreview:(BOOL)doPreview;
[133]25- (void)_startSoundPreview;
[600]26- (void)_soundPreviewDidEnd:(NSNotification *)notification;
[533]27- (void)_resetPreview;
28- (void)_resetOutputVolume;
[600]29- (void)_aliasSelected:(NSMenuItem *)sender;
30- (void)_beepSelected:(NSMenuItem *)sender;
31- (void)_systemSoundSelected:(NSMenuItem *)sender;
[34]32@end
33
[600]34@interface NJRQTMediaPopUpButton (NSOpenPanelRuntime)
35- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
36@end
37
[34]38@implementation NJRQTMediaPopUpButton
39
40// XXX handle refreshing sound list on resume
[39]41// XXX launch preview on a separate thread (if movies take too long to load, they inhibit the interface responsiveness)
[34]42
[40]43// Recent media layout:
44// Most recent media are at TOP of menu (smaller item numbers, starting at [self indexOfItem: otherItem] + 1)
45// Most recent media are at END of array (larger indices)
46
[41]47#pragma mark recently selected media tracking
48
[39]49- (NSString *)_defaultKey;
50{
[103]51 NSAssert([self tag] != 0, NSLocalizedString(@"Can't track recently selected media for popup with tag 0: please set a tag", "Assertion for QuickTime media popup button if tag is 0"));
[39]52 return [NSString stringWithFormat: @"NJRQTMediaPopUpButtonMaxRecentItems tag %d", [self tag]];
53}
54
55- (void)_writeRecentMedia;
56{
57 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
58 [defaults setObject: recentMediaAliasData forKey: [self _defaultKey]];
59 [defaults synchronize];
60}
61
62- (NSMenuItem *)_addRecentMediaAtPath:(NSString *)path withAlias:(BDAlias *)alias;
63{
64 NSString *title = [[NSFileManager defaultManager] displayNameAtPath: path];
65 NSMenu *menu = [self menu];
[47]66 NSMenuItem *item;
67 if (title == nil || path == nil) return nil;
68 item = [menu insertItemWithTitle: title action: @selector(_aliasSelected:) keyEquivalent: @"" atIndex: [menu indexOfItem: otherItem] + 1];
[39]69 [item setTarget: self];
70 [item setRepresentedObject: alias];
[355]71 [item setImageFromPath: path];
[39]72 [recentMediaAliasData addObject: [alias aliasData]];
73 if ([recentMediaAliasData count] > NJRQTMediaPopUpButtonMaxRecentItems) {
74 [menu removeItemAtIndex: [menu numberOfItems] - 1];
75 [recentMediaAliasData removeObjectAtIndex: 0];
76 }
77 return item;
78}
79
80- (void)_addRecentMediaFromAliasesData:(NSArray *)aliasesData;
81{
82 NSEnumerator *e = [aliasesData objectEnumerator];
83 NSData *aliasData;
84 BDAlias *alias;
85 while ( (aliasData = [e nextObject]) != nil) {
86 if ( (alias = [[BDAlias alloc] initWithData: aliasData]) != nil) {
87 [self _addRecentMediaAtPath: [alias fullPath] withAlias: alias];
88 [alias release];
89 }
90 }
91}
92
93- (void)_validateRecentMedia;
94{
[40]95 NSEnumerator *e = [recentMediaAliasData reverseObjectEnumerator];
[39]96 NSData *aliasData;
97 NSMenuItem *item;
98 BDAlias *itemAlias;
99 int otherIndex = [self indexOfItem: otherItem];
100 int aliasDataCount = [recentMediaAliasData count];
101 int lastItemIndex = [self numberOfItems] - 1;
102 int recentItemCount = lastItemIndex - otherIndex;
103 int recentItemIndex = otherIndex;
104 NSAssert2(recentItemCount == aliasDataCount, @"Counted %d recent menu items, %d of alias data", recentItemCount, aliasDataCount);
[40]105 while ( (aliasData = [e nextObject]) != nil) { // go BACKWARD through array while going DOWN menu
[39]106 recentItemIndex++;
107 item = [self itemAtIndex: recentItemIndex];
108 itemAlias = [item representedObject];
109 }
110}
111
[41]112#pragma mark initialize-release
113
[53]114- (void)_setUp;
[34]115{
[355]116 NSMenu *menu = [self menu];
117 [self removeAllItems];
118 [menu setAutoenablesItems: NO];
[39]119
[355]120 NSMenuItem *item = [menu addItemWithTitle: @"Alert sound" action: @selector(_beepSelected:) keyEquivalent: @""];
[34]121 [item setTarget: self];
122 [menu addItem: [NSMenuItem separatorItem]];
[355]123
124 NSMutableArray *soundFolderPaths = [[NSMutableArray alloc] initWithCapacity: kLastDomainConstant - kSystemDomain + 1];
125 for (FSVolumeRefNum domain = kSystemDomain ; domain <= kLastDomainConstant ; domain++) {
126 OSStatus err;
127 FSRef fsr;
128 err = FSFindFolder(domain, kSystemSoundsFolderType, false, &fsr);
129 if (err != noErr) continue;
130
131 UInt8 path[PATH_MAX];
132 err = FSRefMakePath(&fsr, path, PATH_MAX);
133 if (err != noErr) continue;
134
135 CFStringRef pathString = CFStringCreateWithFileSystemRepresentation(NULL, (const char *)path);
136 if (pathString == NULL) continue;
137
138 [soundFolderPaths addObject: (NSString *)pathString];
139 CFRelease(pathString);
140 }
141 NSFileManager *fm = [NSFileManager defaultManager];
142 NSEnumerator *e = [soundFolderPaths objectEnumerator];
143 NSString *folderPath;
144 while ( (folderPath = [e nextObject]) != nil) {
145 if (![fm changeCurrentDirectoryPath: folderPath]) continue;
146
147 NSDirectoryEnumerator *de = [fm enumeratorAtPath: folderPath];
148 NSString *path;
[576]149 NSString *displayName;
[355]150 while ( (path = [de nextObject]) != nil) {
151 BOOL isDir;
152 if (![fm fileExistsAtPath: path isDirectory: &isDir] || isDir) {
153 [de skipDescendents];
154 continue;
155 }
156
157 if (![QTMovie canInitWithFile: path]) continue;
158
[576]159 displayName = [fm displayNameAtPath: path];
160 if ([[NSNumber numberWithBool: NO] isEqualTo: [[de fileAttributes] objectForKey: NSFileExtensionHidden]])
161 displayName = [displayName stringByDeletingPathExtension];
162
163 item = [menu addItemWithTitle: displayName
[355]164 action: @selector(_systemSoundSelected:)
165 keyEquivalent: @""];
[34]166 [item setTarget: self];
[355]167 [item setImageFromPath: path];
168 path = [folderPath stringByAppendingPathComponent: path];
169 [item setRepresentedObject: path];
170 [item setToolTip: path];
[34]171 }
172 }
[355]173 [soundFolderPaths release];
174
175 if ([menu numberOfItems] == 2) {
176 item = [menu addItemWithTitle: NSLocalizedString(@"Can't locate alert sounds", "QuickTime media popup menu item surrogate for alert sound list if no sounds are found") action: nil keyEquivalent: @""];
177 [item setEnabled: NO];
178 }
179
[34]180 [menu addItem: [NSMenuItem separatorItem]];
[103]181 item = [menu addItemWithTitle: NSLocalizedString(@"Other...", "Media popup item to select another sound/movie/image") action: @selector(select:) keyEquivalent: @""];
[34]182 [item setTarget: self];
[39]183 otherItem = [item retain];
184
[41]185 [self _validateWithPreview: NO];
186
[39]187 recentMediaAliasData = [[NSMutableArray alloc] initWithCapacity: NJRQTMediaPopUpButtonMaxRecentItems + 1];
188 [self _addRecentMediaFromAliasesData: [[NSUserDefaults standardUserDefaults] arrayForKey: [self _defaultKey]]];
[41]189 // [self _validateRecentMedia];
[39]190
191 [self registerForDraggedTypes:
192 [NSArray arrayWithObjects: NSFilenamesPboardType, NSURLPboardType, nil]];
[34]193}
194
[53]195- (id)initWithFrame:(NSRect)frame;
196{
197 if ( (self = [super initWithFrame: frame]) != nil) {
198 [self _setUp];
199 }
200 return self;
201}
202
203- (id)initWithCoder:(NSCoder *)coder;
204{
205 if ( (self = [super initWithCoder: coder]) != nil) {
206 [self _setUp];
207 }
208 return self;
209}
210
[39]211- (void)dealloc;
[34]212{
[39]213 [recentMediaAliasData release]; recentMediaAliasData = nil;
214 [otherItem release];
215 [selectedAlias release]; [previousAlias release];
216 [super dealloc];
[34]217}
218
[41]219#pragma mark accessing
220
[39]221- (BDAlias *)selectedAlias;
[34]222{
[39]223 return selectedAlias;
224}
225
226- (void)_setAlias:(BDAlias *)alias;
227{
228 BDAlias *oldAlias = [selectedAlias retain];
229 [previousAlias release];
230 previousAlias = oldAlias;
[34]231 if (selectedAlias != alias) {
232 [selectedAlias release];
233 selectedAlias = [alias retain];
234 }
235}
236
[53]237- (void)setAlias:(BDAlias *)alias;
238{
239 [self _setAlias: alias];
240 if ([self _validateWithPreview: NO]) {
241 [self selectItem: [self _itemForAlias: selectedAlias]];
242 }
243}
244
[39]245- (void)_setPath:(NSString *)path;
[34]246{
[39]247 [self _setAlias: [BDAlias aliasWithPath: path]];
[34]248}
249
[39]250- (NSMenuItem *)_itemForAlias:(BDAlias *)alias;
[34]251{
[355]252 if (alias == nil) return [self itemAtIndex: 0];
[34]253
[41]254 // [self _validateRecentMedia];
[355]255 NSString *path = [alias fullPath];
[34]256
[39]257 // selected a system sound?
[355]258 int itemIndex = [[self menu] indexOfItemWithRepresentedObject: path];
259 if (itemIndex != -1) {
[41]260 // NSLog(@"_itemForAlias: selected system sound");
[355]261 return [self itemAtIndex: itemIndex];
[39]262 } else {
[40]263 NSEnumerator *e = [recentMediaAliasData reverseObjectEnumerator];
[39]264 NSData *aliasData;
265 NSMenuItem *item;
[40]266 int recentIndex = 1;
[34]267
[39]268 while ( (aliasData = [e nextObject]) != nil) {
269 // selected a recently selected, non-system sound?
270 if ([alias aliasDataIsEqual: aliasData]) {
271 int otherIndex = [self indexOfItem: otherItem];
[40]272 int menuIndex = recentIndex + otherIndex;
[39]273 if (menuIndex == otherIndex + 1) return [self itemAtIndex: menuIndex]; // already at top
274 // remove item, add (at top) later
[41]275 // NSLog(@"_itemForAlias removing item: idx %d + otherItemIdx %d + 1 = %d [%@]", recentIndex, otherIndex, menuIndex, [self itemAtIndex: menuIndex]);
[39]276 [self removeItemAtIndex: menuIndex];
[40]277 [recentMediaAliasData removeObjectAtIndex: [recentMediaAliasData count] - recentIndex];
[39]278 break;
[34]279 }
[39]280 recentIndex++;
[34]281 }
[39]282
283 // create the item
284 item = [self _addRecentMediaAtPath: path withAlias: alias];
285 [self _writeRecentMedia];
286 return item;
[34]287 }
288}
289
[41]290- (BOOL)canRepeat;
291{
292 return movieCanRepeat;
293}
294
[133]295- (BOOL)hasAudio;
296{
297 return movieHasAudio;
298}
299
300- (float)outputVolume;
301{
302 return outputVolume;
303}
304
305- (void)setOutputVolume:(float)volume withPreview:(BOOL)doPreview;
306{
307 if (![NJRSoundManager volumeIsNotMutedOrInvalid: volume]) return;
308 outputVolume = volume;
309 if (!doPreview) return;
310 // NSLog(@"setting volume to %f, preview movie %@", volume, [preview movie]);
311 if ([preview movie] == nil) {
312 [self _validateWithPreview: YES];
[533]313 } else {
314 [self _startSoundPreview];
[133]315 }
316}
317
[41]318#pragma mark selected media validation
319
[39]320- (void)_invalidateSelection;
[34]321{
[39]322 [self _setAlias: previousAlias];
323 [self selectItem: [self _itemForAlias: [self selectedAlias]]];
[41]324 [[NSNotificationCenter defaultCenter] postNotificationName: NJRQTMediaPopUpButtonMovieChangedNotification object: self];
[34]325}
326
[533]327- (void)_startSoundPreview;
[133]328{
[533]329 if ([preview movie] == nil || outputVolume == kNoVolume)
330 return;
331
332 if (savedVolume || [NJRSoundManager saveDefaultOutputVolume]) {
[133]333 savedVolume = YES;
334 [NJRSoundManager setDefaultOutputVolume: outputVolume];
335 }
[533]336
337 if ([[preview movie] rate] != 0)
338 return; // don't restart preview if already playing
339
340 [[NSNotificationCenter defaultCenter] addObserver: self
341 selector: @selector(_soundPreviewDidEnd:)
342 name: QTMovieDidEndNotification
343 object: [preview movie]];
344 [preview play: self];
[133]345}
346
[533]347- (void)_soundPreviewDidEnd:(NSNotification *)notification;
[133]348{
[533]349 [self _resetPreview];
[133]350}
351
352- (void)_resetPreview;
353{
[533]354 [preview setMovie: nil];
355 [self _resetOutputVolume];
[133]356}
357
[533]358- (void)_resetOutputVolume;
[60]359{
[533]360 [NJRSoundManager restoreSavedDefaultOutputVolumeIfCurrently: outputVolume];
361 savedVolume = NO;
[60]362}
363
[41]364- (BOOL)_validateWithPreview:(BOOL)doPreview;
[34]365{
[535]366 // prevent _resetPreview from triggering afterward (crashes)
367 [[NSNotificationCenter defaultCenter] removeObserver: self
368 name: QTMovieDidEndNotification
369 object: [preview movie]];
[533]370 [preview pause: self];
[34]371 if (selectedAlias == nil) {
372 [preview setMovie: nil];
[41]373 movieCanRepeat = YES;
[133]374 movieHasAudio = NO; // XXX should be YES - this is broken, NSBeep() is asynchronous
375 if (doPreview) {
376 // XXX [self _updateOutputVolume];
377 NSBeep();
378 // XXX [self _resetOutputVolume];
379 }
[34]380 } else {
[541]381 NSError *error;
382 QTMovie *movie = [[QTMovie alloc] initWithFile: [selectedAlias fullPath] error: &error];
383 movieCanRepeat = ![movie NJR_isStatic];
384 if (movieHasAudio = [movie NJR_hasAudio]) {
385 [preview setMovie: doPreview ? movie : nil];
[60]386 } else {
[133]387 [self _resetPreview];
388 doPreview = NO;
[34]389 if (movie == nil) {
[541]390 NSBeginAlertSheet(@"Format not recognized", nil, nil, nil, [self window], nil, nil, nil, nil, [NSString stringWithFormat: NSLocalizedString(@"The item you selected isn't an image, sound or movie recognized by QuickTime. (%@)\n\nPlease select a different item.", "Message displayed in alert sheet when media document is not recognized by QuickTime"), [error localizedDescription]]);
[39]391 [self _invalidateSelection];
[34]392 return NO;
393 }
[541]394 if (![movie NJR_hasAudio] && ![movie NJR_hasVideo]) {
395 NSBeginAlertSheet(@"No video or audio", nil, nil, nil, [self window], nil, nil, nil, nil, NSLocalizedString(@"'%@' contains neither audio nor video content playable by QuickTime.\n\nPlease select a different item.", "Message displayed in alert sheet when media document is readable, but has neither audio nor video tracks"), [[NSFileManager defaultManager] displayNameAtPath: [selectedAlias fullPath]]);
[39]396 [self _invalidateSelection];
[34]397 [movie release];
398 return NO;
399 }
400 }
[60]401 if (doPreview) {
[133]402 [self _startSoundPreview];
[60]403 }
[34]404 [movie release];
405 }
[41]406 [[NSNotificationCenter defaultCenter] postNotificationName: NJRQTMediaPopUpButtonMovieChangedNotification object: self];
[34]407 return YES;
408}
409
[41]410#pragma mark actions
411
[39]412- (IBAction)stopSoundPreview:(id)sender;
413{
[533]414 [preview pause: self];
[133]415 [self _resetPreview];
[39]416}
417
418- (void)_beepSelected:(NSMenuItem *)sender;
419{
420 [self _setAlias: nil];
[41]421 [self _validateWithPreview: YES];
[39]422}
423
[355]424- (void)_systemSoundSelected:(NSMenuItem *)sender;
[39]425{
[355]426 [self _setPath: [sender representedObject]];
[41]427 if (![self _validateWithPreview: YES]) {
[39]428 [[self menu] removeItem: sender];
429 }
430}
431
432- (void)_aliasSelected:(NSMenuItem *)sender;
433{
434 BDAlias *alias = [sender representedObject];
[602]435 int itemIndex = [self indexOfItem: sender], otherIndex = [self indexOfItem: otherItem];
[39]436 [self _setAlias: alias];
[41]437 if (![self _validateWithPreview: YES]) {
[39]438 [[self menu] removeItem: sender];
[602]439 } else if (itemIndex > otherIndex + 1) { // move "other" item to top of list
440 int recentIndex = [recentMediaAliasData count] - itemIndex + otherIndex;
441 NSMenuItem *item = [[self itemAtIndex: itemIndex] retain];
[39]442 NSData *data = [[recentMediaAliasData objectAtIndex: recentIndex] retain];
[45]443 // [self _validateRecentMedia];
[602]444 [self removeItemAtIndex: itemIndex];
[39]445 [[self menu] insertItem: item atIndex: otherIndex + 1];
446 [self selectItem: item];
447 [item release];
448 NSAssert(recentIndex >= 0, @"Recent media index invalid");
[41]449 // NSLog(@"_aliasSelected removing item %d - %d + %d = %d of recentMediaAliasData", [recentMediaAliasData count], index, otherIndex, recentIndex);
[39]450 [recentMediaAliasData removeObjectAtIndex: recentIndex];
451 [recentMediaAliasData addObject: data];
[40]452 [self _validateRecentMedia];
[39]453 [data release];
[40]454 } // else NSLog(@"_aliasSelected ...already at top");
[39]455}
456
457- (IBAction)select:(id)sender;
458{
459 NSOpenPanel *openPanel = [NSOpenPanel openPanel];
460 NSString *path = [selectedAlias fullPath];
461 [openPanel setAllowsMultipleSelection: NO];
462 [openPanel setCanChooseDirectories: NO];
463 [openPanel setCanChooseFiles: YES];
[537]464 [openPanel setDelegate: self];
[39]465 [openPanel beginSheetForDirectory: [path stringByDeletingLastPathComponent]
466 file: [path lastPathComponent]
[537]467 types: nil
[39]468 modalForWindow: [self window]
469 modalDelegate: self
470 didEndSelector: @selector(openPanelDidEnd:returnCode:contextInfo:)
471 contextInfo: nil];
472}
473
474- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
475{
476 [sheet close];
477
478 if (returnCode == NSOKButton) {
479 NSArray *files = [sheet filenames];
480 NSAssert1([files count] == 1, @"%d items returned, only one expected", [files count]);
481 [self _setPath: [files objectAtIndex: 0]];
[41]482 if ([self _validateWithPreview: YES]) {
[39]483 [self selectItem: [self _itemForAlias: selectedAlias]];
484 }
485 } else {
486 // "Other..." item is still selected, revert to previously selected item
487 // XXX issue with cancelling, top item in recent menu is sometimes duplicated!?
488 [self selectItem: [self _itemForAlias: selectedAlias]];
489 }
490 // [self _validateRecentMedia];
491}
492
[43]493- (void)setEnabled:(BOOL)flag;
494{
495 [super setEnabled: flag];
496 if (flag) ; // XXX [self startSoundPreview: self]; // need to prohibit at startup
497 else [self stopSoundPreview: self];
498}
499
[41]500#pragma mark drag feedback
501
502- (void)drawRect:(NSRect)rect;
503{
504 if (dragAccepted) {
505 NSWindow *window = [self window];
506 NSRect boundsRect = [self bounds];
507 BOOL isFirstResponder = ([window firstResponder] == self);
508 // focus ring and drag feedback interfere with one another
509 if (isFirstResponder) [window makeFirstResponder: window];
510 [super drawRect: rect];
511 [[NSColor selectedControlColor] set];
512 NSFrameRectWithWidthUsingOperation(NSInsetRect(boundsRect, 2, 2), 3, NSCompositeSourceIn);
513 if (isFirstResponder) [window makeFirstResponder: self];
514 } else {
515 [super drawRect: rect];
516 }
517}
518
[34]519@end
[39]520
[537]521@implementation NJRQTMediaPopUpButton (NSSavePanelDelegate)
522
523- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename;
524{
525 BOOL isDir = NO;
526 [[NSFileManager defaultManager] fileExistsAtPath: filename isDirectory: &isDir];
527
528 if (isDir)
529 return YES;
530
531 return [QTMovie canInitWithFile: filename];
532}
533
534@end
535
[39]536@implementation NJRQTMediaPopUpButton (NSDraggingDestination)
537
538- (BOOL)acceptsDragFrom:(id <NSDraggingInfo>)sender;
539{
540 NSURL *url = [NSURL URLFromPasteboard: [sender draggingPasteboard]];
[40]541 NSFileManager *fm = [NSFileManager defaultManager];
542 BOOL isDir;
[39]543
544 if (url == nil || ![url isFileURL]) return NO;
[40]545
546 if (![fm fileExistsAtPath: [url path] isDirectory: &isDir]) return NO;
547
548 if (isDir) return NO;
549
[39]550 return YES;
551}
552
[40]553- (NSString *)_descriptionForDraggingInfo:(id <NSDraggingInfo>)sender;
554{
555 NSDragOperation mask = [sender draggingSourceOperationMask];
556 NSMutableString *s = [NSMutableString stringWithFormat: @"Drag seq %d source: %@",
557 [sender draggingSequenceNumber], [sender draggingSource]];
558 NSPasteboard *draggingPasteboard = [sender draggingPasteboard];
559 NSArray *types = [draggingPasteboard types];
560 NSEnumerator *e = [types objectEnumerator];
561 NSString *type;
562 [s appendString: @"\nDrag operations:"];
563 if (mask & NSDragOperationCopy) [s appendString: @" copy"];
564 if (mask & NSDragOperationLink) [s appendString: @" link"];
565 if (mask & NSDragOperationGeneric) [s appendString: @" generic"];
566 if (mask & NSDragOperationPrivate) [s appendString: @" private"];
567 if (mask & NSDragOperationMove) [s appendString: @" move"];
568 if (mask & NSDragOperationDelete) [s appendString: @" delete"];
569 if (mask & NSDragOperationEvery) [s appendString: @" every"];
570 if (mask & NSDragOperationNone) [s appendString: @" none"];
571 [s appendFormat: @"\nImage: %@ at %@", [sender draggedImage],
572 NSStringFromPoint([sender draggedImageLocation])];
573 [s appendFormat: @"\nDestination: %@ at %@", [sender draggingDestinationWindow],
574 NSStringFromPoint([sender draggingLocation])];
575 [s appendFormat: @"\nPasteboard: %@ types:", draggingPasteboard];
576 while ( (type = [e nextObject]) != nil) {
577 if ([type hasPrefix: @"CorePasteboardFlavorType 0x"]) {
[587]578 const char *osTypeHex = [[type substringFromIndex: [type rangeOfString: @"0x" options: NSBackwardsSearch].location] UTF8String];
[40]579 OSType osType;
580 sscanf(osTypeHex, "%lx", &osType);
581 [s appendFormat: @" '%4s'", &osType];
582 } else {
[103]583 [s appendFormat: @" '%@'", type];
[40]584 }
585 }
586 return s;
587}
588
[39]589- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender;
590{
591 if ([self acceptsDragFrom: sender] && [sender draggingSourceOperationMask] &
[40]592 (NSDragOperationCopy | NSDragOperationLink)) {
[39]593 dragAccepted = YES;
594 [self setNeedsDisplay: YES];
[40]595 // NSLog(@"draggingEntered accept:\n%@", [self _descriptionForDraggingInfo: sender]);
596 return NSDragOperationLink;
[39]597 }
598 return NSDragOperationNone;
599}
600
601- (void)draggingExited:(id <NSDraggingInfo>)sender;
602{
603 dragAccepted = NO;
604 [self setNeedsDisplay: YES];
605}
606
607- (BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender;
608{
609 dragAccepted = NO;
610 [self setNeedsDisplay: YES];
611 return [self acceptsDragFrom: sender];
612}
613
614- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender;
615{
616 if ([sender draggingSource] != self) {
617 NSURL *url = [NSURL URLFromPasteboard: [sender draggingPasteboard]];
618 if (url == nil) return NO;
619 [self _setPath: [url path]];
[41]620 if ([self _validateWithPreview: YES]) {
[40]621 [self selectItem: [self _itemForAlias: selectedAlias]];
622 }
[39]623 }
624 return YES;
625}
626
627@end
Note: See TracBrowser for help on using the repository browser.