macos: View-based FileOutlineView (#7760)

Signed-off-by: Dzmitry Neviadomski <nevack.d@gmail.com>
This commit is contained in:
Dzmitry Neviadomski
2025-11-09 20:21:42 +03:00
committed by GitHub
parent 909fdad807
commit d1985b05c6
21 changed files with 877 additions and 634 deletions

View File

@@ -16,7 +16,6 @@
2856E0656A49F2665D69E760 /* benc.h in Headers */ = {isa = PBXBuildFile; fileRef = 2856E0656A49F2665D69E761 /* benc.h */; }; 2856E0656A49F2665D69E760 /* benc.h in Headers */ = {isa = PBXBuildFile; fileRef = 2856E0656A49F2665D69E761 /* benc.h */; };
2B9BA6C508B488FE586A0AB0 /* torrents.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2B9BA6C508B488FE586A0AB1 /* torrents.cc */; }; 2B9BA6C508B488FE586A0AB0 /* torrents.cc in Sources */ = {isa = PBXBuildFile; fileRef = 2B9BA6C508B488FE586A0AB1 /* torrents.cc */; };
2B9BA6C508B488FE586A0AB2 /* torrents.h in Headers */ = {isa = PBXBuildFile; fileRef = 2B9BA6C508B488FE586A0AB3 /* torrents.h */; }; 2B9BA6C508B488FE586A0AB2 /* torrents.h in Headers */ = {isa = PBXBuildFile; fileRef = 2B9BA6C508B488FE586A0AB3 /* torrents.h */; };
35F373030C2DA89000DAA8F2 /* FilePriorityCell.mm in Sources */ = {isa = PBXBuildFile; fileRef = 35F373010C2DA88F00DAA8F2 /* FilePriorityCell.mm */; };
37D5E15E2AEFE47B00D1ADB3 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; 37D5E15E2AEFE47B00D1ADB3 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
3C7A11970D0B2EE300B5701F /* getgateway.c in Sources */ = {isa = PBXBuildFile; fileRef = 3C7A11910D0B2EE300B5701F /* getgateway.c */; }; 3C7A11970D0B2EE300B5701F /* getgateway.c in Sources */ = {isa = PBXBuildFile; fileRef = 3C7A11910D0B2EE300B5701F /* getgateway.c */; };
3C7A11980D0B2EE300B5701F /* getgateway.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C7A11920D0B2EE300B5701F /* getgateway.h */; }; 3C7A11980D0B2EE300B5701F /* getgateway.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C7A11920D0B2EE300B5701F /* getgateway.h */; };
@@ -125,7 +124,6 @@
A222E9870E6B21D9009FB003 /* BlocklistDownloaderViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = A222E9860E6B21D9009FB003 /* BlocklistDownloaderViewController.mm */; }; A222E9870E6B21D9009FB003 /* BlocklistDownloaderViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = A222E9860E6B21D9009FB003 /* BlocklistDownloaderViewController.mm */; };
A222EA7B0E6C32C4009FB003 /* BlocklistScheduler.mm in Sources */ = {isa = PBXBuildFile; fileRef = A222EA7A0E6C32C4009FB003 /* BlocklistScheduler.mm */; }; A222EA7B0E6C32C4009FB003 /* BlocklistScheduler.mm in Sources */ = {isa = PBXBuildFile; fileRef = A222EA7A0E6C32C4009FB003 /* BlocklistScheduler.mm */; };
A225A4C0187E369C00CDE823 /* ShareToolbarItem.mm in Sources */ = {isa = PBXBuildFile; fileRef = A225A4BF187E369C00CDE823 /* ShareToolbarItem.mm */; }; A225A4C0187E369C00CDE823 /* ShareToolbarItem.mm in Sources */ = {isa = PBXBuildFile; fileRef = A225A4BF187E369C00CDE823 /* ShareToolbarItem.mm */; };
A2265F420B5EF5F40093DDA5 /* FileNameCell.mm in Sources */ = {isa = PBXBuildFile; fileRef = A2265F400B5EF5F40093DDA5 /* FileNameCell.mm */; };
A226FDAC0D0CDF20005A7F71 /* libnatpmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3C7A118D0D0B2EB800B5701F /* libnatpmp.a */; }; A226FDAC0D0CDF20005A7F71 /* libnatpmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3C7A118D0D0B2EB800B5701F /* libnatpmp.a */; };
A22A8D560AEEAFA5007E9CB9 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = A22A8D540AEEAFA5007E9CB9 /* Localizable.strings */; }; A22A8D560AEEAFA5007E9CB9 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = A22A8D540AEEAFA5007E9CB9 /* Localizable.strings */; };
A22B00B2116A9E9F003315FC /* connecthostport.h in Headers */ = {isa = PBXBuildFile; fileRef = A22B00AF116A9E90003315FC /* connecthostport.h */; }; A22B00B2116A9E9F003315FC /* connecthostport.h in Headers */ = {isa = PBXBuildFile; fileRef = A22B00AF116A9E90003315FC /* connecthostport.h */; };
@@ -458,6 +456,9 @@
ED5E0F0F2CD31BC20071433B /* NSStringAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4DE5CC9C0980656F00BE280E /* NSStringAdditions.mm */; }; ED5E0F0F2CD31BC20071433B /* NSStringAdditions.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4DE5CC9C0980656F00BE280E /* NSStringAdditions.mm */; };
ED67FB422B70FCE400D8A037 /* settings.cc in Sources */ = {isa = PBXBuildFile; fileRef = ED67FB402B70FCE400D8A037 /* settings.cc */; }; ED67FB422B70FCE400D8A037 /* settings.cc in Sources */ = {isa = PBXBuildFile; fileRef = ED67FB402B70FCE400D8A037 /* settings.cc */; };
ED67FB432B70FCE400D8A037 /* settings.h in Headers */ = {isa = PBXBuildFile; fileRef = ED67FB412B70FCE400D8A037 /* settings.h */; }; ED67FB432B70FCE400D8A037 /* settings.h in Headers */ = {isa = PBXBuildFile; fileRef = ED67FB412B70FCE400D8A037 /* settings.h */; };
ED6F16B52EB8F1EB007CD864 /* FileNameCellView.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED6F16B22EB8F1EB007CD864 /* FileNameCellView.mm */; };
ED6F16B62EB8F1EB007CD864 /* FilePriorityCellView.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED6F16B42EB8F1EB007CD864 /* FilePriorityCellView.mm */; };
ED6F16B72EB8F1EB007CD864 /* FileCheckCellView.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED6F16B02EB8F1EB007CD864 /* FileCheckCellView.mm */; };
ED86936F2ADAE34D00342B1A /* DefaultAppHelper.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED86936E2ADAE34D00342B1A /* DefaultAppHelper.mm */; }; ED86936F2ADAE34D00342B1A /* DefaultAppHelper.mm in Sources */ = {isa = PBXBuildFile; fileRef = ED86936E2ADAE34D00342B1A /* DefaultAppHelper.mm */; };
ED8A16412735A8AA000D61F9 /* peer-mgr-wishlist.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8A163D2735A8AA000D61F9 /* peer-mgr-wishlist.h */; }; ED8A16412735A8AA000D61F9 /* peer-mgr-wishlist.h in Headers */ = {isa = PBXBuildFile; fileRef = ED8A163D2735A8AA000D61F9 /* peer-mgr-wishlist.h */; };
ED8A16422735A8AA000D61F9 /* peer-mgr-wishlist.cc in Sources */ = {isa = PBXBuildFile; fileRef = ED8A163E2735A8AA000D61F9 /* peer-mgr-wishlist.cc */; }; ED8A16422735A8AA000D61F9 /* peer-mgr-wishlist.cc in Sources */ = {isa = PBXBuildFile; fileRef = ED8A163E2735A8AA000D61F9 /* peer-mgr-wishlist.cc */; };
@@ -686,8 +687,6 @@
29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
2B9BA6C508B488FE586A0AB1 /* torrents.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = torrents.cc; sourceTree = "<group>"; }; 2B9BA6C508B488FE586A0AB1 /* torrents.cc */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = torrents.cc; sourceTree = "<group>"; };
2B9BA6C508B488FE586A0AB3 /* torrents.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; path = torrents.h; sourceTree = "<group>"; }; 2B9BA6C508B488FE586A0AB3 /* torrents.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; path = torrents.h; sourceTree = "<group>"; };
35F373000C2DA88F00DAA8F2 /* FilePriorityCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FilePriorityCell.h; sourceTree = "<group>"; };
35F373010C2DA88F00DAA8F2 /* FilePriorityCell.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FilePriorityCell.mm; sourceTree = "<group>"; };
3C7A118D0D0B2EB800B5701F /* libnatpmp.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libnatpmp.a; sourceTree = BUILT_PRODUCTS_DIR; }; 3C7A118D0D0B2EB800B5701F /* libnatpmp.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libnatpmp.a; sourceTree = BUILT_PRODUCTS_DIR; };
3C7A11910D0B2EE300B5701F /* getgateway.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = getgateway.c; sourceTree = "<group>"; }; 3C7A11910D0B2EE300B5701F /* getgateway.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = getgateway.c; sourceTree = "<group>"; };
3C7A11920D0B2EE300B5701F /* getgateway.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = getgateway.h; sourceTree = "<group>"; }; 3C7A11920D0B2EE300B5701F /* getgateway.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = getgateway.h; sourceTree = "<group>"; };
@@ -921,8 +920,6 @@
A223AA800D220CEB00840069 /* nl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; }; A223AA800D220CEB00840069 /* nl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = "<group>"; };
A225A4BE187E369C00CDE823 /* ShareToolbarItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShareToolbarItem.h; sourceTree = "<group>"; }; A225A4BE187E369C00CDE823 /* ShareToolbarItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShareToolbarItem.h; sourceTree = "<group>"; };
A225A4BF187E369C00CDE823 /* ShareToolbarItem.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ShareToolbarItem.mm; sourceTree = "<group>"; }; A225A4BF187E369C00CDE823 /* ShareToolbarItem.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ShareToolbarItem.mm; sourceTree = "<group>"; };
A2265F3F0B5EF5F40093DDA5 /* FileNameCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FileNameCell.h; sourceTree = "<group>"; };
A2265F400B5EF5F40093DDA5 /* FileNameCell.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FileNameCell.mm; sourceTree = "<group>"; };
A22B00AE116A9E90003315FC /* connecthostport.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = connecthostport.c; sourceTree = "<group>"; }; A22B00AE116A9E90003315FC /* connecthostport.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = connecthostport.c; sourceTree = "<group>"; };
A22B00AF116A9E90003315FC /* connecthostport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = connecthostport.h; sourceTree = "<group>"; }; A22B00AF116A9E90003315FC /* connecthostport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = connecthostport.h; sourceTree = "<group>"; };
A22BAE261388040500FB022F /* NSMutableArrayAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSMutableArrayAdditions.h; sourceTree = "<group>"; }; A22BAE261388040500FB022F /* NSMutableArrayAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSMutableArrayAdditions.h; sourceTree = "<group>"; };
@@ -1450,6 +1447,12 @@
ED5E0F0E2CD3164D0071433B /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/Localizable.strings"; sourceTree = "<group>"; }; ED5E0F0E2CD3164D0071433B /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/Localizable.strings"; sourceTree = "<group>"; };
ED67FB402B70FCE400D8A037 /* settings.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = settings.cc; sourceTree = "<group>"; }; ED67FB402B70FCE400D8A037 /* settings.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = settings.cc; sourceTree = "<group>"; };
ED67FB412B70FCE400D8A037 /* settings.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; path = settings.h; sourceTree = "<group>"; }; ED67FB412B70FCE400D8A037 /* settings.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; path = settings.h; sourceTree = "<group>"; };
ED6F16AF2EB8F1EB007CD864 /* FileCheckCellView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FileCheckCellView.h; sourceTree = "<group>"; };
ED6F16B02EB8F1EB007CD864 /* FileCheckCellView.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FileCheckCellView.mm; sourceTree = "<group>"; };
ED6F16B12EB8F1EB007CD864 /* FileNameCellView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FileNameCellView.h; sourceTree = "<group>"; };
ED6F16B22EB8F1EB007CD864 /* FileNameCellView.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FileNameCellView.mm; sourceTree = "<group>"; };
ED6F16B32EB8F1EB007CD864 /* FilePriorityCellView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FilePriorityCellView.h; sourceTree = "<group>"; };
ED6F16B42EB8F1EB007CD864 /* FilePriorityCellView.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FilePriorityCellView.mm; sourceTree = "<group>"; };
ED86936D2ADAE34D00342B1A /* DefaultAppHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DefaultAppHelper.h; sourceTree = "<group>"; }; ED86936D2ADAE34D00342B1A /* DefaultAppHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DefaultAppHelper.h; sourceTree = "<group>"; };
ED86936E2ADAE34D00342B1A /* DefaultAppHelper.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DefaultAppHelper.mm; sourceTree = "<group>"; }; ED86936E2ADAE34D00342B1A /* DefaultAppHelper.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DefaultAppHelper.mm; sourceTree = "<group>"; };
ED8A163D2735A8AA000D61F9 /* peer-mgr-wishlist.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; path = "peer-mgr-wishlist.h"; sourceTree = "<group>"; }; ED8A163D2735A8AA000D61F9 /* peer-mgr-wishlist.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; path = "peer-mgr-wishlist.h"; sourceTree = "<group>"; };
@@ -2142,10 +2145,12 @@
A26AF2190D2DA35A00FF7140 /* FileOutlineController.mm */, A26AF2190D2DA35A00FF7140 /* FileOutlineController.mm */,
A2AF1C360A3D0F6200F1575D /* FileOutlineView.h */, A2AF1C360A3D0F6200F1575D /* FileOutlineView.h */,
A2AF1C370A3D0F6200F1575D /* FileOutlineView.mm */, A2AF1C370A3D0F6200F1575D /* FileOutlineView.mm */,
A2265F3F0B5EF5F40093DDA5 /* FileNameCell.h */, ED6F16AF2EB8F1EB007CD864 /* FileCheckCellView.h */,
A2265F400B5EF5F40093DDA5 /* FileNameCell.mm */, ED6F16B02EB8F1EB007CD864 /* FileCheckCellView.mm */,
35F373000C2DA88F00DAA8F2 /* FilePriorityCell.h */, ED6F16B12EB8F1EB007CD864 /* FileNameCellView.h */,
35F373010C2DA88F00DAA8F2 /* FilePriorityCell.mm */, ED6F16B22EB8F1EB007CD864 /* FileNameCellView.mm */,
ED6F16B32EB8F1EB007CD864 /* FilePriorityCellView.h */,
ED6F16B42EB8F1EB007CD864 /* FilePriorityCellView.mm */,
); );
name = "File Outline View"; name = "File Outline View";
sourceTree = "<group>"; sourceTree = "<group>";
@@ -3545,14 +3550,15 @@
A2AA579D0ADFCAB400CA59F6 /* PiecesView.mm in Sources */, A2AA579D0ADFCAB400CA59F6 /* PiecesView.mm in Sources */,
A25E74650AF5097C006F11AE /* ExpandedPathToPathTransformer.mm in Sources */, A25E74650AF5097C006F11AE /* ExpandedPathToPathTransformer.mm in Sources */,
A25E74660AF5097D006F11AE /* ExpandedPathToIconTransformer.mm in Sources */, A25E74660AF5097D006F11AE /* ExpandedPathToIconTransformer.mm in Sources */,
A2265F420B5EF5F40093DDA5 /* FileNameCell.mm in Sources */,
A2A1CB7A0BF29D5500AE959F /* PeerProgressIndicatorCell.mm in Sources */, A2A1CB7A0BF29D5500AE959F /* PeerProgressIndicatorCell.mm in Sources */,
457AF8EB28604AFC00BCF74F /* Toolbar.mm in Sources */, 457AF8EB28604AFC00BCF74F /* Toolbar.mm in Sources */,
A2385DD40BFE06C800B24EF6 /* DragOverlayWindow.mm in Sources */, A2385DD40BFE06C800B24EF6 /* DragOverlayWindow.mm in Sources */,
A2FB057F0BFEB6800095564D /* DragOverlayView.mm in Sources */, A2FB057F0BFEB6800095564D /* DragOverlayView.mm in Sources */,
E138A9780C04D88F00C5426C /* ProgressGradients.mm in Sources */, E138A9780C04D88F00C5426C /* ProgressGradients.mm in Sources */,
ED6F16B52EB8F1EB007CD864 /* FileNameCellView.mm in Sources */,
ED6F16B62EB8F1EB007CD864 /* FilePriorityCellView.mm in Sources */,
ED6F16B72EB8F1EB007CD864 /* FileCheckCellView.mm in Sources */,
A2DF37070C220D03006523C1 /* CreatorWindowController.mm in Sources */, A2DF37070C220D03006523C1 /* CreatorWindowController.mm in Sources */,
35F373030C2DA89000DAA8F2 /* FilePriorityCell.mm in Sources */,
A2085DDC0C53BC74000BC3B7 /* AboutWindowController.mm in Sources */, A2085DDC0C53BC74000BC3B7 /* AboutWindowController.mm in Sources */,
A21282A80CA6C66800EAEE0F /* StatusBarView.mm in Sources */, A21282A80CA6C66800EAEE0F /* StatusBarView.mm in Sources */,
A257C1820CAD3003004E121C /* PeerTableView.mm in Sources */, A257C1820CAD3003004E121C /* PeerTableView.mm in Sources */,

