source: trunk/macosx/TorrentCell.m @ 5950

Last change on this file since 5950 was 5950, checked in by livings124, 14 years ago

handle removing with rpc

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