source: trunk/macosx/TorrentCell.m @ 10399

Last change on this file since 10399 was 10399, checked in by livings124, 12 years ago

only outline the progress bar in non-compact view

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