mirror of
https://github.com/transmission/transmission
synced 2025-01-30 10:52:00 +00:00
refactor: view-based TorrentTableView in macOS client (#5147)
Converted TorrentTableView from older style cell based table to more modern view based * floating group rows are now used for an improved groups experience * individual group indicators are hidden when _Use Groups_ is selected to minimize visual clutter (see #3328 ) * removed negated `usesAlternatingRowBackgroundColors` flag for minimal view in Controller.mm (personal preference - easy to restore)
This commit is contained in:
parent
7fdfabe184
commit
635268854b
22 changed files with 1504 additions and 1487 deletions
|
@ -21,7 +21,13 @@
|
|||
3C7A11980D0B2EE300B5701F /* getgateway.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C7A11920D0B2EE300B5701F /* getgateway.h */; };
|
||||
3C7A11990D0B2EE300B5701F /* natpmp.c in Sources */ = {isa = PBXBuildFile; fileRef = 3C7A11930D0B2EE300B5701F /* natpmp.c */; };
|
||||
3C7A119A0D0B2EE300B5701F /* natpmp.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C7A11940D0B2EE300B5701F /* natpmp.h */; };
|
||||
454BB0562941E8D800F99F38 /* GroupTextCell.mm in Sources */ = {isa = PBXBuildFile; fileRef = 454BB0542941E8D800F99F38 /* GroupTextCell.mm */; };
|
||||
4521532B29AF891F009331B0 /* GroupCell.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4521532929AF891E009331B0 /* GroupCell.mm */; };
|
||||
4521532E29AF9D61009331B0 /* TorrentCell.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4521532D29AF9D61009331B0 /* TorrentCell.mm */; };
|
||||
4534164229B0EA8600F544C9 /* SmallTorrentCell.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4534164129B0EA8600F544C9 /* SmallTorrentCell.mm */; };
|
||||
45489C5429AF6FB20098A812 /* TorrentCellActionButton.mm in Sources */ = {isa = PBXBuildFile; fileRef = 45489C5029AF6FB10098A812 /* TorrentCellActionButton.mm */; };
|
||||
45489C5629AF6FB20098A812 /* TorrentCellRevealButton.mm in Sources */ = {isa = PBXBuildFile; fileRef = 45489C5229AF6FB10098A812 /* TorrentCellRevealButton.mm */; };
|
||||
45489C5729AF6FB20098A812 /* TorrentCellControlButton.mm in Sources */ = {isa = PBXBuildFile; fileRef = 45489C5329AF6FB10098A812 /* TorrentCellControlButton.mm */; };
|
||||
4559D21629B32623004EFDF0 /* ProgressBarView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4559D21529B32623004EFDF0 /* ProgressBarView.mm */; };
|
||||
457AF8EB28604AFC00BCF74F /* Toolbar.mm in Sources */ = {isa = PBXBuildFile; fileRef = 457AF8EA28604AFC00BCF74F /* Toolbar.mm */; };
|
||||
45A7D3292843B54D00F0C32A /* GroupPopUpButtonCell.mm in Sources */ = {isa = PBXBuildFile; fileRef = 45A7D3282843B54D00F0C32A /* GroupPopUpButtonCell.mm */; };
|
||||
45A7D32C2843B55F00F0C32A /* PriorityPopUpButtonCell.mm in Sources */ = {isa = PBXBuildFile; fileRef = 45A7D32B2843B55F00F0C32A /* PriorityPopUpButtonCell.mm */; };
|
||||
|
@ -46,7 +52,6 @@
|
|||
4D80185910BBC0B0008A4AF2 /* magnet-metainfo.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D80185710BBC0B0008A4AF2 /* magnet-metainfo.cc */; };
|
||||
4D80185A10BBC0B0008A4AF2 /* magnet-metainfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 4D80185810BBC0B0008A4AF2 /* magnet-metainfo.h */; };
|
||||
4D9A2BF009E16D21002D0FF9 /* libtransmission.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D18389709DEC0030047D688 /* libtransmission.a */; };
|
||||
4DCCBB3E09C3D71100D3CABF /* TorrentCell.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4DCCBB3C09C3D71100D3CABF /* TorrentCell.mm */; };
|
||||
4DE5CC9D0980656F00BE280E /* NSStringAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4DE5CC9C0980656F00BE280E /* NSStringAdditions.mm */; };
|
||||
4DE5CCA70980735700BE280E /* Badger.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4DE5CCA60980735700BE280E /* Badger.mm */; };
|
||||
4DE5CCCB0981D9BE00BE280E /* Defaults.plist in Resources */ = {isa = PBXBuildFile; fileRef = 4DE5CCCA0981D9BE00BE280E /* Defaults.plist */; };
|
||||
|
@ -642,8 +647,21 @@
|
|||
3C7A11920D0B2EE300B5701F /* getgateway.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = getgateway.h; sourceTree = "<group>"; };
|
||||
3C7A11930D0B2EE300B5701F /* natpmp.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = natpmp.c; sourceTree = "<group>"; };
|
||||
3C7A11940D0B2EE300B5701F /* natpmp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = natpmp.h; sourceTree = "<group>"; };
|
||||
454BB0542941E8D800F99F38 /* GroupTextCell.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = GroupTextCell.mm; sourceTree = "<group>"; };
|
||||
454BB0552941E8D800F99F38 /* GroupTextCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GroupTextCell.h; sourceTree = "<group>"; };
|
||||
4521532929AF891E009331B0 /* GroupCell.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = GroupCell.mm; sourceTree = "<group>"; };
|
||||
4521532A29AF891F009331B0 /* GroupCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GroupCell.h; sourceTree = "<group>"; };
|
||||
4521532C29AF9D60009331B0 /* TorrentCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TorrentCell.h; sourceTree = "<group>"; };
|
||||
4521532D29AF9D61009331B0 /* TorrentCell.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TorrentCell.mm; sourceTree = "<group>"; };
|
||||
4534164029B0EA8500F544C9 /* SmallTorrentCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SmallTorrentCell.h; sourceTree = "<group>"; };
|
||||
4534164129B0EA8600F544C9 /* SmallTorrentCell.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SmallTorrentCell.mm; sourceTree = "<group>"; };
|
||||
45489C4529AE70F00098A812 /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; };
|
||||
45489C4729AE79FC0098A812 /* TorrentCellActionButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TorrentCellActionButton.h; sourceTree = "<group>"; };
|
||||
45489C4A29AF10D00098A812 /* TorrentCellRevealButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TorrentCellRevealButton.h; sourceTree = "<group>"; };
|
||||
45489C4B29AF10D10098A812 /* TorrentCellControlButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TorrentCellControlButton.h; sourceTree = "<group>"; };
|
||||
45489C5029AF6FB10098A812 /* TorrentCellActionButton.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TorrentCellActionButton.mm; sourceTree = "<group>"; };
|
||||
45489C5229AF6FB10098A812 /* TorrentCellRevealButton.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TorrentCellRevealButton.mm; sourceTree = "<group>"; };
|
||||
45489C5329AF6FB10098A812 /* TorrentCellControlButton.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TorrentCellControlButton.mm; sourceTree = "<group>"; };
|
||||
4559D21429B32622004EFDF0 /* ProgressBarView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProgressBarView.h; sourceTree = "<group>"; };
|
||||
4559D21529B32623004EFDF0 /* ProgressBarView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ProgressBarView.mm; sourceTree = "<group>"; };
|
||||
455C0939287767270003A078 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/PrefsWindow.strings; sourceTree = "<group>"; };
|
||||
455C093A287767290003A078 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/PrefsWindow.strings; sourceTree = "<group>"; };
|
||||
455C093B2877672C0003A078 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/PrefsWindow.strings; sourceTree = "<group>"; };
|
||||
|
@ -771,8 +789,6 @@
|
|||
4D8017E910BBC073008A4AF2 /* torrent-magnet.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "torrent-magnet.h"; sourceTree = "<group>"; };
|
||||
4D80185710BBC0B0008A4AF2 /* magnet-metainfo.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = "magnet-metainfo.cc"; sourceTree = "<group>"; };
|
||||
4D80185810BBC0B0008A4AF2 /* magnet-metainfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "magnet-metainfo.h"; sourceTree = "<group>"; };
|
||||
4DCCBB3C09C3D71100D3CABF /* TorrentCell.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = TorrentCell.mm; sourceTree = "<group>"; };
|
||||
4DCCBB3D09C3D71100D3CABF /* TorrentCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TorrentCell.h; sourceTree = "<group>"; };
|
||||
4DDBB71909E16BAE00284745 /* transmissioncli */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = transmissioncli; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
4DE5CC9B0980656F00BE280E /* NSStringAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSStringAdditions.h; sourceTree = "<group>"; };
|
||||
4DE5CC9C0980656F00BE280E /* NSStringAdditions.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = NSStringAdditions.mm; sourceTree = "<group>"; };
|
||||
|
@ -1457,10 +1473,20 @@
|
|||
A27F0F320E19AD9800B2DB97 /* TorrentGroup.mm */,
|
||||
4D364D9E091FBB2C00377D12 /* TorrentTableView.h */,
|
||||
4D364D9F091FBB2C00377D12 /* TorrentTableView.mm */,
|
||||
4DCCBB3D09C3D71100D3CABF /* TorrentCell.h */,
|
||||
4DCCBB3C09C3D71100D3CABF /* TorrentCell.mm */,
|
||||
454BB0552941E8D800F99F38 /* GroupTextCell.h */,
|
||||
454BB0542941E8D800F99F38 /* GroupTextCell.mm */,
|
||||
4521532C29AF9D60009331B0 /* TorrentCell.h */,
|
||||
4521532D29AF9D61009331B0 /* TorrentCell.mm */,
|
||||
4534164029B0EA8500F544C9 /* SmallTorrentCell.h */,
|
||||
4534164129B0EA8600F544C9 /* SmallTorrentCell.mm */,
|
||||
4521532A29AF891F009331B0 /* GroupCell.h */,
|
||||
4521532929AF891E009331B0 /* GroupCell.mm */,
|
||||
4559D21429B32622004EFDF0 /* ProgressBarView.h */,
|
||||
4559D21529B32623004EFDF0 /* ProgressBarView.mm */,
|
||||
45489C4729AE79FC0098A812 /* TorrentCellActionButton.h */,
|
||||
45489C5029AF6FB10098A812 /* TorrentCellActionButton.mm */,
|
||||
45489C4B29AF10D10098A812 /* TorrentCellControlButton.h */,
|
||||
45489C5329AF6FB10098A812 /* TorrentCellControlButton.mm */,
|
||||
45489C4A29AF10D00098A812 /* TorrentCellRevealButton.h */,
|
||||
45489C5229AF6FB10098A812 /* TorrentCellRevealButton.mm */,
|
||||
A21A9BE0106D86A800F1C3C1 /* TrackerNode.h */,
|
||||
A21A9BE1106D86A800F1C3C1 /* TrackerNode.mm */,
|
||||
A2725B6C0DE5C4F5003445E7 /* FileListNode.h */,
|
||||
|
@ -2797,7 +2823,7 @@
|
|||
29B97313FDCFA39411CA2CEA /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1400;
|
||||
LastUpgradeCheck = 1420;
|
||||
ORGANIZATIONNAME = "The Transmission Project";
|
||||
TargetAttributes = {
|
||||
8D1107260486CEB800E47090 = {
|
||||
|
@ -3127,13 +3153,14 @@
|
|||
A225A4C0187E369C00CDE823 /* ShareToolbarItem.mm in Sources */,
|
||||
A2D77453154CC72B00A62B93 /* WebSeedTableView.mm in Sources */,
|
||||
8D11072D0486CEB800E47090 /* main.mm in Sources */,
|
||||
45489C5629AF6FB20098A812 /* TorrentCellRevealButton.mm in Sources */,
|
||||
4DF0C5AB0899190500DD8943 /* Controller.mm in Sources */,
|
||||
4D118E1A08CB46B20033958F /* PrefsController.mm in Sources */,
|
||||
4521532E29AF9D61009331B0 /* TorrentCell.mm in Sources */,
|
||||
4D364DA0091FBB2C00377D12 /* TorrentTableView.mm in Sources */,
|
||||
4DE5CC9D0980656F00BE280E /* NSStringAdditions.mm in Sources */,
|
||||
4DE5CCA70980735700BE280E /* Badger.mm in Sources */,
|
||||
4DFBC2DF09C0970D00D5C571 /* Torrent.mm in Sources */,
|
||||
4DCCBB3E09C3D71100D3CABF /* TorrentCell.mm in Sources */,
|
||||
A200B9200A22798F007BBB1E /* InfoWindowController.mm in Sources */,
|
||||
A2AF1C390A3D0F6200F1575D /* FileOutlineView.mm in Sources */,
|
||||
A2710E770A86796000CE4F7D /* PrefsWindow.mm in Sources */,
|
||||
|
@ -3153,12 +3180,14 @@
|
|||
A2085DDC0C53BC74000BC3B7 /* AboutWindowController.mm in Sources */,
|
||||
A21282A80CA6C66800EAEE0F /* StatusBarView.mm in Sources */,
|
||||
A257C1820CAD3003004E121C /* PeerTableView.mm in Sources */,
|
||||
45489C5429AF6FB20098A812 /* TorrentCellActionButton.mm in Sources */,
|
||||
A2A6321B0CD9751700E3DA60 /* BadgeView.mm in Sources */,
|
||||
A2ED7D8F0CEF431B00970975 /* FilterButton.mm in Sources */,
|
||||
45A7D32C2843B55F00F0C32A /* PriorityPopUpButtonCell.mm in Sources */,
|
||||
A25892640CF1F7E800CCCDDF /* StatsWindowController.mm in Sources */,
|
||||
A2C89D600CFCBF57004CC2BC /* ButtonToolbarItem.mm in Sources */,
|
||||
A219798B0D07B78400438EA7 /* GroupToolbarItem.mm in Sources */,
|
||||
4521532B29AF891F009331B0 /* GroupCell.mm in Sources */,
|
||||
C841A28129197724009F18E8 /* NSKeyedUnarchiverAdditions.mm in Sources */,
|
||||
A22180980D148A71007D09ED /* GroupsPrefsController.mm in Sources */,
|
||||
A26AF21A0D2DA35A00FF7140 /* FileOutlineController.mm in Sources */,
|
||||
|
@ -3167,9 +3196,11 @@
|
|||
45A7D3292843B54D00F0C32A /* GroupPopUpButtonCell.mm in Sources */,
|
||||
A2D307A40D9EC6870051FD27 /* BlocklistDownloader.mm in Sources */,
|
||||
A2725B6E0DE5C4F5003445E7 /* FileListNode.mm in Sources */,
|
||||
4559D21629B32623004EFDF0 /* ProgressBarView.mm in Sources */,
|
||||
A2725D5D0DE7507C003445E7 /* TrackerTableView.mm in Sources */,
|
||||
A28F4F770E085BDC003A3882 /* ColorTextField.mm in Sources */,
|
||||
A27F0F330E19AD9800B2DB97 /* TorrentGroup.mm in Sources */,
|
||||
45489C5729AF6FB20098A812 /* TorrentCellControlButton.mm in Sources */,
|
||||
C809AEE7291ECFD000BFDBE1 /* NSDataAdditions.mm in Sources */,
|
||||
A222E9870E6B21D9009FB003 /* BlocklistDownloaderViewController.mm in Sources */,
|
||||
A222EA7B0E6C32C4009FB003 /* BlocklistScheduler.mm in Sources */,
|
||||
|
@ -3192,10 +3223,10 @@
|
|||
A2E57BA713109E6B00A7DAB1 /* FilterBarController.mm in Sources */,
|
||||
A2B5B4E91880665E0071A66A /* ShareTorrentFileHelper.mm in Sources */,
|
||||
A22BAE281388040500FB022F /* NSMutableArrayAdditions.mm in Sources */,
|
||||
454BB0562941E8D800F99F38 /* GroupTextCell.mm in Sources */,
|
||||
A2966E8713DAF74C007B52DF /* GlobalOptionsPopoverViewController.mm in Sources */,
|
||||
A234EA541453563B000F3E97 /* NSImageAdditions.mm in Sources */,
|
||||
A2AB883E16A399A6008FAD50 /* VDKQueue.mm in Sources */,
|
||||
4534164229B0EA8600F544C9 /* SmallTorrentCell.mm in Sources */,
|
||||
A2451E6916ACE4EB00586E0E /* FileRenameSheetController.mm in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
@ -17,88 +16,341 @@
|
|||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" topStrut="YES"/>
|
||||
<rect key="contentRect" x="71" y="712" width="515" height="248"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="875"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1470" height="919"/>
|
||||
<value key="minSize" type="size" width="350" height="5"/>
|
||||
<view key="contentView" id="2">
|
||||
<rect key="frame" x="0.0" y="0.0" width="515" height="248"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="524" height="220"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<stackView distribution="fill" orientation="vertical" alignment="leading" spacing="0.0" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Msk-Iv-kCJ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="515" height="248"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="524" height="220"/>
|
||||
<subviews>
|
||||
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="65" horizontalPageScroll="0.0" verticalLineScroll="65" verticalPageScroll="0.0" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3088">
|
||||
<rect key="frame" x="0.0" y="24" width="515" height="224"/>
|
||||
<rect key="frame" x="0.0" y="24" width="524" height="196"/>
|
||||
<clipView key="contentView" copiesOnScroll="NO" id="5cE-5g-l0I">
|
||||
<rect key="frame" x="0.0" y="0.0" width="515" height="224"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="524" height="196"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<outlineView verticalHuggingPriority="750" allowsExpansionToolTips="YES" alternatingRowBackgroundColors="YES" columnReordering="NO" columnResizing="NO" autosaveColumns="NO" rowHeight="62" indentationPerLevel="16" indentationMarkerFollowsCell="NO" outlineTableColumn="3093" id="3091" customClass="TorrentTableView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="515" height="224"/>
|
||||
<outlineView verticalHuggingPriority="750" allowsExpansionToolTips="YES" alternatingRowBackgroundColors="YES" columnReordering="NO" columnResizing="NO" autosaveColumns="NO" rowHeight="62" rowSizeStyle="automatic" viewBased="YES" indentationPerLevel="16" indentationMarkerFollowsCell="NO" autoresizesOutlineColumn="YES" outlineTableColumn="3093" id="3091" customClass="TorrentTableView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="524" height="196"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<size key="intercellSpacing" width="2" height="3"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="gridColor" white="0.80241936000000003" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<tableColumns>
|
||||
<tableColumn identifier="Color" editable="NO" width="29" minWidth="16" maxWidth="3000" id="3093">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Color">
|
||||
<tableColumn identifier="Torrent" editable="NO" width="492" minWidth="16" maxWidth="3000" id="3093">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Torrent">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" white="0.33333299" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</tableHeaderCell>
|
||||
<imageCell key="dataCell" controlSize="small" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="imageCell:3146:image" id="3146">
|
||||
<imageCell key="dataCell" controlSize="small" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="imageCell:Uue-OE-Cnc:image" id="Uue-OE-Cnc">
|
||||
<font key="font" metaFont="system"/>
|
||||
</imageCell>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="Group" editable="NO" width="284" minWidth="48" maxWidth="3.4028229999999999e+38" id="3140">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Group">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
</tableHeaderCell>
|
||||
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" sendsActionOnEndEditing="YES" alignment="left" id="3145">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES"/>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="DL Image" editable="NO" width="10" minWidth="10" maxWidth="10" id="3124">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
</tableHeaderCell>
|
||||
<imageCell key="dataCell" controlSize="small" refusesFirstResponder="YES" alignment="left" imageAlignment="right" imageScaling="proportionallyDown" image="imageCell:3146:image" id="3138">
|
||||
<font key="font" metaFont="system"/>
|
||||
</imageCell>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="DL" editable="NO" width="70" minWidth="70" maxWidth="70" id="3126">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="DL Speed">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
</tableHeaderCell>
|
||||
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" alignment="left" id="3127">
|
||||
<font key="font" metaFont="label"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="UL Image" editable="NO" width="10" minWidth="10" maxWidth="10" id="3133">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
</tableHeaderCell>
|
||||
<imageCell key="dataCell" controlSize="small" refusesFirstResponder="YES" alignment="left" imageAlignment="right" imageScaling="proportionallyDown" image="imageCell:3146:image" id="3139">
|
||||
<font key="font" metaFont="system"/>
|
||||
</imageCell>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="UL" editable="NO" width="70" minWidth="70" maxWidth="70" id="3135">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="UL Speed">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
</tableHeaderCell>
|
||||
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" alignment="left" id="3136">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<prototypeCellViews>
|
||||
<tableCellView identifier="GroupCell" id="0FS-PW-dzb" customClass="GroupCell">
|
||||
<rect key="frame" x="11" y="1" width="502" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Gwg-pn-lM5">
|
||||
<rect key="frame" x="11" y="2" width="14" height="14"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="14" id="Jy3-b5-hDM"/>
|
||||
<constraint firstAttribute="width" constant="14" id="njz-e9-z2g"/>
|
||||
</constraints>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="NSStatusAvailable" id="9EY-oY-PNG"/>
|
||||
</imageView>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ubd-A0-0oW">
|
||||
<rect key="frame" x="337" y="1" width="16" height="16"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="16" id="B9U-SP-PBV"/>
|
||||
<constraint firstAttribute="width" constant="16" id="FmI-yQ-CfP"/>
|
||||
</constraints>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="DownArrowTemplate" id="AQv-3A-dH5"/>
|
||||
</imageView>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="hYZ-OW-ebc">
|
||||
<rect key="frame" x="421" y="1" width="16" height="16"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="16" id="CGc-C8-J6A"/>
|
||||
<constraint firstAttribute="height" constant="16" id="SqQ-wV-N6u"/>
|
||||
</constraints>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="UpArrowTemplate" id="M11-HA-ONH"/>
|
||||
</imageView>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="JAO-DI-vbm">
|
||||
<rect key="frame" x="351" y="2" width="64" height="14"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="60" id="MR1-WF-Gcs"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="0.0KB/s" id="x8g-Nv-jcD">
|
||||
<font key="font" metaFont="smallSystemBold"/>
|
||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kLh-dT-fWl">
|
||||
<rect key="frame" x="435" y="2" width="64" height="14"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="60" id="FLW-3y-3Ft"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="0.0KB/s" id="HmT-FI-amY">
|
||||
<font key="font" metaFont="smallSystemBold"/>
|
||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" allowsExpansionToolTips="YES" translatesAutoresizingMaskIntoConstraints="NO" id="XfM-wG-Zxc">
|
||||
<rect key="frame" x="28" y="2" width="311" height="14"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingMiddle" sendsActionOnEndEditing="YES" title="Group Title" id="zIM-6D-jsa">
|
||||
<font key="font" metaFont="smallSystemBold"/>
|
||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="hYZ-OW-ebc" firstAttribute="leading" secondItem="JAO-DI-vbm" secondAttribute="trailing" constant="8" symbolic="YES" id="3ZJ-sm-LLQ"/>
|
||||
<constraint firstItem="XfM-wG-Zxc" firstAttribute="leading" secondItem="Gwg-pn-lM5" secondAttribute="trailing" constant="5" id="4bb-lb-TYP"/>
|
||||
<constraint firstItem="JAO-DI-vbm" firstAttribute="leading" secondItem="ubd-A0-0oW" secondAttribute="trailing" id="8gL-pL-QKz"/>
|
||||
<constraint firstAttribute="trailing" secondItem="kLh-dT-fWl" secondAttribute="trailing" constant="5" id="D4t-PI-NVX"/>
|
||||
<constraint firstItem="ubd-A0-0oW" firstAttribute="leading" secondItem="XfM-wG-Zxc" secondAttribute="trailing" id="E4I-Nb-vB5"/>
|
||||
<constraint firstItem="ubd-A0-0oW" firstAttribute="centerY" secondItem="Gwg-pn-lM5" secondAttribute="centerY" id="GZB-R5-O8z"/>
|
||||
<constraint firstItem="hYZ-OW-ebc" firstAttribute="centerY" secondItem="Gwg-pn-lM5" secondAttribute="centerY" id="NWK-Hj-44W"/>
|
||||
<constraint firstItem="Gwg-pn-lM5" firstAttribute="centerY" secondItem="0FS-PW-dzb" secondAttribute="centerY" id="Yau-WM-aqf"/>
|
||||
<constraint firstItem="XfM-wG-Zxc" firstAttribute="centerY" secondItem="Gwg-pn-lM5" secondAttribute="centerY" id="pB3-eZ-C1W"/>
|
||||
<constraint firstItem="Gwg-pn-lM5" firstAttribute="leading" secondItem="0FS-PW-dzb" secondAttribute="leading" constant="11" id="qsg-vi-UIQ"/>
|
||||
<constraint firstItem="JAO-DI-vbm" firstAttribute="centerY" secondItem="Gwg-pn-lM5" secondAttribute="centerY" id="rWB-xX-7V9"/>
|
||||
<constraint firstItem="kLh-dT-fWl" firstAttribute="leading" secondItem="hYZ-OW-ebc" secondAttribute="trailing" id="t7S-2X-ur7"/>
|
||||
<constraint firstItem="kLh-dT-fWl" firstAttribute="centerY" secondItem="Gwg-pn-lM5" secondAttribute="centerY" id="uLM-dC-jCY"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="fGroupDownloadField" destination="JAO-DI-vbm" id="jls-TR-sdX"/>
|
||||
<outlet property="fGroupDownloadView" destination="ubd-A0-0oW" id="l2t-pP-sMi"/>
|
||||
<outlet property="fGroupIndicatorView" destination="Gwg-pn-lM5" id="FCY-PW-4YQ"/>
|
||||
<outlet property="fGroupTitleField" destination="XfM-wG-Zxc" id="o72-mm-63H"/>
|
||||
<outlet property="fGroupUploadAndRatioField" destination="kLh-dT-fWl" id="31q-Z9-mDX"/>
|
||||
<outlet property="fGroupUploadAndRatioView" destination="hYZ-OW-ebc" id="92U-rh-k8B"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
<tableCellView identifier="TorrentCell" id="7Ic-8y-f3v" customClass="TorrentCell">
|
||||
<rect key="frame" x="11" y="22" width="502" height="62"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="FLl-ue-i20">
|
||||
<rect key="frame" x="2" y="26" width="10" height="10"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="10" id="NhU-Lf-nCs"/>
|
||||
<constraint firstAttribute="width" constant="10" id="zar-fl-hEG"/>
|
||||
</constraints>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSStatusAvailable" id="Oyo-d7-APt"/>
|
||||
</imageView>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="WTV-L6-lc2">
|
||||
<rect key="frame" x="15" y="13" width="36" height="36"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="36" id="Qka-wO-mv5"/>
|
||||
<constraint firstAttribute="width" constant="36" id="tDk-n4-lHb"/>
|
||||
</constraints>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSFolder" id="WGQ-kl-WZm"/>
|
||||
</imageView>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gIX-h2-fIl">
|
||||
<rect key="frame" x="70" y="44" width="377" height="15"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingMiddle" title="Title" id="y9O-gd-sXA">
|
||||
<font key="font" metaFont="cellTitle"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="7sy-Kk-M9Y">
|
||||
<rect key="frame" x="70" y="30" width="377" height="13"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingMiddle" title="Progress" id="oSz-TE-8jj">
|
||||
<font key="font" metaFont="system" size="10"/>
|
||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="J4v-0p-u4L">
|
||||
<rect key="frame" x="70" y="1" width="377" height="13"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingMiddle" title="Status" id="lmp-Y3-ZQO">
|
||||
<font key="font" metaFont="system" size="10"/>
|
||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button horizontalHuggingPriority="1000" verticalHuggingPriority="750" horizontalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="kNl-5i-USx" customClass="TorrentCellControlButton">
|
||||
<rect key="frame" x="466" y="15" width="14" height="14"/>
|
||||
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="ResumeOff" imagePosition="only" alignment="center" lineBreakMode="truncatingTail" state="on" imageScaling="proportionallyDown" inset="2" id="oxr-Hn-H15">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Rxb-qW-x6p" customClass="TorrentCellActionButton">
|
||||
<rect key="frame" x="25" y="23" width="16" height="16"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="16" id="WLP-JK-i9A"/>
|
||||
<constraint firstAttribute="height" constant="16" id="bMb-uB-fO6"/>
|
||||
</constraints>
|
||||
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="ActionHover" imagePosition="only" alignment="center" lineBreakMode="truncatingTail" state="on" imageScaling="proportionallyDown" inset="2" id="9Xz-qo-fuz">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
</button>
|
||||
<button horizontalHuggingPriority="1000" verticalHuggingPriority="750" horizontalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="k5I-MN-hYk" customClass="TorrentCellRevealButton">
|
||||
<rect key="frame" x="483" y="15" width="14" height="14"/>
|
||||
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="RevealOff" imagePosition="only" alignment="center" lineBreakMode="truncatingTail" state="on" imageScaling="proportionallyDown" inset="2" id="WF0-QY-3DO">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
</button>
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="Ifq-ub-ay4">
|
||||
<rect key="frame" x="72" y="15" width="373" height="14"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="14" id="D7V-m1-heE"/>
|
||||
</constraints>
|
||||
</customView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="7sy-Kk-M9Y" firstAttribute="top" secondItem="gIX-h2-fIl" secondAttribute="bottom" constant="1" id="3gb-Hf-Uub"/>
|
||||
<constraint firstItem="FLl-ue-i20" firstAttribute="centerY" secondItem="7Ic-8y-f3v" secondAttribute="centerY" id="7iF-OP-ifv"/>
|
||||
<constraint firstItem="WTV-L6-lc2" firstAttribute="leading" secondItem="FLl-ue-i20" secondAttribute="trailing" constant="3" id="8e4-oE-Kzh"/>
|
||||
<constraint firstItem="7sy-Kk-M9Y" firstAttribute="leading" secondItem="gIX-h2-fIl" secondAttribute="leading" id="9xL-MW-2y6"/>
|
||||
<constraint firstItem="J4v-0p-u4L" firstAttribute="top" secondItem="Ifq-ub-ay4" secondAttribute="bottom" constant="1" id="AyP-y5-tmL"/>
|
||||
<constraint firstItem="Ifq-ub-ay4" firstAttribute="leading" secondItem="gIX-h2-fIl" secondAttribute="leading" id="DcH-RS-hMc"/>
|
||||
<constraint firstItem="kNl-5i-USx" firstAttribute="centerY" secondItem="Ifq-ub-ay4" secondAttribute="centerY" id="F2k-wy-sR2"/>
|
||||
<constraint firstItem="WTV-L6-lc2" firstAttribute="centerY" secondItem="FLl-ue-i20" secondAttribute="centerY" id="FXq-kY-5ny"/>
|
||||
<constraint firstItem="FLl-ue-i20" firstAttribute="leading" secondItem="7Ic-8y-f3v" secondAttribute="leading" constant="2" id="Fu9-kh-dtb"/>
|
||||
<constraint firstItem="Ifq-ub-ay4" firstAttribute="trailing" secondItem="7sy-Kk-M9Y" secondAttribute="trailing" id="KFR-7d-Sjr"/>
|
||||
<constraint firstAttribute="trailing" secondItem="k5I-MN-hYk" secondAttribute="trailing" constant="5" id="NRQ-sA-ng6"/>
|
||||
<constraint firstItem="J4v-0p-u4L" firstAttribute="leading" secondItem="gIX-h2-fIl" secondAttribute="leading" id="S8t-RL-exe"/>
|
||||
<constraint firstItem="Rxb-qW-x6p" firstAttribute="centerX" secondItem="WTV-L6-lc2" secondAttribute="centerX" id="Uyo-j3-TSH"/>
|
||||
<constraint firstItem="Ifq-ub-ay4" firstAttribute="top" secondItem="7sy-Kk-M9Y" secondAttribute="bottom" constant="1" id="XRq-cM-zeI"/>
|
||||
<constraint firstItem="kNl-5i-USx" firstAttribute="leading" secondItem="Ifq-ub-ay4" secondAttribute="trailing" constant="21" id="b80-qd-DxN"/>
|
||||
<constraint firstItem="gIX-h2-fIl" firstAttribute="leading" secondItem="WTV-L6-lc2" secondAttribute="trailing" constant="21" id="dgd-qt-xxb"/>
|
||||
<constraint firstItem="k5I-MN-hYk" firstAttribute="leading" secondItem="kNl-5i-USx" secondAttribute="trailing" constant="3" id="fsy-23-flK"/>
|
||||
<constraint firstItem="J4v-0p-u4L" firstAttribute="trailing" secondItem="Ifq-ub-ay4" secondAttribute="trailing" id="j5C-sr-mIK"/>
|
||||
<constraint firstItem="gIX-h2-fIl" firstAttribute="top" secondItem="7Ic-8y-f3v" secondAttribute="top" constant="3" id="lhe-Ce-3SF"/>
|
||||
<constraint firstItem="Ifq-ub-ay4" firstAttribute="trailing" secondItem="gIX-h2-fIl" secondAttribute="trailing" id="t3M-au-Bgz"/>
|
||||
<constraint firstItem="k5I-MN-hYk" firstAttribute="centerY" secondItem="kNl-5i-USx" secondAttribute="centerY" id="xjv-n4-F1m"/>
|
||||
<constraint firstItem="Rxb-qW-x6p" firstAttribute="centerY" secondItem="WTV-L6-lc2" secondAttribute="centerY" id="y0a-c5-pYS"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="fActionButton" destination="Rxb-qW-x6p" id="ICn-NC-ahy"/>
|
||||
<outlet property="fControlButton" destination="kNl-5i-USx" id="iN7-sX-ciA"/>
|
||||
<outlet property="fGroupIndicatorView" destination="FLl-ue-i20" id="0QU-wF-59e"/>
|
||||
<outlet property="fIconView" destination="WTV-L6-lc2" id="gmV-h3-Muk"/>
|
||||
<outlet property="fRevealButton" destination="k5I-MN-hYk" id="Qkq-Yq-UAY"/>
|
||||
<outlet property="fTorrentProgressBarView" destination="Ifq-ub-ay4" id="rEw-BU-47h"/>
|
||||
<outlet property="fTorrentProgressField" destination="7sy-Kk-M9Y" id="nk4-R3-1Wv"/>
|
||||
<outlet property="fTorrentStatusField" destination="J4v-0p-u4L" id="nqa-rT-XUI"/>
|
||||
<outlet property="fTorrentTitleField" destination="gIX-h2-fIl" id="h3s-C4-XNT"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
<tableCellView identifier="SmallTorrentCell" id="ouH-H8-Otv" customClass="SmallTorrentCell">
|
||||
<rect key="frame" x="11" y="87" width="502" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="4yg-IA-aOF">
|
||||
<rect key="frame" x="4" y="8" width="6" height="6"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="6" id="Gfx-gR-O2I"/>
|
||||
<constraint firstAttribute="width" constant="6" id="SCs-lp-qum"/>
|
||||
</constraints>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSStatusAvailable" id="brn-HI-eEh"/>
|
||||
</imageView>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="WWu-yD-Amw">
|
||||
<rect key="frame" x="18" y="3" width="16" height="16"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="16" id="42D-Ja-dYD"/>
|
||||
<constraint firstAttribute="width" constant="16" id="y2G-x9-YIV"/>
|
||||
</constraints>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSFolder" id="Kea-PS-H27"/>
|
||||
</imageView>
|
||||
<button translatesAutoresizingMaskIntoConstraints="NO" id="9Cg-Nh-Hcr" customClass="TorrentCellActionButton">
|
||||
<rect key="frame" x="18" y="3" width="16" height="16"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="16" id="B01-Zu-gNh"/>
|
||||
<constraint firstAttribute="height" constant="16" id="dNQ-J3-27z"/>
|
||||
</constraints>
|
||||
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="ActionHover" imagePosition="only" alignment="center" imageScaling="proportionallyUpOrDown" inset="2" id="ejE-Yu-JNS">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
</button>
|
||||
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="nyW-bO-2kN">
|
||||
<rect key="frame" x="52" y="4" width="370" height="15"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingMiddle" title="Title" id="kbX-Av-eP4">
|
||||
<font key="font" metaFont="cellTitle"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="x9c-7U-Odp">
|
||||
<rect key="frame" x="53" y="2" width="444" height="18"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="18" id="UN3-pW-1Yw"/>
|
||||
</constraints>
|
||||
</customView>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="ExW-pe-aKm">
|
||||
<rect key="frame" x="423" y="4" width="73" height="14"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingMiddle" alignment="right" title="Status String" id="Zmj-A9-NPf">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button translatesAutoresizingMaskIntoConstraints="NO" id="bGq-Kc-jWE" customClass="TorrentCellControlButton">
|
||||
<rect key="frame" x="463" y="4" width="14" height="14"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="14" id="hQW-cC-Lp7"/>
|
||||
<constraint firstAttribute="height" constant="14" id="jWF-m2-n6g"/>
|
||||
</constraints>
|
||||
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="ResumeOff" imagePosition="only" alignment="center" imageScaling="proportionallyUpOrDown" inset="2" id="DmO-My-6OF">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
</button>
|
||||
<button translatesAutoresizingMaskIntoConstraints="NO" id="Hyt-Uv-vDm" customClass="TorrentCellRevealButton">
|
||||
<rect key="frame" x="480" y="4" width="14" height="14"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="14" id="CYn-n4-Daq"/>
|
||||
<constraint firstAttribute="width" constant="14" id="P9R-Cw-RJF"/>
|
||||
</constraints>
|
||||
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="RevealOff" imagePosition="only" alignment="center" imageScaling="proportionallyUpOrDown" inset="2" id="dWx-5q-ABk">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="Hyt-Uv-vDm" firstAttribute="centerY" secondItem="x9c-7U-Odp" secondAttribute="centerY" id="1WR-Ta-vbl"/>
|
||||
<constraint firstItem="nyW-bO-2kN" firstAttribute="centerY" secondItem="4yg-IA-aOF" secondAttribute="centerY" id="25K-kd-55i"/>
|
||||
<constraint firstAttribute="trailing" secondItem="ExW-pe-aKm" secondAttribute="trailing" constant="8" id="5OS-LM-hyI"/>
|
||||
<constraint firstItem="ExW-pe-aKm" firstAttribute="centerY" secondItem="4yg-IA-aOF" secondAttribute="centerY" id="Lmc-wT-yP4"/>
|
||||
<constraint firstItem="nyW-bO-2kN" firstAttribute="leading" secondItem="9Cg-Nh-Hcr" secondAttribute="trailing" constant="20" id="N3b-dy-27i"/>
|
||||
<constraint firstItem="x9c-7U-Odp" firstAttribute="leading" secondItem="9Cg-Nh-Hcr" secondAttribute="trailing" constant="19" id="On5-WE-C7H"/>
|
||||
<constraint firstItem="bGq-Kc-jWE" firstAttribute="centerY" secondItem="x9c-7U-Odp" secondAttribute="centerY" id="PrG-V3-ry0"/>
|
||||
<constraint firstItem="4yg-IA-aOF" firstAttribute="leading" secondItem="ouH-H8-Otv" secondAttribute="leading" constant="4" id="Ps8-HO-fGd"/>
|
||||
<constraint firstItem="9Cg-Nh-Hcr" firstAttribute="centerY" secondItem="4yg-IA-aOF" secondAttribute="centerY" id="Rvd-RT-10P"/>
|
||||
<constraint firstItem="x9c-7U-Odp" firstAttribute="centerY" secondItem="9Cg-Nh-Hcr" secondAttribute="centerY" id="VaS-hJ-vAV"/>
|
||||
<constraint firstAttribute="trailing" secondItem="x9c-7U-Odp" secondAttribute="trailing" constant="5" id="daS-0F-yKf"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Hyt-Uv-vDm" secondAttribute="trailing" constant="8" id="fKb-o5-n1k"/>
|
||||
<constraint firstItem="WWu-yD-Amw" firstAttribute="centerY" secondItem="9Cg-Nh-Hcr" secondAttribute="centerY" id="hYn-MV-EFh"/>
|
||||
<constraint firstItem="4yg-IA-aOF" firstAttribute="centerY" secondItem="ouH-H8-Otv" secondAttribute="centerY" id="izm-2a-TOI"/>
|
||||
<constraint firstItem="Hyt-Uv-vDm" firstAttribute="leading" secondItem="bGq-Kc-jWE" secondAttribute="trailing" constant="3" id="kX1-gB-jyF"/>
|
||||
<constraint firstItem="9Cg-Nh-Hcr" firstAttribute="leading" secondItem="4yg-IA-aOF" secondAttribute="trailing" constant="8" id="lf2-Bm-TFR"/>
|
||||
<constraint firstItem="ExW-pe-aKm" firstAttribute="leading" secondItem="nyW-bO-2kN" secondAttribute="trailing" constant="5" id="nNT-cl-cRg"/>
|
||||
<constraint firstItem="WWu-yD-Amw" firstAttribute="centerX" secondItem="9Cg-Nh-Hcr" secondAttribute="centerX" id="tNz-Ea-fpP"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="fActionButton" destination="9Cg-Nh-Hcr" id="88t-qS-msz"/>
|
||||
<outlet property="fControlButton" destination="bGq-Kc-jWE" id="Oyb-Iu-Na0"/>
|
||||
<outlet property="fGroupIndicatorView" destination="4yg-IA-aOF" id="ROF-Ua-PGS"/>
|
||||
<outlet property="fIconView" destination="WWu-yD-Amw" id="vWy-ch-grE"/>
|
||||
<outlet property="fRevealButton" destination="Hyt-Uv-vDm" id="PHz-zj-UsH"/>
|
||||
<outlet property="fTorrentProgressBarView" destination="x9c-7U-Odp" id="WGd-KA-6xD"/>
|
||||
<outlet property="fTorrentStatusField" destination="ExW-pe-aKm" id="rQQ-ip-8MV"/>
|
||||
<outlet property="fTorrentTitleField" destination="nyW-bO-2kN" id="Xsd-Ro-ren"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
</prototypeCellViews>
|
||||
</tableColumn>
|
||||
</tableColumns>
|
||||
<connections>
|
||||
|
@ -126,10 +378,10 @@
|
|||
</scroller>
|
||||
</scrollView>
|
||||
<customView verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="JCm-xb-Dpw">
|
||||
<rect key="frame" x="0.0" y="0.0" width="515" height="24"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="524" height="24"/>
|
||||
<subviews>
|
||||
<textField verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2700">
|
||||
<rect key="frame" x="199" y="5" width="118" height="14"/>
|
||||
<rect key="frame" x="258" y="5" width="8" height="14"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingTail" allowsUndo="NO" sendsActionOnEndEditing="YES" alignment="center" usesSingleLineMode="YES" id="3049">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -1017,13 +1269,20 @@ CA
|
|||
</menu>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="ActionHover" width="16" height="16"/>
|
||||
<image name="CleanupTemplate" width="18" height="18"/>
|
||||
<image name="DownArrowTemplate" width="8" height="12"/>
|
||||
<image name="EllipsisTemplate" width="18" height="18"/>
|
||||
<image name="NSFolder" width="32" height="32"/>
|
||||
<image name="NSStatusAvailable" width="16" height="16"/>
|
||||
<image name="PriorityHighTemplate" width="12" height="12"/>
|
||||
<image name="PriorityLowTemplate" width="12" height="12"/>
|
||||
<image name="PriorityNormalTemplate" width="12" height="12"/>
|
||||
<image name="ResumeOff" width="14" height="14"/>
|
||||
<image name="RevealOff" width="14" height="14"/>
|
||||
<image name="TortoiseTemplate" width="18" height="18"/>
|
||||
<image name="imageCell:3146:image" width="62" height="62">
|
||||
<image name="UpArrowTemplate" width="8" height="12"/>
|
||||
<image name="imageCell:Uue-OE-Cnc:image" width="62" height="62">
|
||||
<mutableData key="keyedArchiveRepresentation">
|
||||
YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0b3BYJG9iamVjdHMSAAGGoF8QD05T
|
||||
S2V5ZWRBcmNoaXZlctEICVRyb290gAGuCwwZGh8UJCkqMTQ3PUBVJG51bGzWDQ4PEBESExQVFhcYVk5T
|
||||
|
|
|
@ -90,14 +90,14 @@ target_sources(${TR_NAME}-mac
|
|||
FilterButton.mm
|
||||
GlobalOptionsPopoverViewController.h
|
||||
GlobalOptionsPopoverViewController.mm
|
||||
GroupCell.h
|
||||
GroupCell.mm
|
||||
GroupPopUpButtonCell.h
|
||||
GroupPopUpButtonCell.mm
|
||||
GroupsController.h
|
||||
GroupsController.mm
|
||||
GroupsPrefsController.h
|
||||
GroupsPrefsController.mm
|
||||
GroupTextCell.h
|
||||
GroupTextCell.mm
|
||||
GroupToolbarItem.h
|
||||
GroupToolbarItem.mm
|
||||
InfoActivityViewController.h
|
||||
|
@ -148,12 +148,16 @@ target_sources(${TR_NAME}-mac
|
|||
PrefsWindow.mm
|
||||
PriorityPopUpButtonCell.h
|
||||
PriorityPopUpButtonCell.mm
|
||||
ProgressBarView.h
|
||||
ProgressBarView.mm
|
||||
ProgressGradients.h
|
||||
ProgressGradients.mm
|
||||
ShareToolbarItem.h
|
||||
ShareToolbarItem.mm
|
||||
ShareTorrentFileHelper.h
|
||||
ShareTorrentFileHelper.mm
|
||||
SmallTorrentCell.h
|
||||
SmallTorrentCell.mm
|
||||
SparkleProxy.mm
|
||||
StatsWindowController.h
|
||||
StatsWindowController.mm
|
||||
|
@ -167,6 +171,12 @@ target_sources(${TR_NAME}-mac
|
|||
Torrent.mm
|
||||
TorrentCell.h
|
||||
TorrentCell.mm
|
||||
TorrentCellActionButton.h
|
||||
TorrentCellActionButton.mm
|
||||
TorrentCellControlButton.h
|
||||
TorrentCellControlButton.mm
|
||||
TorrentCellRevealButton.h
|
||||
TorrentCellRevealButton.mm
|
||||
TorrentGroup.h
|
||||
TorrentGroup.mm
|
||||
TorrentTableView.h
|
||||
|
|
|
@ -625,11 +625,10 @@ void onTorrentCompletenessChanged(tr_torrent* tor, tr_completeness status, bool
|
|||
|
||||
//set table size
|
||||
BOOL const small = [self.fDefaults boolForKey:@"SmallView"];
|
||||
if (small)
|
||||
{
|
||||
self.fTableView.rowHeight = kRowHeightSmall;
|
||||
}
|
||||
self.fTableView.usesAlternatingRowBackgroundColors = !small;
|
||||
self.fTableView.rowHeight = small ? kRowHeightSmall : kRowHeightRegular;
|
||||
self.fTableView.usesAutomaticRowHeights = NO;
|
||||
self.fTableView.floatsGroupRows = YES;
|
||||
//self.fTableView.usesAlternatingRowBackgroundColors = !small;
|
||||
|
||||
[self.fWindow setContentBorderThickness:NSMinY(self.fTableView.enclosingScrollView.frame) forEdge:NSMinYEdge];
|
||||
self.fWindow.movableByWindowBackground = YES;
|
||||
|
@ -2317,6 +2316,8 @@ void onTorrentCompletenessChanged(tr_torrent* tor, tr_completeness status, bool
|
|||
{
|
||||
[self.fInfoController updateInfoStats];
|
||||
}
|
||||
|
||||
[self.fTableView reloadVisibleRows];
|
||||
}
|
||||
|
||||
//badge dock
|
||||
|
@ -3239,10 +3240,6 @@ void onTorrentCompletenessChanged(tr_torrent* tor, tr_completeness status, bool
|
|||
//set all groups as expanded
|
||||
[self.fTableView removeAllCollapsedGroups];
|
||||
|
||||
//since we're not doing this the right way (boo buggy animation), we need to remember selected values
|
||||
#warning when Lion-only and using views instead of cells, this likely won't be needed
|
||||
NSArray* selectedValues = self.fTableView.selectedValues;
|
||||
|
||||
beganUpdates = YES;
|
||||
[self.fTableView beginUpdates];
|
||||
|
||||
|
@ -3286,11 +3283,6 @@ void onTorrentCompletenessChanged(tr_torrent* tor, tr_completeness status, bool
|
|||
for (TorrentGroup* group in self.fDisplayedTorrents)
|
||||
[self.fTableView expandItem:group];
|
||||
}
|
||||
|
||||
if (selectedValues)
|
||||
{
|
||||
[self.fTableView selectValues:selectedValues];
|
||||
}
|
||||
}
|
||||
|
||||
//sort the torrents (won't sort the groups, though)
|
||||
|
@ -3599,66 +3591,6 @@ void onTorrentCompletenessChanged(tr_torrent* tor, tr_completeness status, bool
|
|||
return ![item isKindOfClass:[Torrent class]];
|
||||
}
|
||||
|
||||
- (id)outlineView:(NSOutlineView*)outlineView objectValueForTableColumn:(NSTableColumn*)tableColumn byItem:(id)item
|
||||
{
|
||||
if ([item isKindOfClass:[Torrent class]])
|
||||
{
|
||||
if (tableColumn)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
return ((Torrent*)item).hashString;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString* ident = tableColumn.identifier;
|
||||
TorrentGroup* group = (TorrentGroup*)item;
|
||||
if ([ident isEqualToString:@"Group"])
|
||||
{
|
||||
NSInteger groupIndex = group.groupIndex;
|
||||
return groupIndex != -1 ? [GroupsController.groups nameForIndex:groupIndex] : NSLocalizedString(@"No Group", "Group table row");
|
||||
}
|
||||
else if ([ident isEqualToString:@"Color"])
|
||||
{
|
||||
NSInteger groupIndex = group.groupIndex;
|
||||
return [GroupsController.groups imageForIndex:groupIndex];
|
||||
}
|
||||
else if ([ident isEqualToString:@"DL Image"])
|
||||
{
|
||||
NSImage* image = [NSImage imageNamed:@"DownArrowGroupTemplate"];
|
||||
image.accessibilityDescription = NSLocalizedString(@"DL", "Torrent -> status image");
|
||||
return image;
|
||||
}
|
||||
else if ([ident isEqualToString:@"UL Image"])
|
||||
{
|
||||
if ([self.fDefaults boolForKey:@"DisplayGroupRowRatio"])
|
||||
{
|
||||
NSImage* image = [NSImage imageNamed:@"YingYangGroupTemplate"];
|
||||
image.accessibilityDescription = NSLocalizedString(@"Ratio", "Torrent -> status image");
|
||||
return image;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSImage* image = [NSImage imageNamed:@"UpArrowGroupTemplate"];
|
||||
image.accessibilityDescription = NSLocalizedString(@"UL", "Torrent -> status image");
|
||||
return image;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ([self.fDefaults boolForKey:@"DisplayGroupRowRatio"])
|
||||
{
|
||||
return [NSString stringForRatio:group.ratio];
|
||||
}
|
||||
else
|
||||
{
|
||||
CGFloat rate = [ident isEqualToString:@"UL"] ? group.uploadRate : group.downloadRate;
|
||||
return [NSString stringForSpeed:rate];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)outlineView:(NSOutlineView*)outlineView writeItems:(NSArray*)items toPasteboard:(NSPasteboard*)pasteboard
|
||||
{
|
||||
//only allow reordering of rows if sorting by order
|
||||
|
@ -3980,7 +3912,7 @@ void onTorrentCompletenessChanged(tr_torrent* tor, tr_completeness status, bool
|
|||
BOOL makeSmall = ![self.fDefaults boolForKey:@"SmallView"];
|
||||
[self.fDefaults setBool:makeSmall forKey:@"SmallView"];
|
||||
|
||||
self.fTableView.usesAlternatingRowBackgroundColors = !makeSmall;
|
||||
//self.fTableView.usesAlternatingRowBackgroundColors = !makeSmall;
|
||||
|
||||
self.fTableView.rowHeight = makeSmall ? kRowHeightSmall : kRowHeightRegular;
|
||||
|
||||
|
@ -5319,6 +5251,12 @@ void onTorrentCompletenessChanged(tr_torrent* tor, tr_completeness status, bool
|
|||
|
||||
height = (kGroupSeparatorHeight + self.fTableView.intercellSpacing.height) * groups +
|
||||
(self.fTableView.rowHeight + self.fTableView.intercellSpacing.height) * (self.fTableView.numberOfRows - groups);
|
||||
|
||||
//account for group padding...
|
||||
if (groups > 1)
|
||||
{
|
||||
height += (groups - 1) * 20;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
18
macosx/GroupCell.h
Normal file
18
macosx/GroupCell.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
// This file Copyright © 2006-2023 Transmission authors and contributors.
|
||||
// It may be used under the MIT (SPDX: MIT) license.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import "TorrentTableView.h"
|
||||
|
||||
@interface GroupCell : NSTableCellView
|
||||
|
||||
@property(nonatomic) IBOutlet NSImageView* fGroupIndicatorView;
|
||||
@property(nonatomic) IBOutlet NSTextField* fGroupTitleField;
|
||||
|
||||
@property(nonatomic) IBOutlet NSImageView* fGroupDownloadView;
|
||||
@property(nonatomic) IBOutlet NSImageView* fGroupUploadAndRatioView;
|
||||
@property(nonatomic) IBOutlet NSTextField* fGroupDownloadField;
|
||||
@property(nonatomic) IBOutlet NSTextField* fGroupUploadAndRatioField;
|
||||
|
||||
@end
|
9
macosx/GroupCell.mm
Normal file
9
macosx/GroupCell.mm
Normal file
|
@ -0,0 +1,9 @@
|
|||
// This file Copyright © 2006-2023 Transmission authors and contributors.
|
||||
// It may be used under the MIT (SPDX: MIT) license.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#import "GroupCell.h"
|
||||
|
||||
@implementation GroupCell
|
||||
|
||||
@end
|
|
@ -1,11 +0,0 @@
|
|||
// This file Copyright © 2022-2023 Transmission authors and contributors.
|
||||
// It may be used under the MIT (SPDX: MIT) license.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
@interface GroupTextCell : NSTextFieldCell
|
||||
|
||||
@property(nonatomic) BOOL selected;
|
||||
|
||||
@end
|
|
@ -1,35 +0,0 @@
|
|||
// This file Copyright © 2022-2023 Transmission authors and contributors.
|
||||
// It may be used under the MIT (SPDX: MIT) license.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#import "GroupTextCell.h"
|
||||
#import "TorrentGroup.h"
|
||||
#import "TorrentTableView.h"
|
||||
|
||||
@implementation GroupTextCell
|
||||
|
||||
//vertically align text
|
||||
- (NSRect)titleRectForBounds:(NSRect)theRect
|
||||
{
|
||||
NSRect titleFrame = [super titleRectForBounds:theRect];
|
||||
NSSize titleSize = [[self attributedStringValue] size];
|
||||
titleFrame.origin.y = NSMidY(theRect) - (CGFloat)1.0 - titleSize.height * (CGFloat)0.5;
|
||||
titleFrame.origin.x = theRect.origin.x;
|
||||
return titleFrame;
|
||||
}
|
||||
|
||||
- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView
|
||||
{
|
||||
//set font size and color
|
||||
NSRect titleRect = [self titleRectForBounds:cellFrame];
|
||||
NSMutableAttributedString* string = [[self attributedStringValue] mutableCopy];
|
||||
NSDictionary* attributes = @{
|
||||
NSFontAttributeName : [NSFont boldSystemFontOfSize:11.0],
|
||||
NSForegroundColorAttributeName : self.selected ? [NSColor labelColor] : [NSColor secondaryLabelColor]
|
||||
};
|
||||
|
||||
[string addAttributes:attributes range:NSMakeRange(0, string.length)];
|
||||
[string drawInRect:titleRect];
|
||||
}
|
||||
|
||||
@end
|
14
macosx/ProgressBarView.h
Normal file
14
macosx/ProgressBarView.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
// This file Copyright © 2006-2023 Transmission authors and contributors.
|
||||
// It may be used under the MIT (SPDX: MIT) license.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
@class TorrentTableView;
|
||||
@class Torrent;
|
||||
|
||||
@interface ProgressBarView : NSView
|
||||
|
||||
- (void)drawBarInRect:(NSRect)barRect forTableView:(TorrentTableView*)tableView withTorrent:(Torrent*)torrent;
|
||||
|
||||
@end
|
203
macosx/ProgressBarView.mm
Normal file
203
macosx/ProgressBarView.mm
Normal file
|
@ -0,0 +1,203 @@
|
|||
// This file Copyright © 2006-2023 Transmission authors and contributors.
|
||||
// It may be used under the MIT (SPDX: MIT) license.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#import "ProgressBarView.h"
|
||||
#import "ProgressGradients.h"
|
||||
#import "TorrentTableView.h"
|
||||
#import "Torrent.h"
|
||||
|
||||
static CGFloat const kPiecesTotalPercent = 0.6;
|
||||
static NSInteger const kMaxPieces = 18 * 18;
|
||||
|
||||
@interface ProgressBarView ()
|
||||
|
||||
@property(nonatomic, readonly) NSUserDefaults* fDefaults;
|
||||
|
||||
@property(nonatomic, readonly) NSColor* fBarBorderColor;
|
||||
@property(nonatomic, readonly) NSColor* fBluePieceColor;
|
||||
@property(nonatomic, readonly) NSColor* fBarMinimalBorderColor;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ProgressBarView
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init]))
|
||||
{
|
||||
_fDefaults = NSUserDefaults.standardUserDefaults;
|
||||
|
||||
_fBluePieceColor = [NSColor colorWithCalibratedRed:0.0 green:0.4 blue:0.8 alpha:1.0];
|
||||
_fBarBorderColor = [NSColor colorWithCalibratedWhite:0.0 alpha:0.2];
|
||||
_fBarMinimalBorderColor = [NSColor colorWithCalibratedWhite:0.0 alpha:0.015];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)drawBarInRect:(NSRect)barRect forTableView:(TorrentTableView*)tableView withTorrent:(Torrent*)torrent
|
||||
{
|
||||
BOOL const minimal = [self.fDefaults boolForKey:@"SmallView"];
|
||||
|
||||
CGFloat const piecesBarPercent = tableView.piecesBarPercent;
|
||||
if (piecesBarPercent > 0.0)
|
||||
{
|
||||
NSRect piecesBarRect, regularBarRect;
|
||||
NSDivideRect(barRect, &piecesBarRect, ®ularBarRect, floor(NSHeight(barRect) * kPiecesTotalPercent * piecesBarPercent), NSMaxYEdge);
|
||||
|
||||
[self drawRegularBar:regularBarRect forTorrent:torrent];
|
||||
[self drawPiecesBar:piecesBarRect forTorrent:torrent];
|
||||
}
|
||||
else
|
||||
{
|
||||
torrent.previousFinishedPieces = nil;
|
||||
|
||||
[self drawRegularBar:barRect forTorrent:torrent];
|
||||
}
|
||||
|
||||
NSColor* borderColor = minimal ? self.fBarMinimalBorderColor : self.fBarBorderColor;
|
||||
[borderColor set];
|
||||
[NSBezierPath strokeRect:NSInsetRect(barRect, 0.5, 0.5)];
|
||||
}
|
||||
|
||||
- (void)drawRegularBar:(NSRect)barRect forTorrent:(Torrent*)torrent
|
||||
{
|
||||
NSRect haveRect, missingRect;
|
||||
NSDivideRect(barRect, &haveRect, &missingRect, round(torrent.progress * NSWidth(barRect)), NSMinXEdge);
|
||||
|
||||
if (!NSIsEmptyRect(haveRect))
|
||||
{
|
||||
if (torrent.active)
|
||||
{
|
||||
if (torrent.checking)
|
||||
{
|
||||
[ProgressGradients.progressYellowGradient drawInRect:haveRect angle:90];
|
||||
}
|
||||
else if (torrent.seeding)
|
||||
{
|
||||
NSRect ratioHaveRect, ratioRemainingRect;
|
||||
NSDivideRect(haveRect, &ratioHaveRect, &ratioRemainingRect, round(torrent.progressStopRatio * NSWidth(haveRect)), NSMinXEdge);
|
||||
|
||||
[ProgressGradients.progressGreenGradient drawInRect:ratioHaveRect angle:90];
|
||||
[ProgressGradients.progressLightGreenGradient drawInRect:ratioRemainingRect angle:90];
|
||||
}
|
||||
else
|
||||
{
|
||||
[ProgressGradients.progressBlueGradient drawInRect:haveRect angle:90];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (torrent.waitingToStart)
|
||||
{
|
||||
if (torrent.allDownloaded)
|
||||
{
|
||||
[ProgressGradients.progressDarkGreenGradient drawInRect:haveRect angle:90];
|
||||
}
|
||||
else
|
||||
{
|
||||
[ProgressGradients.progressDarkBlueGradient drawInRect:haveRect angle:90];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[ProgressGradients.progressGrayGradient drawInRect:haveRect angle:90];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!torrent.allDownloaded)
|
||||
{
|
||||
CGFloat const widthRemaining = round(NSWidth(barRect) * torrent.progressLeft);
|
||||
|
||||
NSRect wantedRect;
|
||||
NSDivideRect(missingRect, &wantedRect, &missingRect, widthRemaining, NSMinXEdge);
|
||||
|
||||
//not-available section
|
||||
if (torrent.active && !torrent.checking && torrent.availableDesired < 1.0 && [self.fDefaults boolForKey:@"DisplayProgressBarAvailable"])
|
||||
{
|
||||
NSRect unavailableRect;
|
||||
NSDivideRect(wantedRect, &wantedRect, &unavailableRect, round(NSWidth(wantedRect) * torrent.availableDesired), NSMinXEdge);
|
||||
|
||||
[ProgressGradients.progressRedGradient drawInRect:unavailableRect angle:90];
|
||||
}
|
||||
|
||||
//remaining section
|
||||
[ProgressGradients.progressWhiteGradient drawInRect:wantedRect angle:90];
|
||||
}
|
||||
|
||||
//unwanted section
|
||||
if (!NSIsEmptyRect(missingRect))
|
||||
{
|
||||
if (!torrent.magnet)
|
||||
{
|
||||
[ProgressGradients.progressLightGrayGradient drawInRect:missingRect angle:90];
|
||||
}
|
||||
else
|
||||
{
|
||||
[ProgressGradients.progressRedGradient drawInRect:missingRect angle:90];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)drawPiecesBar:(NSRect)barRect forTorrent:(Torrent*)torrent
|
||||
{
|
||||
//fill an all-white bar for magnet links
|
||||
if (torrent.magnet)
|
||||
{
|
||||
[[NSColor colorWithCalibratedWhite:1.0 alpha:[self.fDefaults boolForKey:@"SmallView"] ? 0.25 : 1.0] set];
|
||||
NSRectFillUsingOperation(barRect, NSCompositingOperationSourceOver);
|
||||
return;
|
||||
}
|
||||
|
||||
NSInteger pieceCount = MIN(torrent.pieceCount, kMaxPieces);
|
||||
float* piecesPercent = static_cast<float*>(malloc(pieceCount * sizeof(float)));
|
||||
[torrent getAmountFinished:piecesPercent size:pieceCount];
|
||||
|
||||
NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil pixelsWide:pieceCount pixelsHigh:1
|
||||
bitsPerSample:8
|
||||
samplesPerPixel:4
|
||||
hasAlpha:YES
|
||||
isPlanar:NO
|
||||
colorSpaceName:NSCalibratedRGBColorSpace
|
||||
bytesPerRow:0
|
||||
bitsPerPixel:0];
|
||||
|
||||
NSIndexSet* previousFinishedIndexes = torrent.previousFinishedPieces;
|
||||
NSMutableIndexSet* finishedIndexes = [NSMutableIndexSet indexSet];
|
||||
|
||||
for (NSInteger i = 0; i < pieceCount; i++)
|
||||
{
|
||||
NSColor* pieceColor;
|
||||
if (piecesPercent[i] == 1.0f)
|
||||
{
|
||||
if (previousFinishedIndexes && ![previousFinishedIndexes containsIndex:i])
|
||||
{
|
||||
pieceColor = NSColor.orangeColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
pieceColor = self.fBluePieceColor;
|
||||
}
|
||||
[finishedIndexes addIndex:i];
|
||||
}
|
||||
else
|
||||
{
|
||||
pieceColor = [NSColor.whiteColor blendedColorWithFraction:piecesPercent[i] ofColor:self.fBluePieceColor];
|
||||
}
|
||||
|
||||
//it's faster to just set color instead of checking previous color
|
||||
[bitmap setColor:pieceColor atX:i y:0];
|
||||
}
|
||||
|
||||
free(piecesPercent);
|
||||
|
||||
torrent.previousFinishedPieces = finishedIndexes.count > 0 ? finishedIndexes : nil; //don't bother saving if none are complete
|
||||
|
||||
//actually draw image
|
||||
[bitmap drawInRect:barRect fromRect:NSZeroRect operation:NSCompositingOperationSourceOver
|
||||
fraction:([self.fDefaults boolForKey:@"SmallView"] ? 0.25 : 1.0)respectFlipped:YES
|
||||
hints:nil];
|
||||
}
|
||||
|
||||
@end
|
25
macosx/SmallTorrentCell.h
Normal file
25
macosx/SmallTorrentCell.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
// This file Copyright © 2006-2023 Transmission authors and contributors.
|
||||
// It may be used under the MIT (SPDX: MIT) license.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
@class TorrentTableView;
|
||||
|
||||
@interface SmallTorrentCell : NSTableCellView
|
||||
|
||||
@property(nonatomic) IBOutlet NSButton* fActionButton;
|
||||
@property(nonatomic) IBOutlet NSButton* fControlButton;
|
||||
@property(nonatomic) IBOutlet NSButton* fRevealButton;
|
||||
|
||||
@property(nonatomic) IBOutlet NSImageView* fIconView;
|
||||
@property(nonatomic) IBOutlet NSImageView* fGroupIndicatorView;
|
||||
|
||||
@property(nonatomic) IBOutlet NSTextField* fTorrentTitleField;
|
||||
@property(nonatomic) IBOutlet NSTextField* fTorrentStatusField;
|
||||
|
||||
@property(nonatomic) IBOutlet NSView* fTorrentProgressBarView;
|
||||
|
||||
@property(nonatomic) TorrentTableView* fTorrentTableView;
|
||||
|
||||
@end
|
98
macosx/SmallTorrentCell.mm
Normal file
98
macosx/SmallTorrentCell.mm
Normal file
|
@ -0,0 +1,98 @@
|
|||
// This file Copyright © 2006-2023 Transmission authors and contributors.
|
||||
// It may be used under the MIT (SPDX: MIT) license.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#import "SmallTorrentCell.h"
|
||||
#import "ProgressBarView.h"
|
||||
#import "ProgressGradients.h"
|
||||
#import "TorrentTableView.h"
|
||||
#import "Torrent.h"
|
||||
|
||||
@interface SmallTorrentCell ()
|
||||
@property(nonatomic) NSTrackingArea* fTrackingArea;
|
||||
@end
|
||||
|
||||
@implementation SmallTorrentCell
|
||||
|
||||
//draw progress bar
|
||||
- (void)drawRect:(NSRect)dirtyRect
|
||||
{
|
||||
if (self.fTorrentTableView)
|
||||
{
|
||||
NSRect barRect = self.fTorrentProgressBarView.frame;
|
||||
ProgressBarView* progressBar = [[ProgressBarView alloc] init];
|
||||
Torrent* torrent = (Torrent*)self.objectValue;
|
||||
|
||||
[progressBar drawBarInRect:barRect forTableView:self.fTorrentTableView withTorrent:torrent];
|
||||
}
|
||||
|
||||
[super drawRect:dirtyRect];
|
||||
}
|
||||
|
||||
//otherwise progress bar is inverted
|
||||
- (BOOL)isFlipped
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
//show fControlButton and fRevealButton
|
||||
- (void)mouseEntered:(NSEvent*)event
|
||||
{
|
||||
[super mouseEntered:event];
|
||||
|
||||
NSPoint mouseLocation = [self convertPoint:[event locationInWindow] fromView:nil];
|
||||
if (NSPointInRect(mouseLocation, self.fTrackingArea.rect))
|
||||
{
|
||||
[self.fTorrentTableView hoverEventBeganForView:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mouseExited:(NSEvent*)event
|
||||
{
|
||||
[super mouseExited:event];
|
||||
|
||||
NSPoint mouseLocation = [self convertPoint:[event locationInWindow] fromView:nil];
|
||||
if (!NSPointInRect(mouseLocation, self.fTrackingArea.rect))
|
||||
{
|
||||
[self.fTorrentTableView hoverEventEndedForView:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mouseUp:(NSEvent*)event
|
||||
{
|
||||
[super mouseUp:event];
|
||||
[self updateTrackingAreas];
|
||||
}
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
if (self.fTrackingArea != nil)
|
||||
{
|
||||
[self removeTrackingArea:self.fTrackingArea];
|
||||
}
|
||||
|
||||
//tracking rect should not be entire row, but start at fGroupDownloadView
|
||||
NSRect titleRect = self.fTorrentTitleField.frame;
|
||||
CGFloat maxX = NSMaxX(titleRect);
|
||||
NSRect rect = self.bounds;
|
||||
rect.origin.x = maxX;
|
||||
|
||||
NSTrackingAreaOptions opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow);
|
||||
self.fTrackingArea = [[NSTrackingArea alloc] initWithRect:rect options:opts owner:self userInfo:nil];
|
||||
[self addTrackingArea:self.fTrackingArea];
|
||||
|
||||
//check to see if mouse is already within rect
|
||||
NSPoint mouseLocation = [self.window mouseLocationOutsideOfEventStream];
|
||||
mouseLocation = [self.superview convertPoint:mouseLocation fromView:nil];
|
||||
|
||||
if (NSPointInRect(mouseLocation, rect))
|
||||
{
|
||||
[self mouseEntered:[[NSEvent alloc] init]];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self mouseExited:[[NSEvent alloc] init]];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -3,20 +3,23 @@
|
|||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
#import "TorrentTableView.h"
|
||||
|
||||
@interface TorrentCell : NSActionCell
|
||||
@interface TorrentCell : NSTableCellView
|
||||
|
||||
@property(nonatomic) BOOL hover;
|
||||
@property(nonatomic) BOOL hoverControl;
|
||||
@property(nonatomic) BOOL hoverReveal;
|
||||
@property(nonatomic) BOOL hoverAction;
|
||||
@property(nonatomic) IBOutlet NSButton* fActionButton;
|
||||
@property(nonatomic) IBOutlet NSButton* fControlButton;
|
||||
@property(nonatomic) IBOutlet NSButton* fRevealButton;
|
||||
|
||||
- (NSRect)iconRectForBounds:(NSRect)bounds;
|
||||
- (NSRect)actionRectForBounds:(NSRect)bounds;
|
||||
@property(nonatomic) IBOutlet NSImageView* fIconView;
|
||||
@property(nonatomic) IBOutlet NSImageView* fGroupIndicatorView;
|
||||
|
||||
- (void)addTrackingAreasForView:(NSView*)controlView
|
||||
inRect:(NSRect)cellFrame
|
||||
withUserInfo:(NSDictionary*)userInfo
|
||||
mouseLocation:(NSPoint)mouseLocation;
|
||||
@property(nonatomic) IBOutlet NSTextField* fTorrentTitleField;
|
||||
@property(nonatomic) IBOutlet NSTextField* fTorrentProgressField;
|
||||
@property(nonatomic) IBOutlet NSTextField* fTorrentStatusField;
|
||||
|
||||
@property(nonatomic) IBOutlet NSView* fTorrentProgressBarView;
|
||||
|
||||
@property(nonatomic) TorrentTableView* fTorrentTableView;
|
||||
|
||||
@end
|
||||
|
|
|
@ -3,919 +3,31 @@
|
|||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#import "TorrentCell.h"
|
||||
#import "FileNameCell.h"
|
||||
#import "GroupsController.h"
|
||||
#import "NSImageAdditions.h"
|
||||
#import "NSStringAdditions.h"
|
||||
#import "ProgressBarView.h"
|
||||
#import "ProgressGradients.h"
|
||||
#import "Torrent.h"
|
||||
#import "TorrentTableView.h"
|
||||
|
||||
static CGFloat const kBarHeight = 12.0;
|
||||
|
||||
static CGFloat const kImageSizeRegular = 32.0;
|
||||
static CGFloat const kImageSizeMin = 16.0;
|
||||
static CGFloat const kErrorImageSize = 20.0;
|
||||
|
||||
static CGFloat const kGroupImageSizeRegular = 10.0;
|
||||
static CGFloat const kGroupImageSizeMin = 6.0;
|
||||
static CGFloat const kGroupPaddingRegular = 22.0;
|
||||
static CGFloat const kGroupPaddingMin = 14.0;
|
||||
|
||||
static CGFloat const kNormalButtonWidth = 14.0;
|
||||
static CGFloat const kActionButtonWidth = 16.0;
|
||||
|
||||
static CGFloat const kPriorityIconSize = 12.0;
|
||||
|
||||
//ends up being larger than font height
|
||||
static CGFloat const kHeightTitle = 16.0;
|
||||
static CGFloat const kHeightStatus = 12.0;
|
||||
|
||||
static CGFloat const kPaddingHorizontal = 5.0;
|
||||
static CGFloat const kPaddingEdgeMax = 12.0;
|
||||
static CGFloat const kPaddingBetweenButtons = 3.0;
|
||||
static CGFloat const kPaddingBetweenImageAndTitle = kPaddingHorizontal + 1.0;
|
||||
static CGFloat const kPaddingBetweenImageAndBar = kPaddingHorizontal;
|
||||
static CGFloat const kPaddingBetweenTitleAndPriority = 6.0;
|
||||
static CGFloat const kPaddingAboveTitle = 4.0;
|
||||
static CGFloat const kPaddingBetweenTitleAndMinStatus = 3.0;
|
||||
static CGFloat const kPaddingBetweenTitleAndProgress = 1.0;
|
||||
static CGFloat const kPaddingBetweenProgressAndBar = 2.0;
|
||||
static CGFloat const kPaddingBetweenBarAndStatus = 2.0;
|
||||
static CGFloat const kPaddingBetweenBarAndEdgeMin = 3.0;
|
||||
static CGFloat const kPaddingExpansionFrame = 2.0;
|
||||
|
||||
static CGFloat const kPiecesTotalPercent = 0.6;
|
||||
|
||||
static NSInteger const kMaxPieces = 18 * 18;
|
||||
|
||||
static NSMutableParagraphStyle* sParagraphStyle()
|
||||
{
|
||||
NSMutableParagraphStyle* paragraphStyle = [NSParagraphStyle.defaultParagraphStyle mutableCopy];
|
||||
paragraphStyle.lineBreakMode = NSLineBreakByTruncatingMiddle;
|
||||
return paragraphStyle;
|
||||
}
|
||||
static NSDictionary<NSAttributedStringKey, id>* const kTitleAttributes = @{
|
||||
NSFontAttributeName : [NSFont messageFontOfSize:12.0],
|
||||
NSParagraphStyleAttributeName : sParagraphStyle(),
|
||||
NSForegroundColorAttributeName : NSColor.labelColor
|
||||
};
|
||||
static NSDictionary<NSAttributedStringKey, id>* const kStatusAttributes = @{
|
||||
NSFontAttributeName : [NSFont messageFontOfSize:10.0],
|
||||
NSParagraphStyleAttributeName : sParagraphStyle(),
|
||||
NSForegroundColorAttributeName : NSColor.secondaryLabelColor
|
||||
};
|
||||
static NSDictionary<NSAttributedStringKey, id>* const kTitleEmphasizedAttributes = @{
|
||||
NSFontAttributeName : [NSFont messageFontOfSize:12.0],
|
||||
NSParagraphStyleAttributeName : sParagraphStyle(),
|
||||
NSForegroundColorAttributeName : NSColor.whiteColor
|
||||
};
|
||||
static NSDictionary<NSAttributedStringKey, id>* const kStatusEmphasizedAttributes = @{
|
||||
NSFontAttributeName : [NSFont messageFontOfSize:10.0],
|
||||
NSParagraphStyleAttributeName : sParagraphStyle(),
|
||||
NSForegroundColorAttributeName : NSColor.whiteColor
|
||||
};
|
||||
|
||||
@interface TorrentCell ()
|
||||
|
||||
@property(nonatomic) BOOL fTracking;
|
||||
@property(nonatomic) BOOL fMouseDownControlButton;
|
||||
@property(nonatomic) BOOL fMouseDownRevealButton;
|
||||
|
||||
@property(nonatomic, readonly) NSColor* fBarBorderColor;
|
||||
@property(nonatomic, readonly) NSColor* fBluePieceColor;
|
||||
@property(nonatomic, readonly) NSColor* fBarMinimalBorderColor;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TorrentCell
|
||||
|
||||
//only called once and the main table is always needed, so don't worry about releasing
|
||||
- (instancetype)init
|
||||
//draw progress bar
|
||||
- (void)drawRect:(NSRect)dirtyRect
|
||||
{
|
||||
if ((self = [super init]))
|
||||
if (self.fTorrentTableView)
|
||||
{
|
||||
_fBluePieceColor = [NSColor colorWithCalibratedRed:0.0 green:0.4 blue:0.8 alpha:1.0];
|
||||
_fBarBorderColor = [NSColor colorWithCalibratedWhite:0.0 alpha:0.2];
|
||||
_fBarMinimalBorderColor = [NSColor colorWithCalibratedWhite:0.0 alpha:0.015];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
NSRect barRect = self.fTorrentProgressBarView.frame;
|
||||
ProgressBarView* progressBar = [[ProgressBarView alloc] init];
|
||||
Torrent* torrent = (Torrent*)self.objectValue;
|
||||
|
||||
- (id)copyWithZone:(NSZone*)zone
|
||||
{
|
||||
TorrentCell* copy = [super copyWithZone:zone];
|
||||
copy->_fBluePieceColor = _fBluePieceColor;
|
||||
copy->_fBarBorderColor = _fBarBorderColor;
|
||||
copy->_fBarMinimalBorderColor = _fBarMinimalBorderColor;
|
||||
[copy setRepresentedObject:self.representedObject];
|
||||
return copy;
|
||||
}
|
||||
|
||||
- (NSUserDefaults*)fDefaults
|
||||
{
|
||||
return NSUserDefaults.standardUserDefaults;
|
||||
}
|
||||
|
||||
- (NSRect)iconRectForBounds:(NSRect)bounds
|
||||
{
|
||||
BOOL const minimal = [self.fDefaults boolForKey:@"SmallView"];
|
||||
CGFloat const imageSize = minimal ? kImageSizeMin : kImageSizeRegular;
|
||||
CGFloat const padding = minimal ? kGroupPaddingMin : kGroupPaddingRegular;
|
||||
|
||||
return NSMakeRect(NSMinX(bounds) + (padding * 0.5) + kPaddingHorizontal, ceil(NSMidY(bounds) - imageSize * 0.5), imageSize, imageSize);
|
||||
}
|
||||
|
||||
- (NSRect)actionRectForBounds:(NSRect)bounds
|
||||
{
|
||||
NSRect iconRect = [self iconRectForBounds:bounds];
|
||||
NSRect actionRect = [self actionButtonRectForBounds:iconRect];
|
||||
|
||||
return actionRect;
|
||||
}
|
||||
|
||||
- (NSCellHitResult)hitTestForEvent:(NSEvent*)event inRect:(NSRect)cellFrame ofView:(NSView*)controlView
|
||||
{
|
||||
NSPoint point = [controlView convertPoint:event.locationInWindow fromView:nil];
|
||||
|
||||
if (NSMouseInRect(point, [self controlButtonRectForBounds:cellFrame], controlView.flipped) ||
|
||||
NSMouseInRect(point, [self revealButtonRectForBounds:cellFrame], controlView.flipped))
|
||||
{
|
||||
return NSCellHitContentArea | NSCellHitTrackableArea;
|
||||
[progressBar drawBarInRect:barRect forTableView:self.fTorrentTableView withTorrent:torrent];
|
||||
}
|
||||
|
||||
return NSCellHitContentArea;
|
||||
[super drawRect:dirtyRect];
|
||||
}
|
||||
|
||||
+ (BOOL)prefersTrackingUntilMouseUp
|
||||
//otherwise progress bar is inverted
|
||||
- (BOOL)isFlipped
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)trackMouse:(NSEvent*)event inRect:(NSRect)cellFrame ofView:(NSView*)controlView untilMouseUp:(BOOL)flag
|
||||
{
|
||||
self.fTracking = YES;
|
||||
|
||||
self.controlView = controlView;
|
||||
|
||||
NSPoint point = [controlView convertPoint:event.locationInWindow fromView:nil];
|
||||
|
||||
NSRect const controlRect = [self controlButtonRectForBounds:cellFrame];
|
||||
BOOL const checkControl = NSMouseInRect(point, controlRect, controlView.flipped);
|
||||
|
||||
NSRect const revealRect = [self revealButtonRectForBounds:cellFrame];
|
||||
BOOL const checkReveal = NSMouseInRect(point, revealRect, controlView.flipped);
|
||||
|
||||
[(TorrentTableView*)controlView removeTrackingAreas];
|
||||
|
||||
while (event.type != NSEventTypeLeftMouseUp)
|
||||
{
|
||||
point = [controlView convertPoint:event.locationInWindow fromView:nil];
|
||||
|
||||
if (checkControl)
|
||||
{
|
||||
BOOL const inControlButton = NSMouseInRect(point, controlRect, controlView.flipped);
|
||||
if (self.fMouseDownControlButton != inControlButton)
|
||||
{
|
||||
self.fMouseDownControlButton = inControlButton;
|
||||
[controlView setNeedsDisplayInRect:cellFrame];
|
||||
}
|
||||
}
|
||||
else if (checkReveal)
|
||||
{
|
||||
BOOL const inRevealButton = NSMouseInRect(point, revealRect, controlView.flipped);
|
||||
if (self.fMouseDownRevealButton != inRevealButton)
|
||||
{
|
||||
self.fMouseDownRevealButton = inRevealButton;
|
||||
[controlView setNeedsDisplayInRect:cellFrame];
|
||||
}
|
||||
}
|
||||
|
||||
//send events to where necessary
|
||||
if (event.type == NSEventTypeMouseEntered || event.type == NSEventTypeMouseExited)
|
||||
{
|
||||
[NSApp sendEvent:event];
|
||||
}
|
||||
event = [controlView.window nextEventMatchingMask:(NSEventMaskLeftMouseUp | NSEventMaskLeftMouseDragged |
|
||||
NSEventMaskMouseEntered | NSEventMaskMouseExited)];
|
||||
}
|
||||
|
||||
self.fTracking = NO;
|
||||
|
||||
if (self.fMouseDownControlButton)
|
||||
{
|
||||
self.fMouseDownControlButton = NO;
|
||||
|
||||
[(TorrentTableView*)controlView toggleControlForTorrent:self.representedObject];
|
||||
}
|
||||
else if (self.fMouseDownRevealButton)
|
||||
{
|
||||
self.fMouseDownRevealButton = NO;
|
||||
[controlView setNeedsDisplayInRect:cellFrame];
|
||||
|
||||
NSString* location = ((Torrent*)self.representedObject).dataLocation;
|
||||
if (location)
|
||||
{
|
||||
NSURL* file = [NSURL fileURLWithPath:location];
|
||||
[NSWorkspace.sharedWorkspace activateFileViewerSelectingURLs:@[ file ]];
|
||||
}
|
||||
}
|
||||
|
||||
[controlView updateTrackingAreas];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)addTrackingAreasForView:(NSView*)controlView
|
||||
inRect:(NSRect)cellFrame
|
||||
withUserInfo:(NSDictionary*)userInfo
|
||||
mouseLocation:(NSPoint)mouseLocation
|
||||
{
|
||||
NSTrackingAreaOptions const options = NSTrackingEnabledDuringMouseDrag | NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways;
|
||||
|
||||
//whole row
|
||||
if ([self.fDefaults boolForKey:@"SmallView"])
|
||||
{
|
||||
NSTrackingAreaOptions rowOptions = options;
|
||||
if (NSMouseInRect(mouseLocation, cellFrame, controlView.flipped))
|
||||
{
|
||||
rowOptions |= NSTrackingAssumeInside;
|
||||
((TorrentTableView*)controlView).hoverRow = [userInfo[@"Row"] integerValue];
|
||||
}
|
||||
|
||||
NSMutableDictionary* rowInfo = [userInfo mutableCopy];
|
||||
rowInfo[@"Type"] = @"Row";
|
||||
NSTrackingArea* area = [[NSTrackingArea alloc] initWithRect:cellFrame options:rowOptions owner:controlView userInfo:rowInfo];
|
||||
[controlView addTrackingArea:area];
|
||||
}
|
||||
|
||||
//control button
|
||||
NSRect controlButtonRect = [self controlButtonRectForBounds:cellFrame];
|
||||
NSTrackingAreaOptions controlOptions = options;
|
||||
if (NSMouseInRect(mouseLocation, controlButtonRect, controlView.flipped))
|
||||
{
|
||||
controlOptions |= NSTrackingAssumeInside;
|
||||
((TorrentTableView*)controlView).controlButtonHoverRow = [userInfo[@"Row"] integerValue];
|
||||
}
|
||||
|
||||
NSMutableDictionary* controlInfo = [userInfo mutableCopy];
|
||||
controlInfo[@"Type"] = @"Control";
|
||||
NSTrackingArea* area = [[NSTrackingArea alloc] initWithRect:controlButtonRect options:controlOptions owner:controlView
|
||||
userInfo:controlInfo];
|
||||
[controlView addTrackingArea:area];
|
||||
|
||||
//reveal button
|
||||
NSRect revealButtonRect = [self revealButtonRectForBounds:cellFrame];
|
||||
NSTrackingAreaOptions revealOptions = options;
|
||||
if (NSMouseInRect(mouseLocation, revealButtonRect, controlView.flipped))
|
||||
{
|
||||
revealOptions |= NSTrackingAssumeInside;
|
||||
((TorrentTableView*)controlView).revealButtonHoverRow = [userInfo[@"Row"] integerValue];
|
||||
}
|
||||
|
||||
NSMutableDictionary* revealInfo = [userInfo mutableCopy];
|
||||
revealInfo[@"Type"] = @"Reveal";
|
||||
area = [[NSTrackingArea alloc] initWithRect:revealButtonRect options:revealOptions owner:controlView userInfo:revealInfo];
|
||||
[controlView addTrackingArea:area];
|
||||
|
||||
//action button
|
||||
NSRect actionButtonRect = [self actionRectForBounds:cellFrame];
|
||||
NSTrackingAreaOptions actionOptions = options;
|
||||
if (NSMouseInRect(mouseLocation, actionButtonRect, controlView.flipped))
|
||||
{
|
||||
actionOptions |= NSTrackingAssumeInside;
|
||||
((TorrentTableView*)controlView).actionButtonHoverRow = [userInfo[@"Row"] integerValue];
|
||||
}
|
||||
|
||||
NSMutableDictionary* actionInfo = [userInfo mutableCopy];
|
||||
actionInfo[@"Type"] = @"Action";
|
||||
area = [[NSTrackingArea alloc] initWithRect:actionButtonRect options:actionOptions owner:controlView userInfo:actionInfo];
|
||||
[controlView addTrackingArea:area];
|
||||
}
|
||||
|
||||
- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView
|
||||
{
|
||||
Torrent* torrent = self.representedObject;
|
||||
NSAssert(torrent != nil, @"can't have a TorrentCell without a Torrent");
|
||||
|
||||
BOOL const minimal = [self.fDefaults boolForKey:@"SmallView"];
|
||||
|
||||
//bar
|
||||
[self drawBar:minimal ? [self barRectMinForBounds:cellFrame] : [self barRectRegForBounds:cellFrame]];
|
||||
|
||||
//group coloring
|
||||
NSRect const parentRect = [self iconRectForBounds:cellFrame];
|
||||
NSRect iconRect = NSMakeRect(parentRect.origin.x, parentRect.origin.y, parentRect.size.width, parentRect.size.height);
|
||||
|
||||
NSInteger const groupValue = torrent.groupValue;
|
||||
if (groupValue != -1)
|
||||
{
|
||||
NSRect groupRect = [self groupIconRectForBounds:iconRect];
|
||||
NSColor* groupColor = [GroupsController.groups colorForIndex:groupValue];
|
||||
NSImage* icon = [NSImage discIconWithColor:groupColor insetFactor:0];
|
||||
[icon drawInRect:groupRect fromRect:NSZeroRect operation:NSCompositingOperationSourceOver fraction:1.0f];
|
||||
}
|
||||
|
||||
BOOL const error = torrent.anyErrorOrWarning;
|
||||
|
||||
//icon
|
||||
if (!minimal || !(!self.fTracking && self.hoverAction)) //don't show in minimal mode when hovered over
|
||||
{
|
||||
NSImage* icon = (minimal && error) ? [NSImage imageNamed:NSImageNameCaution] : torrent.icon;
|
||||
[icon drawInRect:iconRect fromRect:NSZeroRect operation:NSCompositingOperationSourceOver fraction:1.0 respectFlipped:YES
|
||||
hints:nil];
|
||||
}
|
||||
|
||||
//error badge
|
||||
if (error && !minimal)
|
||||
{
|
||||
NSImage* errorImage = [NSImage imageNamed:NSImageNameCaution];
|
||||
NSRect const errorRect = NSMakeRect(NSMaxX(iconRect) - kErrorImageSize, NSMaxY(iconRect) - kErrorImageSize, kErrorImageSize, kErrorImageSize);
|
||||
[errorImage drawInRect:errorRect fromRect:NSZeroRect operation:NSCompositingOperationSourceOver fraction:1.0
|
||||
respectFlipped:YES
|
||||
hints:nil];
|
||||
}
|
||||
|
||||
AttributesStyle style = self.backgroundStyle == NSBackgroundStyleEmphasized ? AttributesStyleEmphasized : AttributesStyleNormal;
|
||||
|
||||
CGFloat titleRightBound;
|
||||
//minimal status
|
||||
if (minimal)
|
||||
{
|
||||
NSAttributedString* minimalString = [self attributedStatusString:self.minimalStatusString style:style];
|
||||
NSRect minimalStatusRect = [self rectForMinimalStatusWithStringSize:[minimalString size] inBounds:cellFrame];
|
||||
|
||||
if (!self.hover)
|
||||
{
|
||||
[minimalString drawInRect:minimalStatusRect];
|
||||
}
|
||||
|
||||
titleRightBound = NSMinX(minimalStatusRect);
|
||||
}
|
||||
//progress
|
||||
else
|
||||
{
|
||||
NSAttributedString* progressString = [self attributedStatusString:torrent.progressString style:style];
|
||||
NSRect progressRect = [self rectForProgressWithStringInBounds:cellFrame];
|
||||
|
||||
[progressString drawInRect:progressRect];
|
||||
titleRightBound = NSMaxX(cellFrame);
|
||||
}
|
||||
|
||||
if (!minimal || self.hover)
|
||||
{
|
||||
//control button
|
||||
NSString* controlImageSuffix;
|
||||
if (self.fMouseDownControlButton)
|
||||
{
|
||||
controlImageSuffix = @"On";
|
||||
}
|
||||
else if (!self.fTracking && self.hoverControl)
|
||||
{
|
||||
controlImageSuffix = @"Hover";
|
||||
}
|
||||
else
|
||||
{
|
||||
controlImageSuffix = @"Off";
|
||||
}
|
||||
|
||||
NSImage* controlImage;
|
||||
if (torrent.active)
|
||||
{
|
||||
controlImage = [NSImage imageNamed:[@"Pause" stringByAppendingString:controlImageSuffix]];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (NSApp.currentEvent.modifierFlags & NSEventModifierFlagOption)
|
||||
{
|
||||
controlImage = [NSImage imageNamed:[@"ResumeNoWait" stringByAppendingString:controlImageSuffix]];
|
||||
}
|
||||
else if (torrent.waitingToStart)
|
||||
{
|
||||
controlImage = [NSImage imageNamed:[@"Pause" stringByAppendingString:controlImageSuffix]];
|
||||
}
|
||||
else
|
||||
{
|
||||
controlImage = [NSImage imageNamed:[@"Resume" stringByAppendingString:controlImageSuffix]];
|
||||
}
|
||||
}
|
||||
|
||||
NSRect const controlRect = [self controlButtonRectForBounds:cellFrame];
|
||||
[controlImage drawInRect:controlRect fromRect:NSZeroRect operation:NSCompositingOperationSourceOver fraction:1.0
|
||||
respectFlipped:YES
|
||||
hints:nil];
|
||||
if (minimal)
|
||||
{
|
||||
titleRightBound = MIN(titleRightBound, NSMinX(controlRect));
|
||||
}
|
||||
|
||||
//reveal button
|
||||
NSString* revealImageString;
|
||||
if (self.fMouseDownRevealButton)
|
||||
{
|
||||
revealImageString = @"RevealOn";
|
||||
}
|
||||
else if (!self.fTracking && self.hoverReveal)
|
||||
{
|
||||
revealImageString = @"RevealHover";
|
||||
}
|
||||
else
|
||||
{
|
||||
revealImageString = @"RevealOff";
|
||||
}
|
||||
|
||||
NSImage* revealImage = [NSImage imageNamed:revealImageString];
|
||||
[revealImage drawInRect:[self revealButtonRectForBounds:cellFrame] fromRect:NSZeroRect
|
||||
operation:NSCompositingOperationSourceOver
|
||||
fraction:1.0
|
||||
respectFlipped:YES
|
||||
hints:nil];
|
||||
|
||||
//action button
|
||||
#warning image should use new gear
|
||||
if (!self.fTracking && self.hoverAction)
|
||||
{
|
||||
NSImage* actionImage = [NSImage imageNamed:@"ActionHover"];
|
||||
[actionImage drawInRect:[self actionButtonRectForBounds:iconRect] fromRect:NSZeroRect
|
||||
operation:NSCompositingOperationSourceOver
|
||||
fraction:1.0
|
||||
respectFlipped:YES
|
||||
hints:nil];
|
||||
}
|
||||
}
|
||||
|
||||
//title
|
||||
NSAttributedString* titleString = [self attributedTitleWithStyle:style];
|
||||
NSRect titleRect = [self rectForTitleWithStringSize:[titleString size] withRightBound:titleRightBound inBounds:cellFrame
|
||||
minimal:minimal];
|
||||
[titleString drawInRect:titleRect];
|
||||
|
||||
//priority icon
|
||||
if (torrent.priority != TR_PRI_NORMAL)
|
||||
{
|
||||
NSRect const priorityRect = NSMakeRect(
|
||||
NSMaxX(titleRect) + kPaddingBetweenTitleAndPriority,
|
||||
NSMidY(titleRect) - kPriorityIconSize * 0.5,
|
||||
kPriorityIconSize,
|
||||
kPriorityIconSize);
|
||||
|
||||
NSColor* priorityColor = self.backgroundStyle == NSBackgroundStyleEmphasized ? NSColor.whiteColor : NSColor.labelColor;
|
||||
|
||||
NSImage* priorityImage = [[NSImage imageNamed:(torrent.priority == TR_PRI_HIGH ? @"PriorityHighTemplate" : @"PriorityLowTemplate")]
|
||||
imageWithColor:priorityColor];
|
||||
[priorityImage drawInRect:priorityRect fromRect:NSZeroRect operation:NSCompositingOperationSourceOver fraction:1.0
|
||||
respectFlipped:YES
|
||||
hints:nil];
|
||||
}
|
||||
|
||||
//status
|
||||
if (!minimal)
|
||||
{
|
||||
NSAttributedString* statusString = [self attributedStatusString:self.statusString style:style];
|
||||
[statusString drawInRect:[self rectForStatusWithStringInBounds:cellFrame]];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSRect)expansionFrameWithFrame:(NSRect)cellFrame inView:(NSView*)view
|
||||
{
|
||||
BOOL minimal = [self.fDefaults boolForKey:@"SmallView"];
|
||||
|
||||
//this code needs to match the code in drawInteriorWithFrame:withView:
|
||||
CGFloat titleRightBound;
|
||||
if (minimal)
|
||||
{
|
||||
NSAttributedString* minimalString = [self attributedStatusString:self.minimalStatusString style:AttributesStyleNormal];
|
||||
NSRect minimalStatusRect = [self rectForMinimalStatusWithStringSize:[minimalString size] inBounds:cellFrame];
|
||||
|
||||
titleRightBound = NSMinX(minimalStatusRect);
|
||||
|
||||
if (self.hover)
|
||||
{
|
||||
NSRect const controlRect = [self controlButtonRectForBounds:cellFrame];
|
||||
titleRightBound = MIN(titleRightBound, NSMinX(controlRect));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
titleRightBound = NSMaxX(cellFrame);
|
||||
}
|
||||
|
||||
NSAttributedString* titleString = [self attributedTitleWithStyle:AttributesStyleNormal];
|
||||
NSSize titleStringSize = [titleString size];
|
||||
NSRect realRect = [self rectForTitleWithStringSize:titleStringSize withRightBound:titleRightBound inBounds:cellFrame
|
||||
minimal:minimal];
|
||||
|
||||
NSAssert(titleStringSize.width >= NSWidth(realRect), @"Full rect width should not be less than the used title rect width!");
|
||||
|
||||
if (titleStringSize.width > NSWidth(realRect) &&
|
||||
NSMouseInRect([view convertPoint:view.window.mouseLocationOutsideOfEventStream fromView:nil], realRect, view.flipped))
|
||||
{
|
||||
realRect.size.width = titleStringSize.width;
|
||||
return NSInsetRect(realRect, -kPaddingExpansionFrame, -kPaddingExpansionFrame);
|
||||
}
|
||||
|
||||
return NSZeroRect;
|
||||
}
|
||||
|
||||
- (void)drawWithExpansionFrame:(NSRect)cellFrame inView:(NSView*)view
|
||||
{
|
||||
cellFrame.origin.x += kPaddingExpansionFrame;
|
||||
cellFrame.origin.y += kPaddingExpansionFrame;
|
||||
|
||||
NSAttributedString* titleString = [self attributedTitleWithStyle:AttributesStyleNormal];
|
||||
[titleString drawInRect:cellFrame];
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)drawBar:(NSRect)barRect
|
||||
{
|
||||
BOOL const minimal = [self.fDefaults boolForKey:@"SmallView"];
|
||||
|
||||
CGFloat const piecesBarPercent = ((TorrentTableView*)self.controlView).piecesBarPercent;
|
||||
if (piecesBarPercent > 0.0)
|
||||
{
|
||||
NSRect piecesBarRect, regularBarRect;
|
||||
NSDivideRect(barRect, &piecesBarRect, ®ularBarRect, floor(NSHeight(barRect) * kPiecesTotalPercent * piecesBarPercent), NSMaxYEdge);
|
||||
|
||||
[self drawRegularBar:regularBarRect];
|
||||
[self drawPiecesBar:piecesBarRect];
|
||||
}
|
||||
else
|
||||
{
|
||||
((Torrent*)self.representedObject).previousFinishedPieces = nil;
|
||||
|
||||
[self drawRegularBar:barRect];
|
||||
}
|
||||
|
||||
NSColor* borderColor = minimal ? self.fBarMinimalBorderColor : self.fBarBorderColor;
|
||||
[borderColor set];
|
||||
[NSBezierPath strokeRect:NSInsetRect(barRect, 0.5, 0.5)];
|
||||
}
|
||||
|
||||
- (void)drawRegularBar:(NSRect)barRect
|
||||
{
|
||||
Torrent* torrent = self.representedObject;
|
||||
|
||||
NSRect haveRect, missingRect;
|
||||
NSDivideRect(barRect, &haveRect, &missingRect, round(torrent.progress * NSWidth(barRect)), NSMinXEdge);
|
||||
|
||||
if (!NSIsEmptyRect(haveRect))
|
||||
{
|
||||
if (torrent.active)
|
||||
{
|
||||
if (torrent.checking)
|
||||
{
|
||||
[ProgressGradients.progressYellowGradient drawInRect:haveRect angle:90];
|
||||
}
|
||||
else if (torrent.seeding)
|
||||
{
|
||||
NSRect ratioHaveRect, ratioRemainingRect;
|
||||
NSDivideRect(haveRect, &ratioHaveRect, &ratioRemainingRect, round(torrent.progressStopRatio * NSWidth(haveRect)), NSMinXEdge);
|
||||
|
||||
[ProgressGradients.progressGreenGradient drawInRect:ratioHaveRect angle:90];
|
||||
[ProgressGradients.progressLightGreenGradient drawInRect:ratioRemainingRect angle:90];
|
||||
}
|
||||
else
|
||||
{
|
||||
[ProgressGradients.progressBlueGradient drawInRect:haveRect angle:90];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (torrent.waitingToStart)
|
||||
{
|
||||
if (torrent.allDownloaded)
|
||||
{
|
||||
[ProgressGradients.progressDarkGreenGradient drawInRect:haveRect angle:90];
|
||||
}
|
||||
else
|
||||
{
|
||||
[ProgressGradients.progressDarkBlueGradient drawInRect:haveRect angle:90];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[ProgressGradients.progressGrayGradient drawInRect:haveRect angle:90];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!torrent.allDownloaded)
|
||||
{
|
||||
CGFloat const widthRemaining = round(NSWidth(barRect) * torrent.progressLeft);
|
||||
|
||||
NSRect wantedRect;
|
||||
NSDivideRect(missingRect, &wantedRect, &missingRect, widthRemaining, NSMinXEdge);
|
||||
|
||||
//not-available section
|
||||
if (torrent.active && !torrent.checking && torrent.availableDesired < 1.0 && [self.fDefaults boolForKey:@"DisplayProgressBarAvailable"])
|
||||
{
|
||||
NSRect unavailableRect;
|
||||
NSDivideRect(wantedRect, &wantedRect, &unavailableRect, round(NSWidth(wantedRect) * torrent.availableDesired), NSMinXEdge);
|
||||
|
||||
[ProgressGradients.progressRedGradient drawInRect:unavailableRect angle:90];
|
||||
}
|
||||
|
||||
//remaining section
|
||||
[ProgressGradients.progressWhiteGradient drawInRect:wantedRect angle:90];
|
||||
}
|
||||
|
||||
//unwanted section
|
||||
if (!NSIsEmptyRect(missingRect))
|
||||
{
|
||||
if (!torrent.magnet)
|
||||
{
|
||||
[ProgressGradients.progressLightGrayGradient drawInRect:missingRect angle:90];
|
||||
}
|
||||
else
|
||||
{
|
||||
[ProgressGradients.progressRedGradient drawInRect:missingRect angle:90];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)drawPiecesBar:(NSRect)barRect
|
||||
{
|
||||
Torrent* torrent = self.representedObject;
|
||||
|
||||
//fill an all-white bar for magnet links
|
||||
if (torrent.magnet)
|
||||
{
|
||||
[[NSColor colorWithCalibratedWhite:1.0 alpha:[self.fDefaults boolForKey:@"SmallView"] ? 0.25 : 1.0] set];
|
||||
NSRectFillUsingOperation(barRect, NSCompositingOperationSourceOver);
|
||||
return;
|
||||
}
|
||||
|
||||
NSInteger pieceCount = MIN(torrent.pieceCount, kMaxPieces);
|
||||
float* piecesPercent = static_cast<float*>(malloc(pieceCount * sizeof(float)));
|
||||
[torrent getAmountFinished:piecesPercent size:pieceCount];
|
||||
|
||||
NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil pixelsWide:pieceCount pixelsHigh:1
|
||||
bitsPerSample:8
|
||||
samplesPerPixel:4
|
||||
hasAlpha:YES
|
||||
isPlanar:NO
|
||||
colorSpaceName:NSCalibratedRGBColorSpace
|
||||
bytesPerRow:0
|
||||
bitsPerPixel:0];
|
||||
|
||||
NSIndexSet* previousFinishedIndexes = torrent.previousFinishedPieces;
|
||||
NSMutableIndexSet* finishedIndexes = [NSMutableIndexSet indexSet];
|
||||
|
||||
for (NSInteger i = 0; i < pieceCount; i++)
|
||||
{
|
||||
NSColor* pieceColor;
|
||||
if (piecesPercent[i] == 1.0f)
|
||||
{
|
||||
if (previousFinishedIndexes && ![previousFinishedIndexes containsIndex:i])
|
||||
{
|
||||
pieceColor = NSColor.orangeColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
pieceColor = self.fBluePieceColor;
|
||||
}
|
||||
[finishedIndexes addIndex:i];
|
||||
}
|
||||
else
|
||||
{
|
||||
pieceColor = [NSColor.whiteColor blendedColorWithFraction:piecesPercent[i] ofColor:self.fBluePieceColor];
|
||||
}
|
||||
|
||||
//it's faster to just set color instead of checking previous color
|
||||
// faster and non-broken alternative to `[bitmap setColor:pieceColor atX:i y:0]`
|
||||
unsigned char* data = bitmap.bitmapData + (i << 2);
|
||||
data[0] = pieceColor.redComponent * 255;
|
||||
data[1] = pieceColor.greenComponent * 255;
|
||||
data[2] = pieceColor.blueComponent * 255;
|
||||
data[3] = pieceColor.alphaComponent * 255;
|
||||
}
|
||||
|
||||
free(piecesPercent);
|
||||
|
||||
torrent.previousFinishedPieces = finishedIndexes.count > 0 ? finishedIndexes : nil; //don't bother saving if none are complete
|
||||
|
||||
//actually draw image
|
||||
[bitmap drawInRect:barRect fromRect:NSZeroRect operation:NSCompositingOperationSourceOver
|
||||
fraction:[self.fDefaults boolForKey:@"SmallView"] ? 0.25 : 1.0
|
||||
respectFlipped:YES
|
||||
hints:nil];
|
||||
}
|
||||
|
||||
- (NSRect)rectForMinimalStatusWithStringSize:(NSSize)stringSize inBounds:(NSRect)bounds
|
||||
{
|
||||
NSRect result;
|
||||
result.size = stringSize;
|
||||
|
||||
result.origin.x = NSMaxX(bounds) - (kPaddingHorizontal + NSWidth(result) + kPaddingEdgeMax);
|
||||
result.origin.y = ceil(NSMidY(bounds) - NSHeight(result) * 0.5);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSRect)rectForTitleWithStringSize:(NSSize)stringSize
|
||||
withRightBound:(CGFloat)rightBound
|
||||
inBounds:(NSRect)bounds
|
||||
minimal:(BOOL)minimal
|
||||
{
|
||||
NSRect result;
|
||||
result.origin.x = NSMinX(bounds) + kPaddingHorizontal + (minimal ? kImageSizeMin : kImageSizeRegular) + kPaddingBetweenImageAndTitle;
|
||||
result.size.height = kHeightTitle;
|
||||
|
||||
if (minimal)
|
||||
{
|
||||
result.origin.x += kGroupPaddingMin;
|
||||
result.origin.y = ceil(NSMidY(bounds) - NSHeight(result) * 0.5);
|
||||
result.size.width = rightBound - NSMinX(result) - kPaddingBetweenTitleAndMinStatus;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.origin.x += kGroupPaddingRegular;
|
||||
result.origin.y = NSMinY(bounds) + kPaddingAboveTitle;
|
||||
result.size.width = rightBound - NSMinX(result) - kPaddingHorizontal - kPaddingEdgeMax;
|
||||
}
|
||||
|
||||
if (((Torrent*)self.representedObject).priority != TR_PRI_NORMAL)
|
||||
{
|
||||
result.size.width -= kPriorityIconSize + kPaddingBetweenTitleAndPriority;
|
||||
}
|
||||
result.size.width = MIN(NSWidth(result), stringSize.width);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSRect)rectForProgressWithStringInBounds:(NSRect)bounds
|
||||
{
|
||||
NSRect result;
|
||||
result.origin.y = NSMinY(bounds) + kPaddingAboveTitle + kHeightTitle + kPaddingBetweenTitleAndProgress;
|
||||
result.origin.x = NSMinX(bounds) + kPaddingHorizontal + kGroupPaddingRegular + kImageSizeRegular + kPaddingBetweenImageAndTitle;
|
||||
|
||||
result.size.height = kHeightStatus;
|
||||
result.size.width = NSMaxX(bounds) - NSMinX(result) - kPaddingHorizontal;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSRect)rectForStatusWithStringInBounds:(NSRect)bounds
|
||||
{
|
||||
NSRect result;
|
||||
result.origin.y = NSMinY(bounds) + kPaddingAboveTitle + kHeightTitle + kPaddingBetweenTitleAndProgress + kHeightStatus +
|
||||
kPaddingBetweenProgressAndBar + kBarHeight + kPaddingBetweenBarAndStatus;
|
||||
result.origin.x = NSMinX(bounds) + kPaddingHorizontal + kGroupPaddingRegular + kImageSizeRegular + kPaddingBetweenImageAndTitle;
|
||||
|
||||
result.size.height = kHeightStatus;
|
||||
result.size.width = NSMaxX(bounds) - NSMinX(result) - kPaddingHorizontal;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSRect)barRectRegForBounds:(NSRect)bounds
|
||||
{
|
||||
NSRect result;
|
||||
result.size.height = kBarHeight;
|
||||
result.origin.x = NSMinX(bounds) + kPaddingHorizontal + kGroupPaddingRegular + kImageSizeRegular + kPaddingBetweenImageAndBar;
|
||||
result.origin.y = NSMinY(bounds) + kPaddingAboveTitle + kHeightTitle + kPaddingBetweenTitleAndProgress + kHeightStatus +
|
||||
kPaddingBetweenProgressAndBar;
|
||||
|
||||
result.size.width = floor(
|
||||
NSMaxX(bounds) - NSMinX(result) - kPaddingHorizontal - 2.0 * (kPaddingBetweenButtons + kNormalButtonWidth + kPaddingEdgeMax));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSRect)barRectMinForBounds:(NSRect)bounds
|
||||
{
|
||||
NSRect result;
|
||||
result.origin.x = NSMinX(bounds) + kPaddingHorizontal + kImageSizeMin + kGroupPaddingMin + kPaddingBetweenImageAndBar;
|
||||
result.origin.y = NSMinY(bounds) + kPaddingBetweenBarAndEdgeMin;
|
||||
result.size.height = NSHeight(bounds) - 2.0 * kPaddingBetweenBarAndEdgeMin;
|
||||
result.size.width = NSMaxX(bounds) - NSMinX(result) - kPaddingBetweenBarAndEdgeMin - kPaddingEdgeMax;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSRect)controlButtonRectForBounds:(NSRect)bounds
|
||||
{
|
||||
NSRect result;
|
||||
result.size.height = kNormalButtonWidth;
|
||||
result.size.width = kNormalButtonWidth;
|
||||
result.origin.x = NSMaxX(bounds) - (kPaddingHorizontal + kNormalButtonWidth + kPaddingBetweenButtons + kNormalButtonWidth + kPaddingEdgeMax);
|
||||
|
||||
if (![self.fDefaults boolForKey:@"SmallView"])
|
||||
{
|
||||
result.origin.y = NSMinY(bounds) + kPaddingAboveTitle + kHeightTitle - (kNormalButtonWidth - kBarHeight) * 0.5 +
|
||||
kPaddingBetweenTitleAndProgress + kHeightStatus + kPaddingBetweenProgressAndBar;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.origin.y = ceil(NSMidY(bounds) - NSHeight(result) * 0.5);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSRect)revealButtonRectForBounds:(NSRect)bounds
|
||||
{
|
||||
NSRect result;
|
||||
result.size.height = kNormalButtonWidth;
|
||||
result.size.width = kNormalButtonWidth;
|
||||
result.origin.x = NSMaxX(bounds) - (kPaddingHorizontal + kNormalButtonWidth + kPaddingEdgeMax);
|
||||
|
||||
if (![self.fDefaults boolForKey:@"SmallView"])
|
||||
{
|
||||
result.origin.y = NSMinY(bounds) + kPaddingAboveTitle + kHeightTitle - (kNormalButtonWidth - kBarHeight) * 0.5 +
|
||||
kPaddingBetweenTitleAndProgress + kHeightStatus + kPaddingBetweenProgressAndBar;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.origin.y = ceil(NSMidY(bounds) - NSHeight(result) * 0.5);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSRect)actionButtonRectForBounds:(NSRect)bounds
|
||||
{
|
||||
return NSMakeRect(NSMidX(bounds) - kActionButtonWidth * 0.5, NSMidY(bounds) - kActionButtonWidth * 0.5, kActionButtonWidth, kActionButtonWidth);
|
||||
}
|
||||
|
||||
- (NSRect)groupIconRectForBounds:(NSRect)bounds
|
||||
{
|
||||
BOOL const minimal = [self.fDefaults boolForKey:@"SmallView"];
|
||||
CGFloat const imageSize = minimal ? kGroupImageSizeMin : kGroupImageSizeRegular;
|
||||
CGFloat const padding = minimal ? kGroupPaddingMin + 2 : kGroupPaddingRegular + 1.5;
|
||||
|
||||
return NSMakeRect(NSMinX(bounds) - padding * 0.5, NSMidY(bounds) - imageSize * 0.5, imageSize, imageSize);
|
||||
}
|
||||
|
||||
- (NSAttributedString*)attributedTitleWithStyle:(AttributesStyle)style
|
||||
{
|
||||
NSString* title = ((Torrent*)self.representedObject).name;
|
||||
return [[NSAttributedString alloc] initWithString:title
|
||||
attributes:style == AttributesStyleEmphasized ? kTitleEmphasizedAttributes : kTitleAttributes];
|
||||
}
|
||||
|
||||
- (NSAttributedString*)attributedStatusString:(NSString*)string style:(AttributesStyle)style
|
||||
{
|
||||
return [[NSAttributedString alloc] initWithString:string
|
||||
attributes:style == AttributesStyleEmphasized ? kStatusEmphasizedAttributes : kStatusAttributes];
|
||||
}
|
||||
|
||||
- (NSString*)buttonString
|
||||
{
|
||||
if (self.fMouseDownRevealButton || (!self.fTracking && self.hoverReveal))
|
||||
{
|
||||
return NSLocalizedString(@"Show the data file in Finder", "Torrent cell -> button info");
|
||||
}
|
||||
else if (self.fMouseDownControlButton || (!self.fTracking && self.hoverControl))
|
||||
{
|
||||
Torrent* torrent = self.representedObject;
|
||||
if (torrent.active)
|
||||
return NSLocalizedString(@"Pause the transfer", "Torrent Table -> tooltip");
|
||||
else
|
||||
{
|
||||
if (NSApp.currentEvent.modifierFlags & NSEventModifierFlagOption)
|
||||
{
|
||||
return NSLocalizedString(@"Resume the transfer right away", "Torrent cell -> button info");
|
||||
}
|
||||
else if (torrent.waitingToStart)
|
||||
{
|
||||
return NSLocalizedString(@"Stop waiting to start", "Torrent cell -> button info");
|
||||
}
|
||||
else
|
||||
{
|
||||
return NSLocalizedString(@"Resume the transfer", "Torrent cell -> button info");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!self.fTracking && self.hoverAction)
|
||||
{
|
||||
return NSLocalizedString(@"Change transfer settings", "Torrent Table -> tooltip");
|
||||
}
|
||||
else
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString*)statusString
|
||||
{
|
||||
NSString* buttonString;
|
||||
if ((buttonString = self.buttonString))
|
||||
{
|
||||
return buttonString;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ((Torrent*)self.representedObject).statusString;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString*)minimalStatusString
|
||||
{
|
||||
Torrent* torrent = self.representedObject;
|
||||
return [self.fDefaults boolForKey:@"DisplaySmallStatusRegular"] ? torrent.shortStatusString : torrent.remainingTimeString;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
8
macosx/TorrentCellActionButton.h
Normal file
8
macosx/TorrentCellActionButton.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
// This file Copyright © 2006-2023 Transmission authors and contributors.
|
||||
// It may be used under the MIT (SPDX: MIT) license.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface TorrentCellActionButton : NSButton
|
||||
@end
|
87
macosx/TorrentCellActionButton.mm
Normal file
87
macosx/TorrentCellActionButton.mm
Normal file
|
@ -0,0 +1,87 @@
|
|||
// This file Copyright © 2006-2023 Transmission authors and contributors.
|
||||
// It may be used under the MIT (SPDX: MIT) license.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#import "TorrentCellActionButton.h"
|
||||
#import "TorrentTableView.h"
|
||||
#import "Torrent.h"
|
||||
|
||||
@interface TorrentCellActionButton ()
|
||||
@property(nonatomic) NSTrackingArea* fTrackingArea;
|
||||
@property(nonatomic) NSImage* fImage;
|
||||
@property(nonatomic) NSImage* fAlternativeImage;
|
||||
@property(nonatomic) TorrentTableView* torrentTableView;
|
||||
@property(nonatomic) NSUserDefaults* fDefaults;
|
||||
@end
|
||||
|
||||
@implementation TorrentCellActionButton
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
self.fDefaults = NSUserDefaults.standardUserDefaults;
|
||||
self.fImage = self.image;
|
||||
|
||||
// hide image by default and show only on hover
|
||||
self.fAlternativeImage = [[NSImage alloc] init];
|
||||
self.image = self.fAlternativeImage;
|
||||
|
||||
// disable button click highlighting
|
||||
[self.cell setHighlightsBy:NSNoCellMask];
|
||||
}
|
||||
|
||||
- (void)setupTorrentTableView
|
||||
{
|
||||
if (!self.torrentTableView)
|
||||
{
|
||||
self.torrentTableView = (TorrentTableView*)[[[self superview] superview] superview];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mouseEntered:(NSEvent*)event
|
||||
{
|
||||
[super mouseEntered:event];
|
||||
|
||||
self.image = self.fImage;
|
||||
|
||||
[self setupTorrentTableView];
|
||||
[self.torrentTableView hoverEventBeganForView:self];
|
||||
}
|
||||
|
||||
- (void)mouseExited:(NSEvent*)event
|
||||
{
|
||||
[super mouseExited:event];
|
||||
|
||||
self.image = self.fAlternativeImage;
|
||||
|
||||
[self setupTorrentTableView];
|
||||
[self.torrentTableView hoverEventEndedForView:self];
|
||||
}
|
||||
|
||||
- (void)mouseDown:(NSEvent*)event
|
||||
{
|
||||
//when filterbar is shown, we need to remove focus otherwise action fails
|
||||
[self.window makeFirstResponder:self.torrentTableView];
|
||||
|
||||
[super mouseDown:event];
|
||||
|
||||
BOOL minimal = [self.fDefaults boolForKey:@"SmallView"];
|
||||
if (!minimal)
|
||||
{
|
||||
[self setupTorrentTableView];
|
||||
[self.torrentTableView hoverEventEndedForView:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
if (self.fTrackingArea != nil)
|
||||
{
|
||||
[self removeTrackingArea:self.fTrackingArea];
|
||||
}
|
||||
|
||||
NSTrackingAreaOptions opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways);
|
||||
self.fTrackingArea = [[NSTrackingArea alloc] initWithRect:self.bounds options:opts owner:self userInfo:nil];
|
||||
[self addTrackingArea:self.fTrackingArea];
|
||||
}
|
||||
|
||||
@end
|
11
macosx/TorrentCellControlButton.h
Normal file
11
macosx/TorrentCellControlButton.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
// This file Copyright © 2006-2023 Transmission authors and contributors.
|
||||
// It may be used under the MIT (SPDX: MIT) license.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface TorrentCellControlButton : NSButton
|
||||
|
||||
- (void)resetImage;
|
||||
|
||||
@end
|
107
macosx/TorrentCellControlButton.mm
Normal file
107
macosx/TorrentCellControlButton.mm
Normal file
|
@ -0,0 +1,107 @@
|
|||
// This file Copyright © 2006-2023 Transmission authors and contributors.
|
||||
// It may be used under the MIT (SPDX: MIT) license.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#import "TorrentCellControlButton.h"
|
||||
#import "TorrentTableView.h"
|
||||
#import "Torrent.h"
|
||||
|
||||
@interface TorrentCellControlButton ()
|
||||
@property(nonatomic) NSTrackingArea* fTrackingArea;
|
||||
@property(nonatomic, copy) NSString* controlImageSuffix;
|
||||
@property(nonatomic) TorrentTableView* torrentTableView;
|
||||
@end
|
||||
|
||||
@implementation TorrentCellControlButton
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
self.controlImageSuffix = @"Off";
|
||||
[self updateImage];
|
||||
}
|
||||
|
||||
- (void)resetImage
|
||||
{
|
||||
self.controlImageSuffix = @"Off";
|
||||
[self updateImage];
|
||||
}
|
||||
|
||||
- (void)setupTorrentTableView
|
||||
{
|
||||
if (!self.torrentTableView)
|
||||
{
|
||||
self.torrentTableView = (TorrentTableView*)[[[self superview] superview] superview];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mouseEntered:(NSEvent*)event
|
||||
{
|
||||
[super mouseEntered:event];
|
||||
self.controlImageSuffix = @"Hover";
|
||||
[self updateImage];
|
||||
|
||||
[self.torrentTableView hoverEventBeganForView:self];
|
||||
}
|
||||
|
||||
- (void)mouseExited:(NSEvent*)event
|
||||
{
|
||||
[super mouseExited:event];
|
||||
self.controlImageSuffix = @"Off";
|
||||
[self updateImage];
|
||||
|
||||
[self.torrentTableView hoverEventEndedForView:self];
|
||||
}
|
||||
|
||||
- (void)mouseDown:(NSEvent*)event
|
||||
{
|
||||
//when filterbar is shown, we need to remove focus otherwise action fails
|
||||
[self.window makeFirstResponder:self.torrentTableView];
|
||||
|
||||
[super mouseDown:event];
|
||||
self.controlImageSuffix = @"On";
|
||||
[self updateImage];
|
||||
|
||||
[self.torrentTableView hoverEventEndedForView:self];
|
||||
}
|
||||
|
||||
- (void)updateImage
|
||||
{
|
||||
[self setupTorrentTableView];
|
||||
|
||||
NSImage* controlImage;
|
||||
Torrent* torrent = [self.torrentTableView itemAtRow:[self.torrentTableView rowForView:self]];
|
||||
if (torrent.active)
|
||||
{
|
||||
controlImage = [NSImage imageNamed:[@"Pause" stringByAppendingString:self.controlImageSuffix]];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (NSApp.currentEvent.modifierFlags & NSEventModifierFlagOption)
|
||||
{
|
||||
controlImage = [NSImage imageNamed:[@"ResumeNoWait" stringByAppendingString:self.controlImageSuffix]];
|
||||
}
|
||||
else if (torrent.waitingToStart)
|
||||
{
|
||||
controlImage = [NSImage imageNamed:[@"Pause" stringByAppendingString:self.controlImageSuffix]];
|
||||
}
|
||||
else
|
||||
{
|
||||
controlImage = [NSImage imageNamed:[@"Resume" stringByAppendingString:self.controlImageSuffix]];
|
||||
}
|
||||
}
|
||||
self.image = controlImage;
|
||||
}
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
if (self.fTrackingArea != nil)
|
||||
{
|
||||
[self removeTrackingArea:self.fTrackingArea];
|
||||
}
|
||||
|
||||
NSTrackingAreaOptions opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways);
|
||||
self.fTrackingArea = [[NSTrackingArea alloc] initWithRect:self.bounds options:opts owner:self userInfo:nil];
|
||||
[self addTrackingArea:self.fTrackingArea];
|
||||
}
|
||||
|
||||
@end
|
8
macosx/TorrentCellRevealButton.h
Normal file
8
macosx/TorrentCellRevealButton.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
// This file Copyright © 2006-2023 Transmission authors and contributors.
|
||||
// It may be used under the MIT (SPDX: MIT) license.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface TorrentCellRevealButton : NSButton
|
||||
@end
|
79
macosx/TorrentCellRevealButton.mm
Normal file
79
macosx/TorrentCellRevealButton.mm
Normal file
|
@ -0,0 +1,79 @@
|
|||
// This file Copyright © 2006-2023 Transmission authors and contributors.
|
||||
// It may be used under the MIT (SPDX: MIT) license.
|
||||
// License text can be found in the licenses/ folder.
|
||||
|
||||
#import "TorrentCellRevealButton.h"
|
||||
#import "TorrentTableView.h"
|
||||
|
||||
@interface TorrentCellRevealButton ()
|
||||
@property(nonatomic) NSTrackingArea* fTrackingArea;
|
||||
@property(nonatomic, copy) NSString* revealImageString;
|
||||
@property(nonatomic) TorrentTableView* torrentTableView;
|
||||
@end
|
||||
|
||||
@implementation TorrentCellRevealButton
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
self.revealImageString = @"RevealOff";
|
||||
[self updateImage];
|
||||
}
|
||||
|
||||
- (void)setupTorrentTableView
|
||||
{
|
||||
if (!self.torrentTableView)
|
||||
{
|
||||
self.torrentTableView = (TorrentTableView*)[[[self superview] superview] superview];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mouseEntered:(NSEvent*)event
|
||||
{
|
||||
[super mouseEntered:event];
|
||||
self.revealImageString = @"RevealHover";
|
||||
[self updateImage];
|
||||
|
||||
[self.torrentTableView hoverEventBeganForView:self];
|
||||
}
|
||||
|
||||
- (void)mouseExited:(NSEvent*)event
|
||||
{
|
||||
[super mouseExited:event];
|
||||
self.revealImageString = @"RevealOff";
|
||||
[self updateImage];
|
||||
|
||||
[self.torrentTableView hoverEventEndedForView:self];
|
||||
}
|
||||
|
||||
- (void)mouseDown:(NSEvent*)event
|
||||
{
|
||||
//when filterbar is shown, we need to remove focus otherwise action fails
|
||||
[self.window makeFirstResponder:self.torrentTableView];
|
||||
|
||||
[super mouseDown:event];
|
||||
self.revealImageString = @"RevealOn";
|
||||
[self updateImage];
|
||||
}
|
||||
|
||||
- (void)updateImage
|
||||
{
|
||||
[self setupTorrentTableView];
|
||||
|
||||
NSImage* revealImage = [NSImage imageNamed:self.revealImageString];
|
||||
self.image = revealImage;
|
||||
[self setNeedsDisplay:YES];
|
||||
}
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
{
|
||||
if (self.fTrackingArea != nil)
|
||||
{
|
||||
[self removeTrackingArea:self.fTrackingArea];
|
||||
}
|
||||
|
||||
NSTrackingAreaOptions opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways);
|
||||
self.fTrackingArea = [[NSTrackingArea alloc] initWithRect:self.bounds options:opts owner:self userInfo:nil];
|
||||
[self addTrackingArea:self.fTrackingArea];
|
||||
}
|
||||
|
||||
@end
|
|
@ -10,19 +10,15 @@ extern const CGFloat kGroupSeparatorHeight;
|
|||
|
||||
@interface TorrentTableView : NSOutlineView<NSOutlineViewDelegate, NSAnimationDelegate, NSPopoverDelegate>
|
||||
|
||||
- (void)reloadVisibleRows;
|
||||
|
||||
- (BOOL)isGroupCollapsed:(NSInteger)value;
|
||||
- (void)removeCollapsedGroup:(NSInteger)value;
|
||||
- (void)removeAllCollapsedGroups;
|
||||
- (void)saveCollapsedGroups;
|
||||
|
||||
- (void)removeTrackingAreas;
|
||||
@property(nonatomic) NSInteger hoverRow;
|
||||
@property(nonatomic) NSInteger controlButtonHoverRow;
|
||||
@property(nonatomic) NSInteger revealButtonHoverRow;
|
||||
@property(nonatomic) NSInteger actionButtonHoverRow;
|
||||
- (void)restoreSelectionIndexes;
|
||||
|
||||
- (void)selectValues:(NSArray*)values;
|
||||
@property(nonatomic, readonly) NSArray* selectedValues;
|
||||
@property(nonatomic, readonly) NSArray<Torrent*>* selectedTorrents;
|
||||
|
||||
- (NSRect)iconRectForRow:(NSInteger)row;
|
||||
|
@ -30,9 +26,14 @@ extern const CGFloat kGroupSeparatorHeight;
|
|||
- (void)copy:(id)sender;
|
||||
- (void)paste:(id)sender;
|
||||
|
||||
- (void)toggleControlForTorrent:(Torrent*)torrent;
|
||||
- (void)hoverEventBeganForView:(id)view;
|
||||
- (void)hoverEventEndedForView:(id)view;
|
||||
|
||||
- (void)displayTorrentActionPopoverForEvent:(NSEvent*)event;
|
||||
- (void)toggleGroupRowRatio;
|
||||
|
||||
- (IBAction)toggleControlForTorrent:(id)sender;
|
||||
|
||||
- (IBAction)displayTorrentActionPopover:(id)sender;
|
||||
|
||||
- (IBAction)setQuickLimitMode:(id)sender;
|
||||
- (void)setQuickLimit:(id)sender;
|
||||
|
|
|
@ -12,12 +12,19 @@
|
|||
#import "NSStringAdditions.h"
|
||||
#import "Torrent.h"
|
||||
#import "TorrentCell.h"
|
||||
#import "SmallTorrentCell.h"
|
||||
#import "GroupCell.h"
|
||||
#import "TorrentGroup.h"
|
||||
#import "GroupTextCell.h"
|
||||
#import "GroupsController.h"
|
||||
#import "NSImageAdditions.h"
|
||||
#import "TorrentCellActionButton.h"
|
||||
#import "TorrentCellControlButton.h"
|
||||
#import "TorrentCellRevealButton.h"
|
||||
|
||||
CGFloat const kGroupSeparatorHeight = 18.0;
|
||||
|
||||
static NSInteger const kMaxGroup = 999999;
|
||||
static CGFloat const kErrorImageSize = 20.0;
|
||||
|
||||
//eliminate when Lion-only
|
||||
typedef NS_ENUM(NSUInteger, ActionMenuTag) {
|
||||
|
@ -38,9 +45,6 @@ static NSTimeInterval const kToggleProgressSeconds = 0.175;
|
|||
|
||||
@property(nonatomic) IBOutlet Controller* fController;
|
||||
|
||||
@property(nonatomic) TorrentCell* fTorrentCell;
|
||||
@property(nonatomic) GroupTextCell* fGroupTextCell;
|
||||
|
||||
@property(nonatomic, readonly) NSUserDefaults* fDefaults;
|
||||
|
||||
@property(nonatomic, readonly) NSMutableIndexSet* fCollapsedGroups;
|
||||
|
@ -48,7 +52,7 @@ static NSTimeInterval const kToggleProgressSeconds = 0.175;
|
|||
@property(nonatomic) IBOutlet NSMenu* fContextRow;
|
||||
@property(nonatomic) IBOutlet NSMenu* fContextNoRow;
|
||||
|
||||
@property(nonatomic) NSArray* fSelectedValues;
|
||||
@property(nonatomic) NSIndexSet* fSelectedRowIndexes;
|
||||
|
||||
@property(nonatomic) IBOutlet NSMenu* fActionMenu;
|
||||
@property(nonatomic) IBOutlet NSMenu* fUploadMenu;
|
||||
|
@ -64,6 +68,8 @@ static NSTimeInterval const kToggleProgressSeconds = 0.175;
|
|||
@property(nonatomic) BOOL fActionPopoverShown;
|
||||
@property(nonatomic) NSView* fPositioningView;
|
||||
|
||||
@property(nonatomic) NSDictionary* fHoverEventDict;
|
||||
|
||||
@end
|
||||
|
||||
@implementation TorrentTableView
|
||||
|
@ -74,9 +80,6 @@ static NSTimeInterval const kToggleProgressSeconds = 0.175;
|
|||
{
|
||||
_fDefaults = NSUserDefaults.standardUserDefaults;
|
||||
|
||||
_fTorrentCell = [[TorrentCell alloc] init];
|
||||
_fGroupTextCell = [[GroupTextCell alloc] init];
|
||||
|
||||
NSData* groupData;
|
||||
if ((groupData = [_fDefaults dataForKey:@"CollapsedGroupIndexes"]))
|
||||
{
|
||||
|
@ -93,14 +96,10 @@ static NSTimeInterval const kToggleProgressSeconds = 0.175;
|
|||
_fCollapsedGroups = [[NSMutableIndexSet alloc] init];
|
||||
}
|
||||
|
||||
_hoverRow = -1;
|
||||
_controlButtonHoverRow = -1;
|
||||
_revealButtonHoverRow = -1;
|
||||
_actionButtonHoverRow = -1;
|
||||
|
||||
_fActionPopoverShown = NO;
|
||||
|
||||
self.delegate = self;
|
||||
self.indentationPerLevel = 0;
|
||||
|
||||
_piecesBarPercent = [_fDefaults boolForKey:@"PiecesBar"] ? 1.0 : 0.0;
|
||||
|
||||
|
@ -120,19 +119,74 @@ static NSTimeInterval const kToggleProgressSeconds = 0.175;
|
|||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
//set group columns to show ratio, needs to be in awakeFromNib to size columns correctly
|
||||
[self setGroupStatusColumns];
|
||||
|
||||
//disable highlight color and set manually in drawRow
|
||||
[self setSelectionHighlightStyle:NSTableViewSelectionHighlightStyleNone];
|
||||
|
||||
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(refreshTorrentTable) name:@"RefreshTorrentTable"
|
||||
object:nil];
|
||||
[NSNotificationCenter.defaultCenter addObserver:self selector:@selector(setNeedsDisplay) name:@"RefreshTorrentTable" object:nil];
|
||||
}
|
||||
|
||||
- (void)refreshTorrentTable
|
||||
//make sure we don't lose selection on manual reloads
|
||||
- (void)reloadData
|
||||
{
|
||||
self.needsDisplay = YES;
|
||||
[super reloadData];
|
||||
[self restoreSelectionIndexes];
|
||||
}
|
||||
|
||||
- (void)reloadVisibleRows
|
||||
{
|
||||
NSRect visibleRect = self.visibleRect;
|
||||
NSRange range = [self rowsInRect:visibleRect];
|
||||
|
||||
//since we use floating group rows, we need some magic to find visible group rows
|
||||
if ([self.fDefaults boolForKey:@"SortByGroup"])
|
||||
{
|
||||
NSInteger location = range.location;
|
||||
NSInteger length = range.length;
|
||||
NSRange fullRange = NSMakeRange(0, length + location);
|
||||
NSIndexSet* fullIndexSet = [NSIndexSet indexSetWithIndexesInRange:fullRange];
|
||||
NSMutableIndexSet* visibleIndexSet = [[NSMutableIndexSet alloc] init];
|
||||
|
||||
[fullIndexSet enumerateIndexesUsingBlock:^(NSUInteger row, BOOL* stop) {
|
||||
id rowView = [self rowViewAtRow:row makeIfNecessary:NO];
|
||||
if ([rowView isGroupRowStyle])
|
||||
{
|
||||
[visibleIndexSet addIndex:row];
|
||||
}
|
||||
else if (NSIntersectsRect(visibleRect, [self rectOfRow:row]))
|
||||
{
|
||||
[visibleIndexSet addIndex:row];
|
||||
}
|
||||
}];
|
||||
|
||||
[self reloadDataForRowIndexes:visibleIndexSet columnIndexes:[NSIndexSet indexSetWithIndex:0]];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self reloadDataForRowIndexes:[NSIndexSet indexSetWithIndexesInRange:range] columnIndexes:[NSIndexSet indexSetWithIndex:0]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)reloadDataForRowIndexes:(NSIndexSet*)rowIndexes columnIndexes:(NSIndexSet*)columnIndexes
|
||||
{
|
||||
[super reloadDataForRowIndexes:rowIndexes columnIndexes:columnIndexes];
|
||||
|
||||
//redraw fControlButton
|
||||
BOOL minimal = [self.fDefaults boolForKey:@"SmallView"];
|
||||
[rowIndexes enumerateIndexesUsingBlock:^(NSUInteger row, BOOL* stop) {
|
||||
id rowView = [self rowViewAtRow:row makeIfNecessary:NO];
|
||||
if (![rowView isGroupRowStyle])
|
||||
{
|
||||
if (minimal)
|
||||
{
|
||||
SmallTorrentCell* smallCell = [self viewAtColumn:0 row:row makeIfNecessary:NO];
|
||||
[(TorrentCellControlButton*)smallCell.fControlButton resetImage];
|
||||
}
|
||||
else
|
||||
{
|
||||
TorrentCell* torrentCell = [self viewAtColumn:0 row:row makeIfNecessary:NO];
|
||||
[(TorrentCellControlButton*)torrentCell.fControlButton resetImage];
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
[self restoreSelectionIndexes];
|
||||
}
|
||||
|
||||
- (BOOL)isGroupCollapsed:(NSInteger)value
|
||||
|
@ -168,9 +222,11 @@ static NSTimeInterval const kToggleProgressSeconds = 0.175;
|
|||
|
||||
- (BOOL)outlineView:(NSOutlineView*)outlineView isGroupItem:(id)item
|
||||
{
|
||||
//return no and style the groupItem cell manually in willDisplayCell
|
||||
//otherwise we get unwanted padding before each group header
|
||||
return NO;
|
||||
if ([item isKindOfClass:[Torrent class]])
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (CGFloat)outlineView:(NSOutlineView*)outlineView heightOfRowByItem:(id)item
|
||||
|
@ -178,119 +234,207 @@ static NSTimeInterval const kToggleProgressSeconds = 0.175;
|
|||
return [item isKindOfClass:[Torrent class]] ? self.rowHeight : kGroupSeparatorHeight;
|
||||
}
|
||||
|
||||
- (NSCell*)outlineView:(NSOutlineView*)outlineView dataCellForTableColumn:(NSTableColumn*)tableColumn item:(id)item
|
||||
{
|
||||
BOOL const group = ![item isKindOfClass:[Torrent class]];
|
||||
if (!tableColumn)
|
||||
{
|
||||
return !group ? self.fTorrentCell : nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString* ident = tableColumn.identifier;
|
||||
NSArray* imageColumns = @[ @"Color", @"DL Image", @"UL Image" ];
|
||||
if (![imageColumns containsObject:ident])
|
||||
{
|
||||
return group ? self.fGroupTextCell : nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
return group ? [tableColumn dataCellForRow:[self rowForItem:item]] : nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)outlineView:(NSOutlineView*)outlineView
|
||||
willDisplayCell:(id)cell
|
||||
forTableColumn:(NSTableColumn*)tableColumn
|
||||
item:(id)item
|
||||
- (NSView*)outlineView:(NSOutlineView*)outlineView viewForTableColumn:(NSTableColumn*)tableColumn item:(id)item
|
||||
{
|
||||
if ([item isKindOfClass:[Torrent class]])
|
||||
{
|
||||
if (!tableColumn)
|
||||
Torrent* torrent = (Torrent*)item;
|
||||
BOOL const minimal = [self.fDefaults boolForKey:@"SmallView"];
|
||||
BOOL const error = torrent.anyErrorOrWarning;
|
||||
|
||||
if (minimal)
|
||||
{
|
||||
TorrentCell* torrentCell = cell;
|
||||
torrentCell.representedObject = item;
|
||||
SmallTorrentCell* smallCell = [outlineView makeViewWithIdentifier:@"SmallTorrentCell" owner:self];
|
||||
smallCell.fTorrentTableView = self;
|
||||
|
||||
NSInteger const row = [self rowForItem:item];
|
||||
torrentCell.hover = (row == self.hoverRow);
|
||||
torrentCell.hoverControl = (row == self.controlButtonHoverRow);
|
||||
torrentCell.hoverReveal = (row == self.revealButtonHoverRow);
|
||||
torrentCell.hoverAction = (row == self.actionButtonHoverRow);
|
||||
//set this so that we can draw bar in smallCell drawRect
|
||||
smallCell.objectValue = torrent;
|
||||
|
||||
// if cell is selected, set backgroundStyle
|
||||
// then can provide alternate font color in TorrentCell - drawInteriorWithFrame
|
||||
NSIndexSet* selectedRowIndexes = self.selectedRowIndexes;
|
||||
if ([selectedRowIndexes containsIndex:row])
|
||||
smallCell.fTorrentTitleField.stringValue = torrent.name;
|
||||
|
||||
//set torrent icon
|
||||
smallCell.fIconView.image = error ? [NSImage imageNamed:NSImageNameCaution] : torrent.icon;
|
||||
|
||||
smallCell.fTorrentStatusField.stringValue = [self.fDefaults boolForKey:@"DisplaySmallStatusRegular"] ?
|
||||
torrent.shortStatusString :
|
||||
torrent.remainingTimeString;
|
||||
;
|
||||
smallCell.fActionButton.action = @selector(displayTorrentActionPopover:);
|
||||
|
||||
NSInteger const groupValue = torrent.groupValue;
|
||||
NSImage* groupImage;
|
||||
if (groupValue != -1)
|
||||
{
|
||||
torrentCell.backgroundStyle = NSBackgroundStyleEmphasized;
|
||||
if (![self.fDefaults boolForKey:@"SortByGroup"])
|
||||
{
|
||||
NSColor* groupColor = [GroupsController.groups colorForIndex:groupValue];
|
||||
groupImage = [NSImage discIconWithColor:groupColor insetFactor:0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ([cell isKindOfClass:[GroupTextCell class]])
|
||||
{
|
||||
GroupTextCell* groupCell = cell;
|
||||
|
||||
// if cell is selected, set selected flag so we can style the title in GroupTextCell drawInteriorWithFrame
|
||||
NSInteger const row = [self rowForItem:item];
|
||||
NSIndexSet* selectedRowIndexes = self.selectedRowIndexes;
|
||||
groupCell.selected = [selectedRowIndexes containsIndex:row];
|
||||
}
|
||||
}
|
||||
}
|
||||
smallCell.fGroupIndicatorView.image = groupImage;
|
||||
|
||||
//we override row highlighting because we are custom drawing the group rows
|
||||
//see isGroupItem
|
||||
- (void)drawRow:(NSInteger)row clipRect:(NSRect)clipRect
|
||||
{
|
||||
NSColor* highlightColor = nil;
|
||||
smallCell.fControlButton.action = @selector(toggleControlForTorrent:);
|
||||
smallCell.fRevealButton.action = @selector(revealTorrentFile:);
|
||||
|
||||
id item = [self itemAtRow:row];
|
||||
if (self.fHoverEventDict)
|
||||
{
|
||||
NSInteger row = [self rowForItem:item];
|
||||
NSInteger hoverRow = [self.fHoverEventDict[@"row"] integerValue];
|
||||
|
||||
//we only highlight torrent cells
|
||||
if ([item isKindOfClass:[Torrent class]])
|
||||
{
|
||||
//use system highlight color when Transmission is active
|
||||
if (self == [self.window firstResponder] && [self.window isMainWindow] && [self.window isKeyWindow])
|
||||
{
|
||||
highlightColor = [NSColor alternateSelectedControlColor];
|
||||
if (row == hoverRow)
|
||||
{
|
||||
smallCell.fTorrentStatusField.hidden = YES;
|
||||
smallCell.fControlButton.hidden = NO;
|
||||
smallCell.fRevealButton.hidden = NO;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
smallCell.fTorrentStatusField.hidden = NO;
|
||||
smallCell.fControlButton.hidden = YES;
|
||||
smallCell.fRevealButton.hidden = YES;
|
||||
}
|
||||
|
||||
//redraw buttons
|
||||
[smallCell.fControlButton display];
|
||||
[smallCell.fRevealButton display];
|
||||
|
||||
return smallCell;
|
||||
}
|
||||
else
|
||||
{
|
||||
highlightColor = [NSColor disabledControlTextColor];
|
||||
TorrentCell* torrentCell = [outlineView makeViewWithIdentifier:@"TorrentCell" owner:self];
|
||||
torrentCell.fTorrentTableView = self;
|
||||
|
||||
//set this so that we can draw bar in torrentCell drawRect
|
||||
torrentCell.objectValue = torrent;
|
||||
|
||||
torrentCell.fTorrentTitleField.stringValue = torrent.name;
|
||||
torrentCell.fTorrentProgressField.stringValue = torrent.progressString;
|
||||
|
||||
//set torrent icon
|
||||
NSImage* fileImage = torrent.icon;
|
||||
|
||||
//error badge
|
||||
if (error)
|
||||
{
|
||||
NSRect frame = torrentCell.fIconView.frame;
|
||||
NSImage* resultImage = [[NSImage alloc] initWithSize:NSMakeSize(frame.size.height, frame.size.width)];
|
||||
[resultImage lockFocus];
|
||||
|
||||
//draw fileImage
|
||||
[fileImage drawAtPoint:NSZeroPoint fromRect:NSZeroRect operation:NSCompositingOperationSourceOver fraction:1.0];
|
||||
|
||||
//overlay error badge
|
||||
NSImage* errorImage = [NSImage imageNamed:NSImageNameCaution];
|
||||
NSRect const errorRect = NSMakeRect(frame.origin.x, 0, kErrorImageSize, kErrorImageSize);
|
||||
[errorImage drawInRect:errorRect fromRect:NSZeroRect operation:NSCompositingOperationSourceOver fraction:1.0
|
||||
respectFlipped:YES
|
||||
hints:nil];
|
||||
|
||||
[resultImage unlockFocus];
|
||||
|
||||
torrentCell.fIconView.image = resultImage;
|
||||
}
|
||||
else
|
||||
{
|
||||
torrentCell.fIconView.image = fileImage;
|
||||
}
|
||||
|
||||
NSString* status;
|
||||
if (self.fHoverEventDict)
|
||||
{
|
||||
NSInteger row = [self rowForItem:item];
|
||||
NSInteger hoverRow = [self.fHoverEventDict[@"row"] integerValue];
|
||||
|
||||
if (row == hoverRow)
|
||||
{
|
||||
status = self.fHoverEventDict[@"string"];
|
||||
}
|
||||
}
|
||||
torrentCell.fTorrentStatusField.stringValue = status ? status : torrent.statusString;
|
||||
torrentCell.fActionButton.action = @selector(displayTorrentActionPopover:);
|
||||
|
||||
NSInteger const groupValue = torrent.groupValue;
|
||||
NSImage* groupImage;
|
||||
if (groupValue != -1)
|
||||
{
|
||||
if (![self.fDefaults boolForKey:@"SortByGroup"])
|
||||
{
|
||||
NSColor* groupColor = [GroupsController.groups colorForIndex:groupValue];
|
||||
groupImage = [NSImage discIconWithColor:groupColor insetFactor:0];
|
||||
}
|
||||
}
|
||||
|
||||
torrentCell.fGroupIndicatorView.image = groupImage;
|
||||
|
||||
torrentCell.fControlButton.action = @selector(toggleControlForTorrent:);
|
||||
torrentCell.fRevealButton.action = @selector(revealTorrentFile:);
|
||||
|
||||
//redraw buttons
|
||||
[torrentCell.fControlButton display];
|
||||
[torrentCell.fRevealButton display];
|
||||
|
||||
return torrentCell;
|
||||
}
|
||||
|
||||
NSIndexSet* selectedRowIndexes = [self selectedRowIndexes];
|
||||
if ([selectedRowIndexes containsIndex:row])
|
||||
{
|
||||
[highlightColor setFill];
|
||||
NSRectFill([self rectOfRow:row]);
|
||||
}
|
||||
}
|
||||
|
||||
[super drawRow:row clipRect:clipRect];
|
||||
}
|
||||
|
||||
- (NSRect)frameOfCellAtColumn:(NSInteger)column row:(NSInteger)row
|
||||
{
|
||||
if (column == -1)
|
||||
{
|
||||
return [self rectOfRow:row];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSRect rect = [super frameOfCellAtColumn:column row:row];
|
||||
TorrentGroup* group = (TorrentGroup*)item;
|
||||
GroupCell* groupCell = [outlineView makeViewWithIdentifier:@"GroupCell" owner:self];
|
||||
|
||||
//adjust placement for proper vertical alignment
|
||||
if (column == [self columnWithIdentifier:@"Group"])
|
||||
NSInteger groupIndex = group.groupIndex;
|
||||
|
||||
NSColor* groupColor = groupIndex != -1 ? [GroupsController.groups colorForIndex:groupIndex] :
|
||||
[NSColor colorWithWhite:1.0 alpha:0];
|
||||
groupCell.fGroupIndicatorView.image = [NSImage discIconWithColor:groupColor insetFactor:0];
|
||||
|
||||
NSString* groupName = groupIndex != -1 ? [GroupsController.groups nameForIndex:groupIndex] :
|
||||
NSLocalizedString(@"No Group", "Group table row");
|
||||
|
||||
NSInteger row = [self rowForItem:item];
|
||||
if ([self isRowSelected:row])
|
||||
{
|
||||
rect.size.height -= 1.0f;
|
||||
NSMutableAttributedString* string = [[NSMutableAttributedString alloc] initWithString:groupName];
|
||||
NSDictionary* attributes = @{
|
||||
NSFontAttributeName : [NSFont boldSystemFontOfSize:11.0],
|
||||
NSForegroundColorAttributeName : [NSColor labelColor]
|
||||
};
|
||||
|
||||
[string addAttributes:attributes range:NSMakeRange(0, string.length)];
|
||||
groupCell.fGroupTitleField.attributedStringValue = string;
|
||||
}
|
||||
else
|
||||
{
|
||||
groupCell.fGroupTitleField.stringValue = groupName;
|
||||
}
|
||||
|
||||
return rect;
|
||||
groupCell.fGroupDownloadField.stringValue = [NSString stringForSpeed:group.downloadRate];
|
||||
groupCell.fGroupDownloadView.image = [NSImage imageNamed:@"DownArrowGroupTemplate"];
|
||||
|
||||
BOOL displayGroupRowRatio = [self.fDefaults boolForKey:@"DisplayGroupRowRatio"];
|
||||
groupCell.fGroupDownloadField.hidden = displayGroupRowRatio;
|
||||
groupCell.fGroupDownloadView.hidden = displayGroupRowRatio;
|
||||
|
||||
if (displayGroupRowRatio)
|
||||
{
|
||||
groupCell.fGroupUploadAndRatioView.image = [NSImage imageNamed:@"YingYangGroupTemplate"];
|
||||
groupCell.fGroupUploadAndRatioView.image.accessibilityDescription = NSLocalizedString(@"Ratio", "Torrent -> status image");
|
||||
|
||||
groupCell.fGroupUploadAndRatioField.stringValue = [NSString stringForRatio:group.ratio];
|
||||
}
|
||||
else
|
||||
{
|
||||
groupCell.fGroupUploadAndRatioView.image = [NSImage imageNamed:@"UpArrowGroupTemplate"];
|
||||
groupCell.fGroupUploadAndRatioView.image.accessibilityDescription = NSLocalizedString(@"UL", "Torrent -> status image");
|
||||
|
||||
groupCell.fGroupUploadAndRatioField.stringValue = [NSString stringForSpeed:group.uploadRate];
|
||||
}
|
||||
|
||||
return groupCell;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString*)outlineView:(NSOutlineView*)outlineView typeSelectStringForTableColumn:(NSTableColumn*)tableColumn item:(id)item
|
||||
|
@ -342,160 +486,10 @@ static NSTimeInterval const kToggleProgressSeconds = 0.175;
|
|||
}
|
||||
}
|
||||
|
||||
- (void)updateTrackingAreas
|
||||
- (void)outlineViewSelectionDidChange:(NSNotification*)notification
|
||||
{
|
||||
[super updateTrackingAreas];
|
||||
[self removeTrackingAreas];
|
||||
|
||||
NSRange const rows = [self rowsInRect:self.visibleRect];
|
||||
if (rows.length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NSPoint mouseLocation = [self convertPoint:self.window.mouseLocationOutsideOfEventStream fromView:nil];
|
||||
for (NSUInteger row = rows.location; row < NSMaxRange(rows); row++)
|
||||
{
|
||||
if (![[self itemAtRow:row] isKindOfClass:[Torrent class]])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
NSDictionary* userInfo = @{ @"Row" : @(row) };
|
||||
TorrentCell* cell = (TorrentCell*)[self preparedCellAtColumn:-1 row:row];
|
||||
[cell addTrackingAreasForView:self inRect:[self rectOfRow:row] withUserInfo:userInfo mouseLocation:mouseLocation];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)removeTrackingAreas
|
||||
{
|
||||
_hoverRow = -1;
|
||||
_controlButtonHoverRow = -1;
|
||||
_revealButtonHoverRow = -1;
|
||||
_actionButtonHoverRow = -1;
|
||||
|
||||
for (NSTrackingArea* area in self.trackingAreas)
|
||||
{
|
||||
if (area.owner == self && area.userInfo[@"Row"])
|
||||
{
|
||||
[self removeTrackingArea:area];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setHoverRow:(NSInteger)row
|
||||
{
|
||||
NSAssert([self.fDefaults boolForKey:@"SmallView"], @"cannot set a hover row when not in compact view");
|
||||
|
||||
_hoverRow = row;
|
||||
if (row >= 0)
|
||||
{
|
||||
[self setNeedsDisplayInRect:[self rectOfRow:row]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setControlButtonHoverRow:(NSInteger)row
|
||||
{
|
||||
_controlButtonHoverRow = row;
|
||||
if (row >= 0)
|
||||
{
|
||||
[self setNeedsDisplayInRect:[self rectOfRow:row]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setRevealButtonHoverRow:(NSInteger)row
|
||||
{
|
||||
_revealButtonHoverRow = row;
|
||||
if (row >= 0)
|
||||
{
|
||||
[self setNeedsDisplayInRect:[self rectOfRow:row]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setActionButtonHoverRow:(NSInteger)row
|
||||
{
|
||||
_actionButtonHoverRow = row;
|
||||
if (row >= 0)
|
||||
{
|
||||
[self setNeedsDisplayInRect:[self rectOfRow:row]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mouseEntered:(NSEvent*)event
|
||||
{
|
||||
NSDictionary* dict = (NSDictionary*)event.userData;
|
||||
|
||||
NSNumber* row;
|
||||
if ((row = dict[@"Row"]))
|
||||
{
|
||||
NSInteger rowVal = row.integerValue;
|
||||
NSString* type = dict[@"Type"];
|
||||
if ([type isEqualToString:@"Action"])
|
||||
{
|
||||
_actionButtonHoverRow = rowVal;
|
||||
}
|
||||
else if ([type isEqualToString:@"Control"])
|
||||
{
|
||||
_controlButtonHoverRow = rowVal;
|
||||
}
|
||||
else if ([type isEqualToString:@"Reveal"])
|
||||
{
|
||||
_revealButtonHoverRow = rowVal;
|
||||
}
|
||||
else
|
||||
{
|
||||
_hoverRow = rowVal;
|
||||
if (![self.fDefaults boolForKey:@"SmallView"])
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[self setNeedsDisplayInRect:[self rectOfRow:rowVal]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)mouseExited:(NSEvent*)event
|
||||
{
|
||||
NSDictionary* dict = (NSDictionary*)event.userData;
|
||||
|
||||
NSNumber* row;
|
||||
if ((row = dict[@"Row"]))
|
||||
{
|
||||
NSString* type = dict[@"Type"];
|
||||
if ([type isEqualToString:@"Action"])
|
||||
{
|
||||
_actionButtonHoverRow = -1;
|
||||
}
|
||||
else if ([type isEqualToString:@"Control"])
|
||||
{
|
||||
_controlButtonHoverRow = -1;
|
||||
}
|
||||
else if ([type isEqualToString:@"Reveal"])
|
||||
{
|
||||
_revealButtonHoverRow = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
_hoverRow = -1;
|
||||
if (![self.fDefaults boolForKey:@"SmallView"])
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[self setNeedsDisplayInRect:[self rectOfRow:row.integerValue]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)outlineViewSelectionIsChanging:(NSNotification*)notification
|
||||
{
|
||||
#warning eliminate when view-based?
|
||||
//if pushing a button, don't change the selected rows
|
||||
if (self.fSelectedValues)
|
||||
{
|
||||
[self selectValues:self.fSelectedValues];
|
||||
}
|
||||
self.fSelectedRowIndexes = self.selectedRowIndexes;
|
||||
[self reloadVisibleRows];
|
||||
}
|
||||
|
||||
- (void)outlineViewItemDidExpand:(NSNotification*)notification
|
||||
|
@ -532,42 +526,16 @@ static NSTimeInterval const kToggleProgressSeconds = 0.175;
|
|||
NSPoint point = [self convertPoint:event.locationInWindow fromView:nil];
|
||||
NSInteger const row = [self rowAtPoint:point];
|
||||
|
||||
//check to toggle group status before anything else
|
||||
if ([self pointInGroupStatusRect:point])
|
||||
{
|
||||
[self.fDefaults setBool:![self.fDefaults boolForKey:@"DisplayGroupRowRatio"] forKey:@"DisplayGroupRowRatio"];
|
||||
[self setGroupStatusColumns];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
BOOL const pushed = row != -1 &&
|
||||
(self.actionButtonHoverRow == row || self.revealButtonHoverRow == row || self.controlButtonHoverRow == row);
|
||||
|
||||
//if pushing a button, don't change the selected rows
|
||||
if (pushed)
|
||||
{
|
||||
self.fSelectedValues = self.selectedValues;
|
||||
}
|
||||
|
||||
[super mouseDown:event];
|
||||
|
||||
self.fSelectedValues = nil;
|
||||
|
||||
//avoid weird behavior when showing menu by doing this after mouse down
|
||||
if (row != -1 && self.actionButtonHoverRow == row)
|
||||
id item = nil;
|
||||
if (row != -1)
|
||||
{
|
||||
#warning maybe make appear on mouse down
|
||||
[self displayTorrentActionPopoverForEvent:event];
|
||||
item = [self itemAtRow:row];
|
||||
}
|
||||
else if (!pushed && event.clickCount == 2) //double click
|
||||
{
|
||||
id item = nil;
|
||||
if (row != -1)
|
||||
{
|
||||
item = [self itemAtRow:row];
|
||||
}
|
||||
|
||||
if (event.clickCount == 2) //double click
|
||||
{
|
||||
if (!item || [item isKindOfClass:[Torrent class]])
|
||||
{
|
||||
[self.fController showInfo:nil];
|
||||
|
@ -584,52 +552,12 @@ static NSTimeInterval const kToggleProgressSeconds = 0.175;
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)selectValues:(NSArray*)values
|
||||
{
|
||||
NSMutableIndexSet* indexSet = [NSMutableIndexSet indexSet];
|
||||
|
||||
for (id item in values)
|
||||
else if ([self pointInGroupStatusRect:point])
|
||||
{
|
||||
if ([item isKindOfClass:[Torrent class]])
|
||||
{
|
||||
NSInteger const index = [self rowForItem:item];
|
||||
if (index != -1)
|
||||
{
|
||||
[indexSet addIndex:index];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TorrentGroup* group = (TorrentGroup*)item;
|
||||
NSInteger const groupIndex = group.groupIndex;
|
||||
for (NSInteger i = 0; i < self.numberOfRows; i++)
|
||||
{
|
||||
id tableItem = [self itemAtRow:i];
|
||||
if ([tableItem isKindOfClass:[TorrentGroup class]] && groupIndex == ((TorrentGroup*)tableItem).groupIndex)
|
||||
{
|
||||
[indexSet addIndex:i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//we check for this here rather than in the GroupCell
|
||||
//as using floating group rows causes all sorts of weirdness...
|
||||
[self toggleGroupRowRatio];
|
||||
}
|
||||
|
||||
[self selectRowIndexes:indexSet byExtendingSelection:NO];
|
||||
}
|
||||
|
||||
- (NSArray*)selectedValues
|
||||
{
|
||||
NSIndexSet* selectedIndexes = self.selectedRowIndexes;
|
||||
NSMutableArray* values = [NSMutableArray arrayWithCapacity:selectedIndexes.count];
|
||||
|
||||
for (NSUInteger i = selectedIndexes.firstIndex; i != NSNotFound; i = [selectedIndexes indexGreaterThanIndex:i])
|
||||
{
|
||||
[values addObject:[self itemAtRow:i]];
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
- (NSArray<Torrent*>*)selectedTorrents
|
||||
|
@ -676,6 +604,11 @@ static NSTimeInterval const kToggleProgressSeconds = 0.175;
|
|||
}
|
||||
}
|
||||
|
||||
- (void)restoreSelectionIndexes
|
||||
{
|
||||
[self selectRowIndexes:self.fSelectedRowIndexes byExtendingSelection:NO];
|
||||
}
|
||||
|
||||
//make sure that the pause buttons become orange when holding down the option key
|
||||
- (void)flagsChanged:(NSEvent*)event
|
||||
{
|
||||
|
@ -708,7 +641,24 @@ static NSTimeInterval const kToggleProgressSeconds = 0.175;
|
|||
|
||||
- (NSRect)iconRectForRow:(NSInteger)row
|
||||
{
|
||||
return [self.fTorrentCell iconRectForBounds:[self rectOfRow:row]];
|
||||
BOOL minimal = [self.fDefaults boolForKey:@"SmallView"];
|
||||
NSRect rect;
|
||||
|
||||
if (minimal)
|
||||
{
|
||||
SmallTorrentCell* smallCell = [self viewAtColumn:0 row:row makeIfNecessary:NO];
|
||||
rect = smallCell.fActionButton.frame;
|
||||
}
|
||||
else
|
||||
{
|
||||
TorrentCell* torrentCell = [self viewAtColumn:0 row:row makeIfNecessary:NO];
|
||||
rect = torrentCell.fIconView.frame;
|
||||
}
|
||||
|
||||
NSRect rowRect = [self rectOfRow:row];
|
||||
rect.origin.y += rowRect.origin.y;
|
||||
rect.origin.x += self.intercellSpacing.width;
|
||||
return rect;
|
||||
}
|
||||
|
||||
- (BOOL)acceptsFirstResponder
|
||||
|
@ -799,8 +749,101 @@ static NSTimeInterval const kToggleProgressSeconds = 0.175;
|
|||
return YES;
|
||||
}
|
||||
|
||||
- (void)toggleControlForTorrent:(Torrent*)torrent
|
||||
- (void)hoverEventBeganForView:(id)view
|
||||
{
|
||||
NSInteger row = [self rowForView:view];
|
||||
Torrent* torrent = [self itemAtRow:row];
|
||||
|
||||
BOOL minimal = [self.fDefaults boolForKey:@"SmallView"];
|
||||
if (minimal)
|
||||
{
|
||||
if ([view isKindOfClass:[SmallTorrentCell class]])
|
||||
{
|
||||
self.fHoverEventDict = @{ @"row" : [NSNumber numberWithInteger:row] };
|
||||
}
|
||||
else if ([view isKindOfClass:[TorrentCellActionButton class]])
|
||||
{
|
||||
SmallTorrentCell* smallCell = [self viewAtColumn:0 row:row makeIfNecessary:NO];
|
||||
smallCell.fIconView.hidden = YES;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString* statusString;
|
||||
if ([view isKindOfClass:[TorrentCellRevealButton class]])
|
||||
{
|
||||
statusString = NSLocalizedString(@"Show the data file in Finder", "Torrent cell -> button info");
|
||||
}
|
||||
else if ([view isKindOfClass:[TorrentCellControlButton class]])
|
||||
{
|
||||
if (torrent.active)
|
||||
statusString = NSLocalizedString(@"Pause the transfer", "Torrent Table -> tooltip");
|
||||
else
|
||||
{
|
||||
if (NSApp.currentEvent.modifierFlags & NSEventModifierFlagOption)
|
||||
{
|
||||
statusString = NSLocalizedString(@"Resume the transfer right away", "Torrent cell -> button info");
|
||||
}
|
||||
else if (torrent.waitingToStart)
|
||||
{
|
||||
statusString = NSLocalizedString(@"Stop waiting to start", "Torrent cell -> button info");
|
||||
}
|
||||
else
|
||||
{
|
||||
statusString = NSLocalizedString(@"Resume the transfer", "Torrent cell -> button info");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ([view isKindOfClass:[TorrentCellActionButton class]])
|
||||
{
|
||||
statusString = NSLocalizedString(@"Change transfer settings", "Torrent Table -> tooltip");
|
||||
}
|
||||
|
||||
if (statusString)
|
||||
{
|
||||
self.fHoverEventDict = @{ @"string" : statusString, @"row" : [NSNumber numberWithInteger:row] };
|
||||
}
|
||||
}
|
||||
|
||||
[self reloadVisibleRows];
|
||||
}
|
||||
|
||||
- (void)hoverEventEndedForView:(id)view
|
||||
{
|
||||
NSInteger row = [self rowForView:[view superview]];
|
||||
|
||||
BOOL update = YES;
|
||||
BOOL minimal = [self.fDefaults boolForKey:@"SmallView"];
|
||||
if (minimal)
|
||||
{
|
||||
if (minimal && ![view isKindOfClass:[SmallTorrentCell class]])
|
||||
{
|
||||
if ([view isKindOfClass:[TorrentCellActionButton class]])
|
||||
{
|
||||
SmallTorrentCell* smallCell = [self viewAtColumn:0 row:row makeIfNecessary:NO];
|
||||
smallCell.fIconView.hidden = NO;
|
||||
}
|
||||
update = NO;
|
||||
}
|
||||
}
|
||||
|
||||
if (update)
|
||||
{
|
||||
self.fHoverEventDict = nil;
|
||||
[self reloadDataForRowIndexes:[NSIndexSet indexSetWithIndex:row] columnIndexes:[NSIndexSet indexSetWithIndex:0]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)toggleGroupRowRatio
|
||||
{
|
||||
BOOL displayGroupRowRatio = [self.fDefaults boolForKey:@"DisplayGroupRowRatio"];
|
||||
[self.fDefaults setBool:!displayGroupRowRatio forKey:@"DisplayGroupRowRatio"];
|
||||
[self reloadVisibleRows];
|
||||
}
|
||||
|
||||
- (IBAction)toggleControlForTorrent:(id)sender
|
||||
{
|
||||
Torrent* torrent = [self itemAtRow:[self rowForView:[sender superview]]];
|
||||
if (torrent.active)
|
||||
{
|
||||
[self.fController stopTorrents:@[ torrent ]];
|
||||
|
@ -822,22 +865,26 @@ static NSTimeInterval const kToggleProgressSeconds = 0.175;
|
|||
}
|
||||
}
|
||||
|
||||
- (void)displayTorrentActionPopoverForEvent:(NSEvent*)event
|
||||
- (IBAction)revealTorrentFile:(id)sender
|
||||
{
|
||||
NSInteger const row = [self rowAtPoint:[self convertPoint:event.locationInWindow fromView:nil]];
|
||||
if (row < 0)
|
||||
Torrent* torrent = [self itemAtRow:[self rowForView:[sender superview]]];
|
||||
NSString* location = torrent.dataLocation;
|
||||
if (location)
|
||||
{
|
||||
return;
|
||||
NSURL* file = [NSURL fileURLWithPath:location];
|
||||
[NSWorkspace.sharedWorkspace activateFileViewerSelectingURLs:@[ file ]];
|
||||
}
|
||||
}
|
||||
|
||||
NSRect const rect = [self.fTorrentCell actionRectForBounds:[self rectOfRow:row]];
|
||||
|
||||
- (IBAction)displayTorrentActionPopover:(id)sender
|
||||
{
|
||||
if (self.fActionPopoverShown)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Torrent* torrent = [self itemAtRow:row];
|
||||
Torrent* torrent = [self itemAtRow:[self rowForView:[sender superview]]];
|
||||
NSRect rect = [sender bounds];
|
||||
|
||||
NSPopover* popover = [[NSPopover alloc] init];
|
||||
popover.behavior = NSPopoverBehaviorTransient;
|
||||
|
@ -845,7 +892,7 @@ static NSTimeInterval const kToggleProgressSeconds = 0.175;
|
|||
popover.contentViewController = infoViewController;
|
||||
popover.delegate = self;
|
||||
|
||||
[popover showRelativeToRect:rect ofView:self preferredEdge:NSMaxYEdge];
|
||||
[popover showRelativeToRect:rect ofView:sender preferredEdge:NSMaxYEdge];
|
||||
[infoViewController setInfoForTorrents:@[ torrent ]];
|
||||
[infoViewController updateInfo];
|
||||
|
||||
|
@ -862,7 +909,7 @@ static NSTimeInterval const kToggleProgressSeconds = 0.175;
|
|||
}
|
||||
else
|
||||
{
|
||||
[popover showRelativeToRect:rect ofView:self preferredEdge:NSMaxYEdge];
|
||||
[popover showRelativeToRect:rect ofView:sender preferredEdge:NSMaxYEdge];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1112,22 +1159,17 @@ static NSTimeInterval const kToggleProgressSeconds = 0.175;
|
|||
- (BOOL)pointInGroupStatusRect:(NSPoint)point
|
||||
{
|
||||
NSInteger row = [self rowAtPoint:point];
|
||||
if (row < 0 || [[self itemAtRow:row] isKindOfClass:[Torrent class]])
|
||||
if (![[self itemAtRow:row] isKindOfClass:[TorrentGroup class]])
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSString* ident = (self.tableColumns[[self columnAtPoint:point]]).identifier;
|
||||
return [ident isEqualToString:@"UL"] || [ident isEqualToString:@"UL Image"] || [ident isEqualToString:@"DL"] ||
|
||||
[ident isEqualToString:@"DL Image"];
|
||||
}
|
||||
//check if click is within the status/ratio rect
|
||||
GroupCell* groupCell = [self viewAtColumn:0 row:row makeIfNecessary:NO];
|
||||
NSRect titleRect = groupCell.fGroupTitleField.frame;
|
||||
CGFloat maxX = NSMaxX(titleRect);
|
||||
|
||||
- (void)setGroupStatusColumns
|
||||
{
|
||||
BOOL const ratio = [self.fDefaults boolForKey:@"DisplayGroupRowRatio"];
|
||||
|
||||
[self tableColumnWithIdentifier:@"DL"].hidden = ratio;
|
||||
[self tableColumnWithIdentifier:@"DL Image"].hidden = ratio;
|
||||
return point.x > maxX;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
Loading…
Reference in a new issue