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