source: trunk/macosx/TorrentCell.m @ 3730

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

clean up the pieces bar drawing to avoid putting it into an image, and use NSColor object for the piece colors

  • Property svn:keywords set to Date Rev Author Id
File size: 21.3 KB
Line 
1/******************************************************************************
2 * $Id: TorrentCell.m 3730 2007-11-06 04:53:06Z 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 "TorrentCell.h"
26#import "TorrentTableView.h"
27#import "NSStringAdditions.h"
28#import "CTGradientAdditions.h"
29
30#define BAR_HEIGHT 12.0
31
32#define IMAGE_SIZE_REG 32.0
33#define IMAGE_SIZE_MIN 16.0
34
35//end up being larger than font height
36#define HEIGHT_TITLE 16.0
37#define HEIGHT_STATUS 12.0
38
39#define PADDING_HORIZONAL 2.0
40#define PADDING_ABOVE_IMAGE_REG 9.0
41#define PADDING_BETWEEN_IMAGE_AND_TITLE 5.0
42#define PADDING_BETWEEN_IMAGE_AND_BAR 7.0
43#define PADDING_ABOVE_TITLE 3.0
44#define PADDING_ABOVE_MIN_STATUS 4.0
45#define PADDING_BETWEEN_TITLE_AND_MIN_STATUS 2.0
46#define PADDING_BETWEEN_TITLE_AND_PROGRESS 1.0
47#define PADDING_BETWEEN_PROGRESS_AND_BAR 2.0
48#define PADDING_BETWEEN_TITLE_AND_BAR_MIN 3.0
49#define PADDING_BETWEEN_BAR_AND_STATUS 2.0
50
51#define MAX_PIECES 324
52#define BLANK_PIECE -99
53
54@interface TorrentCell (Private)
55
56- (void) drawBar: (NSRect) barRect;
57- (void) drawRegularBar: (NSRect) barRect;
58- (void) drawPiecesBar: (NSRect) barRect;
59
60- (NSRect) rectForMinimalStatusWithString: (NSAttributedString *) string inBounds: (NSRect) bounds;
61- (NSRect) rectForTitleWithString: (NSAttributedString *) string basedOnMinimalStatusRect: (NSRect) statusRect
62            inBounds: (NSRect) bounds;
63- (NSRect) rectForProgressWithString: (NSAttributedString *) string inBounds: (NSRect) bounds;
64- (NSRect) rectForStatusWithString: (NSAttributedString *) string inBounds: (NSRect) bounds;
65
66- (NSAttributedString *) attributedTitleWithColor: (NSColor *) color;
67- (NSAttributedString *) attributedStatusString: (NSString *) string withColor: (NSColor *) color;
68
69@end
70
71@implementation TorrentCell
72
73//only called one, so don't worry about release
74- (id) init
75{
76    if ((self = [super init]))
77        {
78        fDefaults = [NSUserDefaults standardUserDefaults];
79       
80        NSMutableParagraphStyle * paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
81        [paragraphStyle setLineBreakMode: NSLineBreakByTruncatingTail];
82   
83        fTitleAttributes = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
84                                [NSFont messageFontOfSize: 12.0], NSFontAttributeName,
85                                paragraphStyle, NSParagraphStyleAttributeName, nil];
86        fStatusAttributes = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
87                                [NSFont messageFontOfSize: 9.0], NSFontAttributeName,
88                                paragraphStyle, NSParagraphStyleAttributeName, nil];
89        [paragraphStyle release];
90       
91        //store box colors
92        fGrayColor = [[NSColor colorWithCalibratedRed: 0.9 green: 0.9 blue: 0.9 alpha: 1.0] retain];
93        fBlue1Color = [[NSColor colorWithCalibratedRed: 0.8 green: 1.0 blue: 1.0 alpha: 1.0] retain];
94        fBlue2Color = [[NSColor colorWithCalibratedRed: 0.6 green: 1.0 blue: 1.0 alpha: 1.0] retain];
95        fBlue3Color = [[NSColor colorWithCalibratedRed: 0.6 green: 0.8 blue: 1.0 alpha: 1.0] retain];
96        fBlue4Color = [[NSColor colorWithCalibratedRed: 0.4 green: 0.6 blue: 1.0 alpha: 1.0] retain];
97        fBlueColor = [[NSColor colorWithCalibratedRed: 0.0 green: 0.4 blue: 0.8 alpha: 1.0] retain];
98       
99        fBarOverlayColor = [[NSColor colorWithDeviceWhite: 0.0 alpha: 0.2] retain];
100    }
101        return self;
102}
103
104- (id) copyWithZone: (NSZone *) zone
105{
106    TorrentCell * copy = [super copyWithZone: zone];
107   
108    copy->fBitmap = nil;
109    copy->fPieces = NULL;
110   
111    return copy;
112}
113
114- (void) dealloc
115{
116    [fBitmap release];
117    if (fPieces)
118        free(fPieces);
119   
120    [super dealloc];
121}
122
123- (NSRect) iconRectForBounds: (NSRect) bounds
124{
125    NSRect result = bounds;
126   
127    result.origin.x += PADDING_HORIZONAL;
128   
129    float imageSize;
130    if ([fDefaults boolForKey: @"SmallView"])
131    {
132        imageSize = IMAGE_SIZE_MIN;
133        result.origin.y += (result.size.height - imageSize) * 0.5;
134    }
135    else
136    {
137        imageSize = IMAGE_SIZE_REG;
138        result.origin.y += PADDING_ABOVE_IMAGE_REG;
139    }
140   
141    result.size = NSMakeSize(imageSize, imageSize);
142   
143    return result;
144}
145
146- (NSRect) titleRectForBounds: (NSRect) bounds
147{
148    return [self rectForTitleWithString: [self attributedTitleWithColor: nil]
149            basedOnMinimalStatusRect: [self minimalStatusRectForBounds: bounds] inBounds: bounds];
150}
151
152- (NSRect) minimalStatusRectForBounds: (NSRect) bounds
153{
154    Torrent * torrent = [self representedObject];
155    NSString * string = [fDefaults boolForKey: @"DisplaySmallStatusRegular"]
156                            ? [torrent shortStatusString] : [torrent remainingTimeString];
157    return [self rectForMinimalStatusWithString: [self attributedStatusString: string withColor: nil] inBounds: bounds];
158}
159
160- (NSRect) progressRectForBounds: (NSRect) bounds
161{
162    return [self rectForProgressWithString: [self attributedStatusString: [[self representedObject] progressString] withColor: nil]
163                    inBounds: bounds];
164}
165
166- (NSRect) barRectForBounds: (NSRect) bounds
167{
168    BOOL minimal = [fDefaults boolForKey: @"SmallView"];
169   
170    NSRect result = bounds;
171    result.size.height = BAR_HEIGHT;
172    result.origin.x = PADDING_HORIZONAL + (minimal ? IMAGE_SIZE_MIN : IMAGE_SIZE_REG) + PADDING_BETWEEN_IMAGE_AND_BAR;
173   
174    result.origin.y += PADDING_ABOVE_TITLE + HEIGHT_TITLE;
175    if (minimal)
176        result.origin.y += PADDING_BETWEEN_TITLE_AND_BAR_MIN;
177    else
178        result.origin.y += PADDING_BETWEEN_TITLE_AND_PROGRESS + HEIGHT_STATUS + PADDING_BETWEEN_PROGRESS_AND_BAR;
179   
180    result.size.width = round(NSMaxX(bounds) - result.origin.x - PADDING_HORIZONAL - BUTTONS_TOTAL_WIDTH);
181   
182    return result;
183}
184
185- (NSRect) statusRectForBounds: (NSRect) bounds
186{
187    return [self rectForStatusWithString: [self attributedStatusString: [[self representedObject] statusString] withColor: nil]
188                    inBounds: bounds];
189}
190
191- (NSUInteger) hitTestForEvent: (NSEvent *) event inRect: (NSRect) cellFrame ofView: (NSView *) controlView
192{
193    return NSCellHitContentArea;
194}
195
196- (void) drawInteriorWithFrame: (NSRect) cellFrame inView: (NSView *) controlView
197{
198    [super drawInteriorWithFrame: cellFrame inView: controlView];
199   
200    Torrent * torrent = [self representedObject];
201   
202    BOOL minimal = [fDefaults boolForKey: @"SmallView"];
203   
204    //error image
205    BOOL error = [torrent isError];
206    if (error && !fErrorImage)
207    {
208        fErrorImage = [[NSImage imageNamed: @"Error.png"] copy];
209        [fErrorImage setFlipped: YES];
210    }
211   
212    //icon
213    NSImage * icon = minimal && error ? fErrorImage : [torrent icon];
214    NSRect iconRect = [self iconRectForBounds: cellFrame];
215    [icon drawInRect: iconRect fromRect: NSZeroRect operation: NSCompositeSourceOver fraction: 1.0];
216   
217    if (error && !minimal)
218    {
219        NSRect errorRect = NSMakeRect(NSMaxX(iconRect) - IMAGE_SIZE_MIN, NSMaxY(iconRect) - IMAGE_SIZE_MIN,
220                                        IMAGE_SIZE_MIN, IMAGE_SIZE_MIN);
221        [fErrorImage drawInRect: errorRect fromRect: NSZeroRect operation: NSCompositeSourceOver fraction: 1.0];
222    }
223   
224    //text color
225    NSColor * titleColor, * statusColor;
226    if ([self isHighlighted]
227            && [[self highlightColorWithFrame: cellFrame inView: controlView] isEqual: [NSColor alternateSelectedControlColor]])
228    {
229        titleColor = [NSColor whiteColor];
230        statusColor = [NSColor whiteColor];
231    }
232    else
233    {
234        titleColor = [NSColor controlTextColor];
235        statusColor = [NSColor darkGrayColor];
236    }
237   
238    //minimal status
239    NSRect minimalStatusRect;
240    if (minimal)
241    {
242        NSString * string = [fDefaults boolForKey: @"DisplaySmallStatusRegular"]
243                            ? [torrent shortStatusString] : [torrent remainingTimeString];
244        NSAttributedString * minimalString = [self attributedStatusString: string withColor: statusColor];
245        minimalStatusRect = [self rectForMinimalStatusWithString: minimalString inBounds: cellFrame];
246       
247        [minimalString drawInRect: minimalStatusRect];
248    }
249   
250    //title
251    NSAttributedString * titleString = [self attributedTitleWithColor: titleColor];
252    NSRect titleRect = [self rectForTitleWithString: titleString basedOnMinimalStatusRect: minimalStatusRect inBounds: cellFrame];
253    [titleString drawInRect: titleRect];
254   
255    //progress
256    if (!minimal)
257    {
258        NSAttributedString * progressString = [self attributedStatusString: [torrent progressString] withColor: statusColor];
259        NSRect progressRect = [self rectForProgressWithString: progressString inBounds: cellFrame];
260        [progressString drawInRect: progressRect];
261    }
262   
263    //bar
264    [self drawBar: [self barRectForBounds: cellFrame]];
265   
266    //status
267    if (!minimal)
268    {
269        NSAttributedString * statusString = [self attributedStatusString: [torrent statusString] withColor: statusColor];
270        [statusString drawInRect: [self rectForStatusWithString: statusString inBounds: cellFrame]];
271    }
272}
273
274@end
275
276@implementation TorrentCell (Private)
277
278- (void) drawBar: (NSRect) barRect
279{
280    if ([fDefaults boolForKey: @"PiecesBar"])
281    {
282        NSRect regularBarRect = barRect, piecesBarRect = barRect;
283        regularBarRect.size.height /= 3;
284        piecesBarRect.origin.y += regularBarRect.size.height;
285        piecesBarRect.size.height -= regularBarRect.size.height;
286       
287        [self drawRegularBar: regularBarRect];
288        [self drawPiecesBar: piecesBarRect];
289    }
290    else
291    {
292        if (fPieces)
293        {
294            free(fPieces);
295            fPieces = NULL;
296            [fBitmap release];
297            fBitmap = nil;
298        }
299       
300        [self drawRegularBar: barRect];
301    }
302   
303    [fBarOverlayColor set];
304    [NSBezierPath strokeRect: NSInsetRect(barRect, 0.5, 0.5)];
305}
306
307- (void) drawRegularBar: (NSRect) barRect
308{
309    Torrent * torrent = [self representedObject];
310   
311    int leftWidth = barRect.size.width;
312    float progress = [torrent progress];
313   
314    if (progress < 1.0)
315    {
316        float rightProgress = 1.0 - progress, progressLeft = [torrent progressLeft];
317        int rightWidth = leftWidth * rightProgress;
318        leftWidth -= rightWidth;
319       
320        if (progressLeft < rightProgress)
321        {
322            int rightNoIncludeWidth = rightWidth * ((rightProgress - progressLeft) / rightProgress);
323            rightWidth -= rightNoIncludeWidth;
324           
325            NSRect noIncludeRect = barRect;
326            noIncludeRect.origin.x += barRect.size.width - rightNoIncludeWidth;
327            noIncludeRect.size.width = rightNoIncludeWidth;
328           
329            if (!fLightGrayGradient)
330                fLightGrayGradient = [[CTGradient progressLightGrayGradient] retain];
331            [fLightGrayGradient fillRect: noIncludeRect angle: -90];
332        }
333       
334        if (rightWidth > 0)
335        {
336            int availableWidth = 0;
337            /*if (![fDefaults boolForKey: @"DisplayProgressBarAvailable"])
338            {
339                //NSLog(@"notAvailableWidth %d rightWidth %d", notAvailableWidth, rightWidth);
340                availableWidth = MAX(0, (float)rightWidth - barRect.size.width * [torrent notAvailableDesired]);
341               
342                if (availableWidth > 0)
343                {
344                    rightWidth -= availableWidth;
345                   
346                    NSRect availableRect = barRect;
347                    availableRect.origin.x += leftWidth;
348                    availableRect.size.width = availableWidth;
349                   
350                    if (!fYellowGradient)
351                        fYellowGradient = [[CTGradient progressYellowGradient] retain];
352                    [fYellowGradient fillRect: availableRect angle: -90];
353                }
354            }*/
355           
356            if (rightWidth > 0)
357            {
358                NSRect includeRect = barRect;
359                includeRect.origin.x += leftWidth + availableWidth;
360                includeRect.size.width = rightWidth;
361               
362                if (!fWhiteGradient)
363                    fWhiteGradient = [[CTGradient progressWhiteGradient] retain];
364                [fWhiteGradient fillRect: includeRect angle: -90];
365            }
366        }
367    }
368   
369    if (leftWidth > 0)
370    {
371        NSRect completeRect = barRect;
372        completeRect.size.width = leftWidth;
373       
374        if ([torrent isActive])
375        {
376            if ([torrent isChecking])
377            {
378                if (!fYellowGradient)
379                    fYellowGradient = [[CTGradient progressYellowGradient] retain];
380                [fYellowGradient fillRect: completeRect angle: -90];
381            }
382            else if ([torrent isSeeding])
383            {
384                int ratioLeftWidth = leftWidth * (1.0 - [torrent progressStopRatio]);
385                leftWidth -= ratioLeftWidth;
386               
387                if (ratioLeftWidth > 0)
388                {
389                    NSRect ratioLeftRect = barRect;
390                    ratioLeftRect.origin.x += leftWidth;
391                    ratioLeftRect.size.width = ratioLeftWidth;
392                   
393                    if (!fLightGreenGradient)
394                        fLightGreenGradient = [[CTGradient progressLightGreenGradient] retain];
395                    [fLightGreenGradient fillRect: ratioLeftRect angle: -90];
396                }
397               
398                if (leftWidth > 0)
399                {
400                    completeRect.size.width = leftWidth;
401                   
402                    if (!fGreenGradient)
403                        fGreenGradient = [[CTGradient progressGreenGradient] retain];
404                    [fGreenGradient fillRect: completeRect angle: -90];
405                }
406            }
407            else
408            {
409                if (!fBlueGradient)
410                    fBlueGradient = [[CTGradient progressBlueGradient] retain];
411                [fBlueGradient fillRect: completeRect angle: -90];
412            }
413        }
414        else
415        {
416            if ([torrent waitingToStart])
417            {
418                if ([torrent progressLeft] <= 0.0)
419                {
420                    if (!fDarkGreenGradient)
421                        fDarkGreenGradient = [[CTGradient progressDarkGreenGradient] retain];
422                    [fDarkGreenGradient fillRect: completeRect angle: -90];
423                }
424                else
425                {
426                    if (!fDarkBlueGradient)
427                        fDarkBlueGradient = [[CTGradient progressDarkBlueGradient] retain];
428                    [fDarkBlueGradient fillRect: completeRect angle: -90];
429                }
430            }
431            else
432            {
433                if (!fGrayGradient)
434                    fGrayGradient = [[CTGradient progressGrayGradient] retain];
435                [fGrayGradient fillRect: completeRect angle: -90];
436            }
437        }
438    }
439}
440
441- (void) drawPiecesBar: (NSRect) barRect
442{
443    if (!fBitmap)
444        fBitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: nil
445            pixelsWide: MAX_PIECES pixelsHigh: barRect.size.height bitsPerSample: 8 samplesPerPixel: 4 hasAlpha: YES
446            isPlanar: NO colorSpaceName: NSCalibratedRGBColorSpace bytesPerRow: 0 bitsPerPixel: 0];
447   
448    if (!fPieces)
449    {
450        fPieces = malloc(MAX_PIECES);
451        int i;
452        for (i = 0; i < MAX_PIECES; i++)
453            fPieces[i] = BLANK_PIECE;
454    }
455   
456    #warning add flashing orange
457   
458    Torrent * torrent = [self representedObject];
459   
460    int pieceCount = MIN([torrent pieceCount], MAX_PIECES);
461    float * piecePercent = malloc(pieceCount * sizeof(float));
462    [torrent getAmountFinished: piecePercent size: pieceCount];
463   
464    //lines 2 to 14: blue, green, or gray depending on piece availability
465    int i, h, index;
466    float increment = (float)pieceCount / MAX_PIECES;
467    NSColor * pieceColor;
468    BOOL change;
469    for (i = 0; i < MAX_PIECES; i++)
470    {
471        index = i * increment;
472        pieceColor = nil;
473       
474        if (piecePercent[index] >= 1.0)
475        {
476            if (fPieces[i] != -1)
477            {
478                pieceColor = fBlueColor;
479                fPieces[i] = -1;
480            }
481        }
482        else if (piecePercent[index] <= 0.0)
483        {
484            if (fPieces[i] != 0)
485            {
486                pieceColor = fGrayColor;
487                fPieces[i] = 0;
488            }
489        }
490        else if (piecePercent[index] <= 0.25)
491        {
492            if (fPieces[i] != 1)
493            {
494                pieceColor = fBlue1Color;
495                fPieces[i] = 1;
496            }
497        }
498        else if (piecePercent[index] <= 0.5)
499        {
500            if (fPieces[i] != 2)
501            {
502                pieceColor = fBlue2Color;
503                fPieces[i] = 2;
504            }
505        }
506        else if (piecePercent[index] <= 0.75)
507        {
508            if (fPieces[i] != 3)
509            {
510                pieceColor = fBlue3Color;
511                fPieces[i] = 3;
512            }
513        }
514        else
515        {
516            if (fPieces[i] != 4)
517            {
518                pieceColor = fBlue4Color;
519                fPieces[i] = 4;
520            }
521        }
522       
523        if (pieceColor)
524            for (h = 0; h < barRect.size.height; h++)
525                [fBitmap setColor: pieceColor atX: i y: h];
526    }
527   
528    free(piecePercent);
529   
530    //actually draw image
531    [fBitmap drawInRect: barRect];
532   
533    if (!fTransparentGradient)
534        fTransparentGradient = [[CTGradient progressTransparentGradient] retain];
535    [fTransparentGradient fillRect: barRect angle: -90];
536}
537
538- (NSRect) rectForMinimalStatusWithString: (NSAttributedString *) string inBounds: (NSRect) bounds
539{
540    if (![fDefaults boolForKey: @"SmallView"])
541        return NSZeroRect;
542   
543    NSRect result = bounds;
544    result.size = [string size];
545   
546    result.origin.x += bounds.size.width - result.size.width - PADDING_HORIZONAL;
547    result.origin.y += PADDING_ABOVE_MIN_STATUS;
548   
549    return result;
550}
551
552- (NSRect) rectForTitleWithString: (NSAttributedString *) string basedOnMinimalStatusRect: (NSRect) statusRect
553            inBounds: (NSRect) bounds
554{
555    BOOL minimal = [fDefaults boolForKey: @"SmallView"];
556   
557    NSRect result = bounds;
558    result.origin.y += PADDING_ABOVE_TITLE;
559    result.origin.x += PADDING_HORIZONAL + (minimal ? IMAGE_SIZE_MIN : IMAGE_SIZE_REG) + PADDING_BETWEEN_IMAGE_AND_TITLE;
560   
561    result.size = [string size];
562    result.size.width = MIN(result.size.width, NSMaxX(bounds) - result.origin.x - PADDING_HORIZONAL
563                            - (minimal ? PADDING_BETWEEN_TITLE_AND_MIN_STATUS + statusRect.size.width : 0));
564   
565    return result;
566}
567
568- (NSRect) rectForProgressWithString: (NSAttributedString *) string inBounds: (NSRect) bounds
569{
570    if ([fDefaults boolForKey: @"SmallView"])
571        return NSZeroRect;
572   
573    NSRect result = bounds;
574    result.origin.y += PADDING_ABOVE_TITLE + HEIGHT_TITLE + PADDING_BETWEEN_TITLE_AND_PROGRESS;
575    result.origin.x += PADDING_HORIZONAL + IMAGE_SIZE_REG + PADDING_BETWEEN_IMAGE_AND_TITLE;
576   
577    result.size = [string size];
578    result.size.width = MIN(result.size.width, NSMaxX(bounds) - result.origin.x - PADDING_HORIZONAL);
579   
580    return result;
581}
582
583- (NSRect) rectForStatusWithString: (NSAttributedString *) string inBounds: (NSRect) bounds
584{
585    if ([fDefaults boolForKey: @"SmallView"])
586        return NSZeroRect;
587   
588    NSRect result = bounds;
589    result.origin.y += PADDING_ABOVE_TITLE + HEIGHT_TITLE + PADDING_BETWEEN_TITLE_AND_PROGRESS + HEIGHT_STATUS
590                        + PADDING_BETWEEN_PROGRESS_AND_BAR + BAR_HEIGHT + PADDING_BETWEEN_BAR_AND_STATUS;
591    result.origin.x += PADDING_HORIZONAL + IMAGE_SIZE_REG + PADDING_BETWEEN_IMAGE_AND_TITLE;
592   
593    result.size = [string size];
594    result.size.width = MIN(result.size.width, NSMaxX(bounds) - result.origin.x - PADDING_HORIZONAL);
595   
596    return result;
597}
598
599- (NSAttributedString *) attributedTitleWithColor: (NSColor *) color
600{
601    if (color)
602        [fTitleAttributes setObject: color forKey: NSForegroundColorAttributeName];
603   
604    NSString * title = [[self representedObject] name];
605    return [[[NSAttributedString alloc] initWithString: title attributes: fTitleAttributes] autorelease];
606}
607
608- (NSAttributedString *) attributedStatusString: (NSString *) string withColor: (NSColor *) color
609{
610    if (color)
611        [fStatusAttributes setObject: color forKey: NSForegroundColorAttributeName];
612   
613    return [[[NSAttributedString alloc] initWithString: string attributes: fStatusAttributes] autorelease];
614}
615
616@end
Note: See TracBrowser for help on using the repository browser.