View File

@@ -309,7 +309,7 @@ typedef NS_ENUM(NSUInteger, PopupPriority) {
{ {
//check buttons //check buttons
//keep synced with identical code in InfoFileViewController.m //keep synced with identical code in InfoFileViewController.m
NSInteger const filesCheckState = [self.torrent NSControlStateValue const filesCheckState = [self.torrent
checkForFiles:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.torrent.fileCount)]]; checkForFiles:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.torrent.fileCount)]];
self.fCheckAllButton.enabled = filesCheckState != NSControlStateValueOn; //if anything is unchecked self.fCheckAllButton.enabled = filesCheckState != NSControlStateValueOn; //if anything is unchecked
self.fUncheckAllButton.enabled = !self.torrent.allDownloaded; //if there are any checked files that aren't finished self.fUncheckAllButton.enabled = !self.torrent.allDownloaded; //if there are any checked files that aren't finished
@@ -352,7 +352,7 @@ typedef NS_ENUM(NSUInteger, PopupPriority) {
{ {
[self.torrent update]; [self.torrent update];
[self.fFileController refresh]; [self.fFileController reloadVisibleRows];
[self updateCheckButtons:nil]; //call in case button state changed by checking [self updateCheckButtons:nil]; //call in case button state changed by checking

View File

@@ -75,14 +75,16 @@ target_sources(${TR_NAME}-mac
ExpandedPathToPathTransformer.mm ExpandedPathToPathTransformer.mm
FileListNode.h FileListNode.h
FileListNode.mm FileListNode.mm
FileNameCell.h FileCheckCellView.h
FileNameCell.mm FileCheckCellView.mm
FileNameCellView.h
FileNameCellView.mm
FileOutlineController.h FileOutlineController.h
FileOutlineController.mm FileOutlineController.mm
FileOutlineView.h FileOutlineView.h
FileOutlineView.mm FileOutlineView.mm
FilePriorityCell.h FilePriorityCellView.h
FilePriorityCell.mm FilePriorityCellView.mm
FileRenameSheetController.h FileRenameSheetController.h
FileRenameSheetController.mm FileRenameSheetController.mm
FilterBarController.h FilterBarController.h

View File

@@ -0,0 +1,13 @@
// This file Copyright © 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 FileListNode;
@interface FileCheckCellView : NSTableCellView
@property(nonatomic, weak) FileListNode* node;
@end

113
macosx/FileCheckCellView.mm Normal file
View File

@@ -0,0 +1,113 @@
// This file Copyright © Transmission authors and contributors.
// It may be used under the MIT (SPDX: MIT) license.
// License text can be found in the licenses/ folder.
#import "FileCheckCellView.h"
#import "FileListNode.h"
#import "Torrent.h"
@interface FileCheckCellView ()
@property(nonatomic, weak) NSButton* checkButton;
@end
@implementation FileCheckCellView
- (instancetype)initWithFrame:(NSRect)frameRect
{
if ((self = [super initWithFrame:frameRect]))
{
// Create checkbox button
NSButton* checkButton = [[NSButton alloc] initWithFrame:NSZeroRect];
checkButton.translatesAutoresizingMaskIntoConstraints = NO;
[checkButton setButtonType:NSButtonTypeSwitch];
checkButton.title = @"";
checkButton.allowsMixedState = YES;
checkButton.target = self;
checkButton.action = @selector(checkButtonClicked:);
[self addSubview:checkButton];
_checkButton = checkButton;
// Setup constraints
[NSLayoutConstraint activateConstraints:@[
[checkButton.centerXAnchor constraintEqualToAnchor:self.centerXAnchor],
[checkButton.centerYAnchor constraintEqualToAnchor:self.centerYAnchor],
]];
}
return self;
}
- (void)setNode:(FileListNode*)node
{
_node = node;
[self updateDisplay];
}
- (void)updateDisplay
{
if (!self.node)
{
return;
}
FileListNode* node = self.node;
Torrent* torrent = node.torrent;
// Update checkbox state
self.checkButton.state = [torrent checkForFiles:node.indexes];
self.checkButton.enabled = [torrent canChangeDownloadCheckForFiles:node.indexes];
// Update tooltip
[self updateTooltip];
}
- (void)updateTooltip
{
if (!self.node)
{
return;
}
NSString* tooltip = nil;
switch (self.checkButton.state)
{
case NSControlStateValueOff:
tooltip = NSLocalizedString(@"Don't Download", "files tab -> tooltip");
break;
case NSControlStateValueOn:
tooltip = NSLocalizedString(@"Download", "files tab -> tooltip");
break;
case NSControlStateValueMixed:
tooltip = NSLocalizedString(@"Download Some", "files tab -> tooltip");
break;
}
self.checkButton.toolTip = tooltip;
}
- (void)checkButtonClicked:(NSButton*)sender
{
if (!self.node)
{
return;
}
FileListNode* node = self.node;
Torrent* torrent = node.torrent;
NSIndexSet* indexSet;
if (NSEvent.modifierFlags & NSEventModifierFlagOption)
{
indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, torrent.fileCount)];
}
else
{
indexSet = node.indexes;
}
[torrent setFileCheckState:sender.state != NSControlStateValueOff ? NSControlStateValueOn : NSControlStateValueOff
forIndexes:indexSet];
// Notify that we need to refresh
[NSNotificationCenter.defaultCenter postNotificationName:@"UpdateUI" object:nil];
}
@end

View File

@@ -1,17 +0,0 @@
// This file Copyright © 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>
typedef NS_ENUM(NSInteger, AttributesStyle) {
AttributesStyleNormal,
AttributesStyleEmphasized,
AttributesStyleDisabled,
};
@interface FileNameCell : NSActionCell
- (NSRect)imageRectForBounds:(NSRect)bounds;
@end

View File

@@ -1,218 +0,0 @@
// This file Copyright © Transmission authors and contributors.
// It may be used under the MIT (SPDX: MIT) license.
// License text can be found in the licenses/ folder.
#include <libtransmission/transmission.h>
#include <libtransmission/utils.h>
#import "FileNameCell.h"
#import "FileOutlineView.h"
#import "Torrent.h"
#import "FileListNode.h"
#import "NSStringAdditions.h"
static CGFloat const kPaddingHorizontal = 2.0;
static CGFloat const kImageFolderSize = 16.0;
static CGFloat const kImageIconSize = 32.0;
static CGFloat const kPaddingBetweenImageAndTitle = 4.0;
static CGFloat const kPaddingAboveTitleFile = 2.0;
static CGFloat const kPaddingBelowStatusFile = 2.0;
static CGFloat const kPaddingBetweenNameAndFolderStatus = 4.0;
static CGFloat const kPaddingExpansionFrame = 2.0;
static NSMutableParagraphStyle* sParagraphStyle()
{
NSMutableParagraphStyle* paragraphStyle = [NSParagraphStyle.defaultParagraphStyle mutableCopy];
paragraphStyle.lineBreakMode = NSLineBreakByTruncatingMiddle;
return paragraphStyle;
}
static NSMutableParagraphStyle* sStatusParagraphStyle()
{
NSMutableParagraphStyle* paragraphStyle = [NSParagraphStyle.defaultParagraphStyle mutableCopy];
paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;
return paragraphStyle;
}
static NSDictionary<NSAttributedStringKey, id>* const kTitleAttributes = @{
NSFontAttributeName : [NSFont messageFontOfSize:12.0],
NSParagraphStyleAttributeName : sParagraphStyle(),
NSForegroundColorAttributeName : NSColor.controlTextColor
};
static NSDictionary<NSAttributedStringKey, id>* const kStatusAttributes = @{
NSFontAttributeName : [NSFont messageFontOfSize:9.0],
NSParagraphStyleAttributeName : sStatusParagraphStyle(),
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:9.0],
NSParagraphStyleAttributeName : sStatusParagraphStyle(),
NSForegroundColorAttributeName : NSColor.whiteColor
};
static NSDictionary<NSAttributedStringKey, id>* const kTitleDisabledAttributes = @{
NSFontAttributeName : [NSFont messageFontOfSize:12.0],
NSParagraphStyleAttributeName : sParagraphStyle(),
NSForegroundColorAttributeName : NSColor.disabledControlTextColor
};
static NSDictionary<NSAttributedStringKey, id>* const kStatusDisabledAttributes = @{
NSFontAttributeName : [NSFont messageFontOfSize:9.0],
NSParagraphStyleAttributeName : sStatusParagraphStyle(),
NSForegroundColorAttributeName : NSColor.disabledControlTextColor
};
@implementation FileNameCell
- (NSImage*)image
{
FileListNode* node = (FileListNode*)self.objectValue;
return node.icon;
}
- (NSRect)imageRectForBounds:(NSRect)bounds
{
NSRect result = bounds;
result.origin.x += kPaddingHorizontal;
CGFloat const IMAGE_SIZE = ((FileListNode*)self.objectValue).isFolder ? kImageFolderSize : kImageIconSize;
result.origin.y += (result.size.height - IMAGE_SIZE) * 0.5;
result.size = NSMakeSize(IMAGE_SIZE, IMAGE_SIZE);
return result;
}
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView
{
//icon
[self.image drawInRect:[self imageRectForBounds:cellFrame] fromRect:NSZeroRect operation:NSCompositingOperationSourceOver
fraction:1.0
respectFlipped:YES
hints:nil];
FileListNode* node = self.objectValue;
AttributesStyle style;
if (self.backgroundStyle == NSBackgroundStyleEmphasized)
{
style = AttributesStyleEmphasized;
}
else if ([node.torrent checkForFiles:node.indexes] == NSControlStateValueOff)
{
style = AttributesStyleDisabled;
}
else
{
style = AttributesStyleNormal;
}
//title
NSAttributedString* titleString = [self attributedTitleWithStyle:style];
NSRect titleRect = [self rectForTitleWithStringSize:[titleString size] inBounds:cellFrame];
[titleString drawInRect:titleRect];
//status
NSAttributedString* statusString = [self attributedStatusWithStyle:style];
NSRect statusRect = [self rectForStatusWithString:statusString withTitleRect:titleRect inBounds:cellFrame];
[statusString drawInRect:statusRect];
}
- (NSRect)expansionFrameWithFrame:(NSRect)cellFrame inView:(NSView*)view
{
NSAttributedString* titleString = [self attributedTitleWithStyle:AttributesStyleNormal];
NSRect realRect = [self rectForTitleWithStringSize:[titleString size] inBounds:cellFrame];
if ([titleString size].width > NSWidth(realRect) &&
NSMouseInRect([view convertPoint:view.window.mouseLocationOutsideOfEventStream fromView:nil], realRect, view.flipped))
{
realRect.size.width = [titleString size].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
- (NSRect)rectForTitleWithStringSize:(NSSize)stringSize inBounds:(NSRect)bounds
{
NSSize const titleSize = stringSize;
//no right padding, so that there's not too much space between this and the priority image
NSRect result;
if (!((FileListNode*)self.objectValue).isFolder)
{
result.origin.x = NSMinX(bounds) + kPaddingHorizontal + kImageIconSize + kPaddingBetweenImageAndTitle;
result.origin.y = NSMinY(bounds) + kPaddingAboveTitleFile;
result.size.width = NSMaxX(bounds) - NSMinX(result);
}
else
{
result.origin.x = NSMinX(bounds) + kPaddingHorizontal + kImageFolderSize + kPaddingBetweenImageAndTitle;
result.origin.y = NSMidY(bounds) - titleSize.height * 0.5;
result.size.width = MIN(titleSize.width, NSMaxX(bounds) - NSMinX(result));
}
result.size.height = titleSize.height;
return result;
}
- (NSRect)rectForStatusWithString:(NSAttributedString*)string withTitleRect:(NSRect)titleRect inBounds:(NSRect)bounds
{
NSSize const statusSize = [string size];
NSRect result;
if (!((FileListNode*)self.objectValue).isFolder)
{
result.origin.x = NSMinX(titleRect);
result.origin.y = NSMaxY(bounds) - kPaddingBelowStatusFile - statusSize.height;
result.size.width = NSWidth(titleRect);
}
else
{
result.origin.x = NSMaxX(titleRect) + kPaddingBetweenNameAndFolderStatus;
result.origin.y = NSMaxY(titleRect) - statusSize.height - 1.0;
result.size.width = NSMaxX(bounds) - NSMaxX(titleRect);
}
result.size.height = statusSize.height;
return result;
}
- (NSAttributedString*)attributedTitleWithStyle:(AttributesStyle)style
{
NSString* title = ((FileListNode*)self.objectValue).name;
return [[NSAttributedString alloc] initWithString:title attributes:style == AttributesStyleEmphasized ? kTitleEmphasizedAttributes :
style == AttributesStyleDisabled ? kTitleDisabledAttributes :
kTitleAttributes];
}
- (NSAttributedString*)attributedStatusWithStyle:(AttributesStyle)style
{
FileListNode* node = (FileListNode*)self.objectValue;
Torrent* torrent = node.torrent;
CGFloat const progress = [torrent fileProgress:node];
NSString* percentString = [NSString percentString:progress longDecimals:YES];
NSString* status = [NSString stringWithFormat:NSLocalizedString(@"%@ of %@", "Inspector -> Files tab -> file status string"),
percentString,
[NSString stringForFileSize:node.size]];
return [[NSAttributedString alloc] initWithString:status attributes:style == AttributesStyleEmphasized ? kStatusEmphasizedAttributes :
style == AttributesStyleDisabled ? kStatusDisabledAttributes :
kStatusAttributes];
}
@end

13
macosx/FileNameCellView.h Normal file
View File

@@ -0,0 +1,13 @@
// This file Copyright © 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 FileListNode;
@interface FileNameCellView : NSTableCellView
@property(nonatomic, weak) FileListNode* node;
@end

228
macosx/FileNameCellView.mm Normal file
View File

@@ -0,0 +1,228 @@
// This file Copyright © Transmission authors and contributors.
// It may be used under the MIT (SPDX: MIT) license.
// License text can be found in the licenses/ folder.
#include <libtransmission/transmission.h>
#include <libtransmission/utils.h>
#import "FileNameCellView.h"
#import "FileListNode.h"
#import "Torrent.h"
#import "NSStringAdditions.h"
static CGFloat const kPaddingHorizontal = 2.0;
static CGFloat const kImageFolderSize = 16.0;
static CGFloat const kImageIconSize = 32.0;
static CGFloat const kPaddingBetweenImageAndTitle = 4.0;
static CGFloat const kPaddingAboveTitleFile = 2.0;
static CGFloat const kPaddingBelowStatusFile = 2.0;
static CGFloat const kPaddingBetweenNameAndFolderStatus = 4.0;
@interface FileNameCellView ()
@property(nonatomic, weak) NSImageView* iconView;
@property(nonatomic, weak) NSTextField* nameField;
@property(nonatomic, weak) NSTextField* statusField;
@property(nonatomic, strong) NSArray<NSLayoutConstraint*>* dynamicConstraints;
@end
@implementation FileNameCellView
- (instancetype)initWithFrame:(NSRect)frameRect
{
if ((self = [super initWithFrame:frameRect]))
{
// Create icon view
NSImageView* iconView = [[NSImageView alloc] initWithFrame:NSZeroRect];
iconView.translatesAutoresizingMaskIntoConstraints = NO;
iconView.imageScaling = NSImageScaleProportionallyDown;
[self addSubview:iconView];
_iconView = iconView;
// Create name field
NSTextField* nameField = [[NSTextField alloc] initWithFrame:NSZeroRect];
nameField.translatesAutoresizingMaskIntoConstraints = NO;
nameField.editable = NO;
nameField.selectable = NO;
nameField.bordered = NO;
nameField.backgroundColor = NSColor.clearColor;
nameField.font = [NSFont messageFontOfSize:12.0];
nameField.lineBreakMode = NSLineBreakByTruncatingMiddle;
[self addSubview:nameField];
_nameField = nameField;
self.textField = nameField;
// Create status field
NSTextField* statusField = [[NSTextField alloc] initWithFrame:NSZeroRect];
statusField.translatesAutoresizingMaskIntoConstraints = NO;
statusField.editable = NO;
statusField.selectable = NO;
statusField.bordered = NO;
statusField.backgroundColor = NSColor.clearColor;
statusField.font = [NSFont messageFontOfSize:9.0];
statusField.textColor = NSColor.secondaryLabelColor;
statusField.lineBreakMode = NSLineBreakByTruncatingTail;
[self addSubview:statusField];
_statusField = statusField;
// Setup constraints
[self setupConstraints];
}
return self;
}
- (void)setupConstraints
{
NSImageView* iconView = self.iconView;
NSTextField* nameField = self.nameField;
// Fixed constraints that don't change
[NSLayoutConstraint activateConstraints:@[
// Icon view constraints
[iconView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor constant:kPaddingHorizontal],
[iconView.centerYAnchor constraintEqualToAnchor:self.centerYAnchor],
[iconView.widthAnchor constraintEqualToConstant:kImageIconSize],
[iconView.heightAnchor constraintEqualToConstant:kImageIconSize],
// Name field leading constraint
[nameField.leadingAnchor constraintEqualToAnchor:iconView.trailingAnchor constant:kPaddingBetweenImageAndTitle],
]];
self.dynamicConstraints = @[];
}
- (void)setNode:(FileListNode*)node
{
_node = node;
[self updateDisplay];
}
- (void)updateDisplay
{
if (!self.node)
{
return;
}
FileListNode* node = self.node;
// Update icon
self.iconView.image = node.icon;
// Update icon size constraints based on folder/file
CGFloat const imageSize = node.isFolder ? kImageFolderSize : kImageIconSize;
for (NSLayoutConstraint* constraint in self.iconView.constraints)
{
if (constraint.firstAttribute == NSLayoutAttributeWidth || constraint.firstAttribute == NSLayoutAttributeHeight)
{
constraint.constant = imageSize;
}
}
// Update name
self.nameField.stringValue = node.name;
// Update status
Torrent* torrent = node.torrent;
CGFloat const progress = [torrent fileProgress:node];
NSString* percentString = [NSString percentString:progress longDecimals:YES];
NSString* status = [NSString stringWithFormat:NSLocalizedString(@"%@ of %@", "Inspector -> Files tab -> file status string"),
percentString,
[NSString stringForFileSize:node.size]];
self.statusField.stringValue = status;
// Update layout constraints based on folder vs file
[NSLayoutConstraint deactivateConstraints:self.dynamicConstraints];
NSTextField* nameField = self.nameField;
NSTextField* statusField = self.statusField;
if (node.isFolder)
{
// For folders, status appears next to name, both centered
self.statusField.hidden = NO;
self.dynamicConstraints = @[
[nameField.centerYAnchor constraintEqualToAnchor:self.centerYAnchor],
[nameField.trailingAnchor constraintLessThanOrEqualToAnchor:statusField.leadingAnchor
constant:-kPaddingBetweenNameAndFolderStatus],
[statusField.leadingAnchor constraintEqualToAnchor:nameField.trailingAnchor constant:kPaddingBetweenNameAndFolderStatus],
[statusField.centerYAnchor constraintEqualToAnchor:self.centerYAnchor],
[statusField.trailingAnchor constraintLessThanOrEqualToAnchor:self.trailingAnchor],
];
}
else
{
// For files, status appears below name
self.statusField.hidden = NO;
self.dynamicConstraints = @[
[nameField.topAnchor constraintEqualToAnchor:self.topAnchor constant:kPaddingAboveTitleFile],
[nameField.trailingAnchor constraintLessThanOrEqualToAnchor:self.trailingAnchor],
[statusField.leadingAnchor constraintEqualToAnchor:nameField.leadingAnchor],
[statusField.trailingAnchor constraintEqualToAnchor:self.trailingAnchor],
[statusField.bottomAnchor constraintEqualToAnchor:self.bottomAnchor constant:-kPaddingBelowStatusFile],
];
}
[NSLayoutConstraint activateConstraints:self.dynamicConstraints];
// Update colors based on background style and check state
[self updateColors];
// Update tooltip
[self updateTooltip];
}
- (void)updateTooltip
{
if (!self.node)
{
return;
}
FileListNode* node = self.node;
Torrent* torrent = node.torrent;
NSString* path = [torrent fileLocation:node];
if (!path)
{
path = [node.path stringByAppendingPathComponent:node.name];
}
self.toolTip = path;
}
- (void)setBackgroundStyle:(NSBackgroundStyle)backgroundStyle
{
[super setBackgroundStyle:backgroundStyle];
[self updateColors];
}
- (void)updateColors
{
if (!self.node)
{
return;
}
FileListNode* node = self.node;
Torrent* torrent = node.torrent;
if (self.backgroundStyle == NSBackgroundStyleEmphasized)
{
self.nameField.textColor = NSColor.whiteColor;
self.statusField.textColor = NSColor.whiteColor;
}
else if ([torrent checkForFiles:node.indexes] == NSControlStateValueOff)
{
self.nameField.textColor = NSColor.disabledControlTextColor;
self.statusField.textColor = NSColor.disabledControlTextColor;
}
else
{
self.nameField.textColor = NSColor.controlTextColor;
self.statusField.textColor = NSColor.secondaryLabelColor;
}
}
@end

View File

@@ -13,7 +13,7 @@
@property(nonatomic) Torrent* torrent; @property(nonatomic) Torrent* torrent;
@property(nonatomic) NSString* filterText; @property(nonatomic) NSString* filterText;
- (void)refresh; - (void)reloadVisibleRows;
- (void)setCheck:(id)sender; - (void)setCheck:(id)sender;
- (void)setOnlySelectedCheck:(id)sender; - (void)setOnlySelectedCheck:(id)sender;

View File

@@ -6,7 +6,9 @@
#import "Torrent.h" #import "Torrent.h"
#import "FileListNode.h" #import "FileListNode.h"
#import "FileOutlineView.h" #import "FileOutlineView.h"
#import "FilePriorityCell.h" #import "FileNameCellView.h"
#import "FilePriorityCellView.h"
#import "FileCheckCellView.h"
#import "FileRenameSheetController.h" #import "FileRenameSheetController.h"
#import "NSMutableArrayAdditions.h" #import "NSMutableArrayAdditions.h"
#import "NSStringAdditions.h" #import "NSStringAdditions.h"
@@ -24,7 +26,7 @@ typedef NS_ENUM(NSUInteger, FilePriorityMenuTag) { //
FilePriorityMenuTagLow FilePriorityMenuTagLow
}; };
@interface FileOutlineController () @interface FileOutlineController ()<NSOutlineViewDelegate, NSOutlineViewDataSource, NSMenuItemValidation>
@property(nonatomic) NSMutableArray<FileListNode*>* fFileList; @property(nonatomic) NSMutableArray<FileListNode*>* fFileList;
@@ -186,18 +188,18 @@ typedef NS_ENUM(NSUInteger, FilePriorityMenuTag) { //
_filterText = text; _filterText = text;
} }
- (void)refresh - (void)reloadVisibleRows
{ {
self.fOutline.needsDisplay = YES; NSRect visibleRect = self.fOutline.visibleRect;
NSRange range = [self.fOutline rowsInRect:visibleRect];
NSIndexSet* rowIndexes = [NSIndexSet indexSetWithIndexesInRange:range];
NSIndexSet* columnIndexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.fOutline.numberOfColumns)];
[self.fOutline reloadDataForRowIndexes:rowIndexes columnIndexes:columnIndexes];
} }
- (void)outlineViewSelectionDidChange:(NSNotification*)notification #pragma mark - NSOutlineViewDataSource
{
if ([QLPreviewPanel sharedPreviewPanelExists] && [QLPreviewPanel sharedPreviewPanel].visible)
{
[[QLPreviewPanel sharedPreviewPanel] reloadData];
}
}
- (NSInteger)outlineView:(NSOutlineView*)outlineView numberOfChildrenOfItem:(id)item - (NSInteger)outlineView:(NSOutlineView*)outlineView numberOfChildrenOfItem:(id)item
{ {
@@ -222,61 +224,51 @@ typedef NS_ENUM(NSUInteger, FilePriorityMenuTag) { //
return (item ? ((FileListNode*)item).children : self.fFileList)[index]; return (item ? ((FileListNode*)item).children : self.fFileList)[index];
} }
- (id)outlineView:(NSOutlineView*)outlineView objectValueForTableColumn:(NSTableColumn*)tableColumn byItem:(id)item #pragma mark - NSOutlineViewDelegate
{
if ([tableColumn.identifier isEqualToString:@"Check"])
{
return @([self.torrent checkForFiles:((FileListNode*)item).indexes]);
}
else
{
return item;
}
}
- (void)outlineView:(NSOutlineView*)outlineView - (NSView*)outlineView:(NSOutlineView*)outlineView viewForTableColumn:(NSTableColumn*)tableColumn item:(id)item
willDisplayCell:(id)cell
forTableColumn:(NSTableColumn*)tableColumn
item:(id)item
{ {
NSString* identifier = tableColumn.identifier; NSString* identifier = tableColumn.identifier;
if ([identifier isEqualToString:@"Check"]) FileListNode* node = (FileListNode*)item;
if ([identifier isEqualToString:@"Name"])
{ {
[cell setEnabled:[self.torrent canChangeDownloadCheckForFiles:((FileListNode*)item).indexes]]; FileNameCellView* cellView = [outlineView makeViewWithIdentifier:@"NameCell" owner:self];
if (!cellView)
{
cellView = [[FileNameCellView alloc] initWithFrame:NSZeroRect];
cellView.identifier = @"NameCell";
}
cellView.node = node;
return cellView;
} }
else if ([identifier isEqualToString:@"Priority"]) else if ([identifier isEqualToString:@"Priority"])
{ {
[cell setRepresentedObject:item]; FilePriorityCellView* cellView = [outlineView makeViewWithIdentifier:@"PriorityCell" owner:self];
if (!cellView)
NSInteger hoveredRow = self.fOutline.hoveredRow;
((FilePriorityCell*)cell).hovered = hoveredRow != -1 && hoveredRow == [self.fOutline rowForItem:item];
}
}
- (void)outlineView:(NSOutlineView*)outlineView
setObjectValue:(id)object
forTableColumn:(NSTableColumn*)tableColumn
byItem:(id)item
{
NSString* identifier = tableColumn.identifier;
if ([identifier isEqualToString:@"Check"])
{ {
NSIndexSet* indexSet; cellView = [[FilePriorityCellView alloc] initWithFrame:NSZeroRect];
if (NSEvent.modifierFlags & NSEventModifierFlagOption) cellView.identifier = @"PriorityCell";
{
indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.torrent.fileCount)];
} }
else cellView.node = node;
return cellView;
}
else if ([identifier isEqualToString:@"Check"])
{ {
indexSet = ((FileListNode*)item).indexes; FileCheckCellView* cellView = [outlineView makeViewWithIdentifier:@"CheckCell" owner:self];
if (!cellView)
{
cellView = [[FileCheckCellView alloc] initWithFrame:NSZeroRect];
cellView.identifier = @"CheckCell";
}
cellView.node = node;
return cellView;
} }
[self.torrent setFileCheckState:[object intValue] != NSControlStateValueOff ? NSControlStateValueOn : NSControlStateValueOff return nil;
forIndexes:indexSet];
self.fOutline.needsDisplay = YES;
[NSNotificationCenter.defaultCenter postNotificationName:@"UpdateUI" object:nil];
}
} }
- (NSString*)outlineView:(NSOutlineView*)outlineView typeSelectStringForTableColumn:(NSTableColumn*)tableColumn item:(id)item - (NSString*)outlineView:(NSOutlineView*)outlineView typeSelectStringForTableColumn:(NSTableColumn*)tableColumn item:(id)item
@@ -284,60 +276,13 @@ typedef NS_ENUM(NSUInteger, FilePriorityMenuTag) { //
return ((FileListNode*)item).name; return ((FileListNode*)item).name;
} }
- (NSString*)outlineView:(NSOutlineView*)outlineView - (void)outlineViewSelectionDidChange:(NSNotification*)notification
toolTipForCell:(NSCell*)cell
rect:(NSRectPointer)rect
tableColumn:(NSTableColumn*)tableColumn
item:(id)item
mouseLocation:(NSPoint)mouseLocation
{ {
NSString* ident = tableColumn.identifier; [self reloadVisibleRows];
if ([ident isEqualToString:@"Name"]) if ([QLPreviewPanel sharedPreviewPanelExists] && [QLPreviewPanel sharedPreviewPanel].visible)
{ {
NSString* path = [self.torrent fileLocation:item]; [[QLPreviewPanel sharedPreviewPanel] reloadData];
if (!path)
{
FileListNode* node = (FileListNode*)item;
path = [node.path stringByAppendingPathComponent:node.name];
} }
return path;
}
else if ([ident isEqualToString:@"Check"])
{
switch (cell.state)
{
case NSControlStateValueOff:
return NSLocalizedString(@"Don't Download", "files tab -> tooltip");
case NSControlStateValueOn:
return NSLocalizedString(@"Download", "files tab -> tooltip");
case NSControlStateValueMixed:
return NSLocalizedString(@"Download Some", "files tab -> tooltip");
}
}
else if ([ident isEqualToString:@"Priority"])
{
NSSet* priorities = [self.torrent filePrioritiesForIndexes:((FileListNode*)item).indexes];
switch (priorities.count)
{
case 0:
return NSLocalizedString(@"Priority Not Available", "files tab -> tooltip");
case 1:
switch ([[priorities anyObject] intValue])
{
case TR_PRI_LOW:
return NSLocalizedString(@"Low Priority", "files tab -> tooltip");
case TR_PRI_HIGH:
return NSLocalizedString(@"High Priority", "files tab -> tooltip");
case TR_PRI_NORMAL:
return NSLocalizedString(@"Normal Priority", "files tab -> tooltip");
}
break;
default:
return NSLocalizedString(@"Multiple Priorities", "files tab -> tooltip");
}
}
return nil;
} }
- (CGFloat)outlineView:(NSOutlineView*)outlineView heightOfRowByItem:(id)item - (CGFloat)outlineView:(NSOutlineView*)outlineView heightOfRowByItem:(id)item
@@ -352,9 +297,11 @@ typedef NS_ENUM(NSUInteger, FilePriorityMenuTag) { //
} }
} }
#pragma mark - Actions
- (void)setCheck:(id)sender - (void)setCheck:(id)sender
{ {
NSInteger state = [sender tag] == FileCheckMenuTagUncheck ? NSControlStateValueOff : NSControlStateValueOn; NSControlStateValue state = [sender tag] == FileCheckMenuTagUncheck ? NSControlStateValueOff : NSControlStateValueOn;
NSIndexSet* indexSet = self.fOutline.selectedRowIndexes; NSIndexSet* indexSet = self.fOutline.selectedRowIndexes;
NSMutableIndexSet* itemIndexes = [NSMutableIndexSet indexSet]; NSMutableIndexSet* itemIndexes = [NSMutableIndexSet indexSet];
@@ -365,7 +312,8 @@ typedef NS_ENUM(NSUInteger, FilePriorityMenuTag) { //
} }
[self.torrent setFileCheckState:state forIndexes:itemIndexes]; [self.torrent setFileCheckState:state forIndexes:itemIndexes];
self.fOutline.needsDisplay = YES;
[self reloadVisibleRows];
} }
- (void)setOnlySelectedCheck:(id)sender - (void)setOnlySelectedCheck:(id)sender
@@ -384,21 +332,23 @@ typedef NS_ENUM(NSUInteger, FilePriorityMenuTag) { //
[remainingItemIndexes removeIndexes:itemIndexes]; [remainingItemIndexes removeIndexes:itemIndexes];
[self.torrent setFileCheckState:NSControlStateValueOff forIndexes:remainingItemIndexes]; [self.torrent setFileCheckState:NSControlStateValueOff forIndexes:remainingItemIndexes];
self.fOutline.needsDisplay = YES; [self reloadVisibleRows];
} }
- (void)checkAll - (void)checkAll
{ {
NSIndexSet* indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.torrent.fileCount)]; NSIndexSet* indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.torrent.fileCount)];
[self.torrent setFileCheckState:NSControlStateValueOn forIndexes:indexSet]; [self.torrent setFileCheckState:NSControlStateValueOn forIndexes:indexSet];
self.fOutline.needsDisplay = YES;
[self reloadVisibleRows];
} }
- (void)uncheckAll - (void)uncheckAll
{ {
NSIndexSet* indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.torrent.fileCount)]; NSIndexSet* indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.torrent.fileCount)];
[self.torrent setFileCheckState:NSControlStateValueOff forIndexes:indexSet]; [self.torrent setFileCheckState:NSControlStateValueOff forIndexes:indexSet];
self.fOutline.needsDisplay = YES;
[self reloadVisibleRows];
} }
- (void)setPriority:(id)sender - (void)setPriority:(id)sender
@@ -429,7 +379,8 @@ typedef NS_ENUM(NSUInteger, FilePriorityMenuTag) { //
} }
[self.torrent setFilePriority:priority forIndexes:itemIndexes]; [self.torrent setFilePriority:priority forIndexes:itemIndexes];
self.fOutline.needsDisplay = YES;
[self reloadVisibleRows];
} }
- (void)revealFile:(id)sender - (void)revealFile:(id)sender
@@ -480,6 +431,8 @@ typedef NS_ENUM(NSUInteger, FilePriorityMenuTag) { //
} }
} }
#pragma mark - NSMenuItemValidation
#warning make real view controller (Leopard-only) so that Command-R will work #warning make real view controller (Leopard-only) so that Command-R will work
- (BOOL)validateMenuItem:(NSMenuItem*)menuItem - (BOOL)validateMenuItem:(NSMenuItem*)menuItem
{ {
@@ -518,7 +471,7 @@ typedef NS_ENUM(NSUInteger, FilePriorityMenuTag) { //
[itemIndexes addIndexes:node.indexes]; [itemIndexes addIndexes:node.indexes];
} }
NSInteger state = (menuItem.tag == FileCheckMenuTagCheck) ? NSControlStateValueOn : NSControlStateValueOff; NSControlStateValue state = (menuItem.tag == FileCheckMenuTagCheck) ? NSControlStateValueOn : NSControlStateValueOff;
return [self.torrent checkForFiles:itemIndexes] != state && [self.torrent canChangeDownloadCheckForFiles:itemIndexes]; return [self.torrent checkForFiles:itemIndexes] != state && [self.torrent canChangeDownloadCheckForFiles:itemIndexes];
} }

