source: trunk/macosx/MessageWindowController.m @ 5030

Last change on this file since 5030 was 5030, checked in by livings124, 14 years ago

hopefully fix a crash with the open window

  • Property svn:keywords set to Date Rev Author Id
File size: 12.6 KB
Line 
1/******************************************************************************
2 * $Id: MessageWindowController.m 5030 2008-02-13 19:33:04Z livings124 $
3 *
4 * Copyright (c) 2006-2008 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#import "MessageWindowController.h"
26#import <transmission.h>
27
28#define LEVEL_ERROR 0
29#define LEVEL_INFO  1
30#define LEVEL_DEBUG 2
31
32#define UPDATE_SECONDS  0.6
33#define MAX_MESSAGES    4000
34
35@interface MessageWindowController (Private)
36
37- (void) resizeColumn;
38- (NSString *) stringForMessage: (NSDictionary *) message;
39- (NSString *) fileForMessage: (NSDictionary *) message;
40
41@end
42
43@implementation MessageWindowController
44
45- (id) init
46{
47    return [super initWithWindowNibName: @"MessageWindow"];
48}
49
50- (void) dealloc
51{
52    [fTimer invalidate];
53    [fMessages release];
54   
55    [fAttributes release];
56   
57    [super dealloc];
58}
59
60- (void) awakeFromNib
61{
62    NSWindow * window = [self window];
63    [window setFrameAutosaveName: @"MessageWindowFrame"];
64    [window setFrameUsingName: @"MessageWindowFrame"];
65   
66    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(resizeColumn)
67                    name: @"NSTableViewColumnDidResizeNotification" object: fMessageTable];
68   
69    //initially sort peer table by IP
70    if ([[fMessageTable sortDescriptors] count] == 0)
71        [fMessageTable setSortDescriptors: [NSArray arrayWithObject: [[fMessageTable tableColumnWithIdentifier: @"Date"]
72                                            sortDescriptorPrototype]]];
73   
74    fErrorImage = [NSImage imageNamed: @"RedDot.png"];
75    fInfoImage = [NSImage imageNamed: @"YellowDot.png"];
76    fDebugImage = [NSImage imageNamed: @"PurpleDot.png"];
77   
78    //set images to popup button items
79    [[fLevelButton itemAtIndex: LEVEL_ERROR] setImage: fErrorImage];
80    [[fLevelButton itemAtIndex: LEVEL_INFO] setImage: fInfoImage];
81    [[fLevelButton itemAtIndex: LEVEL_DEBUG] setImage: fDebugImage];
82   
83    //select proper level in popup button
84    switch (tr_getMessageLevel())
85    {
86        case TR_MSG_ERR:
87            [fLevelButton selectItemAtIndex: LEVEL_ERROR];
88            break;
89        case TR_MSG_INF:
90            [fLevelButton selectItemAtIndex: LEVEL_INFO];
91            break;
92        case TR_MSG_DBG:
93            [fLevelButton selectItemAtIndex: LEVEL_DEBUG];
94    }
95   
96    fMessages = [[NSMutableArray alloc] init];
97}
98
99- (void) windowDidBecomeKey: (NSNotification *) notification
100{
101    if (!fTimer)
102        fTimer = [NSTimer scheduledTimerWithTimeInterval: UPDATE_SECONDS target: self
103                    selector: @selector(updateLog:) userInfo: nil repeats: YES];
104    [self updateLog: nil];
105}
106
107- (void) windowWillClose: (id)sender
108{
109    [fTimer invalidate];
110    fTimer = nil;
111}
112
113- (void) updateLog: (NSTimer *) timer
114{
115    tr_msg_list * messages, * currentMessage;
116    if ((messages = tr_getQueuedMessages()) == NULL)
117        return;
118   
119    for (currentMessage = messages; currentMessage != NULL; currentMessage = currentMessage->next)
120    {
121        NSMutableDictionary * message  = [NSMutableDictionary dictionaryWithObjectsAndKeys:
122                                        [NSString stringWithUTF8String: currentMessage->message], @"Message",
123                                        [NSDate dateWithTimeIntervalSince1970: currentMessage->when], @"Date",
124                                        [NSNumber numberWithInt: currentMessage->level], @"Level", nil];
125       
126        if (currentMessage->file != NULL)
127        {
128            [message setObject: [NSString stringWithUTF8String: currentMessage->file] forKey: @"File"];
129            [message setObject: [NSNumber numberWithInt: currentMessage->line] forKey: @"Line"];
130        }
131                               
132        [fMessages addObject: message];
133    }
134   
135    tr_freeMessageList(messages);
136   
137    NSScroller * scroller = [[fMessageTable enclosingScrollView] verticalScroller];
138    BOOL shouldScroll = [scroller floatValue] == 1.0 || [scroller isHidden] || [scroller knobProportion] == 1.0;
139   
140    int total = [fMessages count];
141    if (total > MAX_MESSAGES)
142    {
143        //remove the oldest
144        NSSortDescriptor * descriptor = [[[NSSortDescriptor alloc] initWithKey: @"Date" ascending: YES] autorelease];
145        [fMessages sortUsingDescriptors: [NSArray arrayWithObject: descriptor]];
146       
147        [fMessages removeObjectsInRange: NSMakeRange(0, total-MAX_MESSAGES)];
148       
149        [fMessageTable noteHeightOfRowsWithIndexesChanged: [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, MAX_MESSAGES)]];
150        total = MAX_MESSAGES;
151    }
152   
153    [fMessages sortUsingDescriptors: [fMessageTable sortDescriptors]];
154   
155    [fMessageTable reloadData];
156    if (shouldScroll)
157        [fMessageTable scrollRowToVisible: total-1];
158}
159
160- (int) numberOfRowsInTableView: (NSTableView *) tableView
161{
162    return [fMessages count];
163}
164
165- (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) column row: (int) row
166{
167    NSString * ident = [column identifier];
168    NSDictionary * message = [fMessages objectAtIndex: row];
169
170    if ([ident isEqualToString: @"Date"])
171        return [message objectForKey: @"Date"];
172    else if ([ident isEqualToString: @"Level"])
173    {
174        switch ([[message objectForKey: @"Level"] intValue])
175        {
176            case TR_MSG_ERR:
177                return fErrorImage;
178            case TR_MSG_INF:
179                return fInfoImage;
180            case TR_MSG_DBG:
181                return fDebugImage;
182            default:
183                return nil;
184        }
185    }
186    else
187        return [message objectForKey: @"Message"];
188}
189
190#warning don't cut off end
191- (float) tableView: (NSTableView *) tableView heightOfRow: (int) row
192{
193    NSTableColumn * column = [tableView tableColumnWithIdentifier: @"Message"];
194   
195    if (!fAttributes)
196        fAttributes = [[[[column dataCell] attributedStringValue] attributesAtIndex: 0 effectiveRange: NULL] retain];
197   
198    int count = [[[fMessages objectAtIndex: row] objectForKey: @"Message"] sizeWithAttributes: fAttributes].width / [column width];
199    return [tableView rowHeight] * (float)(count+1);
200}
201
202- (void) tableView: (NSTableView *) tableView sortDescriptorsDidChange: (NSArray *) oldDescriptors
203{
204    [fMessages sortUsingDescriptors: [fMessageTable sortDescriptors]];
205    [fMessageTable reloadData];
206}
207
208- (NSString *) tableView: (NSTableView *) tableView toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect
209                tableColumn: (NSTableColumn *) column row: (int) row mouseLocation: (NSPoint) mouseLocation
210{
211    return [self fileForMessage: [fMessages objectAtIndex: row]];
212}
213
214- (void) copy: (id) sender
215{
216    NSPasteboard * pb = [NSPasteboard generalPasteboard];
217    [pb declareTypes: [NSArray arrayWithObject: NSStringPboardType] owner: self];
218   
219    NSIndexSet * indexes = [fMessageTable selectedRowIndexes];
220    NSMutableArray * messageStrings = [NSMutableArray arrayWithCapacity: [indexes count]];
221   
222    NSEnumerator * enumerator = [[fMessages objectsAtIndexes: indexes] objectEnumerator];
223    NSDictionary * message;
224    while ((message = [enumerator nextObject]))
225        [messageStrings addObject: [self stringForMessage: message]];
226   
227    [pb setString: [messageStrings componentsJoinedByString: @"\n"] forType: NSStringPboardType];
228}
229
230- (BOOL) validateMenuItem: (NSMenuItem *) menuItem
231{
232    SEL action = [menuItem action];
233   
234    if (action == @selector(copy:))
235        return [fMessageTable numberOfSelectedRows] > 0;
236   
237    return YES;
238}
239
240- (void) changeLevel: (id) sender
241{
242    [self updateLog: nil];
243   
244    int level;
245    switch ([fLevelButton indexOfSelectedItem])
246    {
247        case LEVEL_ERROR:
248            level = TR_MSG_ERR;
249            break;
250        case LEVEL_INFO:
251            level = TR_MSG_INF;
252            break;
253        case LEVEL_DEBUG:
254            level = TR_MSG_DBG;
255            break;
256    }
257   
258    tr_setMessageLevel(level);
259    [[NSUserDefaults standardUserDefaults] setInteger: level forKey: @"MessageLevel"];
260}
261
262- (void) clearLog: (id) sender
263{
264    [fMessages removeAllObjects];
265    [fMessageTable reloadData];
266}
267
268- (void) writeToFile: (id) sender
269{
270    //make the array sorted by date
271    NSSortDescriptor * descriptor = [[[NSSortDescriptor alloc] initWithKey: @"Date" ascending: YES] autorelease];
272    NSArray * descriptors = [[NSArray alloc] initWithObjects: descriptor, nil];
273    NSArray * sortedMessages = [fMessages sortedArrayUsingDescriptors: descriptors];
274    [descriptors release];
275   
276    //create the text to output
277    NSMutableArray * messageStrings = [NSMutableArray arrayWithCapacity: [fMessages count]];
278    NSEnumerator * enumerator = [sortedMessages objectEnumerator];
279    NSDictionary * message;
280    while ((message = [enumerator nextObject]))
281        [messageStrings addObject: [self stringForMessage: message]];
282   
283    NSString * fileString = [[messageStrings componentsJoinedByString: @"\n"] retain];
284   
285    NSSavePanel * panel = [NSSavePanel savePanel];
286    [panel setRequiredFileType: @"txt"];
287    [panel setCanSelectHiddenExtension: YES];
288   
289    [panel beginSheetForDirectory: nil file: NSLocalizedString(@"untitled", "Save log panel -> default file name")
290            modalForWindow: [self window] modalDelegate: self
291            didEndSelector: @selector(writeToFileSheetClosed:returnCode:contextInfo:) contextInfo: fileString];
292}
293
294- (void) writeToFileSheetClosed: (NSSavePanel *) panel returnCode: (int) code contextInfo: (NSString *) string
295{
296    if (code == NSOKButton)
297    {
298        if (![string writeToFile: [panel filename] atomically: YES encoding: NSUTF8StringEncoding error: nil])
299        {
300            NSAlert * alert = [[NSAlert alloc] init];
301            [alert addButtonWithTitle: NSLocalizedString(@"OK", "Save log alert panel -> button")];
302            [alert setMessageText: [NSString stringWithFormat: NSLocalizedString(@"Log Could Not Be Saved",
303                                    "Save log alert panel -> title")]];
304            [alert setInformativeText: [NSString stringWithFormat:
305                    NSLocalizedString(@"There was a problem creating the file \"%@\".",
306                                        "Save log alert panel -> message"), [[panel filename] lastPathComponent]]];
307            [alert setAlertStyle: NSWarningAlertStyle];
308           
309            [alert runModal];
310            [alert release];
311        }
312    }
313   
314    [string release];
315}
316
317@end
318
319@implementation MessageWindowController (Private)
320
321- (void) resizeColumn
322{
323    [fMessageTable noteHeightOfRowsWithIndexesChanged: [NSIndexSet indexSetWithIndexesInRange:
324                    NSMakeRange(0, [fMessageTable numberOfRows])]];
325}
326
327- (NSString *) stringForMessage: (NSDictionary *) message
328{
329    NSString * level;
330    switch ([[message objectForKey: @"Level"] intValue])
331    {
332        case TR_MSG_ERR:
333            level = @"[Error]";
334            break;
335        case TR_MSG_INF:
336            level = @"[Info]";
337            break;
338        case TR_MSG_DBG:
339            level = @"[Debug]";
340            break;
341        default:
342            level = @"";
343    }
344   
345    NSMutableArray * strings = [NSMutableArray arrayWithObjects: [message objectForKey: @"Date"], level,
346                                [message objectForKey: @"Message"], nil];
347    NSString * file;
348    if ((file = [self fileForMessage: message]))
349        [strings insertObject: file atIndex: 1];
350   
351    return [strings componentsJoinedByString: @" "];
352}
353
354- (NSString *) fileForMessage: (NSDictionary *) message
355{
356    NSString * file;
357    if ((file = [message objectForKey: @"File"]))
358        return [NSString stringWithFormat: @"%@:%@", [[message objectForKey: @"File"] lastPathComponent],
359                [message objectForKey: @"Line"]];
360    else
361        return nil;
362}
363
364@end
Note: See TracBrowser for help on using the repository browser.