Configurable logging (LogTo).
Added "my_" prefix to category method names.
Added MurmurHash.
Added UniqueWindowController.
Bug fixes.
1.1 --- a/Base64.h Sat Mar 08 21:04:41 2008 -0800
1.2 +++ b/Base64.h Thu Mar 20 09:05:58 2008 -0700
1.3 @@ -9,15 +9,18 @@
1.4 #import <Cocoa/Cocoa.h>
1.5
1.6
1.7 -@interface NSData (Base64)
1.8 +//NOTE: Using this requires linking against /usr/lib/libcrypto.dylib.
1.9
1.10 -- (NSString *)base64String;
1.11 -- (NSString *)base64StringWithNewlines:(BOOL)encodeWithNewlines;
1.12
1.13 -- (NSData *)decodeBase64;
1.14 -- (NSData *)decodeBase64WithNewLines:(BOOL)encodedWithNewlines;
1.15 +@interface NSData (MYBase64)
1.16
1.17 -- (NSString *)hexString;
1.18 -- (NSString *)hexDump;
1.19 +- (NSString *)my_base64String;
1.20 +- (NSString *)my_base64StringWithNewlines:(BOOL)encodeWithNewlines;
1.21 +
1.22 +- (NSData *)my_decodeBase64;
1.23 +- (NSData *)my_decodeBase64WithNewLines:(BOOL)encodedWithNewlines;
1.24 +
1.25 +- (NSString *)my_hexString;
1.26 +- (NSString *)my_hexDump;
1.27
1.28 @end
2.1 --- a/Base64.m Sat Mar 08 21:04:41 2008 -0800
2.2 +++ b/Base64.m Thu Mar 20 09:05:58 2008 -0700
2.3 @@ -10,11 +10,13 @@
2.4 //
2.5
2.6 #import "Base64.h"
2.7 +
2.8 +//NOTE: Using this requires linking against /usr/lib/libcrypto.dylib.
2.9 #import <openssl/bio.h>
2.10 #import <openssl/evp.h>
2.11
2.12
2.13 -@implementation NSData (Base64)
2.14 +@implementation NSData (MYBase64)
2.15
2.16
2.17 /**
2.18 @@ -24,9 +26,9 @@
2.19 * Code courtesy of DaveDribin (http://www.dribin.org/dave/)
2.20 * Taken from http://www.cocoadev.com/index.pl?BaseSixtyFour
2.21 **/
2.22 -- (NSString *)base64String
2.23 +- (NSString *)my_base64String
2.24 {
2.25 - return [self base64StringWithNewlines: YES];
2.26 + return [self my_base64StringWithNewlines: YES];
2.27 }
2.28
2.29 /**
2.30 @@ -36,7 +38,7 @@
2.31 * Code courtesy of DaveDribin (http://www.dribin.org/dave/)
2.32 * Taken from http://www.cocoadev.com/index.pl?BaseSixtyFour
2.33 **/
2.34 -- (NSString *)base64StringWithNewlines:(BOOL)encodeWithNewlines
2.35 +- (NSString *)my_base64StringWithNewlines:(BOOL)encodeWithNewlines
2.36 {
2.37 // Create a memory buffer which will contain the Base64 encoded string
2.38 BIO * mem = BIO_new(BIO_s_mem());
2.39 @@ -61,12 +63,12 @@
2.40 return base64String;
2.41 }
2.42
2.43 -- (NSData *)decodeBase64
2.44 +- (NSData *)my_decodeBase64
2.45 {
2.46 - return [self decodeBase64WithNewLines:YES];
2.47 + return [self my_decodeBase64WithNewLines:YES];
2.48 }
2.49
2.50 -- (NSData *)decodeBase64WithNewLines:(BOOL)encodedWithNewlines
2.51 +- (NSData *)my_decodeBase64WithNewLines:(BOOL)encodedWithNewlines
2.52 {
2.53 // Create a memory buffer containing Base64 encoded string data
2.54 BIO * mem = BIO_new_mem_buf((void *) [self bytes], [self length]);
2.55 @@ -90,7 +92,7 @@
2.56 }
2.57
2.58
2.59 -- (NSString *)hexString
2.60 +- (NSString *)my_hexString
2.61 {
2.62 const UInt8 *bytes = self.bytes;
2.63 NSUInteger length = self.length;
2.64 @@ -102,7 +104,7 @@
2.65 autorelease];
2.66 }
2.67
2.68 -- (NSString *)hexDump
2.69 +- (NSString *)my_hexDump
2.70 {
2.71 NSMutableString *ret=[NSMutableString stringWithCapacity:[self length]*2];
2.72 /* dumps size bytes of *data to string. Looks like:
3.1 --- a/GraphicsUtils.m Sat Mar 08 21:04:41 2008 -0800
3.2 +++ b/GraphicsUtils.m Thu Mar 20 09:05:58 2008 -0700
3.3 @@ -30,7 +30,7 @@
3.4 - (NSImage*) my_shrunkToFitIn: (NSSize) maxSize
3.5 {
3.6 NSSize size = self.size;
3.7 - float scale = MIN( size.width/maxSize.width, size.height/maxSize.height );
3.8 + float scale = MIN( maxSize.width/size.width, maxSize.height/size.height );
3.9 if( scale >= 1.0 )
3.10 return self;
3.11
4.1 --- a/Logging.h Sat Mar 08 21:04:41 2008 -0800
4.2 +++ b/Logging.h Thu Mar 20 09:05:58 2008 -0700
4.3 @@ -12,12 +12,23 @@
4.4 NSString* LOC( NSString *key ); // Localized string lookup
4.5
4.6
4.7 -#define Log(FMT,ARGS...) do{if(__builtin_expect(gShouldLog,0)) _Log(FMT,##ARGS);}while(0)
4.8 +#define Log(FMT,ARGS...) do{if(__builtin_expect(_gShouldLog,0)) _Log(FMT,##ARGS);}while(0)
4.9 #define Warn Warn
4.10
4.11 void AlwaysLog( NSString *msg, ... ) __attribute__((format(__NSString__, 1, 2)));
4.12
4.13 +#define LogTo(DOMAIN,FMT,ARGS...) do{if(__builtin_expect(_gShouldLog,0)) _LogTo(@""#DOMAIN,FMT,##ARGS);}while(0)
4.14
4.15 -extern int gShouldLog;
4.16 +BOOL _WillLogTo( NSString *domain );
4.17 +BOOL EnableLog( BOOL enable );
4.18 +#define EnableLogTo( DOMAIN, VALUE ) _EnableLogTo(@""#DOMAIN, VALUE)
4.19 +#define WillLogTo( DOMAIN ) _WillLogTo(@""#DOMAIN)
4.20 +
4.21 +
4.22 +// internals; don't use directly
4.23 +extern int _gShouldLog;
4.24 void _Log( NSString *msg, ... ) __attribute__((format(__NSString__, 1, 2)));
4.25 void Warn( NSString *msg, ... ) __attribute__((format(__NSString__, 1, 2)));
4.26 +void _LogTo( NSString *domain, NSString *msg, ... ) __attribute__((format(__NSString__, 2, 3)));
4.27 +BOOL _WillLogTo( NSString *domain );
4.28 +BOOL _EnableLogTo( NSString *domain, BOOL enable );
5.1 --- a/Logging.m Sat Mar 08 21:04:41 2008 -0800
5.2 +++ b/Logging.m Thu Mar 20 09:05:58 2008 -0700
5.3 @@ -23,7 +23,9 @@
5.4 }
5.5
5.6
5.7 -int gShouldLog = -1;
5.8 +int _gShouldLog = -1;
5.9 +static BOOL sConsole;
5.10 +static NSMutableSet *sEnabledDomains;
5.11
5.12
5.13 static BOOL isConsole( int fd )
5.14 @@ -37,9 +39,65 @@
5.15 }
5.16
5.17
5.18 -static void _Logv( NSString *msg, va_list args )
5.19 +static void InitLogging()
5.20 {
5.21 - if( isConsole(STDERR_FILENO) ) {
5.22 + if( _gShouldLog != -1 )
5.23 + return;
5.24 +
5.25 + NSAutoreleasePool *pool = [NSAutoreleasePool new];
5.26 + _gShouldLog = NO;
5.27 + sEnabledDomains = [[NSMutableSet alloc] init];
5.28 + NSDictionary *dflts = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation];
5.29 + for( NSString *key in dflts ) {
5.30 + if( [key hasPrefix: @"Log"] ) {
5.31 + BOOL value = [[NSUserDefaults standardUserDefaults] boolForKey: key];
5.32 + if( key.length==3 )
5.33 + _gShouldLog = value;
5.34 + else if( value )
5.35 + [sEnabledDomains addObject: [key substringFromIndex: 3]];
5.36 + }
5.37 + }
5.38 + sConsole = isConsole(STDERR_FILENO);
5.39 +
5.40 + Log(@"Logging enabled in domains: {%@}",
5.41 + [[[sEnabledDomains allObjects] sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)]
5.42 + componentsJoinedByString: @", "]);
5.43 + [pool drain];
5.44 +}
5.45 +
5.46 +
5.47 +BOOL EnableLog( BOOL enable )
5.48 +{
5.49 + if( _gShouldLog == -1 )
5.50 + InitLogging();
5.51 + BOOL old = _gShouldLog;
5.52 + _gShouldLog = enable;
5.53 + return old;
5.54 +}
5.55 +
5.56 +BOOL _WillLogTo( NSString *domain )
5.57 +{
5.58 + if( _gShouldLog == -1 )
5.59 + InitLogging();
5.60 + return _gShouldLog && [sEnabledDomains containsObject: domain];
5.61 +}
5.62 +
5.63 +BOOL _EnableLogTo( NSString *domain, BOOL enable )
5.64 +{
5.65 + if( _gShouldLog == -1 )
5.66 + InitLogging();
5.67 + BOOL old = [sEnabledDomains containsObject: domain];
5.68 + if( enable )
5.69 + [sEnabledDomains addObject: domain];
5.70 + else
5.71 + [sEnabledDomains removeObject: domain];
5.72 + return old;
5.73 +}
5.74 +
5.75 +
5.76 +static void _Logv( NSString *prefix, NSString *msg, va_list args )
5.77 +{
5.78 + if( sConsole ) {
5.79 NSAutoreleasePool *pool = [NSAutoreleasePool new];
5.80 static NSDateFormatter *sTimestampFormat;
5.81 if( ! sTimestampFormat ) {
5.82 @@ -49,8 +107,10 @@
5.83 NSDate *now = [[NSDate alloc] init];
5.84 NSString *timestamp = [sTimestampFormat stringFromDate: now];
5.85 [now release];
5.86 + NSString *separator = prefix.length ?@": " :@"";
5.87 msg = [[NSString alloc] initWithFormat: msg arguments: args];
5.88 - NSString *finalMsg = [[NSString alloc] initWithFormat: @"%@| %@\n", timestamp,msg];
5.89 + NSString *finalMsg = [[NSString alloc] initWithFormat: @"%@| %@%@%@\n",
5.90 + timestamp,prefix,separator,msg];
5.91 fputs([finalMsg UTF8String], stderr);
5.92 [finalMsg release];
5.93 [msg release];
5.94 @@ -64,32 +124,42 @@
5.95 {
5.96 va_list args;
5.97 va_start(args,msg);
5.98 - _Logv(msg,args);
5.99 + _Logv(@"",msg,args);
5.100 va_end(args);
5.101 }
5.102
5.103
5.104 void _Log( NSString *msg, ... )
5.105 {
5.106 - if( gShouldLog == -1 )
5.107 - gShouldLog = [[NSUserDefaults standardUserDefaults] boolForKey: @"Log"];
5.108 -
5.109 - if( gShouldLog ) {
5.110 + if( _gShouldLog == -1 )
5.111 + InitLogging();
5.112 + if( _gShouldLog ) {
5.113 va_list args;
5.114 va_start(args,msg);
5.115 - _Logv(msg,args);
5.116 + _Logv(@"",msg,args);
5.117 va_end(args);
5.118 }
5.119 }
5.120
5.121
5.122 +void _LogTo( NSString *domain, NSString *msg, ... )
5.123 +{
5.124 + if( _gShouldLog == -1 )
5.125 + InitLogging();
5.126 + if( _gShouldLog && [sEnabledDomains containsObject: domain] ) {
5.127 + va_list args;
5.128 + va_start(args,msg);
5.129 + _Logv(domain, msg, args);
5.130 + va_end(args);
5.131 + }
5.132 +}
5.133 +
5.134 +
5.135 void Warn( NSString *msg, ... )
5.136 {
5.137 - msg = [@"WARNING*** " stringByAppendingString: msg];
5.138 -
5.139 va_list args;
5.140 va_start(args,msg);
5.141 - _Logv(msg,args);
5.142 + _Logv(@"WARNING*** ",msg,args);
5.143 va_end(args);
5.144 }
5.145
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/MurmurHash.c Thu Mar 20 09:05:58 2008 -0700
6.3 @@ -0,0 +1,80 @@
6.4 +/*
6.5 + * MurmurHash.c
6.6 + * MYUtilities
6.7 + *
6.8 + * This file created by Jens Alfke on 3/17/08.
6.9 + * Algorithm & source code by Austin Appleby, released to public domain.
6.10 + * <http://murmurhash.googlepages.com/>
6.11 + * Downloaded 3/16/2008.
6.12 + * Modified slightly by Jens Alfke (use standard uint32_t and size_t types;
6.13 + * change 'm' and 'r' to #defines for better C compatibility.)
6.14 + *
6.15 + */
6.16 +
6.17 +#include "MurmurHash.h"
6.18 +
6.19 +
6.20 +//-----------------------------------------------------------------------------
6.21 +// MurmurHash2, by Austin Appleby
6.22 +
6.23 +// Note - This code makes a few assumptions about how your machine behaves -
6.24 +
6.25 +// 1. We can read a 4-byte value from any address without crashing
6.26 +// 2. sizeof(int) == 4 **Jens: I fixed this by changing 'unsigned int' to 'uint32_t'**
6.27 +
6.28 +// And it has a few limitations -
6.29 +
6.30 +// 1. It will not work incrementally.
6.31 +// 2. It will not produce the same results on little-endian and big-endian
6.32 +// machines.
6.33 +
6.34 +uint32_t MurmurHash2 ( const void * key, size_t len, uint32_t seed )
6.35 +{
6.36 + // 'm' and 'r' are mixing constants generated offline.
6.37 + // They're not really 'magic', they just happen to work well.
6.38 +
6.39 + #define m 0x5bd1e995
6.40 + #define r 24
6.41 +
6.42 + // Initialize the hash to a 'random' value
6.43 +
6.44 + uint32_t h = seed ^ len;
6.45 +
6.46 + // Mix 4 bytes at a time into the hash
6.47 +
6.48 + const unsigned char * data = (const unsigned char *)key;
6.49 +
6.50 + while(len >= 4)
6.51 + {
6.52 + uint32_t k = *(uint32_t *)data;
6.53 +
6.54 + k *= m;
6.55 + k ^= k >> r;
6.56 + k *= m;
6.57 +
6.58 + h *= m;
6.59 + h ^= k;
6.60 +
6.61 + data += 4;
6.62 + len -= 4;
6.63 + }
6.64 +
6.65 + // Handle the last few bytes of the input array
6.66 +
6.67 + switch(len)
6.68 + {
6.69 + case 3: h ^= data[2] << 16;
6.70 + case 2: h ^= data[1] << 8;
6.71 + case 1: h ^= data[0];
6.72 + h *= m;
6.73 + };
6.74 +
6.75 + // Do a few final mixes of the hash to ensure the last few
6.76 + // bytes are well-incorporated.
6.77 +
6.78 + h ^= h >> 13;
6.79 + h *= m;
6.80 + h ^= h >> 15;
6.81 +
6.82 + return h;
6.83 +}
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/MurmurHash.h Thu Mar 20 09:05:58 2008 -0700
7.3 @@ -0,0 +1,23 @@
7.4 +/*
7.5 + * MurmurHash.h
7.6 + * MYUtilities
7.7 + *
7.8 + * This file created by Jens Alfke on 3/17/08.
7.9 + * Algorithm & source code by Austin Appleby, released to public domain.
7.10 + * <http://murmurhash.googlepages.com/>
7.11 + *
7.12 + */
7.13 +
7.14 +#include <stdint.h>
7.15 +#include <sys/types.h>
7.16 +
7.17 +/** An extremely efficient general-purpose hash function.
7.18 + Murmurhash is claimed to be more than twice as fast as the nearest competitor,
7.19 + and to offer better-distributed output with fewer collisions.
7.20 + It is, however not suitable for cryptographic use.
7.21 + Hash values will differ between bit- and little-endian CPUs, so they shouldn't
7.22 + be stored persistently or transmitted over the network.
7.23 +
7.24 + Written by Austin Appleby: <http://murmurhash.googlepages.com/> */
7.25 +
7.26 +uint32_t MurmurHash2 ( const void * key, size_t len, uint32_t seed );
8.1 --- a/Target.m Sat Mar 08 21:04:41 2008 -0800
8.2 +++ b/Target.m Thu Mar 20 09:05:58 2008 -0700
8.3 @@ -28,26 +28,30 @@
8.4
8.5 id $calltarget( NSInvocation *target, id param )
8.6 {
8.7 + id result = nil;
8.8 if( target && target.target ) {
8.9 - [target setArgument: ¶m atIndex: 2];
8.10 - [target invoke];
8.11 -
8.12 - NSMethodSignature *sig = target.methodSignature;
8.13 - NSUInteger returnLength = sig.methodReturnLength;
8.14 - if( returnLength==0 )
8.15 - return nil; // void
8.16 - else {
8.17 - const char *returnType = sig.methodReturnType;
8.18 - if( returnType[0]=='@' ) {
8.19 - id returnObject;
8.20 - [target getReturnValue: &returnObject];
8.21 - return returnObject;
8.22 + [target retain];
8.23 + @try{
8.24 + [target setArgument: ¶m atIndex: 2];
8.25 + [target invoke];
8.26 +
8.27 + NSMethodSignature *sig = target.methodSignature;
8.28 + NSUInteger returnLength = sig.methodReturnLength;
8.29 + if( returnLength==0 ) {
8.30 + result = nil; // void
8.31 } else {
8.32 - UInt8 returnBuffer[returnLength];
8.33 - [target getReturnValue: &returnBuffer];
8.34 - return [NSValue valueWithBytes: &returnBuffer objCType: returnType];
8.35 + const char *returnType = sig.methodReturnType;
8.36 + if( returnType[0]=='@' ) {
8.37 + [target getReturnValue: &result];
8.38 + } else {
8.39 + UInt8 returnBuffer[returnLength];
8.40 + [target getReturnValue: &returnBuffer];
8.41 + result = [NSValue valueWithBytes: &returnBuffer objCType: returnType];
8.42 + }
8.43 }
8.44 + }@finally{
8.45 + [target release];
8.46 }
8.47 - } else
8.48 - return nil;
8.49 + }
8.50 + return result;
8.51 }
9.1 --- a/Test.h Sat Mar 08 21:04:41 2008 -0800
9.2 +++ b/Test.h Thu Mar 20 09:05:58 2008 -0700
9.3 @@ -17,8 +17,10 @@
9.4 To run only tests without starting the main program, add the argument "Test_Only". */
9.5 #if DEBUG
9.6 void RunTestCases( int argc, const char **argv );
9.7 +extern BOOL gRunningTestCase;
9.8 #else
9.9 #define RunTestCases(ARGC,ARGV)
9.10 +#define gRunningTestCase NO
9.11 #endif
9.12
9.13 /** The TestCase() macro declares a test case.
10.1 --- a/Test.m Sat Mar 08 21:04:41 2008 -0800
10.2 +++ b/Test.m Thu Mar 20 09:05:58 2008 -0700
10.3 @@ -11,11 +11,15 @@
10.4
10.5 #if DEBUG
10.6
10.7 +BOOL gRunningTestCase;
10.8 +
10.9 struct TestCaseLink *gAllTestCases;
10.10 static int sPassed, sFailed;
10.11
10.12 static BOOL RunTestCase( struct TestCaseLink *test )
10.13 {
10.14 + BOOL oldLogging = EnableLog(YES);
10.15 + gRunningTestCase = YES;
10.16 if( test->testptr ) {
10.17 NSAutoreleasePool *pool = [NSAutoreleasePool new];
10.18 Log(@"=== Testing %s ...",test->name);
10.19 @@ -37,6 +41,8 @@
10.20 test->testptr = NULL; // prevents test from being run again
10.21 }
10.22 }
10.23 + gRunningTestCase = NO;
10.24 + EnableLog(oldLogging);
10.25 return test->passed;
10.26 }
10.27
10.28 @@ -63,7 +69,6 @@
10.29
10.30 void RunTestCases( int argc, const char **argv )
10.31 {
10.32 - gShouldLog = YES;
10.33 sPassed = sFailed = 0;
10.34 BOOL stopAfterTests = NO;
10.35 for( int i=1; i<argc; i++ ) {
11.1 --- a/TimeIntervalFormatter.h Sat Mar 08 21:04:41 2008 -0800
11.2 +++ b/TimeIntervalFormatter.h Thu Mar 20 09:05:58 2008 -0700
11.3 @@ -16,4 +16,6 @@
11.4 - (void) setShowsMinutes: (BOOL)showsMinutes;
11.5 - (void) setShowsFractionalSeconds: (BOOL)showsFractionalSeconds;
11.6
11.7 ++ (NSString*) formatTimeInterval: (NSTimeInterval)interval;
11.8 +
11.9 @end
12.1 --- a/TimeIntervalFormatter.m Sat Mar 08 21:04:41 2008 -0800
12.2 +++ b/TimeIntervalFormatter.m Thu Mar 20 09:05:58 2008 -0700
12.3 @@ -11,6 +11,15 @@
12.4 @implementation TimeIntervalFormatter
12.5
12.6
12.7 +- (id) init
12.8 +{
12.9 + self = [super init];
12.10 + if (self != nil) {
12.11 + _showsMinutes = YES;
12.12 + }
12.13 + return self;
12.14 +}
12.15 +
12.16 - (void) awakeFromNib
12.17 {
12.18 _showsMinutes = YES;
12.19 @@ -19,6 +28,14 @@
12.20 - (void) setShowsMinutes: (BOOL)show {_showsMinutes = show;}
12.21 - (void) setShowsFractionalSeconds: (BOOL)show {_showsFractionalSeconds = show;}
12.22
12.23 ++ (NSString*) formatTimeInterval: (NSTimeInterval)interval
12.24 +{
12.25 + TimeIntervalFormatter *fmt = [[self alloc] init];
12.26 + NSString *result = [fmt stringForObjectValue: [NSNumber numberWithDouble: interval]];
12.27 + [fmt release];
12.28 + return result;
12.29 +}
12.30 +
12.31
12.32 - (NSString*) stringForObjectValue: (id)object
12.33 {
13.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
13.2 +++ b/UniqueWindowController.h Thu Mar 20 09:05:58 2008 -0700
13.3 @@ -0,0 +1,25 @@
13.4 +//
13.5 +// UniqueWindowController.h
13.6 +// Cloudy
13.7 +//
13.8 +// Created by Jens Alfke on 3/14/08.
13.9 +// Copyright 2008 __MyCompanyName__. All rights reserved.
13.10 +//
13.11 +
13.12 +#import <Cocoa/Cocoa.h>
13.13 +
13.14 +
13.15 +@interface UniqueWindowController : NSWindowController
13.16 +
13.17 ++ (UniqueWindowController*) instanceWith: (id)model;
13.18 ++ (UniqueWindowController*) openWith: (id)model;
13.19 +
13.20 +@end
13.21 +
13.22 +
13.23 +@interface UniqueWindowController (Abstract)
13.24 +
13.25 +- (id) initWith: (id)model;
13.26 +@property (readonly) id model;
13.27 +
13.28 +@end
13.29 \ No newline at end of file
14.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
14.2 +++ b/UniqueWindowController.m Thu Mar 20 09:05:58 2008 -0700
14.3 @@ -0,0 +1,47 @@
14.4 +//
14.5 +// UniqueWindowController.m
14.6 +// Cloudy
14.7 +//
14.8 +// Created by Jens Alfke on 3/14/08.
14.9 +// Copyright 2008 __MyCompanyName__. All rights reserved.
14.10 +//
14.11 +
14.12 +#import "UniqueWindowController.h"
14.13 +
14.14 +
14.15 +@implementation UniqueWindowController
14.16 +
14.17 +
14.18 ++ (UniqueWindowController*) instanceWith: (id)model
14.19 +{
14.20 + for( NSWindow *window in [NSApp windows] ) {
14.21 + id delegate = window.delegate;
14.22 + if( window.isVisible && [delegate isKindOfClass: [self class]] ) {
14.23 + UniqueWindowController *c = delegate;
14.24 + if( c.model == model )
14.25 + return c;
14.26 + }
14.27 + }
14.28 + return nil;
14.29 +}
14.30 +
14.31 +
14.32 ++ (UniqueWindowController*) openWith: (id)model
14.33 +{
14.34 + UniqueWindowController *w = [self instanceWith: model];
14.35 + if( ! w ) {
14.36 + w = [[self alloc] initWith: model];
14.37 + [w showWindow: self];
14.38 + }
14.39 + [w.window makeKeyAndOrderFront: self];
14.40 + return w;
14.41 +}
14.42 +
14.43 +
14.44 +- (void) windowWillClose: (NSNotification*)n
14.45 +{
14.46 + [self autorelease];
14.47 +}
14.48 +
14.49 +
14.50 +@end