// 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 "InfoFileViewController.h" #import "FileListNode.h" #import "FileOutlineController.h" #import "FileOutlineView.h" #import "Torrent.h" @interface InfoFileViewController () @property(nonatomic, copy) NSArray* fTorrents; @property(nonatomic) BOOL fSet; @property(nonatomic) IBOutlet FileOutlineController* fFileController; @property(nonatomic) IBOutlet NSSearchField* fFileFilterField; @property(nonatomic) IBOutlet NSButton* fCheckAllButton; @property(nonatomic) IBOutlet NSButton* fUncheckAllButton; @end @implementation InfoFileViewController - (instancetype)init { if ((self = [super initWithNibName:@"InfoFileView" bundle:nil])) { self.title = NSLocalizedString(@"Files", "Inspector view -> title"); } return self; } - (void)awakeFromNib { [super awakeFromNib]; CGFloat const height = [NSUserDefaults.standardUserDefaults floatForKey:@"InspectorContentHeightFiles"]; if (height != 0.0) { NSRect viewRect = self.view.frame; viewRect.size.height = height; self.view.frame = viewRect; } [self.fFileFilterField.cell setPlaceholderString:NSLocalizedString(@"Filter", "inspector -> file filter")]; //localize and place all and none buttons self.fCheckAllButton.title = NSLocalizedString(@"All", "inspector -> check all"); self.fUncheckAllButton.title = NSLocalizedString(@"None", "inspector -> check all"); NSRect checkAllFrame = self.fCheckAllButton.frame; NSRect uncheckAllFrame = self.fUncheckAllButton.frame; CGFloat const oldAllWidth = checkAllFrame.size.width; CGFloat const oldNoneWidth = uncheckAllFrame.size.width; [self.fCheckAllButton sizeToFit]; [self.fUncheckAllButton sizeToFit]; CGFloat const newWidth = MAX(self.fCheckAllButton.bounds.size.width, self.fUncheckAllButton.bounds.size.width); CGFloat const uncheckAllChange = newWidth - oldNoneWidth; uncheckAllFrame.size.width = newWidth; uncheckAllFrame.origin.x -= uncheckAllChange; self.fUncheckAllButton.frame = uncheckAllFrame; CGFloat const checkAllChange = newWidth - oldAllWidth; checkAllFrame.size.width = newWidth; checkAllFrame.origin.x -= (checkAllChange + uncheckAllChange); self.fCheckAllButton.frame = checkAllFrame; } - (void)setInfoForTorrents:(NSArray*)torrents { //don't check if it's the same in case the metadata changed self.fTorrents = torrents; self.fSet = NO; } - (void)updateInfo { if (!self.fSet) { [self setupInfo]; } if (self.fTorrents.count == 1) { [self.fFileController refresh]; #warning use TorrentFileCheckChange notification as well Torrent* torrent = self.fTorrents[0]; if (torrent.folder) { NSInteger const filesCheckState = [torrent checkForFiles:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, torrent.fileCount)]]; self.fCheckAllButton.enabled = filesCheckState != NSControlStateValueOn; //if anything is unchecked self.fUncheckAllButton.enabled = !torrent.allDownloaded; //if there are any checked files that aren't finished } } } - (void)saveViewSize { [NSUserDefaults.standardUserDefaults setFloat:NSHeight(self.view.frame) forKey:@"InspectorContentHeightFiles"]; } - (void)setFileFilterText:(id)sender { self.fFileController.filterText = [sender stringValue]; } - (IBAction)checkAll:(id)sender { [self.fFileController checkAll]; } - (IBAction)uncheckAll:(id)sender { [self.fFileController uncheckAll]; } - (void)keyDown:(NSEvent*)event { unichar const firstChar = [event.charactersIgnoringModifiers characterAtIndex:0]; if (firstChar == ' ') { [self toggleQuickLook:nil]; } else { [super keyDown:event]; } } - (void)toggleQuickLook:(id)sender { if ([QLPreviewPanel sharedPreviewPanel].visible) { [[QLPreviewPanel sharedPreviewPanel] orderOut:nil]; } else { [[QLPreviewPanel sharedPreviewPanel] makeKeyAndOrderFront:nil]; } } - (NSArray*)quickLookURLs { FileOutlineView* fileOutlineView = self.fFileController.outlineView; Torrent* torrent = self.fTorrents[0]; NSIndexSet* indexes = fileOutlineView.selectedRowIndexes; NSMutableArray* urlArray = [NSMutableArray arrayWithCapacity:indexes.count]; for (NSUInteger i = indexes.firstIndex; i != NSNotFound; i = [indexes indexGreaterThanIndex:i]) { FileListNode* item = [fileOutlineView itemAtRow:i]; if ([self canQuickLookFile:item]) { [urlArray addObject:[NSURL fileURLWithPath:[torrent fileLocation:item]]]; } } return urlArray; } - (BOOL)canQuickLook { if (self.fTorrents.count != 1) { return NO; } Torrent* torrent = self.fTorrents[0]; if (!torrent.folder) { return NO; } FileOutlineView* fileOutlineView = self.fFileController.outlineView; NSIndexSet* indexes = fileOutlineView.selectedRowIndexes; for (NSUInteger i = indexes.firstIndex; i != NSNotFound; i = [indexes indexGreaterThanIndex:i]) { if ([self canQuickLookFile:[fileOutlineView itemAtRow:i]]) { return YES; } } return NO; } - (NSRect)quickLookSourceFrameForPreviewItem:(id)item { FileOutlineView* fileOutlineView = self.fFileController.outlineView; NSString* fullPath = ((NSURL*)item).path; Torrent* torrent = self.fTorrents[0]; NSRange visibleRows = [fileOutlineView rowsInRect:fileOutlineView.bounds]; for (NSUInteger row = visibleRows.location; row < NSMaxRange(visibleRows); row++) { FileListNode* rowItem = [fileOutlineView itemAtRow:row]; if ([[torrent fileLocation:rowItem] isEqualToString:fullPath]) { NSRect frame = [fileOutlineView iconRectForRow:row]; if (!NSIntersectsRect(fileOutlineView.visibleRect, frame)) { return NSZeroRect; } frame.origin = [fileOutlineView convertPoint:frame.origin toView:nil]; frame = [self.view.window convertRectToScreen:frame]; frame.origin.y -= frame.size.height; return frame; } } return NSZeroRect; } #pragma mark - Private - (void)setupInfo { self.fFileFilterField.stringValue = @""; [self setFileFilterText:self.fFileFilterField]; if (self.fTorrents.count == 1) { Torrent* torrent = self.fTorrents[0]; self.fFileController.torrent = torrent; BOOL const isFolder = torrent.folder; self.fFileFilterField.enabled = isFolder; if (!isFolder) { self.fCheckAllButton.enabled = NO; self.fUncheckAllButton.enabled = NO; } } else { self.fFileController.torrent = nil; self.fFileFilterField.enabled = NO; self.fCheckAllButton.enabled = NO; self.fUncheckAllButton.enabled = NO; } self.fSet = YES; } - (BOOL)canQuickLookFile:(FileListNode*)item { Torrent* torrent = self.fTorrents[0]; return (item.isFolder || [torrent fileProgress:item] >= 1.0) && [torrent fileLocation:item]; } @end