source: trunk/macosx/TorrentCell.m @ 10405

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

more adjustments to compact view

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