RetroShare/retroshare-gui/src/gui/common/RsCollectionDialog.cpp
2026-01-04 18:07:08 +01:00

876 lines
30 KiB
C++

/*******************************************************************************
* retroshare-gui/src/gui/common/RsCollectionDialog.cpp *
* *
* Copyright (C) 2011, Retroshare Team <retroshare.project@gmail.com> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Affero General Public License for more details. *
* *
* You should have received a copy of the GNU Affero General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
#include "gui/common/FilesDefs.h"
#include "RsCollectionDialog.h"
#include "RsCollection.h"
#include "util/misc.h"
#include "util/rsdir.h"
#include <QCheckBox>
#include <QDateTime>
#include <QDir>
#include <QFileSystemModel>
#include <QHeaderView>
#include <QInputDialog>
#include <QKeyEvent>
#include <QMessageBox>
#include <QMenu>
#include <QTextEdit>
#include <QTreeView>
#include <QFileDialog>
#define COLUMN_FILE 0
#define COLUMN_FILEPATH 1
#define COLUMN_SIZE 2
#define COLUMN_HASH 3
#define COLUMN_FILEC 4
#define COLUMN_COUNT 5
// In COLUMN_HASH (COLUMN_FILE reserved for CheckState)
#define ROLE_NAME Qt::UserRole + 1
#define ROLE_PATH Qt::UserRole + 2
#define ROLE_TYPE Qt::UserRole + 3
// In COLUMN_SIZE (don't start from 1 to read)
#define ROLE_SIZE Qt::UserRole + 4
#define ROLE_SELSIZE Qt::UserRole + 5
// In COLUMN_FILEC (don't start from 1 to read)
#define ROLE_FILEC Qt::UserRole + 6
#define ROLE_SELFILEC Qt::UserRole + 7
#define MAX_FILE_ADDED_BEFORE_ASK 500 //Number of files added in Recursive mode before asking to continue
#define IMAGE_SEARCH ":/icons/svg/magnifying-glass.svg"
/**
* @brief The FSMSortFilterProxyModel class sorts directories before files.
*/
class FSMSortFilterProxyModel : public QSortFilterProxyModel
{
public:
FSMSortFilterProxyModel( QObject *parent) : QSortFilterProxyModel(parent)
{
setDynamicSortFilter(false); // essential to avoid random crashes
}
protected:
virtual bool lessThan(const QModelIndex &left, const QModelIndex &right) const
{
QFileSystemModel *fsm = qobject_cast<QFileSystemModel*>(sourceModel());
bool asc = (sortOrder() == Qt::AscendingOrder ? true : false) ;
QFileInfo leftFileInfo = fsm->fileInfo(left);
QFileInfo rightFileInfo = fsm->fileInfo(right);
// If Dot move in the beginning
if (sourceModel()->data(left).toString() == ".")
return asc;
if (sourceModel()->data(right).toString() == ".")
return !asc;
// If DotAndDot move in the beginning
if (sourceModel()->data(left).toString() == "..")
return asc;
if (sourceModel()->data(right).toString() == "..")
return !asc;
// Move dirs upper
if (!leftFileInfo.isDir() && rightFileInfo.isDir())
return !asc;
if (leftFileInfo.isDir() && !rightFileInfo.isDir())
return asc;
/*If sorting by Size (Take real size, not Display string)*/
if ((sortColumn()==1) && (!leftFileInfo.isDir() && !rightFileInfo.isDir())) {
if (leftFileInfo.size() < rightFileInfo.size())
return true;
if (leftFileInfo.size() > rightFileInfo.size())
return false;
}
/*If sorting by Date Modified (Take real date, not Display string)*/
if (sortColumn()==3) {
if (leftFileInfo.lastModified() < rightFileInfo.lastModified())
return true;
if (leftFileInfo.lastModified() > rightFileInfo.lastModified())
return false;
}
return QSortFilterProxyModel::lessThan(left, right);
}
};
/**
* @brief RsCollectionDialog::RsCollectionDialog
* @param collectionFileName: Filename of RSCollection saved
* @param mode: Open dialog as RsColl Creation or RsColl DownLoad
*/
RsCollectionDialog::RsCollectionDialog(const QString& collectionFileName, RsCollectionDialogMode mode)
: _mode(mode), _dirModel(nullptr), _tree_proxyModel(nullptr), _selectionProxy(nullptr)
{
RsCollection::RsCollectionErrorCode err_code;
mCollection = new RsCollection(collectionFileName,err_code);
if(err_code != RsCollection::RsCollectionErrorCode::COLLECTION_NO_ERROR)
{
QMessageBox::information(nullptr,tr("Could not load collection file"),tr("Could not load collection file"));
close();
}
init(collectionFileName);
}
RsCollectionDialog::RsCollectionDialog(const RsCollection& coll, RsCollectionDialogMode mode)
: _mode(mode), _dirModel(nullptr), _tree_proxyModel(nullptr), _selectionProxy(nullptr)
{
mCollection = new RsCollection(coll);
init(QString());
}
void RsCollectionDialog::init(const QString& collectionFileName)
{
ui.setupUi(this) ;
ui._filename_TL->setText(collectionFileName);
// uint32_t size = colFileInfos.size();
// for(uint32_t i=0;i<size;++i)
// {
// const ColFileInfo &colFileInfo = colFileInfos[i];
// _newColFileInfos.push_back(colFileInfo);
// }
setWindowFlags(Qt::Window); // for maximize button
setWindowFlags(windowFlags() & ~Qt::WindowMinimizeButtonHint);
setWindowTitle(QString("%1 - %2").arg(windowTitle()).arg(QFileInfo(collectionFileName).completeBaseName()));
ui.headerFrame->setHeaderImage(FilesDefs::getPixmapFromQtResourcePath(":/icons/collections.png"));
if(_mode == DOWNLOAD)
{
ui.headerFrame->setHeaderText(tr("Download files"));
ui.downloadFolder_LE->show();
ui.downloadFolder_LB->show();
ui.label_filename->hide();
ui._filename_TL->hide();
ui.downloadFolder_LE->setText(QString::fromUtf8(rsFiles->getDownloadDirectory().c_str())) ;
QObject::connect(ui.downloadFolder_LE,SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(openDestinationDirectoryMenu()));
QObject::connect(ui.destinationDir_TB,SIGNAL(pressed()), this, SLOT(openDestinationDirectoryMenu()));
}
else
{
ui.headerFrame->setHeaderText(tr("Collection Editor"));
ui.downloadFolder_LE->hide();
ui.downloadFolder_LB->hide();
ui.destinationDir_TB->hide();
ui.label_filename->show();
ui._filename_TL->show();
}
// 1 - add all elements to the list.
mCollectionModel = new RsCollectionModel(*mCollection);
ui._fileEntriesTW->setModel(mCollectionModel);
connect(mCollectionModel,SIGNAL(sizesChanged()),this,SLOT(updateSizes()));
updateSizes(); // forced because it's only called when the collection is changed, or when the model is created.
// 2 - connect necessary signals/slots
connect(ui._changeFile, SIGNAL(clicked()), this, SLOT(changeFileName()));
connect(ui._add_PB, SIGNAL(clicked()), this, SLOT(addSelection()));
connect(ui._addRecur_PB, SIGNAL(clicked()), this, SLOT(addSelectionRecursive()));
connect(ui._remove_PB, SIGNAL(clicked()), this, SLOT(remove()));
connect(ui._makeDir_PB, SIGNAL(clicked()), this, SLOT(makeDir()));
connect(ui._cancel_PB, SIGNAL(clicked()), this, SLOT(cancel()));
connect(ui._save_PB, SIGNAL(clicked()), this, SLOT(save()));
connect(ui._download_PB, SIGNAL(clicked()), this, SLOT(download()));
connect(ui._hashBox, SIGNAL(fileHashingFinished(QList<HashedFile>)), this, SLOT(fileHashingFinished(QList<HashedFile>)));
// 3 Initialize Local System List ONLY in EDIT mode
if (_mode == EDIT)
{
_dirModel = new QFileSystemModel(this);
_dirModel->setRootPath(QDir::homePath());
_dirModel->setFilter(QDir::AllEntries | QDir::NoSymLinks | QDir::NoDotAndDotDot);
_dirLoaded = false;
connect(_dirModel, SIGNAL(directoryLoaded(QString)), this, SLOT(directoryLoaded(QString)));
_tree_proxyModel = new FSMSortFilterProxyModel(this);
_tree_proxyModel->setSourceModel(_dirModel);
_tree_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
_tree_proxyModel->setSortRole(Qt::DisplayRole);
ui._systemFileTW->setModel(_tree_proxyModel);
_selectionProxy = ui._systemFileTW->selectionModel();
ui._systemFileTW->installEventFilter(this);
}
else
{
_dirModel = nullptr;
_tree_proxyModel = nullptr;
_selectionProxy = nullptr;
_dirLoaded = true;
}
// 4 Restore Configuration
// load settings
processSettings(true);
// 5 Activate button follow creationMode
ui._changeFile->setVisible(_mode == EDIT);
ui._makeDir_PB->setVisible(_mode == EDIT);
ui._save_PB->setVisible(_mode == EDIT);
ui._treeViewFrame->setVisible(_mode == EDIT);
ui._download_PB->setVisible(_mode == DOWNLOAD);
// 6 Add HashBox
setAcceptDrops(true);
ui._hashBox->setDropWidget(this);
ui._hashBox->setAutoHide(true);
ui._hashBox->setDefaultTransferRequestFlags(RS_FILE_REQ_ANONYMOUS_ROUTING) ;
}
void RsCollectionDialog::openDestinationDirectoryMenu()
{
QMenu contextMnu( this );
// pop a menu with existing entries and also a custom entry
// Now get the list of existing directories.
std::list< SharedDirInfo> dirs ;
rsFiles->getSharedDirectories( dirs) ;
for (std::list<SharedDirInfo>::const_iterator it(dirs.begin());it!=dirs.end();++it){
// Check for existence of directory name
QFile directory( QString::fromUtf8((*it).filename.c_str())) ;
if (!directory.exists()) continue ;
if (!(directory.permissions() & QFile::WriteOwner)) continue ;
contextMnu.addAction(QString::fromUtf8((*it).filename.c_str()), this, SLOT(setDestinationDirectory()))->setData(QString::fromUtf8( (*it).filename.c_str() ) ) ;
}
contextMnu.addAction( FilesDefs::getIconFromQtResourcePath(IMAGE_SEARCH),tr("Specify..."),this,SLOT(chooseDestinationDirectory()));
contextMnu.exec(QCursor::pos()) ;
}
void RsCollectionDialog::setDestinationDirectory()
{
QString dest_dir(qobject_cast<QAction*>(sender())->data().toString()) ;
ui.downloadFolder_LE->setText(dest_dir) ;
}
void RsCollectionDialog::chooseDestinationDirectory()
{
QString dest_dir = QFileDialog::getExistingDirectory(this,tr("Choose directory")) ;
if(dest_dir.isNull())
return ;
ui.downloadFolder_LE->setText(dest_dir) ;
}
/**
* @brief RsCollectionDialog::~RsCollectionDialog
*/
RsCollectionDialog::~RsCollectionDialog()
{
// save settings
processSettings(false);
}
/**
* @brief RsCollectionDialog::processSettings
* @param bLoad: Load or Save dialog's settings
*/
void RsCollectionDialog::processSettings(bool bLoad)
{
Settings->beginGroup("RsCollectionDialogV2");
if (bLoad) {
// load settings
if(_mode == EDIT){
// Load windows geometry
restoreGeometry(Settings->value("WindowGeometrie_CM").toByteArray());
// Load splitters state
ui._mainSplitter->restoreState(Settings->value("MainSplitterState_CM").toByteArray());
ui._listSplitter->restoreState(Settings->value("ListSplitterState_CM").toByteArray());
// Load system file header configuration
ui._systemFileTW->header()->restoreState(Settings->value("SystemFileHeader_CM").toByteArray());
// Load file entries header configuration
ui._fileEntriesTW->header()->restoreState(Settings->value("FileEntriesHeader_CM").toByteArray());
} else {
// Load windows geometry
restoreGeometry(Settings->value("WindowGeometrie").toByteArray());
// Load splitters state
ui._mainSplitter->restoreState(Settings->value("MainSplitterState").toByteArray());
ui._listSplitter->restoreState(Settings->value("ListSplitterState").toByteArray());
// Load system file header configuration
ui._systemFileTW->header()->restoreState(Settings->value("SystemFileHeader").toByteArray());
// Load file entries header configuration
ui._fileEntriesTW->header()->restoreState(Settings->value("FileEntriesHeader").toByteArray());
}
} else {
if(_mode == EDIT){
// Save windows geometry
Settings->setValue("WindowGeometrie_CM",saveGeometry());
// Save splitters state
Settings->setValue("MainSplitterState_CM", ui._mainSplitter->saveState());
Settings->setValue("ListSplitterState_CM", ui._listSplitter->saveState());
// Save system file header configuration
Settings->setValue("SystemFileHeader_CM", ui._systemFileTW->header()->saveState());
// Save file entries header configuration
Settings->setValue("FileEntriesHeader_CM", ui._fileEntriesTW->header()->saveState());
} else {
// Save windows geometry
Settings->setValue("WindowGeometrie",saveGeometry());
// Save splitter state
Settings->setValue("MainSplitterState", ui._mainSplitter->saveState());
Settings->setValue("ListSplitterState", ui._listSplitter->saveState());
// Save system file header configuration
Settings->setValue("SystemFileHeader", ui._systemFileTW->header()->saveState());
// Save file entries header configuration
Settings->setValue("FileEntriesHeader", ui._fileEntriesTW->header()->saveState());
}
}
Settings->endGroup();
}
/**
* @brief RsCollectionDialog::directoryLoaded: called when ui._treeView have load an directory as QFileSystemModel don't load all in one time
* @param dirLoaded: Name of directory just loaded
*/
void RsCollectionDialog::directoryLoaded(QString dirLoaded)
{
if(!_dirModel) return;
if(!_dirLoaded)
{
QFileInfo lastDir = QFileInfo(Settings->getLastDir(RshareSettings::LASTDIR_EXTRAFILE));
if (lastDir.absoluteFilePath() == "") return;
if (lastDir.absoluteFilePath() == dirLoaded) _dirLoaded = true;
// Check all parent directory to see if it is loaded
//as QFileSystemModel don't load all
do {
if(lastDir.absolutePath()==dirLoaded) {
//Only expand loaded directory
QModelIndex lastDirIdx = _dirModel->index(lastDir.absoluteFilePath());
//Select LASTDIR_EXTRAFILE or last parent found when loaded
if (!lastDirIdx.isValid()){
_dirLoaded = true;
break;
}
//Ask dirModel to load next parent directory
while (_dirModel->canFetchMore(lastDirIdx)) _dirModel->fetchMore(lastDirIdx);
//Ask to Expand last loaded parent
lastDirIdx = _tree_proxyModel->mapFromSource(lastDirIdx);
if (lastDirIdx.isValid()){
ui._systemFileTW->expand(lastDirIdx);
ui._systemFileTW->setCurrentIndex(lastDirIdx);
}
break;
}
//std::cerr << "lastDir = " << lastDir.absoluteFilePath().toStdString() << std::endl;
QDir c = lastDir.dir() ;
if(!c.cdUp())
break ;
lastDir = QFileInfo(c.path());
} while (true); //(lastDir.absoluteFilePath() != lastDir.absolutePath());
}
}
/**
* @brief RsCollectionDialog::updateSizes: Update Column size of ui._fileEntriesTW)
*/
void RsCollectionDialog::updateSizes()
{
ui._selectedFiles_TL->setText(QString::number(mCollectionModel->totalSelected()));
ui._totalSize_TL->setText(misc::friendlyUnit(mCollectionModel->totalSize()));
}
/**
* @brief purifyFileName: Check if name of file is good or not.
* @param input
* @param bad
* @return
*/
static bool checkFileName(const std::string& input, std::string& corrected)
{
static const std::string bad_chars ( "/\\\"*:?<>|" );
bool ok = true ;
corrected = input ;
for(uint32_t i=0;i<input.length();++i)
for(uint32_t j=0;j<bad_chars.length();++j)
if(input[i] == bad_chars[j])
{
corrected[i] = '_' ;
ok = false ;
}
return ok ;
}
/**
* @brief RsCollectionDialog::changeFileName: Change the name of saved file
*/
void RsCollectionDialog::changeFileName()
{
QString fileName;
if(!misc::getSaveFileName(this, RshareSettings::LASTDIR_EXTRAFILE
, QApplication::translate("RsCollectionFile", "Create collection file")
, QApplication::translate("RsCollectionFile", "Collection files") + " (*." + RsCollection::ExtensionString + ")"
, fileName,0, QFileDialog::DontConfirmOverwrite))
return;
if (!fileName.endsWith("." + RsCollection::ExtensionString))
fileName += "." + RsCollection::ExtensionString ;
std::cerr << "Got file name: " << fileName.toStdString() << std::endl;
QFile file(fileName) ;
RsCollection::RsCollectionErrorCode err;
if(file.exists() && RsCollection::checkFile(fileName,err))
{
QMessageBox mb;
mb.setText(tr("Save Collection File."));
mb.setInformativeText(tr("File already exists.")+"\n"+tr("What do you want to do?"));
QAbstractButton *btnOwerWrite = mb.addButton(tr("Overwrite"), QMessageBox::YesRole);
QAbstractButton *btnMerge = mb.addButton(tr("Merge"), QMessageBox::NoRole);
QAbstractButton *btnCancel = mb.addButton(tr("Cancel"), QMessageBox::ResetRole);
mb.setIcon(QMessageBox::Question);
mb.exec();
if (mb.clickedButton()==btnOwerWrite) {
//Nothing to do
}
else if(mb.clickedButton()==btnMerge)
{
//Open old file to merge it with RsCollection
RsCollection qddOldFileCollection(fileName,err);
if(err != RsCollection::RsCollectionErrorCode::COLLECTION_NO_ERROR)
{
mCollectionModel->preMods();
mCollection->merge_in(qddOldFileCollection.fileTree());
mCollectionModel->postMods();
}
}
else if(mb.clickedButton()==btnCancel)
return;
else
return;
}
else
{
//create a new empty file to check if name if good.
if (!file.open(QFile::WriteOnly))
return;
file.remove();
}
ui._filename_TL->setText(fileName);
}
/**
* @brief RsCollectionDialog::add: */
void RsCollectionDialog::addSelection()
{
addSelection(false);
}
/**
* @brief RsCollectionDialog::addRecursive:
*/
void RsCollectionDialog::addSelectionRecursive()
{
addSelection(true);
}
static void recursBuildFileTree(const QString& path,RsFileTree& tree,RsFileTree::DirIndex dir_index,bool recursive,std::map<QString,RsFileHash>& paths_to_hash)
{
QFileInfo fileInfo = QFileInfo(path);
if (fileInfo.isDir())
{
auto di = tree.addDirectory(dir_index,fileInfo.fileName().toUtf8().constData());
if(recursive)
{
QDir dirParent = fileInfo.absoluteFilePath();
dirParent.setFilter(QDir::AllEntries | QDir::NoSymLinks | QDir::NoDotAndDotDot);
QFileInfoList childrenList = dirParent.entryInfoList();
for(QFileInfo f:childrenList)
recursBuildFileTree(f.absoluteFilePath(),tree,di,recursive,paths_to_hash);
}
}
else
{
// Here we use a temporary hash that serves two purposes:
// 1 - identify the file in the RsFileTree of the collection so that we can update its hash when calculated
// 2 - mark the file as being processed
// The hash s is computed to be the hash of the path of the file. The collection must take care of multiple instances.
Sha1CheckSum s = RsDirUtil::sha1sum((uint8_t*)(fileInfo.filePath().toUtf8().constData()),fileInfo.filePath().toUtf8().size());
tree.addFile(dir_index,fileInfo.fileName().toUtf8().constData(),s,fileInfo.size());
paths_to_hash.insert(std::make_pair(fileInfo.filePath(),s));
}
}
/**
* @brief RsCollectionDialog::addRecursive: Add Selected item to RSCollection
* -Add File seperatly if parent folder not selected
* -Add File in folder if selected
* -Get root folder the selected one
* @param recursive: If true, add all selected directory childrens
*/
void RsCollectionDialog::addSelection(bool recursive)
{
if(!_dirModel) return;
QMap<QString, QString > dirToAdd;
QModelIndexList milSelectionList = ui._systemFileTW->selectionModel()->selectedIndexes();
mCollectionModel->preMods();
std::map<QString,RsFileHash> paths_to_hash; // sha1sum of the paths to hash.
foreach (QModelIndex index, milSelectionList)
if(index.column()==0) //Get only FileName
{
RsFileTree tree;
recursBuildFileTree(_dirModel->filePath(_tree_proxyModel->mapToSource(index)),tree,tree.root(),recursive,paths_to_hash);
mCollection->merge_in(tree);
}
mFilesBeingHashed.insert(paths_to_hash.begin(),paths_to_hash.end());
QStringList paths;
std::list<RsFileHash> hashes;
for(auto it:paths_to_hash)
{
paths.push_back(it.first);
hashes.push_back(it.second);
std::cerr << "Setting file has being hased: ID=" << it.second << " - " << it.first.toUtf8().constData() << std::endl;
}
mCollectionModel->notifyFilesBeingHashed(hashes);
mCollectionModel->postMods();
ui._hashBox->addAttachments(paths,RS_FILE_REQ_ANONYMOUS_ROUTING /*, 0*/);
if(!mFilesBeingHashed.empty())
{
ui._save_PB->setToolTip(tr("Please wait for all files to be properly processed before saving."));
ui._save_PB->setEnabled(false);
}
}
/**
* @brief RsCollectionDialog::remove: Remove selected Items in RSCollection
*/
void RsCollectionDialog::remove()
{
QMap<QString, QString > dirToRemove;
QModelIndexList milSelectionList = ui._fileEntriesTW->selectionModel()->selectedIndexes();
mCollectionModel->preMods();
foreach (QModelIndex index, milSelectionList)
if(index.column()==0) //Get only FileName
{
auto indx = mCollectionModel->getIndex(index);
auto parent_indx = mCollectionModel->getIndex(index.parent());
if(indx.is_file)
mCollection->removeFile(indx.index,parent_indx.index);
else
mCollection->removeDirectory(indx.index,parent_indx.index);
}
mCollectionModel->postMods();
}
/**
* @brief RsCollectionDialog::addDir: Add new empty dir to list
*/
void RsCollectionDialog::makeDir()
{
QModelIndexList selected_indices = ui._fileEntriesTW->selectionModel()->selectedIndexes();
if(selected_indices.size() > 1)
{
QMessageBox::information(nullptr,tr("Too many places selected"),tr("Please select at most one directory where to create the new folder"));
return;
}
QModelIndex place_index;
if(!selected_indices.empty())
place_index = selected_indices.first();
RsCollectionModel::EntryIndex e = mCollectionModel->getIndex(place_index);
if(e.is_file)
{
QMessageBox::information(nullptr,tr("Selected place cannot be a file"),tr("Please select at most one directory where to create the new folder"));
return;
}
QString childName = QInputDialog::getText(this, tr("New Directory"), tr("Enter the new directory's name"), QLineEdit::Normal);
mCollectionModel->preMods();
mCollection->merge_in(*RsFileTree::fromDirectory(childName.toUtf8().constData()),e.index);
mCollectionModel->postMods();
}
/**
* @brief RsCollectionDialog::fileHashingFinished: Connected to ui._hashBox.fileHashingFinished
* Add finished File to collection in respective directory
* @param hashedFiles: List of the file finished
*/
void RsCollectionDialog::fileHashingFinished(QList<HashedFile> hashedFiles)
{
// build a map of old-hash to new-hash for the hashed files, so that it can be passed to the mCollection for update
mCollectionModel->preMods();
std::map<RsFileHash,RsFileHash> old_to_new_hashes;
for(auto f:hashedFiles)
{
auto it = mFilesBeingHashed.find(f.filepath);
if(it == mFilesBeingHashed.end())
{
RsErr() << "Could not find hash-ID correspondence for path " << f.filepath.toUtf8().constData() << ". This is a bug." << std::endl;
continue;
}
std::cerr << "Will update old hash " << it->second << " to new hash " << f.hash << std::endl;
old_to_new_hashes.insert(std::make_pair(it->second,f.hash));
mFilesBeingHashed.erase(it);
mCollectionModel->fileHashingFinished(it->second);
}
mCollection->updateHashes(old_to_new_hashes);
mCollectionModel->postMods();
if(mFilesBeingHashed.empty())
{
ui._save_PB->setToolTip(tr(""));
ui._save_PB->setEnabled(true);
}
}
/**
* @brief RsCollectionDialog::cancel: Cancel RScollection editing or donwloading
*/
void RsCollectionDialog::cancel()
{
std::cerr << "Canceling!" << std::endl;
close() ;
}
/**
* @brief RsCollectionDialog::download: Start downloading checked items
*/
void RsCollectionDialog::download()
{
std::cerr << "Downloading!" << std::endl;
bool auto_correct = false;
bool auto_skip = false;
QString dldir = ui.downloadFolder_LE->text();
std::cerr << "downloading all these files:" << std::endl;
std::function<void(RsFileTree::DirIndex,const std::string&)> recursDL = [&](RsFileTree::DirIndex index,const std::string& path)
{
const auto& dirdata(mCollection->fileTree().directoryData(index));
RsCollectionModel::EntryIndex e;
for(uint32_t i=0;i<dirdata.subdirs.size();++i)
{
e.index = dirdata.subdirs[i];
e.is_file = false;
if(!mCollectionModel->isChecked(e))
continue;
const auto& sdd = mCollection->fileTree().directoryData(e.index);
std::string subpath = RsDirUtil::makePath(path,sdd.name);
std::cerr << "Creating subdir " << sdd.name << " to directory " << path << std::endl;
if(!QDir(QApplication::applicationDirPath()).mkpath(QString::fromUtf8(subpath.c_str())))
QMessageBox::warning(NULL,tr("Unable to make path"),tr("Unable to make path:")+"<br> "+QString::fromUtf8(subpath.c_str())) ;
recursDL(dirdata.subdirs[i],subpath);
}
for(uint32_t i=0;i<dirdata.subfiles.size();++i)
{
e.index = dirdata.subfiles[i];
e.is_file = true;
if(!mCollectionModel->isChecked(e))
continue;
std::string subpath = RsDirUtil::makePath(path,dirdata.name);
const auto& f(mCollection->fileTree().fileData(dirdata.subfiles[i]));
std::string corrected_name;
if(!checkFileName(f.name,corrected_name) && !auto_correct)
{
if(auto_skip)
continue;
QMessageBox mb;
mb.setText(tr("Incompatible filename."));
mb.setInformativeText(tr("This filename is not usable on your system.")+"\n"+tr("Retroshare can replace every problematic chars by '_'.")
+"\n"+tr("What do you want to do?"));
QAbstractButton *btnCorrect = mb.addButton(tr("Correct filename"), QMessageBox::YesRole);
QAbstractButton *btnCorrectAll = mb.addButton(tr("Correct all"), QMessageBox::AcceptRole);
QAbstractButton *btnSkip = mb.addButton(tr("Skip this file"), QMessageBox::NoRole);
QAbstractButton *btnSkipAll = mb.addButton(tr("Skip all"), QMessageBox::RejectRole);
mb.setIcon(QMessageBox::Question);
mb.exec();
if(mb.clickedButton() == btnSkipAll)
{
auto_skip = true;
continue;
}
if(mb.clickedButton() == btnSkip)
continue;
if(mb.clickedButton() == btnCorrectAll)
{
auto_correct = true;
}
if(mb.clickedButton() == btnCorrect)
{
auto_correct = false; // logic placeholder
}
}
rsFiles->FileRequest(corrected_name,f.hash,f.size,path,RS_FILE_REQ_ANONYMOUS_ROUTING,std::list<RsPeerId>());
}
};
recursDL(mCollection->fileTree().root(),dldir.toUtf8().constData());
close();
}
/**
* @brief RsCollectionDialog::save: Update collection to save it in caller.
*/
void RsCollectionDialog::save()
{
if(ui._filename_TL->text().isNull())
changeFileName();
if(ui._filename_TL->text().isNull())
return;
mCollectionModel->preMods();
mCollection->cleanup();
mCollection->save(ui._filename_TL->text());
close();
}
bool RsCollectionDialog::editExistingCollection(const QString& fileName, bool /*showError*/)
{
return RsCollectionDialog(fileName,EDIT).exec();
}
bool RsCollectionDialog::openExistingCollection(const QString& fileName, bool /*showError*/)
{
return RsCollectionDialog(fileName,DOWNLOAD).exec();
}
bool RsCollectionDialog::downloadFiles(const RsCollection &collection)
{
return RsCollectionDialog(collection,DOWNLOAD).exec();
}
bool RsCollectionDialog::openNewCollection(const RsFileTree& tree)
{
RsCollection collection(tree);
QString fileName;
if(!misc::getSaveFileName(nullptr, RshareSettings::LASTDIR_EXTRAFILE
, QApplication::translate("RsCollectionFile", "Create collection file")
, QApplication::translate("RsCollectionFile", "Collection files") + " (*." + RsCollection::ExtensionString + ")"
, fileName,0, QFileDialog::DontConfirmOverwrite))
return false;
if (!fileName.endsWith("." + RsCollection::ExtensionString))
fileName += "." + RsCollection::ExtensionString ;
std::cerr << "Got file name: " << fileName.toStdString() << std::endl;
if(QFile(fileName).exists())
{
QMessageBox mb;
mb.setText(tr("Save Collection File."));
mb.setInformativeText(tr("File already exists.")+"\n"+tr("What do you want to do?"));
QAbstractButton *btnOwerWrite = mb.addButton(tr("Overwrite"), QMessageBox::YesRole);
QAbstractButton *btnCancel = mb.addButton(tr("Cancel"), QMessageBox::ResetRole);
mb.setIcon(QMessageBox::Question);
mb.exec();
if (mb.clickedButton()==btnCancel)
return false;
if (mb.clickedButton()==btnOwerWrite)
{
// Proceed to overwrite
}
}
if(!collection.save(fileName))
return false;
return RsCollectionDialog(fileName,EDIT).exec();
}