source: trunk/macosx/TorrentCell.m @ 9487

Last change on this file since 9487 was 9487, checked in by livings124, 13 years ago

the new way of determining the unwanted width of the progress bar wasn't cutting it, so use the old way

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