| 1 |
#import "JVRubyChatPlugin.h" |
|---|
| 2 |
#import "JVChatWindowController.h" |
|---|
| 3 |
#import "JVChatMessage.h" |
|---|
| 4 |
#import "JVChatRoomPanel.h" |
|---|
| 5 |
#import "JVChatRoomMember.h" |
|---|
| 6 |
#import "NSStringAdditions.h" |
|---|
| 7 |
|
|---|
| 8 |
NSString *JVRubyErrorDomain = @"JVRubyErrorDomain"; |
|---|
| 9 |
|
|---|
| 10 |
@implementation JVRubyChatPlugin |
|---|
| 11 |
+ (void) initialize { |
|---|
| 12 |
static BOOL tooLate = NO; |
|---|
| 13 |
if( ! tooLate ) { |
|---|
| 14 |
ruby_init(); |
|---|
| 15 |
ruby_init_loadpath(); |
|---|
| 16 |
RBRubyCocoaInit(); |
|---|
| 17 |
tooLate = YES; |
|---|
| 18 |
} |
|---|
| 19 |
} |
|---|
| 20 |
|
|---|
| 21 |
- (id) initWithManager:(MVChatPluginManager *) manager { |
|---|
| 22 |
if( ( self = [self init] ) ) { |
|---|
| 23 |
_manager = manager; |
|---|
| 24 |
_path = nil; |
|---|
| 25 |
_modDate = [[NSDate date] retain]; |
|---|
| 26 |
} |
|---|
| 27 |
|
|---|
| 28 |
return self; |
|---|
| 29 |
} |
|---|
| 30 |
|
|---|
| 31 |
- (id) initWithScriptAtPath:(NSString *) path withManager:(MVChatPluginManager *) manager { |
|---|
| 32 |
if( ( self = [self initWithManager:manager] ) ) { |
|---|
| 33 |
_path = [path copyWithZone:[self zone]]; |
|---|
| 34 |
_firstLoad = YES; |
|---|
| 35 |
|
|---|
| 36 |
[self reloadFromDisk]; |
|---|
| 37 |
|
|---|
| 38 |
_firstLoad = NO; |
|---|
| 39 |
|
|---|
| 40 |
if( ! _script ) { |
|---|
| 41 |
[self release]; |
|---|
| 42 |
return nil; |
|---|
| 43 |
} |
|---|
| 44 |
|
|---|
| 45 |
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( checkForModifications: ) name:NSApplicationWillBecomeActiveNotification object:[NSApplication sharedApplication]]; |
|---|
| 46 |
} |
|---|
| 47 |
|
|---|
| 48 |
return self; |
|---|
| 49 |
} |
|---|
| 50 |
|
|---|
| 51 |
- (void) dealloc { |
|---|
| 52 |
[[NSNotificationCenter defaultCenter] removeObserver:self]; |
|---|
| 53 |
|
|---|
| 54 |
[_script release]; |
|---|
| 55 |
[_path release]; |
|---|
| 56 |
[_modDate release]; |
|---|
| 57 |
|
|---|
| 58 |
_path = nil; |
|---|
| 59 |
_script = nil; |
|---|
| 60 |
_manager = nil; |
|---|
| 61 |
_modDate = nil; |
|---|
| 62 |
|
|---|
| 63 |
[super dealloc]; |
|---|
| 64 |
} |
|---|
| 65 |
|
|---|
| 66 |
#pragma mark - |
|---|
| 67 |
|
|---|
| 68 |
- (MVChatPluginManager *) pluginManager { |
|---|
| 69 |
return _manager; |
|---|
| 70 |
} |
|---|
| 71 |
|
|---|
| 72 |
- (NSString *) scriptFilePath { |
|---|
| 73 |
return _path; |
|---|
| 74 |
} |
|---|
| 75 |
|
|---|
| 76 |
- (void) reloadFromDisk { |
|---|
| 77 |
if( [_script respondsToSelector:@selector( unload )] ) |
|---|
| 78 |
[_script performSelector:@selector( unload )]; |
|---|
| 79 |
|
|---|
| 80 |
NSString *contents = nil; |
|---|
| 81 |
if( floor( NSAppKitVersionNumber ) <= NSAppKitVersionNumber10_3 ) |
|---|
| 82 |
contents = [NSString stringWithContentsOfFile:[self scriptFilePath]]; |
|---|
| 83 |
else contents = [NSString stringWithContentsOfFile:[self scriptFilePath] encoding:NSUTF8StringEncoding error:NULL]; |
|---|
| 84 |
|
|---|
| 85 |
[_script autorelease]; |
|---|
| 86 |
_script = [[RBObject alloc] initWithRubyScriptString:contents]; |
|---|
| 87 |
|
|---|
| 88 |
if( ! _script ) { |
|---|
| 89 |
int result = NSRunCriticalAlertPanel( NSLocalizedStringFromTableInBundle( @"Ruby Script Error", nil, [NSBundle bundleForClass:[self class]], "Ruby script error title" ), NSLocalizedStringFromTableInBundle( @"The Ruby script \"%@\" had an error while loading.", nil, [NSBundle bundleForClass:[self class]], "Ruby script error message" ), nil, NSLocalizedStringFromTableInBundle( @"Edit...", nil, [NSBundle bundleForClass:[self class]], "edit button title" ), nil, [[[self scriptFilePath] lastPathComponent] stringByDeletingPathExtension] ); |
|---|
| 90 |
if( result == NSCancelButton ) [[NSWorkspace sharedWorkspace] openFile:[self scriptFilePath]]; |
|---|
| 91 |
return; |
|---|
| 92 |
} |
|---|
| 93 |
|
|---|
| 94 |
if( ! _firstLoad && [_script respondsToSelector:@selector( loadFromPath: )] ) |
|---|
| 95 |
[_script performSelector:@selector( loadFromPath: ) withObject:[self scriptFilePath]]; |
|---|
| 96 |
} |
|---|
| 97 |
|
|---|
| 98 |
#pragma mark - |
|---|
| 99 |
|
|---|
| 100 |
- (void) promptForReload { |
|---|
| 101 |
if( NSRunInformationalAlertPanel( NSLocalizedStringFromTableInBundle( @"Ruby Script Changed", nil, [NSBundle bundleForClass:[self class]], "Ruby script file changed dialog title" ), NSLocalizedStringFromTableInBundle( @"The Ruby script \"%@\" has changed on disk. Any script variables will reset if reloaded.", nil, [NSBundle bundleForClass:[self class]], "Ruby script changed on disk message" ), NSLocalizedStringFromTableInBundle( @"Reload", nil, [NSBundle bundleForClass:[self class]], "reload button title" ), NSLocalizedStringFromTableInBundle( @"Keep Previous Version", nil, [NSBundle bundleForClass:[self class]], "keep previous version button title" ), nil, [[[self scriptFilePath] lastPathComponent] stringByDeletingPathExtension] ) == NSOKButton ) { |
|---|
| 102 |
[self reloadFromDisk]; |
|---|
| 103 |
} |
|---|
| 104 |
} |
|---|
| 105 |
|
|---|
| 106 |
- (void) checkForModifications:(NSNotification *) notification { |
|---|
| 107 |
if( ! [[self scriptFilePath] length] ) return; |
|---|
| 108 |
|
|---|
| 109 |
NSFileManager *fm = [NSFileManager defaultManager]; |
|---|
| 110 |
if( [fm fileExistsAtPath:[self scriptFilePath]] ) { |
|---|
| 111 |
NSDictionary *info = [fm fileAttributesAtPath:[self scriptFilePath] traverseLink:YES]; |
|---|
| 112 |
NSDate *fileModDate = [info fileModificationDate]; |
|---|
| 113 |
if( [fileModDate compare:_modDate] == NSOrderedDescending && [fileModDate compare:[NSDate date]] == NSOrderedAscending ) { |
|---|
| 114 |
[_modDate autorelease]; |
|---|
| 115 |
_modDate = [[NSDate date] retain]; |
|---|
| 116 |
[self performSelector:@selector( promptForReload ) withObject:nil afterDelay:0.]; |
|---|
| 117 |
} |
|---|
| 118 |
} |
|---|
| 119 |
} |
|---|
| 120 |
|
|---|
| 121 |
#pragma mark - |
|---|
| 122 |
|
|---|
| 123 |
- (BOOL) respondsToSelector:(SEL) selector { |
|---|
| 124 |
if( [_script respondsToSelector:selector] ) return YES; |
|---|
| 125 |
else return [super respondsToSelector:selector]; |
|---|
| 126 |
} |
|---|
| 127 |
|
|---|
| 128 |
- (void) forwardInvocation:(NSInvocation *) invocation { |
|---|
| 129 |
if( [_script respondsToSelector:[invocation selector]] ) |
|---|
| 130 |
[invocation invokeWithTarget:_script]; |
|---|
| 131 |
else [super forwardInvocation:invocation]; |
|---|
| 132 |
} |
|---|
| 133 |
|
|---|
| 134 |
- (NSMethodSignature *) methodSignatureForSelector:(SEL) selector { |
|---|
| 135 |
if( [_script respondsToSelector:selector] ) |
|---|
| 136 |
return [(NSObject *)_script methodSignatureForSelector:selector]; |
|---|
| 137 |
else return [super methodSignatureForSelector:selector]; |
|---|
| 138 |
} |
|---|
| 139 |
|
|---|
| 140 |
#pragma mark - |
|---|
| 141 |
|
|---|
| 142 |
- (void) load { // call loadFromPath instead |
|---|
| 143 |
if( [_script respondsToSelector:@selector( loadFromPath: )] ) |
|---|
| 144 |
[_script performSelector:@selector( loadFromPath: ) withObject:[self scriptFilePath]]; |
|---|
| 145 |
} |
|---|
| 146 |
@end |
|---|