@@ -454,6 +454,39 @@ + (NSButton*)newButton {
454
454
455
455
#pragma mark * Private Methods
456
456
457
+ /* !
458
+ @brief Translates from the 'recovery option' as expressed in our -doLayoutError:
459
+ method to the 'recovery option index' expressed in Cocoa's error presentation method,
460
+ -presentError:.
461
+
462
+ @details Cocoa's arrangement allows an unlimited number of error recovery options,
463
+ indexed from 0. Our -doLayoutError: method only allows three options, which are
464
+ indexed like the buttons in NSAlert. In particular, note that the values
465
+ represented by 0 and 1 are reversed.
466
+ */
467
+ + (NSUInteger )recoveryOptionIndexForRecoveryOption : (NSInteger )recoveryOption {
468
+ NSUInteger recoveryOptionIndex ;
469
+ switch (recoveryOption) {
470
+ case NSAlertDefaultReturn /* 1 */ :
471
+ recoveryOptionIndex = 0 ;
472
+ break ;
473
+ case NSAlertAlternateReturn /* 0 */ :
474
+ recoveryOptionIndex = 1 ;
475
+ break ;
476
+ case NSAlertOtherReturn /* -1 */ :
477
+ recoveryOptionIndex = 2 ;
478
+ break ;
479
+ default :
480
+ // This should never happen since we only have 3 buttons and return
481
+ // one of the above three values like NSAlert.
482
+ NSLog (@" Warning 520-3840 %d " , recoveryOption) ;
483
+ recoveryOptionIndex = recoveryOption ;
484
+ break ;
485
+ }
486
+
487
+ return recoveryOptionIndex ;
488
+ }
489
+
457
490
+ (NSInteger )tryRecoveryAttempterForError : (NSError *)error
458
491
recoveryOption : (NSUInteger )recoveryOption
459
492
contextInfo : (NSMutableDictionary *)infoDictionary {
@@ -471,29 +504,70 @@ + (NSInteger)tryRecoveryAttempterForError:(NSError*)error
471
504
NSInvocation * invocation = [error didRecoverInvocation ] ;
472
505
id delegate = [invocation target ] ;
473
506
SEL didRecoverSelector = [invocation selector ] ;
474
- // It seems that the "Cocoa way" of recovering, using the delegate and
475
- // didRecoverSelector, only works if didRecoverSelector has no arguments.
476
- // That's why I put the whole invocation into the context info. I believe
477
- // it's alot cleaner anyhow.
507
+ // I put the whole invocation into the context info, believing it to be alot cleaner.
478
508
if (invocation) {
479
509
// Before we invoke the didRecoverInvocation, we also put it into the
480
510
// current infoDictionary in case an error occurs again and we need
481
511
// to re-recover.
482
512
[infoDictionary setObject: invocation
483
513
forKey: SSYAlertDidRecoverInvocationKey] ;
484
514
}
485
- [recoveryAttempter attemptRecoveryFromError: [[error retain ] autorelease ]
515
+ [recoveryAttempter attemptRecoveryFromError: [[deepestRecoverableError retain ] autorelease ]
486
516
recoveryOption: recoveryOption
487
517
delegate: delegate
488
518
didRecoverSelector: didRecoverSelector
489
519
contextInfo: [[infoDictionary retain ] autorelease ]] ;
490
520
// Also, the retain] autorelease] is probably not necessary since I'm invoking attemptRecoveryFromError:::::
491
521
// directly, but I'm always fearful of crashes due to invalid contextInfo.
492
- result = SSYAlertRecoveryWentAsynchronous ;
522
+ result = SSYAlertRecoveryAttemptedAsynchronously ;
523
+ }
524
+ else if ([recoveryAttempter respondsToSelector: @selector (attemptRecoveryFromError:optionIndex:delegate:didRecoverSelector:contextInfo: )]) {
525
+ /* This is an error produced by Cocoa.
526
+ In particular, in Mac OS X 10.7, it might be one like this:
527
+ Error Domain = NSCocoaErrorDomain
528
+ Code = 67000
529
+ UserInfo = {
530
+ • NSLocalizedRecoverySuggestion=Click Save Anyway to keep your changes and save the
531
+ changes made by the other application as a version, or click Revert to keep the changes from the other
532
+ application and save your changes as a version.
533
+ • NSLocalizedFailureReason=The file has been changed by another application.
534
+ • NSLocalizedDescription=This document’s file has been changed by another application.
535
+ • NSLocalizedRecoveryOptions = ("Save Anyway", "Revert")
536
+ }
537
+ */
538
+ NSInvocation * invocation = [error didRecoverInvocation ] ;
539
+ id delegate = [invocation target ] ;
540
+ SEL didRecoverSelector = [invocation selector ] ;
541
+ // I put the whole invocation into the context info, believing it to be alot cleaner.
542
+ if (invocation) {
543
+ // Before we invoke the didRecoverInvocation, we also put it into the
544
+ // current infoDictionary in case an error occurs again and we need
545
+ // to re-recover.
546
+ [infoDictionary setObject: invocation
547
+ forKey: SSYAlertDidRecoverInvocationKey] ;
548
+ }
549
+ NSInteger recoveryOptionIndex = [self recoveryOptionIndexForRecoveryOption: recoveryOption] ;
550
+
551
+ [recoveryAttempter attemptRecoveryFromError: [[deepestRecoverableError retain ] autorelease ]
552
+ optionIndex: recoveryOptionIndex
553
+ delegate: delegate
554
+ didRecoverSelector: didRecoverSelector
555
+ contextInfo: [[infoDictionary retain ] autorelease ]] ;
556
+ // Also, the retain] autorelease] is probably not necessary since I'm invoking attemptRecoveryFromError:::::
557
+ // directly, but I'm always fearful of crashes due to invalid contextInfo.
558
+ result = SSYAlertRecoveryAttemptedAsynchronously ;
493
559
}
494
560
else if ([recoveryAttempter respondsToSelector: @selector (attemptRecoveryFromError:recoveryOption: )]) {
495
- BOOL ok = [recoveryAttempter attemptRecoveryFromError: error
496
- recoveryOption: recoveryOption] ;
561
+ BOOL ok = [recoveryAttempter attemptRecoveryFromError: deepestRecoverableError
562
+ recoveryOption: recoveryOption] ;
563
+
564
+ result = ok ? SSYAlertRecoverySucceeded : SSYAlertRecoveryFailed ;
565
+ }
566
+ else if ([recoveryAttempter respondsToSelector: @selector (attemptRecoveryFromError:optionIndex: )]) {
567
+ // This is an error produced by Cocoa.
568
+ NSInteger recoveryOptionIndex = [self recoveryOptionIndexForRecoveryOption: recoveryOption] ;
569
+ BOOL ok = [recoveryAttempter attemptRecoveryFromError: deepestRecoverableError
570
+ optionIndex: recoveryOptionIndex] ;
497
571
498
572
result = ok ? SSYAlertRecoverySucceeded : SSYAlertRecoveryFailed ;
499
573
}
@@ -1900,19 +1974,20 @@ - (void)alertError:(NSError*)error
1900
1974
[self noteError: error] ;
1901
1975
}
1902
1976
1903
- - (int )alertError : (NSError *)error {
1977
+ - (SSYAlertRecovery )alertError : (NSError *)error {
1904
1978
if (!error) {
1905
- return SSYAlertRecoveryWasNoError ;
1979
+ return SSYAlertRecoveryThereWasNoError ;
1906
1980
}
1907
1981
1908
- int alertReturn_ = NSAlertErrorReturn ;
1982
+ int alertReturn ;
1909
1983
1910
- if (
1911
- ![gSSYAlertErrorHideManager shouldHideError: error]
1912
- &&
1913
- !(([[error domain ] isEqualToString: NSCocoaErrorDomain ]) && ([error code ] == NSUserCancelledError))
1914
- ) {
1915
-
1984
+ if ([gSSYAlertErrorHideManager shouldHideError: error]) {
1985
+ alertReturn = SSYAlertRecoveryErrorIsHidden ;
1986
+ }
1987
+ else if ([[error domain ] isEqualToString: NSCocoaErrorDomain ] && ([error code ] == NSUserCancelledError)) {
1988
+ alertReturn = SSYAlertRecoveryAppleScriptCodeUserCancelledPreviously ;
1989
+ }
1990
+ else {
1916
1991
[self doLayoutError: error] ;
1917
1992
[self display ] ;
1918
1993
@@ -1923,26 +1998,26 @@ - (int)alertError:(NSError*)error {
1923
1998
[NSApp runModalForWindow: [self window ]] ;
1924
1999
// Will block here until user clicks a button
1925
2000
1926
- alertReturn_ = [self alertReturn ] ;
2001
+ alertReturn = [self alertReturn ] ;
1927
2002
NSInteger recoveryResult = [SSYAlert tryRecoveryAttempterForError: error
1928
- recoveryOption: alertReturn_
2003
+ recoveryOption: alertReturn
1929
2004
contextInfo: nil ] ;
1930
2005
if (recoveryResult != SSYAlertRecoveryNotAttempted) {
1931
- alertReturn_ = recoveryResult ;
2006
+ alertReturn = recoveryResult ;
1932
2007
}
1933
2008
}
1934
2009
1935
2010
[self noteError: error] ;
1936
2011
1937
- return alertReturn_ ;
2012
+ return alertReturn ;
1938
2013
}
1939
2014
1940
- + (int )alertError : (NSError *)error {
2015
+ + (SSYAlertRecovery )alertError : (NSError *)error {
1941
2016
// In BookMacster 1.1.10, I found that Core Data migration, specifically
1942
2017
// -[Bkmx007-008MigrationPolicy createDestinationInstancesForSourceInstance:::]
1943
2018
// was spending 99% of its time creating SSYAlerts for nil errors! Fix…
1944
2019
if (!error) {
1945
- return SSYAlertRecoveryWasNoError ;
2020
+ return SSYAlertRecoveryThereWasNoError ;
1946
2021
}
1947
2022
1948
2023
return [[SSYAlert alert ] alertError: error] ;
0 commit comments