source: trunk/macosx/TorrentCell.m @ 9752

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

restore placement of priority icon

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