source: trunk/macosx/TorrentCell.m @ 6127

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

remember the previous color of the pieces view as much as possible to avoid excessive drawing

  • Property svn:keywords set to Date Rev Author Id
File size: 32.0 KB
Line 
1/******************************************************************************
2 * $Id: TorrentCell.m 6127 2008-06-10 21:13:33Z 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 * piecesPercent = malloc(pieceCount * sizeof(float)),
739        * previousPiecePercent = [torrent getPreviousAmountFinished];
740    [torrent getAmountFinished: piecesPercent 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 (piecesPercent[i] == 1.0 && previousPiecePercent != NULL && previousPiecePercent[i] < 1.0)
751            pieceColor = [NSColor orangeColor];
752        else
753            pieceColor = [[NSColor whiteColor] blendedColorWithFraction: piecesPercent[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: piecesPercent]; //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.