mirror of
https://github.com/transmission/transmission
synced 2024-12-27 18:18:10 +00:00
500 lines
17 KiB
Objective-C
500 lines
17 KiB
Objective-C
/******************************************************************************
|
|
* $Id$
|
|
*
|
|
* Copyright (c) 2006 Transmission authors and contributors
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*****************************************************************************/
|
|
|
|
#import "InfoWindowController.h"
|
|
#import "StringAdditions.h"
|
|
|
|
#define RATIO_NO_CHECK_TAG 0
|
|
#define RATIO_GLOBAL_TAG 1
|
|
#define RATIO_CHECK_TAG 2
|
|
|
|
#define MIN_WINDOW_WIDTH 270
|
|
#define MAX_WINDOW_WIDTH 2000
|
|
|
|
#define TAB_INFO_IDENT @"Info"
|
|
#define TAB_ACTIVITY_IDENT @"Activity"
|
|
#define TAB_OPTIONS_IDENT @"Options"
|
|
#define TAB_FILES_IDENT @"Files"
|
|
|
|
//15 spacing at the bottom of each tab
|
|
#define TAB_INFO_HEIGHT 182.0
|
|
#define TAB_ACTIVITY_HEIGHT 214.0
|
|
#define TAB_OPTIONS_HEIGHT 116.0
|
|
#define TAB_FILES_HEIGHT 250.0
|
|
|
|
@interface InfoWindowController (Private)
|
|
|
|
- (void) setWindowForTab: (NSString *) identifier animate: (BOOL) animate;
|
|
|
|
@end
|
|
|
|
@implementation InfoWindowController
|
|
|
|
- (void) awakeFromNib
|
|
{
|
|
fAppIcon = [[NSApp applicationIconImage] copy];
|
|
|
|
fTorrents = [[NSArray alloc] init];
|
|
fFiles = [[NSMutableArray alloc] initWithCapacity: 6];
|
|
[fFileTable setDoubleAction: @selector(revealFile:)];
|
|
|
|
//window location and size
|
|
NSPanel * window = (NSPanel *)[self window];
|
|
|
|
[window setBecomesKeyOnlyIfNeeded: YES];
|
|
|
|
[window setFrameAutosaveName: @"InspectorWindowFrame"];
|
|
[window setFrameUsingName: @"InspectorWindowFrame"];
|
|
|
|
NSString * identifier = [[NSUserDefaults standardUserDefaults] stringForKey: @"InfoTab"];
|
|
[fTabView selectTabViewItemWithIdentifier: identifier];
|
|
[self setWindowForTab: identifier animate: NO];
|
|
|
|
[self updateInfoForTorrents: [NSArray array]];
|
|
}
|
|
|
|
- (void) dealloc
|
|
{
|
|
[fTorrents release];
|
|
[fFiles release];
|
|
|
|
[fAppIcon release];
|
|
[super dealloc];
|
|
}
|
|
|
|
- (void) updateInfoForTorrents: (NSArray *) torrents
|
|
{
|
|
[fTorrents release];
|
|
fTorrents = [torrents retain];
|
|
|
|
int numberSelected = [fTorrents count];
|
|
if (numberSelected != 1)
|
|
{
|
|
if (numberSelected > 0)
|
|
{
|
|
[fNameField setStringValue: [NSString stringWithFormat:
|
|
@"%d Torrents Selected", numberSelected]];
|
|
|
|
uint64_t size = 0;
|
|
NSEnumerator * enumerator = [torrents objectEnumerator];
|
|
Torrent * torrent;
|
|
while ((torrent = [enumerator nextObject]))
|
|
size += [torrent size];
|
|
|
|
[fSizeField setStringValue: [[NSString stringForFileSize: size]
|
|
stringByAppendingString: @" Total"]];
|
|
}
|
|
else
|
|
{
|
|
[fNameField setStringValue: @"No Torrents Selected"];
|
|
[fSizeField setStringValue: @""];
|
|
|
|
[fDownloadedValidField setStringValue: @""];
|
|
[fDownloadedTotalField setStringValue: @""];
|
|
[fUploadedTotalField setStringValue: @""];
|
|
}
|
|
|
|
[fImageView setImage: fAppIcon];
|
|
|
|
[fNameField setToolTip: nil];
|
|
|
|
[fTrackerField setStringValue: @""];
|
|
[fTrackerField setToolTip: nil];
|
|
[fAnnounceField setStringValue: @""];
|
|
[fAnnounceField setToolTip: nil];
|
|
[fPieceSizeField setStringValue: @""];
|
|
[fPiecesField setStringValue: @""];
|
|
[fHashField setStringValue: @""];
|
|
[fHashField setToolTip: nil];
|
|
|
|
[fTorrentLocationField setStringValue: @""];
|
|
[fTorrentLocationField setToolTip: nil];
|
|
[fDataLocationField setStringValue: @""];
|
|
[fDataLocationField setToolTip: nil];
|
|
[fDateStartedField setStringValue: @""];
|
|
|
|
//don't allow empty fields to be selected
|
|
[fTrackerField setSelectable: NO];
|
|
[fAnnounceField setSelectable: NO];
|
|
[fHashField setSelectable: NO];
|
|
[fTorrentLocationField setSelectable: NO];
|
|
[fDataLocationField setSelectable: NO];
|
|
|
|
[fStateField setStringValue: @""];
|
|
/*
|
|
[fPercentField setStringValue: @""];
|
|
*/
|
|
[fRatioField setStringValue: @""];
|
|
|
|
[fSeedersField setStringValue: @""];
|
|
[fLeechersField setStringValue: @""];
|
|
[fConnectedPeersField setStringValue: @""];
|
|
[fDownloadingFromField setStringValue: @""];
|
|
[fUploadingToField setStringValue: @""];
|
|
}
|
|
else
|
|
{
|
|
Torrent * torrent = [fTorrents objectAtIndex: 0];
|
|
|
|
[fImageView setImage: [torrent icon]];
|
|
|
|
NSString * name = [torrent name];
|
|
[fNameField setStringValue: name];
|
|
[fNameField setToolTip: name];
|
|
[fSizeField setStringValue: [NSString stringForFileSize: [torrent size]]];
|
|
|
|
NSString * tracker = [torrent tracker],
|
|
* announce = [torrent announce],
|
|
* hashString = [torrent hashString];
|
|
[fTrackerField setStringValue: tracker];
|
|
[fTrackerField setToolTip: tracker];
|
|
[fAnnounceField setStringValue: announce];
|
|
[fAnnounceField setToolTip: announce];
|
|
[fPieceSizeField setStringValue: [NSString stringForFileSize: [torrent pieceSize]]];
|
|
[fPiecesField setIntValue: [torrent pieceCount]];
|
|
[fHashField setStringValue: hashString];
|
|
[fHashField setToolTip: hashString];
|
|
|
|
[fTorrentLocationField setStringValue: [torrent torrentLocationString]];
|
|
[fTorrentLocationField setToolTip: [torrent torrentLocation]];
|
|
[fDataLocationField setStringValue: [[torrent dataLocation] stringByAbbreviatingWithTildeInPath]];
|
|
[fDataLocationField setToolTip: [torrent dataLocation]];
|
|
[fDateStartedField setObjectValue: [torrent date]];
|
|
|
|
//allow these fields to be selected
|
|
[fTrackerField setSelectable: YES];
|
|
[fAnnounceField setSelectable: YES];
|
|
[fHashField setSelectable: YES];
|
|
[fTorrentLocationField setSelectable: YES];
|
|
[fDataLocationField setSelectable: YES];
|
|
}
|
|
|
|
//update stats and settings
|
|
[self updateInfoStatsAndSettings];
|
|
|
|
//set file table
|
|
[fFiles removeAllObjects];
|
|
|
|
Torrent * torrent;
|
|
NSEnumerator * enumerator = [fTorrents objectEnumerator];
|
|
while ((torrent = [enumerator nextObject]))
|
|
[fFiles addObjectsFromArray: [torrent fileList]];
|
|
|
|
[fFileTable deselectAll: nil];
|
|
[fFileTable reloadData];
|
|
}
|
|
|
|
- (void) updateInfoStats
|
|
{
|
|
int numberSelected = [fTorrents count];
|
|
if (numberSelected > 0)
|
|
{
|
|
float downloadedValid = 0;
|
|
uint64_t downloadedTotal = 0, uploadedTotal = 0;
|
|
Torrent * torrent;
|
|
NSEnumerator * enumerator = [fTorrents objectEnumerator];
|
|
while ((torrent = [enumerator nextObject]))
|
|
{
|
|
downloadedValid += [torrent downloadedValid];
|
|
downloadedTotal += [torrent downloadedTotal];
|
|
uploadedTotal += [torrent uploadedTotal];
|
|
}
|
|
|
|
[fDownloadedValidField setStringValue: [NSString stringForFileSize: downloadedValid]];
|
|
[fDownloadedTotalField setStringValue: [NSString stringForFileSize: downloadedTotal]];
|
|
[fUploadedTotalField setStringValue: [NSString stringForFileSize: uploadedTotal]];
|
|
|
|
if (numberSelected == 1)
|
|
{
|
|
torrent = [fTorrents objectAtIndex: 0];
|
|
|
|
[fStateField setStringValue: [torrent stateString]];
|
|
/*
|
|
[fPercentField setStringValue: [NSString stringWithFormat: @"%.2f%%", 100.0 * [torrent progress]]];
|
|
*/
|
|
int seeders = [torrent seeders], leechers = [torrent leechers];
|
|
[fSeedersField setStringValue: seeders < 0 ?
|
|
@"" : [NSString stringWithInt: seeders]];
|
|
[fLeechersField setStringValue: leechers < 0 ?
|
|
@"" : [NSString stringWithInt: leechers]];
|
|
|
|
BOOL active = [torrent isActive];
|
|
|
|
[fConnectedPeersField setStringValue: active ? [NSString
|
|
stringWithInt: [torrent totalPeers]] : @""];
|
|
[fDownloadingFromField setStringValue: active ? [NSString
|
|
stringWithInt: [torrent peersUploading]] : @""];
|
|
[fUploadingToField setStringValue: active ? [NSString
|
|
stringWithInt: [torrent peersDownloading]] : @""];
|
|
|
|
[fRatioField setStringValue: [NSString stringForRatioWithDownload:
|
|
downloadedTotal upload: uploadedTotal]];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void) updateInfoStatsAndSettings
|
|
{
|
|
int numberSelected = [fTorrents count];
|
|
|
|
//set wait to start
|
|
BOOL waiting = NO, notWaiting = NO, canEnableWaiting = numberSelected > 0,
|
|
waitingSettingOn = [[[NSUserDefaults standardUserDefaults] stringForKey: @"StartSetting"]
|
|
isEqualToString: @"Wait"];
|
|
|
|
NSEnumerator * enumerator = [fTorrents objectEnumerator];
|
|
Torrent * torrent;
|
|
while ((torrent = [enumerator nextObject]))
|
|
{
|
|
if ([torrent waitingToStart])
|
|
waiting = YES;
|
|
else
|
|
notWaiting = YES;
|
|
|
|
if (canEnableWaiting && !(![torrent isActive] && [torrent progress] < 1.0 && waitingSettingOn))
|
|
canEnableWaiting = NO;
|
|
}
|
|
|
|
[fWaitToStartButton setState: waiting && notWaiting ? NSMixedState : (waiting ? NSOnState : NSOffState)];
|
|
[fWaitToStartButton setEnabled: canEnableWaiting];
|
|
|
|
//set ratio settings
|
|
if (numberSelected > 0)
|
|
{
|
|
enumerator = [fTorrents objectEnumerator];
|
|
torrent = [enumerator nextObject]; //first torrent
|
|
const int INVALID = -99;
|
|
int ratioSetting = [torrent stopRatioSetting];
|
|
float ratioLimit = [torrent ratioLimit];
|
|
|
|
while ((ratioSetting != INVALID || ratioLimit != INVALID)
|
|
&& (torrent = [enumerator nextObject]))
|
|
{
|
|
if (ratioSetting != INVALID && ratioSetting != [torrent stopRatioSetting])
|
|
ratioSetting = INVALID;
|
|
|
|
if (ratioLimit != INVALID && ratioLimit != [torrent ratioLimit])
|
|
ratioLimit = INVALID;
|
|
}
|
|
|
|
[fRatioMatrix setEnabled: YES];
|
|
|
|
if (ratioSetting == RATIO_CHECK)
|
|
{
|
|
[fRatioMatrix selectCellWithTag: RATIO_CHECK_TAG];
|
|
[fRatioLimitField setEnabled: YES];
|
|
}
|
|
else
|
|
{
|
|
if (ratioSetting == RATIO_NO_CHECK)
|
|
[fRatioMatrix selectCellWithTag: RATIO_NO_CHECK_TAG];
|
|
else if (ratioSetting == RATIO_GLOBAL)
|
|
[fRatioMatrix selectCellWithTag: RATIO_GLOBAL_TAG];
|
|
else
|
|
[fRatioMatrix deselectAllCells];
|
|
|
|
[fRatioLimitField setEnabled: NO];
|
|
}
|
|
|
|
if (ratioLimit != INVALID)
|
|
[fRatioLimitField setFloatValue: ratioLimit];
|
|
else
|
|
[fRatioLimitField setStringValue: @""];
|
|
}
|
|
else
|
|
{
|
|
[fRatioMatrix deselectAllCells];
|
|
[fRatioMatrix setEnabled: NO];
|
|
|
|
[fRatioLimitField setEnabled: NO];
|
|
[fRatioLimitField setStringValue: @""];
|
|
}
|
|
|
|
[self updateInfoStats];
|
|
}
|
|
|
|
- (BOOL) validateMenuItem: (NSMenuItem *) menuItem
|
|
{
|
|
SEL action = [menuItem action];
|
|
|
|
if (action == @selector(revealFile:))
|
|
return [fFileTable numberOfSelectedRows] > 0 &&
|
|
[[[fTabView selectedTabViewItem] identifier] isEqualToString: TAB_FILES_IDENT];
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (NSRect) windowWillUseStandardFrame: (NSWindow *) window defaultFrame: (NSRect) defaultFrame
|
|
{
|
|
NSRect windowRect = [window frame];
|
|
windowRect.size.width = [window minSize].width;
|
|
return windowRect;
|
|
}
|
|
|
|
- (void) tabView: (NSTabView *) tabView didSelectTabViewItem: (NSTabViewItem *) tabViewItem
|
|
{
|
|
NSString * identifier = [tabViewItem identifier];
|
|
[self setWindowForTab: identifier animate: YES];
|
|
[[NSUserDefaults standardUserDefaults] setObject: identifier forKey: @"InfoTab"];
|
|
}
|
|
|
|
- (void) setWindowForTab: (NSString *) identifier animate: (BOOL) animate
|
|
{
|
|
float height;
|
|
if ([identifier isEqualToString: TAB_ACTIVITY_IDENT])
|
|
height = TAB_ACTIVITY_HEIGHT;
|
|
else if ([identifier isEqualToString: TAB_OPTIONS_IDENT])
|
|
height = TAB_OPTIONS_HEIGHT;
|
|
else if ([identifier isEqualToString: TAB_FILES_IDENT])
|
|
height = TAB_FILES_HEIGHT;
|
|
else
|
|
height = TAB_INFO_HEIGHT;
|
|
|
|
NSWindow * window = [self window];
|
|
NSRect frame = [window frame];
|
|
NSView * view = [[fTabView selectedTabViewItem] view];
|
|
|
|
float difference = height - [view frame].size.height;
|
|
frame.origin.y -= difference;
|
|
frame.size.height += difference;
|
|
|
|
if (animate)
|
|
{
|
|
[view setHidden: YES];
|
|
[window setFrame: frame display: YES animate: YES];
|
|
[view setHidden: NO];
|
|
}
|
|
else
|
|
[window setFrame: frame display: YES];
|
|
|
|
[window setMinSize: NSMakeSize(MIN_WINDOW_WIDTH, frame.size.height)];
|
|
[window setMaxSize: NSMakeSize(MAX_WINDOW_WIDTH, frame.size.height)];
|
|
}
|
|
|
|
- (void) setNextTab
|
|
{
|
|
if ([fTabView indexOfTabViewItem: [fTabView selectedTabViewItem]] == [fTabView numberOfTabViewItems] - 1)
|
|
[fTabView selectFirstTabViewItem: nil];
|
|
else
|
|
[fTabView selectNextTabViewItem: nil];
|
|
}
|
|
|
|
- (void) setPreviousTab
|
|
{
|
|
if ([fTabView indexOfTabViewItem: [fTabView selectedTabViewItem]] == 0)
|
|
[fTabView selectLastTabViewItem: nil];
|
|
else
|
|
[fTabView selectPreviousTabViewItem: nil];
|
|
}
|
|
|
|
- (int) numberOfRowsInTableView: (NSTableView *) tableView
|
|
{
|
|
return [fFiles count];
|
|
}
|
|
|
|
- (id) tableView: (NSTableView *) tableView objectValueForTableColumn:
|
|
(NSTableColumn *) column row: (int) row
|
|
{
|
|
NSString * file = [fFiles objectAtIndex: row];
|
|
if ([[column identifier] isEqualToString: @"Icon"])
|
|
return [[NSWorkspace sharedWorkspace] iconForFileType: [file pathExtension]];
|
|
else
|
|
return [file lastPathComponent];
|
|
}
|
|
|
|
//only called on >= 10.4
|
|
- (NSString *) tableView: (NSTableView *) tableView toolTipForCell: (NSCell *) cell
|
|
rect: (NSRectPointer) rect tableColumn: (NSTableColumn *) column
|
|
row: (int) row mouseLocation: (NSPoint) mouseLocation
|
|
{
|
|
return [fFiles objectAtIndex: row];
|
|
}
|
|
|
|
- (void) revealFile: (id) sender
|
|
{
|
|
NSIndexSet * indexSet = [fFileTable selectedRowIndexes];
|
|
unsigned int i;
|
|
for (i = [indexSet firstIndex]; i != NSNotFound; i = [indexSet indexGreaterThanIndex: i])
|
|
[[NSWorkspace sharedWorkspace] selectFile: [fFiles objectAtIndex: i] inFileViewerRootedAtPath: nil];
|
|
}
|
|
|
|
- (void) setRatioCheck: (id) sender
|
|
{
|
|
int ratioSetting, tag = [[fRatioMatrix selectedCell] tag];
|
|
if (tag == RATIO_CHECK_TAG)
|
|
ratioSetting = RATIO_CHECK;
|
|
else if (tag == RATIO_NO_CHECK_TAG)
|
|
ratioSetting = RATIO_NO_CHECK;
|
|
else
|
|
ratioSetting = RATIO_GLOBAL;
|
|
|
|
Torrent * torrent;
|
|
NSEnumerator * enumerator = [fTorrents objectEnumerator];
|
|
while ((torrent = [enumerator nextObject]))
|
|
[torrent setStopRatioSetting: ratioSetting];
|
|
|
|
[self setRatioLimit: fRatioLimitField];
|
|
[fRatioLimitField setEnabled: tag == RATIO_CHECK_TAG];
|
|
}
|
|
|
|
- (void) setRatioLimit: (id) sender
|
|
{
|
|
Torrent * torrent;
|
|
NSEnumerator * enumerator = [fTorrents objectEnumerator];
|
|
|
|
float ratioLimit = [sender floatValue];
|
|
if (![[sender stringValue] isEqualToString: [NSString stringWithFormat: @"%.2f", ratioLimit]]
|
|
|| ratioLimit < 0)
|
|
{
|
|
NSBeep();
|
|
float ratioLimit = [[enumerator nextObject] ratioLimit]; //use first torrent
|
|
while ((torrent = [enumerator nextObject]))
|
|
if (ratioLimit != [torrent ratioLimit])
|
|
{
|
|
[sender setStringValue: @""];
|
|
return;
|
|
}
|
|
|
|
[sender setFloatValue: ratioLimit];
|
|
}
|
|
else
|
|
{
|
|
while ((torrent = [enumerator nextObject]))
|
|
[torrent setRatioLimit: ratioLimit];
|
|
}
|
|
}
|
|
|
|
- (void) setWaitToStart: (id) sender
|
|
{
|
|
int state = [sender state];
|
|
|
|
NSEnumerator * enumerator = [fTorrents objectEnumerator];
|
|
Torrent * torrent;
|
|
while ((torrent = [enumerator nextObject]))
|
|
[torrent setWaitToStart: state];
|
|
|
|
[[NSNotificationCenter defaultCenter] postNotificationName: @"TorrentStartSettingChange" object: fTorrents];
|
|
}
|
|
|
|
@end
|