View File

@@ -6,8 +6,6 @@
@interface FileOutlineView : NSOutlineView @interface FileOutlineView : NSOutlineView
@property(nonatomic, readonly) NSInteger hoveredRow;
- (NSRect)iconRectForRow:(NSInteger)row; - (NSRect)iconRectForRow:(NSInteger)row;
@end @end

View File

@@ -4,15 +4,13 @@
#import "InfoWindowController.h" #import "InfoWindowController.h"
#import "FileListNode.h" #import "FileListNode.h"
#import "FileNameCell.h" #import "FileNameCellView.h"
#import "FileOutlineView.h" #import "FileOutlineView.h"
#import "FilePriorityCell.h" #import "FilePriorityCellView.h"
#import "Torrent.h" #import "Torrent.h"
@interface FileOutlineView () @interface FileOutlineView ()
@property(nonatomic) NSInteger hoveredRow;
@end @end
@implementation FileOutlineView @implementation FileOutlineView
@@ -20,16 +18,9 @@
- (void)awakeFromNib - (void)awakeFromNib
{ {
[super awakeFromNib]; [super awakeFromNib];
FileNameCell* nameCell = [[FileNameCell alloc] init];
[self tableColumnWithIdentifier:@"Name"].dataCell = nameCell;
FilePriorityCell* priorityCell = [[FilePriorityCell alloc] init];
[self tableColumnWithIdentifier:@"Priority"].dataCell = priorityCell;
self.autoresizesOutlineColumn = NO; self.autoresizesOutlineColumn = NO;
self.indentationPerLevel = 14.0; self.indentationPerLevel = 14.0;
self.hoveredRow = -1;
} }
- (void)mouseDown:(NSEvent*)event - (void)mouseDown:(NSEvent*)event
@@ -59,61 +50,22 @@
- (NSRect)iconRectForRow:(NSInteger)row - (NSRect)iconRectForRow:(NSInteger)row
{ {
FileNameCell* cell = (FileNameCell*)[self preparedCellAtColumn:[self columnWithIdentifier:@"Name"] row:row]; NSView* view = [self viewAtColumn:[self columnWithIdentifier:@"Name"] row:row makeIfNecessary:NO];
NSRect iconRect = [cell imageRectForBounds:[self rectOfRow:row]]; if (![view isKindOfClass:[FileNameCellView class]])
{
return NSZeroRect;
}
FileNameCellView* cellView = (FileNameCellView*)view;
NSImageView* iconView = [cellView valueForKey:@"iconView"];
if (!iconView)
{
return NSZeroRect;
}
NSRect iconRect = [self convertRect:iconView.frame fromView:cellView];
iconRect.origin.x += self.indentationPerLevel * (CGFloat)([self levelForRow:row] + 1); iconRect.origin.x += self.indentationPerLevel * (CGFloat)([self levelForRow:row] + 1);
return iconRect; return iconRect;
} }
- (void)updateTrackingAreas
{
[super updateTrackingAreas];
for (NSTrackingArea* area in self.trackingAreas)
{
if (area.owner == self && area.userInfo[@"Row"])
{
[self removeTrackingArea:area];
}
}
NSRange visibleRows = [self rowsInRect:self.visibleRect];
if (visibleRows.length == 0)
{
return;
}
NSPoint mouseLocation = [self convertPoint:self.window.mouseLocationOutsideOfEventStream fromView:nil];
for (NSInteger row = visibleRows.location, col = [self columnWithIdentifier:@"Priority"]; (NSUInteger)row < NSMaxRange(visibleRows); row++)
{
FilePriorityCell* cell = (FilePriorityCell*)[self preparedCellAtColumn:col row:row];
NSDictionary* userInfo = @{ @"Row" : @(row) };
[cell addTrackingAreasForView:self inRect:[self frameOfCellAtColumn:col row:row] withUserInfo:userInfo
mouseLocation:mouseLocation];
}
}
- (void)mouseEntered:(NSEvent*)event
{
NSNumber* row;
if ((row = ((NSDictionary*)event.userData)[@"Row"]))
{
self.hoveredRow = row.intValue;
[self setNeedsDisplayInRect:[self frameOfCellAtColumn:[self columnWithIdentifier:@"Priority"] row:self.hoveredRow]];
}
}
- (void)mouseExited:(NSEvent*)event
{
NSNumber* row;
if ((row = ((NSDictionary*)event.userData)[@"Row"]))
{
[self setNeedsDisplayInRect:[self frameOfCellAtColumn:[self columnWithIdentifier:@"Priority"] row:row.intValue]];
self.hoveredRow = -1;
}
}
@end @end

