source: trunk/macosx/TorrentCell.m @ 7568

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

clean up the table text coloring code; trivial cleanup

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