2022-01-20 18:27:56 +00:00
// This file Copyright © 2007-2022 Transmission authors and contributors.
// It may be used under the MIT (SPDX: MIT) license.
// License text can be found in the licenses/ folder.
2007-09-16 01:02:06 +00:00
2018-09-30 10:37:30 +00:00
#include <libtransmission/transmission.h>
2021-11-09 03:30:03 +00:00
#include <libtransmission/utils.h>
#include <libtransmission/web-utils.h> // tr_urlIsValidTracker()
2018-09-30 10:37:30 +00:00
2007-09-16 01:02:06 +00:00
#import "CreatorWindowController.h"
2012-05-28 14:28:50 +00:00
#import "Controller.h"
2012-06-18 01:33:27 +00:00
#import "NSApplicationAdditions.h"
2007-09-16 01:02:06 +00:00
#import "NSStringAdditions.h"
2010-04-23 16:59:14 +00:00
2008-06-04 20:55:38 +00:00
#define TRACKER_ADD_TAG 0
#define TRACKER_REMOVE_TAG 1
2007-09-16 01:02:06 +00:00
@interface CreatorWindowController (Private)
2021-08-15 09:41:48 +00:00
+ (NSURL*)chooseFile;
2008-05-28 23:57:25 +00:00
2021-08-15 09:41:48 +00:00
- (void)updateLocationField;
- (void)createReal;
- (void)checkProgress;
2007-09-16 01:02:06 +00:00
@end
2021-08-15 09:41:48 +00:00
NSMutableSet* creatorWindowControllerSet = nil;
2007-09-16 01:02:06 +00:00
@implementation CreatorWindowController
2021-08-15 09:41:48 +00:00
+ (CreatorWindowController*)createTorrentFile:(tr_session*)handle
2007-09-16 01:02:06 +00:00
{
//get file/folder for torrent
2021-08-15 09:41:48 +00:00
NSURL* path;
2007-09-16 01:02:06 +00:00
if (!(path = [CreatorWindowController chooseFile]))
2021-08-15 09:41:48 +00:00
{
2012-05-28 14:28:50 +00:00
return nil;
2021-08-15 09:41:48 +00:00
}
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
CreatorWindowController* creator = [[self alloc] initWithHandle:handle path:path];
[creator showWindow:nil];
2012-05-28 14:28:50 +00:00
return creator;
2007-09-16 01:02:06 +00:00
}
2021-08-15 09:41:48 +00:00
+ (CreatorWindowController*)createTorrentFile:(tr_session*)handle forFile:(NSURL*)file
2007-09-16 01:02:06 +00:00
{
2021-08-15 09:41:48 +00:00
CreatorWindowController* creator = [[self alloc] initWithHandle:handle path:file];
[creator showWindow:nil];
2012-05-28 14:28:50 +00:00
return creator;
2007-09-16 01:02:06 +00:00
}
2021-08-15 09:41:48 +00:00
- (instancetype)initWithHandle:(tr_session*)handle path:(NSURL*)path
2007-09-16 01:02:06 +00:00
{
2021-08-15 09:41:48 +00:00
if ((self = [super initWithWindowNibName:@"Creator"]))
2009-09-12 01:07:21 +00:00
{
2021-08-15 09:41:48 +00:00
if (!creatorWindowControllerSet)
{
2017-07-29 16:14:22 +00:00
creatorWindowControllerSet = [NSMutableSet set];
}
2021-08-15 09:41:48 +00:00
2007-09-16 01:02:06 +00:00
fStarted = NO;
2017-01-24 17:53:16 +00:00
2017-07-29 16:14:22 +00:00
fPath = path;
2021-08-07 07:27:56 +00:00
fInfo = tr_metaInfoBuilderCreate(fPath.path.UTF8String);
2017-01-24 17:53:16 +00:00
2007-09-16 01:02:06 +00:00
if (fInfo->fileCount == 0)
{
2021-08-15 09:41:48 +00:00
NSAlert* alert = [[NSAlert alloc] init];
[alert addButtonWithTitle:NSLocalizedString(@"OK", "Create torrent -> no files -> button")];
alert.messageText = NSLocalizedString(@"This folder contains no files.", "Create torrent -> no files -> title");
alert.informativeText = NSLocalizedString(
@"There must be at least one file in a folder to create a torrent file.",
2021-08-07 07:27:56 +00:00
"Create torrent -> no files -> warning");
2021-10-31 15:18:27 +00:00
alert.alertStyle = NSAlertStyleWarning;
2017-01-24 17:53:16 +00:00
2007-09-16 01:02:06 +00:00
[alert runModal];
2017-01-24 17:53:16 +00:00
2007-09-16 01:02:06 +00:00
return nil;
}
if (fInfo->totalSize == 0)
{
2021-08-15 09:41:48 +00:00
NSAlert* alert = [[NSAlert alloc] init];
[alert addButtonWithTitle:NSLocalizedString(@"OK", "Create torrent -> zero size -> button")];
alert.messageText = NSLocalizedString(@"The total file size is zero bytes.", "Create torrent -> zero size -> title");
alert.informativeText = NSLocalizedString(@"A torrent file cannot be created for files with no size.", "Create torrent -> zero size -> warning");
2021-10-31 15:18:27 +00:00
alert.alertStyle = NSAlertStyleWarning;
2017-01-24 17:53:16 +00:00
2007-09-16 01:02:06 +00:00
[alert runModal];
2017-01-24 17:53:16 +00:00
2007-09-16 01:02:06 +00:00
return nil;
}
2017-01-24 17:53:16 +00:00
2021-08-07 07:27:56 +00:00
fDefaults = NSUserDefaults.standardUserDefaults;
2017-01-24 17:53:16 +00:00
2008-06-04 20:55:38 +00:00
//get list of trackers
2021-08-15 09:41:48 +00:00
if (!(fTrackers = [[fDefaults arrayForKey:@"CreatorTrackers"] mutableCopy]))
2008-06-04 20:55:38 +00:00
{
2012-05-28 14:28:50 +00:00
fTrackers = [[NSMutableArray alloc] init];
2017-01-24 17:53:16 +00:00
2008-08-14 21:47:23 +00:00
//check for single tracker from versions before 1.3
2021-08-15 09:41:48 +00:00
NSString* tracker;
if ((tracker = [fDefaults stringForKey:@"CreatorTracker"]))
2008-06-04 20:55:38 +00:00
{
2021-08-15 09:41:48 +00:00
[fDefaults removeObjectForKey:@"CreatorTracker"];
if (![tracker isEqualToString:@""])
2008-08-17 22:50:02 +00:00
{
2021-08-15 09:41:48 +00:00
[fTrackers addObject:tracker];
[fDefaults setObject:fTrackers forKey:@"CreatorTrackers"];
2008-08-17 22:50:02 +00:00
}
2008-06-04 20:55:38 +00:00
}
2008-06-24 03:59:34 +00:00
}
2017-01-24 17:53:16 +00:00
2008-08-14 21:47:23 +00:00
//remove potentially invalid addresses
2021-08-15 09:41:48 +00:00
for (NSInteger i = fTrackers.count - 1; i >= 0; i--)
2008-08-14 21:47:23 +00:00
{
2017-07-08 09:16:01 +00:00
if (!tr_urlIsValidTracker([fTrackers[i] UTF8String]))
2021-08-15 09:41:48 +00:00
{
[fTrackers removeObjectAtIndex:i];
}
2008-08-14 21:47:23 +00:00
}
2021-08-15 09:41:48 +00:00
2017-07-29 16:14:22 +00:00
[creatorWindowControllerSet addObject:self];
2007-09-16 01:02:06 +00:00
}
return self;
}
2021-08-15 09:41:48 +00:00
- (void)awakeFromNib
2007-09-16 01:02:06 +00:00
{
2021-08-07 07:27:56 +00:00
self.window.restorationClass = [self class];
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
NSString* name = fPath.lastPathComponent;
2017-01-24 17:53:16 +00:00
2021-08-07 07:27:56 +00:00
self.window.title = name;
2017-01-24 17:53:16 +00:00
2021-08-07 07:27:56 +00:00
fNameField.stringValue = name;
fNameField.toolTip = fPath.path;
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
BOOL const multifile = fInfo->isFolder;
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
NSImage* icon = [NSWorkspace.sharedWorkspace
iconForFileType:multifile ? NSFileTypeForHFSTypeCode(kGenericFolderIcon) : fPath.pathExtension];
2021-08-07 07:27:56 +00:00
icon.size = fIconView.frame.size;
fIconView.image = icon;
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
NSString* statusString = [NSString stringForFileSize:fInfo->totalSize];
2007-09-16 01:02:06 +00:00
if (multifile)
{
2021-08-15 09:41:48 +00:00
NSString* fileString;
2008-09-07 17:57:58 +00:00
NSInteger count = fInfo->fileCount;
2007-09-16 01:02:06 +00:00
if (count != 1)
2021-08-15 09:41:48 +00:00
{
fileString = [NSString
stringWithFormat:NSLocalizedString(@"%@ files", "Create torrent -> info"), [NSString formattedUInteger:count]];
}
2007-09-16 01:02:06 +00:00
else
2021-08-15 09:41:48 +00:00
{
2008-04-07 13:29:46 +00:00
fileString = NSLocalizedString(@"1 file", "Create torrent -> info");
2021-08-15 09:41:48 +00:00
}
statusString = [NSString stringWithFormat:@"%@, %@", fileString, statusString];
2007-09-16 01:02:06 +00:00
}
2021-08-07 07:27:56 +00:00
fStatusField.stringValue = statusString;
2017-01-24 17:53:16 +00:00
2009-09-20 20:58:13 +00:00
if (fInfo->pieceCount == 1)
2021-08-15 09:41:48 +00:00
{
fPiecesField.stringValue = [NSString stringWithFormat:NSLocalizedString(@"1 piece, %@", "Create torrent -> info"),
[NSString stringForFileSize:fInfo->pieceSize]];
}
2007-09-16 01:02:06 +00:00
else
2021-08-15 09:41:48 +00:00
{
fPiecesField.stringValue = [NSString stringWithFormat:NSLocalizedString(@"%d pieces, %@ each", "Create torrent -> info"),
fInfo->pieceCount,
[NSString stringForFileSize:fInfo->pieceSize]];
}
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
fLocation = [[fDefaults URLForKey:@"CreatorLocationURL"] URLByAppendingPathComponent:[name stringByAppendingPathExtension:@"torrent"]];
2012-05-28 14:28:50 +00:00
if (!fLocation)
{
//for 2.5 and earlier
2021-08-15 09:41:48 +00:00
#warning we still store "CreatorLocation" in Defaults.plist, and not "CreatorLocationURL"
NSString* location = [fDefaults stringForKey:@"CreatorLocation"];
fLocation = [[NSURL alloc] initFileURLWithPath:[location.stringByExpandingTildeInPath
stringByAppendingPathComponent:[name stringByAppendingPathExtension:@"torrent"]]];
2012-05-28 14:28:50 +00:00
}
2012-05-28 19:43:53 +00:00
[self updateLocationField];
2017-01-24 17:53:16 +00:00
2007-09-16 01:02:06 +00:00
//set previously saved values
2021-08-15 09:41:48 +00:00
if ([fDefaults objectForKey:@"CreatorPrivate"])
{
2021-10-31 15:18:27 +00:00
fPrivateCheck.state = [fDefaults boolForKey:@"CreatorPrivate"] ? NSControlStateValueOn : NSControlStateValueOff;
2021-08-15 09:41:48 +00:00
}
2017-01-24 17:53:16 +00:00
2021-10-18 23:05:39 +00:00
if ([fDefaults objectForKey:@"CreatorSource"])
{
fSource.stringValue = [fDefaults stringForKey:@"CreatorSource"];
}
2021-10-31 15:18:27 +00:00
fOpenCheck.state = [fDefaults boolForKey:@"CreatorOpen"] ? NSControlStateValueOn : NSControlStateValueOff;
2007-09-16 01:02:06 +00:00
}
2021-08-15 09:41:48 +00:00
- (void)dealloc
2007-09-16 01:02:06 +00:00
{
if (fInfo)
2021-08-15 09:41:48 +00:00
{
2007-09-16 01:02:06 +00:00
tr_metaInfoBuilderFree(fInfo);
2021-08-15 09:41:48 +00:00
}
2017-01-24 17:53:16 +00:00
2008-06-18 16:25:30 +00:00
[fTimer invalidate];
2007-09-16 01:02:06 +00:00
}
2021-08-15 09:41:48 +00:00
+ (void)restoreWindowWithIdentifier:(NSString*)identifier
state:(NSCoder*)state
completionHandler:(void (^)(NSWindow*, NSError*))completionHandler
2012-05-28 14:28:50 +00:00
{
2021-08-15 09:41:48 +00:00
NSURL* path = [state decodeObjectForKey:@"TRCreatorPath"];
if (!path || ![path checkResourceIsReachableAndReturnError:nil])
2012-05-28 14:28:50 +00:00
{
2021-08-15 09:41:48 +00:00
completionHandler(nil, [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCannotOpenFile userInfo:nil]);
2012-05-28 14:28:50 +00:00
return;
}
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
NSWindow* window = [self createTorrentFile:((Controller*)NSApp.delegate).sessionHandle forFile:path].window;
2012-05-28 14:28:50 +00:00
completionHandler(window, nil);
}
2021-08-15 09:41:48 +00:00
- (void)window:(NSWindow*)window willEncodeRestorableState:(NSCoder*)state
2012-05-28 14:28:50 +00:00
{
2021-08-15 09:41:48 +00:00
[state encodeObject:fPath forKey:@"TRCreatorPath"];
[state encodeObject:fLocation forKey:@"TRCreatorLocation"];
[state encodeObject:fTrackers forKey:@"TRCreatorTrackers"];
[state encodeInteger:fOpenCheck.state forKey:@"TRCreatorOpenCheck"];
[state encodeInteger:fPrivateCheck.state forKey:@"TRCreatorPrivateCheck"];
2021-10-18 23:05:39 +00:00
[state encodeObject:fSource.stringValue forKey:@"TRCreatorSource"];
2021-08-15 09:41:48 +00:00
[state encodeObject:fCommentView.string forKey:@"TRCreatorPrivateComment"];
2012-05-28 14:28:50 +00:00
}
2021-08-15 09:41:48 +00:00
- (void)window:(NSWindow*)window didDecodeRestorableState:(NSCoder*)coder
2012-05-28 14:28:50 +00:00
{
2021-08-15 09:41:48 +00:00
fLocation = [coder decodeObjectForKey:@"TRCreatorLocation"];
2012-05-28 19:43:53 +00:00
[self updateLocationField];
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
fTrackers = [coder decodeObjectForKey:@"TRCreatorTrackers"];
2012-05-28 14:28:50 +00:00
[fTrackerTable reloadData];
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
fOpenCheck.state = [coder decodeIntegerForKey:@"TRCreatorOpenCheck"];
fPrivateCheck.state = [coder decodeIntegerForKey:@"TRCreatorPrivateCheck"];
2021-10-18 23:05:39 +00:00
fSource.stringValue = [coder decodeObjectForKey:@"TRCreatorSource"];
2021-08-15 09:41:48 +00:00
fCommentView.string = [coder decodeObjectForKey:@"TRCreatorPrivateComment"];
2012-05-28 14:28:50 +00:00
}
2021-08-15 09:41:48 +00:00
- (IBAction)setLocation:(id)sender
2007-09-16 01:02:06 +00:00
{
2021-08-15 09:41:48 +00:00
NSSavePanel* panel = [NSSavePanel savePanel];
2007-09-16 01:02:06 +00:00
2021-08-07 07:27:56 +00:00
panel.prompt = NSLocalizedString(@"Select", "Create torrent -> location sheet -> button");
2021-08-15 09:41:48 +00:00
panel.message = NSLocalizedString(@"Select the name and location for the torrent file.", "Create torrent -> location sheet -> message");
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
panel.allowedFileTypes = @[ @"org.bittorrent.torrent", @"torrent" ];
2021-08-07 07:27:56 +00:00
panel.canSelectHiddenExtension = YES;
2017-01-24 17:53:16 +00:00
2021-08-07 07:27:56 +00:00
panel.directoryURL = fLocation.URLByDeletingLastPathComponent;
panel.nameFieldStringValue = fLocation.lastPathComponent;
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
[panel beginSheetModalForWindow:self.window completionHandler:^(NSInteger result) {
2021-10-31 15:18:27 +00:00
if (result == NSModalResponseOK)
2011-12-11 22:31:01 +00:00
{
2021-08-07 07:27:56 +00:00
fLocation = panel.URL;
2012-05-28 19:43:53 +00:00
[self updateLocationField];
2011-12-11 22:31:01 +00:00
}
}];
2007-09-16 01:02:06 +00:00
}
2021-08-15 09:41:48 +00:00
- (IBAction)create:(id)sender
2007-09-16 01:02:06 +00:00
{
2009-09-12 00:23:13 +00:00
//make sure the trackers are no longer being verified
2021-08-07 07:27:56 +00:00
if (fTrackerTable.editedRow != -1)
2021-08-15 09:41:48 +00:00
{
[self.window endEditingFor:fTrackerTable];
}
2017-01-24 17:53:16 +00:00
2021-10-31 15:18:27 +00:00
BOOL const isPrivate = fPrivateCheck.state == NSControlStateValueOn;
2021-08-15 09:41:48 +00:00
if (fTrackers.count == 0 && [fDefaults boolForKey:isPrivate ? @"WarningCreatorPrivateBlankAddress" : @"WarningCreatorBlankAddress"])
2007-09-16 01:02:06 +00:00
{
2021-08-15 09:41:48 +00:00
NSAlert* alert = [[NSAlert alloc] init];
2021-08-07 07:27:56 +00:00
alert.messageText = NSLocalizedString(@"There are no tracker addresses.", "Create torrent -> blank address -> title");
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
NSString* infoString = isPrivate ?
NSLocalizedString(
@"A transfer marked as private with no tracker addresses will be unable to connect to peers."
" The torrent file will only be useful if you plan to upload the file to a tracker website"
" that will add the addresses for you.",
"Create torrent -> blank address -> message") :
NSLocalizedString(
@"The transfer will not contact trackers for peers, and will have to rely solely on"
" non-tracker peer discovery methods such as PEX and DHT to download and seed.",
"Create torrent -> blank address -> message");
2017-01-24 17:53:16 +00:00
2021-08-07 07:27:56 +00:00
alert.informativeText = infoString;
2021-08-15 09:41:48 +00:00
[alert addButtonWithTitle:NSLocalizedString(@"Create", "Create torrent -> blank address -> button")];
[alert addButtonWithTitle:NSLocalizedString(@"Cancel", "Create torrent -> blank address -> button")];
2021-08-07 07:27:56 +00:00
alert.showsSuppressionButton = YES;
2008-05-29 00:18:10 +00:00
2021-08-07 07:27:56 +00:00
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
2021-10-31 15:18:27 +00:00
if (alert.suppressionButton.state == NSControlStateValueOn)
2020-11-22 13:02:29 +00:00
{
2021-08-15 09:41:48 +00:00
[NSUserDefaults.standardUserDefaults setBool:NO forKey:@"WarningCreatorBlankAddress"]; //set regardless of private/public
2021-10-31 15:18:27 +00:00
if (fPrivateCheck.state == NSControlStateValueOn)
2021-08-15 09:41:48 +00:00
{
[NSUserDefaults.standardUserDefaults setBool:NO forKey:@"WarningCreatorPrivateBlankAddress"];
}
2020-11-22 13:02:29 +00:00
}
if (returnCode == NSAlertFirstButtonReturn)
2021-08-15 09:41:48 +00:00
{
[self performSelectorOnMainThread:@selector(createReal) withObject:nil waitUntilDone:NO];
}
2020-11-22 13:02:29 +00:00
}];
2007-09-16 01:02:06 +00:00
}
2008-05-29 00:18:10 +00:00
else
2021-08-15 09:41:48 +00:00
{
2008-05-28 23:57:25 +00:00
[self createReal];
2021-08-15 09:41:48 +00:00
}
2007-09-16 01:02:06 +00:00
}
2021-08-15 09:41:48 +00:00
- (IBAction)cancelCreateWindow:(id)sender
2007-09-16 01:02:06 +00:00
{
2021-08-07 07:27:56 +00:00
[self.window close];
2007-09-16 01:02:06 +00:00
}
2021-08-15 09:41:48 +00:00
- (void)windowWillClose:(NSNotification*)notification
2012-03-13 02:52:11 +00:00
{
2017-07-29 16:14:22 +00:00
[creatorWindowControllerSet removeObject:self];
2012-03-13 02:52:11 +00:00
}
2021-08-15 09:41:48 +00:00
- (IBAction)cancelCreateProgress:(id)sender
2007-09-16 01:02:06 +00:00
{
fInfo->abortFlag = 1;
[fTimer fire];
}
2021-08-15 09:41:48 +00:00
- (NSInteger)numberOfRowsInTableView:(NSTableView*)tableView
2008-06-04 20:55:38 +00:00
{
2021-08-07 07:27:56 +00:00
return fTrackers.count;
2008-06-04 20:55:38 +00:00
}
2021-08-15 09:41:48 +00:00
- (id)tableView:(NSTableView*)tableView objectValueForTableColumn:(NSTableColumn*)tableColumn row:(NSInteger)row
2008-06-04 20:55:38 +00:00
{
2017-07-08 09:16:01 +00:00
return fTrackers[row];
2008-06-04 20:55:38 +00:00
}
2021-08-15 09:41:48 +00:00
- (IBAction)addRemoveTracker:(id)sender
2008-06-04 20:55:38 +00:00
{
//don't allow add/remove when currently adding - it leads to weird results
2021-08-07 07:27:56 +00:00
if (fTrackerTable.editedRow != -1)
2021-08-15 09:41:48 +00:00
{
2008-06-04 20:55:38 +00:00
return;
2021-08-15 09:41:48 +00:00
}
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
if ([[sender cell] tagForSegment:[sender selectedSegment]] == TRACKER_REMOVE_TAG)
2008-06-04 20:55:38 +00:00
{
2021-08-15 09:41:48 +00:00
[fTrackers removeObjectsAtIndexes:fTrackerTable.selectedRowIndexes];
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
[fTrackerTable deselectAll:self];
2008-06-04 20:55:38 +00:00
[fTrackerTable reloadData];
}
else
{
2021-08-15 09:41:48 +00:00
[fTrackers addObject:@""];
2008-06-04 20:55:38 +00:00
[fTrackerTable reloadData];
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
NSInteger const row = fTrackers.count - 1;
[fTrackerTable selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
[fTrackerTable editColumn:0 row:row withEvent:nil select:YES];
2008-06-04 20:55:38 +00:00
}
}
2021-08-15 09:41:48 +00:00
- (void)tableView:(NSTableView*)tableView
setObjectValue:(id)object
forTableColumn:(NSTableColumn*)tableColumn
row:(NSInteger)row
2008-06-04 20:55:38 +00:00
{
2021-08-15 09:41:48 +00:00
NSString* tracker = (NSString*)object;
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
tracker = [tracker stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
if ([tracker rangeOfString:@"://"].location == NSNotFound)
{
tracker = [@"http://" stringByAppendingString:tracker];
}
2017-01-24 17:53:16 +00:00
2021-08-07 07:27:56 +00:00
if (!tr_urlIsValidTracker(tracker.UTF8String))
2008-06-04 20:55:38 +00:00
{
NSBeep();
2021-08-15 09:41:48 +00:00
[fTrackers removeObjectAtIndex:row];
2008-06-04 20:55:38 +00:00
}
else
2021-08-15 09:41:48 +00:00
{
2017-07-08 09:16:01 +00:00
fTrackers[row] = tracker;
2021-08-15 09:41:48 +00:00
}
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
[fTrackerTable deselectAll:self];
2008-06-04 20:55:38 +00:00
[fTrackerTable reloadData];
}
2021-08-15 09:41:48 +00:00
- (void)tableViewSelectionDidChange:(NSNotification*)notification
2008-06-04 20:55:38 +00:00
{
2021-08-15 09:41:48 +00:00
[fTrackerAddRemoveControl setEnabled:fTrackerTable.numberOfSelectedRows > 0 forSegment:TRACKER_REMOVE_TAG];
2008-06-04 20:55:38 +00:00
}
2021-08-15 09:41:48 +00:00
- (void)copy:(id)sender
2010-04-23 02:13:54 +00:00
{
2021-08-15 09:41:48 +00:00
NSArray* addresses = [fTrackers objectsAtIndexes:fTrackerTable.selectedRowIndexes];
NSString* text = [addresses componentsJoinedByString:@"\n"];
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
NSPasteboard* pb = NSPasteboard.generalPasteboard;
2011-10-06 00:30:40 +00:00
[pb clearContents];
2021-08-15 09:41:48 +00:00
[pb writeObjects:@[ text ]];
2010-04-23 02:13:54 +00:00
}
2021-08-15 09:41:48 +00:00
- (BOOL)validateMenuItem:(NSMenuItem*)menuItem
2010-04-23 02:05:34 +00:00
{
2021-08-15 09:41:48 +00:00
SEL const action = menuItem.action;
2017-01-24 17:53:16 +00:00
2010-04-23 02:13:54 +00:00
if (action == @selector(copy:))
2021-08-15 09:41:48 +00:00
{
2021-08-07 07:27:56 +00:00
return self.window.firstResponder == fTrackerTable && fTrackerTable.numberOfSelectedRows > 0;
2021-08-15 09:41:48 +00:00
}
2017-01-24 17:53:16 +00:00
2010-04-23 02:05:34 +00:00
if (action == @selector(paste:))
2021-08-15 09:41:48 +00:00
{
return self.window.firstResponder == fTrackerTable &&
[NSPasteboard.generalPasteboard canReadObjectForClasses:@[ [NSString class] ] options:nil];
}
2017-01-24 17:53:16 +00:00
2010-04-23 02:05:34 +00:00
return YES;
}
2021-08-15 09:41:48 +00:00
- (void)paste:(id)sender
2010-04-23 02:05:34 +00:00
{
2021-08-15 09:41:48 +00:00
NSMutableArray* tempTrackers = [NSMutableArray array];
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
NSArray* items = [NSPasteboard.generalPasteboard readObjectsForClasses:@[ [NSString class] ] options:nil];
2011-10-06 00:30:40 +00:00
NSAssert(items != nil, @"no string items to paste; should not be able to call this method");
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
for (NSString* pbItem in items)
2010-04-23 02:05:34 +00:00
{
2021-08-15 09:41:48 +00:00
for (NSString* tracker in [pbItem componentsSeparatedByString:@"\n"])
{
[tempTrackers addObject:tracker];
}
2010-04-23 02:05:34 +00:00
}
2017-01-24 17:53:16 +00:00
2010-04-23 02:05:34 +00:00
BOOL added = NO;
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
for (__strong NSString* tracker in tempTrackers)
2010-04-23 02:05:34 +00:00
{
2021-08-15 09:41:48 +00:00
tracker = [tracker stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
if ([tracker rangeOfString:@"://"].location == NSNotFound)
{
tracker = [@"http://" stringByAppendingString:tracker];
}
2017-01-24 17:53:16 +00:00
2021-08-07 07:27:56 +00:00
if (tr_urlIsValidTracker(tracker.UTF8String))
2010-04-23 02:05:34 +00:00
{
2021-08-15 09:41:48 +00:00
[fTrackers addObject:tracker];
2010-04-23 02:05:34 +00:00
added = YES;
}
}
2017-01-24 17:53:16 +00:00
2010-04-23 02:05:34 +00:00
if (added)
{
2021-08-15 09:41:48 +00:00
[fTrackerTable deselectAll:self];
2010-04-23 02:05:34 +00:00
[fTrackerTable reloadData];
}
else
2021-08-15 09:41:48 +00:00
{
2010-04-23 02:05:34 +00:00
NSBeep();
2021-08-15 09:41:48 +00:00
}
2010-04-23 02:05:34 +00:00
}
2007-09-16 01:02:06 +00:00
@end
@implementation CreatorWindowController (Private)
2021-08-15 09:41:48 +00:00
- (void)updateLocationField
2012-05-28 19:43:53 +00:00
{
2021-08-15 09:41:48 +00:00
NSString* pathString = fLocation.path;
2021-08-07 07:27:56 +00:00
fLocationField.stringValue = pathString.stringByAbbreviatingWithTildeInPath;
fLocationField.toolTip = pathString;
2012-05-28 19:43:53 +00:00
}
2021-08-15 09:41:48 +00:00
+ (NSURL*)chooseFile
2007-09-16 01:02:06 +00:00
{
2021-08-15 09:41:48 +00:00
NSOpenPanel* panel = [NSOpenPanel openPanel];
2017-01-24 17:53:16 +00:00
2021-08-07 07:27:56 +00:00
panel.title = NSLocalizedString(@"Create Torrent File", "Create torrent -> select file");
panel.prompt = NSLocalizedString(@"Select", "Create torrent -> select file");
panel.allowsMultipleSelection = NO;
panel.canChooseFiles = YES;
panel.canChooseDirectories = YES;
panel.canCreateDirectories = NO;
2007-09-16 01:02:06 +00:00
2021-08-07 07:27:56 +00:00
panel.message = NSLocalizedString(@"Select a file or folder for the torrent file.", "Create torrent -> select file");
2017-01-24 17:53:16 +00:00
2021-01-03 04:29:08 +00:00
BOOL success = [panel runModal] == NSModalResponseOK;
2021-08-07 07:27:56 +00:00
return success ? panel.URLs[0] : nil;
2007-09-16 01:02:06 +00:00
}
2021-08-15 09:41:48 +00:00
- (void)createReal
2008-05-28 23:57:25 +00:00
{
2009-09-26 20:13:15 +00:00
//check if the location currently exists
2021-08-15 09:41:48 +00:00
if (![fLocation.URLByDeletingLastPathComponent checkResourceIsReachableAndReturnError:NULL])
2009-09-26 20:13:15 +00:00
{
2021-08-15 09:41:48 +00:00
NSAlert* alert = [[NSAlert alloc] init];
[alert addButtonWithTitle:NSLocalizedString(@"OK", "Create torrent -> directory doesn't exist warning -> button")];
alert.messageText = NSLocalizedString(@"The chosen torrent file location does not exist.", "Create torrent -> directory doesn't exist warning -> title");
alert.informativeText = [NSString stringWithFormat:NSLocalizedString(
@"The directory \"%@\" does not currently exist. "
"Create this directory or choose a different one to create the torrent file.",
"Create torrent -> directory doesn't exist warning -> warning"),
fLocation.URLByDeletingLastPathComponent.path];
2021-10-31 15:18:27 +00:00
alert.alertStyle = NSAlertStyleWarning;
2017-01-24 17:53:16 +00:00
2021-08-07 07:27:56 +00:00
[alert beginSheetModalForWindow:self.window completionHandler:nil];
2009-09-26 20:13:15 +00:00
return;
}
2017-01-24 17:53:16 +00:00
2008-06-02 01:55:15 +00:00
//check if a file with the same name and location already exists
2021-08-15 09:41:48 +00:00
if ([fLocation checkResourceIsReachableAndReturnError:NULL])
2008-06-02 01:55:15 +00:00
{
2021-08-15 09:41:48 +00:00
NSArray* pathComponents = fLocation.pathComponents;
2021-08-07 07:27:56 +00:00
NSInteger count = pathComponents.count;
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
NSAlert* alert = [[NSAlert alloc] init];
[alert addButtonWithTitle:NSLocalizedString(@"OK", "Create torrent -> file already exists warning -> button")];
alert.messageText = NSLocalizedString(
@"A torrent file with this name and directory cannot be created.",
2021-08-07 07:27:56 +00:00
"Create torrent -> file already exists warning -> title");
2021-08-15 09:41:48 +00:00
alert.informativeText = [NSString stringWithFormat:NSLocalizedString(
@"A file with the name \"%@\" already exists in the directory \"%@\". "
"Choose a new name or directory to create the torrent file.",
"Create torrent -> file already exists warning -> warning"),
pathComponents[count - 1],
pathComponents[count - 2]];
2021-10-31 15:18:27 +00:00
alert.alertStyle = NSAlertStyleWarning;
2017-01-24 17:53:16 +00:00
2021-08-07 07:27:56 +00:00
[alert beginSheetModalForWindow:self.window completionHandler:nil];
2008-06-02 01:55:15 +00:00
return;
}
2017-01-24 17:53:16 +00:00
2008-05-28 23:57:25 +00:00
//parse non-empty tracker strings
2021-08-15 09:41:48 +00:00
tr_tracker_info* trackerInfo = tr_new0(tr_tracker_info, fTrackers.count);
2017-01-24 17:53:16 +00:00
2021-08-07 07:27:56 +00:00
for (NSUInteger i = 0; i < fTrackers.count; i++)
2009-10-10 12:58:40 +00:00
{
2021-08-15 09:41:48 +00:00
trackerInfo[i].announce = (char*)[fTrackers[i] UTF8String];
2009-10-10 12:58:40 +00:00
trackerInfo[i].tier = i;
}
2017-01-24 17:53:16 +00:00
2008-05-28 23:57:25 +00:00
//store values
2021-08-15 09:41:48 +00:00
[fDefaults setObject:fTrackers forKey:@"CreatorTrackers"];
2021-10-31 15:18:27 +00:00
[fDefaults setBool:fPrivateCheck.state == NSControlStateValueOn forKey:@"CreatorPrivate"];
2021-10-18 23:05:39 +00:00
[fDefaults setObject: [fSource stringValue] forKey: @"CreatorSource"];
2021-10-31 15:18:27 +00:00
[fDefaults setBool:fOpenCheck.state == NSControlStateValueOn forKey:@"CreatorOpen"];
fOpenWhenCreated = fOpenCheck.state == NSControlStateValueOn; //need this since the check box might not exist, and value in prefs might have changed from another creator window
2021-08-15 09:41:48 +00:00
[fDefaults setURL:fLocation.URLByDeletingLastPathComponent forKey:@"CreatorLocationURL"];
2016-01-06 11:05:37 +00:00
2021-08-07 07:27:56 +00:00
self.window.restorable = NO;
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
[NSNotificationCenter.defaultCenter postNotificationName:@"BeginCreateTorrentFile" object:fLocation userInfo:nil];
tr_makeMetaInfo(
fInfo,
fLocation.path.UTF8String,
trackerInfo,
fTrackers.count,
fCommentView.string.UTF8String,
2021-10-31 15:18:27 +00:00
fPrivateCheck.state == NSControlStateValueOn,
2021-10-18 23:05:39 +00:00
fSource.stringValue.UTF8String);
2008-06-04 20:55:38 +00:00
tr_free(trackerInfo);
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
fTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(checkProgress) userInfo:nil repeats:YES];
2008-05-28 23:57:25 +00:00
}
2021-08-15 09:41:48 +00:00
- (void)checkProgress
2007-09-16 01:02:06 +00:00
{
if (fInfo->isDone)
{
[fTimer invalidate];
fTimer = nil;
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
NSAlert* alert;
2008-03-24 19:38:29 +00:00
switch (fInfo->result)
2007-09-16 01:02:06 +00:00
{
2021-08-15 09:41:48 +00:00
case TR_MAKEMETA_OK:
if (fOpenWhenCreated)
{
NSDictionary* dict = @{ @"File" : fLocation.path, @"Path" : fPath.URLByDeletingLastPathComponent.path };
[NSNotificationCenter.defaultCenter postNotificationName:@"OpenCreatedTorrentFile" object:self userInfo:dict];
}
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
[self.window close];
break;
case TR_MAKEMETA_CANCELLED:
[self.window close];
break;
default:
alert = [[NSAlert alloc] init];
[alert addButtonWithTitle:NSLocalizedString(@"OK", "Create torrent -> failed -> button")];
alert.messageText = [NSString stringWithFormat:NSLocalizedString(@"Creation of \"%@\" failed.", "Create torrent -> failed -> title"),
fLocation.lastPathComponent];
2021-10-31 15:18:27 +00:00
alert.alertStyle = NSAlertStyleWarning;
2021-08-15 09:41:48 +00:00
if (fInfo->result == TR_MAKEMETA_IO_READ)
{
alert.informativeText = [NSString
stringWithFormat:NSLocalizedString(@"Could not read \"%s\": %s.", "Create torrent -> failed -> warning"),
fInfo->errfile,
strerror(fInfo->my_errno)];
}
else if (fInfo->result == TR_MAKEMETA_IO_WRITE)
{
alert.informativeText = [NSString
stringWithFormat:NSLocalizedString(@"Could not write \"%s\": %s.", "Create torrent -> failed -> warning"),
fInfo->errfile,
strerror(fInfo->my_errno)];
}
else //invalid url should have been caught before creating
{
alert.informativeText = [NSString
stringWithFormat:@"%@ (%d)",
NSLocalizedString(@"An unknown error has occurred.", "Create torrent -> failed -> warning"),
fInfo->result];
}
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
[alert beginSheetModalForWindow:self.window completionHandler:^(NSModalResponse returnCode) {
[alert.window orderOut:nil];
2021-08-07 07:27:56 +00:00
[self.window close];
2021-08-15 09:41:48 +00:00
}];
2007-09-16 01:02:06 +00:00
}
}
else
{
2021-08-07 07:27:56 +00:00
fProgressIndicator.doubleValue = (double)fInfo->pieceIndex / fInfo->pieceCount;
2017-01-24 17:53:16 +00:00
2007-09-16 01:02:06 +00:00
if (!fStarted)
{
2008-03-24 19:38:29 +00:00
fStarted = YES;
2017-01-24 17:53:16 +00:00
2021-08-07 07:27:56 +00:00
fProgressView.hidden = YES;
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
NSWindow* window = self.window;
2021-08-07 07:27:56 +00:00
window.frameAutosaveName = @"";
2017-01-24 17:53:16 +00:00
2021-08-07 07:27:56 +00:00
NSRect windowRect = window.frame;
CGFloat difference = fProgressView.frame.size.height - window.contentView.frame.size.height;
2007-09-16 01:02:06 +00:00
windowRect.origin.y -= difference;
windowRect.size.height += difference;
2017-01-24 17:53:16 +00:00
2007-09-16 01:02:06 +00:00
//don't allow vertical resizing
2008-09-07 17:57:58 +00:00
CGFloat height = windowRect.size.height;
2021-08-07 07:27:56 +00:00
window.minSize = NSMakeSize(window.minSize.width, height);
window.maxSize = NSMakeSize(window.maxSize.width, height);
2017-01-24 17:53:16 +00:00
2021-08-07 07:27:56 +00:00
window.contentView = fProgressView;
2021-08-15 09:41:48 +00:00
[window setFrame:windowRect display:YES animate:YES];
2021-08-07 07:27:56 +00:00
fProgressView.hidden = NO;
2017-01-24 17:53:16 +00:00
2021-08-15 09:41:48 +00:00
[window standardWindowButton:NSWindowCloseButton].enabled = NO;
2007-09-16 01:02:06 +00:00
}
}
}
@end