source: trunk/macosx/TorrentCell.m @ 10407

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

appropriately shorten the title length when showing the control buttons in compact view

  • Property svn:keywords set to Date Rev Author Id
File size: 32.3 KB
Line 
1/******************************************************************************
2 * $Id: TorrentCell.m 10407 2010-03-20 18:54:24Z 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 backgroundStyle] != NSBackgroundStyleDark)
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.