Files
transshift/TransmissionRPCClient/FileListController.m
2015-08-17 00:57:11 +03:00

649 lines
21 KiB
Objective-C

//
// FileListController.m
// TransmissionRPCClient
//
// Created by Alexey Chechetkin on 30.06.15.
// Copyright (c) 2015 Alexey Chechetkin. All rights reserved.
//
#import "FileListController.h"
//#import "FileListCell.h"
#import "FSDirectory.h"
#import "FileListFSCell.h"
#import "TRFileInfo.h"
#import "NSObject+DataObject.h"
#define ICON_FILE @"iconFile"
#define ICON_FOLDER_OPENED @"iconFolderOpened"
#define ICON_FOLDER_CLOSED @"iconFolderClosed"
@interface FileListController ()
@end
@implementation FileListController
{
UIImage *_iconImgFile;
UIImage *_iconImgFolderOpened;
UIImage *_iconImgFolderClosed;
//FSDirectory *_fsDir;
BOOL _isSelectOnly;
FSItem *_curItem;
BOOL _needUpdateFolders;
UIBarButtonItem *_btnCheckAll;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// preload images
_iconImgFile = [[UIImage imageNamed:ICON_FILE] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
_iconImgFolderOpened = [[UIImage imageNamed:ICON_FOLDER_OPENED] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
_iconImgFolderClosed = [[UIImage imageNamed:ICON_FOLDER_CLOSED] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
self.refreshControl = refreshControl;
[self.refreshControl addTarget:self action:@selector(askDelegateForDataUpdate) forControlEvents:UIControlEventValueChanged];
_btnCheckAll = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"iconCheckAll22x22"] style:UIBarButtonItemStyleBordered target:self action:@selector(toggleDownloadAllItems)];
_btnCheckAll.enabled = (_fsDir != nil);
if( _isFullyLoaded )
_btnCheckAll.enabled = NO;
UIBarButtonItem *btnSpacer = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
self.toolbarItems = @[ _btnCheckAll, btnSpacer ];
}
- (void)toggleDownloadAllItems
{
FSItem *item = _fsDir.rootItem;
BOOL wanted = !item.wanted;
item.wanted = wanted;
if( !_selectOnly )
{
if( _delegate && wanted &&
[_delegate respondsToSelector:@selector(fileListControllerResumeDownloadingFilesWithIndexes:forTorrentWithId:)])
{
NSArray *fileIndexes = item.rpcFileIndexes;
[_delegate fileListControllerResumeDownloadingFilesWithIndexes:fileIndexes
forTorrentWithId:_torrentId];
}
else if( _delegate && !wanted &&
[_delegate respondsToSelector:@selector(fileListControllerStopDownloadingFilesWithIndexes:forTorrentWithId:)])
{
NSArray *fileIndexes = item.rpcFileIndexes;
[_delegate fileListControllerStopDownloadingFilesWithIndexes:fileIndexes
forTorrentWithId:_torrentId];
}
_btnCheckAll.enabled = NO;
}
[self.tableView reloadData];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.navigationItem.leftBarButtonItem.title = NSLocalizedString(@"Info", @"FileListController nav left button title");
self.navigationController.toolbarHidden = NO;
}
- (void)viewWillDisappear:(BOOL)animated
{
self.navigationController.toolbarHidden = YES;
}
- (void)askDelegateForDataUpdate
{
[self.refreshControl endRefreshing];
if( !_isFullyLoaded && _delegate && [_delegate respondsToSelector:@selector(fileListControllerNeedUpdateFilesForTorrentWithId:)])
[_delegate fileListControllerNeedUpdateFilesForTorrentWithId:_torrentId];
}
- (void)stoppedToDownloadFilesWithIndexes:(NSArray *)indexes
{
_btnCheckAll.enabled = YES;
[self askDelegateForDataUpdate];
}
- (void)resumedToDownloadFilesWithIndexes:(NSArray *)indexes
{
_btnCheckAll.enabled = YES;
[self askDelegateForDataUpdate];
}
- (void)setFsDir:(FSDirectory *)fsDir
{
_fsDir = fsDir;
_isFullyLoaded = fsDir.rootItem.downloadProgress >= 1.0f;
[_fsDir recalcRowIndexes];
_btnCheckAll.enabled = _selectOnly ? NO : !_isFullyLoaded;
[self.tableView reloadData];
}
- (void)updateFiles:(NSArray *)fileStats
{
if( _curItem.isFile )
{
// update file item
BOOL needUpdateCell = NO;
BOOL justDownloaded = NO;
TRFileStat *fileStat = fileStats[ _curItem.rpcIndex ];
if( fileStat.bytesComplited != _curItem.bytesComplited )
{
needUpdateCell = YES;
_curItem.bytesComplited = fileStat.bytesComplited;
if( _curItem.downloadProgress >= 1.0f )
justDownloaded = YES;
}
if ( _curItem.waitingForWantedUpdate && fileStat.wanted == _curItem.wanted )
{
needUpdateCell = YES;
}
else if( fileStat.wanted != _curItem.wanted )
{
needUpdateCell = YES;
}
if( fileStat.priority != _curItem.priority )
{
needUpdateCell = YES;
_curItem.priority = fileStat.priority;
}
if( needUpdateCell )
{
_needUpdateFolders = YES;
NSUInteger row = _curItem.rowIndex;
if( row != FSITEM_INDEXNOTFOUND )
{
// get cell
FileListFSCell *cell = (FileListFSCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:row inSection:0]];
if( cell )
{
if( _curItem.waitingForWantedUpdate && _curItem.wanted == fileStat.wanted )
{
[self updateFileCell:cell withFSItem:_curItem updateWanted:NO];
cell.checkBox.enabled = YES;
_curItem.waitingForWantedUpdate = NO;
}
else
[self updateFileCell:cell withFSItem:_curItem updateWanted:YES];
if( !cell.prioritySegment.enabled && cell.prioritySegment.selectedSegmentIndex == (fileStat.priority + 1) )
cell.prioritySegment.enabled = YES;
if( justDownloaded )
{
[UIView animateWithDuration:0.8 animations:^{
cell.iconImg.transform = CGAffineTransformMakeScale(1.2, 1.2);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3 animations:^{
cell.iconImg.transform = CGAffineTransformIdentity;
}];
}];
}
}
}
}
}
else
{
for( FSItem *i in _curItem.items )
{
_curItem = i;
[self updateFiles:fileStats];
}
}
}
- (void)updateFolders
{
if( _curItem.isFolder )
{
NSUInteger row = _curItem.rowIndex;
if( row != FSITEM_INDEXNOTFOUND )
{
FileListFSCell *cell = (FileListFSCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:row inSection:0]];
if( cell )
{
[self updateFolderCell:cell withFSItem:_curItem updateWanted:NO];
if( !cell.checkBox.enabled && cell.checkBox.on == _curItem.wanted )
cell.checkBox.enabled = YES;
}
}
for( FSItem *i in _curItem.items )
{
_curItem = i;
[self updateFolders];
}
}
}
// update file infos
- (void)setFileStats:(NSArray *)fileStats
{
//NSLog(@"%s", __PRETTY_FUNCTION__);
_curItem = _fsDir.rootItem;
_needUpdateFolders = NO;
[self updateFiles:fileStats];
if( _needUpdateFolders )
{
_curItem = _fsDir.rootItem;
[_fsDir setNeedToRecalcStats];
[self updateFolders];
_isFullyLoaded = _fsDir.rootItem.downloadProgress >= 1.0f;
}
}
- (void)toggleFolderDownloading:(CheckBox *)sender
{
FSItem *item = sender.dataObject;
BOOL wanted = !item.wanted;
item.wanted = wanted;
NSUInteger idx = item.rowIndex;
item.waitingForWantedUpdate = YES;
if( idx != FSITEM_INDEXNOTFOUND )
{
FileListFSCell *cell = (FileListFSCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:idx inSection:0]];
if( cell )
{
[self updateFolderCell:cell withFSItem:item updateWanted:NO];
//[cell.checkBox setOn:wanted animated:YES];
//cell.checkBox.enabled = _selectOnly;
}
}
[self updateFilesForFolderItem:item wanted:wanted];
[_fsDir setNeedToRecalcStats];
[self updateParentFolderForItem:item];
if( _delegate && wanted &&
[_delegate respondsToSelector:@selector(fileListControllerResumeDownloadingFilesWithIndexes:forTorrentWithId:)])
{
NSArray *fileIndexes = item.rpcFileIndexes;
[_delegate fileListControllerResumeDownloadingFilesWithIndexes:fileIndexes
forTorrentWithId:_torrentId];
}
else if( _delegate && !wanted &&
[_delegate respondsToSelector:@selector(fileListControllerStopDownloadingFilesWithIndexes:forTorrentWithId:)])
{
NSArray *fileIndexes = item.rpcFileIndexes;
[_delegate fileListControllerStopDownloadingFilesWithIndexes:fileIndexes
forTorrentWithId:_torrentId];
}
}
- (void)updateFilesForFolderItem:(FSItem *)item wanted:(BOOL)wanted
{
for( FSItem *i in item.items )
{
i.waitingForWantedUpdate = YES;
NSUInteger idx = i.rowIndex;
if( idx != FSITEM_INDEXNOTFOUND )
{
FileListFSCell *cell = (FileListFSCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:idx inSection:0]];
if( cell )
{
if( i.isFile )
[self updateFileCell:cell withFSItem:i updateWanted:NO];
else
[self updateFolderCell:cell withFSItem:i updateWanted:NO];
[cell.checkBox setOn:wanted animated:YES];
cell.checkBox.enabled = _selectOnly;
}
}
if( i.isFolder )
{
[self updateFilesForFolderItem:i wanted:wanted];
}
}
}
- (void)toggleFileDownloading:(CheckBox *)sender
{
FSItem* item = sender.dataObject;
BOOL wanted = !item.wanted;
item.wanted = wanted;
item.waitingForWantedUpdate = YES;
NSIndexPath *idxPath = [NSIndexPath indexPathForRow: item.rowIndex inSection:0];
FileListFSCell *cell = (FileListFSCell *)[self.tableView cellForRowAtIndexPath:idxPath];
if( cell )
{
[self updateFileCell:cell withFSItem:item updateWanted:NO];
//[cell.checkBox setOn:wanted animated:YES];
cell.checkBox.enabled = _selectOnly;
}
[_fsDir setNeedToRecalcStats];
[self updateParentFolderForItem:item];
// update server settings for this torrent
if( _delegate &&
wanted &&
[_delegate respondsToSelector:@selector(fileListControllerResumeDownloadingFilesWithIndexes:forTorrentWithId:)] )
{
[_delegate fileListControllerResumeDownloadingFilesWithIndexes:@[@(item.rpcIndex)] forTorrentWithId:_torrentId];
}
else if( _delegate &&
!wanted &&
[_delegate respondsToSelector:@selector(fileListControllerStopDownloadingFilesWithIndexes:forTorrentWithId:)] )
{
[_delegate fileListControllerStopDownloadingFilesWithIndexes:@[@(item.rpcIndex)] forTorrentWithId:_torrentId];
}
}
- (void)updateParentFolderForItem:(FSItem *)item
{
if( item.parent )
{
item = item.parent;
NSIndexPath *idxPath = [NSIndexPath indexPathForRow:item.rowIndex inSection:0];
FileListFSCell *cell = (FileListFSCell *)[self.tableView cellForRowAtIndexPath:idxPath];
if( cell )
{
if( cell.checkBox.on != item.wanted )
{
[self updateFolderCell:cell withFSItem:item updateWanted:NO];
[cell.checkBox setOn:item.wanted animated:YES];
}
}
// update parent of current item
[self updateParentFolderForItem:item];
}
}
- (void)prioritySegmentToggled:(UISegmentedControl*)sender
{
int priority = (int)sender.selectedSegmentIndex - 1;
if( _selectOnly )
{
return;
}
sender.enabled = NO;
if( _delegate && [_delegate respondsToSelector:@selector(fileListControllerSetPriority:forFilesWithIndexes:forTorrentWithId:)])
{
[_delegate fileListControllerSetPriority:priority forFilesWithIndexes:@[sender.dataObject] forTorrentWithId:_torrentId];
}
}
// toggle collapse flag
- (void)folderTapped:(UITapGestureRecognizer*)sender
{
FSItem *item = sender.dataObject;
item.collapsed = !item.collapsed;
[_fsDir recalcRowIndexes];
NSInteger itemIndex = item.rowIndex;
NSArray *indexPaths = [_fsDir childIndexesForItem:item startRow:itemIndex section:0];
[self.tableView beginUpdates];
if( item.collapsed )
[self.tableView deleteRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationTop];
else
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationBottom];
[self.tableView endUpdates];
FileListFSCell *cell = (FileListFSCell *)[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:itemIndex inSection:0]];
if( cell )
{
cell.iconImg.image = item.isCollapsed ? _iconImgFolderClosed : _iconImgFolderOpened;
}
}
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return _fsDir ? 1 : 0;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
return NSLocalizedString( @"Files & Folders", @"");
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return _fsDir.count;
}
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
FileListFSCell *cell = [tableView dequeueReusableCellWithIdentifier:CELL_ID_FILELISTFSCELL forIndexPath:indexPath];
FSItem *item = [_fsDir itemAtIndex:indexPath.row];
cell.nameLabel.text = item.name;
if( item.isFolder )
[self updateFolderCell:cell withFSItem:item updateWanted:YES];
else
[self updateFileCell:cell withFSItem:item updateWanted:YES];
return cell;
}
- (void)updateFileCell:(FileListFSCell *)cell withFSItem:(FSItem *)item updateWanted:(BOOL)updateWanted
{
// remove all old targets
[cell.checkBox removeTarget:self action:@selector(toggleFileDownloading:) forControlEvents:UIControlEventValueChanged];
[cell.checkBox removeTarget:self action:@selector(toggleFolderDownloading:) forControlEvents:UIControlEventValueChanged];
//cell.nameLabel.text = item.name;
cell.iconImg.image = _iconImgFile;
// make indentation
float leftIdent = ( (item.level - 1) * FILELISTFSCELL_LEFTLABEL_LEVEL_INDENTATION ) + 8;
float checkBoxWidth = 33;
if( _isFullyLoaded )
{
cell.checkBox.hidden = YES;
checkBoxWidth = 0;
}
else
cell.checkBox.hidden = NO;
cell.checkBoxLeadConstraint.constant = leftIdent;
cell.checkBoxWidthConstraint.constant = checkBoxWidth;
cell.iconImg.tintColor = cell.tintColor; // default (blue) tintColor
cell.prioritySegment.hidden = YES; // by default folders don't have priority segment
cell.nameLabel.textColor = [UIColor blackColor]; // by default file/folder names are black
cell.nameLabelTrailConstraint.priority = 751;
cell.nameLabelTrailToSegmentConstraint.priority = 750;
cell.touchView.userInteractionEnabled = NO;
cell.prioritySegment.dataObject = item;
cell.checkBox.dataObject = item;
if (_selectOnly)
{
cell.detailLabel.text = item.lengthString;
}
else
{
cell.detailLabel.text = [NSString stringWithFormat: NSLocalizedString(@"%@ of %@, %@ downloaded", @"FileList cell file info"),
item.bytesComplitedString,
item.lengthString,
item.downloadProgressString];
}
if( item.downloadProgress < 1.0f )
{
if( !_isSelectOnly )
{
// configure priority segment control
[cell.prioritySegment addTarget:self action:@selector(prioritySegmentToggled:) forControlEvents:UIControlEventValueChanged];
cell.prioritySegment.dataObject = @(item.rpcIndex);
cell.prioritySegment.selectedSegmentIndex = item.priority + 1;
cell.prioritySegment.enabled = YES;
cell.prioritySegment.hidden = NO;
}
// configure left checkBox control
[cell.checkBox addTarget:self action:@selector(toggleFileDownloading:) forControlEvents:UIControlEventValueChanged];
if( !item.wanted || _selectOnly )
{
cell.prioritySegment.hidden = YES;
if( !item.wanted )
{
cell.iconImg.tintColor = [UIColor grayColor];
cell.nameLabel.textColor = [UIColor grayColor];
cell.checkBox.color = [UIColor grayColor];
}
}
else
{
cell.nameLabelTrailConstraint.priority = 750;
cell.nameLabelTrailToSegmentConstraint.priority = 751;
}
if( updateWanted )
cell.checkBox.on = item.wanted;
cell.checkBox.color = item.wanted ? cell.tintColor : [UIColor grayColor];
}
else
{
cell.checkBox.hidden = YES;
}
}
- (void)updateFolderCell:(FileListFSCell *)cell withFSItem:(FSItem *)item updateWanted:(BOOL)updateWanted
{
// remove all old targets
[cell.checkBox removeTarget:self action:@selector(toggleFileDownloading:) forControlEvents:UIControlEventValueChanged];
[cell.checkBox removeTarget:self action:@selector(toggleFolderDownloading:) forControlEvents:UIControlEventValueChanged];
//cell.nameLabel.text = item.name;
cell.iconImg.image = item.isCollapsed ? _iconImgFolderClosed : _iconImgFolderOpened;
// make indentation
float leftIdent = ( (item.level - 1) * FILELISTFSCELL_LEFTLABEL_LEVEL_INDENTATION ) + 8;
float checkBoxWidth = 33;
if( _isFullyLoaded )
{
cell.checkBox.hidden = YES;
checkBoxWidth = 0;
}
else
cell.checkBox.hidden = NO;
cell.checkBoxLeadConstraint.constant = leftIdent;
cell.checkBoxWidthConstraint.constant = checkBoxWidth;
cell.iconImg.tintColor = cell.tintColor; // default (blue) tintColor
cell.prioritySegment.hidden = YES; // by default folders don't have priority segment
cell.nameLabel.textColor = [UIColor blackColor]; // by default file/folder names are black
cell.nameLabelTrailConstraint.priority = 751;
cell.nameLabelTrailToSegmentConstraint.priority = 750;
cell.touchView.userInteractionEnabled = NO;
if (_selectOnly)
{
cell.detailLabel.text = [NSString stringWithFormat:NSLocalizedString( @"%i files, %@", @"" ), item.filesCount, item.lengthString];
}
else
{
cell.detailLabel.text = [NSString stringWithFormat: NSLocalizedString(@"%i files, %@ of %@, %@ downloaded", @""),
item.filesCount,
item.bytesComplitedString,
item.lengthString,
item.downloadProgressString];
}
if( item.downloadProgress < 1.0 )
{
BOOL isAllWanted = item.wanted;
// get info for files within folder
if( updateWanted )
cell.checkBox.on = isAllWanted;
cell.checkBox.color = isAllWanted ? cell.tintColor : [UIColor grayColor];
cell.nameLabel.textColor = isAllWanted ? [UIColor blackColor] : [UIColor grayColor];
cell.iconImg.tintColor = isAllWanted ? cell.tintColor : [UIColor grayColor];
// add recognizer for unwanted files
cell.checkBox.dataObject = item;
[cell.checkBox addTarget:self action:@selector(toggleFolderDownloading:) forControlEvents:UIControlEventValueChanged];
}
else
{
cell.checkBox.hidden = YES;
}
// Add tap handeler for folder - open/close
[cell.touchView layoutIfNeeded];
UITapGestureRecognizer *tapFolderRec = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(folderTapped:)];
tapFolderRec.dataObject = item;
cell.touchView.userInteractionEnabled = YES;
[cell.touchView addGestureRecognizer:tapFolderRec];
}
@end