Refactor the info window, so that each view is in a separate xib and view controller. This should make future localizations simpler and the code more modular/readable.

This commit is contained in:
Mitchell Livingston 2010-03-06 23:12:30 +00:00
parent d83f8e09d3
commit d5f4c15fb8
23 changed files with 12739 additions and 7632 deletions

View File

@ -73,6 +73,18 @@
A20152640D1C1BE70081714F /* PinTemplate.png in Resources */ = {isa = PBXBuildFile; fileRef = A20152630D1C1BE70081714F /* PinTemplate.png */; };
A201527E0D1C270F0081714F /* torrent-ctor.c in Sources */ = {isa = PBXBuildFile; fileRef = A20152790D1C26EB0081714F /* torrent-ctor.c */; };
A2085DDC0C53BC74000BC3B7 /* AboutWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = A2085DDA0C53BC74000BC3B7 /* AboutWindowController.m */; };
A209EAC61142CF28002B02D1 /* InfoActivityViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A209EAC31142CF28002B02D1 /* InfoActivityViewController.m */; };
A209EAC71142CF28002B02D1 /* InfoGeneralViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A209EAC51142CF28002B02D1 /* InfoGeneralViewController.m */; };
A209EAEC1142D294002B02D1 /* InfoActivityView.xib in Resources */ = {isa = PBXBuildFile; fileRef = A209EAE81142D294002B02D1 /* InfoActivityView.xib */; };
A209EAED1142D294002B02D1 /* InfoGeneralView.xib in Resources */ = {isa = PBXBuildFile; fileRef = A209EAEA1142D294002B02D1 /* InfoGeneralView.xib */; };
A209EB011142D3A5002B02D1 /* InfoTrackersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A209EB001142D3A5002B02D1 /* InfoTrackersViewController.m */; };
A209EB201142DD85002B02D1 /* InfoTrackersView.xib in Resources */ = {isa = PBXBuildFile; fileRef = A209EB1F1142DD85002B02D1 /* InfoTrackersView.xib */; };
A209EB9D1142E59A002B02D1 /* InfoPeersViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A209EB9C1142E59A002B02D1 /* InfoPeersViewController.m */; };
A209EBA71142EAF3002B02D1 /* InfoPeersView.xib in Resources */ = {isa = PBXBuildFile; fileRef = A209EBA61142EAF3002B02D1 /* InfoPeersView.xib */; };
A209EBCE1142F2B4002B02D1 /* InfoFileViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A209EBCD1142F2B4002B02D1 /* InfoFileViewController.m */; };
A209EBD91142F52B002B02D1 /* InfoFileView.xib in Resources */ = {isa = PBXBuildFile; fileRef = A209EBD81142F52B002B02D1 /* InfoFileView.xib */; };
A209EBF91142FEEE002B02D1 /* InfoOptionsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A209EBF81142FEEE002B02D1 /* InfoOptionsViewController.m */; };
A209EC12114301C6002B02D1 /* InfoOptionsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = A209EC11114301C6002B02D1 /* InfoOptionsView.xib */; };
A20B6F6B0C4D842B0034AB1D /* PriorityLow.png in Resources */ = {isa = PBXBuildFile; fileRef = A20B6F6A0C4D842B0034AB1D /* PriorityLow.png */; };
A20B6F830C4D8A610034AB1D /* PriorityHigh.png in Resources */ = {isa = PBXBuildFile; fileRef = A20B6F820C4D8A610034AB1D /* PriorityHigh.png */; };
A20B6F8C0C4D90980034AB1D /* PriorityMixed.png in Resources */ = {isa = PBXBuildFile; fileRef = A20B6F8B0C4D90980034AB1D /* PriorityMixed.png */; };
@ -483,6 +495,24 @@
A202FF610DDA9275009938FF /* it */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = it; path = macosx/it.lproj/PrefsWindow.xib; sourceTree = "<group>"; };
A2085DD90C53BC74000BC3B7 /* AboutWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AboutWindowController.h; path = macosx/AboutWindowController.h; sourceTree = "<group>"; };
A2085DDA0C53BC74000BC3B7 /* AboutWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AboutWindowController.m; path = macosx/AboutWindowController.m; sourceTree = "<group>"; };
A209EAC21142CF28002B02D1 /* InfoActivityViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = InfoActivityViewController.h; path = macosx/InfoActivityViewController.h; sourceTree = "<group>"; };
A209EAC31142CF28002B02D1 /* InfoActivityViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = InfoActivityViewController.m; path = macosx/InfoActivityViewController.m; sourceTree = "<group>"; };
A209EAC41142CF28002B02D1 /* InfoGeneralViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = InfoGeneralViewController.h; path = macosx/InfoGeneralViewController.h; sourceTree = "<group>"; };
A209EAC51142CF28002B02D1 /* InfoGeneralViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = InfoGeneralViewController.m; path = macosx/InfoGeneralViewController.m; sourceTree = "<group>"; };
A209EAE91142D294002B02D1 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = macosx/en.lproj/InfoActivityView.xib; sourceTree = "<group>"; };
A209EAEB1142D294002B02D1 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = macosx/en.lproj/InfoGeneralView.xib; sourceTree = "<group>"; };
A209EAFF1142D3A5002B02D1 /* InfoTrackersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = InfoTrackersViewController.h; path = macosx/InfoTrackersViewController.h; sourceTree = "<group>"; };
A209EB001142D3A5002B02D1 /* InfoTrackersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = InfoTrackersViewController.m; path = macosx/InfoTrackersViewController.m; sourceTree = "<group>"; };
A209EB1F1142DD85002B02D1 /* InfoTrackersView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = InfoTrackersView.xib; path = macosx/InfoTrackersView.xib; sourceTree = "<group>"; };
A209EB9B1142E59A002B02D1 /* InfoPeersViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = InfoPeersViewController.h; path = macosx/InfoPeersViewController.h; sourceTree = "<group>"; };
A209EB9C1142E59A002B02D1 /* InfoPeersViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = InfoPeersViewController.m; path = macosx/InfoPeersViewController.m; sourceTree = "<group>"; };
A209EBA61142EAF3002B02D1 /* InfoPeersView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = InfoPeersView.xib; path = macosx/InfoPeersView.xib; sourceTree = "<group>"; };
A209EBCC1142F2B4002B02D1 /* InfoFileViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = InfoFileViewController.h; path = macosx/InfoFileViewController.h; sourceTree = "<group>"; };
A209EBCD1142F2B4002B02D1 /* InfoFileViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = InfoFileViewController.m; path = macosx/InfoFileViewController.m; sourceTree = "<group>"; };
A209EBD81142F52B002B02D1 /* InfoFileView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = InfoFileView.xib; path = macosx/InfoFileView.xib; sourceTree = "<group>"; };
A209EBF71142FEEE002B02D1 /* InfoOptionsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = InfoOptionsViewController.h; path = macosx/InfoOptionsViewController.h; sourceTree = "<group>"; };
A209EBF81142FEEE002B02D1 /* InfoOptionsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = InfoOptionsViewController.m; path = macosx/InfoOptionsViewController.m; sourceTree = "<group>"; };
A209EC13114301C6002B02D1 /* en */ = {isa = PBXFileReference; explicitFileType = file.xib; name = en; path = macosx/en.lproj/InfoOptionsView.xib; sourceTree = "<group>"; };
A20B6F6A0C4D842B0034AB1D /* PriorityLow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = PriorityLow.png; path = macosx/Images/PriorityLow.png; sourceTree = "<group>"; };
A20B6F820C4D8A610034AB1D /* PriorityHigh.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = PriorityHigh.png; path = macosx/Images/PriorityHigh.png; sourceTree = "<group>"; };
A20B6F8B0C4D90980034AB1D /* PriorityMixed.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = PriorityMixed.png; path = macosx/Images/PriorityMixed.png; sourceTree = "<group>"; };
@ -928,8 +958,6 @@
A21A9BE1106D86A800F1C3C1 /* TrackerNode.m */,
32CA4F630368D1EE00C91783 /* Transmission_Prefix.pch */,
29B97316FDCFA39411CA2CEA /* main.m */,
A29D84021049C25600D1987A /* NSApplicationAdditions.h */,
A29D84031049C25600D1987A /* NSApplicationAdditions.m */,
4DF0C5AA0899190500DD8943 /* Controller.h */,
4DF0C5A90899190500DD8943 /* Controller.m */,
4DFBC2DD09C0970D00D5C571 /* Torrent.h */,
@ -1051,6 +1079,12 @@
A233BD680D8CF2C7007EE7B4 /* StatsWindow.xib */,
A231274B0D11D0B7003F9AFF /* AboutWindow.xib */,
A2D307B00D9EC9F50051FD27 /* BlocklistStatusWindow.xib */,
A209EAEA1142D294002B02D1 /* InfoGeneralView.xib */,
A209EAE81142D294002B02D1 /* InfoActivityView.xib */,
A209EB1F1142DD85002B02D1 /* InfoTrackersView.xib */,
A209EBA61142EAF3002B02D1 /* InfoPeersView.xib */,
A209EBD81142F52B002B02D1 /* InfoFileView.xib */,
A209EC11114301C6002B02D1 /* InfoOptionsView.xib */,
A20B6FA40C4D97840034AB1D /* PriorityNone.png */,
A20B6F8B0C4D90980034AB1D /* PriorityMixed.png */,
A20B6FAD0C4D9B040034AB1D /* PriorityNormal.png */,
@ -1295,6 +1329,8 @@
children = (
4DE5CC9B0980656F00BE280E /* NSStringAdditions.h */,
4DE5CC9C0980656F00BE280E /* NSStringAdditions.m */,
A29D84021049C25600D1987A /* NSApplicationAdditions.h */,
A29D84031049C25600D1987A /* NSApplicationAdditions.m */,
);
name = Additions;
sourceTree = "<group>";
@ -1392,6 +1428,18 @@
E1B6FBF80C0D719B0015FE4D /* Info Window */ = {
isa = PBXGroup;
children = (
A209EAC41142CF28002B02D1 /* InfoGeneralViewController.h */,
A209EAC51142CF28002B02D1 /* InfoGeneralViewController.m */,
A209EAC21142CF28002B02D1 /* InfoActivityViewController.h */,
A209EAC31142CF28002B02D1 /* InfoActivityViewController.m */,
A209EAFF1142D3A5002B02D1 /* InfoTrackersViewController.h */,
A209EB001142D3A5002B02D1 /* InfoTrackersViewController.m */,
A209EB9B1142E59A002B02D1 /* InfoPeersViewController.h */,
A209EB9C1142E59A002B02D1 /* InfoPeersViewController.m */,
A209EBCC1142F2B4002B02D1 /* InfoFileViewController.h */,
A209EBCD1142F2B4002B02D1 /* InfoFileViewController.m */,
A209EBF71142FEEE002B02D1 /* InfoOptionsViewController.h */,
A209EBF81142FEEE002B02D1 /* InfoOptionsViewController.m */,
A257C17F0CAD3003004E121C /* PeerTableView.h */,
A257C1800CAD3003004E121C /* PeerTableView.m */,
A2E23AC30CB5E1930002BB25 /* InfoTabButtonCell.h */,
@ -1851,6 +1899,12 @@
A215BF5C0F02EBB800350CDB /* GroupRules.xib in Resources */,
A25963E11068376200453B31 /* FavIcon.png in Resources */,
A263D03E10DE6F010038DE27 /* Magnet.png in Resources */,
A209EAEC1142D294002B02D1 /* InfoActivityView.xib in Resources */,
A209EAED1142D294002B02D1 /* InfoGeneralView.xib in Resources */,
A209EB201142DD85002B02D1 /* InfoTrackersView.xib in Resources */,
A209EBA71142EAF3002B02D1 /* InfoPeersView.xib in Resources */,
A209EBD91142F52B002B02D1 /* InfoFileView.xib in Resources */,
A209EC12114301C6002B02D1 /* InfoOptionsView.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -2029,6 +2083,12 @@
A21A9BE2106D86A800F1C3C1 /* TrackerNode.m in Sources */,
A21A9D41106EC2E800F1C3C1 /* TrackerCell.m in Sources */,
A263CFC010DD67670038DE27 /* InfoTextField.m in Sources */,
A209EAC61142CF28002B02D1 /* InfoActivityViewController.m in Sources */,
A209EAC71142CF28002B02D1 /* InfoGeneralViewController.m in Sources */,
A209EB011142D3A5002B02D1 /* InfoTrackersViewController.m in Sources */,
A209EB9D1142E59A002B02D1 /* InfoPeersViewController.m in Sources */,
A209EBCE1142F2B4002B02D1 /* InfoFileViewController.m in Sources */,
A209EBF91142FEEE002B02D1 /* InfoOptionsViewController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -2158,6 +2218,30 @@
name = InfoPlist.strings;
sourceTree = "<group>";
};
A209EAE81142D294002B02D1 /* InfoActivityView.xib */ = {
isa = PBXVariantGroup;
children = (
A209EAE91142D294002B02D1 /* en */,
);
name = InfoActivityView.xib;
sourceTree = "<group>";
};
A209EAEA1142D294002B02D1 /* InfoGeneralView.xib */ = {
isa = PBXVariantGroup;
children = (
A209EAEB1142D294002B02D1 /* en */,
);
name = InfoGeneralView.xib;
sourceTree = "<group>";
};
A209EC11114301C6002B02D1 /* InfoOptionsView.xib */ = {
isa = PBXVariantGroup;
children = (
A209EC13114301C6002B02D1 /* en */,
);
name = InfoOptionsView.xib;
sourceTree = "<group>";
};
A215BF5B0F02EBB800350CDB /* GroupRules.xib */ = {
isa = PBXVariantGroup;
children = (

View File

@ -0,0 +1,52 @@
/******************************************************************************
* $Id$
*
* Copyright (c) 2010 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 <Cocoa/Cocoa.h>
@class PiecesView;
@class Torrent;
@interface InfoActivityViewController : NSViewController
{
NSArray * fTorrents;
IBOutlet NSTextField * fDateAddedField, * fDateCompletedField, * fDateActivityField,
* fStateField, * fProgressField,
* fHaveField, * fDownloadedTotalField, * fUploadedTotalField, * fFailedHashField,
* fRatioField;
IBOutlet NSTextView * fErrorMessageView;
IBOutlet PiecesView * fPiecesView;
IBOutlet NSSegmentedControl * fPiecesControl;
}
- (void) setInfoForTorrents: (NSArray *) torrents;
- (void) updateInfo;
- (void) setPiecesView: (id) sender;
- (void) setPiecesViewForAvailable: (BOOL) available;
- (void) clearPiecesView;
@end

View File

@ -0,0 +1,192 @@
/******************************************************************************
* $Id$
*
* Copyright (c) 2010 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 "InfoActivityViewController.h"
#import "NSStringAdditions.h"
#import "PiecesView.h"
#import "Torrent.h"
#include "utils.h" //tr_getRatio()
#define PIECES_CONTROL_PROGRESS 0
#define PIECES_CONTROL_AVAILABLE 1
@interface InfoActivityViewController (Private)
- (void) resetInfo;
@end
@implementation InfoActivityViewController
- (id) init
{
self = [super initWithNibName: @"InfoActivityView" bundle: nil];
return self;
}
- (void) dealloc
{
[fTorrents release];
[super dealloc];
}
- (void) setInfoForTorrents: (NSArray *) torrents
{
if (fTorrents && [fTorrents isEqualToArray: torrents])
return;
[fTorrents release];
fTorrents = [torrents retain];
[self resetInfo];
}
- (void) updateInfo
{
const NSInteger numberSelected = [fTorrents count];
if (numberSelected == 0)
return;
uint64_t have = 0, haveVerified = 0, downloadedTotal = 0, uploadedTotal = 0, failedHash = 0;
NSDate * lastActivity = nil;
for (Torrent * torrent in fTorrents)
{
have += [torrent haveTotal];
haveVerified += [torrent haveVerified];
downloadedTotal += [torrent downloadedTotal];
uploadedTotal += [torrent uploadedTotal];
failedHash += [torrent failedHash];
NSDate * nextLastActivity;
if ((nextLastActivity = [torrent dateActivity]))
lastActivity = lastActivity ? [lastActivity laterDate: nextLastActivity] : nextLastActivity;
}
if (have == 0)
[fHaveField setStringValue: [NSString stringForFileSize: 0]];
else
{
NSString * verifiedString = [NSString stringWithFormat: NSLocalizedString(@"%@ verified", "Inspector -> Activity tab -> have"),
[NSString stringForFileSize: haveVerified]];
if (have == haveVerified)
[fHaveField setStringValue: verifiedString];
else
[fHaveField setStringValue: [NSString stringWithFormat: @"%@ (%@)", [NSString stringForFileSize: have], verifiedString]];
}
[fDownloadedTotalField setStringValue: [NSString stringForFileSize: downloadedTotal]];
[fUploadedTotalField setStringValue: [NSString stringForFileSize: uploadedTotal]];
[fFailedHashField setStringValue: [NSString stringForFileSize: failedHash]];
[fDateActivityField setObjectValue: lastActivity];
if (numberSelected == 1)
{
Torrent * torrent = [fTorrents objectAtIndex: 0];
[fStateField setStringValue: [torrent stateString]];
if ([torrent isFolder])
[fProgressField setStringValue: [NSString localizedStringWithFormat: NSLocalizedString(@"%.2f%% (%.2f%% selected)",
"Inspector -> Activity tab -> progress"), 100.0 * [torrent progress], 100.0 * [torrent progressDone]]];
else
[fProgressField setStringValue: [NSString localizedStringWithFormat: @"%.2f%%", 100.0 * [torrent progress]]];
[fRatioField setStringValue: [NSString stringForRatio: [torrent ratio]]];
NSString * errorMessage = [torrent errorMessage];
if (![errorMessage isEqualToString: [fErrorMessageView string]])
[fErrorMessageView setString: errorMessage];
[fDateCompletedField setObjectValue: [torrent dateCompleted]];
[fPiecesView updateView];
}
else if (numberSelected > 1)
{
[fRatioField setStringValue: [NSString stringForRatio: tr_getRatio(uploadedTotal, downloadedTotal)]];
}
else;
}
- (void) setPiecesView: (id) sender
{
[self setPiecesViewForAvailable: [sender selectedSegment] == PIECES_CONTROL_AVAILABLE];
}
- (void) setPiecesViewForAvailable: (BOOL) available
{
[fPiecesControl setSelected: available forSegment: PIECES_CONTROL_AVAILABLE];
[fPiecesControl setSelected: !available forSegment: PIECES_CONTROL_PROGRESS];
[[NSUserDefaults standardUserDefaults] setBool: available forKey: @"PiecesViewShowAvailability"];
[fPiecesView updateView];
}
- (void) clearPiecesView
{
[fPiecesView clearView];
}
@end
@implementation InfoActivityViewController (Private)
- (void) resetInfo
{
const NSUInteger count = [fTorrents count];
if (count != 1)
{
if (count == 0)
{
[fHaveField setStringValue: @""];
[fDownloadedTotalField setStringValue: @""];
[fUploadedTotalField setStringValue: @""];
[fFailedHashField setStringValue: @""];
[fDateActivityField setStringValue: @""];
[fRatioField setStringValue: @""];
}
[fStateField setStringValue: @""];
[fProgressField setStringValue: @""];
[fErrorMessageView setString: @""];
[fDateAddedField setStringValue: @""];
[fDateCompletedField setStringValue: @""];
[fPiecesControl setSelected: NO forSegment: PIECES_CONTROL_AVAILABLE];
[fPiecesControl setSelected: NO forSegment: PIECES_CONTROL_PROGRESS];
[fPiecesControl setEnabled: NO];
[fPiecesView setTorrent: nil];
}
else
{
Torrent * torrent = [fTorrents objectAtIndex: 0];
[fDateAddedField setObjectValue: [torrent dateAdded]];
}
}
@end

1260
macosx/InfoFileView.xib Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,47 @@
/******************************************************************************
* $Id$
*
* Copyright (c) 2010 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 <Cocoa/Cocoa.h>
@class FileOutlineController;
@interface InfoFileViewController : NSViewController
{
NSArray * fTorrents;
IBOutlet FileOutlineController * fFileController;
IBOutlet NSSearchField * fFileFilterField;
}
- (void) setInfoForTorrents: (NSArray *) torrents;
- (void) updateInfo;
- (void) setFileFilterText: (id) sender;
- (NSArray *) quickLookURLs;
- (BOOL) canQuickLook;
- (NSRect) quickLookSourceFrameForPreviewItem: (id /*<QLPreviewItem>*/) item;
@end

View File

@ -0,0 +1,173 @@
/******************************************************************************
* $Id$
*
* Copyright (c) 2010 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 "InfoFileViewController.h"
#import "FileListNode.h"
#import "FileOutlineController.h"
#import "FileOutlineView.h"
#import "Torrent.h"
@interface InfoFileViewController (Private)
- (void) resetInfo;
- (BOOL) canQuickLookFile: (FileListNode *) item;
@end
@implementation InfoFileViewController
- (id) init
{
self = [super initWithNibName: @"InfoFileView" bundle: nil];
return self;
}
- (void) awakeFromNib
{
}
- (void) dealloc
{
[fTorrents release];
[super dealloc];
}
- (void) setInfoForTorrents: (NSArray *) torrents
{
if (fTorrents && [fTorrents isEqualToArray: torrents])
return;
[fTorrents release];
fTorrents = [torrents retain];
[self resetInfo];
}
- (void) updateInfo
{
if ([fTorrents count] == 1)
[fFileController reloadData];
}
- (void) setFileFilterText: (id) sender
{
[fFileController setFilterText: [sender stringValue]];
}
- (NSArray *) quickLookURLs
{
FileOutlineView * fileOutlineView = [fFileController outlineView];
Torrent * torrent = [fTorrents objectAtIndex: 0];
NSIndexSet * indexes = [fileOutlineView selectedRowIndexes];
NSMutableArray * urlArray = [NSMutableArray arrayWithCapacity: [indexes count]];
for (NSUInteger i = [indexes firstIndex]; i != NSNotFound; i = [indexes indexGreaterThanIndex: i])
{
FileListNode * item = [fileOutlineView itemAtRow: i];
if ([self canQuickLookFile: item])
[urlArray addObject: [NSURL fileURLWithPath: [torrent fileLocation: item]]];
}
return urlArray;
}
- (BOOL) canQuickLook
{
if ([fTorrents count] != 1)
return NO;
Torrent * torrent = [fTorrents objectAtIndex: 0];
if (![torrent isFolder])
return NO;
FileOutlineView * fileOutlineView = [fFileController outlineView];
NSIndexSet * indexes = [fileOutlineView selectedRowIndexes];
for (NSUInteger i = [indexes firstIndex]; i != NSNotFound; i = [indexes indexGreaterThanIndex: i])
if ([self canQuickLookFile: [fileOutlineView itemAtRow: i]])
return YES;
return NO;
}
#warning uncomment (in header too)
- (NSRect) quickLookSourceFrameForPreviewItem: (id /*<QLPreviewItem>*/) item
{
FileOutlineView * fileOutlineView = [fFileController outlineView];
NSString * fullPath = [(NSURL *)item path];
Torrent * torrent = [fTorrents objectAtIndex: 0];
NSRange visibleRows = [fileOutlineView rowsInRect: [fileOutlineView bounds]];
for (NSUInteger row = visibleRows.location; row < NSMaxRange(visibleRows); row++)
{
FileListNode * rowItem = [fileOutlineView itemAtRow: row];
if ([[torrent fileLocation: rowItem] isEqualToString: fullPath])
{
NSRect frame = [fileOutlineView iconRectForRow: row];
if (!NSIntersectsRect([fileOutlineView visibleRect], frame))
return NSZeroRect;
frame.origin = [fileOutlineView convertPoint: frame.origin toView: nil];
frame.origin = [[[self view] window] convertBaseToScreen: frame.origin];
frame.origin.y -= frame.size.height;
return frame;
}
}
return NSZeroRect;
}
@end
@implementation InfoFileViewController (Private)
- (void) resetInfo
{
[fFileFilterField setStringValue: @""];
if ([fTorrents count] == 1)
{
Torrent * torrent = [fTorrents objectAtIndex: 0];
[fFileController setTorrent: torrent];
[fFileFilterField setEnabled: [torrent isFolder]];
}
else
{
[fFileController setTorrent: nil];
[fFileFilterField setEnabled: NO];
}
}
- (BOOL) canQuickLookFile: (FileListNode *) item
{
Torrent * torrent = [fTorrents objectAtIndex: 0];
return ([item isFolder] || [torrent fileProgress: item] >= 1.0) && [torrent fileLocation: item];
}
@end

View File

@ -0,0 +1,45 @@
/******************************************************************************
* $Id$
*
* Copyright (c) 2010 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 <Cocoa/Cocoa.h>
@interface InfoGeneralViewController : NSViewController
{
NSArray * fTorrents;
IBOutlet NSTextField * fPiecesField, * fHashField, * fSecureField,
* fDataLocationField,
* fCreatorField, * fDateCreatedField;
IBOutlet NSTextView * fCommentView;
IBOutlet NSButton * fRevealDataButton;
}
- (void) setInfoForTorrents: (NSArray *) torrents;
- (void) updateInfo;
- (void) revealDataFile: (id) sender;
@end

View File

@ -0,0 +1,156 @@
/******************************************************************************
* $Id$
*
* Copyright (c) 2010 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 "InfoGeneralViewController.h"
#import "NSApplicationAdditions.h"
#import "NSStringAdditions.h"
#import "Torrent.h"
@interface InfoGeneralViewController (Private)
- (void) resetInfo;
@end
@implementation InfoGeneralViewController
- (id) init
{
self = [super initWithNibName: @"InfoGeneralView" bundle: nil];
return self;
}
- (void) awakeFromNib
{
if (![NSApp isOnSnowLeopardOrBetter])
{
NSImage * revealOn = [[NSImage imageNamed: @"RevealOn.png"] copy],
* revealOff = [[NSImage imageNamed: @"RevealOff.png"] copy];
[revealOn setFlipped: NO];
[revealOff setFlipped: NO];
[fRevealDataButton setImage: revealOff];
[fRevealDataButton setAlternateImage: revealOn];
[revealOn release];
[revealOff release];
}
}
- (void) dealloc
{
[fTorrents release];
[super dealloc];
}
- (void) setInfoForTorrents: (NSArray *) torrents
{
if (fTorrents && [fTorrents isEqualToArray: torrents])
return;
[fTorrents release];
fTorrents = [torrents retain];
[self resetInfo];
}
- (void) updateInfo
{
if ([fTorrents count] != 1)
return;
Torrent * torrent = [fTorrents objectAtIndex: 0];
NSString * location = [torrent dataLocation];
[fDataLocationField setStringValue: location ? [location stringByAbbreviatingWithTildeInPath] : @""];
[fDataLocationField setToolTip: location ? location : @""];
[fRevealDataButton setHidden: !location];
}
- (void) revealDataFile: (id) sender
{
Torrent * torrent = [fTorrents objectAtIndex: 0];
NSString * location = [torrent dataLocation];
if (!location)
return;
if ([NSApp isOnSnowLeopardOrBetter])
{
NSURL * file = [NSURL fileURLWithPath: location];
[[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs: [NSArray arrayWithObject: file]];
}
else
[[NSWorkspace sharedWorkspace] selectFile: location inFileViewerRootedAtPath: nil];
}
@end
@implementation InfoGeneralViewController (Private)
- (void) resetInfo
{
if ([fTorrents count] == 1)
{
Torrent * torrent = [fTorrents objectAtIndex: 0];
NSString * piecesString = ![torrent isMagnet] ? [NSString stringWithFormat: @"%d, %@", [torrent pieceCount],
[NSString stringForFileSize: [torrent pieceSize]]] : @"";
[fPiecesField setStringValue: piecesString];
NSString * hashString = [torrent hashString];
[fHashField setStringValue: hashString];
[fHashField setToolTip: hashString];
[fSecureField setStringValue: [torrent privateTorrent]
? NSLocalizedString(@"Private Torrent, PEX and DHT automatically disabled", "Inspector -> private torrent")
: NSLocalizedString(@"Public Torrent", "Inspector -> private torrent")];
NSString * commentString = [torrent comment];
[fCommentView setString: commentString];
NSString * creatorString = [torrent creator];
[fCreatorField setStringValue: creatorString];
[fDateCreatedField setObjectValue: [torrent dateCreated]];
}
else
{
[fPiecesField setStringValue: @""];
[fHashField setStringValue: @""];
[fHashField setToolTip: nil];
[fSecureField setStringValue: @""];
[fCommentView setString: @""];
[fCreatorField setStringValue: @""];
[fDateCreatedField setStringValue: @""];
[fDataLocationField setStringValue: @""];
[fDataLocationField setToolTip: nil];
[fRevealDataButton setHidden: YES];
}
}
@end

View File

@ -0,0 +1,54 @@
/******************************************************************************
* $Id$
*
* Copyright (c) 2010 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 <Cocoa/Cocoa.h>
@interface InfoOptionsViewController : NSViewController
{
NSArray * fTorrents;
IBOutlet NSPopUpButton * fPriorityPopUp, * fRatioPopUp;
IBOutlet NSButton * fUploadLimitCheck, * fDownloadLimitCheck, * fGlobalLimitCheck;
IBOutlet NSTextField * fUploadLimitField, * fDownloadLimitField, * fRatioLimitField,
* fUploadLimitLabel, * fDownloadLimitLabel, * fPeersConnectLabel,
* fPeersConnectField;
NSString * fInitialString;
}
- (void) setInfoForTorrents: (NSArray *) torrents;
- (void) updateOptions;
- (void) setUseSpeedLimit: (id) sender;
- (void) setSpeedLimit: (id) sender;
- (void) setUseGlobalSpeedLimit: (id) sender;
- (void) setRatioSetting: (id) sender;
- (void) setRatioLimit: (id) sender;
- (void) setPriority: (id) sender;
- (void) setPeersConnectLimit: (id) sender;
@end

View File

@ -0,0 +1,389 @@
/******************************************************************************
* $Id$
*
* Copyright (c) 2010 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 "InfoOptionsViewController.h"
#import "Torrent.h"
#define OPTION_POPUP_GLOBAL 0
#define OPTION_POPUP_NO_LIMIT 1
#define OPTION_POPUP_LIMIT 2
#define OPTION_POPUP_PRIORITY_HIGH 0
#define OPTION_POPUP_PRIORITY_NORMAL 1
#define OPTION_POPUP_PRIORITY_LOW 2
#define INVALID -99
@interface InfoOptionsViewController (Private)
- (void) resetInfo;
@end
@implementation InfoOptionsViewController
- (id) init
{
self = [super initWithNibName: @"InfoOptionsView" bundle: nil];
return self;
}
- (void) dealloc
{
[fTorrents release];
[super dealloc];
}
- (void) setInfoForTorrents: (NSArray *) torrents
{
if (fTorrents && [fTorrents isEqualToArray: torrents])
return;
[fTorrents release];
fTorrents = [torrents retain];
[self resetInfo];
}
- (void) updateOptions
{
if ([fTorrents count] == 0)
return;
//get bandwidth info
NSEnumerator * enumerator = [fTorrents objectEnumerator];
Torrent * torrent = [enumerator nextObject]; //first torrent
NSInteger uploadUseSpeedLimit = [torrent usesSpeedLimit: YES] ? NSOnState : NSOffState,
uploadSpeedLimit = [torrent speedLimit: YES],
downloadUseSpeedLimit = [torrent usesSpeedLimit: NO] ? NSOnState : NSOffState,
downloadSpeedLimit = [torrent speedLimit: NO],
globalUseSpeedLimit = [torrent usesGlobalSpeedLimit] ? NSOnState : NSOffState;
while ((torrent = [enumerator nextObject])
&& (uploadUseSpeedLimit != NSMixedState || uploadSpeedLimit != INVALID
|| downloadUseSpeedLimit != NSMixedState || downloadSpeedLimit != INVALID
|| globalUseSpeedLimit != NSMixedState))
{
if (uploadUseSpeedLimit != NSMixedState && uploadUseSpeedLimit != ([torrent usesSpeedLimit: YES] ? NSOnState : NSOffState))
uploadUseSpeedLimit = NSMixedState;
if (uploadSpeedLimit != INVALID && uploadSpeedLimit != [torrent speedLimit: YES])
uploadSpeedLimit = INVALID;
if (downloadUseSpeedLimit != NSMixedState && downloadUseSpeedLimit != ([torrent usesSpeedLimit: NO] ? NSOnState : NSOffState))
downloadUseSpeedLimit = NSMixedState;
if (downloadSpeedLimit != INVALID && downloadSpeedLimit != [torrent speedLimit: NO])
downloadSpeedLimit = INVALID;
if (globalUseSpeedLimit != NSMixedState && globalUseSpeedLimit != ([torrent usesGlobalSpeedLimit] ? NSOnState : NSOffState))
globalUseSpeedLimit = NSMixedState;
}
//set upload view
[fUploadLimitCheck setState: uploadUseSpeedLimit];
[fUploadLimitCheck setEnabled: YES];
[fUploadLimitLabel setEnabled: uploadUseSpeedLimit == NSOnState];
[fUploadLimitField setEnabled: uploadUseSpeedLimit == NSOnState];
if (uploadSpeedLimit != INVALID)
[fUploadLimitField setIntValue: uploadSpeedLimit];
else
[fUploadLimitField setStringValue: @""];
//set download view
[fDownloadLimitCheck setState: downloadUseSpeedLimit];
[fDownloadLimitCheck setEnabled: YES];
[fDownloadLimitLabel setEnabled: downloadUseSpeedLimit == NSOnState];
[fDownloadLimitField setEnabled: downloadUseSpeedLimit == NSOnState];
if (downloadSpeedLimit != INVALID)
[fDownloadLimitField setIntValue: downloadSpeedLimit];
else
[fDownloadLimitField setStringValue: @""];
//set global check
[fGlobalLimitCheck setState: globalUseSpeedLimit];
[fGlobalLimitCheck setEnabled: YES];
//get ratio info
enumerator = [fTorrents objectEnumerator];
torrent = [enumerator nextObject]; //first torrent
NSInteger checkRatio = [torrent ratioSetting];
CGFloat ratioLimit = [torrent ratioLimit];
while ((torrent = [enumerator nextObject]) && (checkRatio != INVALID || ratioLimit != INVALID))
{
if (checkRatio != INVALID && checkRatio != [torrent ratioSetting])
checkRatio = INVALID;
if (ratioLimit != INVALID && ratioLimit != [torrent ratioLimit])
ratioLimit = INVALID;
}
//set ratio view
NSInteger index;
if (checkRatio == TR_RATIOLIMIT_SINGLE)
index = OPTION_POPUP_LIMIT;
else if (checkRatio == TR_RATIOLIMIT_UNLIMITED)
index = OPTION_POPUP_NO_LIMIT;
else if (checkRatio == TR_RATIOLIMIT_GLOBAL)
index = OPTION_POPUP_GLOBAL;
else
index = -1;
[fRatioPopUp selectItemAtIndex: index];
[fRatioPopUp setEnabled: YES];
[fRatioLimitField setHidden: checkRatio != TR_RATIOLIMIT_SINGLE];
if (ratioLimit != INVALID)
[fRatioLimitField setFloatValue: ratioLimit];
else
[fRatioLimitField setStringValue: @""];
//get priority info
enumerator = [fTorrents objectEnumerator];
torrent = [enumerator nextObject]; //first torrent
NSInteger priority = [torrent priority];
while ((torrent = [enumerator nextObject]) && priority != INVALID)
{
if (priority != INVALID && priority != [torrent priority])
priority = INVALID;
}
//set priority view
if (priority == TR_PRI_HIGH)
index = OPTION_POPUP_PRIORITY_HIGH;
else if (priority == TR_PRI_NORMAL)
index = OPTION_POPUP_PRIORITY_NORMAL;
else if (priority == TR_PRI_LOW)
index = OPTION_POPUP_PRIORITY_LOW;
else
index = -1;
[fPriorityPopUp selectItemAtIndex: index];
[fPriorityPopUp setEnabled: YES];
//get peer info
enumerator = [fTorrents objectEnumerator];
torrent = [enumerator nextObject]; //first torrent
NSInteger maxPeers = [torrent maxPeerConnect];
while ((torrent = [enumerator nextObject]))
{
if (maxPeers != [torrent maxPeerConnect])
{
maxPeers = INVALID;
break;
}
}
//set peer view
[fPeersConnectField setEnabled: YES];
[fPeersConnectLabel setEnabled: YES];
if (maxPeers != INVALID)
[fPeersConnectField setIntValue: maxPeers];
else
[fPeersConnectField setStringValue: @""];
}
- (void) setUseSpeedLimit: (id) sender
{
const BOOL upload = sender == fUploadLimitCheck;
if ([sender state] == NSMixedState)
[sender setState: NSOnState];
const BOOL limit = [sender state] == NSOnState;
for (Torrent * torrent in fTorrents)
[torrent setUseSpeedLimit: limit upload: upload];
NSTextField * field = upload ? fUploadLimitField : fDownloadLimitField;
[field setEnabled: limit];
if (limit)
{
[field selectText: self];
[[[self view] window] makeKeyAndOrderFront: self];
}
NSTextField * label = upload ? fUploadLimitLabel : fDownloadLimitLabel;
[label setEnabled: limit];
}
- (void) setUseGlobalSpeedLimit: (id) sender
{
if ([sender state] == NSMixedState)
[sender setState: NSOnState];
const BOOL limit = [sender state] == NSOnState;
for (Torrent * torrent in fTorrents)
[torrent setUseGlobalSpeedLimit: limit];
}
- (void) setSpeedLimit: (id) sender
{
const BOOL upload = sender == fUploadLimitField;
const NSInteger limit = [sender intValue];
for (Torrent * torrent in fTorrents)
[torrent setSpeedLimit: limit upload: upload];
}
- (void) setRatioSetting: (id) sender
{
NSInteger setting;
bool single = NO;
switch ([sender indexOfSelectedItem])
{
case OPTION_POPUP_LIMIT:
setting = TR_RATIOLIMIT_SINGLE;
single = YES;
break;
case OPTION_POPUP_NO_LIMIT:
setting = TR_RATIOLIMIT_UNLIMITED;
break;
case OPTION_POPUP_GLOBAL:
setting = TR_RATIOLIMIT_GLOBAL;
break;
default:
NSAssert1(NO, @"Unknown option selected in ratio popup: %d", [sender indexOfSelectedItem]);
return;
}
for (Torrent * torrent in fTorrents)
[torrent setRatioSetting: setting];
[fRatioLimitField setHidden: !single];
if (single)
{
[fRatioLimitField selectText: self];
[[[self view] window] makeKeyAndOrderFront: self];
}
}
- (void) setRatioLimit: (id) sender
{
CGFloat limit = [sender floatValue];
for (Torrent * torrent in fTorrents)
[torrent setRatioLimit: limit];
}
- (void) setPriority: (id) sender
{
tr_priority_t priority;
switch ([sender indexOfSelectedItem])
{
case OPTION_POPUP_PRIORITY_HIGH:
priority = TR_PRI_HIGH;
break;
case OPTION_POPUP_PRIORITY_NORMAL:
priority = TR_PRI_NORMAL;
break;
case OPTION_POPUP_PRIORITY_LOW:
priority = TR_PRI_LOW;
break;
default:
NSAssert1(NO, @"Unknown option selected in priority popup: %d", [sender indexOfSelectedItem]);
return;
}
for (Torrent * torrent in fTorrents)
[torrent setPriority: priority];
[[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil];
}
- (void) setPeersConnectLimit: (id) sender
{
NSInteger limit = [sender intValue];
for (Torrent * torrent in fTorrents)
[torrent setMaxPeerConnect: limit];
}
- (BOOL) control: (NSControl *) control textShouldBeginEditing: (NSText *) fieldEditor
{
[fInitialString release];
fInitialString = [[control stringValue] retain];
return YES;
}
- (BOOL) control: (NSControl *) control didFailToFormatString: (NSString *) string errorDescription: (NSString *) error
{
NSBeep();
if (fInitialString)
{
[control setStringValue: fInitialString];
[fInitialString release];
fInitialString = nil;
}
return NO;
}
@end
@implementation InfoOptionsViewController (Private)
- (void) resetInfo
{
if ([fTorrents count] == 0)
{
[fUploadLimitCheck setEnabled: NO];
[fUploadLimitCheck setState: NSOffState];
[fUploadLimitField setEnabled: NO];
[fUploadLimitLabel setEnabled: NO];
[fUploadLimitField setStringValue: @""];
[fDownloadLimitCheck setEnabled: NO];
[fDownloadLimitCheck setState: NSOffState];
[fDownloadLimitField setEnabled: NO];
[fDownloadLimitLabel setEnabled: NO];
[fDownloadLimitField setStringValue: @""];
[fGlobalLimitCheck setEnabled: NO];
[fGlobalLimitCheck setState: NSOffState];
[fPriorityPopUp setEnabled: NO];
[fPriorityPopUp selectItemAtIndex: -1];
[fRatioPopUp setEnabled: NO];
[fRatioPopUp selectItemAtIndex: -1];
[fRatioLimitField setHidden: YES];
[fRatioLimitField setStringValue: @""];
[fPeersConnectField setEnabled: NO];
[fPeersConnectField setStringValue: @""];
[fPeersConnectLabel setEnabled: NO];
}
else
[self updateOptions];
}
@end

1866
macosx/InfoPeersView.xib Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,46 @@
/******************************************************************************
* $Id$
*
* Copyright (c) 2010 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 <Cocoa/Cocoa.h>
@interface InfoPeersViewController : NSViewController
{
NSArray * fTorrents;
NSMutableArray * fPeers, * fWebSeeds;
IBOutlet NSTableView * fPeerTable, * fWebSeedTable;
CGFloat fWebSeedTableHeight, fSpaceBetweenWebSeedAndPeer;
NSViewAnimation * fWebSeedTableAnimation;
IBOutlet NSTextField * fConnectedPeersField;
}
- (void) setInfoForTorrents: (NSArray *) torrents;
- (void) updateInfo;
- (void) clearPeers;
@end

View File

@ -0,0 +1,552 @@
/******************************************************************************
* $Id$
*
* Copyright (c) 2010 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 "InfoPeersViewController.h"
#import "NSApplicationAdditions.h"
#import "NSStringAdditions.h"
#import "PeerProgressIndicatorCell.h"
#import "Torrent.h"
@interface InfoPeersViewController (Private)
- (void) resetInfo;
- (void) setWebSeedTableHidden: (BOOL) hide animate: (BOOL) animate;
- (NSArray *) peerSortDescriptors;
@end
@implementation InfoPeersViewController
- (id) init
{
self = [super initWithNibName: @"InfoPeersView" bundle: nil];
return self;
}
- (void) awakeFromNib
{
//initially sort peer table by IP
if ([[fPeerTable sortDescriptors] count] == 0)
[fPeerTable setSortDescriptors: [NSArray arrayWithObject: [[fPeerTable tableColumnWithIdentifier: @"IP"]
sortDescriptorPrototype]]];
//initially sort webseed table by address
if ([[fWebSeedTable sortDescriptors] count] == 0)
[fWebSeedTable setSortDescriptors: [NSArray arrayWithObject: [[fWebSeedTable tableColumnWithIdentifier: @"Address"]
sortDescriptorPrototype]]];
#warning set localized table headers
//set table header tool tips
[[fPeerTable tableColumnWithIdentifier: @"Encryption"] setHeaderToolTip: NSLocalizedString(@"Encrypted Connection",
"inspector -> peer table -> header tool tip")];
[[fPeerTable tableColumnWithIdentifier: @"Progress"] setHeaderToolTip: NSLocalizedString(@"Available",
"inspector -> peer table -> header tool tip")];
[[fPeerTable tableColumnWithIdentifier: @"UL To"] setHeaderToolTip: NSLocalizedString(@"Uploading To Peer",
"inspector -> peer table -> header tool tip")];
[[fPeerTable tableColumnWithIdentifier: @"DL From"] setHeaderToolTip: NSLocalizedString(@"Downloading From Peer",
"inspector -> peer table -> header tool tip")];
[[fWebSeedTable tableColumnWithIdentifier: @"DL From"] setHeaderToolTip: NSLocalizedString(@"Downloading From Web Seed",
"inspector -> web seed table -> header tool tip")];
//prepare for animating peer table and web seed table
NSRect webSeedTableFrame = [[fWebSeedTable enclosingScrollView] frame];
fWebSeedTableHeight = webSeedTableFrame.size.height;
fSpaceBetweenWebSeedAndPeer = webSeedTableFrame.origin.y - NSMaxY([[fPeerTable enclosingScrollView] frame]);
[self setWebSeedTableHidden: YES animate: NO];
}
- (void) dealloc
{
[fTorrents release];
[fPeers release];
[fWebSeeds release];
[fWebSeedTableAnimation release];
[super dealloc];
}
#warning subclass?
- (void) setInfoForTorrents: (NSArray *) torrents
{
if (fTorrents && [fTorrents isEqualToArray: torrents])
return;
[fTorrents release];
fTorrents = [torrents retain];
[self resetInfo];
}
- (void) updateInfo
{
if ([fTorrents count] == 0)
return;
if (!fPeers)
fPeers = [[NSMutableArray alloc] init];
else
[fPeers removeAllObjects];
if (!fWebSeeds)
fWebSeeds = [[NSMutableArray alloc] init];
else
[fWebSeeds removeAllObjects];
NSUInteger known = 0, connected = 0, tracker = 0, incoming = 0, cache = 0, pex = 0, dht = 0, ltep = 0,
toUs = 0, fromUs = 0;
BOOL anyActive = false;
for (Torrent * torrent in fTorrents)
{
if ([torrent webSeedCount] > 0)
[fWebSeeds addObjectsFromArray: [torrent webSeeds]];
known += [torrent totalPeersKnown];
if ([torrent isActive])
{
anyActive = YES;
[fPeers addObjectsFromArray: [torrent peers]];
const NSUInteger connectedThis = [torrent totalPeersConnected];
if (connectedThis > 0)
{
connected += [torrent totalPeersConnected];
tracker += [torrent totalPeersTracker];
incoming += [torrent totalPeersIncoming];
cache += [torrent totalPeersCache];
pex += [torrent totalPeersPex];
dht += [torrent totalPeersDHT];
ltep += [torrent totalPeersLTEP];
toUs += [torrent peersSendingToUs];
fromUs += [torrent peersGettingFromUs];
}
}
}
[fPeers sortUsingDescriptors: [self peerSortDescriptors]];
[fPeerTable reloadData];
[fWebSeeds sortUsingDescriptors: [fWebSeedTable sortDescriptors]];
[fWebSeedTable reloadData];
NSString * knownString = [NSString stringWithFormat: NSLocalizedString(@"%d known", "Inspector -> Peers tab -> peers"), known];
if (anyActive)
{
NSString * connectedText = [NSString stringWithFormat: NSLocalizedString(@"%d Connected", "Inspector -> Peers tab -> peers"),
connected];
if (connected > 0)
{
NSMutableArray * fromComponents = [NSMutableArray arrayWithCapacity: 6];
if (tracker > 0)
[fromComponents addObject: [NSString stringWithFormat:
NSLocalizedString(@"%d tracker", "Inspector -> Peers tab -> peers"), tracker]];
if (incoming > 0)
[fromComponents addObject: [NSString stringWithFormat:
NSLocalizedString(@"%d incoming", "Inspector -> Peers tab -> peers"), incoming]];
if (cache > 0)
[fromComponents addObject: [NSString stringWithFormat:
NSLocalizedString(@"%d cache", "Inspector -> Peers tab -> peers"), cache]];
if (pex > 0)
[fromComponents addObject: [NSString stringWithFormat:
NSLocalizedString(@"%d PEX", "Inspector -> Peers tab -> peers"), pex]];
if (dht > 0)
[fromComponents addObject: [NSString stringWithFormat:
NSLocalizedString(@"%d DHT", "Inspector -> Peers tab -> peers"), dht]];
if (ltep > 0)
[fromComponents addObject: [NSString stringWithFormat:
NSLocalizedString(@"%d LTEP", "Inspector -> Peers tab -> peers"), ltep]];
NSMutableArray * upDownComponents = [NSMutableArray arrayWithCapacity: 3];
if (toUs > 0)
[upDownComponents addObject: [NSString stringWithFormat:
NSLocalizedString(@"DL from %d", "Inspector -> Peers tab -> peers"), toUs]];
if (fromUs > 0)
[upDownComponents addObject: [NSString stringWithFormat:
NSLocalizedString(@"UL to %d", "Inspector -> Peers tab -> peers"), fromUs]];
[upDownComponents addObject: knownString];
connectedText = [connectedText stringByAppendingFormat: @": %@\n%@", [fromComponents componentsJoinedByString: @", "],
[upDownComponents componentsJoinedByString: @", "]];
}
else
connectedText = [connectedText stringByAppendingFormat: @"\n%@", knownString];
[fConnectedPeersField setStringValue: connectedText];
}
else
{
NSString * activeString;
if ([fTorrents count] == 1)
activeString = NSLocalizedString(@"Transfer Not Active", "Inspector -> Peers tab -> peers");
else
activeString = NSLocalizedString(@"Transfers Not Active", "Inspector -> Peers tab -> peers");
NSString * connectedText = [activeString stringByAppendingFormat: @"\n%@", knownString];
[fConnectedPeersField setStringValue: connectedText];
}
}
- (void) clearPeers
{
//if in the middle of animating, just stop and resize immediately
if (fWebSeedTableAnimation)
[self setWebSeedTableHidden: !fWebSeeds animate: NO];
[fPeers release];
fPeers = nil;
[fWebSeeds release];
fWebSeeds = nil;
}
- (NSInteger) numberOfRowsInTableView: (NSTableView *) tableView
{
if (tableView == fWebSeedTable)
return fWebSeeds ? [fWebSeeds count] : 0;
else
return fPeers ? [fPeers count] : 0;
}
- (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) column row: (NSInteger) row
{
if (tableView == fWebSeedTable)
{
NSString * ident = [column identifier];
NSDictionary * webSeed = [fWebSeeds objectAtIndex: row];
if ([ident isEqualToString: @"DL From"])
{
NSNumber * rate;
return (rate = [webSeed objectForKey: @"DL From Rate"]) ? [NSString stringForSpeedAbbrev: [rate floatValue]] : @"";
}
else
return [webSeed objectForKey: @"Address"];
}
else
{
NSString * ident = [column identifier];
NSDictionary * peer = [fPeers objectAtIndex: row];
if ([ident isEqualToString: @"Encryption"])
return [[peer objectForKey: @"Encryption"] boolValue] ? [NSImage imageNamed: @"Lock.png"] : nil;
else if ([ident isEqualToString: @"Client"])
return [peer objectForKey: @"Client"];
else if ([ident isEqualToString: @"Progress"])
return [peer objectForKey: @"Progress"];
else if ([ident isEqualToString: @"UL To"])
{
NSNumber * rate;
return (rate = [peer objectForKey: @"UL To Rate"]) ? [NSString stringForSpeedAbbrev: [rate floatValue]] : @"";
}
else if ([ident isEqualToString: @"DL From"])
{
NSNumber * rate;
return (rate = [peer objectForKey: @"DL From Rate"]) ? [NSString stringForSpeedAbbrev: [rate floatValue]] : @"";
}
else
return [peer objectForKey: @"IP"];
}
}
- (void) tableView: (NSTableView *) tableView willDisplayCell: (id) cell forTableColumn: (NSTableColumn *) tableColumn
row: (NSInteger) row
{
if (tableView == fPeerTable)
{
NSString * ident = [tableColumn identifier];
if ([ident isEqualToString: @"Progress"])
{
NSDictionary * peer = [fPeers objectAtIndex: row];
[(PeerProgressIndicatorCell *)cell setSeed: [[peer objectForKey: @"Seed"] boolValue]];
}
}
}
- (void) tableView: (NSTableView *) tableView didClickTableColumn: (NSTableColumn *) tableColumn
{
if (tableView == fWebSeedTable)
{
if (fWebSeeds)
{
[fWebSeeds sortUsingDescriptors: [fWebSeedTable sortDescriptors]];
[tableView reloadData];
}
}
else
{
if (fPeers)
{
[fPeers sortUsingDescriptors: [self peerSortDescriptors]];
[tableView reloadData];
}
}
}
- (BOOL) tableView: (NSTableView *) tableView shouldSelectRow: (NSInteger) row
{
return NO;
}
- (NSString *) tableView: (NSTableView *) tableView toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect
tableColumn: (NSTableColumn *) column row: (NSInteger) row mouseLocation: (NSPoint) mouseLocation
{
if (tableView == fPeerTable)
{
const BOOL multiple = [fTorrents count] > 1;
NSDictionary * peer = [fPeers objectAtIndex: row];
NSMutableArray * components = [NSMutableArray arrayWithCapacity: multiple ? 6 : 5];
if (multiple)
[components addObject: [peer objectForKey: @"Name"]];
const CGFloat progress = [[peer objectForKey: @"Progress"] floatValue];
NSString * progressString = [NSString localizedStringWithFormat: NSLocalizedString(@"Progress: %.1f%%",
"Inspector -> Peers tab -> table row tooltip"), progress * 100.0];
if (progress < 1.0 && [[peer objectForKey: @"Seed"] boolValue])
progressString = [progressString stringByAppendingFormat: @" (%@)", NSLocalizedString(@"Partial Seed",
"Inspector -> Peers tab -> table row tooltip")];
[components addObject: progressString];
if ([[peer objectForKey: @"Encryption"] boolValue])
[components addObject: NSLocalizedString(@"Encrypted Connection", "Inspector -> Peers tab -> table row tooltip")];
NSString * portString;
NSInteger port;
if ((port = [[peer objectForKey: @"Port"] intValue]) > 0)
portString = [NSString stringWithFormat: @"%d", port];
else
portString = NSLocalizedString(@"N/A", "Inspector -> Peers tab -> table row tooltip");
[components addObject: [NSString stringWithFormat: @"%@: %@", NSLocalizedString(@"Port",
"Inspector -> Peers tab -> table row tooltip"), portString]];
const NSInteger peerFrom = [[peer objectForKey: @"From"] integerValue];
switch (peerFrom)
{
case TR_PEER_FROM_TRACKER:
[components addObject: NSLocalizedString(@"From: tracker", "Inspector -> Peers tab -> table row tooltip")];
break;
case TR_PEER_FROM_INCOMING:
[components addObject: NSLocalizedString(@"From: incoming connection", "Inspector -> Peers tab -> table row tooltip")];
break;
case TR_PEER_FROM_RESUME:
[components addObject: NSLocalizedString(@"From: cache", "Inspector -> Peers tab -> table row tooltip")];
break;
case TR_PEER_FROM_PEX:
[components addObject: NSLocalizedString(@"From: peer exchange", "Inspector -> Peers tab -> table row tooltip")];
break;
case TR_PEER_FROM_DHT:
[components addObject: NSLocalizedString(@"From: distributed hash table", "Inspector -> Peers tab -> table row tooltip")];
break;
case TR_PEER_FROM_LTEP:
[components addObject: NSLocalizedString(@"From: libtorrent extension protocol handshake",
"Inspector -> Peers tab -> table row tooltip")];
break;
default:
NSAssert1(NO, @"Peer from unknown source: %d", peerFrom);
}
//determing status strings from flags
NSMutableArray * statusArray = [NSMutableArray arrayWithCapacity: 6];
NSString * flags = [peer objectForKey: @"Flags"];
if ([flags rangeOfString: @"D"].location != NSNotFound)
[statusArray addObject: NSLocalizedString(@"Currently downloading (interested and not choked)",
"Inspector -> peer -> status")];
if ([flags rangeOfString: @"d"].location != NSNotFound)
[statusArray addObject: NSLocalizedString(@"You want to download, but peer does not want to send (interested and choked)",
"Inspector -> peer -> status")];
if ([flags rangeOfString: @"U"].location != NSNotFound)
[statusArray addObject: NSLocalizedString(@"Currently uploading (interested and not choked)",
"Inspector -> peer -> status")];
if ([flags rangeOfString: @"u"].location != NSNotFound)
[statusArray addObject: NSLocalizedString(@"Peer wants you to upload, but you do not want to (interested and choked)",
"Inspector -> peer -> status")];
if ([flags rangeOfString: @"K"].location != NSNotFound)
[statusArray addObject: NSLocalizedString(@"Peer is unchoking you, but you are not interested",
"Inspector -> peer -> status")];
if ([flags rangeOfString: @"?"].location != NSNotFound)
[statusArray addObject: NSLocalizedString(@"You unchoked the peer, but the peer is not interested",
"Inspector -> peer -> status")];
if ([statusArray count] > 0)
{
NSString * statusStrings = [statusArray componentsJoinedByString: @"\n\n"];
[components addObject: [@"\n" stringByAppendingString: statusStrings]];
}
return [components componentsJoinedByString: @"\n"];
}
else
{
if ([fTorrents count] > 1)
return [[fWebSeeds objectAtIndex: row] objectForKey: @"Name"];
}
return nil;
}
- (void) animationDidEnd: (NSAnimation *) animation
{
if (animation == fWebSeedTableAnimation)
{
[fWebSeedTableAnimation release];
fWebSeedTableAnimation = nil;
}
}
@end
@implementation InfoPeersViewController (Private)
- (void) resetInfo
{
BOOL hasWebSeeds = NO;
if ([fTorrents count] == 0)
{
[fPeers release];
fPeers = nil;
[fPeerTable reloadData];
#warning should be reset when != to 1?
[fConnectedPeersField setStringValue: @""];
}
else
{
for (Torrent * torrent in fTorrents)
if ([torrent webSeedCount] > 0)
{
hasWebSeeds = YES;
break;
}
}
if (!hasWebSeeds)
{
[fWebSeeds release];
fWebSeeds = nil;
[fWebSeedTable reloadData];
}
[self setWebSeedTableHidden: !hasWebSeeds animate: YES];
}
- (void) setWebSeedTableHidden: (BOOL) hide animate: (BOOL) animate
{
#warning needs to be verified
if (animate && (![[self view] window] || ![[[self view] window] isVisible]))
animate = NO;
if (fWebSeedTableAnimation)
{
[fWebSeedTableAnimation stopAnimation];
[fWebSeedTableAnimation release];
fWebSeedTableAnimation = nil;
}
NSRect webSeedFrame = [[fWebSeedTable enclosingScrollView] frame];
NSRect peerFrame = [[fPeerTable enclosingScrollView] frame];
if (hide)
{
CGFloat webSeedFrameMaxY = NSMaxY(webSeedFrame);
webSeedFrame.size.height = 0;
webSeedFrame.origin.y = webSeedFrameMaxY;
peerFrame.size.height = webSeedFrameMaxY - peerFrame.origin.y;
}
else
{
webSeedFrame.origin.y -= fWebSeedTableHeight - webSeedFrame.size.height;
webSeedFrame.size.height = fWebSeedTableHeight;
peerFrame.size.height = (webSeedFrame.origin.y - fSpaceBetweenWebSeedAndPeer) - peerFrame.origin.y;
}
[[fWebSeedTable enclosingScrollView] setHidden: NO]; //this is needed for some reason
//actually resize tables
if (animate)
{
NSDictionary * webSeedDict = [NSDictionary dictionaryWithObjectsAndKeys:
[fWebSeedTable enclosingScrollView], NSViewAnimationTargetKey,
[NSValue valueWithRect: [[fWebSeedTable enclosingScrollView] frame]], NSViewAnimationStartFrameKey,
[NSValue valueWithRect: webSeedFrame], NSViewAnimationEndFrameKey, nil],
* peerDict = [NSDictionary dictionaryWithObjectsAndKeys:
[fPeerTable enclosingScrollView], NSViewAnimationTargetKey,
[NSValue valueWithRect: [[fPeerTable enclosingScrollView] frame]], NSViewAnimationStartFrameKey,
[NSValue valueWithRect: peerFrame], NSViewAnimationEndFrameKey, nil];
fWebSeedTableAnimation = [[NSViewAnimation alloc] initWithViewAnimations:
[NSArray arrayWithObjects: webSeedDict, peerDict, nil]];
[fWebSeedTableAnimation setDuration: 0.125];
[fWebSeedTableAnimation setAnimationBlockingMode: NSAnimationNonblocking];
[fWebSeedTableAnimation setDelegate: self];
[fWebSeedTableAnimation startAnimation];
}
else
{
[[fWebSeedTable enclosingScrollView] setFrame: webSeedFrame];
[[fPeerTable enclosingScrollView] setFrame: peerFrame];
}
}
- (NSArray *) peerSortDescriptors
{
NSMutableArray * descriptors = [NSMutableArray arrayWithCapacity: 2];
NSArray * oldDescriptors = [fPeerTable sortDescriptors];
BOOL useSecond = YES, asc = YES;
if ([oldDescriptors count] > 0)
{
NSSortDescriptor * descriptor = [oldDescriptors objectAtIndex: 0];
[descriptors addObject: descriptor];
if ((useSecond = ![[descriptor key] isEqualToString: @"IP"]))
asc = [descriptor ascending];
}
//sort by IP after primary sort
if (useSecond)
{
#warning when 10.6-only, replate with sortDescriptorWithKey:ascending:selector:
NSSortDescriptor * secondDescriptor = [[NSSortDescriptor alloc] initWithKey: @"IP" ascending: asc
selector: @selector(compareNumeric:)];
[descriptors addObject: secondDescriptor];
[secondDescriptor release];
}
return descriptors;
}
@end

1055
macosx/InfoTrackersView.xib Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,50 @@
/******************************************************************************
* $Id$
*
* Copyright (c) 2010 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 <Cocoa/Cocoa.h>
@class Torrent;
@class TrackerTableView;
@class TrackerCell;
@interface InfoTrackersViewController : NSViewController
{
NSArray * fTorrents;
NSMutableArray * fTrackers;
IBOutlet TrackerTableView * fTrackerTable;
TrackerCell * fTrackerCell;
IBOutlet NSSegmentedControl * fTrackerAddRemoveControl;
}
- (void) setInfoForTorrents: (NSArray *) torrents;
- (void) updateInfo;
- (void) clearTrackers;
- (void) addRemoveTracker: (id) sender;
@end

View File

@ -0,0 +1,356 @@
/******************************************************************************
* $Id$
*
* Copyright (c) 2010 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 "InfoTrackersViewController.h"
#import "NSApplicationAdditions.h"
#import "Torrent.h"
#import "TrackerCell.h"
#import "TrackerNode.h"
#import "TrackerTableView.h"
#define TRACKER_GROUP_SEPARATOR_HEIGHT 14.0
#define TRACKER_ADD_TAG 0
#define TRACKER_REMOVE_TAG 1
@interface InfoTrackersViewController (Private)
- (void) resetInfo;
- (void) addTrackers;
- (void) removeTrackers;
@end
@implementation InfoTrackersViewController
- (id) init
{
if ((self = [super initWithNibName: @"InfoTrackersView" bundle: nil]))
{
fTrackerCell = [[TrackerCell alloc] init];
}
return self;
}
- (void) dealloc
{
[fTorrents release];
[fTrackers release];
[fTrackerCell release];
[super dealloc];
}
- (void) setInfoForTorrents: (NSArray *) torrents
{
if (fTorrents && [fTorrents isEqualToArray: torrents])
return;
[fTorrents release];
fTorrents = [torrents retain];
[self resetInfo];
}
- (void) updateInfo
{
if ([fTorrents count] == 0)
return;
//get updated tracker stats
if ([fTrackerTable editedRow] == -1)
{
[fTrackers release];
if ([fTorrents count] == 1)
fTrackers = [[[fTorrents objectAtIndex: 0] allTrackerStats] retain];
else
{
fTrackers = [[NSMutableArray alloc] init];
for (Torrent * torrent in fTorrents)
[fTrackers addObjectsFromArray: [torrent allTrackerStats]];
}
[fTrackerTable setTrackers: fTrackers];
[fTrackerTable reloadData];
}
else
{
NSAssert1([fTorrents count] == 1, @"Attempting to add tracker with %d transfers selected", [fTorrents count]);
if ([NSApp isOnSnowLeopardOrBetter])
{
NSIndexSet * addedIndexes = [NSIndexSet indexSetWithIndexesInRange: NSMakeRange([fTrackers count]-2, 2)];
NSArray * tierAndTrackerBeingAdded = [fTrackers objectsAtIndexes: addedIndexes];
[fTrackers release];
fTrackers = [[[fTorrents objectAtIndex: 0] allTrackerStats] retain];
[fTrackers addObjectsFromArray: tierAndTrackerBeingAdded];
[fTrackerTable setTrackers: fTrackers];
NSIndexSet * updateIndexes = [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [fTrackers count]-2)],
* columnIndexes = [NSIndexSet indexSetWithIndexesInRange: NSMakeRange(0, [[fTrackerTable tableColumns] count])];
[fTrackerTable reloadDataForRowIndexes: updateIndexes columnIndexes: columnIndexes];
}
}
}
- (void) clearTrackers
{
[fTrackers release];
fTrackers = nil;
}
- (NSInteger) numberOfRowsInTableView: (NSTableView *) tableView
{
return fTrackers ? [fTrackers count] : 0;
}
- (id) tableView: (NSTableView *) tableView objectValueForTableColumn: (NSTableColumn *) column row: (NSInteger) row
{
id item = [fTrackers objectAtIndex: row];
if ([item isKindOfClass: [NSDictionary class]])
{
const NSInteger tier = [[item objectForKey: @"Tier"] integerValue];
NSString * tierString = tier == -1 ? NSLocalizedString(@"New Tier", "Inspector -> tracker table")
: [NSString stringWithFormat: NSLocalizedString(@"Tier %d", "Inspector -> tracker table"), tier];
if ([fTorrents count] > 1)
tierString = [tierString stringByAppendingFormat: @" - %@", [item objectForKey: @"Name"]];
return tierString;
}
else
return item; //TrackerNode or NSString
}
- (NSCell *) tableView: (NSTableView *) tableView dataCellForTableColumn: (NSTableColumn *) tableColumn row: (NSInteger) row
{
const BOOL tracker = [[fTrackers objectAtIndex: row] isKindOfClass: [TrackerNode class]];
return tracker ? fTrackerCell : [tableColumn dataCellForRow: row];
}
- (CGFloat) tableView: (NSTableView *) tableView heightOfRow: (NSInteger) row
{
//check for NSDictionay instead of TrackerNode because of display issue when adding a row
if ([[fTrackers objectAtIndex: row] isKindOfClass: [NSDictionary class]])
return TRACKER_GROUP_SEPARATOR_HEIGHT;
else
return [tableView rowHeight];
}
- (void) tableViewSelectionDidChange: (NSNotification *) notification
{
[fTrackerAddRemoveControl setEnabled: [fTrackerTable numberOfSelectedRows] > 0 forSegment: TRACKER_REMOVE_TAG];
}
- (BOOL) tableView: (NSTableView *) tableView isGroupRow: (NSInteger) row
{
return ![[fTrackers objectAtIndex: row] isKindOfClass: [TrackerNode class]] && [tableView editedRow] != row;
}
- (NSString *) tableView: (NSTableView *) tableView toolTipForCell: (NSCell *) cell rect: (NSRectPointer) rect
tableColumn: (NSTableColumn *) column row: (NSInteger) row mouseLocation: (NSPoint) mouseLocation
{
id node = [fTrackers objectAtIndex: row];
if ([node isKindOfClass: [TrackerNode class]])
return [(TrackerNode *)node fullAnnounceAddress];
else
return nil;
}
- (void) tableView: (NSTableView *) tableView setObjectValue: (id) object forTableColumn: (NSTableColumn *) tableColumn
row: (NSInteger) row
{
Torrent * torrent= [fTorrents objectAtIndex: 0];
BOOL added = NO;
for (NSString * tracker in [object componentsSeparatedByString: @"\n"])
if ([torrent addTrackerToNewTier: tracker])
added = YES;
if (!added)
NSBeep();
//reset table with either new or old value
[fTrackers release];
fTrackers = [[torrent allTrackerStats] retain];
[fTrackerTable setTrackers: fTrackers];
[fTrackerTable reloadData];
[fTrackerTable deselectAll: self];
[[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil]; //incase sort by tracker
}
- (void) addRemoveTracker: (id) sender
{
//don't allow add/remove when currently adding - it leads to weird results
if ([fTrackerTable editedRow] != -1)
return;
if ([[sender cell] tagForSegment: [sender selectedSegment]] == TRACKER_REMOVE_TAG)
[self removeTrackers];
else
[self addTrackers];
}
@end
@implementation InfoTrackersViewController (Private)
- (void) resetInfo
{
const NSUInteger numberSelected = [fTorrents count];
if (numberSelected != 1)
{
if (numberSelected == 0)
{
[fTrackers release];
fTrackers = nil;
[fTrackerTable setTrackers: nil];
[fTrackerTable reloadData];
}
[fTrackerTable setTorrent: nil];
[fTrackerAddRemoveControl setEnabled: NO forSegment: TRACKER_ADD_TAG];
[fTrackerAddRemoveControl setEnabled: NO forSegment: TRACKER_REMOVE_TAG];
}
else
{
[fTrackerTable setTorrent: [fTorrents objectAtIndex: 0]];
[fTrackerAddRemoveControl setEnabled: YES forSegment: TRACKER_ADD_TAG];
[fTrackerAddRemoveControl setEnabled: NO forSegment: TRACKER_REMOVE_TAG];
}
[fTrackerTable deselectAll: self];
}
#warning doesn't like blank addresses
- (void) addTrackers
{
[[[self view] window] makeKeyWindow];
NSAssert1([fTorrents count] == 1, @"Attempting to add tracker with %d transfers selected", [fTorrents count]);
[fTrackers addObject: [NSDictionary dictionaryWithObject: [NSNumber numberWithInteger: -1] forKey: @"Tier"]];
[fTrackers addObject: @""];
[fTrackerTable setTrackers: fTrackers];
[fTrackerTable reloadData];
[fTrackerTable selectRowIndexes: [NSIndexSet indexSetWithIndex: [fTrackers count]-1] byExtendingSelection: NO];
[fTrackerTable editColumn: [fTrackerTable columnWithIdentifier: @"Tracker"] row: [fTrackers count]-1 withEvent: nil select: YES];
}
- (void) removeTrackers
{
NSMutableDictionary * removeIdentifiers = [NSMutableDictionary dictionaryWithCapacity: [fTorrents count]];
NSUInteger removeCount = 0;
NSIndexSet * selectedIndexes = [fTrackerTable selectedRowIndexes];
BOOL groupSelected = NO;
for (NSUInteger i = 0; i < [fTrackers count]; ++i)
{
id object = [fTrackers objectAtIndex: i];
if ([object isKindOfClass: [TrackerNode class]])
{
if (groupSelected || [selectedIndexes containsIndex: i])
{
Torrent * torrent = [(TrackerNode *)object torrent];
NSMutableIndexSet * removeIndexSet;
if (!(removeIndexSet = [removeIdentifiers objectForKey: torrent]))
{
removeIndexSet = [NSMutableIndexSet indexSet];
[removeIdentifiers setObject: removeIndexSet forKey: torrent];
}
[removeIndexSet addIndex: [(TrackerNode *)object identifier]];
++removeCount;
}
}
else
{
groupSelected = [selectedIndexes containsIndex: i];
if (!groupSelected && i > [selectedIndexes lastIndex])
break;
}
}
NSAssert(removeCount > 0, @"Trying to remove no trackers.");
if ([[NSUserDefaults standardUserDefaults] boolForKey: @"WarningRemoveTrackers"])
{
NSAlert * alert = [[NSAlert alloc] init];
if (removeCount > 1)
{
[alert setMessageText: [NSString stringWithFormat: NSLocalizedString(@"Are you sure you want to remove %d trackers?",
"Remove trackers alert -> title"), removeCount]];
[alert setInformativeText: NSLocalizedString(@"Once removed, Transmission will no longer attempt to contact them."
" This cannot be undone.", "Remove trackers alert -> message")];
}
else
{
[alert setMessageText: NSLocalizedString(@"Are you sure you want to remove this tracker?", "Remove trackers alert -> title")];
[alert setInformativeText: NSLocalizedString(@"Once removed, Transmission will no longer attempt to contact it."
" This cannot be undone.", "Remove trackers alert -> message")];
}
[alert addButtonWithTitle: NSLocalizedString(@"Remove", "Remove trackers alert -> button")];
[alert addButtonWithTitle: NSLocalizedString(@"Cancel", "Remove trackers alert -> button")];
[alert setShowsSuppressionButton: YES];
NSInteger result = [alert runModal];
if ([[alert suppressionButton] state] == NSOnState)
[[NSUserDefaults standardUserDefaults] setBool: NO forKey: @"WarningRemoveTrackers"];
[alert release];
if (result != NSAlertFirstButtonReturn)
return;
}
for (Torrent * torrent in removeIdentifiers)
[torrent removeTrackersWithIdentifiers: [removeIdentifiers objectForKey: torrent]];
//reset table with either new or old value
[fTrackers release];
fTrackers = [[NSMutableArray alloc] init];
for (Torrent * torrent in fTorrents)
[fTrackers addObjectsFromArray: [torrent allTrackerStats]];
[fTrackerTable setTrackers: fTrackers];
[fTrackerTable reloadData];
[fTrackerTable deselectAll: self];
[[NSNotificationCenter defaultCenter] postNotificationName: @"UpdateUI" object: nil]; //incase sort by tracker
}
@end

View File

@ -23,62 +23,30 @@
*****************************************************************************/
#import <Cocoa/Cocoa.h>
#import <transmission.h>
@class Torrent;
@class TrackerTableView;
@class TrackerCell;
@class FileOutlineController;
@class PiecesView;
@class InfoGeneralViewController;
@class InfoActivityViewController;
@class InfoTrackersViewController;
@class InfoPeersViewController;
@class InfoFileViewController;
@class InfoOptionsViewController;
@interface InfoWindowController : NSWindowController
{
NSArray * fTorrents;
IBOutlet NSView * fInfoView, * fActivityView, * fTrackerView, * fPeersView, * fFilesView, * fOptionsView;
InfoGeneralViewController * fGeneralViewController;
InfoActivityViewController * fActivityViewController;
InfoTrackersViewController * fTrackersViewController;
InfoPeersViewController * fPeersViewController;
InfoFileViewController * fFileViewController;
InfoOptionsViewController * fOptionsViewController;
NSInteger fCurrentTabTag;
IBOutlet NSMatrix * fTabMatrix;
IBOutlet NSImageView * fImageView;
IBOutlet NSTextField * fNameField, * fBasicInfoField;
IBOutlet NSTextField * fPiecesField, * fHashField, * fSecureField,
* fDataLocationField,
* fDateAddedField, * fDateCompletedField, * fDateActivityField,
* fCreatorField, * fDateCreatedField,
* fStateField, * fProgressField,
* fHaveField, * fDownloadedTotalField, * fUploadedTotalField, * fFailedHashField,
* fRatioField;
IBOutlet NSTextView * fCommentView;
IBOutlet NSButton * fRevealDataButton;
NSMutableArray * fTrackers;
IBOutlet TrackerTableView * fTrackerTable;
TrackerCell * fTrackerCell;
IBOutlet NSSegmentedControl * fTrackerAddRemoveControl;
NSMutableArray * fPeers, * fWebSeeds;
IBOutlet NSTableView * fPeerTable, * fWebSeedTable;
IBOutlet NSTextField * fConnectedPeersField;
IBOutlet NSTextView * fErrorMessageView;
IBOutlet PiecesView * fPiecesView;
IBOutlet NSSegmentedControl * fPiecesControl;
CGFloat fWebSeedTableHeight, fSpaceBetweenWebSeedAndPeer;
NSViewAnimation * fWebSeedTableAnimation;
IBOutlet FileOutlineController * fFileController;
IBOutlet NSSearchField * fFileFilterField;
IBOutlet NSPopUpButton * fPriorityPopUp, * fRatioPopUp;
IBOutlet NSButton * fUploadLimitCheck, * fDownloadLimitCheck, * fGlobalLimitCheck;
IBOutlet NSTextField * fUploadLimitField, * fDownloadLimitField, * fRatioLimitField,
* fUploadLimitLabel, * fDownloadLimitLabel, * fPeersConnectLabel,
* fPeersConnectField;
NSString * fInitialString;
#warning change to QLPreviewPanel
id fPreviewPanel;
}
- (void) setInfoForTorrents: (NSArray *) torrents;
@ -90,28 +58,8 @@
- (void) setNextTab;
- (void) setPreviousTab;
- (void) addRemoveTracker: (id) sender;
- (NSArray *) quickLookURLs;
- (BOOL) canQuickLook;
- (NSRect) quickLookSourceFrameForPreviewItem: (id /*<QLPreviewItem>*/) item;
- (void) setPiecesView: (id) sender;
- (void) setPiecesViewForAvailable: (BOOL) available;
- (void) revealDataFile: (id) sender;
- (void) setFileFilterText: (id) sender;
- (void) setUseSpeedLimit: (id) sender;
- (void) setSpeedLimit: (id) sender;
- (void) setUseGlobalSpeedLimit: (id) sender;
- (void) setRatioSetting: (id) sender;
- (void) setRatioLimit: (id) sender;
- (void) setPriority: (id) sender;
- (void) setPeersConnectLimit: (id) sender;
@end

File diff suppressed because it is too large Load Diff

View File

@ -209,9 +209,10 @@ enum
- (void) mouseDown: (NSEvent *) event
{
if (fTorrent)
#warning fix
/*if (fTorrent)
[[[self window] windowController] setPiecesViewForAvailable:
![[NSUserDefaults standardUserDefaults] boolForKey: @"PiecesViewShowAvailability"]];
![[NSUserDefaults standardUserDefaults] boolForKey: @"PiecesViewShowAvailability"]];*/
[super mouseDown: event];
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff