1/****************************************************************************** 2 * $Id: InfoWindowController.m 13434 2012-08-13 00:52:04Z livings124 $ 3 * 4 * Copyright (c) 2006-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 "InfoWindowController.h" 26#import "InfoViewController.h" 27#import "InfoGeneralViewController.h" 28#import "InfoActivityViewController.h" 29#import "InfoTrackersViewController.h" 30#import "InfoPeersViewController.h" 31#import "InfoFileViewController.h" 32#import "InfoOptionsViewController.h" 33#import "InfoTabButtonCell.h" 34#import "NSApplicationAdditions.h" 35#import "NSStringAdditions.h" 36#import "Torrent.h" 37 38#define TAB_INFO_IDENT @"Info" 39#define TAB_ACTIVITY_IDENT @"Activity" 40#define TAB_TRACKER_IDENT @"Tracker" 41#define TAB_PEERS_IDENT @"Peers" 42#define TAB_FILES_IDENT @"Files" 43#define TAB_OPTIONS_IDENT @"Options" 44 45#define TAB_MIN_HEIGHT 250 46 47#define INVALID -99 48 49typedef enum 50{ 51 TAB_GENERAL_TAG = 0, 52 TAB_ACTIVITY_TAG = 1, 53 TAB_TRACKERS_TAG = 2, 54 TAB_PEERS_TAG = 3, 55 TAB_FILE_TAG = 4, 56 TAB_OPTIONS_TAG = 5 57} tabTag; 58 59@interface InfoWindowController (Private) 60 61- (void) resetInfo; 62- (void) resetInfoForTorrent: (NSNotification *) notification; 63 64@end 65 66@implementation InfoWindowController 67 68- (id) init 69{ 70 self = [super initWithWindowNibName: @"InfoWindow"]; 71 return self; 72} 73 74- (void) awakeFromNib 75{ 76 [fNoneSelectedField setStringValue: NSLocalizedString(@"No Torrents Selected", "Inspector -> selected torrents")]; 77 78 //window location and size 79 NSPanel * window = (NSPanel *)[self window]; 80 81 [window setFloatingPanel: NO]; 82 83 const CGFloat windowHeight = NSHeight([window frame]); 84 85 [window setFrameAutosaveName: @"InspectorWindow"]; 86 [window setFrameUsingName: @"InspectorWindow"]; 87 88 NSRect windowRect = [window frame]; 89 windowRect.origin.y -= windowHeight - NSHeight(windowRect); 90 windowRect.size.height = windowHeight; 91 [window setFrame: windowRect display: NO]; 92 93 [window setBecomesKeyOnlyIfNeeded: YES]; 94 95 //set tab tooltips 96 [fTabMatrix setToolTip: NSLocalizedString(@"General Info", "Inspector -> tab") forCell: [fTabMatrix cellWithTag: TAB_GENERAL_TAG]]; 97 [fTabMatrix setToolTip: NSLocalizedString(@"Activity", "Inspector -> tab") forCell: [fTabMatrix cellWithTag: TAB_ACTIVITY_TAG]]; 98 [fTabMatrix setToolTip: NSLocalizedString(@"Trackers", "Inspector -> tab") forCell: [fTabMatrix cellWithTag: TAB_TRACKERS_TAG]]; 99 [fTabMatrix setToolTip: NSLocalizedString(@"Peers", "Inspector -> tab") forCell: [fTabMatrix cellWithTag: TAB_PEERS_TAG]]; 100 [fTabMatrix setToolTip: NSLocalizedString(@"Files", "Inspector -> tab") forCell: [fTabMatrix cellWithTag: TAB_FILE_TAG]]; 101 [fTabMatrix setToolTip: NSLocalizedString(@"Options", "Inspector -> tab") forCell: [fTabMatrix cellWithTag: TAB_OPTIONS_TAG]]; 102 103 //set selected tab 104 fCurrentTabTag = INVALID; 105 NSString * identifier = [[NSUserDefaults standardUserDefaults] stringForKey: @"InspectorSelected"]; 106 NSInteger tag; 107 if ([identifier isEqualToString: TAB_INFO_IDENT]) 108 tag = TAB_GENERAL_TAG; 109 else if ([identifier isEqualToString: TAB_ACTIVITY_IDENT]) 110 tag = TAB_ACTIVITY_TAG; 111 else if ([identifier isEqualToString: TAB_TRACKER_IDENT]) 112 tag = TAB_TRACKERS_TAG; 113 else if ([identifier isEqualToString: TAB_PEERS_IDENT]) 114 tag = TAB_PEERS_TAG; 115 else if ([identifier isEqualToString: TAB_FILES_IDENT]) 116 tag = TAB_FILE_TAG; 117 else if ([identifier isEqualToString: TAB_OPTIONS_IDENT]) 118 tag = TAB_OPTIONS_TAG; 119 else //safety 120 { 121 [[NSUserDefaults standardUserDefaults] setObject: TAB_INFO_IDENT forKey: @"InspectorSelected"]; 122 tag = TAB_GENERAL_TAG; 123 } 124 [fTabMatrix selectCellWithTag: tag]; 125 [self setTab: nil]; 126 127 //set blank inspector 128 [self setInfoForTorrents: [NSArray array]]; 129 130 //allow for update notifications 131 NSNotificationCenter * nc = [NSNotificationCenter defaultCenter]; 132 [nc addObserver: self selector: @selector(resetInfoForTorrent:) name: @"ResetInspector" object: nil]; 133 [nc addObserver: self selector: @selector(updateInfoStats) name: @"UpdateStats" object: nil]; 134 [nc addObserver: self selector: @selector(updateOptions) name: @"UpdateOptions" object: nil]; 135} 136 137- (void) dealloc 138{ 139 [[NSNotificationCenter defaultCenter] removeObserver: self]; 140 141 if ([fViewController respondsToSelector: @selector(saveViewSize)]) 142 [fViewController saveViewSize]; 143 144 [fGeneralViewController release]; 145 [fActivityViewController release]; 146 [fTrackersViewController release]; 147 [fPeersViewController release]; 148 [fFileViewController release]; 149 [fOptionsViewController release]; 150 151 [fTorrents release]; 152 153 [super dealloc]; 154} 155 156- (void) setInfoForTorrents: (NSArray *) torrents 157{ 158 if (fTorrents && [fTorrents isEqualToArray: torrents]) 159 return; 160 161 [fTorrents release]; 162 fTorrents = [torrents retain]; 163 164 [self resetInfo]; 165} 166 167- (NSRect) windowWillUseStandardFrame: (NSWindow *) window defaultFrame: (NSRect) defaultFrame 168{ 169 NSRect windowRect = [window frame]; 170 windowRect.size.width = [window minSize].width; 171 return windowRect; 172} 173 174- (NSSize) windowWillResize: (NSWindow *) window toSize: (NSSize) proposedFrameSize 175{ 176 //this is an edge-case - just stop the animation 177 [fPeersViewController stopWebSeedAnimation]; 178 179 return proposedFrameSize; 180} 181 182- (void) windowWillClose: (NSNotification *) notification 183{ 184 if (fCurrentTabTag == TAB_FILE_TAG && ([QLPreviewPanel sharedPreviewPanelExists] && [[QLPreviewPanel sharedPreviewPanel] isVisible])) 185 [[QLPreviewPanel sharedPreviewPanel] reloadData]; 186} 187 188- (void) setTab: (id) sender 189{ 190 const NSInteger oldTabTag = fCurrentTabTag; 191 fCurrentTabTag = [fTabMatrix selectedTag]; 192 if (fCurrentTabTag == oldTabTag) 193 return; 194 195 //take care of old view 196 CGFloat oldHeight = 0; 197 if (oldTabTag != INVALID) 198 { 199 //deselect old tab item 200 [(InfoTabButtonCell *)[fTabMatrix cellWithTag: oldTabTag] setSelectedTab: NO]; 201 202 if ([fViewController respondsToSelector: @selector(saveViewSize)]) 203 [fViewController saveViewSize]; 204 205 if ([fViewController respondsToSelector: @selector(clearView)]) 206 [fViewController clearView]; 207 208 NSView * oldView = [fViewController view]; 209 oldHeight = NSHeight([oldView frame]); 210 211 //remove old view 212 [oldView removeFromSuperview]; 213 } 214 215 //set new tab item 216 NSString * identifier; 217 switch (fCurrentTabTag) 218 { 219 case TAB_GENERAL_TAG: 220 if (!fGeneralViewController) 221 { 222 fGeneralViewController = [[InfoGeneralViewController alloc] init]; 223 [fGeneralViewController setInfoForTorrents: fTorrents]; 224 } 225 226 fViewController = fGeneralViewController; 227 identifier = TAB_INFO_IDENT; 228 break; 229 case TAB_ACTIVITY_TAG: 230 if (!fActivityViewController) 231 { 232 fActivityViewController = [[InfoActivityViewController alloc] init]; 233 [fActivityViewController setInfoForTorrents: fTorrents]; 234 } 235 236 fViewController = fActivityViewController; 237 identifier = TAB_ACTIVITY_IDENT; 238 break; 239 case TAB_TRACKERS_TAG: 240 if (!fTrackersViewController) 241 { 242 fTrackersViewController = [[InfoTrackersViewController alloc] init]; 243 [fTrackersViewController setInfoForTorrents: fTorrents]; 244 } 245 246 fViewController = fTrackersViewController; 247 identifier = TAB_TRACKER_IDENT; 248 break; 249 case TAB_PEERS_TAG: 250 if (!fPeersViewController) 251 { 252 fPeersViewController = [[InfoPeersViewController alloc] init]; 253 [fPeersViewController setInfoForTorrents: fTorrents]; 254 } 255 256 fViewController = fPeersViewController; 257 identifier = TAB_PEERS_IDENT; 258 break; 259 case TAB_FILE_TAG: 260 if (!fFileViewController) 261 { 262 fFileViewController = [[InfoFileViewController alloc] init]; 263 [fFileViewController setInfoForTorrents: fTorrents]; 264 } 265 266 fViewController = fFileViewController; 267 identifier = TAB_FILES_IDENT; 268 break; 269 case TAB_OPTIONS_TAG: 270 if (!fOptionsViewController) 271 { 272 fOptionsViewController = [[InfoOptionsViewController alloc] init]; 273 [fOptionsViewController setInfoForTorrents: fTorrents]; 274 } 275 276 fViewController = fOptionsViewController; 277 identifier = TAB_OPTIONS_IDENT; 278 break; 279 default: 280 NSAssert1(NO, @"Unknown info tab selected: %ld", fCurrentTabTag); 281 return; 282 } 283 284 [[NSUserDefaults standardUserDefaults] setObject: identifier forKey: @"InspectorSelected"]; 285 286 NSWindow * window = [self window]; 287 288 [window setTitle: [NSString stringWithFormat: @"%@ - %@", [fViewController title], 289 NSLocalizedString(@"Torrent Inspector", "Inspector -> title")]]; 290 291 //selected tab item 292 [(InfoTabButtonCell *)[fTabMatrix selectedCell] setSelectedTab: YES]; 293 294 NSView * view = [fViewController view]; 295 296 [fViewController updateInfo]; 297 298 NSRect windowRect = [window frame], viewRect = [view frame]; 299 300 const CGFloat difference = (NSHeight(viewRect) - oldHeight) * [window userSpaceScaleFactor]; 301 windowRect.origin.y -= difference; 302 windowRect.size.height += difference; 303 304 if ([fViewController respondsToSelector: @selector(saveViewSize)]) //a little bit hacky, but avoids requiring an extra method 305 { 306 if ([window screen]) 307 { 308 const CGFloat screenHeight = NSHeight([[window screen] visibleFrame]); 309 if (NSHeight(windowRect) > screenHeight) 310 { 311 const CGFloat difference = (screenHeight - NSHeight(windowRect)) * [window userSpaceScaleFactor]; 312 windowRect.origin.y -= difference; 313 windowRect.size.height += difference; 314 315 viewRect.size.height += difference; 316 } 317 } 318 319 [window setMinSize: NSMakeSize([window minSize].width, NSHeight(windowRect) - NSHeight(viewRect) + TAB_MIN_HEIGHT)]; 320 [window setMaxSize: NSMakeSize(FLT_MAX, FLT_MAX)]; 321 } 322 else 323 { 324 [window setMinSize: NSMakeSize([window minSize].width, NSHeight(windowRect))]; 325 [window setMaxSize: NSMakeSize(FLT_MAX, NSHeight(windowRect))]; 326 } 327 328 viewRect.size.width = NSWidth(windowRect); 329 [view setFrame: viewRect]; 330 331 [window setFrame: windowRect display: YES animate: oldTabTag != INVALID]; 332 [[window contentView] addSubview: view]; 333 334 if ((fCurrentTabTag == TAB_FILE_TAG || oldTabTag == TAB_FILE_TAG) 335 && ([QLPreviewPanel sharedPreviewPanelExists] && [[QLPreviewPanel sharedPreviewPanel] isVisible])) 336 [[QLPreviewPanel sharedPreviewPanel] reloadData]; 337} 338 339- (void) setNextTab 340{ 341 NSInteger tag = [fTabMatrix selectedTag]+1; 342 if (tag >= [fTabMatrix numberOfColumns]) 343 tag = 0; 344 345 [fTabMatrix selectCellWithTag: tag]; 346 [self setTab: nil]; 347} 348 349- (void) setPreviousTab 350{ 351 NSInteger tag = [fTabMatrix selectedTag]-1; 352 if (tag < 0) 353 tag = [fTabMatrix numberOfColumns]-1; 354 355 [fTabMatrix selectCellWithTag: tag]; 356 [self setTab: nil]; 357} 358 359- (void) swipeWithEvent: (NSEvent *) event 360{ 361 if ([event deltaX] < 0.0) 362 [self setNextTab]; 363 else if ([event deltaX] > 0.0) 364 [self setPreviousTab]; 365} 366 367- (void) updateInfoStats 368{ 369 [fViewController updateInfo]; 370} 371 372- (void) updateOptions 373{ 374 [fOptionsViewController updateOptions]; 375} 376 377- (NSArray *) quickLookURLs 378{ 379 return [fFileViewController quickLookURLs]; 380} 381 382- (BOOL) canQuickLook 383{ 384 if (fCurrentTabTag != TAB_FILE_TAG || ![[self window] isVisible]) 385 return NO; 386 387 return [fFileViewController canQuickLook]; 388} 389 390- (NSRect) quickLookSourceFrameForPreviewItem: (id <QLPreviewItem>) item 391{ 392 return [fFileViewController quickLookSourceFrameForPreviewItem: item]; 393} 394 395@end 396 397@implementation InfoWindowController (Private) 398 399- (void) resetInfo 400{ 401 const NSUInteger numberSelected = [fTorrents count]; 402 if (numberSelected != 1) 403 { 404 if (numberSelected > 0) 405 { 406 [fImageView setImage: [NSImage imageNamed: NSImageNameMultipleDocuments]]; 407 408 [fNameField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%@ Torrents Selected", 409 "Inspector -> selected torrents"), 410 [NSString formattedUInteger: numberSelected]]]; 411 [fNameField setHidden: NO]; 412 413 uint64_t size = 0; 414 NSUInteger fileCount = 0, magnetCount = 0; 415 for (Torrent * torrent in fTorrents) 416 { 417 size += [torrent size]; 418 fileCount += [torrent fileCount]; 419 if ([torrent isMagnet]) 420 ++magnetCount; 421 } 422 423 NSMutableArray * fileStrings = [NSMutableArray arrayWithCapacity: 2]; 424 if (fileCount > 0) 425 { 426 NSString * fileString; 427 if (fileCount == 1) 428 fileString = NSLocalizedString(@"1 file", "Inspector -> selected torrents"); 429 else 430 fileString = [NSString stringWithFormat: NSLocalizedString(@"%@ files", "Inspector -> selected torrents"), 431 [NSString formattedUInteger: fileCount]]; 432 [fileStrings addObject: fileString]; 433 } 434 if (magnetCount > 0) 435 { 436 NSString * magnetString; 437 if (magnetCount == 1) 438 magnetString = NSLocalizedString(@"1 magnetized transfer", "Inspector -> selected torrents"); 439 else 440 magnetString = [NSString stringWithFormat: NSLocalizedString(@"%@ magnetized transfers", 441 "Inspector -> selected torrents"), [NSString formattedUInteger: magnetCount]]; 442 [fileStrings addObject: magnetString]; 443 } 444 445 NSString * fileString = [fileStrings componentsJoinedByString: @" + "]; 446 447 if (magnetCount < numberSelected) 448 { 449 [fBasicInfoField setStringValue: [NSString stringWithFormat: @"%@, %@", fileString, 450 [NSString stringWithFormat: NSLocalizedString(@"%@ total", "Inspector -> selected torrents"), 451 [NSString stringForFileSize: size]]]]; 452 453 NSString * byteString; 454 if ([NSApp isOnMountainLionOrBetter]) 455 { 456 NSByteCountFormatter * formatter = [[NSByteCountFormatterMtLion alloc] init]; 457 [formatter setAllowedUnits: NSByteCountFormatterUseBytes]; 458 byteString = [formatter stringFromByteCount: size]; 459 [formatter release]; 460 } 461 else 462 byteString = [NSString stringWithFormat: NSLocalizedString(@"%@ bytes", "Inspector -> selected torrents"), [NSString formattedUInteger: size]]; 463 [fBasicInfoField setToolTip: byteString]; 464 } 465 else 466 { 467 [fBasicInfoField setStringValue: fileString]; 468 [fBasicInfoField setToolTip: nil]; 469 } 470 [fBasicInfoField setHidden: NO]; 471 472 [fNoneSelectedField setHidden: YES]; 473 } 474 else 475 { 476 [fImageView setImage: [NSImage imageNamed: NSImageNameApplicationIcon]]; 477 [fNoneSelectedField setHidden: NO]; 478 479 [fNameField setHidden: YES]; 480 [fBasicInfoField setHidden: YES]; 481 } 482 483 [fNameField setToolTip: nil]; 484 } 485 else 486 { 487 Torrent * torrent = [fTorrents objectAtIndex: 0]; 488 489 [fImageView setImage: [torrent icon]]; 490 491 NSString * name = [torrent name]; 492 [fNameField setStringValue: name]; 493 [fNameField setToolTip: name]; 494 [fNameField setHidden: NO]; 495 496 if (![torrent isMagnet]) 497 { 498 NSString * basicString = [NSString stringForFileSize: [torrent size]]; 499 if ([torrent isFolder]) 500 { 501 NSString * fileString; 502 const NSUInteger fileCount = [torrent fileCount]; 503 if (fileCount == 1) 504 fileString = NSLocalizedString(@"1 file", "Inspector -> selected torrents"); 505 else 506 fileString= [NSString stringWithFormat: NSLocalizedString(@"%@ files", "Inspector -> selected torrents"), 507 [NSString formattedUInteger: fileCount]]; 508 basicString = [NSString stringWithFormat: @"%@, %@", fileString, basicString]; 509 } 510 [fBasicInfoField setStringValue: basicString]; 511 512 NSString * byteString; 513 if ([NSApp isOnMountainLionOrBetter]) 514 { 515 NSByteCountFormatter * formatter = [[NSByteCountFormatterMtLion alloc] init]; 516 [formatter setAllowedUnits: NSByteCountFormatterUseBytes]; 517 byteString = [formatter stringFromByteCount: [torrent size]]; 518 [formatter release]; 519 } 520 else 521 byteString = [NSString stringWithFormat: NSLocalizedString(@"%@ bytes", "Inspector -> selected torrents"), [NSString formattedUInteger: [torrent size]]]; 522 [fBasicInfoField setToolTip: byteString]; 523 } 524 else 525 { 526 [fBasicInfoField setStringValue: NSLocalizedString(@"Magnetized transfer", "Inspector -> selected torrents")]; 527 [fBasicInfoField setToolTip: nil]; 528 } 529 [fBasicInfoField setHidden: NO]; 530 531 [fNoneSelectedField setHidden: YES]; 532 } 533 534 [fGeneralViewController setInfoForTorrents: fTorrents]; 535 [fActivityViewController setInfoForTorrents: fTorrents]; 536 [fTrackersViewController setInfoForTorrents: fTorrents]; 537 [fPeersViewController setInfoForTorrents: fTorrents]; 538 [fFileViewController setInfoForTorrents: fTorrents]; 539 [fOptionsViewController setInfoForTorrents: fTorrents]; 540 541 [fViewController updateInfo]; 542} 543 544- (void) resetInfoForTorrent: (NSNotification *) notification 545{ 546 if (fTorrents && [fTorrents containsObject: [notification object]]) 547 [self resetInfo]; 548} 549 550@end 551