source: trunk/macosx/TorrentCell.m @ 11617

Last change on this file since 11617 was 11617, checked in by livings124, 11 years ago

update the copyright years in the Mac code to 2011

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