source: trunk/macosx/TorrentCell.m @ 10408

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

in compact view, don't show the progress bar if the cell is selected, regardless if the table has focus

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