source: trunk/macosx/TorrentCell.m @ 9488

Last change on this file since 9488 was 9488, checked in by livings124, 13 years ago

the red unavailable bar wasn't showing if none of the desired data was available

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