root/tags/2D14/JVChatConsolePanel.m

Revision 2603, 24.5 kB (checked in by eridius, 3 years ago)

Do a little judicious renaming.
And here is a test of a potential Trac bug: whee!
if there is a [[BR]] then the bug is there.
Test two: whee!
Note that there was a space on that one

Line 
1 #import <ChatCore/MVChatConnection.h>
2 #import <ChatCore/MVChatPluginManager.h>
3 #import <ChatCore/NSMethodSignatureAdditions.h>
4
5 #import "JVChatConsolePanel.h"
6 #import "JVChatController.h"
7 #import "MVTextView.h"
8 #import "JVSplitView.h"
9
10 static NSString *JVToolbarToggleVerboseItemIdentifier = @"JVToolbarToggleVerboseItem";
11 static NSString *JVToolbarTogglePrivateMessagesItemIdentifier = @"JVToolbarTogglePrivateMessagesItem";
12 static NSString *JVToolbarClearItemIdentifier = @"JVToolbarClearItem";
13
14 @implementation JVChatConsolePanel
15 - (id) initWithConnection:(MVChatConnection *) connection {
16         if( ( self = [self init] ) ) {
17                 _sendHistory = [[NSMutableArray array] retain];
18                 [_sendHistory insertObject:[[[NSAttributedString alloc] initWithString:@""] autorelease] atIndex:0];
19
20                 _historyIndex = 0;
21                 _paused = NO;
22                 _forceSplitViewPosition = YES;
23
24                 _connection = [connection retain];
25                 _verbose = [[NSUserDefaults standardUserDefaults] boolForKey:@"JVChatVerboseConsoleMessages"];
26                 _ignorePRIVMSG = [[NSUserDefaults standardUserDefaults] boolForKey:@"JVChatConsolePanelIgnoreUserChatMessages"];
27
28                 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _gotRawMessage: ) name:MVChatConnectionGotRawMessageNotification object:connection];
29                 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( clearConsole: ) name:MVChatConnectionWillConnectNotification object:connection];
30
31                 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _refreshIcon: ) name:MVChatConnectionDidConnectNotification object:connection];
32                 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _refreshIcon: ) name:MVChatConnectionDidDisconnectNotification object:connection];
33         }
34         return self;
35 }
36
37 - (void) awakeFromNib {
38         [send setUsesSystemCompleteOnTab:[[NSUserDefaults standardUserDefaults] boolForKey:@"JVUsePantherTextCompleteOnTab"]];
39         [send setContinuousSpellCheckingEnabled:NO];
40         [send setUsesFontPanel:NO];
41         [send setUsesRuler:NO];
42         [send setAllowsUndo:YES];
43         [send setImportsGraphics:NO];
44         [send setUsesFindPanel:NO];
45         [send setUsesFontPanel:NO];
46         [send reset:nil];
47
48         [display setEditable:NO];
49         [display setContinuousSpellCheckingEnabled:NO];
50         [display setUsesFontPanel:NO];
51         [display setUsesRuler:NO];
52         [display setImportsGraphics:NO];
53
54         if( [[NSUserDefaults standardUserDefaults] boolForKey:@"JVChatInputAutoResizes"] )
55                 [(JVSplitView *)[[[send superview] superview] superview] setIsPaneSplitter:YES];
56 }
57
58 - (void) dealloc {
59         [[NSNotificationCenter defaultCenter] removeObserver:self];
60
61         [contents release];
62         [_sendHistory release];
63         [_connection release];
64
65         contents = nil;
66         _connection = nil;
67         _sendHistory = nil;
68         _windowController = nil;
69
70         [super dealloc];
71 }
72
73 - (NSString *) description {
74         return [NSString stringWithFormat:@"%@: %@", NSStringFromClass( [self class] ), [[self connection] server]];
75 }
76
77 #pragma mark -
78
79 - (IBAction) clearConsole:(id) sender {
80         [display setString:@""];
81 }
82
83 - (IBAction) toggleVerbose:(id) sender {
84         _verbose = ! _verbose;
85 }
86
87 - (IBAction) toggleMessages:(id) sender {
88         _ignorePRIVMSG = ! _ignorePRIVMSG;
89 }
90
91 - (IBAction) close:(id) sender {
92         [[JVChatController defaultController] disposeViewController:self];
93 }
94
95 #pragma mark -
96
97 - (JVChatWindowController *) windowController {
98         return [[_windowController retain] autorelease];
99 }
100
101 - (void) setWindowController:(JVChatWindowController *) controller {
102         _windowController = controller;
103 }
104
105 #pragma mark -
106
107 - (NSView *) view {
108         if( ! _nibLoaded ) _nibLoaded = [NSBundle loadNibNamed:@"JVChatConsole" owner:self];
109         return contents;
110 }
111
112 - (NSResponder *) firstResponder {
113         return send;
114 }
115
116 - (NSToolbar *) toolbar {
117         NSToolbar *toolbar = [[NSToolbar alloc] initWithIdentifier:@"Console"];
118         [toolbar setDelegate:self];
119         [toolbar setAllowsUserCustomization:YES];
120         [toolbar setAutosavesConfiguration:YES];
121         return [toolbar autorelease];
122 }
123
124 #pragma mark -
125
126 - (BOOL) isEnabled {
127         return ! ( ! [[self connection] isConnected] && [[self connection] status] != MVChatConnectionConnectingStatus );
128 }
129
130 - (NSString *) title {
131         return [[self connection] server];
132 }
133
134 - (NSString *) windowTitle {
135         return [NSString stringWithFormat:NSLocalizedString( @"%@ - Console", "chat console - window title" ), [[self connection] server]];
136 }
137
138 - (NSString *) information {
139         if( ! [[self connection] isConnected] && [[self connection] status] != MVChatConnectionConnectingStatus )
140                 return NSLocalizedString( @"disconnected", "disconnected status info line in drawer" );
141         return nil;
142 }
143
144 - (NSString *) toolTip {
145         if( ! [[self connection] lag] ) return [self title];
146         return [NSString stringWithFormat:NSLocalizedString( @"%@\n%.3f seconds lag", "console tooltip witg lag (server delay) info in seconds" ), [self title], [[self connection] lag] / 1000.];
147 }
148
149 #pragma mark -
150
151 - (id <JVChatListItem>) parent {
152         return nil;
153 }
154
155 - (NSArray *) children {
156         return nil;
157 }
158
159 #pragma mark -
160
161 - (NSMenu *) menu {
162         NSMenu *menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
163         NSMenuItem *item = nil;
164
165         item = [[[NSMenuItem alloc] initWithTitle:NSLocalizedString( @"Get Info", "get info contextual menu item title" ) action:@selector( getInfo: ) keyEquivalent:@""] autorelease];
166         [item setTarget:_windowController];
167         [menu addItem:item];
168
169         item = [[[NSMenuItem alloc] initWithTitle:NSLocalizedString( @"Clear Display", "clear console contextual menu item title" ) action:@selector( clearConsole: ) keyEquivalent:@""] autorelease];
170         [item setTarget:self];
171         [menu addItem:item];
172
173         [menu addItem:[NSMenuItem separatorItem]];
174
175         item = [[[NSMenuItem alloc] initWithTitle:NSLocalizedString( @"Detach From Window", "detach from window contextual menu item title" ) action:@selector( detachView: ) keyEquivalent:@""] autorelease];
176         [item setRepresentedObject:self];
177         [item setTarget:[JVChatController defaultController]];
178         [menu addItem:item];
179
180         item = [[[NSMenuItem alloc] initWithTitle:NSLocalizedString( @"Close", "close contextual menu item title" ) action:@selector( close: ) keyEquivalent:@""] autorelease];
181         [item setTarget:self];
182         [menu addItem:item];
183
184         return [[menu retain] autorelease];
185 }
186
187 - (NSImage *) icon {
188         return [NSImage imageNamed:@"console"];
189 }
190
191 #pragma mark -
192
193 - (NSString *) identifier {
194         return [NSString stringWithFormat:@"Console %@", [[self connection] server]];
195 }
196
197 - (MVChatConnection *) connection {
198         return _connection;
199 }
200
201 #pragma mark -
202
203 - (void) willSelect {
204         if( ! [[NSUserDefaults standardUserDefaults] boolForKey:@"JVChatInputAutoResizes"] ) {
205                 [(JVSplitView *)[[[send superview] superview] superview] setPositionUsingName:@"JVChatSplitViewPosition"];
206         } else [self textDidChange:nil];
207 }
208
209 #pragma mark -
210
211 - (void) pause {
212         if( [[self connection] type] == MVChatConnectionIRCType )
213                 _paused = YES;
214 }
215
216 - (void) resume {
217         _paused = NO;
218 }
219
220 - (BOOL) isPaused {
221         return _paused;
222 }
223
224 - (void) addMessageToDisplay:(NSString *) message asOutboundMessage:(BOOL) outbound {
225         NSAttributedString *msg = nil;
226         NSMutableString *strMsg = [[message mutableCopy] autorelease];
227         NSMutableDictionary *attrs = [NSMutableDictionary dictionary];
228         NSMutableParagraphStyle *para = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] autorelease];
229         unsigned int numeric = 0;
230
231         if( [[self connection] type] == MVChatConnectionIRCType ) {
232                 if( ! [strMsg length] || ( _ignorePRIVMSG && [strMsg rangeOfString:@"PRIVMSG"].location != NSNotFound && [strMsg rangeOfString:@"\001"].location == NSNotFound ) )
233                         return;
234
235                 if( ! _verbose ) {
236                         [strMsg replaceOccurrencesOfString:@":" withString:@"" options:NSAnchoredSearch range:NSMakeRange( 0, 1 )];
237                         [strMsg replaceOccurrencesOfString:@" :" withString:@" " options:NSLiteralSearch range:NSMakeRange( 0, [strMsg length] )];
238                 }
239
240                 [strMsg replaceOccurrencesOfString:@"\r\n" withString:@"" options:NSAnchoredSearch | NSBackwardsSearch range:NSMakeRange( 0, [strMsg length] )];
241
242                 if( ! outbound && ! _verbose ) {
243                         NSMutableArray *parts = [[[strMsg componentsSeparatedByString:@" "] mutableCopy] autorelease];
244                         NSString *tempStr = nil;
245                         unsigned i = 0, c = 0;
246
247                         if( [parts count] >= 3 && [[parts objectAtIndex:2] isEqualToString:[[self connection] nickname]] )
248                                 [parts removeObjectAtIndex:2];
249
250                         if( [parts count] >= 2 && ( numeric = [[parts objectAtIndex:1] intValue] ) )
251                                 [parts removeObjectAtIndex:1];
252                         else if( [parts count] >= 2 && ( [[parts objectAtIndex:1] isEqualToString:@"PRIVMSG"] && [strMsg rangeOfString:@"\001"].location != NSNotFound && [strMsg rangeOfString:@" ACTION"].location == NSNotFound ) )
253                                 [parts replaceObjectAtIndex:1 withObject:@"CTCP REQUEST"];
254                         else if( [parts count] >= 2 && ( [[parts objectAtIndex:1] isEqualToString:@"NOTICE"] && [strMsg rangeOfString:@"\001"].location != NSNotFound ) )
255                                 [parts replaceObjectAtIndex:1 withObject:@"CTCP REPLY"];
256
257                         tempStr = [parts objectAtIndex:0];
258                         if( tempStr && [tempStr rangeOfString:@"@"].location == NSNotFound && [tempStr rangeOfString:@"."].location != NSNotFound && [NSURL URLWithString:[@"irc://" stringByAppendingString:tempStr]] ) {
259                                 [parts removeObjectAtIndex:0];
260                         } else if( [tempStr hasPrefix:[NSString stringWithFormat:@"%@!", [[self connection] nickname]]] ) {
261                                 [parts removeObjectAtIndex:0];
262                         }
263
264                         for( i = 0, c = [parts count]; i < c; i++ ) {
265                                 tempStr = [@"irc://" stringByAppendingString:[parts objectAtIndex:i]];
266                                 if( ( tempStr = [[NSURL URLWithString:tempStr] user] ) && [tempStr rangeOfString:@"!"].location != NSNotFound )
267                                         [parts replaceObjectAtIndex:i withObject:[tempStr substringToIndex:[tempStr rangeOfString:@"!"].location]];
268                         }
269
270                         strMsg = (NSMutableString *) [parts componentsJoinedByString:@" "];
271                         if( numeric ) strMsg = [NSString stringWithFormat:@"%03u: %@", numeric, strMsg];
272                 } else if( outbound && ! _verbose ) {
273                         if( [strMsg rangeOfString:@"NOTICE"].location != NSNotFound && [strMsg rangeOfString:@"\001"].location != NSNotFound ) {
274                                 [strMsg replaceOccurrencesOfString:@"NOTICE" withString:@"CTCP REPLY" options:NSAnchoredSearch range:NSMakeRange( 0, 6 )];
275                         } else if( [strMsg rangeOfString:@"PRIVMSG"].location != NSNotFound && [strMsg rangeOfString:@"\001"].location != NSNotFound && [strMsg rangeOfString:@" ACTION"].location == NSNotFound ) {
276                                 [strMsg replaceOccurrencesOfString:@"PRIVMSG" withString:@"CTCP REQUEST" options:NSAnchoredSearch range:NSMakeRange( 0, 7 )];
277                         }
278                 }
279         }
280
281         [para setParagraphSpacing:3.];
282         [para setMaximumLineHeight:9.];
283         if( ! outbound ) [para setMaximumLineHeight:9.];
284         else [para setMaximumLineHeight:11.];
285
286         if( outbound ) [attrs setObject:[NSFont boldSystemFontOfSize:11.] forKey:NSFontAttributeName];
287         else [attrs setObject:[[NSFontManager sharedFontManager] fontWithFamily:@"Monaco" traits:0 weight:5 size:9.] forKey:NSFontAttributeName];
288         [attrs setObject:para forKey:NSParagraphStyleAttributeName];
289
290         msg = [[[NSAttributedString alloc] initWithString:strMsg attributes:attrs] autorelease];
291         [[display textStorage] beginEditing];
292         if( [[display textStorage] length] )
293                 [display replaceCharactersInRange:NSMakeRange( [[display textStorage] length], 0 ) withString:@"\n"];
294         [[display textStorage] appendAttributedString:msg];
295         [[display textStorage] endEditing];
296         if( NSMinY( [display visibleRect] ) >= ( NSHeight( [display bounds] ) - ( NSHeight( [display visibleRect] ) * 1.1 ) ) )
297                 [display scrollRangeToVisible:NSMakeRange( [[display textStorage] length], 0 )];
298 }
299
300 #pragma mark -
301
302 - (void) send:(id) sender {
303         NSMutableAttributedString *subMsg = nil;
304         NSRange range;
305
306         if( ! [[self connection] isConnected] ) return;
307
308         [self resume];
309
310         _historyIndex = 0;
311         if( ! [[send textStorage] length] ) return;
312         if( [_sendHistory count] )
313                 [_sendHistory replaceObjectAtIndex:0 withObject:[[[NSAttributedString alloc] initWithString:@""] autorelease]];
314         [_sendHistory insertObject:[[[send textStorage] copy] autorelease] atIndex:1];
315         if( [_sendHistory count] > [[[NSUserDefaults standardUserDefaults] objectForKey:@"JVChatMaximumHistory"] unsignedIntValue] )
316                 [_sendHistory removeObjectAtIndex:[_sendHistory count] - 1];
317
318         while( [[send textStorage] length] ) {
319                 range = [[[send textStorage] string] rangeOfString:@"\n"];
320                 if( ! range.length ) range.location = [[send textStorage] length];
321                 subMsg = [[[[send textStorage] attributedSubstringFromRange:NSMakeRange( 0, range.location )] mutableCopy] autorelease];
322
323                 if( ( [subMsg length] >= 1 && range.length ) || ( [subMsg length] && ! range.length ) ) {
324                         if( [[subMsg string] hasPrefix:@"/"] ) {
325                                 NSScanner *scanner = [NSScanner scannerWithString:[subMsg string]];
326                                 NSString *command = nil;
327                                 NSAttributedString *arguments = nil;
328
329                                 [scanner scanString:@"/" intoString:nil];
330                                 [scanner scanUpToCharactersFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet] intoString:&command];
331                                 if( [[subMsg string] length] >= [scanner scanLocation] + 1 )
332                                         [scanner setScanLocation:[scanner scanLocation] + 1];
333
334                                 arguments = [subMsg attributedSubstringFromRange:NSMakeRange( [scanner scanLocation], range.location - [scanner scanLocation] )];
335
336                                 NSMethodSignature *signature = [NSMethodSignature methodSignatureWithReturnAndArgumentTypes:@encode( BOOL ), @encode( NSString * ), @encode( NSAttributedString * ), @encode( MVChatConnection * ), @encode( id ), nil];
337                                 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
338
339                                 [invocation setSelector:@selector( processUserCommand:withArguments:toConnection:inView: )];
340                                 [invocation setArgument:&command atIndex:2];
341                                 [invocation setArgument:&arguments atIndex:3];
342                                 [invocation setArgument:&_connection atIndex:4];
343                                 [invocation setArgument:&self atIndex:5];
344
345                                 NSArray *results = [[MVChatPluginManager defaultManager] makePluginsPerformInvocation:invocation stoppingOnFirstSuccessfulReturn:YES];
346
347                                 if( ! [[results lastObject] boolValue] )
348                                         [[self connection] sendRawMessage:[command stringByAppendingFormat:@" %@", [arguments string]]];
349                         } else {
350                                 [[self connection] sendRawMessage:[subMsg string]];
351                         }
352                 }
353                 if( range.length ) range.location++;
354                 [[send textStorage] deleteCharactersInRange:NSMakeRange( 0, range.location )];
355         }
356
357         [send reset:nil];
358         [self textDidChange:nil];
359         [display scrollRangeToVisible:NSMakeRange( [[display textStorage] length], 0 )];
360 }
361
362 - (BOOL) textView:(NSTextView *) textView enterKeyPressed:(NSEvent *) event {
363         [self send:nil];
364         return YES;
365 }
366
367 - (BOOL) textView:(NSTextView *) textView returnKeyPressed:(NSEvent *) event {
368         [self send:nil];
369         return YES;
370 }
371
372 - (BOOL) upArrowKeyPressed {
373         if( ! _historyIndex && [_sendHistory count] )
374                 [_sendHistory replaceObjectAtIndex:0 withObject:[[[send textStorage] copy] autorelease]];
375         _historyIndex++;
376         if( _historyIndex >= [_sendHistory count] ) {
377                 _historyIndex = [_sendHistory count] - 1;
378                 if( (signed) _historyIndex < 0 ) _historyIndex = 0;
379                 return YES;
380         }
381         [send reset:nil];
382         [[send textStorage] insertAttributedString:[_sendHistory objectAtIndex:_historyIndex] atIndex:0];
383         return YES;
384 }
385
386 - (BOOL) downArrowKeyPressed {
387         if( ! _historyIndex && [_sendHistory count] )
388                 [_sendHistory replaceObjectAtIndex:0 withObject:[[[send textStorage] copy] autorelease]];
389         if( [[send textStorage] length] ) _historyIndex--;
390         if( _historyIndex < 0 ) {
391                 [send reset:nil];
392                 _historyIndex = -1;
393                 return YES;
394         } else if( ! [_sendHistory count] ) {
395                 _historyIndex = 0;
396                 return YES;
397         }
398         [send reset:nil];
399         [[send textStorage] insertAttributedString:[_sendHistory objectAtIndex:_historyIndex] atIndex:0];
400         return YES;
401 }
402
403 - (BOOL) textView:(NSTextView *) textView functionKeyPressed:(NSEvent *) event {
404         unichar chr = 0;
405
406         if( [[event charactersIgnoringModifiers] length] ) {
407                 chr = [[event charactersIgnoringModifiers] characterAtIndex:0];
408         } else return NO;
409
410         // exclude device-dependent flags, caps-lock and fn key (necessary for pg up/pg dn/home/end on portables)
411         if( [event modifierFlags] & ~( NSFunctionKeyMask | NSNumericPadKeyMask | NSAlphaShiftKeyMask | 0xffff ) ) return NO;
412
413         BOOL usesOnlyArrows = [[NSUserDefaults standardUserDefaults] boolForKey:@"JVSendHistoryUsesOnlyArrows"];
414        
415         if( chr == NSUpArrowFunctionKey && ( usesOnlyArrows || [event modifierFlags] & NSAlternateKeyMask ) ) {
416                 return [self upArrowKeyPressed];
417         } else if( chr == NSDownArrowFunctionKey && ( usesOnlyArrows || [event modifierFlags] & NSAlternateKeyMask ) ) {
418                 return [self downArrowKeyPressed];
419         }
420
421         return NO;
422 }
423
424 - (NSArray *) completionsFor:(NSString *) inFragment {
425         return nil;
426 }
427
428 - (BOOL) textView:(NSTextView *) textView escapeKeyPressed:(NSEvent *) event {
429         [send reset:nil];
430         return YES;
431 }
432
433 - (void) textDidChange:(NSNotification *) notification {
434         _historyIndex = 0;
435
436         if( ! [[NSUserDefaults standardUserDefaults] boolForKey:@"JVChatInputAutoResizes"] )
437                 return;
438
439         // We need to resize the textview to fit the content.
440         // The scroll views are two superviews up: NSTextView -> NSClipView -> NSScrollView
441         NSSplitView *splitView = (NSSplitView *)[[[send superview] superview] superview];
442         NSRect splitViewFrame = [splitView frame];
443         NSSize contentSize = [send minimumSizeForContent];
444         NSRect sendFrame = [[[send superview] superview] frame];
445         float dividerThickness = [splitView dividerThickness];
446         float maxContentHeight = ( NSHeight( splitViewFrame ) - dividerThickness - 75. );
447         float newContentHeight =  MIN( maxContentHeight, MAX( 25., contentSize.height + 8. ) );
448
449         if( newContentHeight == NSHeight( sendFrame ) ) return;
450
451         NSRect displayFrame = [[[display superview] superview] frame];
452
453         // Set size of the web view to the maximum size possible
454         displayFrame.size.height = NSHeight( splitViewFrame ) - dividerThickness - newContentHeight;
455         displayFrame.origin = NSMakePoint( 0., 0. );
456
457         // Keep the send box the same size
458         sendFrame.size.height = newContentHeight;
459         sendFrame.origin.y = NSHeight( displayFrame ) + dividerThickness;
460
461         [[display window] disableFlushWindow]; // prevent any draw (white) flashing that might occur
462
463         NSScroller *scroller = [(NSScrollView *)[[display superview] superview] verticalScroller];
464         if( ! scroller || [scroller floatValue] == 1. ) _scrollerIsAtBottom = YES;
465         else _scrollerIsAtBottom = NO;
466
467         // Commit the changes
468         [[[send superview] superview] setFrame:sendFrame];
469         [[[display superview] superview] setFrame:displayFrame];
470
471         if( _scrollerIsAtBottom )
472                 [display scrollRangeToVisible:NSMakeRange( [[display textStorage] length], 0 )];
473
474         [display displayIfNeeded]; // makes the WebView draw correctly
475         [splitView setNeedsDisplay:YES]; // makes the divider redraw correctly later
476         [[display window] enableFlushWindow]; // flush everything we have drawn
477 }
478
479 #pragma mark -
480 #pragma mark Scripting Support
481
482 - (NSNumber *) uniqueIdentifier {
483         return [NSNumber numberWithUnsignedInt:(unsigned long) self];
484 }
485
486 - (NSWindow *) window {
487         return [[self windowController] window];
488 }
489
490 - (id) valueForUndefinedKey:(NSString *) key {
491         if( [NSScriptCommand currentCommand] ) {
492                 [[NSScriptCommand currentCommand] setScriptErrorNumber:1000];
493                 [[NSScriptCommand currentCommand] setScriptErrorString:[NSString stringWithFormat:@"The panel id %@ doesn't have the \"%@\" property.", [self uniqueIdentifier], key]];
494                 return nil;
495         }
496
497         return [super valueForUndefinedKey:key];
498 }
499
500 - (void) setValue:(id) value forUndefinedKey:(NSString *) key {
501         if( [NSScriptCommand currentCommand] ) {
502                 [[NSScriptCommand currentCommand] setScriptErrorNumber:1000];
503                 [[NSScriptCommand currentCommand] setScriptErrorString:[NSString stringWithFormat:@"The \"%@\" property of panel id %@ is read only.", key, [self uniqueIdentifier]]];
504                 return;
505         }
506
507         [super setValue:value forUndefinedKey:key];
508 }
509
510 #pragma mark -
511 #pragma mark Toolbar Support
512
513 - (NSToolbarItem *) toolbar:(NSToolbar *) toolbar itemForItemIdentifier:(NSString *) identifier willBeInsertedIntoToolbar:(BOOL) willBeInserted {
514         NSToolbarItem *toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier:identifier] autorelease];
515         if( [identifier isEqual:JVToolbarToggleChatDrawerItemIdentifier] ) {
516                 toolbarItem = [_windowController toggleChatDrawerToolbarItem];
517         } else if( [identifier isEqual:JVToolbarClearItemIdentifier] ) {
518                 [toolbarItem setLabel:NSLocalizedString( @"Clear", "clear console toolbar button name" )];
519                 [toolbarItem setPaletteLabel:NSLocalizedString( @"Clear Console", "clear console toolbar customize palette name" )];
520
521                 [toolbarItem setToolTip:NSLocalizedString( @"Clear Console", "clear console tooltip" )];
522                 [toolbarItem setImage:[NSImage imageNamed:@"clear"]];
523
524                 [toolbarItem setTarget:self];
525                 [toolbarItem setAction:@selector( clearConsole: )];
526         } else if( [identifier isEqual:JVToolbarToggleVerboseItemIdentifier] ) {
527                 [toolbarItem setLabel:NSLocalizedString( @"Verbose", "verbose toolbar button name" )];
528                 [toolbarItem setPaletteLabel:NSLocalizedString( @"Toggle Verbose", "toggle verbose toolbar customize palette name" )];
529
530                 [toolbarItem setToolTip:NSLocalizedString( @"Toggle Verbose Output", "toggle verbose output tooltip" )];
531                 [toolbarItem setImage:[NSImage imageNamed:@"reveal"]];
532
533                 [toolbarItem setTarget:self];
534                 [toolbarItem setAction:@selector( toggleVerbose: )];
535         } else if( [identifier isEqual:JVToolbarTogglePrivateMessagesItemIdentifier] ) {
536                 [toolbarItem setLabel:NSLocalizedString( @"Messages", "toggle private messages toolbar button name" )];
537                 [toolbarItem setPaletteLabel:NSLocalizedString( @"Toggle Messages", "toggle private messages toolbar customize palette name" )];
538
539                 [toolbarItem setToolTip:NSLocalizedString( @"Toggle Private Messages Output", "toggle private messages output tooltip" )];
540                 [toolbarItem setImage:[NSImage imageNamed:@"room"]];
541
542                 [toolbarItem setTarget:self];
543                 [toolbarItem setAction:@selector( toggleMessages: )];
544         } else toolbarItem = nil;
545         return toolbarItem;
546 }
547
548 - (NSArray *) toolbarDefaultItemIdentifiers:(NSToolbar *) toolbar {
549         NSArray *list = [NSArray arrayWithObjects:JVToolbarToggleChatDrawerItemIdentifier, JVToolbarClearItemIdentifier, nil];
550         return [[list retain] autorelease];
551 }
552
553 - (NSArray *) toolbarAllowedItemIdentifiers:(NSToolbar *) toolbar {
554         NSArray *list = [NSArray arrayWithObjects:JVToolbarToggleChatDrawerItemIdentifier, JVToolbarToggleVerboseItemIdentifier, JVToolbarTogglePrivateMessagesItemIdentifier, JVToolbarClearItemIdentifier, NSToolbarCustomizeToolbarItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier, NSToolbarSpaceItemIdentifier, NSToolbarSeparatorItemIdentifier, nil];
555         return [[list retain] autorelease];
556 }
557
558 #pragma mark -
559 #pragma mark SplitView Support
560
561 - (float) splitView:(NSSplitView *) splitView constrainSplitPosition:(float) proposedPosition ofSubviewAt:(int) index {
562         if( [[NSUserDefaults standardUserDefaults] boolForKey:@"JVChatInputAutoResizes"] )
563                 return ( NSHeight( [[[splitView subviews] objectAtIndex:index] frame] ) ); // prevents manual resize
564         return proposedPosition;
565 }
566
567 - (void) splitViewDidResizeSubviews:(NSNotification *) notification {
568         // Cache the height of the send box so we can keep it constant during window resizes.
569         NSRect sendFrame = [[[send superview] superview] frame];
570         _sendHeight = sendFrame.size.height;
571
572         if( _scrollerIsAtBottom )
573                 [display scrollRangeToVisible:NSMakeRange( [[display textStorage] length], 0 )];
574
575         if( ! _forceSplitViewPosition && ! [[NSUserDefaults standardUserDefaults] boolForKey:@"JVChatInputAutoResizes"] )
576                 [(JVSplitView *)[notification object] savePositionUsingName:@"JVChatSplitViewPosition"];
577
578         _forceSplitViewPosition = NO;
579 }
580
581 - (void) splitViewWillResizeSubviews:(NSNotification *) notification {
582         NSScroller *scroller = [(NSScrollView *)[[display superview] superview] verticalScroller];
583         if( ! scroller || [scroller floatValue] == 1. ) _scrollerIsAtBottom = YES;
584         else _scrollerIsAtBottom = NO;
585 }
586
587 - (void) splitView:(NSSplitView *) sender resizeSubviewsWithOldSize:(NSSize) oldSize {
588         float dividerThickness = [sender dividerThickness];
589         NSRect newFrame = [sender frame];
590
591         // Keep the size of the send box constant during window resizes
592
593         // We need to resize the scroll view frames of the webview and the textview.
594         // The scroll views are two superviews up: NSTextView(WebView) -> NSClipView -> NSScrollView
595         NSRect sendFrame = [[[send superview] superview] frame];
596         NSRect displayFrame = [[[display superview] superview] frame];
597
598         // Set size of the web view to the maximum size possible
599         displayFrame.size.height = NSHeight( newFrame ) - dividerThickness - _sendHeight;
600         displayFrame.size.width = NSWidth( newFrame );
601         displayFrame.origin = NSMakePoint( 0., 0. );
602
603         // Keep the send box the same size
604         sendFrame.size.height = _sendHeight;
605         sendFrame.size.width = NSWidth( newFrame );
606         sendFrame.origin.y = NSHeight( displayFrame ) + dividerThickness;
607
608         // Commit the changes
609         [[[send superview] superview] setFrame:sendFrame];
610         [[[display superview] superview] setFrame:displayFrame];
611 }
612 @end
613
614 #pragma mark -
615
616 @implementation JVChatConsolePanel (JVChatConsolePanelPrivate)
617 - (void) _gotRawMessage:(NSNotification *) notification {
618         if( _paused ) return;
619         [self addMessageToDisplay:[[notification userInfo] objectForKey:@"message"] asOutboundMessage:[[[notification userInfo] objectForKey:@"outbound"] boolValue]];
620 }
621
622 - (void) _refreshIcon:(NSNotification *) notification {
623         [_windowController reloadListItem:self andChildren:NO];
624 }
625 @end
Note: See TracBrowser for help on using the browser.