source: trunk/macosx/TorrentCell.m @ 12626

Last change on this file since 12626 was 12626, checked in by livings124, 10 years ago

change the (temporary) name at the cell level, not the torrent level

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