source: trunk/macosx/TorrentCell.m @ 6125

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

the pieces bar and pieces view now show shades of blue/green based on progress/availability instead of preset "levels" of color - in other words, it mixes white with either blue or green where the amount of color is the exact percentage of the piece that has been downloaded

  • Property svn:keywords set to Date Rev Author Id
File size: 32.0 KB
Line 
1/******************************************************************************
2 * $Id: TorrentCell.m 6125 2008-06-10 19:56:53Z 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 "TorrentCell.h"
26#import "TorrentTableView.h"
27#import "GroupsController.h"
28#import "NSApplicationAdditions.h"
29#import "NSStringAdditions.h"
30#import "NSBezierPathAdditions.h"
31#import "CTGradientAdditions.h"
32
33#define BAR_HEIGHT 12.0
34
35#define IMAGE_SIZE_REG 32.0
36#define IMAGE_SIZE_MIN 16.0
37
38#define NORMAL_BUTTON_WIDTH 14.0
39#define ACTION_BUTTON_WIDTH 16.0
40
41//ends up being larger than font height
42#define HEIGHT_TITLE 16.0
43#define HEIGHT_STATUS 12.0
44
45#define PADDING_HORIZONTAL 3.0
46#define PADDING_BETWEEN_IMAGE_AND_TITLE 5.0
47#define PADDING_BETWEEN_IMAGE_AND_BAR 7.0
48#define PADDING_ABOVE_TITLE 4.0
49#define PADDING_ABOVE_MIN_STATUS 4.0
50#define PADDING_BETWEEN_TITLE_AND_MIN_STATUS 2.0
51#define PADDING_BETWEEN_TITLE_AND_PROGRESS 1.0
52#define PADDING_BETWEEN_PROGRESS_AND_BAR 2.0
53#define PADDING_BETWEEN_TITLE_AND_BAR_MIN 3.0
54#define PADDING_BETWEEN_BAR_AND_STATUS 2.0
55
56#define PIECES_TOTAL_PERCENT 0.6
57
58#define MAX_PIECES (18*18)
59
60@interface TorrentCell (Private)
61
62- (void) drawBar: (NSRect) barRect;
63- (void) drawRegularBar: (NSRect) barRect;
64- (void) drawPiecesBar: (NSRect) barRect;
65
66- (NSRect) rectForMinimalStatusWithString: (NSAttributedString *) string inBounds: (NSRect) bounds;
67- (NSRect) rectForTitleWithString: (NSAttributedString *) string basedOnMinimalStatusRect: (NSRect) statusRect
68            inBounds: (NSRect) bounds;
69- (NSRect) rectForProgressWithString: (NSAttributedString *) string inBounds: (NSRect) bounds;
70- (NSRect) rectForStatusWithString: (NSAttributedString *) string inBounds: (NSRect) bounds;
71
72- (NSAttributedString *) attributedTitleWithColor: (NSColor *) color;
73- (NSAttributedString *) attributedStatusString: (NSString *) string withColor: (NSColor *) color;
74
75- (NSString *) buttonString;
76- (NSString *) statusString;
77- (NSString *) minimalStatusString;
78
79@end
80
81@implementation TorrentCell
82
83//only called once and the main table is always needed, so don't worry about releasing
84- (id) init
85{
86    if ((self = [super init]))
87        {
88        fDefaults = [NSUserDefaults standardUserDefaults];
89       
90        NSMutableParagraphStyle * paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
91        [paragraphStyle setLineBreakMode: NSLineBreakByTruncatingTail];
92   
93        fTitleAttributes = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
94                                [NSFont messageFontOfSize: 12.0], NSFontAttributeName,
95                                paragraphStyle, NSParagraphStyleAttributeName, nil];
96        fStatusAttributes = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
97                                [NSFont messageFontOfSize: 9.0], NSFontAttributeName,
98                                paragraphStyle, NSParagraphStyleAttributeName, nil];
99        [paragraphStyle release];
100       
101        fBluePieceColor = [[NSColor colorWithCalibratedRed: 0.0 green: 0.4 blue: 0.8 alpha: 1.0] retain];
102        fBarBorderColor = [[NSColor colorWithDeviceWhite: 0.0 alpha: 0.2] retain];
103    }
104        return self;
105}
106
107- (id) copyWithZone: (NSZone *) zone
108{
109    TorrentCell * copy = [super copyWithZone: zone];
110   
111    copy->fGrayGradient = [fGrayGradient retain];
112    copy->fLightGrayGradient = [fLightGrayGradient retain];
113    copy->fBlueGradient = [fBlueGradient retain];
114    copy->fDarkBlueGradient = [fDarkBlueGradient retain];
115    copy->fGreenGradient = [fGreenGradient retain];
116    copy->fLightGreenGradient = [fLightGreenGradient retain];
117    copy->fDarkGreenGradient = [fDarkGreenGradient retain];
118    copy->fYellowGradient = [fYellowGradient retain];
119    copy->fRedGradient = [fRedGradient retain];
120   
121    return copy;
122}
123
124- (void) dealloc
125{
126    [fGrayGradient release];
127    [fLightGrayGradient release];
128    [fBlueGradient release];
129    [fDarkBlueGradient release];
130    [fGreenGradient release];
131    [fLightGreenGradient release];
132    [fDarkGreenGradient release];
133    [fYellowGradient release];
134    [fRedGradient release];
135   
136    [super dealloc];
137}
138
139- (NSRect) iconRectForBounds: (NSRect) bounds
140{
141    float imageSize = [fDefaults boolForKey: @"SmallView"] ? IMAGE_SIZE_MIN : IMAGE_SIZE_REG;
142   
143    NSRect result = bounds;
144    result.origin.x += PADDING_HORIZONTAL;
145    result.origin.y += floorf((result.size.height - imageSize) * 0.5);
146    result.size = NSMakeSize(imageSize, imageSize);
147   
148    return result;
149}
150
151- (NSRect) titleRectForBounds: (NSRect) bounds
152{
153    return [self rectForTitleWithString: [self attributedTitleWithColor: nil]
154            basedOnMinimalStatusRect: [self minimalStatusRectForBounds: bounds] inBounds: bounds];
155}
156
157- (NSRect) minimalStatusRectForBounds: (NSRect) bounds
158{
159    if (![fDefaults boolForKey: @"SmallView"])
160        return NSZeroRect;
161   
162    return [self rectForMinimalStatusWithString: [self attributedStatusString: [self minimalStatusString] withColor: nil]
163            inBounds: bounds];
164}
165
166- (NSRect) progressRectForBounds: (NSRect) bounds
167{
168    if ([fDefaults boolForKey: @"SmallView"])
169        return NSZeroRect;
170   
171    return [self rectForProgressWithString: [self attributedStatusString: [[self representedObject] progressString] withColor: nil]
172            inBounds: bounds];
173}
174
175- (NSRect) barRectForBounds: (NSRect) bounds
176{
177    BOOL minimal = [fDefaults boolForKey: @"SmallView"];
178   
179    NSRect result = bounds;
180    result.size.height = BAR_HEIGHT;
181    result.origin.x += (minimal ? IMAGE_SIZE_MIN : IMAGE_SIZE_REG) + PADDING_BETWEEN_IMAGE_AND_BAR;
182   
183    result.origin.y += PADDING_ABOVE_TITLE + HEIGHT_TITLE;
184    if (minimal)
185        result.origin.y += PADDING_BETWEEN_TITLE_AND_BAR_MIN;
186    else
187        result.origin.y += PADDING_BETWEEN_TITLE_AND_PROGRESS + HEIGHT_STATUS + PADDING_BETWEEN_PROGRESS_AND_BAR;
188   
189    result.size.width = round(NSMaxX(bounds) - result.origin.x - PADDING_HORIZONTAL - 2.0 * (PADDING_HORIZONTAL + NORMAL_BUTTON_WIDTH));
190   
191    return result;
192}
193
194- (NSRect) statusRectForBounds: (NSRect) bounds
195{
196    if ([fDefaults boolForKey: @"SmallView"])
197        return NSZeroRect;
198   
199    return [self rectForStatusWithString: [self attributedStatusString: [self statusString] withColor: nil] inBounds: bounds];
200}
201
202- (NSRect) controlButtonRectForBounds: (NSRect) bounds
203{
204    NSRect result = bounds;
205    result.size.height = NORMAL_BUTTON_WIDTH;
206    result.size.width = NORMAL_BUTTON_WIDTH;
207    result.origin.x = NSMaxX(bounds) - 2.0 * (PADDING_HORIZONTAL + NORMAL_BUTTON_WIDTH);
208   
209    result.origin.y += PADDING_ABOVE_TITLE + HEIGHT_TITLE - (NORMAL_BUTTON_WIDTH - BAR_HEIGHT) * 0.5;
210    if ([fDefaults boolForKey: @"SmallView"])
211        result.origin.y += PADDING_BETWEEN_TITLE_AND_BAR_MIN;
212    else
213        result.origin.y += PADDING_BETWEEN_TITLE_AND_PROGRESS + HEIGHT_STATUS + PADDING_BETWEEN_PROGRESS_AND_BAR;
214   
215    return result;
216}
217
218- (NSRect) revealButtonRectForBounds: (NSRect) bounds
219{
220    NSRect result = bounds;
221    result.size.height = NORMAL_BUTTON_WIDTH;
222    result.size.width = NORMAL_BUTTON_WIDTH;
223    result.origin.x = NSMaxX(bounds) - (PADDING_HORIZONTAL + NORMAL_BUTTON_WIDTH);
224   
225    result.origin.y += PADDING_ABOVE_TITLE + HEIGHT_TITLE - (NORMAL_BUTTON_WIDTH - BAR_HEIGHT) * 0.5;
226    if ([fDefaults boolForKey: @"SmallView"])
227        result.origin.y += PADDING_BETWEEN_TITLE_AND_BAR_MIN;
228    else
229        result.origin.y += PADDING_BETWEEN_TITLE_AND_PROGRESS + HEIGHT_STATUS + PADDING_BETWEEN_PROGRESS_AND_BAR;
230   
231    return result;
232}
233
234- (NSRect) actionButtonRectForBounds: (NSRect) bounds
235{
236    NSRect result = [self iconRectForBounds: bounds];
237    if (![fDefaults boolForKey: @"SmallView"])
238    {
239        result.origin.x += (result.size.width - ACTION_BUTTON_WIDTH) * 0.5;
240        result.origin.y += (result.size.height - ACTION_BUTTON_WIDTH) * 0.5;
241        result.size.width = ACTION_BUTTON_WIDTH;
242        result.size.height = ACTION_BUTTON_WIDTH;
243    }
244   
245    return result;
246}
247
248- (NSUInteger) hitTestForEvent: (NSEvent *) event inRect: (NSRect) cellFrame ofView: (NSView *) controlView
249{
250    NSPoint point = [controlView convertPoint: [event locationInWindow] fromView: nil];
251   
252    if (NSMouseInRect(point, [self controlButtonRectForBounds: cellFrame], [controlView isFlipped])
253        || NSMouseInRect(point, [self revealButtonRectForBounds: cellFrame], [controlView isFlipped]))
254        return NSCellHitContentArea | NSCellHitTrackableArea;
255   
256    return NSCellHitContentArea;
257}
258
259+ (BOOL) prefersTrackingUntilMouseUp
260{
261    return YES;
262}
263
264- (BOOL) trackMouse: (NSEvent *) event inRect: (NSRect) cellFrame ofView: (NSView *) controlView untilMouseUp: (BOOL) flag
265{
266    fTracking = YES;
267   
268    [self setControlView: controlView];
269   
270    NSPoint point = [controlView convertPoint: [event locationInWindow] fromView: nil];
271   
272    NSRect controlRect= [self controlButtonRectForBounds: cellFrame];
273    BOOL checkControl = NSMouseInRect(point, controlRect, [controlView isFlipped]);
274   
275    NSRect revealRect = [self revealButtonRectForBounds: cellFrame];
276    BOOL checkReveal = NSMouseInRect(point, revealRect, [controlView isFlipped]);
277   
278    [(TorrentTableView *)controlView removeButtonTrackingAreas];
279   
280    while ([event type] != NSLeftMouseUp)
281    {
282        point = [controlView convertPoint: [event locationInWindow] fromView: nil];
283       
284        if (checkControl)
285        {
286            BOOL inControlButton = NSMouseInRect(point, controlRect, [controlView isFlipped]);
287            if (fMouseDownControlButton != inControlButton)
288            {
289                fMouseDownControlButton = inControlButton;
290                [controlView setNeedsDisplayInRect: cellFrame];
291            }
292        }
293        else if (checkReveal)
294        {
295            BOOL inRevealButton = NSMouseInRect(point, revealRect, [controlView isFlipped]);
296            if (fMouseDownRevealButton != inRevealButton)
297            {
298                fMouseDownRevealButton = inRevealButton;
299                [controlView setNeedsDisplayInRect: cellFrame];
300            }
301        }
302        else;
303       
304        //send events to where necessary
305        if ([event type] == NSMouseEntered || [event type] == NSMouseExited)
306            [NSApp sendEvent: event];
307        event = [[controlView window] nextEventMatchingMask:
308                    (NSLeftMouseUpMask | NSLeftMouseDraggedMask | NSMouseEnteredMask | NSMouseExitedMask)];
309    }
310   
311    fTracking = NO;
312
313    if (fMouseDownControlButton)
314    {
315        fMouseDownControlButton = NO;
316       
317        [(TorrentTableView *)controlView toggleControlForTorrent: [self representedObject]];
318    }
319    else if (fMouseDownRevealButton)
320    {
321        fMouseDownRevealButton = NO;
322        [controlView setNeedsDisplayInRect: cellFrame];
323       
324        [[self representedObject] revealData];
325    }
326    else;
327   
328    if ([NSApp isOnLeopardOrBetter])
329        [controlView updateTrackingAreas];
330   
331    return YES;
332}
333
334- (void) addTrackingAreasForView: (NSView *) controlView inRect: (NSRect) cellFrame withUserInfo: (NSDictionary *) userInfo
335            mouseLocation: (NSPoint) mouseLocation
336{
337    NSTrackingAreaOptions options = NSTrackingEnabledDuringMouseDrag | NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways;
338   
339    //control button
340    NSRect controlButtonRect = [self controlButtonRectForBounds: cellFrame];
341    NSTrackingAreaOptions controlOptions = options;
342    if (NSMouseInRect(mouseLocation, controlButtonRect, [controlView isFlipped]))
343    {
344        controlOptions |= NSTrackingAssumeInside;
345        [(TorrentTableView *)controlView setControlButtonHover: [[userInfo objectForKey: @"Row"] intValue]];
346    }
347   
348    NSMutableDictionary * controlInfo = [userInfo mutableCopy];
349    [controlInfo setObject: @"Control" forKey: @"Type"];
350    NSTrackingArea * area = [[NSTrackingArea alloc] initWithRect: controlButtonRect options: controlOptions owner: controlView
351                                userInfo: controlInfo];
352    [controlView addTrackingArea: area];
353    [controlInfo release];
354    [area release];
355   
356    //reveal button
357    NSRect revealButtonRect = [self revealButtonRectForBounds: cellFrame];
358    NSTrackingAreaOptions revealOptions = options;
359    if (NSMouseInRect(mouseLocation, revealButtonRect, [controlView isFlipped]))
360    {
361        revealOptions |= NSTrackingAssumeInside;
362        [(TorrentTableView *)controlView setRevealButtonHover: [[userInfo objectForKey: @"Row"] intValue]];
363    }
364   
365    NSMutableDictionary * revealInfo = [userInfo mutableCopy];
366    [revealInfo setObject: @"Reveal" forKey: @"Type"];
367    area = [[NSTrackingArea alloc] initWithRect: revealButtonRect options: revealOptions owner: controlView userInfo: revealInfo];
368    [controlView addTrackingArea: area];
369    [revealInfo release];
370    [area release];
371   
372    //action button
373    NSRect actionButtonRect = [self iconRectForBounds: cellFrame]; //use the whole icon
374    NSTrackingAreaOptions actionOptions = options;
375    if (NSMouseInRect(mouseLocation, actionButtonRect, [controlView isFlipped]))
376    {
377        actionOptions |= NSTrackingAssumeInside;
378        [(TorrentTableView *)controlView setActionButtonHover: [[userInfo objectForKey: @"Row"] intValue]];
379    }
380   
381    NSMutableDictionary * actionInfo = [userInfo mutableCopy];
382    [actionInfo setObject: @"Action" forKey: @"Type"];
383    area = [[NSTrackingArea alloc] initWithRect: actionButtonRect options: actionOptions owner: controlView userInfo: actionInfo];
384    [controlView addTrackingArea: area];
385    [actionInfo release];
386    [area release];
387}
388
389- (void) setControlHover: (BOOL) hover
390{
391    fHoverControl = hover;
392}
393
394- (void) setRevealHover: (BOOL) hover
395{
396    fHoverReveal = hover;
397}
398
399- (void) setActionHover: (BOOL) hover
400{
401    fHoverAction = hover;
402}
403
404- (void) setActionPushed: (BOOL) pushed
405{
406    fMouseDownActionButton = pushed;
407}
408
409- (void) drawInteriorWithFrame: (NSRect) cellFrame inView: (NSView *) controlView
410{
411    Torrent * torrent = [self representedObject];
412   
413    BOOL minimal = [fDefaults boolForKey: @"SmallView"];
414   
415    //group coloring
416    NSRect iconRect = [self iconRectForBounds: cellFrame];
417   
418    int groupValue = [torrent groupValue];
419    if (groupValue != -1)
420    {
421        NSRect groupRect = NSInsetRect(iconRect, -1.0, -2.0);
422        if (!minimal)
423        {
424            groupRect.size.height--;
425            groupRect.origin.y--;
426        }
427        float radius = minimal ? 3.0 : 6.0;
428       
429        NSColor * groupColor = [[GroupsController groups] colorForIndex: groupValue],
430                * darkGroupColor = [groupColor blendedColorWithFraction: 0.2 ofColor: [NSColor whiteColor]];
431       
432        //border
433        NSBezierPath * bp = [NSBezierPath bezierPathWithRoundedRect: groupRect radius: radius];
434        [darkGroupColor set];
435        [bp setLineWidth: 2.0];
436        [bp stroke];
437       
438        //inside
439        bp = [NSBezierPath bezierPathWithRoundedRect: groupRect radius: radius];
440        CTGradient * gradient = [CTGradient gradientWithBeginningColor: [groupColor blendedColorWithFraction: 0.7
441                                    ofColor: [NSColor whiteColor]] endingColor: darkGroupColor];
442        [gradient fillBezierPath: bp angle: 90.0];
443    }
444   
445    //error image
446    BOOL error = [torrent isError];
447    if (error && !fErrorImage)
448    {
449        fErrorImage = [NSImage imageNamed: @"Error.png"];
450        [fErrorImage setFlipped: YES];
451    }
452   
453    //icon
454    if (!minimal || !(!fTracking && fHoverAction)) //don't show in minimal mode when hovered over
455    {
456        NSImage * icon = (minimal && error) ? fErrorImage : [torrent icon];
457        [icon drawInRect: iconRect fromRect: NSZeroRect operation: NSCompositeSourceOver fraction: 1.0];
458    }
459   
460    if (error && !minimal)
461    {
462        NSRect errorRect = NSMakeRect(NSMaxX(iconRect) - IMAGE_SIZE_MIN, NSMaxY(iconRect) - IMAGE_SIZE_MIN,
463                                        IMAGE_SIZE_MIN, IMAGE_SIZE_MIN);
464        [fErrorImage drawInRect: errorRect fromRect: NSZeroRect operation: NSCompositeSourceOver fraction: 1.0];
465    }
466   
467    //text color
468    NSColor * titleColor, * statusColor;
469    if ([self isHighlighted]
470        && [[self highlightColorWithFrame: cellFrame inView: controlView] isEqual: [NSColor alternateSelectedControlColor]])
471    {
472        titleColor = [NSColor whiteColor];
473        statusColor = [NSColor whiteColor];
474    }
475    else
476    {
477        titleColor = [NSColor controlTextColor];
478        statusColor = [NSColor darkGrayColor];
479    }
480   
481    //minimal status
482    NSRect minimalStatusRect;
483    if (minimal)
484    {
485        NSAttributedString * minimalString = [self attributedStatusString: [self minimalStatusString] withColor: statusColor];
486        minimalStatusRect = [self rectForMinimalStatusWithString: minimalString inBounds: cellFrame];
487       
488        [minimalString drawInRect: minimalStatusRect];
489    }
490   
491    //title
492    NSAttributedString * titleString = [self attributedTitleWithColor: titleColor];
493    NSRect titleRect = [self rectForTitleWithString: titleString basedOnMinimalStatusRect: minimalStatusRect inBounds: cellFrame];
494    [titleString drawInRect: titleRect];
495   
496    //progress
497    if (!minimal)
498    {
499        NSAttributedString * progressString = [self attributedStatusString: [torrent progressString] withColor: statusColor];
500        NSRect progressRect = [self rectForProgressWithString: progressString inBounds: cellFrame];
501       
502        [progressString drawInRect: progressRect];
503    }
504   
505    //bar
506    [self drawBar: [self barRectForBounds: cellFrame]];
507   
508    //control button
509    NSString * controlImageSuffix;
510    if (fMouseDownControlButton)
511        controlImageSuffix = @"On.png";
512    else if (!fTracking && fHoverControl)
513        controlImageSuffix = @"Hover.png";
514    else
515        controlImageSuffix = @"Off.png";
516   
517    NSImage * controlImage;
518    if ([torrent isActive])
519        controlImage = [NSImage imageNamed: [@"Pause" stringByAppendingString: controlImageSuffix]];
520    else
521    {
522        if ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask)
523            controlImage = [NSImage imageNamed: [@"ResumeNoWait" stringByAppendingString: controlImageSuffix]];
524        else if ([torrent waitingToStart])
525            controlImage = [NSImage imageNamed: [@"Pause" stringByAppendingString: controlImageSuffix]];
526        else
527            controlImage = [NSImage imageNamed: [@"Resume" stringByAppendingString: controlImageSuffix]];
528    }
529   
530    [controlImage setFlipped: YES];
531    [controlImage drawInRect: [self controlButtonRectForBounds: cellFrame] fromRect: NSZeroRect operation: NSCompositeSourceOver
532        fraction: 1.0];
533   
534    //reveal button
535    NSString * revealImageString;
536    if (fMouseDownRevealButton)
537        revealImageString = @"RevealOn.png";
538    else if (!fTracking && fHoverReveal)
539        revealImageString = @"RevealHover.png";
540    else
541        revealImageString = @"RevealOff.png";
542   
543    NSImage * revealImage = [NSImage imageNamed: revealImageString];
544    [revealImage setFlipped: YES];
545    [revealImage drawInRect: [self revealButtonRectForBounds: cellFrame] fromRect: NSZeroRect operation: NSCompositeSourceOver
546        fraction: 1.0];
547   
548    //action button
549    NSString * actionImageString;
550    if (fMouseDownActionButton)
551        actionImageString = @"ActionOn.png";
552    else if (!fTracking && fHoverAction)
553        actionImageString = @"ActionHover.png";
554    else
555        actionImageString = nil;
556   
557    if (actionImageString)
558    {
559        NSImage * actionImage = [NSImage imageNamed: actionImageString];
560        [actionImage setFlipped: YES];
561        [actionImage drawInRect: [self actionButtonRectForBounds: cellFrame] fromRect: NSZeroRect operation: NSCompositeSourceOver
562            fraction: 1.0];
563    }
564   
565    //status
566    if (!minimal)
567    {
568        NSAttributedString * statusString = [self attributedStatusString: [self statusString] withColor: statusColor];
569        [statusString drawInRect: [self rectForStatusWithString: statusString inBounds: cellFrame]];
570    }
571}
572
573@end
574
575@implementation TorrentCell (Private)
576
577- (void) drawBar: (NSRect) barRect
578{
579    float piecesBarPercent = [(TorrentTableView *)[self controlView] piecesBarPercent];
580    if (piecesBarPercent > 0.0)
581    {
582        NSRect regularBarRect = barRect, piecesBarRect = barRect;
583        piecesBarRect.size.height *= PIECES_TOTAL_PERCENT * piecesBarPercent;
584        regularBarRect.size.height -= piecesBarRect.size.height;
585        piecesBarRect.origin.y += regularBarRect.size.height;
586       
587        [self drawRegularBar: regularBarRect];
588        [self drawPiecesBar: piecesBarRect];
589    }
590    else
591    {
592        [[self representedObject] setPreviousAmountFinished: NULL];
593       
594        [self drawRegularBar: barRect];
595    }
596   
597    [fBarBorderColor set];
598    [NSBezierPath strokeRect: NSInsetRect(barRect, 0.5, 0.5)];
599}
600
601- (void) drawRegularBar: (NSRect) barRect
602{
603    Torrent * torrent = [self representedObject];
604   
605    int leftWidth = barRect.size.width;
606    float progress = [torrent progress];
607   
608    if (progress < 1.0)
609    {
610        float rightProgress = 1.0 - progress, progressLeft = [torrent progressLeft];
611        int rightWidth = leftWidth * rightProgress;
612        leftWidth -= rightWidth;
613       
614        if (progressLeft < rightProgress)
615        {
616            int rightNoIncludeWidth = rightWidth * ((rightProgress - progressLeft) / rightProgress);
617            rightWidth -= rightNoIncludeWidth;
618           
619            NSRect noIncludeRect = barRect;
620            noIncludeRect.origin.x += barRect.size.width - rightNoIncludeWidth;
621            noIncludeRect.size.width = rightNoIncludeWidth;
622           
623            if (!fLightGrayGradient)
624                fLightGrayGradient = [[CTGradient progressLightGrayGradient] retain];
625            [fLightGrayGradient fillRect: noIncludeRect angle: -90];
626        }
627       
628        if (rightWidth > 0)
629        {
630            if ([torrent isActive] && ![torrent allDownloaded] && ![torrent isChecking]
631                && [fDefaults boolForKey: @"DisplayProgressBarAvailable"])
632            {
633                int notAvailableWidth = ceil(rightWidth * [torrent notAvailableDesired]);
634                if (notAvailableWidth > 0)
635                {
636                    rightWidth -= notAvailableWidth;
637                   
638                    NSRect notAvailableRect = barRect;
639                    notAvailableRect.origin.x += leftWidth + rightWidth;
640                    notAvailableRect.size.width = notAvailableWidth;
641                   
642                    if (!fRedGradient)
643                        fRedGradient = [[CTGradient progressRedGradient] retain];
644                    [fRedGradient fillRect: notAvailableRect angle: -90];
645                }
646            }
647           
648            if (rightWidth > 0)
649            {
650                NSRect includeRect = barRect;
651                includeRect.origin.x += leftWidth;
652                includeRect.size.width = rightWidth;
653               
654                if (!fWhiteGradient)
655                    fWhiteGradient = [[CTGradient progressWhiteGradient] retain];
656                [fWhiteGradient fillRect: includeRect angle: -90];
657            }
658        }
659    }
660   
661    if (leftWidth > 0)
662    {
663        NSRect completeRect = barRect;
664        completeRect.size.width = leftWidth;
665       
666        if ([torrent isActive])
667        {
668            if ([torrent isChecking])
669            {
670                if (!fYellowGradient)
671                    fYellowGradient = [[CTGradient progressYellowGradient] retain];
672                [fYellowGradient fillRect: completeRect angle: -90];
673            }
674            else if ([torrent isSeeding])
675            {
676                int ratioLeftWidth = leftWidth * (1.0 - [torrent progressStopRatio]);
677                leftWidth -= ratioLeftWidth;
678               
679                if (ratioLeftWidth > 0)
680                {
681                    NSRect ratioLeftRect = barRect;
682                    ratioLeftRect.origin.x += leftWidth;
683                    ratioLeftRect.size.width = ratioLeftWidth;
684                   
685                    if (!fLightGreenGradient)
686                        fLightGreenGradient = [[CTGradient progressLightGreenGradient] retain];
687                    [fLightGreenGradient fillRect: ratioLeftRect angle: -90];
688                }
689               
690                if (leftWidth > 0)
691                {
692                    completeRect.size.width = leftWidth;
693                   
694                    if (!fGreenGradient)
695                        fGreenGradient = [[CTGradient progressGreenGradient] retain];
696                    [fGreenGradient fillRect: completeRect angle: -90];
697                }
698            }
699            else
700            {
701                if (!fBlueGradient)
702                    fBlueGradient = [[CTGradient progressBlueGradient] retain];
703                [fBlueGradient fillRect: completeRect angle: -90];
704            }
705        }
706        else
707        {
708            if ([torrent waitingToStart])
709            {
710                if ([torrent progressLeft] <= 0.0)
711                {
712                    if (!fDarkGreenGradient)
713                        fDarkGreenGradient = [[CTGradient progressDarkGreenGradient] retain];
714                    [fDarkGreenGradient fillRect: completeRect angle: -90];
715                }
716                else
717                {
718                    if (!fDarkBlueGradient)
719                        fDarkBlueGradient = [[CTGradient progressDarkBlueGradient] retain];
720                    [fDarkBlueGradient fillRect: completeRect angle: -90];
721                }
722            }
723            else
724            {
725                if (!fGrayGradient)
726                    fGrayGradient = [[CTGradient progressGrayGradient] retain];
727                [fGrayGradient fillRect: completeRect angle: -90];
728            }
729        }
730    }
731}
732
733- (void) drawPiecesBar: (NSRect) barRect
734{
735    Torrent * torrent = [self representedObject];
736   
737    int pieceCount = MIN([torrent pieceCount], MAX_PIECES);
738    float * piecePercent = malloc(pieceCount * sizeof(float)),
739        * previousPiecePercent = [torrent getPreviousAmountFinished];
740    [torrent getAmountFinished: piecePercent size: pieceCount];
741   
742    NSBitmapImageRep * bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes: nil
743                                    pixelsWide: pieceCount pixelsHigh: 1 bitsPerSample: 8 samplesPerPixel: 4 hasAlpha: YES
744                                    isPlanar: NO colorSpaceName: NSCalibratedRGBColorSpace bytesPerRow: 0 bitsPerPixel: 0];
745   
746    int i;
747    for (i = 0; i < pieceCount; i++)
748    {
749        NSColor * pieceColor;
750        if (piecePercent[i] == 1.0 && previousPiecePercent != NULL && previousPiecePercent[i] < 1.0)
751            pieceColor = [NSColor orangeColor];
752        else
753            pieceColor = [[NSColor whiteColor] blendedColorWithFraction: piecePercent[i] ofColor: fBluePieceColor];
754       
755        //it's faster to just set color instead of checking previous color
756        [bitmap setColor: pieceColor atX: i y: 0];
757    }
758   
759    [torrent setPreviousAmountFinished: piecePercent]; //holds onto piecePercent, so no need to release it here
760   
761    //actually draw image
762    [bitmap drawInRect: barRect];
763   
764    [bitmap release];
765}
766
767- (NSRect) rectForMinimalStatusWithString: (NSAttributedString *) string inBounds: (NSRect) bounds
768{
769    NSRect result = bounds;
770    result.size = [string size];
771   
772    result.origin.x += bounds.size.width - result.size.width - PADDING_HORIZONTAL;
773    result.origin.y += PADDING_ABOVE_MIN_STATUS;
774   
775    return result;
776}
777
778- (NSRect) rectForTitleWithString: (NSAttributedString *) string basedOnMinimalStatusRect: (NSRect) statusRect
779            inBounds: (NSRect) bounds
780{
781    BOOL minimal = [fDefaults boolForKey: @"SmallView"];
782   
783    NSRect result = bounds;
784    result.origin.y += PADDING_ABOVE_TITLE;
785    result.origin.x += PADDING_HORIZONTAL + (minimal ? IMAGE_SIZE_MIN : IMAGE_SIZE_REG) + PADDING_BETWEEN_IMAGE_AND_TITLE;
786   
787    result.size = [string size];
788    result.size.width = MIN(result.size.width, NSMaxX(bounds) - result.origin.x - PADDING_HORIZONTAL
789                            - (minimal ? PADDING_BETWEEN_TITLE_AND_MIN_STATUS + statusRect.size.width : 0));
790   
791    return result;
792}
793
794- (NSRect) rectForProgressWithString: (NSAttributedString *) string inBounds: (NSRect) bounds
795{
796    NSRect result = bounds;
797    result.origin.y += PADDING_ABOVE_TITLE + HEIGHT_TITLE + PADDING_BETWEEN_TITLE_AND_PROGRESS;
798    result.origin.x += PADDING_HORIZONTAL + IMAGE_SIZE_REG + PADDING_BETWEEN_IMAGE_AND_TITLE;
799   
800    result.size = [string size];
801    result.size.width = MIN(result.size.width, NSMaxX(bounds) - result.origin.x - PADDING_HORIZONTAL);
802   
803    return result;
804}
805
806- (NSRect) rectForStatusWithString: (NSAttributedString *) string inBounds: (NSRect) bounds
807{
808    NSRect result = bounds;
809    result.origin.y += PADDING_ABOVE_TITLE + HEIGHT_TITLE + PADDING_BETWEEN_TITLE_AND_PROGRESS + HEIGHT_STATUS
810                        + PADDING_BETWEEN_PROGRESS_AND_BAR + BAR_HEIGHT + PADDING_BETWEEN_BAR_AND_STATUS;
811    result.origin.x += PADDING_HORIZONTAL + IMAGE_SIZE_REG + PADDING_BETWEEN_IMAGE_AND_TITLE;
812   
813    result.size = [string size];
814    result.size.width = MIN(result.size.width, NSMaxX(bounds) - result.origin.x - PADDING_HORIZONTAL);
815   
816    return result;
817}
818
819- (NSAttributedString *) attributedTitleWithColor: (NSColor *) color
820{
821    if (color)
822        [fTitleAttributes setObject: color forKey: NSForegroundColorAttributeName];
823   
824    NSString * title = [[self representedObject] name];
825    return [[[NSAttributedString alloc] initWithString: title attributes: fTitleAttributes] autorelease];
826}
827
828- (NSAttributedString *) attributedStatusString: (NSString *) string withColor: (NSColor *) color
829{
830    if (color)
831        [fStatusAttributes setObject: color forKey: NSForegroundColorAttributeName];
832   
833    return [[[NSAttributedString alloc] initWithString: string attributes: fStatusAttributes] autorelease];
834}
835
836- (NSString *) buttonString
837{
838    if (fMouseDownRevealButton || (!fTracking && fHoverReveal))
839        return NSLocalizedString(@"Reveal the data file in Finder", "Torrent cell -> button info");
840    else if (fMouseDownControlButton || (!fTracking && fHoverControl))
841    {
842        Torrent * torrent = [self representedObject];
843        if ([torrent isActive])
844            return NSLocalizedString(@"Pause the transfer", "Torrent Table -> tooltip");
845        else
846        {
847            if ([[NSApp currentEvent] modifierFlags] & NSAlternateKeyMask && [fDefaults boolForKey: @"Queue"])
848                return NSLocalizedString(@"Resume the transfer right away", "Torrent cell -> button info");
849            else if ([torrent waitingToStart])
850                return NSLocalizedString(@"Stop waiting to start", "Torrent cell -> button info");
851            else
852                return NSLocalizedString(@"Resume the transfer", "Torrent cell -> button info");
853        }
854    }
855    else if (!fTracking && fHoverAction)
856        return NSLocalizedString(@"Change transfer settings", "Torrent Table -> tooltip");
857    else
858        return nil;
859}
860
861- (NSString *) statusString
862{
863    NSString * buttonString;
864    if ((buttonString = [self buttonString]))
865        return buttonString;
866    else
867        return [[self representedObject] statusString];
868}
869
870- (NSString *) minimalStatusString
871{
872    NSString * buttonString;
873    if ((buttonString = [self buttonString]))
874        return buttonString;
875    else
876    {
877        Torrent * torrent = [self representedObject];
878        return [fDefaults boolForKey: @"DisplaySmallStatusRegular"] ? [torrent shortStatusString] : [torrent remainingTimeString];
879    }
880}
881
882@end
Note: See TracBrowser for help on using the repository browser.