mirror of
https://github.com/transmission/transmission
synced 2024-12-25 09:13:06 +00:00
604 lines
21 KiB
Objective-C
604 lines
21 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_GLOBAL_TAG 0
|
|
#define RATIO_NO_CHECK_TAG 1
|
|
#define RATIO_CHECK_TAG 2
|
|
|
|
#define MIN_WINDOW_WIDTH 300
|
|
#define MAX_WINDOW_WIDTH 5000
|
|
|
|
#define TAB_INFO_IDENT @"Info"
|
|
#define TAB_ACTIVITY_IDENT @"Activity"
|
|
#define TAB_PEERS_IDENT @"Peers"
|
|
#define TAB_FILES_IDENT @"Files"
|
|
#define TAB_OPTIONS_IDENT @"Options"
|
|
|
|
//15 spacing at the bottom of each tab
|
|
#define TAB_INFO_HEIGHT 182.0
|
|
#define TAB_ACTIVITY_HEIGHT 109.0
|
|
#define TAB_PEERS_HEIGHT 260.0
|
|
#define TAB_FILES_HEIGHT 260.0
|
|
#define TAB_OPTIONS_HEIGHT 83.0
|
|
|
|
@interface InfoWindowController (Private)
|
|
|
|
- (void) reloadPeerTable;
|
|
- (void) setWindowForTab: (NSString *) identifier animate: (BOOL) animate;
|
|
- (NSArray *) peerSortDescriptors;
|
|
|
|
@end
|
|
|
|
@implementation InfoWindowController
|
|
|
|
- (void) awakeFromNib
|
|
{
|
|
fAppIcon = [[NSApp applicationIconImage] copy];
|
|
fDotGreen = [NSImage imageNamed: @"GreenDot.tiff"];
|
|
fDotRed = [NSImage imageNamed: @"RedDot.tiff"];
|
|
fCheckImage = [NSImage imageNamed: @"NSMenuCheckmark"];
|
|
|
|
fTorrents = [[NSArray alloc] init];
|
|
fPeers = [[NSMutableArray alloc] initWithCapacity: 30];
|
|
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"];
|
|
|
|
//select tab
|
|
NSString * identifier = [[NSUserDefaults standardUserDefaults] stringForKey: @"InfoTab"];
|
|
if ([fTabView indexOfTabViewItemWithIdentifier: identifier] == NSNotFound)
|
|
identifier = TAB_INFO_IDENT;
|
|
|
|
[fTabView selectTabViewItemWithIdentifier: identifier];
|
|
[self setWindowForTab: identifier animate: NO];
|
|
|
|
//initially sort peer table by IP
|
|
if ([[fPeerTable sortDescriptors] count] == 0)
|
|
[fPeerTable setSortDescriptors: [NSArray arrayWithObject: [[fPeerTable tableColumnWithIdentifier: @"IP"]
|
|
sortDescriptorPrototype]]];
|
|
|
|
[self updateInfoForTorrents: [NSArray array]];
|
|
}
|
|
|
|
- (void) dealloc
|
|
{
|
|
[fTorrents release];
|
|
[fPeers 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: @""];
|
|
|
|
[fRevealDataButton setHidden: YES];
|
|
[fRevealTorrentButton setHidden: YES];
|
|
|
|
//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: @""];
|
|
[fRatioField setStringValue: @""];
|
|
|
|
[fSeedersField setStringValue: @""];
|
|
[fLeechersField setStringValue: @""];
|
|
[fConnectedPeersField setStringValue: @""];
|
|
[fDownloadingFromField setStringValue: @""];
|
|
[fUploadingToField setStringValue: @""];
|
|
[fSwarmSpeedField setStringValue: @""];
|
|
|
|
[fPeers removeAllObjects];
|
|
[fPeerTable reloadData];
|
|
|
|
[fPiecesView setTorrent: nil];
|
|
}
|
|
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]];
|
|
|
|
[fRevealDataButton setHidden: NO];
|
|
[fRevealTorrentButton setHidden: ![torrent publicTorrent]];
|
|
|
|
//allow these fields to be selected
|
|
[fTrackerField setSelectable: YES];
|
|
[fAnnounceField setSelectable: YES];
|
|
[fHashField setSelectable: YES];
|
|
[fTorrentLocationField setSelectable: YES];
|
|
[fDataLocationField setSelectable: YES];
|
|
|
|
[fPiecesView setTorrent: torrent];
|
|
}
|
|
|
|
//update stats and settings
|
|
[self updateInfoStats];
|
|
[self updateInfoSettings];
|
|
|
|
//set file table
|
|
[fFiles removeAllObjects];
|
|
|
|
if (numberSelected > 0)
|
|
{
|
|
Torrent * torrent;
|
|
NSEnumerator * enumerator = [fTorrents objectEnumerator];
|
|
while ((torrent = [enumerator nextObject]))
|
|
[fFiles addObjectsFromArray: [torrent fileList]];
|
|
|
|
[fFileTableStatusField setStringValue: [NSString stringWithFormat: @"%d file%s", [fFiles count],
|
|
[fFiles count] == 1 ? "" : "s"]];
|
|
}
|
|
else
|
|
[fFileTableStatusField setStringValue: @"info not available"];
|
|
|
|
[fFileTable deselectAll: nil];
|
|
[fFileTable reloadData];
|
|
}
|
|
|
|
- (void) updateInfoStats
|
|
{
|
|
int numberSelected = [fTorrents count];
|
|
if (numberSelected == 0)
|
|
return;
|
|
|
|
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];
|
|
|
|
//append percentage to amount downloaded if 1 torrent
|
|
[fDownloadedValidField setStringValue: [[fDownloadedValidField stringValue]
|
|
stringByAppendingFormat: @" (%.2f%%)", 100.0 * [torrent progress]]];
|
|
|
|
[fStateField setStringValue: [torrent stateString]];
|
|
|
|
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 stringWithFormat: @"%d (%d incoming)",
|
|
[torrent totalPeers], [torrent totalPeersIncoming]]: @""];
|
|
[fDownloadingFromField setStringValue: active ? [NSString stringWithInt: [torrent peersUploading]] : @""];
|
|
[fUploadingToField setStringValue: active ? [NSString stringWithInt: [torrent peersDownloading]] : @""];
|
|
|
|
[fRatioField setStringValue: [NSString stringForRatioWithDownload: downloadedTotal upload: uploadedTotal]];
|
|
|
|
[fSwarmSpeedField setStringValue: [torrent isActive] ? [NSString stringForSpeed: [torrent swarmSpeed]] : @""];
|
|
|
|
//set peers table if visible
|
|
if ([[[fTabView selectedTabViewItem] identifier] isEqualToString: TAB_PEERS_IDENT])
|
|
[self reloadPeerTable];
|
|
|
|
//update pieces view if visible
|
|
if ([[[fTabView selectedTabViewItem] identifier] isEqualToString: TAB_ACTIVITY_IDENT])
|
|
[fPiecesView updateView: NO];
|
|
}
|
|
}
|
|
|
|
- (void) updateInfoSettings
|
|
{
|
|
int numberSelected = [fTorrents count];
|
|
|
|
//set ratio settings
|
|
if (numberSelected > 0)
|
|
{
|
|
NSEnumerator * enumerator = [fTorrents objectEnumerator];
|
|
Torrent * 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];
|
|
}
|
|
|
|
//requires a non-empty torrent array
|
|
- (void) reloadPeerTable
|
|
{
|
|
Torrent * torrent = [fTorrents objectAtIndex: 0];
|
|
|
|
[fPeers setArray: [torrent peers]];
|
|
[fPeers sortUsingDescriptors: [self peerSortDescriptors]];
|
|
|
|
[fPeerTable reloadData];
|
|
}
|
|
|
|
- (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;
|
|
|
|
[fPiecesView updateView: YES];
|
|
}
|
|
else if ([identifier isEqualToString: TAB_PEERS_IDENT])
|
|
{
|
|
height = TAB_PEERS_HEIGHT;
|
|
|
|
if ([fTorrents count] == 1)
|
|
[self reloadPeerTable]; //initial update of peer table
|
|
}
|
|
else if ([identifier isEqualToString: TAB_FILES_IDENT])
|
|
height = TAB_FILES_HEIGHT;
|
|
else if ([identifier isEqualToString: TAB_OPTIONS_IDENT])
|
|
height = TAB_OPTIONS_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
|
|
{
|
|
if (tableView == fPeerTable)
|
|
return [fPeers count];
|
|
else
|
|
return [fFiles count];
|
|
}
|
|
|
|
- (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) column row: (int) row
|
|
{
|
|
NSString * ident = [column identifier];
|
|
if (tableView == fPeerTable)
|
|
{
|
|
NSDictionary * peer = [fPeers objectAtIndex: row];
|
|
|
|
if ([ident isEqualToString: @"Connected"])
|
|
return [[peer objectForKey: @"Connected"] boolValue] ? fDotGreen : fDotRed;
|
|
else if ([ident isEqualToString: @"UL To"])
|
|
return [[peer objectForKey: @"UL To"] boolValue] ? fCheckImage : nil;
|
|
else if ([ident isEqualToString: @"DL From"])
|
|
return [[peer objectForKey: @"DL From"] boolValue] ? fCheckImage : nil;
|
|
else if ([ident isEqualToString: @"Client"])
|
|
return [peer objectForKey: @"Client"];
|
|
else
|
|
return [peer objectForKey: @"IP"];
|
|
}
|
|
else
|
|
{
|
|
NSDictionary * file = [fFiles objectAtIndex: row];
|
|
if ([ident isEqualToString: @"Icon"])
|
|
return [[NSWorkspace sharedWorkspace] iconForFileType: [[file objectForKey: @"Name"] pathExtension]];
|
|
else if ([ident isEqualToString: @"Size"])
|
|
return [NSString stringForFileSize: [[file objectForKey: @"Size"] unsignedLongLongValue]];
|
|
else
|
|
return [[file objectForKey: @"Name"] lastPathComponent];
|
|
}
|
|
}
|
|
|
|
- (void) tableView: (NSTableView *) tableView didClickTableColumn: (NSTableColumn *) tableColumn
|
|
{
|
|
if (tableView == fPeerTable)
|
|
{
|
|
[fPeers sortUsingDescriptors: [self peerSortDescriptors]];
|
|
[tableView reloadData];
|
|
}
|
|
}
|
|
|
|
- (BOOL) tableView: (NSTableView *) tableView shouldSelectRow:(int) row
|
|
{
|
|
return tableView != fPeerTable;
|
|
}
|
|
|
|
//only called on >= 10.4
|
|
- (NSString *) tableView: (NSTableView *) tableView toolTipForCell: (NSCell *) cell
|
|
rect: (NSRectPointer) rect tableColumn: (NSTableColumn *) column
|
|
row: (int) row mouseLocation: (NSPoint) mouseLocation
|
|
{
|
|
if (tableView == fFileTable)
|
|
{
|
|
NSDictionary * file = [fFiles objectAtIndex: row];
|
|
if ([[column identifier] isEqualToString: @"Size"])
|
|
return [[[file objectForKey: @"Size"] stringValue] stringByAppendingString: @" bytes"];
|
|
else
|
|
return [file objectForKey: @"Name"];
|
|
}
|
|
else if (tableView == fPeerTable)
|
|
return [NSString stringWithFormat: @"From %@ connection",
|
|
[[[fPeers objectAtIndex: row] objectForKey: @"Incoming"] boolValue] ? @"incoming" : @"outgoing"];
|
|
else
|
|
return nil;
|
|
}
|
|
|
|
- (NSArray *) peerSortDescriptors
|
|
{
|
|
NSMutableArray * descriptors = [NSMutableArray array];
|
|
|
|
NSArray * oldDescriptors = [fPeerTable sortDescriptors];
|
|
if ([oldDescriptors count] > 0)
|
|
[descriptors addObject: [oldDescriptors objectAtIndex: 0]];
|
|
|
|
[descriptors addObject: [[fPeerTable tableColumnWithIdentifier: @"IP"] sortDescriptorPrototype]];
|
|
|
|
return descriptors;
|
|
}
|
|
|
|
- (void) revealTorrentFile: (id) sender
|
|
{
|
|
[[fTorrents objectAtIndex: 0] revealPublicTorrent];
|
|
}
|
|
|
|
- (void) revealDataFile: (id) sender
|
|
{
|
|
[[fTorrents objectAtIndex: 0] revealData];
|
|
}
|
|
|
|
- (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] objectForKey: @"Name"]
|
|
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];
|
|
}
|
|
}
|
|
|
|
@end
|