1/****************************************************************************** 2 * Copyright (c) 2007-2012 Transmission authors and contributors 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 * DEALINGS IN THE SOFTWARE. 21 *****************************************************************************/ 22 23#include <libtransmission/transmission.h> 24#include <libtransmission/utils.h> 25 26#import "FileNameCell.h" 27#import "FileOutlineView.h" 28#import "Torrent.h" 29#import "FileListNode.h" 30#import "NSStringAdditions.h" 31 32#define PADDING_HORIZONAL 2.0 33#define IMAGE_FOLDER_SIZE 16.0 34#define IMAGE_ICON_SIZE 32.0 35#define PADDING_BETWEEN_IMAGE_AND_TITLE 4.0 36#define PADDING_ABOVE_TITLE_FILE 2.0 37#define PADDING_BELOW_STATUS_FILE 2.0 38#define PADDING_BETWEEN_NAME_AND_FOLDER_STATUS 4.0 39#define PADDING_EXPANSION_FRAME 2.0 40 41@interface FileNameCell (Private) 42 43- (NSRect) rectForTitleWithString: (NSAttributedString *) string inBounds: (NSRect) bounds; 44- (NSRect) rectForStatusWithString: (NSAttributedString *) string withTitleRect: (NSRect) titleRect inBounds: (NSRect) bounds; 45 46- (NSAttributedString *) attributedTitle; 47- (NSAttributedString *) attributedStatus; 48 49@end 50 51@implementation FileNameCell 52 53- (id) init 54{ 55 if ((self = [super init])) 56 { 57 NSMutableParagraphStyle * paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; 58 [paragraphStyle setLineBreakMode: NSLineBreakByTruncatingMiddle]; 59 60 fTitleAttributes = [[NSMutableDictionary alloc] initWithObjectsAndKeys: 61 [NSFont messageFontOfSize: 12.0], NSFontAttributeName, 62 paragraphStyle, NSParagraphStyleAttributeName, nil]; 63 64 NSMutableParagraphStyle * statusParagraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; 65 [statusParagraphStyle setLineBreakMode: NSLineBreakByTruncatingTail]; 66 67 fStatusAttributes = [[NSMutableDictionary alloc] initWithObjectsAndKeys: 68 [NSFont messageFontOfSize: 9.0], NSFontAttributeName, 69 statusParagraphStyle, NSParagraphStyleAttributeName, nil]; 70 71 } 72 return self; 73} 74 75 76- (id) copyWithZone: (NSZone *) zone 77{ 78 FileNameCell * copy = [super copyWithZone: zone]; 79 80 copy->fTitleAttributes = fTitleAttributes; 81 copy->fStatusAttributes = fStatusAttributes; 82 83 return copy; 84} 85 86- (NSImage *) image 87{ 88 FileListNode * node = (FileListNode *)[self objectValue]; 89 return [node icon]; 90} 91 92- (NSRect) imageRectForBounds: (NSRect) bounds 93{ 94 NSRect result = bounds; 95 96 result.origin.x += PADDING_HORIZONAL; 97 98 const CGFloat IMAGE_SIZE = [(FileListNode *)[self objectValue] isFolder] ? IMAGE_FOLDER_SIZE : IMAGE_ICON_SIZE; 99 result.origin.y += (result.size.height - IMAGE_SIZE) * 0.5; 100 result.size = NSMakeSize(IMAGE_SIZE, IMAGE_SIZE); 101 102 return result; 103} 104 105- (void) drawWithFrame: (NSRect) cellFrame inView: (NSView *) controlView 106{ 107 //icon 108 [[self image] drawInRect: [self imageRectForBounds: cellFrame] fromRect: NSZeroRect operation: NSCompositeSourceOver fraction: 1.0 respectFlipped: YES hints: nil]; 109 110 NSColor * titleColor, * statusColor; 111 if ([self backgroundStyle] == NSBackgroundStyleDark) 112 titleColor = statusColor = [NSColor whiteColor]; 113 else if ([[(FileListNode *)[self objectValue] torrent] checkForFiles: [(FileListNode *)[self objectValue] indexes]] == NSOffState) 114 titleColor = statusColor = [NSColor disabledControlTextColor]; 115 else 116 { 117 titleColor = [NSColor controlTextColor]; 118 statusColor = [NSColor secondaryLabelColor]; 119 } 120 121 fTitleAttributes[NSForegroundColorAttributeName] = titleColor; 122 fStatusAttributes[NSForegroundColorAttributeName] = statusColor; 123 124 //title 125 NSAttributedString * titleString = [self attributedTitle]; 126 NSRect titleRect = [self rectForTitleWithString: titleString inBounds: cellFrame]; 127 [titleString drawInRect: titleRect]; 128 129 //status 130 NSAttributedString * statusString = [self attributedStatus]; 131 NSRect statusRect = [self rectForStatusWithString: statusString withTitleRect: titleRect inBounds: cellFrame]; 132 [statusString drawInRect: statusRect]; 133} 134 135- (NSRect) expansionFrameWithFrame: (NSRect) cellFrame inView: (NSView *) view 136{ 137 NSAttributedString * titleString = [self attributedTitle]; 138 NSRect realRect = [self rectForTitleWithString: titleString inBounds: cellFrame]; 139 140 if ([titleString size].width > NSWidth(realRect) 141 && NSMouseInRect([view convertPoint: [[view window] mouseLocationOutsideOfEventStream] fromView: nil], realRect, [view isFlipped])) 142 { 143 realRect.size.width = [titleString size].width; 144 return NSInsetRect(realRect, -PADDING_EXPANSION_FRAME, -PADDING_EXPANSION_FRAME); 145 } 146 147 return NSZeroRect; 148} 149 150- (void) drawWithExpansionFrame: (NSRect) cellFrame inView: (NSView *)view 151{ 152 cellFrame.origin.x += PADDING_EXPANSION_FRAME; 153 cellFrame.origin.y += PADDING_EXPANSION_FRAME; 154 155 fTitleAttributes[NSForegroundColorAttributeName] = [NSColor controlTextColor]; 156 NSAttributedString * titleString = [self attributedTitle]; 157 [titleString drawInRect: cellFrame]; 158} 159 160@end 161 162@implementation FileNameCell (Private) 163 164- (NSRect) rectForTitleWithString: (NSAttributedString *) string inBounds: (NSRect) bounds 165{ 166 const NSSize titleSize = [string size]; 167 168 //no right padding, so that there's not too much space between this and the priority image 169 NSRect result; 170 if (![(FileListNode *)[self objectValue] isFolder]) 171 { 172 result.origin.x = NSMinX(bounds) + PADDING_HORIZONAL + IMAGE_ICON_SIZE + PADDING_BETWEEN_IMAGE_AND_TITLE; 173 result.origin.y = NSMinY(bounds) + PADDING_ABOVE_TITLE_FILE; 174 result.size.width = NSMaxX(bounds) - NSMinX(result); 175 } 176 else 177 { 178 result.origin.x = NSMinX(bounds) + PADDING_HORIZONAL + IMAGE_FOLDER_SIZE + PADDING_BETWEEN_IMAGE_AND_TITLE; 179 result.origin.y = NSMidY(bounds) - titleSize.height * 0.5; 180 result.size.width = MIN(titleSize.width, NSMaxX(bounds) - NSMinX(result)); 181 } 182 result.size.height = titleSize.height; 183 184 return result; 185} 186 187- (NSRect) rectForStatusWithString: (NSAttributedString *) string withTitleRect: (NSRect) titleRect inBounds: (NSRect) bounds 188{ 189 const NSSize statusSize = [string size]; 190 191 NSRect result; 192 if (![(FileListNode *)[self objectValue] isFolder]) 193 { 194 result.origin.x = NSMinX(titleRect); 195 result.origin.y = NSMaxY(bounds) - PADDING_BELOW_STATUS_FILE - statusSize.height; 196 result.size.width = NSWidth(titleRect); 197 } 198 else 199 { 200 result.origin.x = NSMaxX(titleRect) + PADDING_BETWEEN_NAME_AND_FOLDER_STATUS; 201 result.origin.y = NSMaxY(titleRect) - statusSize.height - 1.0; 202 result.size.width = NSMaxX(bounds) - NSMaxX(titleRect); 203 } 204 result.size.height = statusSize.height; 205 206 return result; 207} 208 209- (NSAttributedString *) attributedTitle 210{ 211 NSString * title = [(FileListNode *)[self objectValue] name]; 212 return [[NSAttributedString alloc] initWithString: title attributes: fTitleAttributes]; 213} 214 215- (NSAttributedString *) attributedStatus 216{ 217 FileListNode * node = (FileListNode *)[self objectValue]; 218 Torrent * torrent = [node torrent]; 219 220 const CGFloat progress = [torrent fileProgress: node]; 221 NSString * percentString = [NSString percentString: progress longDecimals: YES]; 222 223 NSString * status = [NSString stringWithFormat: NSLocalizedString(@"%@ of %@", 224 "Inspector -> Files tab -> file status string"), percentString, [NSString stringForFileSize: [node size]]]; 225 226 return [[NSAttributedString alloc] initWithString: status attributes: fStatusAttributes]; 227} 228 229@end 230