View File

@@ -1,16 +0,0 @@
// This file Copyright © 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 FilePriorityCell : NSSegmentedCell
@property(nonatomic) BOOL hovered;
- (void)addTrackingAreasForView:(NSView*)controlView
inRect:(NSRect)cellFrame
withUserInfo:(NSDictionary*)userInfo
mouseLocation:(NSPoint)mouseLocation;
@end

View File

@@ -1,171 +0,0 @@
// This file Copyright © Transmission authors and contributors.
// It may be used under the MIT (SPDX: MIT) license.
// License text can be found in the licenses/ folder.
#import "FilePriorityCell.h"
#import "FileOutlineView.h"
#import "FileListNode.h"
#import "NSImageAdditions.h"
#import "Torrent.h"
static CGFloat const kImageOverlap = 1.0;
@implementation FilePriorityCell
- (instancetype)init
{
if ((self = [super init]))
{
self.trackingMode = NSSegmentSwitchTrackingSelectAny;
self.controlSize = NSControlSizeMini;
self.segmentCount = 3;
for (NSInteger i = 0; i < self.segmentCount; i++)
{
[self setLabel:@"" forSegment:i];
[self setWidth:9.0f forSegment:i]; //9 is minimum size to get proper look
}
[self setImage:[NSImage imageNamed:@"PriorityControlLow"] forSegment:0];
[self setImage:[NSImage imageNamed:@"PriorityControlNormal"] forSegment:1];
[self setImage:[NSImage imageNamed:@"PriorityControlHigh"] forSegment:2];
_hovered = NO;
}
return self;
}
- (id)copyWithZone:(NSZone*)zone
{
FilePriorityCell* copy = [super copyWithZone:zone];
[copy setRepresentedObject:self.representedObject];
return copy;
}
- (void)setSelected:(BOOL)flag forSegment:(NSInteger)segment
{
[super setSelected:flag forSegment:segment];
//only for when clicking manually
tr_priority_t priority;
switch (segment)
{
case 0:
priority = TR_PRI_LOW;
break;
case 1:
priority = TR_PRI_NORMAL;
break;
case 2:
priority = TR_PRI_HIGH;
break;
default:
NSAssert1(NO, @"Unknown segment: %ld", segment);
return;
}
FileListNode* node = self.representedObject;
Torrent* torrent = node.torrent;
[torrent setFilePriority:priority forIndexes:node.indexes];
FileOutlineView* controlView = (FileOutlineView*)self.controlView;
controlView.needsDisplay = YES;
}
- (void)addTrackingAreasForView:(NSView*)controlView
inRect:(NSRect)cellFrame
withUserInfo:(NSDictionary*)userInfo
mouseLocation:(NSPoint)mouseLocation
{
NSTrackingAreaOptions options = NSTrackingEnabledDuringMouseDrag | NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways;
if (NSMouseInRect(mouseLocation, cellFrame, controlView.flipped))
{
options |= NSTrackingAssumeInside;
[controlView setNeedsDisplayInRect:cellFrame];
}
NSTrackingArea* area = [[NSTrackingArea alloc] initWithRect:cellFrame options:options owner:controlView userInfo:userInfo];
[controlView addTrackingArea:area];
}
- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView
{
FileListNode* node = self.representedObject;
Torrent* torrent = node.torrent;
NSSet* priorities = [torrent filePrioritiesForIndexes:node.indexes];
NSUInteger const count = priorities.count;
if (self.hovered && count > 0)
{
[super setSelected:[priorities containsObject:@(TR_PRI_LOW)] forSegment:0];
[super setSelected:[priorities containsObject:@(TR_PRI_NORMAL)] forSegment:1];
[super setSelected:[priorities containsObject:@(TR_PRI_HIGH)] forSegment:2];
[super drawWithFrame:cellFrame inView:controlView];
}
else
{
NSMutableArray* images = [NSMutableArray arrayWithCapacity:MAX(count, 1u)];
CGFloat totalWidth;
if (count == 0)
{
//if ([self backgroundStyle] != NSBackgroundStyleEmphasized)
{
NSImage* image = [[NSImage imageNamed:@"PriorityNormalTemplate"] imageWithColor:NSColor.lightGrayColor];
[images addObject:image];
totalWidth = image.size.width;
}
}
else
{
NSColor* priorityColor = self.backgroundStyle == NSBackgroundStyleEmphasized ? NSColor.whiteColor : NSColor.darkGrayColor;
totalWidth = 0.0;
if ([priorities containsObject:@(TR_PRI_LOW)])
{
NSImage* image = [[NSImage imageNamed:@"PriorityLowTemplate"] imageWithColor:priorityColor];
[images addObject:image];
totalWidth += image.size.width;
}
if ([priorities containsObject:@(TR_PRI_NORMAL)])
{
NSImage* image = [[NSImage imageNamed:@"PriorityNormalTemplate"] imageWithColor:priorityColor];
[images addObject:image];
totalWidth += image.size.width;
}
if ([priorities containsObject:@(TR_PRI_HIGH)])
{
NSImage* image = [[NSImage imageNamed:@"PriorityHighTemplate"] imageWithColor:priorityColor];
[images addObject:image];
totalWidth += image.size.width;
}
}
if (count > 1)
{
totalWidth -= kImageOverlap * (count - 1);
}
CGFloat currentWidth = floor(NSMidX(cellFrame) - totalWidth * 0.5);
for (NSImage* image in images)
{
NSSize const imageSize = image.size;
NSRect const imageRect = NSMakeRect(
currentWidth,
floor(NSMidY(cellFrame) - imageSize.height * 0.5),
imageSize.width,
imageSize.height);
[image drawInRect:imageRect fromRect:NSZeroRect operation:NSCompositingOperationSourceOver fraction:1.0
respectFlipped:YES
hints:nil];
currentWidth += imageSize.width - kImageOverlap;
}
}
}
@end

