1 | /****************************************************************************** |
---|
2 | * $Id: InfoActivityViewController.m 14341 2014-10-17 05:12:00Z livings124 $ |
---|
3 | * |
---|
4 | * Copyright (c) 2010-2012 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 "InfoActivityViewController.h" |
---|
26 | #import "NSApplicationAdditions.h" |
---|
27 | #import "NSStringAdditions.h" |
---|
28 | #import "PiecesView.h" |
---|
29 | #import "Torrent.h" |
---|
30 | |
---|
31 | #include "transmission.h" // required by utils.h |
---|
32 | #include "utils.h" //tr_getRatio() |
---|
33 | |
---|
34 | #define PIECES_CONTROL_PROGRESS 0 |
---|
35 | #define PIECES_CONTROL_AVAILABLE 1 |
---|
36 | |
---|
37 | @interface InfoActivityViewController (Private) |
---|
38 | |
---|
39 | - (void) setupInfo; |
---|
40 | |
---|
41 | @end |
---|
42 | |
---|
43 | @implementation InfoActivityViewController |
---|
44 | |
---|
45 | - (id) init |
---|
46 | { |
---|
47 | if ((self = [super initWithNibName: @"InfoActivityView" bundle: nil])) |
---|
48 | { |
---|
49 | [self setTitle: NSLocalizedString(@"Activity", "Inspector view -> title")]; |
---|
50 | } |
---|
51 | |
---|
52 | return self; |
---|
53 | } |
---|
54 | |
---|
55 | - (void) awakeFromNib |
---|
56 | { |
---|
57 | [fTransferSectionLabel sizeToFit]; |
---|
58 | [fDatesSectionLabel sizeToFit]; |
---|
59 | [fTimeSectionLabel sizeToFit]; |
---|
60 | |
---|
61 | NSArray * labels = @[ fStateLabel, fProgressLabel, fHaveLabel, fDownloadedLabel, fUploadedLabel, fFailedDLLabel, fRatioLabel, fErrorLabel, fDateAddedLabel, fDateCompletedLabel, fDateActivityLabel, fDownloadTimeLabel, fSeedTimeLabel ]; |
---|
62 | |
---|
63 | CGFloat oldMaxWidth = 0.0, originX, newMaxWidth = 0.0; |
---|
64 | for (NSTextField * label in labels) |
---|
65 | { |
---|
66 | const NSRect oldFrame = [label frame]; |
---|
67 | if (oldFrame.size.width > oldMaxWidth) |
---|
68 | { |
---|
69 | oldMaxWidth = oldFrame.size.width; |
---|
70 | originX = oldFrame.origin.x; |
---|
71 | } |
---|
72 | |
---|
73 | [label sizeToFit]; |
---|
74 | const CGFloat newWidth = [label bounds].size.width; |
---|
75 | if (newWidth > newMaxWidth) |
---|
76 | newMaxWidth = newWidth; |
---|
77 | } |
---|
78 | |
---|
79 | for (NSTextField * label in labels) |
---|
80 | { |
---|
81 | NSRect frame = [label frame]; |
---|
82 | frame.origin.x = originX + (newMaxWidth - frame.size.width); |
---|
83 | [label setFrame: frame]; |
---|
84 | } |
---|
85 | |
---|
86 | NSArray * fields = @[ fDateAddedField, fDateCompletedField, fDateActivityField, fStateField, fProgressField, fHaveField, fDownloadedTotalField, fUploadedTotalField, fFailedHashField, fRatioField, fDownloadTimeField, fSeedTimeField, fErrorScrollView ]; |
---|
87 | |
---|
88 | const CGFloat widthIncrease = newMaxWidth - oldMaxWidth; |
---|
89 | for (NSView * field in fields) { |
---|
90 | NSRect frame = [field frame]; |
---|
91 | frame.origin.x += widthIncrease; |
---|
92 | frame.size.width -= widthIncrease; |
---|
93 | [field setFrame: frame]; |
---|
94 | } |
---|
95 | |
---|
96 | //set the click action of the pieces view |
---|
97 | #warning after 2.8 just hook this up in the xib |
---|
98 | [fPiecesView setAction:@selector(updatePiecesView:)]; |
---|
99 | [fPiecesView setTarget:self]; |
---|
100 | } |
---|
101 | |
---|
102 | - (void) dealloc |
---|
103 | { |
---|
104 | [[NSNotificationCenter defaultCenter] removeObserver: self]; |
---|
105 | |
---|
106 | [fTorrents release]; |
---|
107 | |
---|
108 | [super dealloc]; |
---|
109 | } |
---|
110 | |
---|
111 | - (void) setInfoForTorrents: (NSArray *) torrents |
---|
112 | { |
---|
113 | //don't check if it's the same in case the metadata changed |
---|
114 | [fTorrents release]; |
---|
115 | fTorrents = [torrents retain]; |
---|
116 | |
---|
117 | fSet = NO; |
---|
118 | } |
---|
119 | |
---|
120 | - (void) updateInfo |
---|
121 | { |
---|
122 | if (!fSet) |
---|
123 | [self setupInfo]; |
---|
124 | |
---|
125 | const NSInteger numberSelected = [fTorrents count]; |
---|
126 | if (numberSelected == 0) |
---|
127 | return; |
---|
128 | |
---|
129 | uint64_t have = 0, haveVerified = 0, downloadedTotal = 0, uploadedTotal = 0, failedHash = 0; |
---|
130 | NSDate * lastActivity = nil; |
---|
131 | for (Torrent * torrent in fTorrents) |
---|
132 | { |
---|
133 | have += [torrent haveTotal]; |
---|
134 | haveVerified += [torrent haveVerified]; |
---|
135 | downloadedTotal += [torrent downloadedTotal]; |
---|
136 | uploadedTotal += [torrent uploadedTotal]; |
---|
137 | failedHash += [torrent failedHash]; |
---|
138 | |
---|
139 | NSDate * nextLastActivity; |
---|
140 | if ((nextLastActivity = [torrent dateActivity])) |
---|
141 | lastActivity = lastActivity ? [lastActivity laterDate: nextLastActivity] : nextLastActivity; |
---|
142 | } |
---|
143 | |
---|
144 | if (have == 0) |
---|
145 | [fHaveField setStringValue: [NSString stringForFileSize: 0]]; |
---|
146 | else |
---|
147 | { |
---|
148 | NSString * verifiedString = [NSString stringWithFormat: NSLocalizedString(@"%@ verified", "Inspector -> Activity tab -> have"), |
---|
149 | [NSString stringForFileSize: haveVerified]]; |
---|
150 | if (have == haveVerified) |
---|
151 | [fHaveField setStringValue: verifiedString]; |
---|
152 | else |
---|
153 | [fHaveField setStringValue: [NSString stringWithFormat: @"%@ (%@)", [NSString stringForFileSize: have], verifiedString]]; |
---|
154 | } |
---|
155 | |
---|
156 | [fDownloadedTotalField setStringValue: [NSString stringForFileSize: downloadedTotal]]; |
---|
157 | [fUploadedTotalField setStringValue: [NSString stringForFileSize: uploadedTotal]]; |
---|
158 | [fFailedHashField setStringValue: [NSString stringForFileSize: failedHash]]; |
---|
159 | |
---|
160 | [fDateActivityField setObjectValue: lastActivity]; |
---|
161 | |
---|
162 | if (numberSelected == 1) |
---|
163 | { |
---|
164 | Torrent * torrent = [fTorrents objectAtIndex: 0]; |
---|
165 | |
---|
166 | [fStateField setStringValue: [torrent stateString]]; |
---|
167 | |
---|
168 | NSString * progressString = [NSString percentString: [torrent progress] longDecimals: YES]; |
---|
169 | if ([torrent isFolder]) |
---|
170 | { |
---|
171 | NSString * progressSelectedString = [NSString stringWithFormat: |
---|
172 | NSLocalizedString(@"%@ selected", "Inspector -> Activity tab -> progress"), |
---|
173 | [NSString percentString: [torrent progressDone] longDecimals: YES]]; |
---|
174 | progressString = [progressString stringByAppendingFormat: @" (%@)", progressSelectedString]; |
---|
175 | } |
---|
176 | [fProgressField setStringValue: progressString]; |
---|
177 | |
---|
178 | [fRatioField setStringValue: [NSString stringForRatio: [torrent ratio]]]; |
---|
179 | |
---|
180 | NSString * errorMessage = [torrent errorMessage]; |
---|
181 | if (![errorMessage isEqualToString: [fErrorMessageView string]]) |
---|
182 | [fErrorMessageView setString: errorMessage]; |
---|
183 | |
---|
184 | [fDateCompletedField setObjectValue: [torrent dateCompleted]]; |
---|
185 | |
---|
186 | //uses a relative date, so can't be set once |
---|
187 | [fDateAddedField setObjectValue: [torrent dateAdded]]; |
---|
188 | |
---|
189 | if ([NSApp isOnYosemiteOrBetter]) { |
---|
190 | static NSDateComponentsFormatter *timeFormatter; |
---|
191 | static dispatch_once_t onceToken; |
---|
192 | dispatch_once(&onceToken, ^{ |
---|
193 | timeFormatter = [NSDateComponentsFormatter new]; |
---|
194 | timeFormatter.unitsStyle = NSDateComponentsFormatterUnitsStyleShort; |
---|
195 | timeFormatter.allowedUnits = NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond; |
---|
196 | timeFormatter.zeroFormattingBehavior = NSDateComponentsFormatterZeroFormattingBehaviorDropLeading; |
---|
197 | }); |
---|
198 | |
---|
199 | [fDownloadTimeField setStringValue: [timeFormatter stringFromTimeInterval:[torrent secondsDownloading]]]; |
---|
200 | [fSeedTimeField setStringValue: [timeFormatter stringFromTimeInterval:[torrent secondsSeeding]]]; |
---|
201 | } |
---|
202 | else { |
---|
203 | [fDownloadTimeField setStringValue: [NSString timeString: [torrent secondsDownloading] includesTimeRemainingPhrase:NO showSeconds: YES]]; |
---|
204 | [fSeedTimeField setStringValue: [NSString timeString: [torrent secondsSeeding] includesTimeRemainingPhrase:NO showSeconds: YES]]; |
---|
205 | } |
---|
206 | |
---|
207 | [fPiecesView updateView]; |
---|
208 | } |
---|
209 | else if (numberSelected > 1) |
---|
210 | { |
---|
211 | [fRatioField setStringValue: [NSString stringForRatio: tr_getRatio(uploadedTotal, downloadedTotal)]]; |
---|
212 | } |
---|
213 | else; |
---|
214 | } |
---|
215 | |
---|
216 | - (void) setPiecesView: (id) sender |
---|
217 | { |
---|
218 | const BOOL availability = [sender selectedSegment] == PIECES_CONTROL_AVAILABLE; |
---|
219 | [[NSUserDefaults standardUserDefaults] setBool: availability forKey: @"PiecesViewShowAvailability"]; |
---|
220 | [self updatePiecesView:nil]; |
---|
221 | } |
---|
222 | |
---|
223 | |
---|
224 | - (void) updatePiecesView: (id) sender |
---|
225 | { |
---|
226 | const BOOL piecesAvailableSegment = [[NSUserDefaults standardUserDefaults] boolForKey: @"PiecesViewShowAvailability"]; |
---|
227 | |
---|
228 | [fPiecesControl setSelected: piecesAvailableSegment forSegment: PIECES_CONTROL_AVAILABLE]; |
---|
229 | [fPiecesControl setSelected: !piecesAvailableSegment forSegment: PIECES_CONTROL_PROGRESS]; |
---|
230 | |
---|
231 | [fPiecesView updateView]; |
---|
232 | } |
---|
233 | |
---|
234 | - (void) clearView |
---|
235 | { |
---|
236 | [fPiecesView clearView]; |
---|
237 | } |
---|
238 | |
---|
239 | @end |
---|
240 | |
---|
241 | @implementation InfoActivityViewController (Private) |
---|
242 | |
---|
243 | - (void) setupInfo |
---|
244 | { |
---|
245 | const NSUInteger count = [fTorrents count]; |
---|
246 | if (count != 1) |
---|
247 | { |
---|
248 | if (count == 0) |
---|
249 | { |
---|
250 | [fHaveField setStringValue: @""]; |
---|
251 | [fDownloadedTotalField setStringValue: @""]; |
---|
252 | [fUploadedTotalField setStringValue: @""]; |
---|
253 | [fFailedHashField setStringValue: @""]; |
---|
254 | [fDateActivityField setObjectValue: @""]; //using [field setStringValue: @""] causes "December 31, 1969 7:00 PM" to be displayed, at least on 10.7.3 |
---|
255 | [fRatioField setStringValue: @""]; |
---|
256 | } |
---|
257 | |
---|
258 | [fStateField setStringValue: @""]; |
---|
259 | [fProgressField setStringValue: @""]; |
---|
260 | |
---|
261 | [fErrorMessageView setString: @""]; |
---|
262 | |
---|
263 | //using [field setStringValue: @""] causes "December 31, 1969 7:00 PM" to be displayed, at least on 10.7.3 |
---|
264 | [fDateAddedField setObjectValue: @""]; |
---|
265 | [fDateCompletedField setObjectValue: @""]; |
---|
266 | |
---|
267 | [fDownloadTimeField setStringValue: @""]; |
---|
268 | [fSeedTimeField setStringValue: @""]; |
---|
269 | |
---|
270 | [fPiecesControl setSelected: NO forSegment: PIECES_CONTROL_AVAILABLE]; |
---|
271 | [fPiecesControl setSelected: NO forSegment: PIECES_CONTROL_PROGRESS]; |
---|
272 | [fPiecesControl setEnabled: NO]; |
---|
273 | [fPiecesView setTorrent: nil]; |
---|
274 | } |
---|
275 | else |
---|
276 | { |
---|
277 | Torrent * torrent = [fTorrents objectAtIndex: 0]; |
---|
278 | |
---|
279 | const BOOL piecesAvailableSegment = [[NSUserDefaults standardUserDefaults] boolForKey: @"PiecesViewShowAvailability"]; |
---|
280 | [fPiecesControl setSelected: piecesAvailableSegment forSegment: PIECES_CONTROL_AVAILABLE]; |
---|
281 | [fPiecesControl setSelected: !piecesAvailableSegment forSegment: PIECES_CONTROL_PROGRESS]; |
---|
282 | [fPiecesControl setEnabled: YES]; |
---|
283 | |
---|
284 | [fPiecesView setTorrent: torrent]; |
---|
285 | } |
---|
286 | |
---|
287 | fSet = YES; |
---|
288 | } |
---|
289 | |
---|
290 | @end |
---|