source: trunk/macosx/TorrentCell.m @ 835

Last change on this file since 835 was 835, checked in by livings124, 16 years ago

Display an error image on the torrent icon when there is an error with the torrent.

  • Property svn:keywords set to Date Rev Author Id
File size: 14.7 KB
Line 
1/******************************************************************************
2 * $Id: TorrentCell.m 835 2006-08-29 00:09:49Z livings124 $
3 *
4 * Copyright (c) 2006 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 "StringAdditions.h"
28
29#define BAR_HEIGHT 12.0
30
31@interface TorrentCell (Private)
32
33- (void) placeBar: (NSImage *) barImage width: (float) width point: (NSPoint) point;
34- (void) buildSimpleBar: (float) width point: (NSPoint) point;
35- (void) buildAdvancedBar: (float) widthFloat point: (NSPoint) point;
36
37@end
38
39@implementation TorrentCell
40
41// Used to optimize drawing. They contain packed RGBA pixels for every color needed.
42#define BE OSSwapBigToHostConstInt32
43static uint32_t kBorder[] =
44    { BE(0x00000005), BE(0x00000010), BE(0x00000015), BE(0x00000015),
45      BE(0x00000015), BE(0x00000015), BE(0x00000015), BE(0x00000015),
46      BE(0x00000015), BE(0x00000015), BE(0x00000010), BE(0x00000005) };
47
48static uint32_t kBack[] = { BE(0xB4B4B4FF), BE(0xE3E3E3FF) };
49
50static uint32_t kRed   = BE(0xFF6450FF), //255, 100, 80
51                kBlue1 = BE(0xA0DCFFFF), //160, 220, 255
52                kBlue2 = BE(0x78BEFFFF), //120, 190, 255
53                kBlue3 = BE(0x50A0FFFF), //80, 160, 255
54                kBlue4 = BE(0x1E46B4FF), //30, 70, 180
55                kGray  = BE(0x828282FF), //130, 130, 130
56                kGreen = BE(0x00FF00FF); //0, 255, 0
57
58- (id) init
59{
60    if ((self = [super init]))
61    {
62        fDefaults = [NSUserDefaults standardUserDefaults];
63       
64        fStatusRegular = [fDefaults boolForKey: @"SmallStatusRegular"];
65   
66        NSSize startSize = NSMakeSize(100.0, BAR_HEIGHT);
67       
68        fProgressWhite = [NSImage imageNamed: @"ProgressBarWhite.png"];
69        [fProgressWhite setScalesWhenResized: YES];
70       
71       
72        fProgressBlue = [NSImage imageNamed: @"ProgressBarBlue.png"];
73        [fProgressBlue setScalesWhenResized: YES];
74        [fProgressBlue setSize: startSize];
75       
76        fProgressGray = [NSImage imageNamed: @"ProgressBarGray.png"];
77        [fProgressGray setScalesWhenResized: YES];
78        [fProgressGray setSize: startSize];
79       
80        fProgressGreen = [NSImage imageNamed: @"ProgressBarGreen.png"];
81        [fProgressGreen setScalesWhenResized: YES];
82       
83        fProgressAdvanced = [NSImage imageNamed: @"ProgressBarAdvanced.png"];
84        [fProgressAdvanced setScalesWhenResized: YES];
85       
86       
87        fProgressEndWhite = [NSImage imageNamed: @"ProgressBarEndWhite.png"];
88        fProgressEndBlue = [NSImage imageNamed: @"ProgressBarEndBlue.png"];
89        fProgressEndGray = [NSImage imageNamed: @"ProgressBarEndGray.png"];
90        fProgressEndGreen = [NSImage imageNamed: @"ProgressBarEndGreen.png"];
91        fProgressEndAdvanced = [NSImage imageNamed: @"ProgressBarEndAdvanced.png"];
92       
93        fErrorImage = [NSImage imageNamed: @"Error.tiff"];
94        [fErrorImage setFlipped: YES];
95        [fErrorImage setScalesWhenResized: YES];
96        [fErrorImage setSize: NSMakeSize(16.0, 16.0)];
97    }
98    return self;
99}
100
101- (void) setTorrent: (Torrent *) torrent
102{
103    fTorrent = torrent;
104}
105
106- (void) placeBar: (NSImage *) barImage width: (float) width point: (NSPoint) point
107{
108    if ([barImage size].width < width)
109        [barImage setSize: NSMakeSize(width * 1.5, BAR_HEIGHT)];
110
111    [barImage compositeToPoint: point fromRect: NSMakeRect(0, 0, width, BAR_HEIGHT)
112                        operation: NSCompositeSourceOver];
113}
114
115- (void) buildSimpleBar: (float) width point: (NSPoint) point
116{
117    width -= 2.0;
118    if ([fTorrent isSeeding])
119    {
120        [fProgressEndGreen compositeToPoint: point operation: NSCompositeSourceOver];
121       
122        point.x += 1.0;
123        [self placeBar: fProgressGreen width: width point: point];
124       
125        point.x += width;
126        [fProgressEndGreen compositeToPoint: point operation: NSCompositeSourceOver];
127    }
128    else
129    {
130        float completedWidth = [fTorrent progress] * width,
131                remainingWidth = width - completedWidth;
132        BOOL isActive = [fTorrent isActive];
133       
134        //left end
135        NSImage * barLeftEnd;
136        if (remainingWidth == width)
137            barLeftEnd = fProgressEndWhite;
138        else if (isActive)
139            barLeftEnd = fProgressEndBlue;
140        else
141            barLeftEnd = fProgressEndGray;
142       
143        [barLeftEnd compositeToPoint: point operation: NSCompositeSourceOver];
144       
145        //active bar
146        point.x += 1.0;
147        [self placeBar: isActive ? fProgressBlue : fProgressGray width: completedWidth point: point];
148       
149        //remaining bar
150        point.x += completedWidth;
151        [self placeBar: fProgressWhite width: remainingWidth point: point];
152       
153        //right end
154        NSImage * barRightEnd;
155        if (completedWidth < width)
156            barRightEnd = fProgressEndWhite;
157        else if (isActive)
158            barRightEnd = fProgressEndBlue;
159        else
160            barRightEnd = fProgressEndGray;
161       
162        point.x += remainingWidth;
163        [barRightEnd compositeToPoint: point operation: NSCompositeSourceOver];
164    }
165}
166
167- (void) buildAdvancedBar: (float) widthFloat point: (NSPoint) point
168{
169    //if seeding, there's no need for the advanced bar
170    if ([fTorrent isSeeding])
171    {
172        [self buildSimpleBar: widthFloat point: point];
173        return;
174    }
175
176    int width = widthFloat; //integers for bars
177   
178    NSBitmapImageRep * bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: nil
179        pixelsWide: width pixelsHigh: BAR_HEIGHT bitsPerSample: 8 samplesPerPixel: 4 hasAlpha: YES
180        isPlanar: NO colorSpaceName: NSCalibratedRGBColorSpace bytesPerRow: 0 bitsPerPixel: 0];
181
182    int h, w;
183    uint32_t * p;
184    uint8_t * bitmapData = [bitmap bitmapData];
185    int bytesPerRow = [bitmap bytesPerRow];
186
187    //left and right borders
188    p = (uint32_t *) bitmapData;
189    for(h = 0; h < BAR_HEIGHT; h++)
190    {
191        p[0] = kBorder[h];
192        p[width - 1] = kBorder[h];
193        p += bytesPerRow / 4;
194    }
195
196    int8_t * pieces = malloc(width);
197    [fTorrent getAvailability: pieces size: width];
198    int avail = 0;
199    for (w = 0; w < width; w++)
200        if (pieces[w] != 0)
201            avail++;
202
203    //first two lines: dark blue to show progression, green to show available
204    int end = lrintf(floor([fTorrent progress] * (width - 2)));
205    p = (uint32_t *) (bitmapData) + 1;
206
207    for (w = 0; w < end; w++)
208    {
209        p[w] = kBlue4;
210        p[w + bytesPerRow / 4] = kBlue4;
211    }
212    for (; w < avail; w++)
213    {
214        p[w] = kGreen;
215        p[w + bytesPerRow / 4] = kGreen;
216    }
217    for (; w < width - 2; w++)
218    {
219        p[w] = kBack[0];
220        p[w + bytesPerRow / 4] = kBack[1];
221    }
222   
223    //lines 2 to 14: blue or grey depending on whether we have the piece or not
224    uint32_t color;
225    for( w = 0; w < width - 2; w++ )
226    {
227        //point to pixel ( 2 + w, 2 ). We will then draw "vertically"
228        p = (uint32_t *) ( bitmapData + 2 * bytesPerRow ) + 1 + w;
229
230        if (pieces[w] < 0)
231            color = kGray;
232        else if (pieces[w] == 0)
233            color = kRed;
234        else if (pieces[w] == 1)
235            color = kBlue1;
236        else if (pieces[w] == 2)
237            color = kBlue2;
238        else
239            color = kBlue3;
240
241        for( h = 2; h < BAR_HEIGHT; h++ )
242        {
243            p[0] = color;
244            p = (uint32_t *) ( (uint8_t *) p + bytesPerRow );
245        }
246    }
247
248    free( pieces );
249   
250    //actually draw image
251    NSImage * img = [[NSImage alloc] initWithSize: [bitmap size]];
252    [img addRepresentation: bitmap];
253   
254    //bar size with float, not double, to match standard bar
255    [img setScalesWhenResized: YES];
256    [img setSize: NSMakeSize(widthFloat, BAR_HEIGHT)];
257   
258    [img compositeToPoint: point operation: NSCompositeSourceOver];
259    [img release];
260    [bitmap release];
261   
262    //draw overlay over advanced bar
263    [fProgressEndAdvanced compositeToPoint: point operation: NSCompositeSourceOver];
264   
265    widthFloat -= 2.0;
266    point.x += 1.0;
267    [self placeBar: fProgressAdvanced width: widthFloat point: point];
268   
269    point.x += widthFloat;
270    [fProgressEndAdvanced compositeToPoint: point operation: NSCompositeSourceOver];
271}
272
273- (void) toggleMinimalStatus
274{
275    fStatusRegular = !fStatusRegular;
276    [fDefaults setBool: fStatusRegular forKey: @"SmallStatusRegular"];
277}
278
279- (void) drawWithFrame: (NSRect) cellFrame inView: (NSView *) view
280{
281    BOOL highlighted = [self isHighlighted] && [[self highlightColorWithFrame: cellFrame inView: view]
282                                                        isEqual: [NSColor alternateSelectedControlColor]];
283    NSDictionary * nameAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
284                    highlighted ? [NSColor whiteColor] : [NSColor controlTextColor], NSForegroundColorAttributeName,
285                    [NSFont messageFontOfSize: 12.0], NSFontAttributeName, nil];
286    NSDictionary * statusAttributes = [[NSDictionary alloc] initWithObjectsAndKeys:
287                    highlighted ? [NSColor whiteColor] : [NSColor darkGrayColor], NSForegroundColorAttributeName,
288                    [NSFont messageFontOfSize: 9.0], NSFontAttributeName, nil];
289
290    NSPoint pen = cellFrame.origin;
291    const float PADDING = 3.0, LINE_PADDING = 2.0, EXTRA_NAME_SHIFT = 1.0;
292
293    if (![fDefaults boolForKey: @"SmallView"]) //regular size
294    {
295        //icon
296        NSImage * icon = [fTorrent iconFlipped];
297        NSSize iconSize = [icon size];
298       
299        pen.x += PADDING;
300        pen.y += (cellFrame.size.height - iconSize.height) * 0.5;
301       
302        [icon drawAtPoint: pen fromRect: NSMakeRect(0, 0, iconSize.width, iconSize.height)
303                operation: NSCompositeSourceOver fraction: 1.0];
304       
305        //error badge
306        if ([fTorrent isError])
307        {
308            NSSize errorIconSize = [fErrorImage size];
309            [fErrorImage drawAtPoint: NSMakePoint(pen.x + iconSize.width - errorIconSize.width,
310                                                    pen.y + iconSize.height  - errorIconSize.height)
311                fromRect: NSMakeRect(0, 0, errorIconSize.width, errorIconSize.height)
312                operation: NSCompositeSourceOver fraction: 1.0];
313        }
314
315        float mainWidth = cellFrame.size.width - iconSize.width - 3.0 * PADDING - EXTRA_NAME_SHIFT;
316
317        //name string
318        pen.x += iconSize.width + PADDING + EXTRA_NAME_SHIFT;
319        pen.y = cellFrame.origin.y + PADDING;
320        NSAttributedString * nameString = [[fTorrent name] attributedStringFittingInWidth: mainWidth
321                                                attributes: nameAttributes];
322        [nameString drawAtPoint: pen];
323       
324        //progress string
325        pen.y += [nameString size].height + LINE_PADDING - 1.0;
326       
327        NSAttributedString * progressString = [[fTorrent progressString]
328            attributedStringFittingInWidth: mainWidth attributes: statusAttributes];
329        [progressString drawAtPoint: pen];
330
331        //progress bar
332        pen.x -= EXTRA_NAME_SHIFT;
333        pen.y += [progressString size].height + LINE_PADDING + BAR_HEIGHT;
334       
335        float barWidth = mainWidth + EXTRA_NAME_SHIFT - BUTTONS_TOTAL_WIDTH + PADDING;
336       
337        if ([fDefaults boolForKey: @"UseAdvancedBar"])
338            [self buildAdvancedBar: barWidth point: pen];
339        else
340            [self buildSimpleBar: barWidth point: pen];
341
342        //status string
343        pen.x += EXTRA_NAME_SHIFT;
344        pen.y += LINE_PADDING;
345        NSAttributedString * statusString = [[fTorrent statusString]
346            attributedStringFittingInWidth: mainWidth attributes: statusAttributes];
347        [statusString drawAtPoint: pen];
348    }
349    else //small size
350    {
351        //icon
352        NSImage * icon = ![fTorrent isError] ? [fTorrent iconSmall] : fErrorImage;
353        NSSize iconSize = [icon size];
354       
355        pen.x += PADDING;
356        pen.y += (cellFrame.size.height - iconSize.height) * 0.5;
357       
358        [icon drawAtPoint: pen fromRect: NSMakeRect(0, 0, iconSize.width, iconSize.height)
359                operation: NSCompositeSourceOver fraction: 1.0];
360
361        //name and status string
362        float mainWidth = cellFrame.size.width - iconSize.width - 3.0 * PADDING - EXTRA_NAME_SHIFT;
363       
364        NSString * realStatusString = !fStatusRegular && [fTorrent isActive] ? [fTorrent remainingTimeString]
365                                                                            : [fTorrent shortStatusString];
366       
367        NSAttributedString * statusString = [[[NSAttributedString alloc] initWithString: realStatusString
368                                                    attributes: statusAttributes] autorelease];
369        NSAttributedString * nameString = [[fTorrent name] attributedStringFittingInWidth:
370                                mainWidth - [statusString size].width - LINE_PADDING attributes: nameAttributes];
371                     
372        //place name string
373        pen.x += iconSize.width + PADDING + EXTRA_NAME_SHIFT;
374        pen.y = cellFrame.origin.y + LINE_PADDING;
375
376        [nameString drawAtPoint: pen];
377       
378        //place status string
379        pen.x = NSMaxX(cellFrame) - PADDING - [statusString size].width;
380        pen.y += ([nameString size].height - [statusString size].height) * 0.5;
381       
382        [statusString drawAtPoint: pen];
383       
384        //progress bar
385        pen.x = cellFrame.origin.x + iconSize.width + 2.0 * PADDING;
386        pen.y = cellFrame.origin.y + [nameString size].height + LINE_PADDING + PADDING + BAR_HEIGHT;
387       
388        float barWidth = mainWidth + EXTRA_NAME_SHIFT - BUTTONS_TOTAL_WIDTH + PADDING;
389       
390        if ([fDefaults boolForKey: @"UseAdvancedBar"])
391            [self buildAdvancedBar: barWidth point: pen];
392        else
393            [self buildSimpleBar: barWidth point: pen];
394    }
395   
396    [nameAttributes release];
397    [statusAttributes release];
398}
399
400@end
Note: See TracBrowser for help on using the repository browser.