View File

@@ -0,0 +1,14 @@
// This file Copyright © 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 FileListNode;
@interface FilePriorityCellView : NSTableCellView
@property(nonatomic, weak) FileListNode* node;
@property(nonatomic) BOOL hovered;
@end

View File

@@ -0,0 +1,313 @@
// This file Copyright © Transmission authors and contributors.
// It may be used under the MIT (SPDX: MIT) license.
// License text can be found in the licenses/ folder.
#import "FilePriorityCellView.h"
#import "FileListNode.h"
#import "NSImageAdditions.h"
#import "Torrent.h"
static CGFloat const kImageOverlap = 1.0;
@interface FilePriorityCellView ()
@property(nonatomic, weak) NSSegmentedControl* segmentedControl;
@property(nonatomic, weak) NSView* iconsContainerView;
@property(nonatomic, strong) NSTrackingArea* trackingArea;
@end
@implementation FilePriorityCellView
- (instancetype)initWithFrame:(NSRect)frameRect
{
if ((self = [super initWithFrame:frameRect]))
{
// Create segmented control for hover state
NSSegmentedControl* segmentedControl = [[NSSegmentedControl alloc] initWithFrame:NSZeroRect];
segmentedControl.translatesAutoresizingMaskIntoConstraints = NO;
segmentedControl.trackingMode = NSSegmentSwitchTrackingSelectAny;
segmentedControl.controlSize = NSControlSizeMini;
segmentedControl.segmentCount = 3;
for (NSInteger i = 0; i < segmentedControl.segmentCount; i++)
{
[segmentedControl setLabel:@"" forSegment:i];
[segmentedControl setWidth:9.0f forSegment:i];
}
[segmentedControl setImage:[NSImage imageNamed:@"PriorityControlLow"] forSegment:0];
[segmentedControl setImage:[NSImage imageNamed:@"PriorityControlNormal"] forSegment:1];
[segmentedControl setImage:[NSImage imageNamed:@"PriorityControlHigh"] forSegment:2];
segmentedControl.target = self;
segmentedControl.action = @selector(segmentedControlClicked:);
segmentedControl.hidden = YES;
[self addSubview:segmentedControl];
_segmentedControl = segmentedControl;
// Create container view for priority icons
NSView* iconsContainerView = [[NSView alloc] initWithFrame:NSZeroRect];
iconsContainerView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:iconsContainerView];
_iconsContainerView = iconsContainerView;
// Setup constraints
[NSLayoutConstraint activateConstraints:@[
[segmentedControl.centerXAnchor constraintEqualToAnchor:self.centerXAnchor],
[segmentedControl.centerYAnchor constraintEqualToAnchor:self.centerYAnchor],
[iconsContainerView.centerXAnchor constraintEqualToAnchor:self.centerXAnchor],
[iconsContainerView.centerYAnchor constraintEqualToAnchor:self.centerYAnchor],
[iconsContainerView.widthAnchor constraintLessThanOrEqualToAnchor:self.widthAnchor],
[iconsContainerView.heightAnchor constraintLessThanOrEqualToAnchor:self.heightAnchor],
]];
_hovered = NO;
}
return self;
}
- (void)setNode:(FileListNode*)node
{
_node = node;
[self updateDisplay];
}
- (void)setHovered:(BOOL)hovered
{
_hovered = hovered;
[self updateDisplay];
}
- (void)updateDisplay
{
if (!self.node)
{
return;
}
FileListNode* node = self.node;
Torrent* torrent = node.torrent;
NSSet* priorities = [torrent filePrioritiesForIndexes:node.indexes];
NSUInteger const count = priorities.count;
if (self.hovered && count > 0)
{
// Show segmented control
self.segmentedControl.hidden = NO;
self.iconsContainerView.hidden = YES;
[self.segmentedControl setSelected:[priorities containsObject:@(TR_PRI_LOW)] forSegment:0];
[self.segmentedControl setSelected:[priorities containsObject:@(TR_PRI_NORMAL)] forSegment:1];
[self.segmentedControl setSelected:[priorities containsObject:@(TR_PRI_HIGH)] forSegment:2];
}
else
{
// Show static priority icons
self.segmentedControl.hidden = YES;
self.iconsContainerView.hidden = NO;
[self updatePriorityIcons:priorities];
}
// Update tooltip
[self updateTooltip];
}
- (void)updatePriorityIcons:(NSSet*)priorities
{
// Remove all existing image views
for (NSView* subview in self.iconsContainerView.subviews)
{
[subview removeFromSuperview];
}
NSUInteger const count = priorities.count;
NSMutableArray* images = [NSMutableArray arrayWithCapacity:MAX(count, 1u)];
if (count == 0)
{
NSImage* image = [[NSImage imageNamed:@"PriorityNormalTemplate"] imageWithColor:NSColor.lightGrayColor];
[images addObject:image];
}
else
{
NSColor* priorityColor = self.backgroundStyle == NSBackgroundStyleEmphasized ? NSColor.whiteColor : NSColor.darkGrayColor;
if ([priorities containsObject:@(TR_PRI_LOW)])
{
NSImage* image = [[NSImage imageNamed:@"PriorityLowTemplate"] imageWithColor:priorityColor];
[images addObject:image];
}
if ([priorities containsObject:@(TR_PRI_NORMAL)])
{
NSImage* image = [[NSImage imageNamed:@"PriorityNormalTemplate"] imageWithColor:priorityColor];
[images addObject:image];
}
if ([priorities containsObject:@(TR_PRI_HIGH)])
{
NSImage* image = [[NSImage imageNamed:@"PriorityHighTemplate"] imageWithColor:priorityColor];
[images addObject:image];
}
}
NSView* previousView = nil;
for (NSImage* image in images)
{
NSImageView* imageView = [[NSImageView alloc] initWithFrame:NSZeroRect];
imageView.translatesAutoresizingMaskIntoConstraints = NO;
imageView.image = image;
[self.iconsContainerView addSubview:imageView];
NSSize const imageSize = image.size;
[NSLayoutConstraint activateConstraints:@[
[imageView.widthAnchor constraintEqualToConstant:imageSize.width],
[imageView.heightAnchor constraintEqualToConstant:imageSize.height],
[imageView.centerYAnchor constraintEqualToAnchor:self.iconsContainerView.centerYAnchor],
]];
if (previousView == nil)
{
[imageView.leadingAnchor constraintEqualToAnchor:self.iconsContainerView.leadingAnchor].active = YES;
}
else
{
[imageView.leadingAnchor constraintEqualToAnchor:previousView.trailingAnchor constant:-kImageOverlap].active = YES;
}
previousView = imageView;
}
if (previousView)
{
[previousView.trailingAnchor constraintEqualToAnchor:self.iconsContainerView.trailingAnchor].active = YES;
}
}
- (void)segmentedControlClicked:(NSSegmentedControl*)sender
{
NSInteger segment = sender.selectedSegment;
if (segment == -1)
{
return;
}
tr_priority_t priority;
switch (segment)
{
case 0:
priority = TR_PRI_LOW;
break;
case 1:
priority = TR_PRI_NORMAL;
break;
case 2:
priority = TR_PRI_HIGH;
break;
default:
NSAssert1(NO, @"Unknown segment: %ld", segment);
return;
}
FileListNode* node = self.node;
Torrent* torrent = node.torrent;
[torrent setFilePriority:priority forIndexes:node.indexes];
// Notify that we need to refresh
[NSNotificationCenter.defaultCenter postNotificationName:@"UpdateUI" object:nil];
}
- (void)setBackgroundStyle:(NSBackgroundStyle)backgroundStyle
{
[super setBackgroundStyle:backgroundStyle];
[self updateDisplay];
}
- (void)updateTrackingAreas
{
[super updateTrackingAreas];
if (self.trackingArea)
{
[self removeTrackingArea:self.trackingArea];
}
NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | NSTrackingActiveInActiveApp;
// Check if mouse is currently inside the bounds
NSPoint mouseLocation = [self.window mouseLocationOutsideOfEventStream];
NSPoint localPoint = [self convertPoint:mouseLocation fromView:nil];
if (NSPointInRect(localPoint, self.bounds))
{
options |= NSTrackingAssumeInside;
if (!self.hovered)
{
self.hovered = YES;
}
}
else
{
// Mouse is not inside, reset hovered state
if (self.hovered)
{
self.hovered = NO;
}
}
self.trackingArea = [[NSTrackingArea alloc] initWithRect:self.bounds options:options owner:self userInfo:nil];
[self addTrackingArea:self.trackingArea];
}
- (void)mouseEntered:(NSEvent*)event
{
self.hovered = YES;
}
- (void)mouseExited:(NSEvent*)event
{
self.hovered = NO;
}
- (void)updateTooltip
{
if (!self.node)
{
return;
}
FileListNode* node = self.node;
Torrent* torrent = node.torrent;
NSSet* priorities = [torrent filePrioritiesForIndexes:node.indexes];
NSString* tooltip = nil;
switch (priorities.count)
{
case 0:
tooltip = NSLocalizedString(@"Priority Not Available", "files tab -> tooltip");
break;
case 1:
switch ([[priorities anyObject] intValue])
{
case TR_PRI_LOW:
tooltip = NSLocalizedString(@"Low Priority", "files tab -> tooltip");
break;
case TR_PRI_HIGH:
tooltip = NSLocalizedString(@"High Priority", "files tab -> tooltip");
break;
case TR_PRI_NORMAL:
tooltip = NSLocalizedString(@"Normal Priority", "files tab -> tooltip");
break;
}
break;
default:
tooltip = NSLocalizedString(@"Multiple Priorities", "files tab -> tooltip");
break;
}
self.toolTip = tooltip;
}
@end

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?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="24412" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies> <dependencies>
<deployment identifier="macosx"/> <deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/> <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="24412"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<objects> <objects>
@@ -20,8 +20,8 @@
<customView clipsToBounds="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2" userLabel="Files"> <customView clipsToBounds="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2" userLabel="Files">
<rect key="frame" x="0.0" y="0.0" width="340" height="365"/> <rect key="frame" x="0.0" y="0.0" width="340" height="365"/>
<subviews> <subviews>
<searchField wantsLayer="YES" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3"> <searchField wantsLayer="YES" focusRingType="none" verticalHuggingPriority="750" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3">
<rect key="frame" x="12" y="12" width="110" height="19"/> <rect key="frame" x="12" y="12" width="110" height="20"/>
<constraints> <constraints>
<constraint firstAttribute="width" constant="110" id="sKS-V5-H9b"/> <constraint firstAttribute="width" constant="110" id="sKS-V5-H9b"/>
</constraints> </constraints>
@@ -35,19 +35,19 @@
</connections> </connections>
</searchField> </searchField>
<scrollView horizontalLineScroll="36" horizontalPageScroll="10" verticalLineScroll="36" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4"> <scrollView horizontalLineScroll="36" horizontalPageScroll="10" verticalLineScroll="36" verticalPageScroll="10" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4">
<rect key="frame" x="12" y="39" width="316" height="314"/> <rect key="frame" x="12" y="40" width="316" height="313"/>
<clipView key="contentView" id="l96-jk-uz9"> <clipView key="contentView" id="l96-jk-uz9">
<rect key="frame" x="1" y="1" width="314" height="312"/> <rect key="frame" x="1" y="1" width="301" height="311"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<outlineView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnReordering="NO" columnResizing="NO" autosaveColumns="NO" rowHeight="34" indentationPerLevel="16" autoresizesOutlineColumn="YES" outlineTableColumn="10" id="7" customClass="FileOutlineView"> <outlineView verticalHuggingPriority="750" allowsExpansionToolTips="YES" alternatingRowBackgroundColors="YES" columnReordering="NO" columnResizing="NO" autosaveColumns="NO" rowHeight="34" rowSizeStyle="automatic" viewBased="YES" indentationPerLevel="16" autoresizesOutlineColumn="YES" outlineTableColumn="10" id="7" customClass="FileOutlineView">
<rect key="frame" x="0.0" y="0.0" width="314" height="312"/> <rect key="frame" x="0.0" y="0.0" width="301" height="311"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="2"/> <size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/> <color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns> <tableColumns>
<tableColumn identifier="Name" editable="NO" width="231" minWidth="38.599119999999999" maxWidth="1000" id="10"> <tableColumn identifier="Name" editable="NO" width="218" minWidth="38.599119999999999" maxWidth="1000" id="10">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Name"> <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Name">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="0.33333299" alpha="1" colorSpace="calibratedWhite"/> <color key="backgroundColor" white="0.33333299" alpha="1" colorSpace="calibratedWhite"/>
@@ -58,6 +58,26 @@
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES"/> <tableColumnResizingMask key="resizingMask" resizeWithTable="YES"/>
<prototypeCellViews>
<tableCellView id="ddm-cs-nIR">
<rect key="frame" x="1" y="1" width="223" height="34"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="HIK-di-9tP">
<rect key="frame" x="0.0" y="9" width="223" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="D7f-0h-h8A">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="HIK-di-9tP" id="bHT-uH-6AK"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn> </tableColumn>
<tableColumn identifier="Priority" editable="NO" width="34" minWidth="10" maxWidth="1000" id="8"> <tableColumn identifier="Priority" editable="NO" width="34" minWidth="10" maxWidth="1000" id="8">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Rank"> <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Rank">
@@ -69,6 +89,26 @@
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/> <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/> <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell> </textFieldCell>
<prototypeCellViews>
<tableCellView id="4RH-Kf-WF8">
<rect key="frame" x="227" y="1" width="34" height="34"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DCs-8X-Mcb">
<rect key="frame" x="0.0" y="9" width="34" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="Doc-xO-gx2">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="DCs-8X-Mcb" id="ad1-R3-pcd"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn> </tableColumn>
<tableColumn identifier="Check" editable="NO" width="31" minWidth="10" maxWidth="1000" id="9"> <tableColumn identifier="Check" editable="NO" width="31" minWidth="10" maxWidth="1000" id="9">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="DL"> <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="DL">
@@ -79,6 +119,26 @@
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/> <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="cellTitle"/> <font key="font" metaFont="cellTitle"/>
</buttonCell> </buttonCell>
<prototypeCellViews>
<tableCellView id="l8k-SJ-e7E">
<rect key="frame" x="264" y="1" width="35" height="34"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="CkN-iw-rOX">
<rect key="frame" x="0.0" y="9" width="35" height="16"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="a2Y-Cf-rdu">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<connections>
<outlet property="textField" destination="CkN-iw-rOX" id="Thn-jw-ARn"/>
</connections>
</tableCellView>
</prototypeCellViews>
</tableColumn> </tableColumn>
</tableColumns> </tableColumns>
<connections> <connections>
@@ -98,12 +158,12 @@
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
</scroller> </scroller>
<scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" controlSize="small" horizontal="NO" id="6"> <scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" controlSize="small" horizontal="NO" id="6">
<rect key="frame" x="301" y="1" width="14" height="312"/> <rect key="frame" x="302" y="1" width="13" height="311"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
</scroller> </scroller>
</scrollView> </scrollView>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="25"> <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="25">
<rect key="frame" x="286" y="12" width="42" height="17"/> <rect key="frame" x="280" y="12" width="48" height="20"/>
<buttonCell key="cell" type="roundRect" title="None" bezelStyle="roundedRect" alignment="center" controlSize="small" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="26"> <buttonCell key="cell" type="roundRect" title="None" bezelStyle="roundedRect" alignment="center" controlSize="small" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="26">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="smallSystem"/> <font key="font" metaFont="smallSystem"/>
@@ -113,7 +173,7 @@
</connections> </connections>
</button> </button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="27"> <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="27">
<rect key="frame" x="236" y="12" width="42" height="17"/> <rect key="frame" x="224" y="12" width="48" height="20"/>
<buttonCell key="cell" type="roundRect" title="All" bezelStyle="roundedRect" alignment="center" controlSize="small" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="28"> <buttonCell key="cell" type="roundRect" title="All" bezelStyle="roundedRect" alignment="center" controlSize="small" state="on" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="28">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/> <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="smallSystem"/> <font key="font" metaFont="smallSystem"/>

