1/****************************************************************************** 2 * $Id: InfoPeersViewController.m 13434 2012-08-13 00:52:04Z 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 "InfoPeersViewController.h" 26#import "NSApplicationAdditions.h" 27#import "NSStringAdditions.h" 28#import "PeerProgressIndicatorCell.h" 29#import "Torrent.h" 30#import "WebSeedTableView.h" 31 32#import "transmission.h" // required by utils.h 33#import "utils.h" 34 35@interface InfoPeersViewController (Private) 36 37- (void) setupInfo; 38 39- (void) setWebSeedTableHidden: (BOOL) hide animate: (BOOL) animate; 40- (NSArray *) peerSortDescriptors; 41 42@end 43 44@implementation InfoPeersViewController 45 46- (id) init 47{ 48 if ((self = [super initWithNibName: @"InfoPeersView" bundle: nil])) 49 { 50 [self setTitle: NSLocalizedString(@"Peers", "Inspector view -> title")]; 51 } 52 53 return self; 54} 55 56- (void) awakeFromNib 57{ 58 const CGFloat height = [[NSUserDefaults standardUserDefaults] floatForKey: @"InspectorContentHeightPeers"]; 59 if (height != 0.0) 60 { 61 NSRect viewRect = [[self view] frame]; 62 viewRect.size.height = height; 63 [[self view] setFrame: viewRect]; 64 } 65 66 //set table header text 67 [[[fPeerTable tableColumnWithIdentifier: @"IP"] headerCell] setStringValue: NSLocalizedString(@"IP Address", 68 "inspector -> peer table -> header")]; 69 [[[fPeerTable tableColumnWithIdentifier: @"Client"] headerCell] setStringValue: NSLocalizedString(@"Client", 70 "inspector -> peer table -> header")]; 71 [[[fPeerTable tableColumnWithIdentifier: @"DL From"] headerCell] setStringValue: NSLocalizedString(@"DL", 72 "inspector -> peer table -> header")]; 73 [[[fPeerTable tableColumnWithIdentifier: @"UL To"] headerCell] setStringValue: NSLocalizedString(@"UL", 74 "inspector -> peer table -> header")]; 75 76 [[[fWebSeedTable tableColumnWithIdentifier: @"Address"] headerCell] setStringValue: NSLocalizedString(@"Web Seeds", 77 "inspector -> web seed table -> header")]; 78 [[[fWebSeedTable tableColumnWithIdentifier: @"DL From"] headerCell] setStringValue: NSLocalizedString(@"DL", 79 "inspector -> web seed table -> header")]; 80 81 //set table header tool tips 82 [[fPeerTable tableColumnWithIdentifier: @"Encryption"] setHeaderToolTip: NSLocalizedString(@"Encrypted Connection", 83 "inspector -> peer table -> header tool tip")]; 84 [[fPeerTable tableColumnWithIdentifier: @"Progress"] setHeaderToolTip: NSLocalizedString(@"Available", 85 "inspector -> peer table -> header tool tip")]; 86 [[fPeerTable tableColumnWithIdentifier: @"DL From"] setHeaderToolTip: NSLocalizedString(@"Downloading From Peer", 87 "inspector -> peer table -> header tool tip")]; 88 [[fPeerTable tableColumnWithIdentifier: @"UL To"] setHeaderToolTip: NSLocalizedString(@"Uploading To Peer", 89 "inspector -> peer table -> header tool tip")]; 90 91 [[fWebSeedTable tableColumnWithIdentifier: @"DL From"] setHeaderToolTip: NSLocalizedString(@"Downloading From Web Seed", 92 "inspector -> web seed table -> header tool tip")]; 93 94 //prepare for animating peer table and web seed table 95 NSRect webSeedTableFrame = [[fWebSeedTable enclosingScrollView] frame]; 96 fWebSeedTableHeight = webSeedTableFrame.size.height; 97 fSpaceBetweenWebSeedAndPeer = webSeedTableFrame.origin.y - NSMaxY([[fPeerTable enclosingScrollView] frame]); 98 99 [self setWebSeedTableHidden: YES animate: NO]; 100} 101 102- (void) dealloc 103{ 104 [fTorrents release]; 105 106 [fPeers release]; 107 [fWebSeeds release]; 108 109 [fWebSeedTableAnimation release]; 110 111 [super dealloc]; 112} 113 114#warning subclass? 115- (void) setInfoForTorrents: (NSArray *) torrents 116{ 117 //don't check if it's the same in case the metadata changed 118 [fTorrents release]; 119 fTorrents = [torrents retain]; 120 121 fSet = NO; 122} 123 124- (void) updateInfo 125{ 126 if (!fSet) 127 [self setupInfo]; 128 129 if ([fTorrents count] == 0) 130 return; 131 132 if (!fPeers) 133 fPeers = [[NSMutableArray alloc] init]; 134 else 135 [fPeers removeAllObjects]; 136 137 if (!fWebSeeds) 138 fWebSeeds = [[NSMutableArray alloc] init]; 139 else 140 [fWebSeeds removeAllObjects]; 141 142 NSUInteger connected = 0, tracker = 0, incoming = 0, cache = 0, lpd = 0, pex = 0, dht = 0, ltep = 0, 143 toUs = 0, fromUs = 0; 144 BOOL anyActive = false; 145 for (Torrent * torrent in fTorrents) 146 { 147 if ([torrent webSeedCount] > 0) 148 [fWebSeeds addObjectsFromArray: [torrent webSeeds]]; 149 150 if ([torrent isActive]) 151 { 152 anyActive = YES; 153 [fPeers addObjectsFromArray: [torrent peers]]; 154 155 const NSUInteger connectedThis = [torrent totalPeersConnected]; 156 if (connectedThis > 0) 157 { 158 connected += [torrent totalPeersConnected]; 159 tracker += [torrent totalPeersTracker]; 160 incoming += [torrent totalPeersIncoming]; 161 cache += [torrent totalPeersCache]; 162 lpd += [torrent totalPeersLocal]; 163 pex += [torrent totalPeersPex]; 164 dht += [torrent totalPeersDHT]; 165 ltep += [torrent totalPeersLTEP]; 166 167 toUs += [torrent peersSendingToUs]; 168 fromUs += [torrent peersGettingFromUs]; 169 } 170 } 171 } 172 173 [fPeers sortUsingDescriptors: [self peerSortDescriptors]]; 174 [fPeerTable reloadData]; 175 176 [fWebSeeds sortUsingDescriptors: [fWebSeedTable sortDescriptors]]; 177 [fWebSeedTable reloadData]; 178 [fWebSeedTable setWebSeeds: fWebSeeds]; 179 180 if (anyActive) 181 { 182 NSString * connectedText = [NSString stringWithFormat: NSLocalizedString(@"%d Connected", "Inspector -> Peers tab -> peers"), 183 connected]; 184 185 if (connected > 0) 186 { 187 NSMutableArray * upDownComponents = [NSMutableArray arrayWithCapacity: 2]; 188 if (toUs > 0) 189 [upDownComponents addObject: [NSString stringWithFormat: 190 NSLocalizedString(@"DL from %d", "Inspector -> Peers tab -> peers"), toUs]]; 191 if (fromUs > 0) 192 [upDownComponents addObject: [NSString stringWithFormat: 193 NSLocalizedString(@"UL to %d", "Inspector -> Peers tab -> peers"), fromUs]]; 194 if ([upDownComponents count] > 0) 195 connectedText = [connectedText stringByAppendingFormat: @": %@", [upDownComponents componentsJoinedByString: @", "]]; 196 197 NSMutableArray * fromComponents = [NSMutableArray arrayWithCapacity: 7]; 198 if (tracker > 0) 199 [fromComponents addObject: [NSString stringWithFormat: 200 NSLocalizedString(@"%d tracker", "Inspector -> Peers tab -> peers"), tracker]]; 201 if (incoming > 0) 202 [fromComponents addObject: [NSString stringWithFormat: 203 NSLocalizedString(@"%d incoming", "Inspector -> Peers tab -> peers"), incoming]]; 204 if (cache > 0) 205 [fromComponents addObject: [NSString stringWithFormat: 206 NSLocalizedString(@"%d cache", "Inspector -> Peers tab -> peers"), cache]]; 207 if (lpd > 0) 208 [fromComponents addObject: [NSString stringWithFormat: 209 NSLocalizedString(@"%d local discovery", "Inspector -> Peers tab -> peers"), lpd]]; 210 if (pex > 0) 211 [fromComponents addObject: [NSString stringWithFormat: 212 NSLocalizedString(@"%d PEX", "Inspector -> Peers tab -> peers"), pex]]; 213 if (dht > 0) 214 [fromComponents addObject: [NSString stringWithFormat: 215 NSLocalizedString(@"%d DHT", "Inspector -> Peers tab -> peers"), dht]]; 216 if (ltep > 0) 217 [fromComponents addObject: [NSString stringWithFormat: 218 NSLocalizedString(@"%d LTEP", "Inspector -> Peers tab -> peers"), ltep]]; 219 220 connectedText = [connectedText stringByAppendingFormat: @"\n%@", [fromComponents componentsJoinedByString: @", "]]; 221 } 222 223 [fConnectedPeersField setStringValue: connectedText]; 224 } 225 else 226 { 227 NSString * notActiveString; 228 if ([fTorrents count] == 1) 229 notActiveString = NSLocalizedString(@"Transfer Not Active", "Inspector -> Peers tab -> peers"); 230 else 231 notActiveString = NSLocalizedString(@"Transfers Not Active", "Inspector -> Peers tab -> peers"); 232 233 [fConnectedPeersField setStringValue: notActiveString]; 234 } 235} 236 237- (void) saveViewSize 238{ 239 [[NSUserDefaults standardUserDefaults] setFloat: NSHeight([[self view] frame]) forKey: @"InspectorContentHeightPeers"]; 240} 241 242- (void) clearView 243{ 244 //if in the middle of animating, just stop and resize immediately 245 if (fWebSeedTableAnimation) 246 [self setWebSeedTableHidden: !fWebSeeds animate: NO]; 247 248 [fPeers release]; 249 fPeers = nil; 250 [fWebSeeds release]; 251 fWebSeeds = nil; 252} 253 254- (NSInteger) numberOfRowsInTableView: (NSTableView *) tableView 255{ 256 if (tableView == fWebSeedTable) 257 return fWebSeeds ? [fWebSeeds count] : 0; 258 else 259 return fPeers ? [fPeers count] : 0; 260} 261 262- (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) column row: (NSInteger) row 263{ 264 if (tableView == fWebSeedTable) 265 { 266 NSString * ident = [column identifier]; 267 NSDictionary * webSeed = [fWebSeeds objectAtIndex: row]; 268 269 if ([ident isEqualToString: @"DL From"]) 270 { 271 NSNumber * rate; 272 return (rate = [webSeed objectForKey: @"DL From Rate"]) ? [NSString stringForSpeedAbbrev: [rate doubleValue]] : @""; 273 } 274 else 275 return [webSeed objectForKey: @"Address"]; 276 } 277 else 278 { 279 NSString * ident = [column identifier]; 280 NSDictionary * peer = [fPeers objectAtIndex: row]; 281 282 if ([ident isEqualToString: @"Encryption"]) 283 return [[peer objectForKey: @"Encryption"] boolValue] ? [NSImage imageNamed: @"Lock"] : nil; 284 else if ([ident isEqualToString: @"Client"]) 285 return [peer objectForKey: @"Client"]; 286 else if ([ident isEqualToString: @"Progress"]) 287 return [peer objectForKey: @"Progress"]; 288 else if ([ident isEqualToString: @"UL To"]) 289 { 290 NSNumber * rate; 291 return (rate = [peer objectForKey: @"UL To Rate"]) ? [NSString stringForSpeedAbbrev: [rate doubleValue]] : @""; 292 } 293 else if ([ident isEqualToString: @"DL From"]) 294 { 295 NSNumber * rate; 296 return (rate = [peer objectForKey: @"DL From Rate"]) ? [NSString stringForSpeedAbbrev: [rate doubleValue]] : @""; 297 } 298 else 299 return [peer objectForKey: @"IP"]; 300 } 301} 302 303- (void) tableView: (NSTableView *) tableView willDisplayCell: (id) cell forTableColumn: (NSTableColumn *) tableColumn 304 row: (NSInteger) row 305{ 306 if (tableView == fPeerTable) 307 { 308 NSString * ident = [tableColumn identifier]; 309 310 if ([ident isEqualToString: @"Progress"]) 311 { 312 NSDictionary * peer = [fPeers objectAtIndex: row]; 313 [(PeerProgressIndicatorCell *)cell setSeed: [[peer objectForKey: @"Seed"] boolValue]]; 314 } 315 } 316} 317 318- (void) tableView: (NSTableView *) tableView didClickTableColumn: (NSTableColumn *) tableColumn 319{ 320 if (tableView == fWebSeedTable) 321 { 322 if (fWebSeeds) 323 { 324 [fWebSeeds sortUsingDescriptors: [fWebSeedTable sortDescriptors]]; 325 [tableView reloadData]; 326 } 327 } 328 else 329 { 330 if (fPeers) 331 { 332 [fPeers sortUsingDescriptors: [self peerSortDescriptors]]; 333 [tableView reloadData]; 334 } 335 } 336} 337 338- (BOOL) tableView: (NSTableView *) tableView shouldSelectRow: (NSInteger) row 339{ 340 return tableView != fPeerTable; 341} 342 343- (NSString *) tableView: (NSTableView *) tableView toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect 344 tableColumn: (NSTableColumn *) column row: (NSInteger) row mouseLocation: (NSPoint) mouseLocation 345{ 346 if (tableView == fPeerTable) 347 { 348 const BOOL multiple = [fTorrents count] > 1; 349 350 NSDictionary * peer = [fPeers objectAtIndex: row]; 351 NSMutableArray * components = [NSMutableArray arrayWithCapacity: multiple ? 6 : 5]; 352 353 if (multiple) 354 [components addObject: [peer objectForKey: @"Name"]]; 355 356 const CGFloat progress = [[peer objectForKey: @"Progress"] floatValue]; 357 NSString * progressString = [NSString stringWithFormat: NSLocalizedString(@"Progress: %@", 358 "Inspector -> Peers tab -> table row tooltip"), 359 [NSString percentString: progress longDecimals: NO]]; 360 if (progress < 1.0 && [[peer objectForKey: @"Seed"] boolValue]) 361 progressString = [progressString stringByAppendingFormat: @" (%@)", NSLocalizedString(@"Partial Seed", 362 "Inspector -> Peers tab -> table row tooltip")]; 363 [components addObject: progressString]; 364 365 NSString * protocolString = [[peer objectForKey: @"uTP"] boolValue] ? @"\u00b5TP" : @"TCP"; 366 if ([[peer objectForKey: @"Encryption"] boolValue]) 367 protocolString = [protocolString stringByAppendingFormat: @" (%@)", 368 NSLocalizedString(@"encrypted", "Inspector -> Peers tab -> table row tooltip")]; 369 [components addObject: [NSString stringWithFormat: 370 NSLocalizedString(@"Protocol: %@", "Inspector -> Peers tab -> table row tooltip"), 371 protocolString]]; 372 373 NSString * portString; 374 NSInteger port; 375 if ((port = [[peer objectForKey: @"Port"] intValue]) > 0) 376 portString = [NSString stringWithFormat: @"%ld", port]; 377 else 378 portString = NSLocalizedString(@"N/A", "Inspector -> Peers tab -> table row tooltip"); 379 [components addObject: [NSString stringWithFormat: @"%@: %@", NSLocalizedString(@"Port", 380 "Inspector -> Peers tab -> table row tooltip"), portString]]; 381 382 const NSInteger peerFrom = [[peer objectForKey: @"From"] integerValue]; 383 switch (peerFrom) 384 { 385 case TR_PEER_FROM_TRACKER: 386 [components addObject: NSLocalizedString(@"From: tracker", "Inspector -> Peers tab -> table row tooltip")]; 387 break; 388 case TR_PEER_FROM_INCOMING: 389 [components addObject: NSLocalizedString(@"From: incoming connection", "Inspector -> Peers tab -> table row tooltip")]; 390 break; 391 case TR_PEER_FROM_RESUME: 392 [components addObject: NSLocalizedString(@"From: cache", "Inspector -> Peers tab -> table row tooltip")]; 393 break; 394 case TR_PEER_FROM_LPD: 395 [components addObject: NSLocalizedString(@"From: local peer discovery", "Inspector -> Peers tab -> table row tooltip")]; 396 break; 397 case TR_PEER_FROM_PEX: 398 [components addObject: NSLocalizedString(@"From: peer exchange", "Inspector -> Peers tab -> table row tooltip")]; 399 break; 400 case TR_PEER_FROM_DHT: 401 [components addObject: NSLocalizedString(@"From: distributed hash table", "Inspector -> Peers tab -> table row tooltip")]; 402 break; 403 case TR_PEER_FROM_LTEP: 404 [components addObject: NSLocalizedString(@"From: libtorrent extension protocol handshake", 405 "Inspector -> Peers tab -> table row tooltip")]; 406 break; 407 default: 408 NSAssert1(NO, @"Peer from unknown source: %ld", peerFrom); 409 } 410 411 //determing status strings from flags 412 NSMutableArray * statusArray = [NSMutableArray arrayWithCapacity: 6]; 413 NSString * flags = [peer objectForKey: @"Flags"]; 414 415 if ([flags rangeOfString: @"D"].location != NSNotFound) 416 [statusArray addObject: NSLocalizedString(@"Currently downloading (interested and not choked)", 417 "Inspector -> peer -> status")]; 418 if ([flags rangeOfString: @"d"].location != NSNotFound) 419 [statusArray addObject: NSLocalizedString(@"You want to download, but peer does not want to send (interested and choked)", 420 "Inspector -> peer -> status")]; 421 if ([flags rangeOfString: @"U"].location != NSNotFound) 422 [statusArray addObject: NSLocalizedString(@"Currently uploading (interested and not choked)", 423 "Inspector -> peer -> status")]; 424 if ([flags rangeOfString: @"u"].location != NSNotFound) 425 [statusArray addObject: NSLocalizedString(@"Peer wants you to upload, but you do not want to (interested and choked)", 426 "Inspector -> peer -> status")]; 427 if ([flags rangeOfString: @"K"].location != NSNotFound) 428 [statusArray addObject: NSLocalizedString(@"Peer is unchoking you, but you are not interested", 429 "Inspector -> peer -> status")]; 430 if ([flags rangeOfString: @"?"].location != NSNotFound) 431 [statusArray addObject: NSLocalizedString(@"You unchoked the peer, but the peer is not interested", 432 "Inspector -> peer -> status")]; 433 434 if ([statusArray count] > 0) 435 { 436 NSString * statusStrings = [statusArray componentsJoinedByString: @"\n\n"]; 437 [components addObject: [@"\n" stringByAppendingString: statusStrings]]; 438 } 439 440 return [components componentsJoinedByString: @"\n"]; 441 } 442 else 443 { 444 if ([fTorrents count] > 1) 445 return [[fWebSeeds objectAtIndex: row] objectForKey: @"Name"]; 446 } 447 448 return nil; 449} 450 451- (void) animationDidEnd: (NSAnimation *) animation 452{ 453 if (animation == fWebSeedTableAnimation) 454 { 455 [fWebSeedTableAnimation release]; 456 fWebSeedTableAnimation = nil; 457 } 458} 459 460- (void) stopWebSeedAnimation 461{ 462 if (fWebSeedTableAnimation) 463 { 464 [fWebSeedTableAnimation stopAnimation]; // jumps to end frame 465 [fWebSeedTableAnimation release]; 466 fWebSeedTableAnimation = nil; 467 } 468} 469 470@end 471 472@implementation InfoPeersViewController (Private) 473 474- (void) setupInfo 475{ 476 __block BOOL hasWebSeeds = NO; 477 478 if ([fTorrents count] == 0) 479 { 480 [fPeers release]; 481 fPeers = nil; 482 [fPeerTable reloadData]; 483 484 [fConnectedPeersField setStringValue: @""]; 485 } 486 else 487 { 488 [fTorrents enumerateObjectsWithOptions: NSEnumerationConcurrent usingBlock: ^(Torrent * torrent, NSUInteger idx, BOOL *stop) { 489 if ([torrent webSeedCount] > 0) 490 { 491 hasWebSeeds = YES; 492 *stop = YES; 493 } 494 }]; 495 } 496 497 if (!hasWebSeeds) 498 { 499 [fWebSeeds release]; 500 fWebSeeds = nil; 501 [fWebSeedTable reloadData]; 502 } 503 else 504 [fWebSeedTable deselectAll: self]; 505 [self setWebSeedTableHidden: !hasWebSeeds animate: YES]; 506 507 fSet = YES; 508} 509 510- (void) setWebSeedTableHidden: (BOOL) hide animate: (BOOL) animate 511{ 512 if (animate && (![[self view] window] || ![[[self view] window] isVisible])) 513 animate = NO; 514 515 if (fWebSeedTableAnimation) 516 { 517 [fWebSeedTableAnimation stopAnimation]; 518 [fWebSeedTableAnimation release]; 519 fWebSeedTableAnimation = nil; 520 } 521 522 NSRect webSeedFrame = [[fWebSeedTable enclosingScrollView] frame]; 523 NSRect peerFrame = [[fPeerTable enclosingScrollView] frame]; 524 525 if (hide) 526 { 527 CGFloat webSeedFrameMaxY = NSMaxY(webSeedFrame); 528 webSeedFrame.size.height = 0; 529 webSeedFrame.origin.y = webSeedFrameMaxY; 530 531 peerFrame.size.height = webSeedFrameMaxY - peerFrame.origin.y; 532 } 533 else 534 { 535 webSeedFrame.origin.y -= fWebSeedTableHeight - webSeedFrame.size.height; 536 webSeedFrame.size.height = fWebSeedTableHeight; 537 538 peerFrame.size.height = (webSeedFrame.origin.y - fSpaceBetweenWebSeedAndPeer) - peerFrame.origin.y; 539 } 540 541 [[fWebSeedTable enclosingScrollView] setHidden: NO]; //this is needed for some reason 542 543 //actually resize tables 544 if (animate) 545 { 546 NSDictionary * webSeedDict = [NSDictionary dictionaryWithObjectsAndKeys: 547 [fWebSeedTable enclosingScrollView], NSViewAnimationTargetKey, 548 [NSValue valueWithRect: [[fWebSeedTable enclosingScrollView] frame]], NSViewAnimationStartFrameKey, 549 [NSValue valueWithRect: webSeedFrame], NSViewAnimationEndFrameKey, nil], 550 * peerDict = [NSDictionary dictionaryWithObjectsAndKeys: 551 [fPeerTable enclosingScrollView], NSViewAnimationTargetKey, 552 [NSValue valueWithRect: [[fPeerTable enclosingScrollView] frame]], NSViewAnimationStartFrameKey, 553 [NSValue valueWithRect: peerFrame], NSViewAnimationEndFrameKey, nil]; 554 555 fWebSeedTableAnimation = [[NSViewAnimation alloc] initWithViewAnimations: 556 [NSArray arrayWithObjects: webSeedDict, peerDict, nil]]; 557 [fWebSeedTableAnimation setDuration: 0.125]; 558 [fWebSeedTableAnimation setAnimationBlockingMode: NSAnimationNonblocking]; 559 [fWebSeedTableAnimation setDelegate: self]; 560 561 [fWebSeedTableAnimation startAnimation]; 562 } 563 else 564 { 565 [[fWebSeedTable enclosingScrollView] setFrame: webSeedFrame]; 566 [[fPeerTable enclosingScrollView] setFrame: peerFrame]; 567 } 568} 569 570- (NSArray *) peerSortDescriptors 571{ 572 NSMutableArray * descriptors = [NSMutableArray arrayWithCapacity: 2]; 573 574 NSArray * oldDescriptors = [fPeerTable sortDescriptors]; 575 BOOL useSecond = YES, asc = YES; 576 if ([oldDescriptors count] > 0) 577 { 578 NSSortDescriptor * descriptor = [oldDescriptors objectAtIndex: 0]; 579 [descriptors addObject: descriptor]; 580 581 if ((useSecond = ![[descriptor key] isEqualToString: @"IP"])) 582 asc = [descriptor ascending]; 583 } 584 585 //sort by IP after primary sort 586 if (useSecond) 587 { 588 NSSortDescriptor * secondDescriptor = [NSSortDescriptor sortDescriptorWithKey: @"IP" ascending: asc selector: @selector(compareNumeric:)]; 589 [descriptors addObject: secondDescriptor]; 590 } 591 592 return descriptors; 593} 594 595@end 596