source: trunk/macosx/MessageWindowController.m @ 3875

Last change on this file since 3875 was 3875, checked in by livings124, 15 years ago

more straightforward way to get string width for message window

  • Property svn:keywords set to Date Rev Author Id
File size: 11.2 KB
Line 
1/******************************************************************************
2 * $Id: MessageWindowController.m 3875 2007-11-18 05:45:28Z livings124 $
3 *
4 * Copyright (c) 2006-2007 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
40@end
41
42@implementation MessageWindowController
43
44- (id) init
45{
46    if ((self = [super initWithWindowNibName: @"MessageWindow"]))
47    {
48        fMessages = [[NSMutableArray alloc] init];
49       
50        fTimer = [NSTimer scheduledTimerWithTimeInterval: UPDATE_SECONDS target: self
51                    selector: @selector(updateLog:) userInfo: nil repeats: YES];
52       
53        tr_setMessageLevel([[NSUserDefaults standardUserDefaults] integerForKey: @"MessageLevel"]);
54        tr_setMessageQueuing(1);
55    }
56    return self;
57}
58
59- (void) dealloc
60{
61    [fTimer invalidate];
62    [fMessages release];
63   
64    [fAttributes release];
65   
66    [super dealloc];
67}
68
69- (void) awakeFromNib
70{
71    NSWindow * window = [self window];
72    [window setFrameAutosaveName: @"MessageWindowFrame"];
73    [window setFrameUsingName: @"MessageWindowFrame"];
74   
75    [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(resizeColumn)
76                    name: @"NSTableViewColumnDidResizeNotification" object: fMessageTable];
77   
78    //initially sort peer table by IP
79    if ([[fMessageTable sortDescriptors] count] == 0)
80    {
81        [fMessageTable setSortDescriptors: [NSArray arrayWithObject: [[fMessageTable tableColumnWithIdentifier: @"Date"]
82                                            sortDescriptorPrototype]]];
83        [self updateLog: nil];
84    }
85   
86    fErrorImage = [NSImage imageNamed: @"RedDot.png"];
87    fInfoImage = [NSImage imageNamed: @"YellowDot.png"];
88    fDebugImage = [NSImage imageNamed: @"PurpleDot.png"];
89   
90    //set images to popup button items
91    [[fLevelButton itemAtIndex: LEVEL_ERROR] setImage: fErrorImage];
92    [[fLevelButton itemAtIndex: LEVEL_INFO] setImage: fInfoImage];
93    [[fLevelButton itemAtIndex: LEVEL_DEBUG] setImage: fDebugImage];
94   
95    //select proper level in popup button
96    int level = tr_getMessageLevel();
97    switch (level)
98    {
99        case TR_MSG_ERR:
100            [fLevelButton selectItemAtIndex: LEVEL_ERROR];
101            break;
102        case TR_MSG_INF:
103            [fLevelButton selectItemAtIndex: LEVEL_INFO];
104            break;
105        case TR_MSG_DBG:
106            [fLevelButton selectItemAtIndex: LEVEL_DEBUG];
107    }
108}
109
110- (void) updateLog: (NSTimer *) timer
111{
112    tr_msg_list * messages, * currentMessage;
113    if ((messages = tr_getQueuedMessages()) == NULL)
114        return;
115   
116    for (currentMessage = messages; currentMessage != NULL; currentMessage = currentMessage->next)
117        [fMessages addObject: [NSDictionary dictionaryWithObjectsAndKeys:
118                                [NSString stringWithUTF8String: currentMessage->message], @"Message",
119                                [NSDate dateWithTimeIntervalSince1970: currentMessage->when], @"Date",
120                                [NSNumber numberWithInt: currentMessage->level], @"Level", nil]];
121   
122    tr_freeMessageList(messages);
123   
124    int total = [fMessages count];
125    if (total > MAX_MESSAGES)
126    {
127        //remove the oldest
128        NSSortDescriptor * descriptor = [[[NSSortDescriptor alloc] initWithKey: @"Date" ascending: YES] autorelease];
129        NSArray * descriptors = [[NSArray alloc] initWithObjects: descriptor, nil];
130        [fMessages sortUsingDescriptors: descriptors];
131        [descriptors release];
132       
133        [fMessages removeObjectsInRange: NSMakeRange(0, total-MAX_MESSAGES)];
134       
135        [fMessageTable noteHeightOfRowsWithIndexesChanged: [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, MAX_MESSAGES)]];
136    }
137   
138    [fMessages sortUsingDescriptors: [fMessageTable sortDescriptors]];
139   
140    [fMessageTable reloadData];
141}
142
143- (int) numberOfRowsInTableView: (NSTableView *) tableView
144{
145    return [fMessages count];
146}
147
148- (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) column row: (int) row
149{
150    NSString * ident = [column identifier];
151    NSDictionary * message = [fMessages objectAtIndex: row];
152
153    if ([ident isEqualToString: @"Date"])
154        return [message objectForKey: @"Date"];
155    else if ([ident isEqualToString: @"Level"])
156    {
157        switch ([[message objectForKey: @"Level"] intValue])
158        {
159            case TR_MSG_ERR:
160                return fErrorImage;
161            case TR_MSG_INF:
162                return fInfoImage;
163            case TR_MSG_DBG:
164                return fDebugImage;
165            default:
166                return nil;
167        }
168    }
169    else
170        return [message objectForKey: @"Message"];
171}
172
173#warning don't cut off end
174- (float) tableView: (NSTableView *) tableView heightOfRow: (int) row
175{
176    NSTableColumn * column = [tableView tableColumnWithIdentifier: @"Message"];
177   
178    int count = 0;
179    float width = [column width];
180    if (width > 0)
181    {
182        if (!fAttributes)
183            fAttributes = [[[[column dataCell] attributedStringValue] attributesAtIndex: 0 effectiveRange: NULL] retain];
184       
185        count = [[[fMessages objectAtIndex: row] objectForKey: @"Message"] sizeWithAttributes: fAttributes].width / width;
186    }
187   
188    return [tableView rowHeight] * (float)(count+1);
189}
190
191- (void) tableView: (NSTableView *) tableView sortDescriptorsDidChange: (NSArray *) oldDescriptors
192{
193    [fMessages sortUsingDescriptors: [fMessageTable sortDescriptors]];
194    [fMessageTable reloadData];
195}
196
197- (void) copy: (id) sender
198{
199    NSPasteboard * pb = [NSPasteboard generalPasteboard];
200    [pb declareTypes: [NSArray arrayWithObject: NSStringPboardType] owner: self];
201   
202    NSIndexSet * indexes = [fMessageTable selectedRowIndexes];
203    NSMutableArray * messageStrings = [NSMutableArray arrayWithCapacity: [indexes count]];
204   
205    NSEnumerator * enumerator = [[fMessages objectsAtIndexes: indexes] objectEnumerator];
206    NSDictionary * message;
207    while ((message = [enumerator nextObject]))
208        [messageStrings addObject: [self stringForMessage: message]];
209   
210    [pb setString: [messageStrings componentsJoinedByString: @"\n"] forType: NSStringPboardType];
211}
212
213- (BOOL) validateMenuItem: (NSMenuItem *) menuItem
214{
215    SEL action = [menuItem action];
216   
217    if (action == @selector(copy:))
218        return [fMessageTable numberOfSelectedRows] > 0;
219   
220    return YES;
221}
222
223- (void) changeLevel: (id) sender
224{
225    [self updateLog: nil];
226   
227    int level;
228    switch ([fLevelButton indexOfSelectedItem])
229    {
230        case LEVEL_ERROR:
231            level = TR_MSG_ERR;
232            break;
233        case LEVEL_INFO:
234            level = TR_MSG_INF;
235            break;
236        case LEVEL_DEBUG:
237            level = TR_MSG_DBG;
238            break;
239    }
240   
241    tr_setMessageLevel(level);
242    [[NSUserDefaults standardUserDefaults] setInteger: level forKey: @"MessageLevel"];
243}
244
245- (void) clearLog: (id) sender
246{
247    [fMessages removeAllObjects];
248    [fMessageTable reloadData];
249}
250
251- (void) writeToFile: (id) sender
252{
253    //make the array sorted by date
254    NSSortDescriptor * descriptor = [[[NSSortDescriptor alloc] initWithKey: @"Date" ascending: YES] autorelease];
255    NSArray * descriptors = [[NSArray alloc] initWithObjects: descriptor, nil];
256    NSArray * sortedMessages = [fMessages sortedArrayUsingDescriptors: descriptors];
257    [descriptors release];
258   
259    //create the text to output
260    NSMutableArray * messageStrings = [NSMutableArray arrayWithCapacity: [fMessages count]];
261    NSEnumerator * enumerator = [sortedMessages objectEnumerator];
262    NSDictionary * message;
263    while ((message = [enumerator nextObject]))
264        [messageStrings addObject: [self stringForMessage: message]];
265   
266    NSString * fileString = [[messageStrings componentsJoinedByString: @"\n"] retain];
267   
268    NSSavePanel * panel = [NSSavePanel savePanel];
269    [panel setRequiredFileType: @"txt"];
270    [panel setCanSelectHiddenExtension: YES];
271   
272    [panel beginSheetForDirectory: nil file: NSLocalizedString(@"untitled", "Save log panel -> default file name")
273            modalForWindow: [self window] modalDelegate: self
274            didEndSelector: @selector(writeToFileSheetClosed:returnCode:contextInfo:) contextInfo: fileString];
275}
276
277- (void) writeToFileSheetClosed: (NSSavePanel *) panel returnCode: (int) code contextInfo: (NSString *) string
278{
279    if (code == NSOKButton)
280    {
281        if (![string writeToFile: [panel filename] atomically: YES encoding: NSUTF8StringEncoding error: nil])
282        {
283            NSAlert * alert = [[NSAlert alloc] init];
284            [alert addButtonWithTitle: NSLocalizedString(@"OK", "Save log alert panel -> button")];
285            [alert setMessageText: [NSString stringWithFormat: NSLocalizedString(@"Log Could Not Be Saved",
286                                    "Save log alert panel -> title")]];
287            [alert setInformativeText: [NSString stringWithFormat:
288                    NSLocalizedString(@"There was a problem creating the file \"%@\".",
289                                        "Save log alert panel -> message"), [[panel filename] lastPathComponent]]];
290            [alert setAlertStyle: NSWarningAlertStyle];
291           
292            [alert runModal];
293            [alert release];
294        }
295    }
296   
297    [string release];
298}
299
300@end
301
302@implementation MessageWindowController (Private)
303
304- (void) resizeColumn
305{
306    [fMessageTable noteHeightOfRowsWithIndexesChanged: [NSIndexSet indexSetWithIndexesInRange:
307                    NSMakeRange(0, [fMessageTable numberOfRows])]];
308}
309
310- (NSString *) stringForMessage: (NSDictionary *) message
311{
312    NSString * level;
313    switch ([[message objectForKey: @"Level"] intValue])
314    {
315        case TR_MSG_ERR:
316            level = @"Error";
317            break;
318        case TR_MSG_INF:
319            level = @"Info";
320            break;
321        case TR_MSG_DBG:
322            level = @"Debug";
323            break;
324        default:
325            level = @"";
326    }
327   
328    return [NSString stringWithFormat: @"%@ [%@] %@", [message objectForKey: @"Date"], level, [message objectForKey: @"Message"]];
329}
330
331@end
Note: See TracBrowser for help on using the repository browser.