View File

@@ -88,13 +88,13 @@
if (self.fTorrents.count == 1) if (self.fTorrents.count == 1)
{ {
[self.fFileController refresh]; [self.fFileController reloadVisibleRows];
#warning use TorrentFileCheckChange notification as well #warning use TorrentFileCheckChange notification as well
Torrent* torrent = self.fTorrents[0]; Torrent* torrent = self.fTorrents[0];
if (torrent.folder) if (torrent.folder)
{ {
NSInteger const filesCheckState = [torrent NSControlStateValue const filesCheckState = [torrent
checkForFiles:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, torrent.fileCount)]]; checkForFiles:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, torrent.fileCount)]];
self.fCheckAllButton.enabled = filesCheckState != NSControlStateValueOn; //if anything is unchecked self.fCheckAllButton.enabled = filesCheckState != NSControlStateValueOn; //if anything is unchecked
self.fUncheckAllButton.enabled = !torrent.allDownloaded; //if there are any checked files that aren't finished self.fUncheckAllButton.enabled = !torrent.allDownloaded; //if there are any checked files that aren't finished

View File

@@ -195,8 +195,8 @@ extern NSString* const kTorrentDidChangeGroupNotification;
- (CGFloat)fileProgress:(FileListNode*)node; - (CGFloat)fileProgress:(FileListNode*)node;
- (BOOL)canChangeDownloadCheckForFile:(NSUInteger)index; - (BOOL)canChangeDownloadCheckForFile:(NSUInteger)index;
- (BOOL)canChangeDownloadCheckForFiles:(NSIndexSet*)indexSet; - (BOOL)canChangeDownloadCheckForFiles:(NSIndexSet*)indexSet;
- (NSInteger)checkForFiles:(NSIndexSet*)indexSet; - (NSControlStateValue)checkForFiles:(NSIndexSet*)indexSet;
- (void)setFileCheckState:(NSInteger)state forIndexes:(NSIndexSet*)indexSet; - (void)setFileCheckState:(NSControlStateValue)state forIndexes:(NSIndexSet*)indexSet;
- (void)setFilePriority:(tr_priority_t)priority forIndexes:(NSIndexSet*)indexSet; - (void)setFilePriority:(tr_priority_t)priority forIndexes:(NSIndexSet*)indexSet;
- (BOOL)hasFilePriority:(tr_priority_t)priority forIndexes:(NSIndexSet*)indexSet; - (BOOL)hasFilePriority:(tr_priority_t)priority forIndexes:(NSIndexSet*)indexSet;
- (NSSet*)filePrioritiesForIndexes:(NSIndexSet*)indexSet; - (NSSet*)filePrioritiesForIndexes:(NSIndexSet*)indexSet;

