1/****************************************************************************** 2 * Copyright (c) 2010-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#import "InfoOptionsViewController.h" 24#import "NSStringAdditions.h" 25#import "Torrent.h" 26 27#define OPTION_POPUP_GLOBAL 0 28#define OPTION_POPUP_NO_LIMIT 1 29#define OPTION_POPUP_LIMIT 2 30 31#define OPTION_POPUP_PRIORITY_HIGH 0 32#define OPTION_POPUP_PRIORITY_NORMAL 1 33#define OPTION_POPUP_PRIORITY_LOW 2 34 35#define INVALID -99 36 37@interface InfoOptionsViewController (Private) 38 39- (void) setupInfo; 40- (void) setGlobalLabels; 41- (void) updateOptionsNotification: (NSNotification *) notification; 42 43@end 44 45@implementation InfoOptionsViewController 46 47- (id) init 48{ 49 if ((self = [super initWithNibName: @"InfoOptionsView" bundle: nil])) 50 { 51 [self setTitle: NSLocalizedString(@"Options", "Inspector view -> title")]; 52 } 53 54 return self; 55} 56 57- (void) awakeFromNib 58{ 59 [self setGlobalLabels]; 60 61 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(setGlobalLabels) name: @"UpdateGlobalOptions" object: nil]; 62 [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(updateOptionsNotification:) name: @"UpdateOptionsNotification" object: nil]; 63} 64 65- (void) dealloc 66{ 67 [[NSNotificationCenter defaultCenter] removeObserver: self]; 68} 69 70- (void) setInfoForTorrents: (NSArray *) torrents 71{ 72 //don't check if it's the same in case the metadata changed 73 fTorrents = torrents; 74 75 fSet = NO; 76} 77 78- (void) updateInfo 79{ 80 if (!fSet) 81 [self setupInfo]; 82 83 fSet = YES; 84} 85 86- (void) updateOptions 87{ 88 if ([fTorrents count] == 0) 89 return; 90 91 //get bandwidth info 92 NSEnumerator * enumerator = [fTorrents objectEnumerator]; 93 Torrent * torrent = [enumerator nextObject]; //first torrent 94 95 NSInteger uploadUseSpeedLimit = [torrent usesSpeedLimit: YES] ? NSOnState : NSOffState, 96 uploadSpeedLimit = [torrent speedLimit: YES], 97 downloadUseSpeedLimit = [torrent usesSpeedLimit: NO] ? NSOnState : NSOffState, 98 downloadSpeedLimit = [torrent speedLimit: NO], 99 globalUseSpeedLimit = [torrent usesGlobalSpeedLimit] ? NSOnState : NSOffState; 100 101 while ((torrent = [enumerator nextObject]) 102 && (uploadUseSpeedLimit != NSMixedState || uploadSpeedLimit != INVALID 103 || downloadUseSpeedLimit != NSMixedState || downloadSpeedLimit != INVALID 104 || globalUseSpeedLimit != NSMixedState)) 105 { 106 if (uploadUseSpeedLimit != NSMixedState && uploadUseSpeedLimit != ([torrent usesSpeedLimit: YES] ? NSOnState : NSOffState)) 107 uploadUseSpeedLimit = NSMixedState; 108 109 if (uploadSpeedLimit != INVALID && uploadSpeedLimit != [torrent speedLimit: YES]) 110 uploadSpeedLimit = INVALID; 111 112 if (downloadUseSpeedLimit != NSMixedState && downloadUseSpeedLimit != ([torrent usesSpeedLimit: NO] ? NSOnState : NSOffState)) 113 downloadUseSpeedLimit = NSMixedState; 114 115 if (downloadSpeedLimit != INVALID && downloadSpeedLimit != [torrent speedLimit: NO]) 116 downloadSpeedLimit = INVALID; 117 118 if (globalUseSpeedLimit != NSMixedState && globalUseSpeedLimit != ([torrent usesGlobalSpeedLimit] ? NSOnState : NSOffState)) 119 globalUseSpeedLimit = NSMixedState; 120 } 121 122 //set upload view 123 [fUploadLimitCheck setState: uploadUseSpeedLimit]; 124 [fUploadLimitCheck setEnabled: YES]; 125 126 [fUploadLimitLabel setEnabled: uploadUseSpeedLimit == NSOnState]; 127 [fUploadLimitField setEnabled: uploadUseSpeedLimit == NSOnState]; 128 if (uploadSpeedLimit != INVALID) 129 [fUploadLimitField setIntValue: uploadSpeedLimit]; 130 else 131 [fUploadLimitField setStringValue: @""]; 132 133 //set download view 134 [fDownloadLimitCheck setState: downloadUseSpeedLimit]; 135 [fDownloadLimitCheck setEnabled: YES]; 136 137 [fDownloadLimitLabel setEnabled: downloadUseSpeedLimit == NSOnState]; 138 [fDownloadLimitField setEnabled: downloadUseSpeedLimit == NSOnState]; 139 if (downloadSpeedLimit != INVALID) 140 [fDownloadLimitField setIntValue: downloadSpeedLimit]; 141 else 142 [fDownloadLimitField setStringValue: @""]; 143 144 //set global check 145 [fGlobalLimitCheck setState: globalUseSpeedLimit]; 146 [fGlobalLimitCheck setEnabled: YES]; 147 148 //get ratio and idle info 149 enumerator = [fTorrents objectEnumerator]; 150 torrent = [enumerator nextObject]; //first torrent 151 152 NSInteger checkRatio = [torrent ratioSetting], checkIdle = [torrent idleSetting], 153 removeWhenFinishSeeding = [torrent removeWhenFinishSeeding] ? NSOnState : NSOffState; 154 CGFloat ratioLimit = [torrent ratioLimit]; 155 NSUInteger idleLimit = [torrent idleLimitMinutes]; 156 157 while ((torrent = [enumerator nextObject]) 158 && (checkRatio != INVALID || ratioLimit != INVALID || checkIdle != INVALID || idleLimit != INVALID)) 159 { 160 if (checkRatio != INVALID && checkRatio != [torrent ratioSetting]) 161 checkRatio = INVALID; 162 163 if (ratioLimit != INVALID && ratioLimit != [torrent ratioLimit]) 164 ratioLimit = INVALID; 165 166 if (checkIdle != INVALID && checkIdle != [torrent idleSetting]) 167 checkIdle = INVALID; 168 169 if (idleLimit != INVALID && idleLimit != [torrent idleLimitMinutes]) 170 idleLimit = INVALID; 171 172 if (removeWhenFinishSeeding != NSMixedState && removeWhenFinishSeeding != ([torrent removeWhenFinishSeeding] ? NSOnState : NSOffState)) 173 removeWhenFinishSeeding = NSMixedState; 174 } 175 176 //set ratio view 177 NSInteger index; 178 if (checkRatio == TR_RATIOLIMIT_SINGLE) 179 index = OPTION_POPUP_LIMIT; 180 else if (checkRatio == TR_RATIOLIMIT_UNLIMITED) 181 index = OPTION_POPUP_NO_LIMIT; 182 else if (checkRatio == TR_RATIOLIMIT_GLOBAL) 183 index = OPTION_POPUP_GLOBAL; 184 else 185 index = -1; 186 [fRatioPopUp selectItemAtIndex: index]; 187 [fRatioPopUp setEnabled: YES]; 188 189 [fRatioLimitField setHidden: checkRatio != TR_RATIOLIMIT_SINGLE]; 190 if (ratioLimit != INVALID) 191 [fRatioLimitField setFloatValue: ratioLimit]; 192 else 193 [fRatioLimitField setStringValue: @""]; 194 195 [fRatioLimitGlobalLabel setHidden: checkRatio != TR_RATIOLIMIT_GLOBAL]; 196 197 //set idle view 198 if (checkIdle == TR_IDLELIMIT_SINGLE) 199 index = OPTION_POPUP_LIMIT; 200 else if (checkIdle == TR_IDLELIMIT_UNLIMITED) 201 index = OPTION_POPUP_NO_LIMIT; 202 else if (checkIdle == TR_IDLELIMIT_GLOBAL) 203 index = OPTION_POPUP_GLOBAL; 204 else 205 index = -1; 206 [fIdlePopUp selectItemAtIndex: index]; 207 [fIdlePopUp setEnabled: YES]; 208 209 [fIdleLimitField setHidden: checkIdle != TR_IDLELIMIT_SINGLE]; 210 if (idleLimit != INVALID) 211 [fIdleLimitField setIntegerValue: idleLimit]; 212 else 213 [fIdleLimitField setStringValue: @""]; 214 [fIdleLimitLabel setHidden: checkIdle != TR_IDLELIMIT_SINGLE]; 215 216 [fIdleLimitGlobalLabel setHidden: checkIdle != TR_IDLELIMIT_GLOBAL]; 217 218 //set remove transfer when seeding finishes 219 [fRemoveSeedingCompleteCheck setState: removeWhenFinishSeeding]; 220 [fRemoveSeedingCompleteCheck setEnabled: YES]; 221 222 //get priority info 223 enumerator = [fTorrents objectEnumerator]; 224 torrent = [enumerator nextObject]; //first torrent 225 226 NSInteger priority = [torrent priority]; 227 228 while ((torrent = [enumerator nextObject]) && priority != INVALID) 229 { 230 if (priority != INVALID && priority != [torrent priority]) 231 priority = INVALID; 232 } 233 234 //set priority view 235 if (priority == TR_PRI_HIGH) 236 index = OPTION_POPUP_PRIORITY_HIGH; 237 else if (priority == TR_PRI_NORMAL) 238 index = OPTION_POPUP_PRIORITY_NORMAL; 239 else if (priority == TR_PRI_LOW) 240 index = OPTION_POPUP_PRIORITY_LOW; 241 else 242 index = -1; 243 [fPriorityPopUp selectItemAtIndex: index]; 244 [fPriorityPopUp setEnabled: YES]; 245 246 //get peer info 247 enumerator = [fTorrents objectEnumerator]; 248 torrent = [enumerator nextObject]; //first torrent 249 250 NSInteger maxPeers = [torrent maxPeerConnect]; 251 252 while ((torrent = [enumerator nextObject])) 253 { 254 if (maxPeers != [torrent maxPeerConnect]) 255 { 256 maxPeers = INVALID; 257 break; 258 } 259 } 260 261 //set peer view 262 [fPeersConnectField setEnabled: YES]; 263 [fPeersConnectLabel setEnabled: YES]; 264 if (maxPeers != INVALID) 265 [fPeersConnectField setIntValue: maxPeers]; 266 else 267 [fPeersConnectField setStringValue: @""]; 268} 269 270- (void) setUseSpeedLimit: (id) sender 271{ 272 const BOOL upload = sender == fUploadLimitCheck; 273 274 if ([(NSButton *)sender state] == NSMixedState) 275 [sender setState: NSOnState]; 276 const BOOL limit = [(NSButton *)sender state] == NSOnState; 277 278 for (Torrent * torrent in fTorrents) 279 [torrent setUseSpeedLimit: limit upload: upload]; 280 281 NSTextField * field = upload ? fUploadLimitField : fDownloadLimitField; 282 [field setEnabled: limit]; 283 if (limit) 284 { 285 [field selectText: self]; 286 [[[self view] window] makeKeyAndOrderFront: self]; 287 } 288 289 NSTextField * label = upload ? fUploadLimitLabel : fDownloadLimitLabel; 290 [label setEnabled: limit]; 291 292 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateOptionsNotification" object: self]; 293} 294 295- (void) setUseGlobalSpeedLimit: (id) sender 296{ 297 if ([(NSButton *)sender state] == NSMixedState) 298 [sender setState: NSOnState]; 299 const BOOL limit = [(NSButton *)sender state] == NSOnState; 300 301 for (Torrent * torrent in fTorrents) 302 [torrent setUseGlobalSpeedLimit: limit]; 303 304 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateOptionsNotification" object: self]; 305} 306 307- (void) setSpeedLimit: (id) sender 308{ 309 const BOOL upload = sender == fUploadLimitField; 310 const NSInteger limit = [sender intValue]; 311 312 for (Torrent * torrent in fTorrents) 313 [torrent setSpeedLimit: limit upload: upload]; 314 315 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateOptionsNotification" object: self]; 316} 317 318- (void) setRatioSetting: (id) sender 319{ 320 NSInteger setting; 321 BOOL single = NO; 322 switch ([sender indexOfSelectedItem]) 323 { 324 case OPTION_POPUP_LIMIT: 325 setting = TR_RATIOLIMIT_SINGLE; 326 single = YES; 327 break; 328 case OPTION_POPUP_NO_LIMIT: 329 setting = TR_RATIOLIMIT_UNLIMITED; 330 break; 331 case OPTION_POPUP_GLOBAL: 332 setting = TR_RATIOLIMIT_GLOBAL; 333 break; 334 default: 335 NSAssert1(NO, @"Unknown option selected in ratio popup: %ld", [sender indexOfSelectedItem]); 336 return; 337 } 338 339 for (Torrent * torrent in fTorrents) 340 [torrent setRatioSetting: setting]; 341 342 [fRatioLimitField setHidden: !single]; 343 if (single) 344 { 345 [fRatioLimitField selectText: self]; 346 [[[self view] window] makeKeyAndOrderFront: self]; 347 } 348 349 [fRatioLimitGlobalLabel setHidden: setting != TR_RATIOLIMIT_GLOBAL]; 350 351 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateOptionsNotification" object: self]; 352} 353 354- (void) setRatioLimit: (id) sender 355{ 356 const CGFloat limit = [sender floatValue]; 357 358 for (Torrent * torrent in fTorrents) 359 [torrent setRatioLimit: limit]; 360 361 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateOptionsNotification" object: self]; 362} 363 364- (void) setIdleSetting: (id) sender 365{ 366 NSInteger setting; 367 BOOL single = NO; 368 switch ([sender indexOfSelectedItem]) 369 { 370 case OPTION_POPUP_LIMIT: 371 setting = TR_IDLELIMIT_SINGLE; 372 single = YES; 373 break; 374 case OPTION_POPUP_NO_LIMIT: 375 setting = TR_IDLELIMIT_UNLIMITED; 376 break; 377 case OPTION_POPUP_GLOBAL: 378 setting = TR_IDLELIMIT_GLOBAL; 379 break; 380 default: 381 NSAssert1(NO, @"Unknown option selected in idle popup: %ld", [sender indexOfSelectedItem]); 382 return; 383 } 384 385 for (Torrent * torrent in fTorrents) 386 [torrent setIdleSetting: setting]; 387 388 [fIdleLimitField setHidden: !single]; 389 [fIdleLimitLabel setHidden: !single]; 390 if (single) 391 { 392 [fIdleLimitField selectText: self]; 393 [[[self view] window] makeKeyAndOrderFront: self]; 394 } 395 396 [fIdleLimitGlobalLabel setHidden: setting != TR_IDLELIMIT_GLOBAL]; 397 398 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateOptionsNotification" object: self]; 399} 400 401- (void) setIdleLimit: (id) sender 402{ 403 const NSUInteger limit = [sender integerValue]; 404 405 for (Torrent * torrent in fTorrents) 406 [torrent setIdleLimitMinutes: limit]; 407 408 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateOptionsNotification" object: self]; 409} 410 411- (IBAction) setRemoveWhenSeedingCompletes: (id) sender 412{ 413 if ([(NSButton *)sender state] == NSMixedState) 414 [sender setState: NSOnState]; 415 const BOOL enable = [(NSButton *)sender state] == NSOnState; 416 417 for (Torrent * torrent in fTorrents) 418 [torrent setRemoveWhenFinishSeeding: enable]; 419 420 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateOptionsNotification" object: self]; 421} 422 423- (void) setPriority: (id) sender 424{ 425 tr_priority_t priority; 426 switch ([sender indexOfSelectedItem]) 427 { 428 case OPTION_POPUP_PRIORITY_HIGH: 429 priority = TR_PRI_HIGH; 430 break; 431 case OPTION_POPUP_PRIORITY_NORMAL: 432 priority = TR_PRI_NORMAL; 433 break; 434 case OPTION_POPUP_PRIORITY_LOW: 435 priority = TR_PRI_LOW; 436 break; 437 default: 438 NSAssert1(NO, @"Unknown option selected in priority popup: %ld", [sender indexOfSelectedItem]); 439 return; 440 } 441 442 for (Torrent * torrent in fTorrents) 443 [torrent setPriority: priority]; 444 445 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil]; 446 447 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateOptionsNotification" object: self]; 448} 449 450- (void) setPeersConnectLimit: (id) sender 451{ 452 NSInteger limit = [sender intValue]; 453 454 for (Torrent * torrent in fTorrents) 455 [torrent setMaxPeerConnect: limit]; 456 457 [[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateOptionsNotification" object: self]; 458} 459 460- (BOOL) control: (NSControl *) control textShouldBeginEditing: (NSText *) fieldEditor 461{ 462 fInitialString = [control stringValue]; 463 464 return YES; 465} 466 467- (BOOL) control: (NSControl *) control didFailToFormatString: (NSString *) string errorDescription: (NSString *) error 468{ 469 NSBeep(); 470 if (fInitialString) 471 { 472 [control setStringValue: fInitialString]; 473 fInitialString = nil; 474 } 475 return NO; 476} 477 478@end 479 480@implementation InfoOptionsViewController (Private) 481 482- (void) setupInfo 483{ 484 if ([fTorrents count] == 0) 485 { 486 [fUploadLimitCheck setEnabled: NO]; 487 [fUploadLimitCheck setState: NSOffState]; 488 [fUploadLimitField setEnabled: NO]; 489 [fUploadLimitLabel setEnabled: NO]; 490 [fUploadLimitField setStringValue: @""]; 491 492 [fDownloadLimitCheck setEnabled: NO]; 493 [fDownloadLimitCheck setState: NSOffState]; 494 [fDownloadLimitField setEnabled: NO]; 495 [fDownloadLimitLabel setEnabled: NO]; 496 [fDownloadLimitField setStringValue: @""]; 497 498 [fGlobalLimitCheck setEnabled: NO]; 499 [fGlobalLimitCheck setState: NSOffState]; 500 501 [fPriorityPopUp setEnabled: NO]; 502 [fPriorityPopUp selectItemAtIndex: -1]; 503 504 [fRatioPopUp setEnabled: NO]; 505 [fRatioPopUp selectItemAtIndex: -1]; 506 [fRatioLimitField setHidden: YES]; 507 [fRatioLimitField setStringValue: @""]; 508 [fRatioLimitGlobalLabel setHidden: YES]; 509 510 [fIdlePopUp setEnabled: NO]; 511 [fIdlePopUp selectItemAtIndex: -1]; 512 [fIdleLimitField setHidden: YES]; 513 [fIdleLimitField setStringValue: @""]; 514 [fIdleLimitLabel setHidden: YES]; 515 [fIdleLimitGlobalLabel setHidden: YES]; 516 517 [fRemoveSeedingCompleteCheck setEnabled: NO]; 518 [fRemoveSeedingCompleteCheck setState: NSOffState]; 519 520 [fPeersConnectField setEnabled: NO]; 521 [fPeersConnectField setStringValue: @""]; 522 [fPeersConnectLabel setEnabled: NO]; 523 } 524 else 525 [self updateOptions]; 526} 527 528- (void) setGlobalLabels 529{ 530 NSString * global = [[NSUserDefaults standardUserDefaults] boolForKey: @"RatioCheck"] 531 ? [NSString stringForRatio: [[NSUserDefaults standardUserDefaults] floatForKey: @"RatioLimit"]] 532 : NSLocalizedString(@"disabled", "Info options -> global setting"); 533 [fRatioLimitGlobalLabel setStringValue: global]; 534 535 //idle field 536 NSString * globalIdle; 537 if ([[NSUserDefaults standardUserDefaults] boolForKey: @"IdleLimitCheck"]) 538 { 539 const NSInteger globalMin = [[NSUserDefaults standardUserDefaults] integerForKey: @"IdleLimitMinutes"]; 540 globalIdle = globalMin == 1 ? NSLocalizedString(@"1 minute", "Info options -> global setting") 541 : [NSString localizedStringWithFormat: NSLocalizedString(@"%d minutes", "Info options -> global setting"), globalMin]; 542 } 543 else 544 globalIdle = NSLocalizedString(@"disabled", "Info options -> global setting"); 545 [fIdleLimitGlobalLabel setStringValue: globalIdle]; 546} 547 548- (void) updateOptionsNotification: (NSNotification *) notification 549{ 550 if ([notification object] != self) 551 [self updateOptions]; 552} 553 554@end 555