source: trunk/macosx/TorrentCell.m @ 9257

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

on 10.6 simplify the check for already-running Transmission instances

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