View File

@@ -1547,7 +1547,7 @@ bool trashDataFile(char const* filename, void* /*user_data*/, tr_error* error)
return canChange; return canChange;
} }
- (NSInteger)checkForFiles:(NSIndexSet*)indexSet - (NSControlStateValue)checkForFiles:(NSIndexSet*)indexSet
{ {
BOOL onState = NO, offState = NO; BOOL onState = NO, offState = NO;
for (NSUInteger index = indexSet.firstIndex; index != NSNotFound; index = [indexSet indexGreaterThanIndex:index]) for (NSUInteger index = indexSet.firstIndex; index != NSNotFound; index = [indexSet indexGreaterThanIndex:index])
@@ -1570,7 +1570,7 @@ bool trashDataFile(char const* filename, void* /*user_data*/, tr_error* error)
return onState ? NSControlStateValueOn : NSControlStateValueOff; return onState ? NSControlStateValueOn : NSControlStateValueOff;
} }
- (void)setFileCheckState:(NSInteger)state forIndexes:(NSIndexSet*)indexSet - (void)setFileCheckState:(NSControlStateValue)state forIndexes:(NSIndexSet*)indexSet
{ {
NSUInteger count = indexSet.count; NSUInteger count = indexSet.count;
tr_file_index_t* files = static_cast<tr_file_index_t*>(malloc(count * sizeof(tr_file_index_t))); tr_file_index_t* files = static_cast<tr_file_index_t*>(malloc(count * sizeof(tr_file_index_t)));