root/trunk/lilypad/Sources/LPXmlConsoleController.m @ 254

Revision 254, 11.4 KB (checked in by jppavao, 5 years ago)

Sending an empty string in the XML input sheet of the XML console is no longer allowed. Fixes #213.

Line 
1//
2//  LPXmlConsoleController.m
3//  Lilypad
4//
5//      Copyright (C) 2006-2008 PT.COM,  All rights reserved.
6//      Authors: Joao Pavao <jpavao@co.sapo.pt>
7//           Jason Kim <jason@512k.org>
8//
9//      For more information on licensing, read the README file.
10//      Para mais informações sobre o licenciamento, leia o ficheiro README.
11//
12
13#import "LPXmlConsoleController.h"
14#import "NSAttributedString+FactoryAdditions.h"
15#import "LPAccount.h"
16
17
18@implementation LPXmlConsoleController
19
20
21- initWithAccount:(LPAccount *)account
22{
23        if (self = [self initWithWindowNibName:@"XMLConsole"]) {
24                m_account = [account retain];
25                m_recentXMLStanzasBuffer = [[NSMutableArray alloc] init];
26                m_checkXML = YES;
27               
28                [[NSNotificationCenter defaultCenter] addObserver:self
29                                                                                                 selector:@selector(accountDidSendOrReceiveXMLString:)
30                                                                                                         name:LPAccountDidReceiveXMLStringNotification
31                                                                                                   object:account];
32                [[NSNotificationCenter defaultCenter] addObserver:self
33                                                                                                 selector:@selector(accountDidSendOrReceiveXMLString:)
34                                                                                                         name:LPAccountDidSendXMLStringNotification
35                                                                                                   object:account];
36        }
37        return self;
38}
39
40
41- (void)dealloc
42{
43        [[NSNotificationCenter defaultCenter] removeObserver:self];
44       
45        [m_recentXMLStanzasBuffer release];
46        [m_account release];
47        [super dealloc];
48}
49
50
51- (void)windowDidLoad
52{
53        [[self window] setTitle:[NSString stringWithFormat:@"XML Console for Account \"%@\" (%@)",
54                                                         [m_account description], [m_account JID]]];
55       
56        // Set up the toolbar
57        NSToolbar *tb = [[NSToolbar alloc] initWithIdentifier:@"XMLConsoleToolbar"];
58        [tb setDelegate:self];
59        [tb setAllowsUserCustomization:YES];
60        [tb setAutosavesConfiguration:YES];
61        [[self window] setToolbar:tb];
62        [tb release];
63       
64        // Set up some state.
65        [self setWindowFrameAutosaveName:@"LPXmlConsoleWindow"];
66}
67
68
69#pragma mark -
70#pragma mark Toolbar Delegate Methods
71
72
73- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar
74{
75        return [NSArray arrayWithObjects:
76                        @"XMLConsoleToolbarItemEnabled",
77                        NSToolbarSeparatorItemIdentifier,
78                        @"XMLConsoleToolbarItemSendXML",
79                        NSToolbarFlexibleSpaceItemIdentifier,
80                        @"XMLConsoleToolbarItemSave",
81                        NSToolbarSeparatorItemIdentifier,
82                        @"XMLConsoleToolbarItemClear",
83                        nil];
84}
85
86
87- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar
88{
89        return [NSArray arrayWithObjects:
90                        @"XMLConsoleToolbarItemEnabled",
91                        @"XMLConsoleToolbarItemSendXML",
92                        @"XMLConsoleToolbarItemSave",
93                        @"XMLConsoleToolbarItemClear",
94                        NSToolbarCustomizeToolbarItemIdentifier,
95                        NSToolbarFlexibleSpaceItemIdentifier,
96                        NSToolbarSeparatorItemIdentifier,
97                        NSToolbarSpaceItemIdentifier,
98                        nil];
99}
100
101
102- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)identifier willBeInsertedIntoToolbar:(BOOL)willBeInserted 
103{
104        // Create our toolbar items.
105    NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:identifier];
106       
107        if ([identifier isEqualToString:@"XMLConsoleToolbarItemEnabled"])
108        {
109                [item setPaletteLabel:NSLocalizedString(@"Enable/Disable Logging", @"toolbar button label")];
110                [item setView:m_enableCheckbox];
111                [item setMinSize:[m_enableCheckbox frame].size];
112                [item setMaxSize:[m_enableCheckbox frame].size];
113                [item setToolTip:NSLocalizedString(@"Enable the logging of sent and received XML messages.", @"toolbar button")];
114        }
115        else if ([identifier isEqualToString:@"XMLConsoleToolbarItemClear"])
116        {
117                [item setLabel:NSLocalizedString(@"Clear", @"toolbar button label")];
118                [item setPaletteLabel:NSLocalizedString(@"Clear Console", @"toolbar button label")];
119                [item setImage:[NSImage imageNamed:@"clear_log.tiff"]];
120                [item setToolTip:NSLocalizedString(@"Clear the console log.", @"toolbar button")];
121                [item setAction:@selector(clear:)];
122                [item setTarget:self];
123        }
124        else if ([identifier isEqualToString:@"XMLConsoleToolbarItemSave"])
125        {
126                [item setLabel:NSLocalizedString(@"Save to File", @"toolbar button label")];
127                [item setPaletteLabel:NSLocalizedString(@"Save to File", @"toolbar button label")];
128                [item setImage:[NSImage imageNamed:@"save_log.icns"]];
129                [item setToolTip:NSLocalizedString(@"Save the console log to a file.", @"toolbar button")];
130                [item setAction:@selector(save:)];
131                [item setTarget:self];
132        }
133        else if ([identifier isEqualToString:@"XMLConsoleToolbarItemSendXML"])
134        {
135                [item setLabel:NSLocalizedString(@"Send XML", @"toolbar button label")];
136                [item setPaletteLabel:NSLocalizedString(@"Send XML", @"toolbar button label")];
137                [item setImage:[NSImage imageNamed:@"send_xml.tiff"]];
138                [item setToolTip:NSLocalizedString(@"Send an XML stanza.", @"toolbar button")];
139                [item setAction:@selector(showInputSheet:)];
140                [item setTarget:self];
141        }
142       
143        return [item autorelease];
144}
145
146
147#pragma mark -
148#pragma mark Actions
149
150
151- (IBAction)clear:(id)sender
152{
153        [m_xmlTextView setString:@""];
154}
155
156
157- (IBAction)save:(id)sender
158{
159        NSSavePanel *savePanel = [NSSavePanel savePanel];
160        [savePanel beginSheetForDirectory:nil
161                                                                 file:@"xmpplog.rtfd" 
162                                           modalForWindow:[self window]
163                                                modalDelegate:self 
164                                           didEndSelector:@selector(savePanelDidEnd:returnCode:contextInfo:)
165                                                  contextInfo:nil];
166}
167
168
169- (IBAction)showInputSheet:(id)sender
170{
171        NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
172                [NSFont userFixedPitchFontOfSize:10.0], NSFontAttributeName,
173                nil];
174       
175        // This must be done to avoid reverting to Helvetica 12pt.
176        [m_inputTextView setString:@""];
177        [m_inputTextView setTypingAttributes:attributes];
178       
179        [m_invalidXMLLabel setHidden:YES];
180        [m_inputSendButton setEnabled:NO];
181       
182        // Show the input sheet.
183        [NSApp beginSheet:m_inputSheet
184           modalForWindow:[self window]
185                modalDelegate:self
186           didEndSelector:@selector(inputSheetDidEnd:returnCode:contextInfo:)
187                  contextInfo:nil];
188}
189
190
191- (IBAction)inputSheetOK:(id)sender
192{
193        BOOL canSendXML = YES;
194       
195        if ([self isXMLCheckEnabled]) {
196                NSError *error = nil;
197                NSXMLElement *xmlElem = [[NSXMLElement alloc] initWithXMLString:[m_inputTextView string] error:&error];
198               
199                if (xmlElem != nil && error == nil) {
200                        // It's valid
201                        [m_invalidXMLLabel setHidden:YES];
202                        canSendXML = YES;
203                }
204                else {
205                        // It's invalid!
206                        NSBeep();
207                        [m_invalidXMLLabel setHidden:NO];
208                        canSendXML = NO;
209                }
210                [xmlElem release];
211        }
212       
213        if (canSendXML) {
214                [m_account sendXMLString:[m_inputTextView string]];
215                [NSApp endSheet:m_inputSheet];
216        }
217}
218
219
220- (IBAction)inputSheetCancel:(id)sender
221{
222        [NSApp endSheet:m_inputSheet];
223}
224
225
226#pragma mark -
227#pragma mark Instance Methods
228
229- (BOOL)isLoggingEnabled
230{
231        return m_enabled;
232}
233
234- (void)setLoggingEnabled:(BOOL)flag
235{
236        if (m_enabled != flag) {
237                m_enabled = flag;
238               
239               
240                NSTextStorage *textStorage = [m_xmlTextView textStorage];
241                BOOL wasScrolledToBottom = (NSMaxY([m_xmlTextView visibleRect]) >= (NSMaxY([m_xmlTextView bounds]) - 30.0));
242                NSFont *font = [NSFont userFixedPitchFontOfSize:10.0];
243               
244                [textStorage beginEditing];
245                {
246                        // Dump the contents of the recent XML stanzas buffer to the console
247                        if (m_enabled && [m_recentXMLStanzasBuffer count] > 0) {
248                                NSString *bufferDumpHeaderStr = @"\n\n<!-- ***** Dumping some recently exchanged XML messages: ***** -->\n";
249                                [textStorage appendAttributedString:[NSAttributedString attributedStringFromString:bufferDumpHeaderStr
250                                                                                                                                                                                          font:font
251                                                                                                                                                                                         color:[NSColor darkGrayColor]]];
252                               
253                                NSEnumerator *attribStrEnum = [m_recentXMLStanzasBuffer objectEnumerator];
254                                NSAttributedString *attribString = nil;
255                                while (attribString = [attribStrEnum nextObject]) {
256                                        [textStorage appendAttributedString:attribString];
257                                }
258                        }
259                       
260                        NSString *liveDumpHeaderStr = [NSString stringWithFormat:@"\n\n<!-- ***** LIVE logging %@ at %@ ***** -->\n",
261                                                                                   (m_enabled ? @"STARTED" : @"STOPPED"), [NSDate date]];
262                       
263                        [textStorage appendAttributedString:[NSAttributedString attributedStringFromString:liveDumpHeaderStr
264                                                                                                                                                                                  font:font
265                                                                                                                                                                                 color:[NSColor darkGrayColor]]];
266                }
267                [textStorage endEditing];
268               
269                // Auto-Scroll to the bottom of the content view, but only if we were already at the bottom.
270                if (wasScrolledToBottom) {
271                        [m_xmlTextView scrollRangeToVisible:NSMakeRange([textStorage length], 0)];
272                }
273               
274                if (m_enabled) {
275                        [m_recentXMLStanzasBuffer removeAllObjects];
276                }
277        }
278}
279
280- (BOOL)isXMLCheckEnabled
281{
282        return m_checkXML;
283}
284
285- (void)setXMLCheckEnabled:(BOOL)flag
286{
287        m_checkXML = flag;
288       
289        if (!flag) {
290                [m_invalidXMLLabel setHidden:YES];
291        }
292}
293
294- (NSAttributedString *)attributedStringForConsoleFromXMLString:(NSString *)xmlString inbound:(BOOL)isInbound
295{
296        NSMutableAttributedString *resultingAttribStr = [[NSMutableAttributedString alloc] init];
297       
298        NSFont          *font = [NSFont userFixedPitchFontOfSize:10.0];
299        NSString        *colorKey = (isInbound ? @"ChatFriendColor" : @"ChatMyColor");
300        NSData          *colorData = [[NSUserDefaults standardUserDefaults] dataForKey:colorKey];
301        NSColor         *color = [NSUnarchiver unarchiveObjectWithData:colorData];
302       
303        if ([xmlString length] > 0) {
304                // Create the attributed strings
305                NSString *dateTagStr = [NSString stringWithFormat:@"\n\n<!-- %@ at %@ -->\n",
306                                                                (isInbound ? @"Received" : @"Sent"), [NSDate date]];
307                NSAttributedString *attributedDateTagStr = [NSAttributedString attributedStringFromString:dateTagStr
308                                                                                                                                                                                         font:font
309                                                                                                                                                                                        color:[NSColor grayColor]];
310                NSAttributedString *attributedXMLStr = [NSAttributedString attributedStringFromString:xmlString
311                                                                                                                                                                                 font:font
312                                                                                                                                                                                color:color];
313               
314                [resultingAttribStr beginEditing];
315                [resultingAttribStr appendAttributedString:attributedDateTagStr];
316                [resultingAttribStr appendAttributedString:attributedXMLStr];
317                [resultingAttribStr endEditing];
318        }
319       
320        return [resultingAttribStr autorelease];
321}
322
323#define RECENT_XML_STANZAS_BUFFER_MAX_COUNT             1000
324
325- (void)appendXMLString:(NSString *)xmlString inbound:(BOOL)isInbound
326{
327        NSAttributedString *attributedXMLStr = [self attributedStringForConsoleFromXMLString:xmlString inbound:isInbound];
328       
329        if (m_enabled) {
330                NSTextStorage *textStorage = [m_xmlTextView textStorage];
331                BOOL wasScrolledToBottom = (NSMaxY([m_xmlTextView visibleRect]) >= (NSMaxY([m_xmlTextView bounds]) - 30.0));
332               
333                [textStorage appendAttributedString:attributedXMLStr];
334               
335                // Auto-Scroll to the bottom of the content view, but only if we were already at the bottom.
336                if (wasScrolledToBottom) {
337                        [m_xmlTextView scrollRangeToVisible:NSMakeRange([textStorage length], 0)];
338                }
339        }
340        else {
341                // Save it on our buffer for the most recent XML stanzas
342                [m_recentXMLStanzasBuffer addObject:attributedXMLStr];
343               
344                NSUInteger newCount = [m_recentXMLStanzasBuffer count];
345               
346                if (newCount > RECENT_XML_STANZAS_BUFFER_MAX_COUNT) {
347                        [m_recentXMLStanzasBuffer removeObjectsInRange:NSMakeRange(0, newCount - RECENT_XML_STANZAS_BUFFER_MAX_COUNT)];
348                }
349        }
350}
351
352
353#pragma mark -
354#pragma mark Input Sheet Delegate
355
356
357- (void)savePanelDidEnd:(NSSavePanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
358{
359        [m_xmlTextView writeRTFDToFile:[sheet filename] atomically:YES];       
360}
361
362- (void)inputSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
363{
364        [m_inputSheet orderOut:self];
365}
366
367
368#pragma mark -
369#pragma mark NSTextView Delegate
370
371
372- (void)textDidChange:(NSNotification *)aNotification
373{
374        [m_invalidXMLLabel setHidden:YES];
375        [m_inputSendButton setEnabled:([[m_inputTextView string] length] > 0)];
376}
377
378
379#pragma mark -
380#pragma mark Account Notification Methods
381
382
383- (void)accountDidSendOrReceiveXMLString:(NSNotification *)notification
384{
385        [self appendXMLString:[[notification userInfo] objectForKey:LPXMLString]
386                                  inbound:[[notification name] isEqualToString:LPAccountDidReceiveXMLStringNotification]];
387}
388
389
390@end
Note: See TracBrowser for help on using the browser.