source: trunk/macosx/TorrentCell.m @ 6889

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

more changes in counter scope

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