source: trunk/macosx/TorrentCell.m @ 7032

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

even more type changes

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