1/****************************************************************************** 2 * $Id: CreatorWindowController.m 13492 2012-09-10 02:37:29Z livings124 $ 3 * 4 * Copyright (c) 2007-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 "CreatorWindowController.h" 26#import "Controller.h" 27#import "NSApplicationAdditions.h" 28#import "NSStringAdditions.h" 29 30#import "transmission.h" // required by utils.h 31#import "utils.h" // tr_urlIsValidTracker() 32 33#define TRACKER_ADD_TAG 0 34#define TRACKER_REMOVE_TAG 1 35 36@interface CreatorWindowController (Private) 37 38+ (NSURL *) chooseFile; 39 40- (void) updateLocationField; 41- (void) createBlankAddressAlertDidEnd: (NSAlert *) alert returnCode: (NSInteger) returnCode contextInfo: (void *) contextInfo; 42- (void) createReal; 43- (void) checkProgress; 44- (void) failureSheetClosed: (NSAlert *) alert returnCode: (NSInteger) code contextInfo: (void *) info; 45 46@end 47 48@implementation CreatorWindowController 49 50+ (CreatorWindowController *) createTorrentFile: (tr_session *) handle 51{ 52 //get file/folder for torrent 53 NSURL * path; 54 if (!(path = [CreatorWindowController chooseFile])) 55 return nil; 56 57 CreatorWindowController * creator = [[self alloc] initWithHandle: handle path: path]; 58 [creator showWindow: nil]; 59 return creator; 60} 61 62+ (CreatorWindowController *) createTorrentFile: (tr_session *) handle forFile: (NSURL *) file 63{ 64 CreatorWindowController * creator = [[self alloc] initWithHandle: handle path: file]; 65 [creator showWindow: nil]; 66 return creator; 67} 68 69- (id) initWithHandle: (tr_session *) handle path: (NSURL *) path 70{ 71 if ((self = [super initWithWindowNibName: @"Creator"])) 72 { 73 fStarted = NO; 74 75 fPath = [path retain]; 76 fInfo = tr_metaInfoBuilderCreate([[fPath path] UTF8String]); 77 78 if (fInfo->fileCount == 0) 79 { 80 NSAlert * alert = [[NSAlert alloc] init]; 81 [alert addButtonWithTitle: NSLocalizedString(@"OK", "Create torrent -> no files -> button")]; 82 [alert setMessageText: NSLocalizedString(@"This folder contains no files.", 83 "Create torrent -> no files -> title")]; 84 [alert setInformativeText: NSLocalizedString(@"There must be at least one file in a folder to create a torrent file.", 85 "Create torrent -> no files -> warning")]; 86 [alert setAlertStyle: NSWarningAlertStyle]; 87 88 [alert runModal]; 89 [alert release]; 90 91 [self release]; 92 return nil; 93 } 94 if (fInfo->totalSize == 0) 95 { 96 NSAlert * alert = [[NSAlert alloc] init]; 97 [alert addButtonWithTitle: NSLocalizedString(@"OK", "Create torrent -> zero size -> button")]; 98 [alert setMessageText: NSLocalizedString(@"The total file size is zero bytes.", 99 "Create torrent -> zero size -> title")]; 100 [alert setInformativeText: NSLocalizedString(@"A torrent file cannot be created for files with no size.", 101 "Create torrent -> zero size -> warning")]; 102 [alert setAlertStyle: NSWarningAlertStyle]; 103 104 [alert runModal]; 105 [alert release]; 106 107 [self release]; 108 return nil; 109 } 110 111 fDefaults = [NSUserDefaults standardUserDefaults]; 112 113 //get list of trackers 114 if (!(fTrackers = [[fDefaults arrayForKey: @"CreatorTrackers"] mutableCopy])) 115 { 116 fTrackers = [[NSMutableArray alloc] init]; 117 118 //check for single tracker from versions before 1.3 119 NSString * tracker; 120 if ((tracker = [fDefaults stringForKey: @"CreatorTracker"])) 121 { 122 [fDefaults removeObjectForKey: @"CreatorTracker"]; 123 if (![tracker isEqualToString: @""]) 124 { 125 [fTrackers addObject: tracker]; 126 [fDefaults setObject: fTrackers forKey: @"CreatorTrackers"]; 127 } 128 } 129 } 130 131 //remove potentially invalid addresses 132 for (NSInteger i = [fTrackers count]-1; i >= 0; i--) 133 { 134 if (!tr_urlIsValidTracker([[fTrackers objectAtIndex: i] UTF8String])) 135 [fTrackers removeObjectAtIndex: i]; 136 } 137 } 138 return self; 139} 140 141- (void) awakeFromNib 142{ 143 if ([NSApp isOnLionOrBetter]) 144 [[self window] setRestorationClass: [self class]]; 145 146 NSString * name = [fPath lastPathComponent]; 147 148 [[self window] setTitle: name]; 149 150 [fNameField setStringValue: name]; 151 [fNameField setToolTip: [fPath path]]; 152 153 const BOOL multifile = !fInfo->isSingleFile; 154 155 NSImage * icon = [[NSWorkspace sharedWorkspace] iconForFileType: multifile 156 ? NSFileTypeForHFSTypeCode(kGenericFolderIcon) : [fPath pathExtension]]; 157 [icon setSize: [fIconView frame].size]; 158 [fIconView setImage: icon]; 159 160 NSString * statusString = [NSString stringForFileSize: fInfo->totalSize]; 161 if (multifile) 162 { 163 NSString * fileString; 164 NSInteger count = fInfo->fileCount; 165 if (count != 1) 166 fileString = [NSString stringWithFormat: NSLocalizedString(@"%@ files", "Create torrent -> info"), 167 [NSString formattedUInteger: count]]; 168 else 169 fileString = NSLocalizedString(@"1 file", "Create torrent -> info"); 170 statusString = [NSString stringWithFormat: @"%@, %@", fileString, statusString]; 171 } 172 [fStatusField setStringValue: statusString]; 173 174 if (fInfo->pieceCount == 1) 175 [fPiecesField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"1 piece, %@", "Create torrent -> info"), 176 [NSString stringForFileSize: fInfo->pieceSize]]]; 177 else 178 [fPiecesField setStringValue: [NSString stringWithFormat: NSLocalizedString(@"%d pieces, %@ each", "Create torrent -> info"), 179 fInfo->pieceCount, [NSString stringForFileSize: fInfo->pieceSize]]]; 180 181 fLocation = [[[fDefaults URLForKey: @"CreatorLocationURL"] URLByAppendingPathComponent: [name stringByAppendingPathExtension: @"torrent"]] retain]; 182 if (!fLocation) 183 { 184 //for 2.5 and earlier 185 #warning we still store "CreatorLocation" in Defaults.plist, and not "CreatorLocationURL" 186 NSString * location = [fDefaults stringForKey: @"CreatorLocation"]; 187 fLocation = [[NSURL alloc] initFileURLWithPath: [[location stringByExpandingTildeInPath] stringByAppendingPathComponent: [name stringByAppendingPathExtension: @"torrent"]]]; 188 } 189 [self updateLocationField]; 190 191 //set previously saved values 192 if ([fDefaults objectForKey: @"CreatorPrivate"]) 193 [fPrivateCheck setState: [fDefaults boolForKey: @"CreatorPrivate"] ? NSOnState : NSOffState]; 194 195 [fOpenCheck setState: [fDefaults boolForKey: @"CreatorOpen"] ? NSOnState : NSOffState]; 196} 197 198- (void) dealloc 199{ 200 [fPath release]; 201 [fLocation release]; 202 203 [fTrackers release]; 204 205 if (fInfo) 206 tr_metaInfoBuilderFree(fInfo); 207 208 [fTimer invalidate]; 209 [fTimer release]; 210 211 [super dealloc]; 212} 213 214+ (void) restoreWindowWithIdentifier: (NSString *) identifier state: (NSCoder *) state completionHandler: (void (^)(NSWindow *, NSError *)) completionHandler 215{ 216 NSURL * path = [state decodeObjectForKey: @"TRCreatorPath"]; 217 if (!path || ![path checkResourceIsReachableAndReturnError: nil]) 218 { 219 completionHandler(nil, [NSError errorWithDomain: NSURLErrorDomain code: NSURLErrorCannotOpenFile userInfo: nil]); 220 return; 221 } 222 223 NSWindow * window = [[self createTorrentFile: [(Controller *)[NSApp delegate] sessionHandle] forFile: path] window]; 224 completionHandler(window, nil); 225} 226 227- (void) window: (NSWindow *) window willEncodeRestorableState: (NSCoder *) state 228{ 229 [state encodeObject: fPath forKey: @"TRCreatorPath"]; 230 [state encodeObject: fLocation forKey: @"TRCreatorLocation"]; 231 [state encodeObject: fTrackers forKey: @"TRCreatorTrackers"]; 232 [state encodeInteger: [fOpenCheck state] forKey: @"TRCreatorOpenCheck"]; 233 [state encodeInteger: [fPrivateCheck state] forKey: @"TRCreatorPrivateCheck"]; 234 [state encodeObject: [fCommentView string] forKey: @"TRCreatorPrivateComment"]; 235} 236 237- (void) window: (NSWindow *) window didDecodeRestorableState: (NSCoder *) coder 238{ 239 [fLocation release]; 240 fLocation = [[coder decodeObjectForKey: @"TRCreatorLocation"] retain]; 241 [self updateLocationField]; 242 243 [fTrackers release]; 244 fTrackers = [[coder decodeObjectForKey: @"TRCreatorTrackers"] retain]; 245 [fTrackerTable reloadData]; 246 247 [fOpenCheck setState: [coder decodeIntegerForKey: @"TRCreatorOpenCheck"]]; 248 [fPrivateCheck setState: [coder decodeIntegerForKey: @"TRCreatorPrivateCheck"]]; 249 [fCommentView setString: [coder decodeObjectForKey: @"TRCreatorPrivateComment"]]; 250} 251 252- (IBAction) setLocation: (id) sender 253{ 254 NSSavePanel * panel = [NSSavePanel savePanel]; 255 256 [panel setPrompt: NSLocalizedString(@"Select", "Create torrent -> location sheet -> button")]; 257 [panel setMessage: NSLocalizedString(@"Select the name and location for the torrent file.", 258 "Create torrent -> location sheet -> message")]; 259 260 [panel setAllowedFileTypes: [NSArray arrayWithObjects: @"org.bittorrent.torrent", @"torrent", nil]]; 261 [panel setCanSelectHiddenExtension: YES]; 262 263 [panel setDirectoryURL: [fLocation URLByDeletingLastPathComponent]]; 264 [panel setNameFieldStringValue: [fLocation lastPathComponent]]; 265 266 [panel beginSheetModalForWindow: [self window] completionHandler: ^(NSInteger result) { 267 if (result == NSFileHandlingPanelOKButton) 268 { 269 [fLocation release]; 270 fLocation = [[panel URL] retain]; 271 [self updateLocationField]; 272 } 273 }]; 274} 275 276- (IBAction) create: (id) sender 277{ 278 //make sure the trackers are no longer being verified 279 if ([fTrackerTable editedRow] != -1) 280 [[self window] endEditingFor: fTrackerTable]; 281 282 const BOOL isPrivate = [fPrivateCheck state] == NSOnState; 283 if ([fTrackers count] == 0 284 && [fDefaults boolForKey: isPrivate ? @"WarningCreatorPrivateBlankAddress" : @"WarningCreatorBlankAddress"]) 285 { 286 NSAlert * alert = [[NSAlert alloc] init]; 287 [alert setMessageText: NSLocalizedString(@"There are no tracker addresses.", "Create torrent -> blank address -> title")]; 288 289 NSString * infoString = isPrivate 290 ? NSLocalizedString(@"A transfer marked as private with no tracker addresses will be unable to connect to peers." 291 " The torrent file will only be useful if you plan to upload the file to a tracker website" 292 " that will add the addresses for you.", "Create torrent -> blank address -> message") 293 : NSLocalizedString(@"The transfer will not contact trackers for peers, and will have to rely solely on" 294 " non-tracker peer discovery methods such as PEX and DHT to download and seed.", 295 "Create torrent -> blank address -> message"); 296 297 [alert setInformativeText: infoString]; 298 [alert addButtonWithTitle: NSLocalizedString(@"Create", "Create torrent -> blank address -> button")]; 299 [alert addButtonWithTitle: NSLocalizedString(@"Cancel", "Create torrent -> blank address -> button")]; 300 [alert setShowsSuppressionButton: YES]; 301 302 [alert beginSheetModalForWindow: [self window] modalDelegate: self 303 didEndSelector: @selector(createBlankAddressAlertDidEnd:returnCode:contextInfo:) contextInfo: nil]; 304 } 305 else 306 [self createReal]; 307} 308 309- (IBAction) cancelCreateWindow: (id) sender 310{ 311 [[self window] close]; 312} 313 314- (void) windowWillClose: (NSNotification *) notification 315{ 316 [self autorelease]; 317} 318 319- (IBAction) cancelCreateProgress: (id) sender 320{ 321 fInfo->abortFlag = 1; 322 [fTimer fire]; 323} 324 325- (NSInteger) numberOfRowsInTableView: (NSTableView *) tableView 326{ 327 return [fTrackers count]; 328} 329 330- (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) tableColumn row: (NSInteger) row 331{ 332 return [fTrackers objectAtIndex: row]; 333} 334 335- (IBAction) addRemoveTracker: (id) sender 336{ 337 //don't allow add/remove when currently adding - it leads to weird results 338 if ([fTrackerTable editedRow] != -1) 339 return; 340 341 if ([[sender cell] tagForSegment: [sender selectedSegment]] == TRACKER_REMOVE_TAG) 342 { 343 [fTrackers removeObjectsAtIndexes: [fTrackerTable selectedRowIndexes]]; 344 345 [fTrackerTable deselectAll: self]; 346 [fTrackerTable reloadData]; 347 } 348 else 349 { 350 [fTrackers addObject: @""]; 351 [fTrackerTable reloadData]; 352 353 const NSInteger row = [fTrackers count] - 1; 354 [fTrackerTable selectRowIndexes: [NSIndexSet indexSetWithIndex: row] byExtendingSelection: NO]; 355 [fTrackerTable editColumn: 0 row: row withEvent: nil select: YES]; 356 } 357} 358 359- (void) tableView: (NSTableView *) tableView setObjectValue: (id) object forTableColumn: (NSTableColumn *) tableColumn 360 row: (NSInteger) row 361{ 362 NSString * tracker = (NSString *)object; 363 364 tracker = [tracker stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]]; 365 366 if ([tracker rangeOfString: @"://"].location == NSNotFound) 367 tracker = [@"http://" stringByAppendingString: tracker]; 368 369 if (!tr_urlIsValidTracker([tracker UTF8String])) 370 { 371 NSBeep(); 372 [fTrackers removeObjectAtIndex: row]; 373 } 374 else 375 [fTrackers replaceObjectAtIndex: row withObject: tracker]; 376 377 [fTrackerTable deselectAll: self]; 378 [fTrackerTable reloadData]; 379} 380 381- (void) tableViewSelectionDidChange: (NSNotification *) notification 382{ 383 [fTrackerAddRemoveControl setEnabled: [fTrackerTable numberOfSelectedRows] > 0 forSegment: TRACKER_REMOVE_TAG]; 384} 385 386- (void) copy: (id) sender 387{ 388 NSArray * addresses = [fTrackers objectsAtIndexes: [fTrackerTable selectedRowIndexes]]; 389 NSString * text = [addresses componentsJoinedByString: @"\n"]; 390 391 NSPasteboard * pb = [NSPasteboard generalPasteboard]; 392 [pb clearContents]; 393 [pb writeObjects: [NSArray arrayWithObject: text]]; 394} 395 396- (BOOL) validateMenuItem: (NSMenuItem *) menuItem 397{ 398 const SEL action = [menuItem action]; 399 400 if (action == @selector(copy:)) 401 return [[self window] firstResponder] == fTrackerTable && [fTrackerTable numberOfSelectedRows] > 0; 402 403 if (action == @selector(paste:)) 404 return [[self window] firstResponder] == fTrackerTable 405 && [[NSPasteboard generalPasteboard] canReadObjectForClasses: [NSArray arrayWithObject: [NSString class]] options: nil]; 406 407 return YES; 408} 409 410- (void) paste: (id) sender 411{ 412 NSMutableArray * tempTrackers = [NSMutableArray array]; 413 414 NSArray * items = [[NSPasteboard generalPasteboard] readObjectsForClasses: [NSArray arrayWithObject: [NSString class]] options: nil]; 415 NSAssert(items != nil, @"no string items to paste; should not be able to call this method"); 416 417 for (NSString * pbItem in items) 418 { 419 for (NSString * tracker in [pbItem componentsSeparatedByString: @"\n"]) 420 [tempTrackers addObject: tracker]; 421 } 422 423 BOOL added = NO; 424 425 for (NSString * tracker in tempTrackers) 426 { 427 tracker = [tracker stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]]; 428 429 if ([tracker rangeOfString: @"://"].location == NSNotFound) 430 tracker = [@"http://" stringByAppendingString: tracker]; 431 432 if (tr_urlIsValidTracker([tracker UTF8String])) 433 { 434 [fTrackers addObject: tracker]; 435 added = YES; 436 } 437 } 438 439 if (added) 440 { 441 [fTrackerTable deselectAll: self]; 442 [fTrackerTable reloadData]; 443 } 444 else 445 NSBeep(); 446} 447 448@end 449 450@implementation CreatorWindowController (Private) 451 452- (void) updateLocationField 453{ 454 NSString * pathString = [fLocation path]; 455 [fLocationField setStringValue: [pathString stringByAbbreviatingWithTildeInPath]]; 456 [fLocationField setToolTip: pathString]; 457} 458 459+ (NSURL *) chooseFile 460{ 461 NSOpenPanel * panel = [NSOpenPanel openPanel]; 462 463 [panel setTitle: NSLocalizedString(@"Create Torrent File", "Create torrent -> select file")]; 464 [panel setPrompt: NSLocalizedString(@"Select", "Create torrent -> select file")]; 465 [panel setAllowsMultipleSelection: NO]; 466 [panel setCanChooseFiles: YES]; 467 [panel setCanChooseDirectories: YES]; 468 [panel setCanCreateDirectories: NO]; 469 470 [panel setMessage: NSLocalizedString(@"Select a file or folder for the torrent file.", "Create torrent -> select file")]; 471 472 BOOL success = [panel runModal] == NSOKButton; 473 return success ? [[panel URLs] objectAtIndex: 0] : nil; 474} 475 476- (void) createBlankAddressAlertDidEnd: (NSAlert *) alert returnCode: (NSInteger) returnCode contextInfo: (void *) contextInfo 477{ 478 if ([[alert suppressionButton] state] == NSOnState) 479 { 480 [[NSUserDefaults standardUserDefaults] setBool: NO forKey: @"WarningCreatorBlankAddress"]; //set regardless of private/public 481 if ([fPrivateCheck state] == NSOnState) 482 [[NSUserDefaults standardUserDefaults] setBool: NO forKey: @"WarningCreatorPrivateBlankAddress"]; 483 } 484 485 [alert release]; 486 487 if (returnCode == NSAlertFirstButtonReturn) 488 [self performSelectorOnMainThread: @selector(createReal) withObject: nil waitUntilDone: NO]; 489} 490 491- (void) createReal 492{ 493 //check if the location currently exists 494 if (![[fLocation URLByDeletingLastPathComponent] checkResourceIsReachableAndReturnError: NULL]) 495 { 496 NSAlert * alert = [[[NSAlert alloc] init] autorelease]; 497 [alert addButtonWithTitle: NSLocalizedString(@"OK", "Create torrent -> directory doesn't exist warning -> button")]; 498 [alert setMessageText: NSLocalizedString(@"The chosen torrent file location does not exist.", 499 "Create torrent -> directory doesn't exist warning -> title")]; 500 [alert setInformativeText: [NSString stringWithFormat: 501 NSLocalizedString(@"The directory \"%@\" does not currently exist. " 502 "Create this directory or choose a different one to create the torrent file.", 503 "Create torrent -> directory doesn't exist warning -> warning"), 504 [[fLocation URLByDeletingLastPathComponent] path]]]; 505 [alert setAlertStyle: NSWarningAlertStyle]; 506 507 [alert beginSheetModalForWindow: [self window] modalDelegate: self didEndSelector: nil contextInfo: nil]; 508 return; 509 } 510 511 //check if a file with the same name and location already exists 512 if ([fLocation checkResourceIsReachableAndReturnError: NULL]) 513 { 514 NSArray * pathComponents = [fLocation pathComponents]; 515 NSInteger count = [pathComponents count]; 516 517 NSAlert * alert = [[[NSAlert alloc] init] autorelease]; 518 [alert addButtonWithTitle: NSLocalizedString(@"OK", "Create torrent -> file already exists warning -> button")]; 519 [alert setMessageText: NSLocalizedString(@"A torrent file with this name and directory cannot be created.", 520 "Create torrent -> file already exists warning -> title")]; 521 [alert setInformativeText: [NSString stringWithFormat: 522 NSLocalizedString(@"A file with the name \"%@\" already exists in the directory \"%@\". " 523 "Choose a new name or directory to create the torrent file.", 524 "Create torrent -> file already exists warning -> warning"), 525 [pathComponents objectAtIndex: count-1], [pathComponents objectAtIndex: count-2]]]; 526 [alert setAlertStyle: NSWarningAlertStyle]; 527 528 [alert beginSheetModalForWindow: [self window] modalDelegate: self didEndSelector: nil contextInfo: nil]; 529 return; 530 } 531 532 //parse non-empty tracker strings 533 tr_tracker_info * trackerInfo = tr_new0(tr_tracker_info, [fTrackers count]); 534 535 for (NSUInteger i = 0; i < [fTrackers count]; i++) 536 { 537 trackerInfo[i].announce = (char *)[[fTrackers objectAtIndex: i] UTF8String]; 538 trackerInfo[i].tier = i; 539 } 540 541 //store values 542 [fDefaults setObject: fTrackers forKey: @"CreatorTrackers"]; 543 [fDefaults setBool: [fPrivateCheck state] == NSOnState forKey: @"CreatorPrivate"]; 544 [fDefaults setBool: [fOpenCheck state] == NSOnState forKey: @"CreatorOpen"]; 545 fOpenWhenCreated = [fOpenCheck state] == NSOnState; //need this since the check box might not exist, and value in prefs might have changed from another creator window 546 [fDefaults setURL: [fLocation URLByDeletingLastPathComponent] forKey: @"CreatorLocationURL"]; 547 548 if ([NSApp isOnLionOrBetter]) 549 [[self window] setRestorable: NO]; 550 551 [[NSNotificationCenter defaultCenter] postNotificationName: @"BeginCreateTorrentFile" object: fLocation userInfo: nil]; 552 tr_makeMetaInfo(fInfo, [[fLocation path] UTF8String], trackerInfo, [fTrackers count], [[fCommentView string] UTF8String], [fPrivateCheck state] == NSOnState); 553 tr_free(trackerInfo); 554 555 fTimer = [[NSTimer scheduledTimerWithTimeInterval: 0.1 target: self selector: @selector(checkProgress) userInfo: nil repeats: YES] retain]; 556} 557 558- (void) checkProgress 559{ 560 if (fInfo->isDone) 561 { 562 [fTimer invalidate]; 563 [fTimer release]; 564 fTimer = nil; 565 566 NSAlert * alert; 567 switch (fInfo->result) 568 { 569 case TR_MAKEMETA_OK: 570 if (fOpenWhenCreated) 571 { 572 NSDictionary * dict = [[NSDictionary alloc] initWithObjectsAndKeys: [fLocation path], @"File", 573 [[fPath URLByDeletingLastPathComponent] path], @"Path", nil]; 574 [[NSNotificationCenter defaultCenter] postNotificationName: @"OpenCreatedTorrentFile" object: self userInfo: dict]; 575 } 576 577 [[self window] close]; 578 break; 579 580 case TR_MAKEMETA_CANCELLED: 581 [[self window] close]; 582 break; 583 584 default: 585 alert = [[[NSAlert alloc] init] autorelease]; 586 [alert addButtonWithTitle: NSLocalizedString(@"OK", "Create torrent -> failed -> button")]; 587 [alert setMessageText: [NSString stringWithFormat: NSLocalizedString(@"Creation of \"%@\" failed.", 588 "Create torrent -> failed -> title"), [fLocation lastPathComponent]]]; 589 [alert setAlertStyle: NSWarningAlertStyle]; 590 591 if (fInfo->result == TR_MAKEMETA_IO_READ) 592 [alert setInformativeText: [NSString stringWithFormat: NSLocalizedString(@"Could not read \"%s\": %s.", 593 "Create torrent -> failed -> warning"), fInfo->errfile, strerror(fInfo->my_errno)]]; 594 else if (fInfo->result == TR_MAKEMETA_IO_WRITE) 595 [alert setInformativeText: [NSString stringWithFormat: NSLocalizedString(@"Could not write \"%s\": %s.", 596 "Create torrent -> failed -> warning"), fInfo->errfile, strerror(fInfo->my_errno)]]; 597 else //invalid url should have been caught before creating 598 [alert setInformativeText: [NSString stringWithFormat: @"%@ (%d)", 599 NSLocalizedString(@"An unknown error has occurred.", "Create torrent -> failed -> warning"), fInfo->result]]; 600 601 [alert beginSheetModalForWindow: [self window] modalDelegate: self 602 didEndSelector: @selector(failureSheetClosed:returnCode:contextInfo:) contextInfo: nil]; 603 } 604 } 605 else 606 { 607 [fProgressIndicator setDoubleValue: (double)fInfo->pieceIndex / fInfo->pieceCount]; 608 609 if (!fStarted) 610 { 611 fStarted = YES; 612 613 [fProgressView setHidden: YES]; 614 615 NSWindow * window = [self window]; 616 [window setFrameAutosaveName: @""]; 617 618 NSRect windowRect = [window frame]; 619 CGFloat difference = [fProgressView frame].size.height - [[window contentView] frame].size.height; 620 windowRect.origin.y -= difference; 621 windowRect.size.height += difference; 622 623 //don't allow vertical resizing 624 CGFloat height = windowRect.size.height; 625 [window setMinSize: NSMakeSize([window minSize].width, height)]; 626 [window setMaxSize: NSMakeSize([window maxSize].width, height)]; 627 628 [window setContentView: fProgressView]; 629 [window setFrame: windowRect display: YES animate: YES]; 630 [fProgressView setHidden: NO]; 631 632 [[window standardWindowButton: NSWindowCloseButton] setEnabled: NO]; 633 } 634 } 635} 636 637- (void) failureSheetClosed: (NSAlert *) alert returnCode: (NSInteger) code contextInfo: (void *) info 638{ 639 [[alert window] orderOut: nil]; 640 [[self window] close]; 641} 642 643@end 644